Fix/build (#1041)
* fix(bookkeeping): harden correction account changes * feat(tax): enhance tax deadline generation with new settings and filing methods - Added new company settings: tax_turnover_over_40m, vat_has_eu_trade, vat_filing_method, periodisk_sammanstallning_enabled, and periodisk_sammanstallning_filing_method. - Updated deadline generation logic to accommodate new settings affecting VAT and employer declaration deadlines. - Implemented tests for new functionality, ensuring that completed obligations are preserved and not replaced by new pending rows. - Introduced a cron job to backfill missing tax deadlines for companies with settings but no upcoming deadlines. - Updated API routes for generating tax deadlines and handling cron jobs. - Modified database schema to include new columns for tax filing profiles and constraints for filing methods. * fix(invoices): record credit note reconciliation guard * fix(tax): correct automatic deadline settings * fix(tax): key AGI deadline to VAT taxable base and add storforetag payment deadline The 26th filing day for the skattedeklaration (AGI and VAT together) hinges on one statutory measure, a VAT taxable base above SEK 40 million (SFL 26 kap.), not a separate employer turnover. Drop employer_turnover_over_40m and derive the AGI schedule from vat_registered plus vat_taxable_base_over_40m, so a non-VAT-reporting employer is never shown the 26th when its binding date is the 12th. Also: - add a skatteinbetalning deadline row (12th, 17 January) for storforetag, whose deducted tax and employer contributions are due before the 26th filing date - normalize legally incoherent over-40m flag combinations to the earlier small-company schedule in a follow-up migration - replace hardcoded 27 December dates with the banking-day adjustment - extend the 40m help text to cover the SKV-decided early filing election and the payment-still-on-the-12th rule - document the regeneration race repaired by the daily backfill cron Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(migrations): add AGI and VAT filing logic with employer column removal * feat(settings): implement VAT registration logic and update related flags; enhance deadline handling --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1443235cec
commit
a5e37d3510
@@ -164,9 +164,29 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-07-15] Superseded the hard deletion part of the 2026-07-14 credit note draft decision: numbered credit note drafts are now retained as cancelled rows and reopened on retry so the KR series remains complete.
|
||||
[2026-07-15] Customer personnummer uses application field encryption with masked API and UI output rather than a database-only cipher: the existing key custody and AES-256-GCM implementation can protect values before they reach Postgres, while ordinary reads never expose the full identifier.
|
||||
[2026-07-15] Credit note creation uses a completion marker plus unique company guards instead of a large creation RPC: incomplete parents are never returned, concurrent requests converge, and all journal writes remain in the bookkeeping engine.
|
||||
[2026-07-15] Correction account changes refresh a line description only when it is blank or matches the previous company or BAS account name: all company accounts, including inactive ones, provide provenance while custom voucher text stays intact.
|
||||
[2026-07-16] Supabase MCP is project-scoped to the linked erp-base project and read-only by default: the repository treats local credentials as production access, and both Supabase guidance and repository rules require minimizing production mutation risk.
|
||||
[2026-07-16] Tax deadline recovery runs daily and EU sales-list deadlines require explicit opt-in: missing deadline rows must self-heal, while VAT registration alone does not prove an EU reporting obligation.
|
||||
[2026-07-16] Split the SEK 40 million deadline threshold into VAT taxable base and employer turnover settings: Skatteverket applies different measures to VAT and AGI, so one shared answer can produce a wrong statutory date.
|
||||
[2026-07-16] Tax deadline recovery compares every expected type and period key with stored upcoming obligations: the presence of one deadline no longer hides a partial generation failure for the same company.
|
||||
[2026-07-16] Retained the legacy tax_turnover_over_40m column after backfilling the split deadline thresholds: dropping it in the same migration could break an older application instance during a rolling deploy or rollback; new code does not read or write it.
|
||||
[2026-07-16] Superseded the same-day threshold split: the 26th filing day for both VAT and AGI hinges on one statutory measure, the VAT taxable base over SEK 40 million (SFL 26 kap., confirmed against Skatteverket's guidance), so employer_turnover_over_40m is dropped and AGI follows vat_registered plus vat_taxable_base_over_40m. A separate employer flag let a non-VAT-reporting employer be shown the 26th when its binding date is the 12th.
|
||||
[2026-07-16] The 20260716120353 threshold-split migration stays untouched and a follow-up migration drops the employer column: staging already recorded that version, so editing the file would desynchronize environments instead of converging them.
|
||||
[2026-07-16] The over-40m normalization clears the VAT flag for legally incoherent stored combinations (not VAT-registered or not monthly) instead of guessing the intended schedule: every ambiguity resolves to the earlier small-company dates, which can never cause a late filing.
|
||||
[2026-07-16] Storföretag get a dedicated skatteinbetalning deadline row (12th, 17 January): their skattedeklaration is filed the 26th but deducted tax and employer contributions must be on skattekontot by the 12th, and showing only the filing date hides the earlier payment obligation.
|
||||
[2026-07-16] Deadline dates rely on the banking-day adjuster instead of hardcoding Skatteverket's 27 December: annandag jul adjustment produces the published date, and one mechanism keeps VAT and AGI December handling identical.
|
||||
[2026-07-16] Closed #1000 (v1 match-supplier-invoice FX + cash-method settlement account): threaded the already-resolved paymentAccount into createSupplierInvoicePaymentEntry (was gated on isPureSek) and createSupplierInvoiceCashEntry (was passed undefined), and widened the findUnresolvableAccounts chart pre-validation from the pure-SEK accrual path to every !customLines branch, since every non-customLines branch now consumes the resolved account. The dashboard route needed no code change (its FX/cash branches were already threaded inside PR #985 itself); added branch-level regression tests on both routes instead. The entry generators keep their internal 1930 default: it is the documented no-link fallback, reached via resolveSettlementAccount returning 1930 for transactions without a cash_account_id.
|
||||
|
||||
[2026-07-16] Two bugs from one customer report (an AB). Bug 1 (acct 2893 showed 2393's "langfristig del" memo after an andringsverifikation): root cause = CorrectionEntryDialog never re-derived line_description on account change (JournalEntryForm does). Fixed forward via a pure helper (correction-line-description.ts) that refreshes the memo only when it is empty or still equals the prev account's name (preserves hand-typed memos). Chose NO prod data repair: the wrong memo sits on a POSTED verifikat (immutable per migration-017 trigger); it is cosmetic (account number + amounts correct, all reports key off the number); ~26 posted lines across 11 cos share this stale-echo pattern, all fix-forward only. Deferred the twin entry-level header fix (#1031). Bug 2 (auto tax-deadlines never appeared): root cause = generation only fired on a settings save where a TAX field CHANGED value (didTaxFieldsChange); settings are filled once at onboarding so re-saving generated nothing -> only 5/776 real cos had system deadlines. Chose count-based self-heal (regenerate when the company has 0 system deadlines) over always-regenerate, because generateTaxDeadlinesForUser deletes+reinserts and would reset is_completed/status on every unrelated save. Also wired the /deadlines empty-state to the existing (dead) /api/tax-deadlines/generate route, and fixed a 1000-row PostgREST cap in the annual cron. Backfilled 771 real cos with zero system deadlines via scripts/backfill-tax-deadlines.ts. Deferred moms_period=yearly config (#1030, 295 filers, largest VAT cohort): helarsmoms deadline (SFL 26 kap. 33-33b) depends on EU-trade status (no flag in CompanySettingsForDeadlines) and, for AB, the income-tax-return date.
|
||||
[2026-07-15] Repaired the single legacy paid credit note blocking invoices_credit_note_not_paid validation by normalizing its invoice metadata to sent, clearing payment fields, setting zero payable remainder, and linking its existing balanced posted V44 reversal: the immutable voucher already exactly reversed V42 and was not edited or duplicated.
|
||||
[2026-07-14] Issue #1016 (create_transactions never binds cash_account_id): fixed forward-only via an optional ledger_account hint on gnubok_create_transactions + commitCreateTransaction, resolved through a new ensureManualCashAccount find-or-create helper (lib/cash-accounts/service.ts). No migration: cash_accounts.bank_connection_id is already nullable and source='manual' already exists (the seeded 1930 is a manual row), so the handoff's premise that manual kassakonton need a schema change was wrong. Pre-creating a manual row does NOT race upsertFromPsd2 (the ingest.ts "never auto-create" worry): a later PSD2 connection promotes a manual holder in place, the intended flow (#916/#56). Hint restricted to ^19\d{2}$ (BAS kassa/bank group) so a transaction can't bind to a non-cash account. Scoped to the MCP create path per user decision; POST /api/cash-accounts + settings UI, relaxing ingest.ts's settlement_account auto-create, and historical backfill of cash_account_id=null rows (deferred to #1001) are follow-ups.
|
||||
[2026-07-14] Wise (TransferWise) CSV import added as a bank-file format plugin (lib/import/bank-file/formats/wise.ts), flowing through the existing upload->preview->confirm->execute wizard. Decisions: (1) parser preserves NATIVE currency per row (multi-currency statement); SEK conversion is left to the downstream FX/booking pipeline (Riksbanken), not done in the parser, so nothing here converts. Booking in the account's own currency via the Wise API rate is a deferred/extended feature. (2) Direction IN/OUT drives the sign; the row is booked on the side that moved (target for IN, source for OUT). (3) Non-zero Wise fees become their OWN negative "Wise avgift" transaction row (source and target fee both handled) rather than being folded or dropped, so the balance ties out and the fee lands in the inbox to categorize (e.g. 6570). (4) Only COMPLETED rows import; pending/cancelled/refunded skipped. (5) external_id keys on the stable Wise ID (TRANSFER-.../PLAN_ORDER-...) carried in raw_line, with a -fee/-tgtfee suffix for fee rows, so re-importing dedups exactly instead of via row hash (generateExternalId gains a 'wise' branch mirroring camt053). Scoped to the format plugin per user decision; per-currency auto-routing to manual kassakonton (would pair with #1016) not done here.
|
||||
[2026-07-16] Merge of the always-regenerate-on-tax-save branch with main's count-based self-heal keeps both triggers: a save containing tax-relevant fields regenerates (the generator now preserves completed rows, so main's is_completed-clobbering objection no longer applies), and a save without them backfills only when the company has zero system deadlines. The daily repair identity gains the due date (type:period:due_date): main's backfill created rows for 771 companies under the superseded schedules, and a type-and-period key alone would never flag their wrong dates for repair.
|
||||
[2026-07-17] Renamed migration 20260716150000_agi_follows_vat_taxable_base.sql to 20260717070000: main merged an unrelated migration under the same version (20260716150000_company_settings_defer_invoice_booking, PR #1040) and the preview branch aborted on schema_migrations_pkey; the branch's version was never recorded remotely, so a rename converges instead of orphaning.
|
||||
[2026-07-17] Rejected the PR-review claim that quarterly momsdeklaration is due the 26th of the month after the quarter: per SFL 26 kap. 26 § and Skatteverket's schedule, quarterly filers (only possible at or below SEK 40M) file by the 12th of the second month after quarter end (17 August for Q2); the 26th applies exclusively to over-40M monthly filers (26 kap. 30 §). The removed 26th dates were the wrong ones; the new 12 May / 17 Aug / 12 Nov / 12 Feb dates stand.
|
||||
[2026-07-17] Confirmed 26 February for enskild firma annual VAT with EU trade (26 kap. 33-33a §§ SFL, Skatteverket helårsmoms schedule); without EU trade the return follows the income declaration date (12 May). Documented in a code comment instead of changing dates.
|
||||
[2026-07-17] Storföretag skatteinbetalning uses the 17th only in January: the 17 August exception belongs to the below-40M schedule; above 40M the payment date is the 12th every month except January. Documented in deadline-config rather than adding an August exception.
|
||||
[2026-07-17] Deadline repair identity split: incomplete rows match on type:period:due_date (stale dates must be repaired) but completed rows match on type:period alone, since the generator never replaces a filed obligation and a date-keyed completed row would re-flag the company on every cron run without converging. The generator's completed-row lookup floor also moved from today to a year before the earliest generated year so a filed obligation with a passed superseded date is not resurrected as pending.
|
||||
[2026-07-17] Settings PUT keeps update(body) without a pick() allow-list: UpdateSettingsSchema is the write boundary (z.object strips unknown keys; company_id/onboarding_complete are not in the schema, org_number is deleted post-onboarding), so the mass-assignment finding does not apply.
|
||||
[2026-07-17] Turning vat_registered off (or vat_has_eu_trade off) now coerces the dependent flags (vat_taxable_base_over_40m, vat_has_eu_trade, periodisk_sammanstallning_enabled) to false server-side instead of 400-ing on the stale stored combination; explicitly enabling PS without registration or EU trade still 400s. PS period/filing-method preferences are deliberately preserved while PS is disabled (they are inert until re-enabled).
|
||||
[2026-07-17] Kept the cron's cross-tenant company_settings/deadlines scans on the service client: a daily all-company repair job is inherently cross-tenant, is cron-secret-gated, and per-company scoping would turn one paginated query into N queries; the per-company writes remain scoped by company_id inside the generator.
|
||||
|
||||
Reference in New Issue
Block a user