diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b424d59c..70382047 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,4 +1,13 @@ version: 2 +# Deliberately throttled to avoid a PR flood. Two knobs do the work: +# - open-pull-requests-limit: 1 -> at most ONE open PR per ecosystem at a +# time. Dependabot will not open next week's PR until the current one is +# merged or closed, so PRs can never pile up. +# - groups (patterns: "*") -> every available bump (major/minor/patch) +# is batched into that single PR instead of one PR per package. +# Combined with the weekly schedule this means: normally one npm PR a week (or +# none), and only in a rare week where Docker/Actions also move do you see more +# than one PR at all. updates: # Base images in the root Dockerfile (node:22-alpine). - package-ecosystem: docker @@ -6,7 +15,11 @@ updates: schedule: interval: weekly day: monday - open-pull-requests-limit: 5 + open-pull-requests-limit: 1 + groups: + docker: + patterns: + - "*" labels: - dependencies - docker @@ -17,7 +30,11 @@ updates: schedule: interval: weekly day: monday - open-pull-requests-limit: 5 + open-pull-requests-limit: 1 + groups: + docker-cron: + patterns: + - "*" labels: - dependencies - docker @@ -28,7 +45,11 @@ updates: schedule: interval: weekly day: monday - open-pull-requests-limit: 5 + open-pull-requests-limit: 1 + groups: + github-actions: + patterns: + - "*" labels: - dependencies - ci @@ -39,16 +60,15 @@ updates: schedule: interval: weekly day: monday - open-pull-requests-limit: 10 + open-pull-requests-limit: 1 labels: - dependencies - npm groups: - # Batch low-risk minor/patch bumps so the reviewer queue stays small. - minor-and-patch: - update-types: - - minor - - patch + # Batch ALL bumps (major/minor/patch) into a single weekly PR. + npm: + patterns: + - "*" ignore: # @anthropic-ai/bedrock-sdk is PINNED to an exact version in package.json. # 0.32.0 arrived inside a grouped minor-and-patch bump (#884) and broke diff --git a/app/api/bookkeeping/accounts/__tests__/accounts.test.ts b/app/api/bookkeeping/accounts/__tests__/accounts.test.ts index 9e8117fc..80255d72 100644 --- a/app/api/bookkeeping/accounts/__tests__/accounts.test.ts +++ b/app/api/bookkeeping/accounts/__tests__/accounts.test.ts @@ -131,6 +131,29 @@ describe('POST /api/bookkeeping/accounts', () => { expect(status).toBe(409) expect(body.error).toContain('5010') }) + + it('forwards default_vat_rate into the insert', async () => { + const { supabase, calls } = createCapturingSupabase([ + { data: { account_number: '3740', default_vat_rate: 0 } }, + ]) + auth(supabase) + const req = createMockRequest('/api/bookkeeping/accounts', { + method: 'POST', + body: { + account_number: '3740', + account_name: 'Öres- och kronutjämning', + account_type: 'revenue', + normal_balance: 'debit', + default_vat_rate: 0, + }, + }) + const { status } = await parseJsonResponse(await createPOST(req, routeParams)) + expect(status).toBe(200) + const insertArg = calls.find((c) => c.method === 'insert')?.args[0] as { + default_vat_rate?: number | null + } + expect(insertArg?.default_vat_rate).toBe(0) + }) }) describe('DELETE /api/bookkeeping/accounts/[number]', () => { @@ -218,6 +241,25 @@ describe('PUT /api/bookkeeping/accounts/[number]', () => { expect(status).toBe(200) expect(body.data.account_name).toBe('Nytt namn') }) + + it('forwards default_vat_rate into the update', async () => { + const { supabase, calls } = createCapturingSupabase([ + { data: { account_number: '3740', default_vat_rate: 0 } }, + ]) + auth(supabase) + const req = createMockRequest('/api/bookkeeping/accounts/3740', { + method: 'PUT', + body: { default_vat_rate: 0 }, + }) + const { status } = await parseJsonResponse( + await PUT(req, { params: Promise.resolve({ number: '3740' }) }) + ) + expect(status).toBe(200) + const updateArg = calls.find((c) => c.method === 'update')?.args[0] as { + default_vat_rate?: number | null + } + expect(updateArg?.default_vat_rate).toBe(0) + }) }) describe('POST /api/bookkeeping/accounts/activate', () => { diff --git a/app/api/bookkeeping/accounts/route.ts b/app/api/bookkeeping/accounts/route.ts index a50ca43c..d51671bb 100644 --- a/app/api/bookkeeping/accounts/route.ts +++ b/app/api/bookkeeping/accounts/route.ts @@ -79,6 +79,7 @@ export const POST = withRouteContext( is_system_account: false, description: body.description || null, default_vat_code: body.default_vat_code || null, + default_vat_rate: body.default_vat_rate ?? null, sru_code: body.sru_code || null, sort_order: parseInt(body.account_number), }) diff --git a/components/bookkeeping/AddAccountDialog.tsx b/components/bookkeeping/AddAccountDialog.tsx index d40c89ff..ff42e280 100644 --- a/components/bookkeeping/AddAccountDialog.tsx +++ b/components/bookkeeping/AddAccountDialog.tsx @@ -37,7 +37,9 @@ export function AddAccountDialog({ const [accountNumber, setAccountNumber] = useState('') const [accountName, setAccountName] = useState('') const [description, setDescription] = useState('') - const [defaultVatCode, setDefaultVatCode] = useState('') + // "Standard moms": the moms-sats a booking line defaults to when this konto is + // picked. 'none' = no default. SelectItem values are stringified decimals. + const [defaultVatRate, setDefaultVatRate] = useState('none') const [sruCode, setSruCode] = useState('') const [normalBalance, setNormalBalance] = useState<'debit' | 'credit'>('debit') const [isSaving, setIsSaving] = useState(false) @@ -84,7 +86,7 @@ export function AddAccountDialog({ account_type: derived?.account_type || 'expense', normal_balance: normalBalance, description: description || null, - default_vat_code: defaultVatCode || null, + default_vat_rate: defaultVatRate === 'none' ? null : parseFloat(defaultVatRate), sru_code: sruCode || null, }), }) @@ -100,7 +102,7 @@ export function AddAccountDialog({ setAccountNumber('') setAccountName('') setDescription('') - setDefaultVatCode('') + setDefaultVatRate('none') setSruCode('') onCreated(createdAccount) onOpenChange(false) @@ -197,12 +199,19 @@ export function AddAccountDialog({
- - setDefaultVatCode(e.target.value)} - placeholder="T.ex. MP1" - /> + +
diff --git a/components/bookkeeping/EditAccountDialog.tsx b/components/bookkeeping/EditAccountDialog.tsx index 47121c3f..d1b38de2 100644 --- a/components/bookkeeping/EditAccountDialog.tsx +++ b/components/bookkeeping/EditAccountDialog.tsx @@ -59,7 +59,12 @@ export function EditAccountDialog({ open, onOpenChange, account, onSaved }: Edit const { toast } = useToast() const [accountName, setAccountName] = useState(account.account_name) const [description, setDescription] = useState(account.description || '') - const [defaultVatCode, setDefaultVatCode] = useState(account.default_vat_code || '') + // "Standard moms": the moms-sats a booking line defaults to when this konto is + // picked (currently the leverantörsfaktura-rad). 'none' = no default. Stored + // as a decimal fraction; SelectItem values are the stringified decimals. + const [defaultVatRate, setDefaultVatRate] = useState( + account.default_vat_rate != null ? String(account.default_vat_rate) : 'none', + ) const [sruCode, setSruCode] = useState(account.sru_code || '') const [isActive, setIsActive] = useState(account.is_active) const [isSaving, setIsSaving] = useState(false) @@ -250,7 +255,7 @@ export function EditAccountDialog({ open, onOpenChange, account, onSaved }: Edit body: JSON.stringify({ account_name: accountName, description: description || null, - default_vat_code: defaultVatCode || null, + default_vat_rate: defaultVatRate === 'none' ? null : parseFloat(defaultVatRate), sru_code: sruCode || null, is_active: isActive, }), @@ -310,12 +315,19 @@ export function EditAccountDialog({ open, onOpenChange, account, onSaved }: Edit
- - setDefaultVatCode(e.target.value)} - placeholder="T.ex. MP1" - /> + +
diff --git a/components/settings/InvoiceSettingsForm.tsx b/components/settings/InvoiceSettingsForm.tsx index 412e68ff..01b1bc1f 100644 --- a/components/settings/InvoiceSettingsForm.tsx +++ b/components/settings/InvoiceSettingsForm.tsx @@ -50,6 +50,22 @@ export function InvoiceSettingsForm({ settings }: InvoiceSettingsFormProps) {
+
+ +
+ +
+

+ {t('arrival_start_help')} +

+
+