* feat(onboarding): company setup from the conversation and POST /api/v1/companies Third PR of agent-first onboarding (#1814). Once connected, the agent can now set up a company end to end without the web wizard, and partner platforms can provision companies over REST. - create_company_for_user: service-role-only SECURITY DEFINER twin of create_company_with_owner taking the owner explicitly (service clients have no auth.uid()). pg-real test covers creation, role gating, unknown owner and foreign team. - lib/company/create-company.ts: the wizard's creation sequence (org number, TIC snapshot, BAS chart, settings, first fiscal period, tax deadlines, rollback) extracted into createCompanyCore; the Server Action delegates to it, behaviour unchanged. - lib/company/onboarding-input.ts: one Zod schema + planner for the agent/API paths; a VAT-registered company without moms_period is refused (a missing period silently yields zero VAT deadlines). - MCP: gnubok_create_company (two-phase: preview, then confirm=true; companies:write, company-independent), gnubok_connect_bank and gnubok_connect_skatteverket (status + the browser link, gated on bank_sync / skatteverket, search-only in the catalog), the "onboarding" skill, and initialize instructions pointing at it. - Consent page pre-ticks companies:write for an account with no company yet, so the setup does not dead-end on insufficient scope after signup. - POST /api/v1/companies (companies:write, dry-run aware) on the same core; scope map, registry, spec snapshot and the generated API skill updated. - tools/list payload ceiling raised 59.95K -> 60.4K for the one new default-catalog tool (documented in the guard). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 * fix(onboarding): explicit f_skatt, org number when VAT-registered, EF first year ends 31 Dec Review findings on #1864 (Swedish compliance review): - f_skatt is required, never defaulted to approved (SE-R-005 risk). - org_number is required when vat_registered: the invoice momsregistreringsnummer derives from it (ML 17 kap 24 §). - An enskild firma's first fiscal year must end on 31 December and its start month is forced to 1 even with first_fiscal_year set, mirroring the wizard's own rule text (BFL 3 kap. 1 §). - POST /api/v1/companies no longer claims Idempotency-Key support (the wrapper only honours it on company-scoped routes). - pg-real: createCompanyCore's chart seed runs under the real service_role, which the unit tests could not prove. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 * test(pg): starter chart has 41 accounts, assert non-empty The service_role chart-seed proof passed the part that mattered (no 42501 from seed_chart_of_accounts) and failed on a wrong row-count guess: the seeded chart is a curated starter set, not the full BAS list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 * fix(migrations): move create_company_for_user to 20260825120000 main gained 20260824170000_bulk_book_transactions_service_actor.sql with the same version while this branch was open; two files on one version abort every Supabase branch apply and the prod auto-apply. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 * chore(api): refresh spec snapshot and generated skill after rebasing onto main Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 * fix(mcp): flat create_company result, refuse localhost connect links, test hygiene CodeRabbit on #1864: the confirmed-create result was wrapped in the { data, next } envelope while its outputSchema promised top-level fields; it now returns the fields with next as a sibling. The two connect-link tools refuse to build a link when NEXT_PUBLIC_APP_URL is unset instead of handing a remote user a localhost URL. Tests clear mocks and the event bus in beforeEach. Not changed: the rollback already survives user_preferences.active_company_id (that FK is ON DELETE SET NULL since 20260331010000), and v1 error details stay in the surface's English developer convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
126 lines
6.1 KiB
TypeScript
126 lines
6.1 KiB
TypeScript
import type { Skill } from './types'
|
|
|
|
const body = `# Onboarding: set up a company in Accounted from the conversation
|
|
|
|
From "my company is not in Accounted yet" to a working ledger without the
|
|
user opening the web app first. The only browser steps are the ones that
|
|
legally need a human with BankID: connecting (creating the account),
|
|
approving the bank consent, and authorising Skatteverket. Everything else
|
|
happens here.
|
|
|
|
## When to use
|
|
|
|
- "Sätt upp bokföring för mitt AB / min enskilda firma"
|
|
- "Jag har precis startat bolag, hur kommer jag igång?"
|
|
- "Lägg till ett nytt bolag" (an existing user adding a second company)
|
|
- A byrå/consultant onboarding a new client company (pass \`team_id\`)
|
|
|
|
## Step 0: connect
|
|
|
|
If this session is not connected yet, the first company-scoped call (for
|
|
example \`gnubok_create_company\`) returns an authentication challenge that the
|
|
client shows as a Connect prompt. Tell the user: "Klicka på Connect; har du
|
|
inget konto skapar du det där (BankID eller e-post), det tar en minut." The
|
|
call is retried automatically once connected. Do not send the user to the web
|
|
app to sign up first.
|
|
|
|
## Step 1: gather the facts (ask, never assume)
|
|
|
|
Collect these before creating anything. The order mirrors the in-app wizard.
|
|
|
|
1. **Organisationsnummer** (10 digits). Required when the company is
|
|
VAT-registered (the momsregistreringsnummer on every invoice derives from
|
|
it) and strongly recommended otherwise: it drives Skatteverket/SIE exports. An enskild firma's
|
|
org number is the owner's personnummer; that is fine to store here.
|
|
2. **Company form**: \`aktiebolag\` or \`enskild_firma\`. Only these two are
|
|
supported today; HB/KB/förening are not.
|
|
3. **Company name** as registered.
|
|
4. **F-skatt**: godkänd för F-skatt? Always ask; the tool refuses to assume it.
|
|
A brand-new company may still be waiting for Skatteverket's approval (then
|
|
false).
|
|
5. **Fiscal year**: for enskild firma always the calendar year (do not ask);
|
|
its first year may be shorter or up to 18 months but always ends 31 December.
|
|
For an AB ask whether it is the calendar year or another 12-month period
|
|
(\`fiscal_year_start_month\`). For a company in its FIRST year ask for the
|
|
exact first fiscal year start and end (BFL 3 kap.: it may be shorter than
|
|
12 months or up to 18 months) and pass \`first_fiscal_year\`.
|
|
6. **VAT**: momsregistrerad? If yes, which period: \`monthly\`, \`quarterly\` or
|
|
\`yearly\`. This is required when VAT-registered: without it Accounted
|
|
generates no VAT deadlines at all, silently. If the user does not know,
|
|
the rule of thumb: turnover under 1 MSEK may report yearly, under 40 MSEK
|
|
quarterly, above that monthly; Skatteverket's registration decision states
|
|
the actual period. Never guess it into the tool; ask.
|
|
7. **Accounting method**: \`accrual\` (faktureringsmetoden) or \`cash\`
|
|
(kontantmetoden / bokslutsmetoden). Cash is only allowed under 3 MSEK
|
|
turnover and is common for small enskild firma; AB with invoices usually
|
|
run accrual.
|
|
|
|
## Step 2: preview, confirm, create
|
|
|
|
Call \`gnubok_create_company\` WITHOUT \`confirm\` first. Read the preview back
|
|
to the user in plain Swedish: company form, org number, the fiscal period
|
|
dates, VAT setup (registered + period), method. Only after an explicit "ja"
|
|
call it again with the same arguments and \`confirm: true\`.
|
|
|
|
What creation does in one step: company + owner membership, BAS chart of
|
|
accounts for the company form, settings, the first fiscal period, and the
|
|
automatic tax deadlines (moms, F-skatt, AGI, inkomstdeklaration). The 30-day
|
|
trial with bank sync, Skatteverket, AI and e-mail starts immediately. This
|
|
connection uses the new company automatically from the next call; no
|
|
re-authentication.
|
|
|
|
## Step 3: connect the bank
|
|
|
|
Call \`gnubok_connect_bank\`. It reports existing connections and returns a
|
|
\`connect_url\`. The user opens it in a browser where they are logged in to
|
|
Accounted, picks the bank and approves with BankID (PSD2 consent, up to 180
|
|
days). Transactions start syncing within a minute. If they prefer not to
|
|
connect a bank, they can import bank statements as files in the web app
|
|
instead; do not block on this step.
|
|
|
|
## Step 4: connect Skatteverket (optional but recommended)
|
|
|
|
Call \`gnubok_connect_skatteverket\`. Same pattern: the user opens the
|
|
\`connect_url\`, identifies with BankID as firmatecknare at Skatteverket, and
|
|
lands back in Accounted. This enables skattekonto sync and filing of
|
|
momsdeklaration and arbetsgivardeklaration from here. Filing is never
|
|
mandatory through Accounted: every declaration can be downloaded and filed
|
|
manually.
|
|
|
|
## Step 5: first bookkeeping
|
|
|
|
Once transactions arrive: \`gnubok_list_uncategorized_transactions\` and the
|
|
categorize flow (\`gnubok_suggest_categories\`, \`gnubok_categorize_transaction\`,
|
|
approval). For a company with history in another system, offer the SIE
|
|
import (\`gnubok_import_sie\` in the search catalog) before categorizing.
|
|
|
|
## Tools
|
|
|
|
- \`gnubok_create_company\`: preview (no confirm) then create (confirm=true)
|
|
- \`gnubok_list_companies\`: see which companies this connection can reach
|
|
- \`gnubok_connect_bank\`: status + connect link for PSD2 bank consent
|
|
- \`gnubok_connect_skatteverket\`: status + connect link for Skatteverket
|
|
- \`gnubok_get_agent_briefing\`: the company's settings and state once created
|
|
- \`gnubok_list_uncategorized_transactions\`: the first real bookkeeping step
|
|
|
|
## Pitfalls
|
|
|
|
- A VAT-registered company without a moms period is refused on purpose; do
|
|
not work around it by claiming the company is not VAT-registered.
|
|
- Do not create a company twice on a retry: check \`gnubok_list_companies\`
|
|
if a create call was interrupted.
|
|
- Bookkeeping duty starts when the company exists in Accounted with a fiscal
|
|
period. Never create a company "to try things out" for a real
|
|
organisation; use the sandbox in the web app for demos.
|
|
`
|
|
|
|
export const onboardingSkill: Skill = {
|
|
slug: 'onboarding',
|
|
name: 'Onboarding: New Company Setup',
|
|
summary:
|
|
'Set up a company from the conversation: gather facts, preview and create with gnubok_create_company, then hand out the bank and Skatteverket connect links.',
|
|
tags: ['onboarding', 'setup', 'company', 'bank', 'skatteverket', 'agent-first'],
|
|
body,
|
|
tier: 'workflow',
|
|
}
|