Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

73 KiB
Raw Permalink Blame History

Scratchpad -- MSP i18n: Invoicing Sub-batch

  • Plan slug: 2026-04-09-msp-i18n-invoicing
  • Created: 2026-04-09
  • Last synced to codebase: 2026-04-17

Status Recheck (2026-04-17)

Still 0% implemented. Verified against the current codebase:

  • server/public/locales/en/msp/invoicing.jsondoes not exist.
  • All 22 components listed in the PRD still have useTranslation=0.
  • features.json / tests.json: 0/37 features, 0/50 tests marked implemented.

PRD file-path correction (important)

The PRD's "File Inventory" header says files live in packages/billing/src/components/, but every component actually lives in one of three subdirectories. Update mental model to:

Component Actual path (relative to packages/billing/src/components/)
AutomaticInvoices.tsx billing-dashboard/AutomaticInvoices.tsx
ManualInvoices.tsx billing-dashboard/ManualInvoices.tsx
DraftsTab.tsx billing-dashboard/invoicing/DraftsTab.tsx
FinalizedTab.tsx billing-dashboard/invoicing/FinalizedTab.tsx
RecurringServicePeriodsTab.tsx billing-dashboard/RecurringServicePeriodsTab.tsx
BillingCycles.tsx billing-dashboard/BillingCycles.tsx
InvoicePreviewPanel.tsx billing-dashboard/invoicing/InvoicePreviewPanel.tsx
InvoiceTemplateEditor.tsx billing-dashboard/InvoiceTemplateEditor.tsx
InvoiceTemplates.tsx billing-dashboard/InvoiceTemplates.tsx
ExternalTaxBatchImportDashboard.tsx invoices/ExternalTaxBatchImportDashboard.tsx
ExternalTaxImportPanel.tsx invoices/ExternalTaxImportPanel.tsx
SendInvoiceEmailDialog.tsx billing-dashboard/invoicing/SendInvoiceEmailDialog.tsx
TaxReconciliationView.tsx invoices/TaxReconciliationView.tsx
GenerateTab.tsx billing-dashboard/invoicing/GenerateTab.tsx
PrepaymentInvoices.tsx billing-dashboard/PrepaymentInvoices.tsx
ContractInvoiceItems.tsx billing-dashboard/invoices/ContractInvoiceItems.tsx
InvoicingHub.tsx billing-dashboard/InvoicingHub.tsx
InvoiceTemplateManager.tsx billing-dashboard/InvoiceTemplateManager.tsx
InvoiceTaxSourceBadge.tsx invoices/InvoiceTaxSourceBadge.tsx
InvoiceAnnotations.tsx billing-dashboard/InvoiceAnnotations.tsx
PurchaseOrderSummaryBanner.tsx billing-dashboard/invoicing/PurchaseOrderSummaryBanner.tsx
PaperInvoice.tsx (excluded) billing-dashboard/PaperInvoice.tsx

Upstream changes since 2026-04-09 (affect this plan)

Commit What changed Impact on this plan
d3ad4fa4f feat(billing): allow editing draft invoice details DraftsTab.tsx gained inline draft-editing UI. File grew 550 → 558 LOC. Add an extra feature to wire new edit-draft strings (field labels, save/cancel, validation) under draftsTab.editDraft.*.
9dd19bf02 Fix draft invoice preview nullable due date typing Typing tweak in InvoicePreviewPanel. No new strings.
7cd6f79e2 fix: persist automatic invoice filter for ready and approval views AutomaticInvoices.tsx filter UI persistence; file grew 1983 → 2073 LOC (~+90 LOC). String count may be slightly higher than the PRD's ~120 estimate. Audit filter-mode labels (ready / needs approval) when extracting.
63353605a feat(F001-F018): enforce recurring approval blockers in due-work, UI, and generation AutomaticInvoices.tsx surfaces unapproved-time blocker strings. Include blocker/alert strings in automaticInvoices.blockers.*.
0825b1191 fix(accounting): move net_amount backfill migration to 2026-04-16, 22924f65c fix(accounting): use charge.net_amount for Xero/QBO export LineAmount, 8671fdcbd feat(accounting): nudge tenants to let the accounting system calculate tax, ef36541f7 fix(accounting): coerce bigint/numeric charge fields in export adapters, 60be5c878 fix(accounting): honor invoiceIds filter on export batches, f849c9f8f fix(accounting): return 409 instead of 500 when retry matches zero invoices, 50b4164ad fix(xero): make invoice re-export idempotent via InvoiceID + LineItemID threading, 2a14053b8 fix(accounting): make external-mode tax writeback actually complete Back-end/adapter-layer accounting work. No UI strings added to the 22 in-scope components. No plan changes. Noted to avoid merge conflicts when touching ExternalTax* / TaxReconciliationView files.
Contract-lines batch shipped 2026-04-14; adopted the enum-labels pattern (useBillingFrequencyOptions / useFormatBillingFrequency from @alga-psa/billing/hooks/useBillingEnumOptions, keys under features/billing.json#enums.billingFrequency.*). BillingCycles.tsx renders a month-anchor select (PRD's "MONTH_OPTIONS" gotcha); RecurringServicePeriodsTab.tsx and AutomaticInvoices.tsx render billing-frequency badges. New guidance: any billing-frequency label rendering (badges, select options, cell renderers) must use useFormatBillingFrequency() / useBillingFrequencyOptions() — do not re-translate those strings in msp/invoicing.json. Route must load features/billing (it already does for /msp/billing). Month-name labels in BillingCycles.tsx remain local to this batch (not shared).
i18n/billing_contracts merged (PR #2325) and i18n/biling_ctd merged (PR #2313) — contract-facing invoice wiring translated (test(T008): cover contract detail invoice tab translation keys, feat(F015): localize contract detail quick actions dialogs and invoices). Those are msp/contracts surfaces, not the 22 in-scope invoicing components. No overlap; ensure the "Invoices" tab inside contract detail keeps using msp/contracts keys, not msp/invoicing.

Scratchpad addendum

  • Add an explicit feature for DraftsTab edit-draft fields (commit d3ad4fa4f).
  • Add an explicit feature for AutomaticInvoices persisted-filter labels (commit 7cd6f79e2) and unapproved-time blocker alerts (commit 63353605a).
  • Remove/avoid re-translating billing-frequency labels; route them through useFormatBillingFrequency().
  • Re-estimate AutomaticInvoices from ~120 to ~135 strings to account for the +90 LOC.

No structural plan overhaul; proceed with the corrected paths and the three extra sub-features above.


What This Is

A green-field wiring pass: 22 invoicing components x useTranslation('msp/invoicing'). Unlike the tickets migration (which reused an existing namespace), this batch creates a brand new msp/invoicing.json namespace from scratch. Estimated ~500 translatable strings.

Decisions

  • (2026-04-09) New namespace msp/invoicing rather than adding to an existing file. Invoicing is a distinct billing domain with no overlap with other translated surfaces. The namespace sits under msp/ alongside msp/settings, msp/dashboard, etc.
  • (2026-04-09) Use t('key', { defaultValue: 'English fallback' }) everywhere for fallback safety, matching the pattern specified in the project i18n docs.
  • (2026-04-09) PaperInvoice.tsx (44 LOC) is excluded -- pure layout wrapper with zero user-visible strings. It renders children and sets CSS dimensions only.
  • (2026-04-09) InvoiceTemplateManager.tsx (92 LOC) has only 3 heading strings. It is primarily a sample invoice preview utility. Wire it for completeness.
  • (2026-04-09) Ship as independent PRs per size tier: large (AutomaticInvoices + ManualInvoices), medium (DraftsTab + FinalizedTab + RecurringServicePeriodsTab + BillingCycles + InvoicePreviewPanel + template components + tax components + SendInvoiceEmailDialog + GenerateTab), small (remaining 6 components), and a final translations-only PR.

Gotchas

Currency formatting

Several components use hardcoded new Intl.NumberFormat('en-US', ...) or formatCurrency from @alga-psa/core. These should be replaced with locale-aware formatting via useFormatters from @alga-psa/ui/lib/i18n/client. Files affected:

  • DraftsTab.tsx line 294-299 -- hardcoded Intl.NumberFormat('en-US', ...)
  • ContractInvoiceItems.tsx -- hardcoded $ prefix in template literals
  • ExternalTaxBatchImportDashboard.tsx -- local formatCurrency with hardcoded $
  • ExternalTaxImportPanel.tsx -- local formatCurrency with hardcoded $
  • TaxReconciliationView.tsx -- local formatCurrency with hardcoded $
  • PurchaseOrderSummaryBanner.tsx -- uses formatCurrencyFromMinorUnits with 'en-US'

Date formatting

toPlainDate(value).toLocaleString() calls in DraftsTab and FinalizedTab should receive the current locale rather than defaulting to the browser locale. Use useFormatters or pass locale explicitly.

Duplicate adapter name maps

Three files define the same ADAPTER_NAMES map (ExternalTaxBatchImportDashboard, ExternalTaxImportPanel, InvoiceTaxSourceBadge). Consider extracting to a shared translated key group externalTax.adapterNames.* to avoid triple-maintenance.

AutomaticInvoices.tsx is massive (1983 LOC)

This file has ~120 translatable strings spanning 5 distinct UI sections. Split into 4 features (F010-F014) for tractability. The inline helper functions (getParentGroupSummary, resolveIncompatibilityReasons, formatCadenceSourceBadge, getRecurringAssignmentContext) return English strings that must move to t() calls. Some of these are called outside the component body, so they will need the t function passed as a parameter or the strings must be moved inline.

Helper functions returning hardcoded strings

Several module-level functions return English text:

  • getParentGroupSummary() returns combinability labels
  • resolveIncompatibilityReasons() returns reason strings
  • formatCadenceSourceBadge() returns cadence source labels
  • summarizeCadenceSources() joins labels
  • getRecurringAssignmentContext() returns assignment context strings

These are defined outside the React component, so they cannot directly call t(). Options: (1) move them inside the component, (2) return translation keys and call t() at the render site, or (3) pass t as a parameter. Option (2) is cleanest for this pattern -- return keys, translate at render.

Interpolation needs

Several strings require {{count}} or {{name}} interpolation:

  • "Actions (N)" -- bulk action button labels in DraftsTab, FinalizedTab
  • "Generate Invoices for Selected Periods (N)" -- AutomaticInvoices
  • "Reversing N draft invoices will delete them..." -- DraftsTab plural
  • "N invoice(s) pending tax import" -- ExternalTaxBatchImportDashboard
  • "N Invoice(s)" / "N ready to send" / "N missing email" -- SendInvoiceEmailDialog
  • "Send N Email(s)" -- SendInvoiceEmailDialog
  • "Sending N invoice(s)..." -- toast in SendInvoiceEmailDialog
  • "Importing N/M..." -- ExternalTaxBatchImportDashboard progress
  • "over by $X" -- PO overage dialogs

Month names in BillingCycles

The MONTH_OPTIONS array has hardcoded January-December. These should be translated via t() keys like billingCycles.months.january through billingCycles.months.december.

RecipientSourceLabels in SendInvoiceEmailDialog

The recipientSourceLabels object maps enum values to display strings. These should become t('sendEmail.recipientSource.billingContact') etc.

TAX_SOURCE_CONFIG in InvoiceTaxSourceBadge

The config object has label and tooltip fields with English strings. These should be translated at render time, not at definition time (move t() calls to render).

Key Count Estimate

Group Estimated keys
automaticInvoices ~120
manualInvoices ~50
draftsTab ~30
finalizedTab ~30
recurringServicePeriods ~40
billingCycles ~25
invoicePreview ~20
templateEditor ~20
templates ~20
externalTax ~50
sendEmail ~25
generateTab ~15
prepayment ~15
contractItems ~10
hub ~5
templateManager ~5
taxBadge ~10
annotations ~5
purchaseOrder ~5
common ~15
Total ~515

Progress Log

  • (2026-04-18) F001 complete: created server/public/locales/en/msp/invoicing.json with the planned top-level namespace groups in PRD order and seeded English keys across all invoicing domains. Rationale: unblock component wiring against a stable msp/invoicing namespace now, then expand/refine individual keys as each component lands instead of coupling every first component edit to file creation. Commands: node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync('server/public/locales/en/msp/invoicing.json','utf8')); console.log(Object.keys(data).join(','));" Gotcha: keeping the JSON top-level key order aligned with the PRD makes the eventual namespace-shape test straightforward and avoids churn when additional locale packs are generated.

  • (2026-04-18) F002 complete: updated packages/core/src/lib/i18n/config.ts so ROUTE_NAMESPACES['/msp/billing'] preloads msp/invoicing alongside the existing billing/report/contract namespaces. Rationale: the invoicing screens render under /msp/billing tabs, so namespace preloading must be in place before any useTranslation('msp/invoicing') call can resolve outside fallback/default strings.

  • (2026-04-18) F003 complete: wired the Ready to Invoice chrome in packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx to useTranslation('msp/invoicing') for the section heading, explanatory copy, preview/generate actions, filter controls, and parent-group table headers. Validation: added packages/billing/tests/AutomaticInvoices.i18n.test.ts and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts from server/ (pass). Gotcha: in this test/runtime fallback path, interpolated defaultValue strings are rendered literally, so count-bearing button copy needs a pre-expanded fallback string even when the translation key still receives count for real locale interpolation.

  • (2026-04-18) F004 complete: converted AutomaticInvoices parent-group summary helpers from hardcoded English to translation-key metadata, then translated the rendered item/contract/line counts, combinability badges, and incompatibility-reason list through msp/invoicing. Validation: expanded packages/billing/tests/AutomaticInvoices.i18n.test.ts with the T004 static contract and reran npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts from server/ (pass). Decision: the helper layer now returns stable enum-like keys and CSS state only; actual user-facing copy is resolved at render time so later locale work can change strings without touching grouping logic.

  • (2026-04-18) F005 complete: localized AutomaticInvoices child-row chrome for cadence, billing timing, service period, pending-amount fallback, assignment-context labels, unresolved-work marker, and attribution warnings; also added a targeted formatter for approval-blocker reasons with raw fallback for unknown blocker text. Validation: extended packages/billing/tests/AutomaticInvoices.i18n.test.ts with the T005 static contract and reran npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts from server/ (pass). Gotcha: cadence labels are shared between ready rows and recurring-history badges, so formatCadenceSourceBadge() now exposes a translation key alongside its English fallback instead of forcing the component to reverse-map labels later.

  • (2026-04-18) F006 complete: localized AutomaticInvoices recurring-history action items plus the reverse/delete/preview/PO-overage dialog copy, including preview table headers/totals and the PO overage decision options. Validation: extended packages/billing/tests/AutomaticInvoices.i18n.test.ts with combined T006/T007/T008 coverage and reran npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts from server/ (pass). Decision: reverse/delete selection state now keeps raw cadence-source values instead of pretranslated English so every dialog render path can consistently reuse the same formatCadenceSourceText() translation bridge.

  • (2026-04-18) F007 complete: localized AutomaticInvoices recurring-history chrome (title, filter, table headers, badges, row-menu sr-only text), wired the materialization-gap panel to msp/invoicing, and translated the ready-surface loading/error panel copy that sits on the same workflow surface. The recurring-history invoice-date cell now uses useFormatters().formatDate(...) instead of toLocaleString() so it follows the active locale. Validation: reran npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts from server/ (pass). Also ran npx tsc -p packages/billing/tsconfig.json --noEmit; the only remaining failure is a pre-existing unrelated repo issue in packages/ui/src/lib/dateFnsLocale.ts requiring a missing pt locale entry. Commands: npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts, npx tsc -p packages/billing/tsconfig.json --noEmit Decision: the history panel now reuses the same cadence translation bridge as the ready table (formatCadenceSourceText) so contract-anniversary/client-schedule labels stay consistent across both AutomaticInvoices sub-surfaces.

  • (2026-04-18) Added F038/T051: the original AutomaticInvoices split missed several user-visible strings outside F003-F007 (needs-approval panel, preview limitation note, expand/collapse aria labels, selection hints, metadata plural copy, unknown fallbacks, PO fallback labels). Rationale: the PRD acceptance criterion is “every user-visible string” in the component. Tracking the remainder as a dedicated atomic follow-up keeps the running commits honest and avoids silently declaring AutomaticInvoices complete while leaving untranslated chrome behind.

  • (2026-04-18) F008 complete: wired packages/billing/src/components/billing-dashboard/ManualInvoices.tsx to useTranslation('msp/invoicing') for the create/edit headings, explanatory copy, client and invoice-number labels, client-picker and invoice-number placeholders, automated-items table chrome, line-item section headings, and total label. This pass also swapped the automated-items table and total summary from @alga-psa/core formatCurrency(..., 'en-US', ...) to useFormatters().formatCurrency(...). Validation: added packages/billing/tests/ManualInvoices.i18n.test.ts for T011 static coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/ManualInvoices.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Gotcha: ManualInvoices.tsx has helper subcomponents outside the main form body, so localized table/header copy and locale-aware currency formatting needed to be wired in both ManualInvoicesContent and AutomatedItemsTable rather than only at the parent render site.

  • (2026-04-18) F009 complete: localized the remaining ManualInvoices control/error surface: add-charge/add-discount buttons, submit/save/processing states, prepayment and credit-expiration copy, validation/backend-known error messages, unknown-service fallback text, and the ErrorBoundary fallback title/retry action. Validation: expanded packages/billing/tests/ManualInvoices.i18n.test.ts with static T012/T013 coverage and reran npx vitest run --config vitest.config.ts ../packages/billing/tests/ManualInvoices.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit; the only remaining failure is still the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: known backend/manual-form errors now flow through a single translateManualInvoiceError() helper so both thrown exceptions and success:false action payloads resolve through the same translation keys instead of duplicating string-matching logic in multiple branches.

  • (2026-04-18) F010 complete: wired packages/billing/src/components/billing-dashboard/invoicing/DraftsTab.tsx to useTranslation('msp/invoicing') across the drafts table columns, draft badge, search/bulk-action controls, row menu items, loading and empty states, reverse confirmation dialog, and the user-visible error messages. The tables amount/date cells now use useFormatters() instead of hardcoded Intl.NumberFormat('en-US') / toLocaleString(). Validation: added packages/billing/tests/DraftsTab.i18n.test.ts for static T014/T015 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/DraftsTab.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit; the only remaining failure is still the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Gotcha: the locale file already used pluralized bulkActions_* and reverse-dialog message keys, so the new static contract test has to validate those plural leaves rather than a single flat bulkActions/message node even though the component calls t('draftsTab.bulkActions', { count }) and t('draftsTab.reverseDialog.message', { count }).

  • (2026-04-18) F011 complete: localized packages/billing/src/components/billing-dashboard/invoicing/FinalizedTab.tsx for the finalized invoices table chrome, finalized badge, search/bulk controls, row actions (download/email/unfinalize), loading and empty states, and finalized-tab error messages. Amount/date cells now use useFormatters() instead of formatCurrencyFromMinorUnits(..., 'en-US', ...) / toLocaleString(). Validation: added packages/billing/tests/FinalizedTab.i18n.test.ts for static T016/T017 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/FinalizedTab.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit; the only remaining failure is still the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F012 complete: wired packages/billing/src/components/billing-dashboard/RecurringServicePeriodsTab.tsx to useTranslation('msp/invoicing') for the page heading/description, schedule selector and schedule-key entry controls, localized load/validation errors, the recurring-obligation summary labels, and the summary stat cards. The schedule option formatter now builds its labels from translated cadence/timing/unknown-client values instead of hardcoded English. Validation: added packages/billing/tests/RecurringServicePeriodsTab.i18n.test.ts with static T018 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/RecurringServicePeriodsTab.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: although F012 only requires the shell/form layer, this pass also introduced key-based helpers for recurring-service-period lifecycle labels, provenance reasons, governance actions, and regeneration-conflict copy so the remaining table/panel work in F013 can reuse the same translation bridge instead of rendering raw English from shared helpers.

  • (2026-04-18) F013 complete: finished the RecurringServicePeriodsTab.tsx table/panel surface by localizing the table headers, repair-state panel, repair result summary, regeneration-preview panel, conflict count/empty state, and the table row metadata that previously leaked English from shared helper return values (lifecycle labels/details, provenance reasons, governance-action chips, conflict labels/reasons). Validation: expanded packages/billing/tests/RecurringServicePeriodsTab.i18n.test.ts for static T019/T020 coverage and reran npx vitest run --config vitest.config.ts ../packages/billing/tests/RecurringServicePeriodsTab.i18n.test.ts from server/ (pass). Gotcha: the shared recurring-service-period helpers expose translated English strings, not translation keys, so this component now maps lifecycle state / provenance reason / governance action / conflict kind back to stable msp/invoicing keys at render time instead of trusting the helper copy directly.

  • (2026-04-18) F014 complete: wired packages/billing/src/components/billing-dashboard/BillingCycles.tsx to useTranslation('msp/invoicing') for the page heading, tooltip/description, filter controls, table headers, loading and error states, and the “View Client Billing” action. The anchor summary formatter now translates month names plus Weekday, Rolling, Starts, and Day patterns, and the table body no longer leaks English for cycle labels, assignment-id prefixes, or No active assignments / Not set / Unknown fallbacks. Validation: added packages/billing/tests/BillingCycles.i18n.test.ts for static T021/T022 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/BillingCycles.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F015 complete: wired packages/billing/src/components/billing-dashboard/invoicing/InvoicePreviewPanel.tsx to useTranslation('msp/invoicing') for the panel heading, template selector placeholder, empty/loading/error states, source-quote CTA, and all preview action buttons. This pass also localized the template “(Standard)” badge and the preview/action alert messages so translated button labels do not fall back to English inside error alerts. Validation: added packages/billing/tests/InvoicePreviewPanel.i18n.test.ts for static T023/T024 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoicePreviewPanel.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F016 complete: localized packages/billing/src/components/billing-dashboard/InvoiceTemplateEditor.tsx for the back navigation, create/edit headings, template-name and AST labels, visual/code tabs, read-only code alert, timestamp labels, save/cancel states, and the validation/load/save/AST-export error messages. The created/updated timestamps now use useFormatters().formatDate(..., { dateStyle, timeStyle }) instead of raw toLocaleString(). Validation: added packages/billing/tests/InvoiceTemplateEditor.i18n.test.ts for static T025/T026 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoiceTemplateEditor.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F017 complete: rewired packages/billing/src/components/billing-dashboard/InvoiceTemplates.tsx to useTranslation('msp/invoicing') for the list title, table headers, type labels, action-menu items, sr-only menu label, create button, loading state, and clone/set-default/fetch errors. This pass also localized system-generated clone names ((Copy), Copy of {{name}}), the standard-template suffix, the delete-dialog fallback entity name, and the client-side delete-validation fallback messages. Validation: added packages/billing/tests/InvoiceTemplates.i18n.test.ts for static T027/T028 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoiceTemplates.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F018 complete: localized packages/billing/src/components/invoices/ExternalTaxBatchImportDashboard.tsx for the batch-import card title/description, pending summary, table columns, import/refresh/import-all actions, progress/results states, empty state, help text, adapter names, and toast/error copy. This pass also replaced the components hardcoded $... and toLocaleDateString() helpers with useFormatters().formatCurrency(..., 'USD') and useFormatters().formatDate(...) so the rendered amounts/dates follow the active locale instead of English defaults. Validation: added packages/billing/tests/ExternalTaxBatchImportDashboard.i18n.test.ts for static T029/T030 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/ExternalTaxBatchImportDashboard.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: because the dashboard payload carries cents but no per-invoice currency code, the locale-aware formatter currently preserves existing semantics with 'USD' rather than a hardcoded English string template. If later API work exposes row currency, this helper can switch without touching the translated UI chrome.

  • (2026-04-18) F019 complete: localized packages/billing/src/components/invoices/ExternalTaxImportPanel.tsx for the panel title, show/hide history toggle, pending/imported alert copy, adapter-specific import CTA, reconciliation labels, significant-difference warning, import history heading/states, tooltip text, help text, and import-result toast/error copy. This pass also replaced the components hardcoded currency/date helpers with useFormatters().formatCurrency(..., 'USD') and useFormatters().formatDate(...) for reconciliation totals and history timestamps. Validation: added packages/billing/tests/ExternalTaxImportPanel.i18n.test.ts for static T031/T032 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/ExternalTaxImportPanel.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: both external-tax components now share the same translated adapter-name mapping pattern (quickbooks_online -> externalTax.adapterNames.quickbooks, etc.) so QuickBooks/Xero/Sage labels stay consistent across the batch dashboard, invoice-level panel, and the later tax-badge/reconciliation work.

  • (2026-04-18) F020 complete: localized packages/billing/src/components/billing-dashboard/invoicing/SendInvoiceEmailDialog.tsx for the dialog title, loading state, invoice/recipient summary counts, recipient-source badges, missing-email fallback, due/additional-message labels, preview sentence, cancel/send/sending button states, and the load/send toast/error messages. Validation: added packages/billing/tests/SendInvoiceEmailDialog.i18n.test.ts for static T033/T034 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/SendInvoiceEmailDialog.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: the email-preview copy now resolves through a single interpolated sendEmail.preview key with translated default sender/company fallbacks, which keeps pseudo-locale coverage intact even when the backend returns no fromEmail or companyName.

  • (2026-04-18) F021 complete: localized packages/billing/src/components/invoices/TaxReconciliationView.tsx for the reconciliation card title/description, status tooltips, internal/external summary labels, significant-difference warning, line-breakdown heading, table headers, line-number fallback, total row, loading/no-data states, and help text. This pass also replaced the components hardcoded currency formatting with useFormatters().formatCurrency(..., 'USD') and switched percentage text to locale-aware formatNumber(...). Validation: added packages/billing/tests/TaxReconciliationView.i18n.test.ts for static T035/T036 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/TaxReconciliationView.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: reconciliation-specific copy now lives under externalTax.reconciliationView.* instead of overloading the simpler single-invoice import-panel keys. That keeps the invoice-panel labels short while giving the standalone reconciliation card its own line-breakdown/table vocabulary.

  • (2026-04-18) F022 complete: localized packages/billing/src/components/billing-dashboard/invoicing/GenerateTab.tsx for the invoice-type label, automatic/manual/prepayment option labels, the three type descriptions, the load-data error, and the success-dialog message. The English source for generateTab.descriptions.* was updated to match the current component copy instead of the earlier scaffolded placeholder text. Validation: added packages/billing/tests/GenerateTab.i18n.test.ts for static T037 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/GenerateTab.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F023 complete: localized packages/billing/src/components/billing-dashboard/PrepaymentInvoices.tsx for the heading/description variants, field labels, type options, amount/description placeholders, validation errors, and submit button states. Validation: added packages/billing/tests/PrepaymentInvoices.i18n.test.ts for static T038 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/PrepaymentInvoices.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: the form now preserves the specific “credit memos are not yet supported” validation path instead of collapsing it into the generic “Error generating invoice” catch-all, which keeps the translated UX aligned with the actual blocked behavior.

  • (2026-04-18) F024 complete: localized packages/billing/src/components/billing-dashboard/invoices/ContractInvoiceItems.tsx for the shared table headers, product badge, contract/other-item subtotal labels, and the “Other Items” section heading. This pass also replaced the components hardcoded $... strings with useFormatters().formatCurrency(..., 'USD'). Validation: added packages/billing/tests/ContractInvoiceItems.i18n.test.ts for static T039 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/ContractInvoiceItems.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F025 complete: localized packages/billing/src/components/billing-dashboard/InvoicingHub.tsx for the page heading and the Generate / Drafts / Finalized sub-tab labels. Validation: added packages/billing/tests/InvoicingHub.i18n.test.ts for static T040 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoicingHub.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts.

  • (2026-04-18) F026 complete: localized packages/billing/src/components/billing-dashboard/InvoiceTemplateManager.tsx for the manager heading, sample-invoices heading, template-preview heading, and the sample invoice Invoice #... prefix using useTranslation('msp/invoicing'). Validation: added packages/billing/tests/InvoiceTemplateManager.i18n.test.ts for static T041 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoiceTemplateManager.i18n.test.ts from server/ (pass). Gotcha: this component still carries legacy unused local template state from older editor wiring; the i18n pass left that behavior untouched and only translated the user-facing chrome.

  • (2026-04-18) F027 complete: localized packages/billing/src/components/invoices/InvoiceTaxSourceBadge.tsx for badge labels, static/default tooltips, adapter-name mappings, and the dynamic adapter/imported-at tooltip variants. The imported-at suffix now uses useFormatters().formatDate(...) instead of toLocaleDateString(). Validation: added packages/billing/tests/InvoiceTaxSourceBadge.i18n.test.ts for static T042 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoiceTaxSourceBadge.i18n.test.ts from server/ (pass). Decision: the badge config now stores stable translation-key metadata (labelKey / tooltipKey) instead of English copy so no user-facing string remains stranded in module scope outside hook access.

  • (2026-04-18) F028 complete: localized packages/billing/src/components/billing-dashboard/InvoiceAnnotations.tsx for the section heading, internal/external labels, textarea placeholder, and add button copy using msp/invoicing. Validation: added packages/billing/tests/InvoiceAnnotations.i18n.test.ts for static T043 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoiceAnnotations.i18n.test.ts from server/ (pass). Gotcha: the component still hardcodes current_user_id for annotation creation, but that is existing non-i18n debt and remained out of scope for this sub-batch.

  • (2026-04-18) F029 complete: localized packages/billing/src/components/billing-dashboard/invoicing/PurchaseOrderSummaryBanner.tsx for the PO number/authorized/consumed/remaining labels and replaced the banners hardcoded formatCurrencyFromMinorUnits(..., 'en-US', ...) calls with useFormatters().formatCurrency(...). Validation: added packages/billing/tests/PurchaseOrderSummaryBanner.i18n.test.ts for static T044 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/PurchaseOrderSummaryBanner.i18n.test.ts from server/ (pass). Decision: the banner keeps its existing currencyCode prop and now formats cents-to-major-units inline before delegating to the locale-aware formatter, which avoids reintroducing an English locale argument through shared core helpers.

  • (2026-04-18) F038 complete: finished the remaining packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx chrome that the original split missed: needs-approval section title/body/field labels/action, grouped-preview limitation note, expand/collapse aria labels, ready-table selection hints, parent-group attribution-metadata plural copy, unknown cadence/client fallbacks, and PO-overage fallback labels. Validation: expanded packages/billing/tests/AutomaticInvoices.i18n.test.ts with static T051 coverage and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts from server/ (pass). Reran npx tsc -p packages/billing/tsconfig.json --noEmit --pretty false; the only failure remains the unrelated missing-pt locale mapping in packages/ui/src/lib/dateFnsLocale.ts. Decision: unknown cadence handling now flows through key metadata returned by formatCadenceSourceBadge() so grouped summary chips, row badges, and delete/reverse dialogs all share the same translation path instead of leaking raw helper strings.

  • (2026-04-18) Added F039: repo-wide node scripts/validate-translations.cjs checks every real locale present under server/public/locales, and this repo already ships a Portuguese (pt) locale pack. Rationale: without server/public/locales/pt/msp/invoicing.json, translation validation would fail even if the originally planned six production locales and two pseudo-locales were complete. Track Portuguese parity explicitly rather than sneaking in an untracked file.

  • (2026-04-18) F030 complete: generated server/public/locales/fr/msp/invoicing.json for the full msp/invoicing namespace. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail, .purchaseOrder.labels, .templateManager' server/public/locales/fr/msp/invoicing.json and parsed it with python3 - <<'PY' ... json.loads(...) ... PY (pass). Decision: locale generation now uses translation-memory reuse from existing aligned locale packs first, then fills only unmatched strings via chunked machine translation while preserving {{variables}} and \n placeholders verbatim.

  • (2026-04-18) F031 complete: generated server/public/locales/es/msp/invoicing.json for the full msp/invoicing namespace. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail.summary, .purchaseOrder.labels, .templateManager' server/public/locales/es/msp/invoicing.json and parsed it with python3 - <<'PY' ... json.loads(...) ... PY (pass). Gotcha: machine translation incorrectly rendered the short PO label once; normalized server/public/locales/es/msp/invoicing.json purchaseOrder.labels.short back to PO before committing so downstream overage copy keeps the standard abbreviation.

  • (2026-04-18) F032 complete: generated server/public/locales/de/msp/invoicing.json for the full msp/invoicing namespace. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail.summary, .purchaseOrder.labels, .templateManager' server/public/locales/de/msp/invoicing.json and parsed it with python3 - <<'PY' ... json.loads(...) ... PY (pass). Decision: after the Spanish PO miss, the generator was updated to keep the short PO abbreviation as a literal no-translate token for the remaining locale packs.

  • (2026-04-18) F033 complete: generated server/public/locales/nl/msp/invoicing.json for the full msp/invoicing namespace. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail.summary, .purchaseOrder.labels, .templateManager' server/public/locales/nl/msp/invoicing.json and parsed it with python3 - <<'PY' ... json.loads(...) ... PY (pass).

  • (2026-04-18) F034 complete: generated server/public/locales/it/msp/invoicing.json for the full msp/invoicing namespace. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail.summary, .purchaseOrder.labels, .templateManager' server/public/locales/it/msp/invoicing.json, parsed it with python3 - <<'PY' ... json.loads(...) ... PY, and audited accent coverage with python3 - <<'PY' ... print(sorted(set(ch for ch in text if ch in \"àèéìòù\"))) ... PY (pass; file includes àèéìòù).

  • (2026-04-18) F039 complete: generated server/public/locales/pt/msp/invoicing.json so repo-wide locale parity validation can succeed against the already-shipped Portuguese locale pack. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail.summary, .purchaseOrder.labels, .templateManager' server/public/locales/pt/msp/invoicing.json and parsed it with python3 - <<'PY' ... json.loads(...) ... PY (pass).

  • (2026-04-18) F035 complete: generated server/public/locales/pl/msp/invoicing.json for the full msp/invoicing namespace. Validation: spot-checked the generated file with jq '.automaticInvoices.ready.needsApproval, .sendEmail.summary, .purchaseOrder.labels, .templateManager' server/public/locales/pl/msp/invoicing.json and parsed it with python3 - <<'PY' ... json.loads(...) ... PY (pass).

  • (2026-04-18) F036 complete: regenerated pseudo-locales with node scripts/generate-pseudo-locales.cjs, which updated server/public/locales/xx/msp/invoicing.json and server/public/locales/yy/msp/invoicing.json from the current English source. Validation: spot-checked pseudo output with jq '.automaticInvoices.ready.needsApproval, .templateManager, .purchaseOrder.labels' server/public/locales/{xx,yy}/msp/invoicing.json and confirmed the fill markers plus preserved {{count}} / {{number}} placeholders are present.

  • (2026-04-18) F037 complete: ran node scripts/validate-translations.cjs and cleared the only reported issue. Validation: first pass failed on server/public/locales/it/msp/invoicing.json billingCycles.values.monthDay, where the translator had mangled the placeholder mask into TOKEN {{month}} PHVAR1; corrected it to {{day}} {{month}} and reran validation (pass, 0 errors / 0 warnings).

  • (2026-04-19) T001 complete: reran node scripts/validate-translations.cjs after the real/pseudo locale generation work and confirmed full repo-wide parity passes for de, es, fr, it, nl, pl, pt, xx, yy. Follow-up fix: while authoring locale smoke coverage, found billingCycles.values.dash had been machine-translated to null/- in several real locale files even though validator ignores value semantics; normalized that leaf back to the intended em dash () across fr/es/de/nl/it/pl/pt, then reran validation (pass).

  • (2026-04-19) T002 complete: added packages/billing/tests/InvoicingLocaleSmoke.i18n.test.ts and ran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoicingLocaleSmoke.i18n.test.ts from server/ (pass). Coverage: the new T002 assertion locks the English namespace top-level group order/shape to the PRD, and the same file also provides the representative locale smoke coverage needed for T048-T050.

  • (2026-04-19) T003 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices ready-to-invoice chrome; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T004 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices parent-group combinability coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T005 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices child execution-row coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T006 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices reverse-dialog coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T007 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices preview-dialog coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T008 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices PO-overage dialog coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T009 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices recurring-history coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T010 complete: checklist closed against AutomaticInvoices.i18n.test.ts for AutomaticInvoices materialization-gap coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T011 complete: checklist closed against ManualInvoices.i18n.test.ts for ManualInvoices form heading/field coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T012 complete: checklist closed against ManualInvoices.i18n.test.ts for ManualInvoices action/validation coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T013 complete: checklist closed against ManualInvoices.i18n.test.ts for ManualInvoices error-fallback coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T014 complete: checklist closed against DraftsTab.i18n.test.ts for DraftsTab table/search coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T015 complete: checklist closed against DraftsTab.i18n.test.ts for DraftsTab empty-state/dialog coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T016 complete: checklist closed against FinalizedTab.i18n.test.ts for FinalizedTab table/search coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T017 complete: checklist closed against FinalizedTab.i18n.test.ts for FinalizedTab empty-state/bulk-action coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T018 complete: checklist closed against RecurringServicePeriodsTab.i18n.test.ts for RecurringServicePeriodsTab page/form coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T019 complete: checklist closed against RecurringServicePeriodsTab.i18n.test.ts for RecurringServicePeriodsTab table/stat coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T020 complete: checklist closed against RecurringServicePeriodsTab.i18n.test.ts for RecurringServicePeriodsTab repair/regeneration coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T021 complete: checklist closed against BillingCycles.i18n.test.ts for BillingCycles heading/column/action coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T022 complete: checklist closed against BillingCycles.i18n.test.ts for BillingCycles month/anchor fallback coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T023 complete: checklist closed against InvoicePreviewPanel.i18n.test.ts for InvoicePreviewPanel heading/action coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T024 complete: checklist closed against InvoicePreviewPanel.i18n.test.ts for InvoicePreviewPanel empty/loading/error coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T025 complete: checklist closed against InvoiceTemplateEditor.i18n.test.ts for InvoiceTemplateEditor heading/tab/action coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T026 complete: checklist closed against InvoiceTemplateEditor.i18n.test.ts for InvoiceTemplateEditor validation/timestamp coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T027 complete: checklist closed against InvoiceTemplates.i18n.test.ts for InvoiceTemplates heading/table/create coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T028 complete: checklist closed against InvoiceTemplates.i18n.test.ts for InvoiceTemplates action/error coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T029 complete: checklist closed against ExternalTaxBatchImportDashboard.i18n.test.ts for ExternalTaxBatchImportDashboard card/table/import coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T030 complete: checklist closed against ExternalTaxBatchImportDashboard.i18n.test.ts for ExternalTaxBatchImportDashboard progress/result coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T031 complete: checklist closed against ExternalTaxImportPanel.i18n.test.ts for ExternalTaxImportPanel title/alert/reconciliation coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T032 complete: checklist closed against ExternalTaxImportPanel.i18n.test.ts for ExternalTaxImportPanel history/warning/help coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T033 complete: checklist closed against SendInvoiceEmailDialog.i18n.test.ts for SendInvoiceEmailDialog summary/button coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T034 complete: checklist closed against SendInvoiceEmailDialog.i18n.test.ts for SendInvoiceEmailDialog recipient/preview coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T035 complete: checklist closed against TaxReconciliationView.i18n.test.ts for TaxReconciliationView title/table coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T036 complete: checklist closed against TaxReconciliationView.i18n.test.ts for TaxReconciliationView alert/state/help coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T037 complete: checklist closed against GenerateTab.i18n.test.ts for GenerateTab type/description coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T038 complete: checklist closed against PrepaymentInvoices.i18n.test.ts for PrepaymentInvoices form coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T039 complete: checklist closed against ContractInvoiceItems.i18n.test.ts for ContractInvoiceItems table/subtotal coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T040 complete: checklist closed against InvoicingHub.i18n.test.ts for InvoicingHub tab coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T041 complete: checklist closed against InvoiceTemplateManager.i18n.test.ts for InvoiceTemplateManager heading/prefix coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T042 complete: checklist closed against InvoiceTaxSourceBadge.i18n.test.ts for InvoiceTaxSourceBadge badge/tooltip coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T043 complete: checklist closed against InvoiceAnnotations.i18n.test.ts for InvoiceAnnotations surface coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T044 complete: checklist closed against PurchaseOrderSummaryBanner.i18n.test.ts for PurchaseOrderSummaryBanner label/formatter coverage; coverage was added and validated during the paired feature work, so no new code changes were required in this close-out commit.

  • (2026-04-19) T045 complete: reran node scripts/validate-translations.cjs from the repo root (pass, 0 errors / 0 warnings) and closed the Italian accent-preservation checklist item against the earlier server/public/locales/it/msp/invoicing.json accent audit (àèéìòù present).

  • (2026-04-19) T046 complete: closed interpolation-variable preservation from the same fresh node scripts/validate-translations.cjs pass, which verified placeholder parity across de, es, fr, it, nl, pl, pt, xx, yy for the full msp/invoicing namespace.

  • (2026-04-19) T048 complete: reran npx vitest run --config vitest.config.ts ../packages/billing/tests/InvoicingLocaleSmoke.i18n.test.ts from server/ (pass) and closed the pseudo-locale invoicing-hub smoke check against the representative xx assertions for hub, generateTab, draftsTab, and finalizedTab chrome in InvoicingLocaleSmoke.i18n.test.ts.

  • (2026-04-19) T049 complete: closed the template pseudo-locale smoke check from the same fresh InvoicingLocaleSmoke.i18n.test.ts run, which asserts representative xx coverage for the template list, template editor, and template manager surfaces.

  • (2026-04-19) T050 complete: closed the German billing-cycles smoke check from the same fresh InvoicingLocaleSmoke.i18n.test.ts run, which verifies translated de column headers, action text, month names, and anchor-label building blocks for billingCycles.

  • (2026-04-19) T047 complete: reran the consolidated billing i18n regression suite from server/ with npx vitest run --config vitest.config.ts ../packages/billing/tests/AutomaticInvoices.i18n.test.ts ../packages/billing/tests/ManualInvoices.i18n.test.ts ../packages/billing/tests/DraftsTab.i18n.test.ts ../packages/billing/tests/FinalizedTab.i18n.test.ts ../packages/billing/tests/RecurringServicePeriodsTab.i18n.test.ts ../packages/billing/tests/BillingCycles.i18n.test.ts ../packages/billing/tests/InvoicePreviewPanel.i18n.test.ts ../packages/billing/tests/InvoiceTemplateEditor.i18n.test.ts ../packages/billing/tests/InvoiceTemplates.i18n.test.ts ../packages/billing/tests/ExternalTaxBatchImportDashboard.i18n.test.ts ../packages/billing/tests/ExternalTaxImportPanel.i18n.test.ts ../packages/billing/tests/SendInvoiceEmailDialog.i18n.test.ts ../packages/billing/tests/TaxReconciliationView.i18n.test.ts ../packages/billing/tests/GenerateTab.i18n.test.ts ../packages/billing/tests/PrepaymentInvoices.i18n.test.ts ../packages/billing/tests/ContractInvoiceItems.i18n.test.ts ../packages/billing/tests/InvoicingHub.i18n.test.ts ../packages/billing/tests/InvoiceTemplateManager.i18n.test.ts ../packages/billing/tests/InvoiceTaxSourceBadge.i18n.test.ts ../packages/billing/tests/InvoiceAnnotations.i18n.test.ts ../packages/billing/tests/PurchaseOrderSummaryBanner.i18n.test.ts ../packages/billing/tests/InvoicingLocaleSmoke.i18n.test.ts (pass: 22 files, 45 tests).

  • (2026-04-19) T051 complete: closed the remaining AutomaticInvoices approval/ready chrome checklist item from the same fresh regression run, which includes AutomaticInvoices.i18n.test.ts passing all 7 assertions including the F038 remainder coverage for approval-panel copy, grouped-preview note, aria labels, selection hints, attribution metadata, and unknown fallbacks.