INV is the AR plane. Invoices begin life as a draft auto-populated from TIME entries against an engagement's billable rules; a human reviews + sends; the customer pays through one of four payment rails (Stripe, Wise, domestic VND PSP, manual bank transfer); the receipt matches back to the invoice (cash application); the GL takes the revenue. The Vietnamese-context specifics — Decree 123 e-invoice schema, Circular 78 GDT format, Mẫu 01/GTGT, MST validation, VietQR collection — are wrapped in three reusable CyberSkill skills (vn-vat-invoice, vn-mst-validate, vn-bank-transfer) so the same primitives serve INV here and any external SaaS that adopts the cyberskill-vn collection.
Why INV exists
Without an invoicing module, the gap between "did the work" and "got paid" lives in a spreadsheet, a Stripe dashboard, and somebody's memory. Multi-currency makes that gap worse (which rate? when? rounded how?). Vietnamese e-invoice requirements make it worse still (which Mẫu? which line item code? was the MST validated?). INV closes that gap with a single source of truth — one invoice row per billable event, one payment row per receipt, one aging report per close, and one webhook handler per payment rail — and exposes the same primitives as reusable skills so the patterns generalise.
Engagements carry billable rules (rate, currency, cap). TIME entries per engagement aggregate into invoice line items. Invoice draft is a one-click read.
Decree 123 + Circular 78 GDT format. Mẫu 01/GTGT line items. MST validation on customer + tenant. VietQR collection. cyberskill-vn skills wrap the lot.
Webhooks from Stripe/Wise/banks match incoming receipts to outstanding invoices. CUO drafts dunning emails on overdue; human sends.
INV is also part of the CyberSkill margin story: faster collection compresses DSO; multi-rail payment options improve customer experience; e-invoice automation avoids the 3-day round trip via the old VN paper-stamp process. Each is a small win that compounds.
What it does — 5W1H2C5M
Every cell traces back to PRD §9.16 and SRS §4.16.
| Axis | Question | Answer |
|---|---|---|
| 5W · What | What is INV? | An AR + multi-currency invoicing + payment integration service. Drafts invoices from TIME entries; sends; tracks payments; ages receivables; reconciles cash to invoices; routes Vietnamese e-invoices to GDT via the vn-vat-invoice skill. |
| 5W · Who | Who uses INV? | CFO / Finance: close, AR aging, dunning approval. Project lead / Account Manager: invoice draft preview + send. Customer: receives invoice + pays. CUO/CFO-skill: drafts dunning + payment-status narrative. |
| 5W · When | When does INV act? | (a) Engagement milestone or month-end → invoice draft. (b) Send invoice → email/portal. (c) Payment webhook → cash application. (d) Overdue tick → dunning. (e) Quarter-end → AR aging report. (f) Year-end → 1099-style summaries. |
| 5W · Where | Where does it run? | P2: single region (SG-1) backed by AWS RDS Postgres. Per-tenant data-residency for VN tenants (vn-hanoi-1) by P3. |
| 5W · Why | Why a separate module? | Because AR-ledger drift and tax-compliance surprise are existential to small companies. INV is the one source of truth for "what did we bill, what did they pay, who owes what". |
| 1H · How | How does it work? | Postgres for invoice + payment + reminder rows. Stripe / Wise webhooks → cash_application engine matches receipts to invoices by metadata + amount. VietQR generated via vn-bank-transfer skill; bank confirms via screen-scrape OR open-banking API (Napas247 P3). Vietnamese e-invoice via vn-vat-invoice skill → POST to GDT. |
| 2C · Cost | Cost budget? | P2: ~$40/month (RDS + Fargate + Stripe/Wise pass-through fees). 50-tenant: ~$150/month. Per-invoice cost: ~$0.02 + payment processor fee. |
| 2C · Constraints | Constraints? | (a) Vietnam Decree 123 + Circular 78 — e-invoice format. (b) Currency rounding policy (VND has no decimals; round to nearest 1,000 for display). (c) Per-engagement billable rules drive line items ((FR pending)). (d) Approved invoices are append-only ((FR pending) SRS); corrections via credit note. |
| 5M · Materials | Stack? | Rust 1.81 · axum · sqlx · PostgreSQL 16 · stripe-rust · wise sdk · typst / tectonic for PDF · async-graphql · cyberskill-vn skills · AWS S3 (immutable archive). |
| 5M · Methods | Method choices? | Idempotent webhooks (Stripe idempotency-key + DB UNIQUE on external_event_id). VND rate snapshot daily from SBV REST endpoint. Multi-rail collection abstracted behind PaymentRail trait. Append-only invoice rows; credit-note for corrections. |
| 5M · Machines | Deployment? | Fargate in SG-1. Multi-AZ Postgres RDS. S3 immutable bucket for PDFs. |
| 5M · Manpower | Who maintains? | CFO (R for close + dunning approval) + 0.25 FTE eng. |
| 5M · Measurement | How measured? | KPIs: DSO (days sales outstanding); cash-application match rate; e-invoice GDT acceptance rate; dunning effectiveness. |
Architecture
INV composes three external systems (Stripe, Wise, Vietnamese payment stack), one e-invoice issuer (GDT via the vn-vat-invoice skill), and one rate source (SBV daily snapshot) into a single AR ledger. The diagram below shows the canonical invoice → payment lifecycle.
(invoice + payment portal)"] CUO["🤖 CUO/CFO-skill
drafts + narrates"] end subgraph EDGE ["Edge"] AR["Apollo Router
JWT + RBAC"] end subgraph INV ["INV service (Rust · axum)"] GQL["GraphQL subgraph
Invoice · Payment"] REST["REST admin
send · void · credit"] MCP["MCP tool catalogue"] DRAFT["draft.rs
from TIME entries"] PDF["pdf_renderer.rs
tectonic deterministic"] SEND["send.rs
email + portal link"] WEBHOOK["webhook.rs
Stripe · Wise · bank"] CASH["cash_application.rs
match receipt → invoice"] AGING["aging.rs
0-30 · 31-60 · 61-90 · 90+"] DUN["dunning.rs
drafts only · human send"] FX["fx.rs
SBV daily snapshot"] end subgraph SKILLS ["cyberskill-vn (skill bridges)"] VAT["vn-vat-invoice
Mẫu 01/GTGT · GDT XML"] MST["vn-mst-validate
customer + tenant MST"] BANK["vn-bank-transfer
VietQR generator"] end subgraph STORES ["Stores"] PG[("PostgreSQL
invoice · line_item
payment · credit_note
reminder · rate_snapshot")] S3[("AWS S3
invoice PDFs · receipts
immutable bucket")] end subgraph EXT ["External rails"] STR["Stripe (USD/EUR)"] WISE["Wise (multi-currency)"] VQR["VietQR / Napas247 (VND)"] GDT["🇻🇳 GDT e-invoice
Tổng cục Thuế"] SBV["SBV daily rate API"] end subgraph SINKS ["Audit + events"] BRAIN["🧠 BRAIN
inv.* lifecycle"] NATS["📡 NATS
inv.invoice.sent · inv.payment.received"] CRM["🏢 CRM
payment-status sync"] OBS["👁 OBS"] end PM --> AR CFO --> AR CUST --> AR CUO --> AR AR --> GQL AR --> REST AR --> MCP REST --> DRAFT DRAFT --> PDF PDF --> VAT VAT --> GDT REST --> MST REST --> BANK REST --> SEND WEBHOOK --> CASH CASH --> PG STR -.webhook.-> WEBHOOK WISE -.webhook.-> WEBHOOK VQR -.webhook.-> WEBHOOK FX -.daily.-> SBV REST --> PG GQL --> PG PDF --> S3 AGING --> PG DUN --> SEND REST --> BRAIN REST --> NATS NATS --> CRM INV --> OBS classDef planned fill:#a7f3d0,stroke:#064e3b classDef store fill:#f5f3ff,stroke:#7c3aed classDef ext fill:#fef6e0,stroke:#9c750a classDef sink fill:#e8d4c2,stroke:#45210e class GQL,REST,MCP,DRAFT,PDF,SEND,WEBHOOK,CASH,AGING,DUN,FX,VAT,MST,BANK planned class PG,S3 store class STR,WISE,VQR,GDT,SBV ext class BRAIN,NATS,CRM,OBS sink
Internal components
| Component | Path (planned) | Responsibility |
|---|---|---|
invoice.rs | services/inv/src/invoice.rs | Invoice CRUD. Append-only after status="sent"; corrections via credit_note. |
draft.rs | services/inv/src/draft.rs | Auto-populate draft from TIME entries per engagement + billable rules. |
line_item.rs | services/inv/src/line_item.rs | Per-invoice line items; aggregates billable rules; supports VN VAT category codes. |
payment.rs | services/inv/src/payment.rs | Payment row writer. Idempotent by external_event_id. |
credit_note.rs | services/inv/src/credit_note.rs | Credit-note + refund. Always tied to an invoice; reduces outstanding balance. |
pdf_renderer.rs | services/inv/src/pdf_renderer.rs | Deterministic PDF via tectonic. Tenant brand applied via per-tenant template. |
cash_application.rs | services/inv/src/cash_application.rs | Match incoming receipt → outstanding invoice by metadata + amount. Manual override UI for ambiguous cases. |
webhook.rs | services/inv/src/webhook.rs | Stripe + Wise + bank webhook handlers. Signature verification; idempotency-key check. |
aging.rs | services/inv/src/aging.rs | AR aging bucket report: 0–30 / 31–60 / 61–90 / 90+ days. |
dunning.rs | services/inv/src/dunning.rs | Drafts dunning emails (T+1, T+7, T+30 of overdue). Sends ONLY after CFO/AM approval. |
fx.rs | services/inv/src/fx.rs | Daily SBV rate snapshot. Cached per day; new invoices use today's snapshot. |
vn_vat_bridge.rs | services/inv/src/vn_vat_bridge.rs | Bridge to vn-vat-invoice skill — Mẫu 01/GTGT generation + GDT submission. |
mst_bridge.rs | services/inv/src/mst_bridge.rs | Bridge to vn-mst-validate skill — customer + tenant MST validation before send. |
bank_bridge.rs | services/inv/src/bank_bridge.rs | Bridge to vn-bank-transfer skill — VietQR generator. |
audit_bridge.rs | services/inv/src/audit_bridge.rs | Lifecycle rows to BRAIN. |
nats_bridge.rs | services/inv/src/nats_bridge.rs | Event publisher — sent/paid/overdue events for CRM + KPI dashboards. |
migrations/ | services/inv/migrations/ | sqlx migrations. UNIQUE on external_event_id; append-only on invoice after status=sent. |
Data model
Invoice + line items + payment + credit note are the core rows. Reminders are a side-table. FX rate snapshots are daily. Webhook event IDs carry a UNIQUE constraint so duplicate webhooks are no-ops.
Payment-rail comparison (P2 launch set)
| Rail | Currencies | Typical fee | Settlement | Webhook | Use case |
|---|---|---|---|---|---|
| Stripe | USD, EUR (multi-currency intent supported) | 2.9% + $0.30 | T+2 | signed (HMAC) | Default for international customers; cards. |
| Wise | 40+ incl. VND, SGD, USD, EUR | 0.4–1% mid-market | T+0 to T+2 | polling + webhook (P3) | Cross-border + local-rail collection. |
| VietQR / Napas247 | VND | ~5,000 VND / txn | instant (24/7) | bank API (BIDV, MB, VCB) | Vietnamese B2B + B2C; preferred for VN customers. |
| Manual bank transfer | any | bank-dependent | T+1 to T+5 | none (manual reconciliation) | Fallback when other rails unavailable. |
| MoMo / ZaloPay / VNPay (P3) | VND | 1.5–2.5% | instant | signed | VN consumer wallets — added at P3. |
API surface
Three surfaces: GraphQL subgraph for invoice/payment reads, REST admin for send/credit/void operations, MCP tool catalogue for the CUO/CFO-skill (draft dunning, narrate aging — never send without human approval).
GraphQL subgraph
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.5", import: ["@key", "@requiresScopes"])
type Invoice @key(fields: "id") {
id: ID!
number: String!
customer: Customer!
engagement: Engagement
currency: String!
subtotal: Money!
vat: Money!
total: Money!
outstanding: Money!
issuedAt: Date!
dueAt: Date!
status: InvoiceStatus!
lineItems: [LineItem!]!
payments: [Payment!]! @requiresScopes(scopes: [["inv.payment_read"]])
pdfUrl: String! # pre-signed S3
}
type Payment @key(fields: "id") {
id: ID!
invoiceId: ID!
rail: PaymentRail!
amount: Money!
receivedAt: DateTime!
status: PaymentStatus!
}
type CreditNote @key(fields: "id") {
id: ID!
invoiceId: ID!
amount: Money!
reason: String!
issuedAt: Date!
}
type AgingReport {
bucket0_30: Money!
bucket31_60: Money!
bucket61_90: Money!
bucket90Plus: Money!
total: Money!
byCustomer: [CustomerAging!]!
}
enum InvoiceStatus { DRAFT SENT VIEWED PAID OVERDUE LATE ESCALATED VOID }
enum PaymentStatus { RECEIVED MATCHED RECONCILED REVERSED }
enum PaymentRail { STRIPE WISE VIETQR MOMO ZALOPAY VNPAY MANUAL }
type Query {
invoice(id: ID!): Invoice
invoices(customerId: ID, status: InvoiceStatus, since: Date): [Invoice!]!
agingReport(asOf: Date): AgingReport!
@requiresScopes(scopes: [["inv.aging_read"]])
}
type Mutation {
draftInvoice(input: DraftInvoiceInput!): Invoice!
sendInvoice(id: ID!): Invoice!
@requiresScopes(scopes: [["inv.send"]])
voidInvoice(id: ID!, reason: String!): Invoice!
@requiresScopes(scopes: [["inv.void"]])
issueCreditNote(input: CreditNoteInput!): CreditNote!
@requiresScopes(scopes: [["inv.credit"]])
approveDunning(reminderId: ID!): Reminder!
@requiresScopes(scopes: [["inv.dunning_approve"]])
}
REST + webhook surface
| Method | Path | Purpose |
|---|---|---|
| POST | /admin/invoice/draft | Draft from TIME entries against engagement billable rules. |
| POST | /admin/invoice/{id}/send | Send invoice — render PDF, validate MST (VN), submit e-invoice to GDT (VN), email customer. |
| POST | /admin/invoice/{id}/void | Void invoice. Append-only — sets status=void; does NOT delete. |
| POST | /admin/invoice/{id}/credit-note | Issue credit note (partial or full). |
| GET | /admin/aging?as_of=YYYY-MM-DD | AR aging report. |
| POST | /admin/dunning/{reminder_id}/approve | CFO/AM approves CUO-drafted dunning email; sends. |
| POST | /webhook/stripe | Stripe webhook — HMAC verified; cash_application triggered. |
| POST | /webhook/wise | Wise webhook (P3); polling fallback P2. |
| POST | /webhook/bank/{bank_code} | Bank API webhook (BIDV, MB, VCB) for VietQR receipts. |
| GET | /portal/invoice/{token} | Customer-facing invoice + payment options page. |
| POST | /internal/fx/snapshot | Daily SBV rate snapshot job. |
| POST | /admin/cash-application/manual | Manual override — link a payment to an invoice when auto-match fails. |
MCP tool catalogue (CUO/CFO-skill)
| Tool name | Inputs | Outputs | Annotations |
|---|---|---|---|
cyberos.inv.draft_invoice | engagement_id, period | draft Invoice (not sent) | readwrite (draft only) · human-confirm |
cyberos.inv.list_overdue | since? | Invoice[] | readonly · scope=inv.aging_read |
cyberos.inv.draft_dunning | invoice_id | email draft | readwrite (draft only) · human-confirm before send |
cyberos.inv.explain_aging | as_of? | narrative AR report | readonly |
cyberos.inv.summarise_payment_status | invoice_id | narrative | readonly |
cyberos.inv.validate_mst | mst_string | {valid, taxpayer_info} | readonly · bridges vn-mst-validate skill |
cyberos.inv.generate_vietqr | amount, bank, account | QR data + image | readonly · bridges vn-bank-transfer skill |
Key flows
Flow 1 — Draft invoice from TIME entries
+ line_item rows PG-->>I: invoice_id I-->>PM: draft invoice ready for review Note over PM: PM previews PDF, may edit lines
before "send".
Flow 2 — Send (with VN e-invoice via vn-vat-invoice skill)
Flow 3 — Stripe payment received + cash application
(HMAC signed) W->>W: verify signature W->>PG: INSERT payment (UNIQUE external_event_id) alt new event W->>CASH: match payment to invoice
(by metadata.invoice_id OR amount + customer) alt single match CASH->>PG: invoice.outstanding -= amount
set status="paid" if outstanding=0 CASH->>N: publish inv.payment.received CRM-->>N: subscriber → updates account record else ambiguous CASH->>PG: payment.status="received" (unmatched) CASH->>N: publish inv.payment.unmatched (alerts AM/CFO) end else duplicate event W->>W: idempotent no-op end
Flow 4 — VietQR collection (VN customer paying via bank)
{account, amount, addInfo: "INV-2026-04-001"} BANK-->>I: QR string + image I->>C: email with VietQR image + amount + addInfo C->>B247: scan QR · approve transfer B247-->>I: webhook "transfer received" I->>W: route to webhook handler W->>CASH: match by addInfo (invoice number) + amount CASH->>CASH: idempotent match · update invoice CASH->>I: status="paid" Note over CASH: matching strictness:
(invoice_number in addInfo) AND (amount within ±1,000 VND tolerance)
Flow 5 — Dunning automation (drafts only · human send)
OWASP LLM08 "excessive agency" mitigated.
Invoice lifecycle
An invoice traverses up to eight states. Once sent, the invoice row is append-only — corrections happen via credit_note or by voiding and reissuing.
AR aging buckets
| Bucket | Days past due | Default action |
|---|---|---|
| Current | ≤ 0 | None — not yet due |
| 0–30 | 1–30 | T+1 friendly reminder · T+7 firmer reminder |
| 31–60 | 31–60 | T+30 escalation · CFO sees in close report |
| 61–90 | 61–90 | AM phone call · CFO weekly review |
| 90+ | 91+ | Legal counsel consultation · write-off policy review |
Functional Requirements
The CyberOS FR catalogue is being rebuilt one feature at a time via the open fr-author Agent Skill.
Previous FR enumerations were archived 2026-05-14 and are no longer reflected on this page. PRD/SRS narrative remains authoritative for the spec; specific FRs land here as they are re-authored.
Non-Functional Requirements
PRD §11.2.1 (performance) and §11.2.4 (reliability) bind on INV — especially payment reconciliation correctness.
| NFR ID | Concern | Target | Measurement |
|---|---|---|---|
N(FR pending) | Invoice generation p95 | ≤ 2 s | bench/draft.rs · k6 load |
N(FR pending) | PDF render p95 (deterministic) | ≤ 600 ms | bench/pdf.rs |
N(FR pending) | VN e-invoice GDT submission p95 | ≤ 5 s (network bound) | k6 against staging GDT |
N(FR pending) | Payment-webhook idempotency | 100% (zero duplicate side-effects) | chaos test: replay webhook 100× |
N(FR pending) | Cash-application auto-match rate | ≥ 90% (the rest manual) | OBS dashboard · monthly review |
N(FR pending) | Webhook signature verification | 100% (zero unsigned acceptance) | integration test injects unsigned webhook |
N(FR pending) | VND rounding correctness | 0 sub-1,000 drift / 1k invoices | property test |
N(FR pending) | INV availability | ≥ 99.5% | SLO monitor |
N(FR pending) | Invoice PDF durability (10-year) | 0 lost objects | S3 immutable + quarterly inventory |
N(FR pending) | Customer payment method tokens encrypted | KMS-wrapped | schema inspection · CI gate |
N(FR pending) | PCI scope minimisation (no card data stored) | = 0 raw PAN columns | schema audit · Stripe handles PCI |
Dependencies
INV reads from TIME, CRM, and AUTH; bridges to three skills (vn-vat-invoice, vn-mst-validate, vn-bank-transfer); integrates with four external rails (Stripe, Wise, VietQR/Napas247, GDT). Events flow to CRM via NATS.
entries · billable"] CRM["🏢 CRM
customer records"] BRAIN["🧠 BRAIN"] OBS["👁 OBS"] NATS["📡 NATS"] S3["🗂 S3 (immutable)"] end subgraph skills ["Skill bridges"] VAT["🛠 vn-vat-invoice"] MST["🛠 vn-mst-validate"] BANK["🛠 vn-bank-transfer"] end subgraph external ["External rails"] STR["Stripe"] WISE["Wise"] VQR["VietQR / Napas247"] GDT["🇻🇳 GDT"] SBV["SBV rate API"] end INV["🧾 INV"] subgraph downstream ["INV feeds"] CRM2["🏢 CRM
payment status"] GL["📊 GL
revenue recognition"] BRAIN2["🧠 BRAIN
audit"] CUO["🤖 CUO/CFO-skill"] end AUTH --> INV TIME --> INV CRM --> INV BRAIN --> INV OBS --> INV NATS --> INV S3 --> INV VAT --> INV MST --> INV BANK --> INV STR --> INV WISE --> INV VQR --> INV GDT --> INV SBV --> INV INV --> CRM2 INV --> GL INV --> BRAIN2 INV --> CUO classDef planned fill:#a7f3d0,stroke:#064e3b classDef shipped fill:#f5ede6,stroke:#45210e classDef ext fill:#fef6e0,stroke:#9c750a class INV planned class AUTH,TIME,CRM,CRM2,GL,CUO,OBS,NATS planned class BRAIN,BRAIN2,S3,VAT,MST,BANK shipped class STR,WISE,VQR,GDT,SBV ext
Compliance scope
INV is the tax-and-financial-records front door. Decree 123, Circular 78, and 10-year retention are non-negotiable for VN tenants.
| Regulation / standard | Article / clause | INV feature that satisfies it |
|---|---|---|
| Decree 123/2020/NĐ-CP | Art. 7 — Electronic invoice mandate | vn-vat-invoice skill generates GDT-compliant e-invoice for every VN tenant invoice. |
| Circular 78/2021/TT-BTC | Schema — GDT XML format | Skill emits Circular 78 XML; submits to GDT via authenticated endpoint. |
| Decree 119/2018/NĐ-CP | Art. 4 — E-invoice retention | S3 immutable bucket; 10-year retention lock. |
| Vietnam VAT Law (Law 13/2008) | Art. 8 — VAT rate categories | Line items carry VAT category code: 0%, 5%, 8%, 10% (Vietnamese categories). |
| Vietnam Tax Administration Law (2019) | Art. 33 — MST | MST validated (tenant + customer) via vn-mst-validate before submission. |
| Vietnam PDPL (Law 91/2025) | Art. 14 — DSAR | Customer invoice history exportable as DSAR bundle. |
| GDPR (EU 2016/679) | Art. 6 — Lawful processing | Customer billing data processed on contractual basis. |
| PCI DSS | Cardholder data scope minimisation | No raw PAN stored; Stripe handles card data; INV stores only Stripe customer tokens. |
| ISO/IEC 27001:2022 | A.8.10 — Information deletion | Invoice rows append-only; void via status flag, not delete. |
| ISO/IEC 27001:2022 | A.5.13 — Information labelling | Customer + invoice records classified confidential; access scoped. |
| SOC 2 Type II | CC8.1 — Change management | Append-only ledger; credit notes audited. |
| VN Accounting Standards (VAS) | VAS 14 — Revenue | Revenue recognition triggered by invoice.status="sent"; GL bridge per accrual. |
| SBV Circular 35/2018 | FX reporting | Daily rate snapshot from SBV; cross-border invoice carries snapshot reference. |
Risk entries
INV's risks are largely about reconciliation correctness and webhook spoofing. The cash-application false-match risk is the highest single-incident risk.
| ID | Risk | Likelihood | Impact | Owner | Mitigation |
|---|---|---|---|---|---|
R-INV-001 | Webhook spoofing leads to false payment record | Low | High | CTO | HMAC signature verification on every webhook; unsigned events rejected; integration test injects unsigned payload. |
R-INV-002 | Duplicate webhook causes double cash-application | Medium | Medium | CTO | UNIQUE on external_event_id; idempotent INSERT; chaos test replays webhook 100×. |
R-INV-003 | Cash-application false match (wrong invoice) | Medium | Medium | CFO | Strict matching: metadata.invoice_id OR (amount + customer + ±1k VND tolerance); ambiguous cases land in manual-override queue. |
R-INV-004 | GDT e-invoice submission rejected (Mẫu format drift) | Medium | Medium | CTO + CFO | vn-vat-invoice skill versioned; CI test against GDT sandbox; alert on rejected status. |
R-INV-005 | Currency rounding drift on cross-currency invoice | Medium | Low | CFO | Round-half-to-even at line-item level; VND no decimals; property test asserts deterministic. |
R-INV-006 | SBV rate API outage blocks new cross-currency invoices | Medium | Low | CTO | Last-known-good cached; on outage, use yesterday's rate + flag invoice for review. |
R-INV-007 | CUO sends dunning email without approval | Low | Medium | CSO | MCP tool annotated destructive=false; reminder.status="drafted" cannot transition to "sent" without approval_id; CI verifies. |
R-INV-008 | Stripe webhook secret leaked | Low | High | CSO | Secret in AWS Secrets Manager; rotation every 90 days; access audited. |
R-INV-009 | Invoice number gap (regulatory issue in VN) | Medium | Medium | CFO | Per-tenant sequence with gap-detection job; void invoices retain number; audit shows void/issued chain. |
R-INV-010 | 10-year retention violated by S3 lifecycle bug | Low | High | CTO | Immutable bucket; lifecycle policy review at deploy; quarterly inventory. |
KPIs
INV health rolls up into 9 KPIs across AR efficiency, reconciliation accuracy, and compliance.
| KPI | Formula | Source | Target |
|---|---|---|---|
| DSO (Days Sales Outstanding) | (AR balance / sales) × period days | INV DB | ≤ 35 days |
| Auto-match rate | auto-matched payments / total | cash_application | ≥ 90% |
| Invoice generation p95 | histogram | OBS | ≤ 2 s |
| GDT e-invoice acceptance rate | accepted / submitted | vn_einvoice | ≥ 99.5% |
| Dunning effectiveness | (invoices paid within 14d of dunning) / dunning sent | INV DB | ≥ 60% |
| Aging 90+ as % of total | aging_90_plus / total_outstanding | aging_report | ≤ 5% |
| Manual cash-application queue depth | unmatched payments > 24h | OBS | ≤ 2 at any time |
| Webhook spoofing attempts blocked | signature-failed count | OBS | tracked; alert on bursts |
| Invoice number gap incidents | detected gaps / period | cron gap-check | = 0 unexplained |
RACI matrix
INV is operationally owned by CFO; AM drives per-engagement billing; CFO approves dunning; CTO owns reliability.
| Activity | CEO | CFO | AM/PL | CTO | CSO | DPO |
|---|---|---|---|---|---|---|
| Engagement billable-rules setup | I | C | A/R | I | I | I |
| Invoice draft → send | I | C | A/R | I | I | I |
| Cash application (manual override) | I | A/R | C | I | I | I |
| AR aging review | I | A/R | C | I | I | I |
| Dunning approval | I | A/R | C | I | I | I |
| VN e-invoice integration (GDT) | I | C | I | A/R | C | I |
| Webhook secret rotation | I | I | I | R | A | I |
| 10-year retention audit | I | R | I | A | C | C |
| DSAR (customer scope) | I | C | R | I | C | A |
R Responsible · A Accountable · C Consulted · I Informed.
Planned CLI surface
Admin CLI cyberos-inv for CFO/AM.
1. Draft an invoice
$ cyberos-inv invoice draft \
--engagement acme-q2-2026 \
--period 2026-04
[draft] reading TIME entries for engagement acme-q2-2026 (2026-04)
[time] 47 entries · 168 hours
[engagement] rate=$150/hr · currency=USD · cap=N/A
[draft] subtotal: $25,200.00
[draft] vat: $0.00 (USD invoice · no VN VAT)
[draft] total: $25,200.00
[invoice] INV-2026-04-001 · status=draft
[audit] brain seq=16001
2. Send an invoice (VN tenant · e-invoice)
$ cyberos-inv invoice send --id INV-2026-04-005
[mst] validating tenant MST: 0301234567 ✓
[mst] validating customer MST: 0307654321 ✓
[vn-vat] generating Mẫu 01/GTGT XML (Circular 78)
[gdt] submitting to Tổng cục Thuế…
[gdt] accepted · message_id = GDT-2026-04-9F3E2A1B
[pdf] deterministic render via tectonic
[s3] archived to s3://inv/2026/04/INV-2026-04-005.pdf (immutable 10y)
[email] sent to ke-toan@acme.vn
[nats] inv.invoice.sent published
[status] sent
[audit] brain seq=16011
3. Generate VietQR for an invoice
$ cyberos-inv vietqr --invoice INV-2026-04-005
[vn-bank-transfer skill]
account: 0307654321 @ BIDV
amount: 45,000,000 VND
addInfo: INV-2026-04-005
qr: 00020101021238…6304abcd
[image] vietqr-INV-2026-04-005.png (240×240)
[expected_settlement] instant via Napas247
4. AR aging report
$ cyberos-inv aging --as-of 2026-05-14
[AR aging report · 2026-05-14]
bucket amount (USD eq) invoices
current $48,200 12
0-30 overdue $12,400 3
31-60 $ 4,800 1
61-90 $ 800 1
90+ $ 200 1
───────── ──────────── ───
total $66,400 18
[top overdue]
INV-2026-02-003 acme corp $4,800 45 days
INV-2025-12-007 globex industries $800 71 days
INV-2025-11-012 initech $200 120 days
5. Approve dunning emails
$ cyberos-inv dunning list --status drafted
[3 reminders awaiting approval]
rem-id invoice kind amount age
R-001 INV-2026-04-001 T+7 $25,200 8d
R-002 INV-2026-03-007 T+30 $ 8,400 34d
R-003 INV-2025-11-012 esc. $ 200 120d
$ cyberos-inv dunning approve --reminder R-001 --cfo-sign
[approved] cfo: hoa@cyberskill.com ✓
[sent] email delivered to ar@acme.com
[audit] brain seq=16031
6. Manual cash application
$ cyberos-inv cash-app match \
--payment pay_01HZJ8… \
--invoice INV-2026-04-005 \
--reason "addInfo missing but bank ref matches"
[match] payment pay_01HZJ8… → INV-2026-04-005
[update] invoice.outstanding 45M VND → 0
[update] invoice.status sent → paid
[nats] inv.payment.received published
[audit] brain seq=16041
7. Issue credit note
$ cyberos-inv credit-note issue \
--invoice INV-2026-04-001 \
--amount 1200 \
--currency USD \
--reason "scope reduction agreed with customer"
[credit-note] CN-2026-04-001 issued
[invoice] INV-2026-04-001 outstanding: $25,200 → $24,000
[audit] brain seq=16051
Phase status & estimates
cyberos-inv entrypoint| Capability | Status |
|---|---|
| Invoice draft from TIME entries | planned · P2 |
| Multi-currency (VND/USD/SGD/EUR) + SBV snapshot | planned · P2 |
| Stripe (USD/EUR) integration + webhook | planned · P2 |
| Wise integration (multi-currency) | planned · P2 |
| VietQR / Napas247 collection | planned · P2 |
| Vietnamese e-invoice (Decree 123 + Circular 78) | planned · P2 |
| MST validation (vn-mst-validate skill) | skill shipped |
| vn-vat-invoice + vn-bank-transfer skills | skills shipped |
| AR aging report | planned · P2 |
| Dunning automation (drafts only · human send) | planned · P2 |
| Cash application (auto-match + manual override) | planned · P2 |
| Credit-note + void workflow | planned · P2 |
| Webhook idempotency + signature verification | planned · P2 |
| Customer payment portal (token-link) | planned · P2 |
| Recurring invoice schedule (subscription) | planned · P3 |
| MoMo / ZaloPay / VNPay rails | planned · P3 |
| Open-banking integration (Napas247 real-time) | planned · P3 |
References
- PRD §9.16 — INV module FRs ((FR pending) through (FR pending)).
- PRD §19.16 — INV architecture posture, multi-rail composition.
- PRD §11.2.1 — Performance NFRs (invoice generation p95).
- PRD §11.2.4 — Reliability NFRs (webhook idempotency, cash application).
- SRS §4.16 — Formal (FR pending) through (FR pending) with verification methods.
- Decree 123/2020/NĐ-CP — Vietnamese e-invoice mandate.
- Circular 78/2021/TT-BTC — GDT XML schema for e-invoices.
- Decree 119/2018/NĐ-CP — E-invoice retention (10 years).
- Vietnam VAT Law (Law 13/2008/QH12) — VAT category codes.
- Vietnam Tax Administration Law (2019) — MST regulations.
- Vietnam PDPL (Law 91/2025) — DSAR; customer billing data.
- SBV Circular 35/2018 — Daily FX rate disclosure obligations.
- VAS 14 — Revenue — Vietnamese Accounting Standards revenue recognition.
- PCI DSS 4.0 — Card data scope; INV stays out of scope by using Stripe.
- cyberskill-vn collection — vn-vat-invoice · vn-mst-validate · vn-bank-transfer skills.
- Architecture context: services.html#inv.