Feat/voucher docs (#664)

* feat: implement inbox document picker and linking functionality

* feat: implement self-billing invoice functionality

- Added support for registering self-billed invoices received from customers.
- Updated the invoice schema to include fields for self-billing metadata such as `is_self_billed`, `external_invoice_number`, `self_billing_agreement_ref`, and `received_date`.
- Created API route for handling self-billed invoice submissions, including validation and error handling.
- Implemented database migrations to add necessary columns and constraints for self-billing invoices.
- Developed tests to ensure correct behavior of self-billing invoice creation and validation rules.
- Updated Swedish localization files to include new terms related to self-billing.

* feat: enforce SIE import requirement for non-Fortnox providers in migration process

* feat: streamline invoice processing and enhance error logging across APIs
This commit is contained in:
Mattsson
2026-06-04 13:14:57 +02:00
committed by GitHub
parent c1be9f15dd
commit 3e42fc6f32
45 changed files with 2675 additions and 529 deletions
+72 -7
View File
@@ -793,8 +793,6 @@
"toast_invalid_fiscal_year": "Invalid fiscal year",
"toast_error_title": "Error",
"toast_create_failed": "Could not create company. Please try again.",
"toast_company_exists_title": "Company already exists",
"toast_company_exists_description": "This company already exists in {appName}. Ask an existing administrator to invite you.",
"toast_org_invalid_title": "Invalid organisation number",
"toast_org_invalid_description": "Check that you entered a valid 10- or 12-digit organisation number.",
"toast_welcome_title": "Welcome!",
@@ -820,7 +818,7 @@
"step2_lookup_not_found": "No company found with that organisation number.",
"step2_lookup_failed": "Could not fetch company details. You can fill them in manually.",
"step2_ceased_inline": "{companyName} — the company is deregistered",
"step2_company_exists": "This company already exists in {appName}. Ask an existing administrator to invite you.",
"step2_company_exists_own": "You already have {name} with this organisation number. You can still continue.",
"step2_company_name_ab": "Company name *",
"step2_company_name_ef": "Business name (or your name for EF) *",
"step2_company_name_placeholder_ab": "AB Företaget",
@@ -906,7 +904,6 @@
"section_your_companies": "Your companies in {appName}",
"section_bankid_companies": "Companies linked to your BankID",
"already_in_app": "Already in {appName}",
"ask_admin_invite": "Ask an existing administrator to invite you.",
"setup_manually": "Set up manually",
"no_companies_found": "No companies found. Add your first company below.",
"or_separator": "or",
@@ -915,8 +912,6 @@
"toast_lookup_failed_description": "Fill in the remaining details manually.",
"toast_company_ceased_title": "The company is deregistered",
"toast_company_ceased_description": "You cannot set up bookkeeping for a deregistered company.",
"toast_company_exists_title": "Company already exists",
"toast_company_exists_description": "Ask an existing administrator to invite you.",
"toast_org_invalid_title": "Invalid organisation number",
"toast_org_invalid_description": "Continue with manual setup.",
"toast_create_failed_title": "Could not create company",
@@ -2101,6 +2096,8 @@
"vat_label_short": "VAT",
"total_label": "Total",
"review_and_create": "Review & create",
"mode_invoice": "Invoice",
"mode_self_billed": "Self-billing",
"viewer_disabled_tooltip": "You only have read-only access to this company",
"review_dialog_title_invoice": "Review invoice",
"review_dialog_title_proforma": "Review proforma invoice",
@@ -2209,6 +2206,9 @@
"status_credited": "Credited",
"badge_proforma": "Proforma",
"badge_delivery_note": "Delivery note",
"badge_self_billed": "Self-billing",
"external_number_label": "Invoice number (customer's)",
"agreement_ref_label": "Agreement reference",
"created_at": "Created {date}",
"sent_at_suffix": " • Sent {date}",
"convert_to_invoice": "Convert to invoice",
@@ -2892,7 +2892,22 @@
"remove_blocked_cancel_cta": "Close",
"replace_uploading": "Replacing...",
"remove_failed": "Could not remove the document.",
"replace_failed": "Could not upload new version."
"replace_failed": "Could not upload new version.",
"choose_from_inbox": "Choose from inbox",
"picker_title": "Choose a document from the inbox",
"picker_description": "Documents received by email or upload that haven't been used yet.",
"picker_search_placeholder": "Search supplier or file name…",
"picker_results": "{count}",
"picker_empty": "No unused documents in the inbox.",
"picker_empty_search": "No documents match \"{query}\".",
"picker_source_email": "Email",
"picker_source_upload": "Upload",
"picker_linked": "Document attached.",
"picker_link_failed": "Could not attach the document.",
"picker_preview": "Preview",
"picker_attach": "Attach document",
"picker_close": "Close",
"picker_preview_unavailable": "Preview could not be displayed."
},
"journal_status": {
"status_draft": "Draft",
@@ -3567,10 +3582,59 @@
"ref_transfer_rollback": "Rollback",
"ref_manual": "Manual"
},
"self_billing": {
"title": "Register self-billing invoice",
"subtitle": "A self-billing invoice you received — the customer invoiced in your name. For you it is a sale with output VAT.",
"back": "Back",
"issuer_card_title": "Issuer and invoice reference",
"issuer_card_description": "The customer who issued the self-billing invoice, and the number they assigned.",
"customer_label": "Customer (issuer)",
"select_customer_placeholder": "Select customer",
"external_number_label": "Invoice number (customer's)",
"external_number_placeholder": "e.g. SF-2026-014",
"agreement_ref_label": "Agreement reference",
"agreement_ref_placeholder": "Self-billing agreement",
"items_card_title": "Lines",
"items_card_description": "The amounts from the received self-billing invoice.",
"description_label": "Description",
"description_placeholder": "Description",
"quantity_label": "Qty",
"unit_label": "Unit",
"unit_price_label": "Unit price",
"vat_label": "VAT",
"row_label": "Row {index}",
"add_row": "Add row",
"notes_card_title": "Notes",
"notes_placeholder": "Internal notes (optional)",
"details_card_title": "Details",
"currency_label": "Currency",
"invoice_date_label": "Invoice date",
"received_date_label": "Received date",
"due_date_label": "Due date",
"summary_card_title": "Summary",
"subtotal_label": "Net",
"output_vat_label": "Output VAT",
"total_label": "Total",
"register": "Register self-billing invoice",
"viewer_disabled_tooltip": "You only have viewer access in this company",
"load_customers_failed": "Could not load customers",
"created_title": "Self-billing invoice registered",
"created_description": "Self-billing invoice {number} has been booked as a sale.",
"create_failed_title": "Could not register the self-billing invoice",
"validation_customer_required": "Select a customer",
"validation_external_number_required": "Invoice number is required",
"validation_invoice_date_required": "Invoice date is required",
"validation_received_date_required": "Received date is required",
"validation_due_date_required": "Due date is required",
"validation_description_required": "Description is required",
"validation_quantity_min": "Quantity must be at least 0.01",
"validation_min_one_row": "At least one row is required"
},
"invoices": {
"title": "Invoices",
"recurring": "Recurring",
"new_invoice": "New invoice",
"new_self_billed": "Self-billing",
"viewer_disabled_tooltip": "You only have viewer access in this company",
"load_failed_title": "Could not load invoices",
"load_failed_description": "Check your connection and try again.",
@@ -3595,6 +3659,7 @@
"badge_credit": "Credit",
"badge_proforma": "Proforma",
"badge_delivery_note": "Delivery note",
"badge_self_billed": "Self-billing",
"status_draft": "Draft",
"status_sent": "Sent",
"status_paid": "Paid",
+72 -7
View File
@@ -793,8 +793,6 @@
"toast_invalid_fiscal_year": "Ogiltigt räkenskapsår",
"toast_error_title": "Fel",
"toast_create_failed": "Kunde inte skapa företag. Försök igen.",
"toast_company_exists_title": "Företaget finns redan",
"toast_company_exists_description": "Det här företaget finns redan i {appName}. Be en befintlig administratör att bjuda in dig.",
"toast_org_invalid_title": "Ogiltigt organisationsnummer",
"toast_org_invalid_description": "Kontrollera att du angett ett giltigt 10- eller 12-siffrigt organisationsnummer.",
"toast_welcome_title": "Välkommen!",
@@ -820,7 +818,7 @@
"step2_lookup_not_found": "Inget företag hittades med det organisationsnumret.",
"step2_lookup_failed": "Kunde inte hämta företagsuppgifter. Du kan fylla i manuellt.",
"step2_ceased_inline": "{companyName} — företaget är avregistrerat",
"step2_company_exists": "Det här företaget finns redan i {appName}. Be en befintlig administratör att bjuda in dig.",
"step2_company_exists_own": "Du har redan företaget {name} med detta organisationsnummer. Du kan ändå fortsätta.",
"step2_company_name_ab": "Företagsnamn *",
"step2_company_name_ef": "Verksamhetsnamn (Eller ditt namn vid EF) *",
"step2_company_name_placeholder_ab": "AB Företaget",
@@ -906,7 +904,6 @@
"section_your_companies": "Dina företag i {appName}",
"section_bankid_companies": "Företag kopplade till ditt BankID",
"already_in_app": "Finns redan i {appName}",
"ask_admin_invite": "Be en befintlig administratör att bjuda in dig.",
"setup_manually": "Sätts upp manuellt",
"no_companies_found": "Inga företag hittades. Lägg till ditt första företag nedan.",
"or_separator": "eller",
@@ -915,8 +912,6 @@
"toast_lookup_failed_description": "Fyll i resterande uppgifter manuellt.",
"toast_company_ceased_title": "Företaget är avregistrerat",
"toast_company_ceased_description": "Det går inte att sätta upp bokföring för ett avregistrerat företag.",
"toast_company_exists_title": "Företaget finns redan",
"toast_company_exists_description": "Be en befintlig administratör att bjuda in dig.",
"toast_org_invalid_title": "Ogiltigt organisationsnummer",
"toast_org_invalid_description": "Fortsätt med manuell uppsättning.",
"toast_create_failed_title": "Kunde inte skapa företag",
@@ -2101,6 +2096,8 @@
"vat_label_short": "Moms",
"total_label": "Totalt",
"review_and_create": "Granska & skapa",
"mode_invoice": "Faktura",
"mode_self_billed": "Självfaktura",
"viewer_disabled_tooltip": "Du har endast läsbehörighet i detta företag",
"review_dialog_title_invoice": "Granska faktura",
"review_dialog_title_proforma": "Granska proformafaktura",
@@ -2209,6 +2206,9 @@
"status_credited": "Krediterad",
"badge_proforma": "Proforma",
"badge_delivery_note": "Följesedel",
"badge_self_billed": "Självfaktura",
"external_number_label": "Fakturanummer (kundens)",
"agreement_ref_label": "Avtalsreferens",
"created_at": "Skapad {date}",
"sent_at_suffix": " • Skickad {date}",
"convert_to_invoice": "Konvertera till faktura",
@@ -2892,7 +2892,22 @@
"remove_blocked_cancel_cta": "Stäng",
"replace_uploading": "Ersätter...",
"remove_failed": "Kunde inte ta bort underlaget.",
"replace_failed": "Kunde inte ladda upp ny version."
"replace_failed": "Kunde inte ladda upp ny version.",
"choose_from_inbox": "Välj från inkorgen",
"picker_title": "Välj underlag från inkorgen",
"picker_description": "Underlag som kommit in via e-post eller uppladdning och ännu inte använts.",
"picker_search_placeholder": "Sök leverantör eller filnamn…",
"picker_results": "{count} st",
"picker_empty": "Inga oanvända underlag i inkorgen.",
"picker_empty_search": "Inga underlag matchar \"{query}\".",
"picker_source_email": "E-post",
"picker_source_upload": "Uppladdning",
"picker_linked": "Underlag kopplat.",
"picker_link_failed": "Kunde inte koppla underlaget.",
"picker_preview": "Förhandsgranska",
"picker_attach": "Koppla underlag",
"picker_close": "Stäng",
"picker_preview_unavailable": "Förhandsgranskning kunde inte visas."
},
"journal_status": {
"status_draft": "Utkast",
@@ -3567,10 +3582,59 @@
"ref_transfer_rollback": "Återställning",
"ref_manual": "Manuell"
},
"self_billing": {
"title": "Registrera självfaktura",
"subtitle": "En självfaktura du tagit emot — kunden har fakturerat i ditt namn. För dig är det en försäljning med utgående moms.",
"back": "Tillbaka",
"issuer_card_title": "Utställare och fakturareferens",
"issuer_card_description": "Kunden som ställt ut självfakturan, och fakturanumret de tilldelat.",
"customer_label": "Kund (utställare)",
"select_customer_placeholder": "Välj kund",
"external_number_label": "Fakturanummer (kundens)",
"external_number_placeholder": "t.ex. SF-2026-014",
"agreement_ref_label": "Avtalsreferens",
"agreement_ref_placeholder": "Självfaktureringsavtal",
"items_card_title": "Rader",
"items_card_description": "Beloppen från den mottagna självfakturan.",
"description_label": "Beskrivning",
"description_placeholder": "Beskrivning",
"quantity_label": "Antal",
"unit_label": "Enhet",
"unit_price_label": "À-pris",
"vat_label": "Moms",
"row_label": "Rad {index}",
"add_row": "Lägg till rad",
"notes_card_title": "Anteckningar",
"notes_placeholder": "Interna anteckningar (valfritt)",
"details_card_title": "Uppgifter",
"currency_label": "Valuta",
"invoice_date_label": "Fakturadatum",
"received_date_label": "Mottaget datum",
"due_date_label": "Förfallodatum",
"summary_card_title": "Sammanfattning",
"subtotal_label": "Netto",
"output_vat_label": "Utgående moms",
"total_label": "Totalt",
"register": "Registrera självfaktura",
"viewer_disabled_tooltip": "Du har endast läsbehörighet i detta företag",
"load_customers_failed": "Kunde inte ladda kunder",
"created_title": "Självfaktura registrerad",
"created_description": "Självfaktura {number} har bokförts som försäljning.",
"create_failed_title": "Kunde inte registrera självfakturan",
"validation_customer_required": "Välj en kund",
"validation_external_number_required": "Fakturanummer krävs",
"validation_invoice_date_required": "Fakturadatum krävs",
"validation_received_date_required": "Mottaget datum krävs",
"validation_due_date_required": "Förfallodatum krävs",
"validation_description_required": "Beskrivning krävs",
"validation_quantity_min": "Antal måste vara minst 0,01",
"validation_min_one_row": "Minst en rad krävs"
},
"invoices": {
"title": "Fakturor",
"recurring": "Återkommande",
"new_invoice": "Ny faktura",
"new_self_billed": "Självfaktura",
"viewer_disabled_tooltip": "Du har endast läsbehörighet i detta företag",
"load_failed_title": "Kunde inte ladda fakturor",
"load_failed_description": "Kontrollera din anslutning och försök igen.",
@@ -3595,6 +3659,7 @@
"badge_credit": "Kredit",
"badge_proforma": "Proforma",
"badge_delivery_note": "Följesedel",
"badge_self_billed": "Självfaktura",
"status_draft": "Utkast",
"status_sent": "Skickad",
"status_paid": "Betald",