Fields it adds

FieldTypeRequiredDescription
invoice_number string yes Human readable invoice identifier presented to customers.
status string yes Canonical invoice status after provider mapping.
provider_status string no Original provider reported status or badge.
issued_at datetime yes Timestamp when the invoice was finalized or posted.
due_at datetime no Payment due timestamp derived from payment terms.
paid_at datetime no Timestamp when full payment cleared.
total_minor integer yes Total amount due expressed in minor currency units.
balance_minor integer no Remaining amount outstanding in minor units.
currency string yes ISO 4217 currency for the invoice.
payment_terms string no Payment term label describing when payment is expected.
collection_state string no Dunning phase or retry program detail presented to success teams.
last_reminder_at datetime no Timestamp of most recent payment reminder issued to customer.
aging_bucket_days integer no Number of days outstanding grouped for aging reports.
attempt_count integer no Number of automatic payment attempts made on this invoice (Stripe smart-retries attempt_count). Increments on each retry in the dunning window. Source: docs.stripe.com/billing/revenue-recovery/smart-retries.
next_payment_attempt datetime no When the next automatic payment retry is scheduled (Stripe smart-retries next_payment_attempt). Defines the open end of the retry window — modeled on the Refundable.refundable_until datetime-window precedent. Absent/null when no further attempts are scheduled (retries exhausted → invoice goes uncollectible, mirroring the subscription `unpaid` state). Source: docs.stripe.com/billing/revenue-recovery/smart-retries.
memo string no Internal finance note or customer visible memo.

What it shows in each context

list
BillingSummaryBadge
card
BillingSummaryBadge

Parameters

statusEnum string[], required
Canonical invoice status values allowed by the domain.
paymentTerms string[]
Supported payment term descriptors (ex: net_15, due_upon_receipt).

Objects that use it

The trait file

domains/saas-billing/traits/payable.trait.yaml
trait:
  name: SaaSBillingPayable
  version: 1.0.0
  description: Captures invoice level billing terms, status, and outstanding balance metadata for SaaS finance teams.
  category: domain
  tags:
    - billing
    - invoice
    - domain

parameters:
  - name: statusEnum
    type: string[]
    required: true
    description: Canonical invoice status values allowed by the domain.
    default:
      - draft
      - posted
      - open
      - processing
      - past_due
      - paid
      - refunded
      - uncollectible
      - void
  - name: paymentTerms
    type: string[]
    required: false
    description: "Supported payment term descriptors (ex: net_15, due_upon_receipt)."
    default:
      - due_upon_receipt
      - net_15
      - net_30
      - net_45

schema:
  invoice_number:
    type: string
    required: true
    description: Human readable invoice identifier presented to customers.
  status:
    type: string
    required: true
    description: Canonical invoice status after provider mapping.
    validation:
      enumFromParameter: statusEnum
  provider_status:
    type: string
    required: false
    description: Original provider reported status or badge.
  issued_at:
    type: datetime
    required: true
    description: Timestamp when the invoice was finalized or posted.
  due_at:
    type: datetime
    required: false
    description: Payment due timestamp derived from payment terms.
  paid_at:
    type: datetime
    required: false
    description: Timestamp when full payment cleared.
  total_minor:
    type: integer
    required: true
    description: Total amount due expressed in minor currency units.
  balance_minor:
    type: integer
    required: false
    description: Remaining amount outstanding in minor units.
    default: 0
  currency:
    type: string
    required: true
    description: ISO 4217 currency for the invoice.
  payment_terms:
    type: string
    required: false
    description: Payment term label describing when payment is expected.
    validation:
      enumFromParameter: paymentTerms
  collection_state:
    type: string
    required: false
    description: Dunning phase or retry program detail presented to success teams.
  last_reminder_at:
    type: datetime
    required: false
    description: Timestamp of most recent payment reminder issued to customer.
  aging_bucket_days:
    type: integer
    required: false
    description: Number of days outstanding grouped for aging reports.
  attempt_count:
    type: integer
    required: false
    description: >-
      Number of automatic payment attempts made on this invoice (Stripe smart-retries
      attempt_count). Increments on each retry in the dunning window. Source:
      docs.stripe.com/billing/revenue-recovery/smart-retries.
    default: 0
    validation:
      minimum: 0
  next_payment_attempt:
    type: datetime
    required: false
    description: >-
      When the next automatic payment retry is scheduled (Stripe smart-retries
      next_payment_attempt). Defines the open end of the retry window — modeled on the
      Refundable.refundable_until datetime-window precedent. Absent/null when no further
      attempts are scheduled (retries exhausted → invoice goes uncollectible, mirroring
      the subscription `unpaid` state). Source: docs.stripe.com/billing/revenue-recovery/smart-retries.
  memo:
    type: string
    required: false
    description: Internal finance note or customer visible memo.

semantics:
  invoice_number:
    semantic_type: billing.invoice.number
    token_mapping: tokenMap(billing.invoice.number)
  status:
    semantic_type: billing.invoice.status
    token_mapping: tokenMap(billing.invoice.status.*)
  provider_status:
    semantic_type: billing.invoice.provider_status
    token_mapping: tokenMap(billing.invoice.provider_status)
  issued_at:
    semantic_type: billing.invoice.issued_at
    token_mapping: tokenMap(billing.invoice.issued_at)
  due_at:
    semantic_type: billing.invoice.due_at
    token_mapping: tokenMap(billing.invoice.due_at)
  paid_at:
    semantic_type: billing.invoice.paid_at
    token_mapping: tokenMap(billing.invoice.paid_at)
  total_minor:
    semantic_type: billing.invoice.total_minor
    token_mapping: tokenMap(billing.invoice.total_minor)
    ui_hints:
      component: CurrencyAmount
      currencyField: currency
      minorUnits: 100
  balance_minor:
    semantic_type: billing.invoice.balance_minor
    token_mapping: tokenMap(billing.invoice.balance_minor)
    ui_hints:
      component: CurrencyAmount
      currencyField: currency
      minorUnits: 100
  currency:
    semantic_type: billing.invoice.currency
    token_mapping: tokenMap(billing.invoice.currency)
  payment_terms:
    semantic_type: billing.invoice.payment_terms
    token_mapping: tokenMap(billing.invoice.payment_terms)
  collection_state:
    semantic_type: billing.invoice.collection_state
    token_mapping: tokenMap(billing.invoice.collection_state)
  last_reminder_at:
    semantic_type: billing.invoice.last_reminder_at
    token_mapping: tokenMap(billing.invoice.last_reminder_at)
  aging_bucket_days:
    semantic_type: billing.invoice.aging_bucket_days
    token_mapping: tokenMap(billing.invoice.aging_bucket_days)
  attempt_count:
    semantic_type: billing.invoice.attempt_count
    token_mapping: tokenMap(billing.invoice.attempt_count)
  next_payment_attempt:
    semantic_type: billing.invoice.next_payment_attempt
    token_mapping: tokenMap(billing.invoice.next_payment_attempt)
  memo:
    semantic_type: billing.invoice.memo
    token_mapping: tokenMap(billing.invoice.memo)

view_extensions:
  # s222-m03 (#2502 ruling 13): an invoice's row, page header and card show its total, as a financial trait's recipes do.
  list:
    - component: BillingSummaryBadge
      position: after
      props:
        amountField: total_minor
        currencyField: currency
        minorUnits: 100
        showInterval: false
  card:
    - component: BillingSummaryBadge
      position: after
      props:
        amountField: total_minor
        currencyField: currency
        minorUnits: 100
        showInterval: false

tokens:
  billing.invoice.number: "var(--cmp-text-strong)"
  billing.invoice.total_minor: "var(--cmp-text-body-strong)"
  billing.invoice.balance_minor: "var(--cmp-text-warning)"
  billing.invoice.status.past_due: "var(--cmp-status-critical-text)"
  billing.invoice.status.paid: "var(--cmp-status-success-text)"

metadata:
  owners:
    - revenue-ops@oods.systems
  maturity: beta
  regionsUsed:
    - list
    - card
  notes:
    - Canonical status options sourced from r4.5 mapping research.
    - >-
      Dunning/access reconciliation (s126-m05, scoped to the access-revocation need):
      the 9-value statusEnum is a SUPERSET of the canonical 5-state invoice model
      (draft, posted, paid, past_due, void — see docs/billing/lifecycle-states.md).
      Dunning-relevant mapping: `open`/`processing` = pre-dunning (awaiting/settling
      payment); `past_due` = overdue, retries ongoing → subscription past_due (GRACE,
      keep access); `uncollectible` = retries exhausted → mirrors subscription `unpaid`
      (REVOKE access). The retry window is carried by attempt_count + next_payment_attempt.
      The subscription-status → access decision is encoded in
      src/domain/billing/states.ts hasServiceAccess().