Bug/customer invoice bug (#628)
* fix(supplier-invoices): self-assess reverse-charge VAT + link payments to vouchers Reverse-charge supplier invoices now carry a per-item reverse_charge_rate (0.06/0.12/0.25). Under omvänd skattskyldighet the supplier charges 0% VAT, so the line vat_rate stays 0 and the buyer self-assesses fiktiv moms at the statutory rate. Centralizes rate resolution (resolveReverseChargeRate) and the ruta 20-24 basis-account guard (isReverseChargeBasisAccount) in vat-entries so the booking engine and review-dialog preview can no longer drift. Adds the link_supplier_invoice_voucher pending operation: mark a leverantorsfaktura paid by linking an existing posted verifikat that debits 2440, with no new journal entry. Exposes find-candidates/link MCP tools and the bulk-reconcile helper, scoped under suppliers:read/write. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(vat): report yearly VAT over the rakenskapsar, not the calendar year Annual VAT (helarsmoms) is filed per beskattningsar/rakenskapsar (SFL 26 kap), which can be extended or shortened up to 18 months. The previous Jan-Dec calendar span silently dropped part of an extended first year. calculateVatDeclaration now accepts a fiscalPeriodId and resolves the period's actual bounds for yearly; monthly/quarterly stay calendar. The reports UI passes the selected fiscal period, defaults the periodicity from the company's moms_period setting, and carries the period into the ruta drill-down. full-archive export threads the period id through too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(migration): resolve supplier invoice status from payment amounts The provider's lifecycle status and its payment status are computed independently upstream and can contradict each other (e.g. a Fortnox invoice marked booked but fully paid). Both the arcim entity-mapper and the Fortnox mapper now let payment state win: fully paid -> paid, partial -> partially_paid, otherwise the mapped lifecycle status, with credit notes forced terminal. Balance is compared numerically (never strict === 0) so float drift or a residual ore resolves cleanly, and an absent Balance is treated as unpaid. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(enable-banking): only ingest booked transactions to stop re-import drift Pending entries are skipped during sync: a pending row is unstable across syncs (a later 'synka nu' returns it still pending or finally booked, often with a different effective date). Because both the dedup external_id and the content-dedup key are date-derived, that drift minted a new id and re-imported a transaction that already existed - observed in production as the same amount+description landing twice with different dates. Gating the import set on a stable booking_date removes the drift at the source and leaves booked rows' ids byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(gitignore): ignore local SIE test fixtures tests/fixtures/sie/ may contain real or scrubbed company data and must never be committed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(invoice): handle errors during registration journal entry creation and ensure invoice rollback feat(tests): add test for reverse charge rate handling on supplier invoice line items feat(fortnox): ensure paid status reflects zero balance for fully paid invoices chore(migrations): add reverse_charge_rate to supplier_invoice_items and backfill link_supplier_invoice_voucher --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
358c25094d
commit
f6ee0c2a82
+61
@@ -0,0 +1,61 @@
|
||||
-- Backfill `link_supplier_invoice_voucher` into the
|
||||
-- pending_operations.operation_type CHECK constraint.
|
||||
--
|
||||
-- The supplier-side mirror of `link_invoice_voucher` (added in
|
||||
-- 20260528120001). The new MCP tool gnubok_link_supplier_invoice_to_voucher
|
||||
-- stages a `link_supplier_invoice_voucher` pending operation, which is then
|
||||
-- committed by commitLinkSupplierInvoiceVoucher via the
|
||||
-- link_supplier_invoice_to_voucher RPC (20260529130000 / 20260529140000).
|
||||
-- The op also has a risk-tier entry ('medium', reversible — no journal entry
|
||||
-- is created or modified).
|
||||
--
|
||||
-- Without this migration any INSERT staged by the new tool would be rejected
|
||||
-- with a constraint violation, silently blocking the supplier_invoice_payments
|
||||
-- audit-trail row required by BFL 5 kap 6–7§ (every affärshändelse must have a
|
||||
-- verifikation with a logged payment match).
|
||||
|
||||
ALTER TABLE public.pending_operations
|
||||
DROP CONSTRAINT IF EXISTS pending_operations_operation_type_check;
|
||||
|
||||
ALTER TABLE public.pending_operations
|
||||
ADD CONSTRAINT pending_operations_operation_type_check
|
||||
CHECK (operation_type IN (
|
||||
'categorize_transaction',
|
||||
'create_customer',
|
||||
'create_invoice',
|
||||
'mark_invoice_paid',
|
||||
'send_invoice',
|
||||
'mark_invoice_sent',
|
||||
'match_transaction_invoice',
|
||||
'close_period',
|
||||
'lock_period',
|
||||
'unlock_period',
|
||||
'set_opening_balances',
|
||||
'run_year_end',
|
||||
'run_currency_revaluation',
|
||||
'import_sie',
|
||||
'explain_voucher_gap',
|
||||
'uncategorize_transaction',
|
||||
'approve_supplier_invoice',
|
||||
'credit_supplier_invoice',
|
||||
'credit_invoice',
|
||||
'convert_invoice',
|
||||
'create_transaction',
|
||||
'attach_document_to_transaction',
|
||||
'create_voucher',
|
||||
'correct_entry',
|
||||
'reverse_entry',
|
||||
'create_supplier',
|
||||
'create_supplier_invoice_from_inbox',
|
||||
'post_annual_depreciation',
|
||||
'link_invoice_voucher',
|
||||
'undo_sie_import',
|
||||
'match_batch_allocate',
|
||||
'bulk_book_transactions',
|
||||
'create_salary_run',
|
||||
'generate_agi',
|
||||
'link_transaction_journal_entry',
|
||||
'link_supplier_invoice_voucher' -- supplier-side mirror of link_invoice_voucher
|
||||
));
|
||||
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Add reverse_charge_rate to supplier_invoice_items.
|
||||
--
|
||||
-- For omvänd skattskyldighet (reverse charge, ML 16 kap) the EU/non-EU or
|
||||
-- domestic-RC supplier charges no VAT, so the line's own vat_rate is 0 — the
|
||||
-- v1 supplier-invoice API even mandates vat_rate=0 on every RC line. The buyer
|
||||
-- must nonetheless self-assess BOTH output and input VAT at the Swedish
|
||||
-- statutory rate that would apply to the service domestically: 25% under
|
||||
-- huvudregeln for EU services (ML 6 kap 34 §), or 12%/6% for reduced-rated
|
||||
-- services. That self-assessed rate is conceptually distinct from "what the
|
||||
-- supplier charged" (0%), so it gets its own column instead of overloading
|
||||
-- vat_rate — which previously caused the engine to skip the fiktiv-moms lines
|
||||
-- (2614/2624/2634 + 2645/2647) and basbeloppsrader (44xx/45xx) entirely,
|
||||
-- understating momsdeklaration ruta 20-24 / 30-32 / 48.
|
||||
--
|
||||
-- NULL = not a reverse-charge line (the booking engine then falls back to a
|
||||
-- positive vat_rate if present, else the 25% huvudregel default).
|
||||
-- 0.06 / 0.12 / 0.25 = explicit self-assessed rate (set by the UI picker).
|
||||
|
||||
ALTER TABLE supplier_invoice_items
|
||||
ADD COLUMN IF NOT EXISTS reverse_charge_rate numeric
|
||||
CHECK (reverse_charge_rate IS NULL OR reverse_charge_rate IN (0.06, 0.12, 0.25));
|
||||
|
||||
COMMENT ON COLUMN supplier_invoice_items.reverse_charge_rate IS
|
||||
'Self-assessed VAT rate for omvänd skattskyldighet (decimal 0.06/0.12/0.25). NULL for non-RC lines. The line vat_rate stays 0 (supplier charges no VAT); this rate drives fiktiv moms (2614/2624/2634 + 2645/2647) and basbelopp (44xx/45xx) booking — see lib/bookkeeping/supplier-invoice-entries.ts.';
|
||||
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
Reference in New Issue
Block a user