feat(invoices): allow BAS class 1-3 posting-account overrides and complete the aktiekapital note (#1121)
- invoice/article posting-account overrides accept active class 1-3 accounts; class 1-2 (balance-sheet) accounts are rejected on VAT-bearing lines so the ruta 05 tax base always books to a 3xxx account - shared posting-account regex across server schemas, pending-operation re-validation, and client forms - share-capital settings (aktiekapital/antal_aktier) feed the annual-report note; kvotvarde derived per ABL 1 kap 6 $; all-or-nothing pair constraint - signed per-rate VAT breakdown on credit-note PDFs; U+2212 to ASCII hyphen Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b0044bfe98
commit
43f7ccab9e
@@ -18,6 +18,7 @@ import { createClient } from '@/lib/supabase/client'
|
||||
import AccountCombobox from '@/components/bookkeeping/AccountCombobox'
|
||||
import { AddAccountDialog } from '@/components/bookkeeping/AddAccountDialog'
|
||||
import type { BASAccount, CreateArticleInput } from '@/types'
|
||||
import { INVOICE_POSTING_ACCOUNT_REGEX } from '@/lib/invoices/posting-account'
|
||||
|
||||
// A row from the currencies reference table (lib migration
|
||||
// 20260630110000_currencies_reference_table.sql).
|
||||
@@ -48,11 +49,11 @@ export default function ArticleForm({
|
||||
const { company } = useCompany()
|
||||
const supabase = createClient()
|
||||
const t = useTranslations('form_article')
|
||||
// Active class-3 (revenue) accounts for the combobox. The combobox accepts
|
||||
// Active class 1-3 posting accounts for the combobox. The combobox accepts
|
||||
// unknown 4-digit numbers optimistically: the API answers with
|
||||
// ACCOUNTS_NOT_IN_CHART for activatable BAS accounts, and the host page's
|
||||
// ActivateAccountsDialog flow takes over (same UX as the journal entry form).
|
||||
const [revenueAccounts, setRevenueAccounts] = useState<BASAccount[]>([])
|
||||
const [postingAccounts, setPostingAccounts] = useState<BASAccount[]>([])
|
||||
// Inline account creation: what the user typed in the combobox when they hit
|
||||
// "Skapa konto": non-null opens AddAccountDialog prefilled with it.
|
||||
const [createAccountPrefill, setCreateAccountPrefill] = useState<string | null>(null)
|
||||
@@ -66,9 +67,11 @@ export default function ArticleForm({
|
||||
|
||||
async function fetchRevenueAccounts() {
|
||||
try {
|
||||
const res = await fetch('/api/bookkeeping/accounts?class=3')
|
||||
const res = await fetch('/api/bookkeeping/accounts')
|
||||
const body = await res.json()
|
||||
setRevenueAccounts((body?.data as BASAccount[]) || [])
|
||||
const accounts = ((body?.data as BASAccount[]) || [])
|
||||
.filter((account) => account.account_class >= 1 && account.account_class <= 3)
|
||||
setPostingAccounts(accounts)
|
||||
} catch {
|
||||
// Non-fatal: the combobox degrades to free 4-digit entry.
|
||||
}
|
||||
@@ -141,7 +144,11 @@ export default function ArticleForm({
|
||||
// ISO 4217 alpha-3; the authoritative allow-list is the currencies
|
||||
// table (the DB FK rejects unknown codes).
|
||||
currency: z.string().regex(/^[A-Z]{3}$/),
|
||||
revenue_account: z.string().optional(),
|
||||
revenue_account: z
|
||||
.string()
|
||||
.regex(INVOICE_POSTING_ACCOUNT_REGEX, t('posting_account_invalid'))
|
||||
.or(z.literal(''))
|
||||
.optional(),
|
||||
cost_price: z.number().nonnegative().optional(),
|
||||
ean: z.string().optional(),
|
||||
housework_type: z.string().optional(),
|
||||
@@ -345,12 +352,15 @@ export default function ArticleForm({
|
||||
render={({ field }) => (
|
||||
<AccountCombobox
|
||||
value={field.value || ''}
|
||||
accounts={revenueAccounts}
|
||||
accounts={postingAccounts}
|
||||
onChange={field.onChange}
|
||||
onCreateAccount={(prefill) => setCreateAccountPrefill(prefill)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.revenue_account && (
|
||||
<p className="text-sm text-destructive">{errors.revenue_account.message}</p>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">{t('revenue_account_hint')}</p>
|
||||
</div>
|
||||
|
||||
@@ -476,7 +486,7 @@ export default function ArticleForm({
|
||||
|
||||
{/* Inline custom-account creation (renders in a portal, outside the form).
|
||||
After create: refresh the chart and select the new number as the
|
||||
article's revenue account: mirrors the journal entry form. */}
|
||||
article's posting account: mirrors the journal entry form. */}
|
||||
<AddAccountDialog
|
||||
open={createAccountPrefill != null}
|
||||
onOpenChange={(next) => {
|
||||
|
||||
Reference in New Issue
Block a user