Files
accounted/lib/onboarding/checklist.ts
T
Jakob Wennberg 44cff5e5e4 feat(onboarding): activation quick wins on the Hem checklist (#1461)
* feat(onboarding): activation quick wins on the Hem checklist

First implementation slice of the approved activation concept
(artifact de543d57, dev_docs/onboarding_activation_analysis.md §9):

- New "Kvitton och underlag" checklist step, gated on the invoice-inbox
  extension like the Skatteverket step; done once the company has ever
  received an inbox item (email/WhatsApp/upload). Non-AI companies route
  to billing, matching the assistant step.
- Personalized VAT line in the Skatteverket step: the company's real next
  momsdeklaration due date from the deadlines table, with an explicit
  "välj momsperiod" prompt when vat_registered is set but moms_period is
  null (that state silently generates zero VAT deadlines).
- Truthful Att göra empty state: while the setup checklist is open and no
  journal entry is posted, the all-clear reads "Bokföringen är tom än"
  instead of a false "Allt klart!".
- Activation funnel events (onboarding_setup_step_started / _completed /
  _dismissed) via posthog-js, mirroring the existing guarded capture
  pattern; sandbox never renders the block so no extra gate is needed.

Pure helpers live in lib/onboarding/checklist.ts with tests; step
numbering now adapts to both optional extensions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: record the receipts-signal and moms-period-guard decisions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(onboarding): review triage: error-safe emptyLedger, stale-state guard, copy

- A failed posted-entries count no longer reads as an empty ledger.
- Confirming a suggested match books an entry, so the empty-ledger copy
  retires for the rest of the session (postedSinceLoad).
- 'Bokföringen är tom än så länge' reads naturally.

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>
2026-08-08 15:14:34 +02:00

50 lines
1.9 KiB
TypeScript

import type { MomsPeriod } from '@/types'
/**
* What the Skatteverket checklist step should say about VAT deadlines.
*
* - 'date': the company's next momsdeklaration due date is known; show it.
* - 'missing_period': the company is VAT-registered but moms_period is unset,
* which makes the deadline engine silently generate ZERO VAT deadlines
* (lib/tax/deadline-config.ts conditions all require a concrete period).
* An empty deadlines query in that state means misconfiguration, not
* "no VAT duty", so the UI must prompt for the period instead of showing
* nothing.
* - null: not VAT-registered (no line), or VAT-registered with a period set
* but no upcoming row surfaced (transient or horizon gap; say nothing
* rather than guessing).
*/
export type VatDeadlineLine =
| { kind: 'date'; dueDate: string }
| { kind: 'missing_period' }
| null
export function vatDeadlineLine(input: {
vatRegistered: boolean | null | undefined
momsPeriod: MomsPeriod | null | undefined
nextVatDueDate: string | null | undefined
}): VatDeadlineLine {
if (!input.vatRegistered) return null
if (!input.momsPeriod) return { kind: 'missing_period' }
if (!input.nextVatDueDate) return null
return { kind: 'date', dueDate: input.nextVatDueDate }
}
/**
* Display ordinals for the setup checklist steps. Books and bank are always
* present; Skatteverket and the receipts/inbox step render only when their
* extensions are enabled; the assistant step is always last. `count` drives
* the "{count} steg så är bokföringen igång" title.
*/
export function checklistNumbers(gates: { hasSkatteverket: boolean; hasInbox: boolean }): {
count: number
skv: number
receipts: number
assistant: number
} {
const skv = 3
const receipts = 3 + (gates.hasSkatteverket ? 1 : 0)
const assistant = receipts + (gates.hasInbox ? 1 : 0)
return { count: assistant, skv, receipts, assistant }
}