feat(billing): rebuild the Abonnemang page as a clean order summary (#1696)
* feat(billing): rebuild the Abonnemang page as a clean order summary The sell view is now four outcome lines (one per paid capability), one freeze-and-retain sentence, and price / first charge / cancellation as flat Fönster rows above a single CTA. The decorative skyline banner and the repeated reassurance copy are gone; each money term is stated once, where the decision is made. Copy moves from hardcoded Swedish into the settings_billing namespace (sv+en). BillingActions shrinks to the CTA; plan choice lives in the price row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(billing): sell view as one number, four short benefits, one button Second pass on the Abonnemang page: the row version still read as cluttered. The price is now the headline (display serif, interval toggle beside it, one exkl./inkl. line), the benefits are noun + gloss in a 2x2 grid, and the money terms are one sentence under the CTA. Legal text stays behind the ?. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(billing): list every paid capability, framed as the external connections PAID_CAPABILITIES has seven keys, the page listed four. Add Betalningar (stripe_payments) and Webshop (woocommerce_sync + shopify_sync) and phrase the no-subscription line as the tier model actually works: only the external connections pause, everything else stays. 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
bb5fafe87b
commit
bd85395cd6
@@ -1070,6 +1070,8 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-08-18] IBAN prefill reads only enabled, still-connected SEK cash_accounts: disconnect keeps rows (bank_connection_id nulled) and the connect picker mirrors deselected accounts, so an unfiltered read could offer a closed or third-party IBAN as invoice payee (skeptic refutation on PR #1695).
|
||||
[2026-08-18] Editor PDF preview (#1686) recomputes ROT/RUT server-side from the posted lines with the same helpers as build-invoice-write.ts (computeDeduction / computeInvoiceDeductionTotal, base inkl. moms at the rendered rate, invoice-doc only) and resolves the masked personnummer the same way (typed value, else an individual customer's kundkort personnummer): the client is not trusted with the deduction math, and the preview must state the same avdrag row, info box and "Att betala" as the invoice that gets created. The editor now also posts deduction_personnummer / deduction_housing_designation to the preview route, only when a line claims a deduction (same privacy rule as buildInvoiceWritePayload). Swish QR amount in the preview follows buildSwishQrDataUrl, fixed separately in #1685.
|
||||
[2026-08-18] Skatteverket read data is visible to every company member, no new role gate (#1673): token rows are per (user, company) but the fetched skattekonto/declaration data belongs to the company, and viewers already read `skattekonto_transactions` and the local snapshot with no role check; membership (dispatcher-resolved ctx.companyId + company-scoped SELECT policy on `skatteverket_tokens`) is the gate. Reads resolve the caller's own token first, then the most recently issued active token of any member (all rows ordered, never `.maybeSingle()`, which errored once two members had connected). Writes (moms utkast/las/submit, AGI submit/spara/las, connect/disconnect, /status) stay on the caller's own token: BankID signing is personal.
|
||||
[2026-08-18] Abonnemang sell view rebuilt as a Fönster order summary (I abonnemanget: four outcome lines, one per PAID capability, plus one freeze-and-retain sentence; Pris och villkor: price with the interval toggle in the row, first charge date, cancellation as flat rows; one CTA; one Stripe line). Founder found the 07-11/07-27 version cluttered and not upgrade-inspiring: the skyline hero (h-40, decorative) ate a quarter of the 680px settings window and pushed the offer below the fold, and the same reassurance was repeated four times (attn line, under CTA, 'Så funkar det' rows). BillingHero removed (asset stays registered for onboarding); benefits rewritten from feature names to what happens for the user; every money term stated exactly once, where the decision is made. Copy moved from hardcoded Swedish into settings_billing (sv+en). Alternative considered and not built: a landing-page style offer stack (serif headline + bullets + CTA), punchier but breaks the Fönster grammar and hides the money terms in one line.
|
||||
[2026-08-18] Abonnemang sell view, second pass the same day: the Fönster row version (Pris / Första debitering / Uppsägning rows + eyebrows) still read as cluttered to the founder (five pieces of small grey text, an uppercase label per row, three price facts in one row). Replaced with the 'one number' layout: the price in the display serif with the interval toggle beside it and one exkl./inkl. line, four benefits as noun + short gloss in a 2x2 grid, one CTA, one terms sentence (first charge date, cancel any time, Stripe), one 'ingenting raderas' sentence with the legal behind '?'. Pane 633px -> 609px; word count roughly halved again. Rows and eyebrows stay for the paying/demo views where they describe state, not an offer.
|
||||
[2026-08-18] AGI receipt fallback (#1597): GET /agi/status serves the signed record from agi_declarations (kvittensnummer, response_data.signeradAv/signeradTid, submitted_at) only when the agi_submission_{period} cache is absent, and the declaration-sourced record deliberately carries NO salaryRunId: the period row is UNIQUE per company+period and regenerating a correction repoints its salary_run_id at the correction run while the stored kvittens still belongs to the original, so trusting the column would render the correction as filed with a superseded receipt. Ownership rests on signeradTid/submittedAt vs the run's agi_submitted_at stamp (same value) plus updatedAt = submitted_at, which predates any later correction's XML. Cache present still wins because it is the only place the in-flight states live. Rejected: a second client fetch in AGIPanel (two sources of truth for one card) and merging both records in the route (mixes another declaration's fields into an in-flight state).
|
||||
[2026-08-18] Trial seeding trigger widened to every PAID capability (20260818170000): the 2026-06-29 trigger hardcoded the four launch keys while PAID_CAPABILITIES grew to seven (stripe_payments, woocommerce_sync, shopify_sync); the Stripe webhook seeds from the constant, so payers had all seven and trialers four (prod 2026-08-18: 226/230 active trialers lacked stripe_payments). Fixed by redefining the function with all seven and mirroring existing trial bank_sync grants for the three keys; the pg test now compares the seeded set against PAID_CAPABILITIES itself so the two cannot drift silently again. Rejected: generating the VALUES list from the TS constant at build time (no codegen path into migrations exists; a test that pins them together is the cheaper guard).
|
||||
[2026-08-18] Invoice ROT/RUT personnummer surfaces (detail page, invoice PDF, preview PDF, editor kept-hint) switch to the payroll mask convention YYYYMMDD-XXXX (birth date visible, last four hidden), computed on read from deduction_personnummer_encrypted via lib/invoices/deduction-personnummer.ts: no schema change, nothing stored, never throws (bad ciphertext logs and renders no personnummer). The browser gets the mask from GET /api/invoices/[id]/rot-rut and never both the mask and the last four (that is the full number); v1 REST and MCP keep deduction_personnummer_last4 for compatibility (an additive deduction_personnummer_masked is a possible follow-up). InvoicePDF derives the mask itself when the caller passes the stored row, so none of the 11 render call sites can silently drop the personnummer; the preview route passes an already-masked value since it only has plaintext. The separate Skattereduktion card on the invoice detail page is folded into Detaljer as plain rows (Personnummer, Fastighet, Skattereduktion status with the begäran lifecycle) per founder decision 2026-08-18: it duplicated the totals block.
|
||||
|
||||
Reference in New Issue
Block a user