Accrue.Invoices.RenderContext (accrue v0.3.1)

Copy Markdown View Source

Format-neutral hydrated invoice payload (D6-04).

Built once per render by Accrue.Invoices.Render.build_assigns/2 and passed through BOTH the email body (via <mj-raw> + HtmlBridge) and the PDF shell (via Accrue.Invoices.Layouts.print_shell/1). This guarantees every format sees byte-identical money strings and the same frozen branding snapshot.

Branding snapshot freeze (Pitfall 8)

The :branding field holds a keyword list captured at build time from Accrue.Config.branding/0. It MUST NOT be re-read downstream — if a component needs branding it reads ctx.branding. This prevents config drift between the email header, the PDF header, and the footer within a single render.

Pre-formatted fields

Money + date fields are pre-formatted into formatted_* strings at build time (CLDR calls live off the hot template path). Components read ctx.formatted_total directly instead of calling format_money/3 from inside ~H.

Summary

Types

t()

@type t() :: %Accrue.Invoices.RenderContext{
  branding: keyword(),
  currency: atom(),
  customer: term(),
  discount_minor: integer() | nil,
  formatted_discount: String.t() | nil,
  formatted_issued_at: String.t() | nil,
  formatted_subtotal: String.t() | nil,
  formatted_tax: String.t() | nil,
  formatted_total: String.t() | nil,
  hosted_invoice_url: String.t() | nil,
  invoice: term(),
  line_items: [term()] | nil,
  locale: String.t(),
  now: DateTime.t() | nil,
  receipt_url: String.t() | nil,
  subtotal_minor: integer() | nil,
  tax_minor: integer() | nil,
  timezone: String.t(),
  total_minor: integer() | nil
}