Bug/momsdek overflow (#465)
* feat(settings): add option for company name position in invoice PDF * feat(migrations): add backfill for VAT account labels to correct bad seed data * feat(migrations): add backfill for VAT account labels to correct bad seed data * fix(ui): improve accessibility for company name position toggle in PDF settings * fix(bookkeeping): align BAS 2026 reference data with official PDF Reconciled lib/bookkeeping/bas-data/ against the BAS 2026 v1.1 official chart (1286 accounts). All real discrepancies fixed: - 2089 Fond för utvecklingsutgifter: k2_excluded → true - 8417 Räntekostnader för dold räntekompensation: k2_excluded → true - 1250, 1260 renamed to "(Fritt konto för Inventarier, verktyg och installationer)" — BAS 2026 freed these slots - Periodiseringsfond 2120-2139: added year suffixes (2120 = "...2020" etc.) and added 8 missing accounts (2121-2127, 2129) for years 2019, 2021-2027. Dropped phantom 2022/2024 prior-parser garbage. - 4075-4078: EUland → EU-land - 8411: förlagsoch → förlags- och Verified: 1282 of 1286 PDF accounts match exactly after edits (remaining 4 are PDF-parser artifacts, not real data). Build clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(migrations): backfill BAS 2026 account labels in chart_of_accounts Companion to the TS reference fix. Updates existing companies' rows where they still carry seed-data typos or generic names that don't match BAS 2026: - 4075-4078: EUland → EU-land (hyphenation) - 8411: förlagsoch → förlags- och (hyphenation) - 2120, 2130-2137, 2139: rename "Periodiseringsfond" (generic, no year) to the BAS 2026 canonical name with year suffix Defensive: every WHERE clause matches an EXACT current value. Rows that have been manually renamed by users — including those with a wrong year that may reference legacy fonds from an earlier BAS numbering cycle — are left untouched. No row is deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(migrations): fix wrong-year labels on Periodiseringsfond accounts Follow-up to 20260513140000. The first backfill only renamed accounts whose name was the generic "Periodiseringsfond" (no year). Many customers were seeded from an older BAS numbering cycle where 2126 = "2016", 2127 = "2017", etc. — BAS 2026 reuses those account numbers for years 2026/2027. This migration aligns the year tag with the BAS 2026 meaning of each account number across 2120-2127, 2129, 2130-2137, 2139. Only rows whose name still starts with "Periodiseringsfond" are touched — customers who renamed the account to something custom keep their name. Verified on staging: all 18 accounts now carry BAS 2026 canonical names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(data): update account names and descriptions for clarity and consistency * fix(migrations): backfill account names for BAS 2026 freed accounts and refine Periodiseringsfond name matching * feat: enhance VAT handling with reverse charge logic and supplier type support * feat: implement VAT declaration validation rules and enhance moms box mapping --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
9e0b89ee7e
commit
da39eb2d43
@@ -2,6 +2,7 @@ import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
import {
|
||||
generateInputVatLine,
|
||||
generateReverseChargeLines,
|
||||
generateReverseChargeBasisLines,
|
||||
} from './vat-entries'
|
||||
import { findMatchingTemplates, buildMappingResultFromTemplate } from './booking-templates'
|
||||
import {
|
||||
@@ -221,8 +222,13 @@ function buildResult(rule: MappingRule, transaction: Transaction, entityType?: E
|
||||
const vatLines: VatJournalLine[] = []
|
||||
if (isExpense && !rule.default_private && rule.vat_treatment) {
|
||||
if (rule.vat_treatment === 'reverse_charge') {
|
||||
// EU reverse charge: fiktiv moms (offsetting entries)
|
||||
const rcLines = generateReverseChargeLines(absAmount)
|
||||
// Reverse charge: emit BOTH the fiktiv-moms pair (2645/2614) AND the
|
||||
// basbelopp pair (44xx|45xx / 4598). The basbelopp pair populates
|
||||
// momsdeklaration rutor 20–24; without it Skatteverket rejects with
|
||||
// FK004. Mapping rules don't carry supplier-country today, so we
|
||||
// default to EU services — the most common reverse-charge scenario.
|
||||
const rcRate = 0.25
|
||||
const rcLines = generateReverseChargeLines(absAmount, rcRate, false)
|
||||
for (const rcl of rcLines) {
|
||||
vatLines.push({
|
||||
account_number: rcl.account_number,
|
||||
@@ -231,6 +237,19 @@ function buildResult(rule: MappingRule, transaction: Transaction, entityType?: E
|
||||
description: rcl.line_description || '',
|
||||
})
|
||||
}
|
||||
|
||||
// Skip basbelopp emission if the rule already books to a basis account.
|
||||
if (!/^4[45]\d{2}$/.test(debitAccount)) {
|
||||
const basisLines = generateReverseChargeBasisLines(absAmount, rcRate, 'eu_business')
|
||||
for (const bl of basisLines) {
|
||||
vatLines.push({
|
||||
account_number: bl.account_number,
|
||||
debit_amount: bl.debit_amount,
|
||||
credit_amount: bl.credit_amount,
|
||||
description: bl.line_description || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (rule.vat_treatment === 'standard_25' || rule.vat_treatment === 'reduced_12' || rule.vat_treatment === 'reduced_6') {
|
||||
const vatRate =
|
||||
rule.vat_treatment === 'standard_25' ? 0.25
|
||||
|
||||
Reference in New Issue
Block a user