feat(onboarding): minimal input: orgnr + moms period is the whole ask (#1952)
* feat(onboarding): minimal input: orgnr + moms period is the whole ask
Two fixes from the third E2E attempt (2026-08-26):
1. accounting_method is now optional in CompanySetupSchema and defaults by
form in planCompanySetup: aktiebolag = accrual (the norm), enskild
firma = cash (the common small-EF choice; legal under 3 MSEK, BFL 4
kap 4 paragraf). The plan flags the default (resolved.accountingMethodDefaulted)
and gnubok_create_company's preview carries accounting_method_defaulted
so the readback names it and the user overrides in the same 'ja'.
Never silent: the preview is the checkpoint. Applies to the MCP tool
and POST /api/v1/companies (additive; response shows the resolved
value). The lookup tool's still_to_ask no longer lists it.
2. The agent refused a real orgnr because the user said 'nytt bolag' and
the registry showed an established company ('Stopp. Numret matchar
inte ett nytt bolag'): lookup instructions now state that an
established company with F-skatt/VAT is the NORMAL case (new = new to
Accounted) and the orgnr is never second-guessed for looking
established.
Skill + plugin (v1.2.1) updated; API skill regenerated; DECISIONS.md entry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(onboarding): surface the kontantmetod 3-MSEK condition on the defaulted cash method
Compliance-review finding on #1952: the EF cash default carries a legal
eligibility condition (turnover normally under 3 MSEK, BFL 4 kap 4 §)
that a client not reading the onboarding skill would never see. The
create preview now carries accounting_method_note with the condition
whenever cash was defaulted, and the v1 pitfall states it for API
integrators. The registry cannot verify turnover, so the confirm-time
human check is the gate; the default itself stays (a brand-new EF has
zero turnover by definition).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
Jakob Wennberg
parent
4ac7b45c8a
commit
4af7469523
@@ -55,6 +55,25 @@ describe('gnubok_create_company', () => {
|
||||
expect(tool.annotations.destructiveHint).toBe(false)
|
||||
})
|
||||
|
||||
it('defaults an omitted accounting_method by form and flags it in the preview', async () => {
|
||||
const supabase = supabaseWithTeam(TEAM_ID)
|
||||
const { accounting_method: _dropped, ...withoutMethod } = setup
|
||||
const result = (await tool.execute(withoutMethod, '', 'user-1', supabase as never)) as Record<string, unknown>
|
||||
|
||||
expect(result.created).toBe(false)
|
||||
const preview = result.preview as Record<string, unknown>
|
||||
expect(preview.accounting_method).toBe('accrual')
|
||||
expect(preview.accounting_method_defaulted).toBe(true)
|
||||
})
|
||||
|
||||
it('does not flag an explicitly chosen accounting_method as defaulted', async () => {
|
||||
const supabase = supabaseWithTeam(TEAM_ID)
|
||||
const result = (await tool.execute(setup, '', 'user-1', supabase as never)) as Record<string, unknown>
|
||||
const preview = result.preview as Record<string, unknown>
|
||||
expect(preview.accounting_method).toBe('accrual')
|
||||
expect('accounting_method_defaulted' in preview).toBe(false)
|
||||
})
|
||||
|
||||
it('previews without creating when confirm is not true', async () => {
|
||||
const supabase = supabaseWithTeam(TEAM_ID)
|
||||
const result = (await tool.execute(setup, '', 'user-1', supabase as never)) as Record<string, unknown>
|
||||
|
||||
@@ -83,7 +83,9 @@ describe('gnubok_lookup_company', () => {
|
||||
|
||||
const ask = result.still_to_ask as string[]
|
||||
expect(ask.some((q) => q.startsWith('moms_period ('))).toBe(true)
|
||||
expect(ask.some((q) => q.startsWith('accounting_method'))).toBe(true)
|
||||
// accounting_method defaults by form in create_company (flagged in its
|
||||
// preview) and is deliberately not a question here.
|
||||
expect(ask.some((q) => q.startsWith('accounting_method'))).toBe(false)
|
||||
// Registry facts are confirmed, never re-asked.
|
||||
expect(ask.some((q) => q.startsWith('entity_type'))).toBe(false)
|
||||
expect(ask.some((q) => q.startsWith('vat_registered'))).toBe(false)
|
||||
|
||||
@@ -3126,7 +3126,9 @@ export const tools: McpTool[] = [
|
||||
if (!vatIsFact) stillToAsk.push('vat_registered (the registry shows no VAT registration; confirm with the user)')
|
||||
if (vatIsFact) stillToAsk.push('moms_period (monthly, quarterly or yearly; never guess)')
|
||||
else stillToAsk.push('moms_period IF vat_registered turns out true')
|
||||
stillToAsk.push('accounting_method (accrual = faktureringsmetoden, cash = kontantmetoden; never guess)')
|
||||
// accounting_method is deliberately NOT in this list: gnubok_create_company
|
||||
// defaults it by form (AB accrual, EF cash) and flags the default in the
|
||||
// preview, where the user confirms or overrides it in the same "ja".
|
||||
|
||||
const startMonth = parseStartMonthDay(lookup.fiscalYear?.startMonthDay)
|
||||
// No closed fiscal period in the registry = no annual report filed yet
|
||||
@@ -3182,7 +3184,7 @@ export const tools: McpTool[] = [
|
||||
still_to_ask: stillToAsk,
|
||||
warnings,
|
||||
instructions:
|
||||
'Present the company facts as a short summary for the user to CONFIRM (name, address, F-skatt, VAT status; do not re-ask them). Then ask ONLY the still_to_ask questions, merge the answers into suggested_create_company_input, and call gnubok_create_company (preview first, read it back, then confirm=true).',
|
||||
'Present the company facts as a short summary for the user to CONFIRM (name, address, F-skatt, VAT status; do not re-ask them). An established company with F-skatt/VAT is the NORMAL case even when the user says "nytt bolag" (new = new to Accounted): never refuse or second-guess the orgnr because the company looks established. Ask ONLY the still_to_ask questions, merge answers into suggested_create_company_input, and call gnubok_create_company (preview, read back incl. the defaulted accounting method, then confirm=true).',
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -3191,7 +3193,7 @@ export const tools: McpTool[] = [
|
||||
name: 'gnubok_create_company',
|
||||
title: 'Create Company',
|
||||
description:
|
||||
'Create a NEW company for the connected user, set up for bookkeeping (chart, settings, first fiscal period, tax deadlines; 30-day trial). Call gnubok_lookup_company FIRST to prefill facts from the orgnr. Preview (no confirm), read it back, then confirm=true. Skill: onboarding.',
|
||||
'Create a NEW company, set up for bookkeeping (chart, settings, first fiscal period, tax deadlines; 30-day trial). Ask ONLY orgnr + moms period: gnubok_lookup_company prefills the rest, accounting_method defaults by form. Preview (no confirm), read back, then confirm=true.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
@@ -3201,7 +3203,7 @@ export const tools: McpTool[] = [
|
||||
org_number: { type: 'string', description: '10 digits; required when VAT-registered' },
|
||||
vat_registered: { type: 'boolean' },
|
||||
moms_period: { type: 'string', enum: ['monthly', 'quarterly', 'yearly'], description: 'Required when vat_registered' },
|
||||
accounting_method: { type: 'string', enum: ['accrual', 'cash'] },
|
||||
accounting_method: { type: 'string', enum: ['accrual', 'cash'], description: 'Omit to default by form: aktiebolag accrual, enskild firma cash; the preview flags the default' },
|
||||
f_skatt: { type: 'boolean' },
|
||||
fiscal_year_start_month: { type: 'integer', minimum: 1, maximum: 12 },
|
||||
first_fiscal_year: {
|
||||
@@ -3217,7 +3219,7 @@ export const tools: McpTool[] = [
|
||||
team_id: { type: 'string', format: 'uuid' },
|
||||
confirm: { type: 'boolean', description: 'true creates; omitted = preview' },
|
||||
},
|
||||
required: ['name', 'entity_type', 'vat_registered', 'accounting_method', 'f_skatt'],
|
||||
required: ['name', 'entity_type', 'vat_registered', 'f_skatt'],
|
||||
},
|
||||
outputSchema: {
|
||||
type: 'object',
|
||||
@@ -3258,7 +3260,18 @@ export const tools: McpTool[] = [
|
||||
vat_registered: parsed.data.vat_registered,
|
||||
vat_number: (plan.input.settings.vat_number as string | null) ?? null,
|
||||
moms_period: parsed.data.vat_registered ? parsed.data.moms_period ?? null : null,
|
||||
accounting_method: parsed.data.accounting_method,
|
||||
accounting_method: plan.resolved.accountingMethod,
|
||||
// Present, never silent: the readback must name the defaulted method
|
||||
// so the user can override it before confirm. The cash default also
|
||||
// carries its eligibility condition (BFL 4 kap 4 §): the registry
|
||||
// cannot verify turnover, so the human must.
|
||||
...(plan.resolved.accountingMethodDefaulted ? { accounting_method_defaulted: true } : {}),
|
||||
...(plan.resolved.accountingMethodDefaulted && plan.resolved.accountingMethod === 'cash'
|
||||
? {
|
||||
accounting_method_note:
|
||||
'Kontantmetoden förutsätter en omsättning som normalt understiger 3 MSEK (BFL 4 kap 4 §); annars gäller faktureringsmetoden. Bekräfta detta med användaren.',
|
||||
}
|
||||
: {}),
|
||||
f_skatt: parsed.data.f_skatt,
|
||||
fiscal_period: plan.fiscalPeriod,
|
||||
team_id: teamId,
|
||||
@@ -3270,7 +3283,7 @@ export const tools: McpTool[] = [
|
||||
requires_confirmation: true,
|
||||
preview,
|
||||
message:
|
||||
'Nothing was created. Read the preview back to the user (especially the fiscal period dates and the VAT setup), then call gnubok_create_company again with the same arguments and confirm=true.',
|
||||
'Nothing was created. Read the preview back to the user (especially the fiscal period dates, the VAT setup, and the accounting method when accounting_method_defaulted is true: name the default and let them override), then call gnubok_create_company again with the same arguments and confirm=true.',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,14 @@ Rules baked into that split (same as the web onboarding):
|
||||
- **F-skatt** from the registry is a fact, both true and false.
|
||||
- **VAT** is a fact ONLY when positively registered. "No VAT registration
|
||||
found" is a question, never an assumption (ML 17 kap 24 §).
|
||||
- **Moms period** and **accounting method** are ALWAYS the user's answer.
|
||||
Rules of thumb if unsure: under 1 MSEK turnover may report VAT yearly,
|
||||
under 40 MSEK quarterly, above monthly; cash method only under 3 MSEK.
|
||||
- **Moms period** is ALWAYS the user's answer when VAT-registered. Rules of
|
||||
thumb if unsure: under 1 MSEK turnover may report VAT yearly, under 40
|
||||
MSEK quarterly, above monthly.
|
||||
- **Accounting method is NOT a question**: it defaults by form (AB =
|
||||
faktureringsmetoden, enskild firma = kontantmetoden) and the preview
|
||||
flags the default. Name it in the readback ("faktureringsmetoden,
|
||||
standard för AB, säg till om du vill ha kontantmetoden") so the user can
|
||||
override in the same "ja". Cash requires turnover under 3 MSEK.
|
||||
- **Enskild firma name**: verksamhetsnamnet is freely choosable; suggest the
|
||||
registered name but let the user pick. An AB's registered name is a fact.
|
||||
- **Fiscal year**: registry data becomes a confirm question, never an open
|
||||
|
||||
Reference in New Issue
Block a user