fix: address user feedback — RC preview, bank sync lookback, CSV import robustness (#233)
Three confirmed issues from user feedback: 1. Reverse charge preview now uses per-item VAT rates and correct accounts (2645/2647, 2614/2624/2634) instead of hardcoded 25%/2614 2. Bank sync uses 90-day lookback on first sync (when last_synced_at is null) instead of hardcoded 7 days for all syncs 3. Bank file import improvements: - Shared date normalizer supporting DD.MM.YYYY, DD/MM/YYYY, YYYYMMDD - Silent row skips now reported with reason in issues[] - Decimal separator mismatch detection in generic CSV - Swedish error message with format diagnostics on detection failure - Date format selector in column mapping UI Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
2fcebffb34
commit
9753f18533
@@ -44,6 +44,7 @@ export default function BankFileColumnMappingStep({
|
||||
const [balanceCol, setBalanceCol] = useState<number>(-1)
|
||||
const [delimiter, setDelimiter] = useState<string>(',')
|
||||
const [decimalSep, setDecimalSep] = useState<',' | '.'>(',')
|
||||
const [dateFormat, setDateFormat] = useState<string>('YYYY-MM-DD')
|
||||
|
||||
const isValid = dateCol >= 0 && descCol >= 0 && amountCol >= 0
|
||||
|
||||
@@ -58,7 +59,7 @@ export default function BankFileColumnMappingStep({
|
||||
delimiter,
|
||||
decimal_separator: decimalSep,
|
||||
skip_rows: 1, // Skip header
|
||||
date_format: 'YYYY-MM-DD',
|
||||
date_format: dateFormat,
|
||||
}
|
||||
onConfirm(mapping)
|
||||
}
|
||||
@@ -78,8 +79,8 @@ export default function BankFileColumnMappingStep({
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* Delimiter and decimal settings */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{/* Delimiter, decimal, and date format settings */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Avgränsare</Label>
|
||||
<Select value={delimiter} onValueChange={(v) => { if (v) setDelimiter(v) }}>
|
||||
@@ -105,6 +106,20 @@ export default function BankFileColumnMappingStep({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Datumformat</Label>
|
||||
<Select value={dateFormat} onValueChange={(v) => { if (v) setDateFormat(v) }}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="YYYY-MM-DD">YYYY-MM-DD</SelectItem>
|
||||
<SelectItem value="DD.MM.YYYY">DD.MM.YYYY</SelectItem>
|
||||
<SelectItem value="DD/MM/YYYY">DD/MM/YYYY</SelectItem>
|
||||
<SelectItem value="YYYYMMDD">YYYYMMDD</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Required column mappings */}
|
||||
|
||||
@@ -40,12 +40,19 @@ interface JournalPreviewLine {
|
||||
credit: number
|
||||
}
|
||||
|
||||
function getOutputVatAccount(rate: number): string {
|
||||
if (rate === 0.12) return '2624'
|
||||
if (rate === 0.06) return '2634'
|
||||
return '2614'
|
||||
}
|
||||
|
||||
function buildJournalPreview(
|
||||
items: ReviewLineItem[],
|
||||
subtotal: number,
|
||||
totalVat: number,
|
||||
total: number,
|
||||
reverseCharge: boolean,
|
||||
supplierType?: string,
|
||||
): JournalPreviewLine[] {
|
||||
const lines: JournalPreviewLine[] = []
|
||||
|
||||
@@ -67,21 +74,35 @@ function buildJournalPreview(
|
||||
}
|
||||
|
||||
if (reverseCharge) {
|
||||
// EU reverse charge: fiktiv moms
|
||||
const vatRate = 0.25
|
||||
const fiktivVat = Math.round(subtotal * vatRate * 100) / 100
|
||||
lines.push({
|
||||
account_number: '2645',
|
||||
description: 'Beräknad ingående moms',
|
||||
debit: fiktivVat,
|
||||
credit: 0,
|
||||
})
|
||||
lines.push({
|
||||
account_number: '2614',
|
||||
description: 'Utgående moms omvänd',
|
||||
debit: 0,
|
||||
credit: fiktivVat,
|
||||
})
|
||||
// Reverse charge: fiktiv moms per VAT rate (matches engine groupVatByRate logic)
|
||||
const isDomesticRC = supplierType === 'swedish_business'
|
||||
const inputAccount = isDomesticRC ? '2647' : '2645'
|
||||
|
||||
const vatByRate = new Map<number, number>()
|
||||
for (const item of items) {
|
||||
if (item.vat_rate > 0) {
|
||||
const current = vatByRate.get(item.vat_rate) || 0
|
||||
vatByRate.set(item.vat_rate, current + Math.round(item.amount * 100) / 100)
|
||||
}
|
||||
}
|
||||
|
||||
for (const [rate, netAmount] of vatByRate) {
|
||||
const fiktivVat = Math.round(netAmount * rate * 100) / 100
|
||||
const outputAccount = getOutputVatAccount(rate)
|
||||
lines.push({
|
||||
account_number: inputAccount,
|
||||
description: inputAccount,
|
||||
debit: fiktivVat,
|
||||
credit: 0,
|
||||
})
|
||||
lines.push({
|
||||
account_number: outputAccount,
|
||||
description: outputAccount,
|
||||
debit: 0,
|
||||
credit: fiktivVat,
|
||||
})
|
||||
}
|
||||
|
||||
// Credit: 2440 at subtotal (no real VAT for reverse charge)
|
||||
lines.push({
|
||||
account_number: '2440',
|
||||
@@ -113,8 +134,11 @@ function buildJournalPreview(
|
||||
const ACCOUNT_LABELS: Record<string, string> = {
|
||||
'2440': 'Leverantörsskulder',
|
||||
'2641': 'Ingående moms',
|
||||
'2645': 'Beräknad ingående moms',
|
||||
'2614': 'Utg. moms omvänd skattskyldighet',
|
||||
'2645': 'Beräknad ing. moms förvärv utlandet',
|
||||
'2647': 'Beräknad ing. moms omvänd i Sverige',
|
||||
'2614': 'Utg. moms omvänd 25%',
|
||||
'2624': 'Utg. moms omvänd 12%',
|
||||
'2634': 'Utg. moms omvänd 6%',
|
||||
}
|
||||
|
||||
export function SupplierInvoiceReviewContent({
|
||||
@@ -132,7 +156,7 @@ export function SupplierInvoiceReviewContent({
|
||||
totalVat,
|
||||
total,
|
||||
}: SupplierInvoiceReviewContentProps) {
|
||||
const journalLines = buildJournalPreview(items, subtotal, totalVat, total, reverseCharge)
|
||||
const journalLines = buildJournalPreview(items, subtotal, totalVat, total, reverseCharge, supplier.supplier_type)
|
||||
const totalDebit = journalLines.reduce((sum, l) => sum + l.debit, 0)
|
||||
const totalCredit = journalLines.reduce((sum, l) => sum + l.credit, 0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user