fix(invoices): reminder emails use the per-currency payment account, never the SEK IBAN (#1806)
* fix(invoices): reminder emails use the per-currency payment account, never the SEK IBAN Invoice PDF and invoice email resolve payment details by invoice currency (invoice_payment_accounts, #1116), but the overdue-reminder templates still read the legacy company_settings fields, so a EUR reminder printed the SEK account's IBAN. A customer has paid to the wrong account from one. Both reminder generators now go through companyWithInvoicePaymentAccount, and sendReminder applies the same gate as invoice send: no usable account for the invoice currency means the reminder is skipped with INVOICE_PAYMENT_ACCOUNT_MISSING:<currency> in the result, instead of going out with no (or the wrong) payment details. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(invoices): run the reminder payment-account gate before the fee entry and reminder row Skeptic refutation (3/3): the gate lived in sendReminder, after processOverdueReminders had already posted the 60 kr påminnelseavgift verifikat and inserted the invoice_reminders row, so a skipped EUR reminder booked a fee for an email never sent and burned the level for good. The gate now runs in the loop before any write; sendReminder keeps it only as a backstop for direct callers. New processor test asserts no fee entry, no row and no email for the skipped case, and all three once an account exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bf3104dd21
commit
1f6dd778e5
@@ -42,7 +42,9 @@ import {
|
||||
processOverdueReminders,
|
||||
determineReminderLevel,
|
||||
calculateDaysOverdue,
|
||||
sendReminder,
|
||||
} from '../reminder-processor'
|
||||
import { makeCompanySettings, makeCustomer, makeInvoice } from '@/tests/helpers'
|
||||
import { getReminderDaysConfig } from '@/lib/email/reminder-templates'
|
||||
|
||||
describe('determineReminderLevel', () => {
|
||||
@@ -173,3 +175,32 @@ describe('processOverdueReminders: credit-note filter', () => {
|
||||
expect(inStatus?.args[1]).toContain('overdue')
|
||||
})
|
||||
})
|
||||
|
||||
describe('sendReminder payment-account gate', () => {
|
||||
const customer = makeCustomer({ email: 'kund@example.se' })
|
||||
const surcharges = { interestAmount: 0, interestRate: 0.1, interestDays: 0, reminderFee: 60 }
|
||||
|
||||
it('skips a EUR reminder when the company has no EUR payment account (no SEK fallback)', async () => {
|
||||
const sekOnly = makeCompanySettings({ bankgiro: '123-4567', iban: 'SE4550000000058398257466' })
|
||||
const invoice = { ...makeInvoice({ currency: 'EUR', total: 500 }), customer }
|
||||
const result = await sendReminder(invoice, sekOnly, 1, 'tok', surcharges)
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.error).toBe('INVOICE_PAYMENT_ACCOUNT_MISSING:EUR')
|
||||
})
|
||||
|
||||
it('sends a SEK reminder on legacy SEK details', async () => {
|
||||
const sekOnly = makeCompanySettings({ bankgiro: '123-4567' })
|
||||
const invoice = { ...makeInvoice({ currency: 'SEK', total: 500 }), customer }
|
||||
const result = await sendReminder(invoice, sekOnly, 1, 'tok', surcharges)
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
it('sends a EUR reminder once a EUR account is configured', async () => {
|
||||
const company = makeCompanySettings({
|
||||
invoice_payment_accounts: { EUR: { iban: 'DE89370400440532013000', bic: 'DEUTDEFF' } } as never,
|
||||
})
|
||||
const invoice = { ...makeInvoice({ currency: 'EUR', total: 500 }), customer }
|
||||
const result = await sendReminder(invoice, company, 1, 'tok', surcharges)
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user