Accrue.Billing.InvoiceActions (accrue v0.3.1)

Copy Markdown View Source

Phase 3 Plan 05 invoice write surface (BILL-17/18/19).

Exposes the five user-path invoice actions on Accrue.Billing via the Plan 01 defdelegate facade: finalize_invoice, void_invoice, pay_invoice, mark_uncollectible, send_invoice. Each follows the D3-18 one-shape Repo.transact/2 pattern:

telemetry.span
  |> Repo.transact (
       Processor.<op>(stripe_id, opts)
       |> InvoiceProjection.decompose/1
       |> update row via Invoice.changeset/2 (user path, enforces transitions)
       |> upsert child items by stripe_id
       |> Events.record/1 (same transaction  EVT-04)
     )
  |> IntentResult.wrap (pay_invoice only  may surface SCA)

pay_invoice/2 returns an intent_result tagged union because Stripe may surface SCA/3DS; the other four return plain {:ok, %Invoice{}}. Every action has a bang variant that raises on {:error, _}; pay_invoice!/2 additionally raises Accrue.ActionRequiredError on :requires_action.

The webhook path uses the force-status bypass on the Invoice schema — that bypass is NOT reachable from this module. Illegal user-path transitions (e.g. draft -> paid) are rejected by Invoice.changeset/2 with an error on :status and propagate as {:error, %Ecto.Changeset{}}.

Summary

Functions

finalize_invoice(inv, opts \\ [])

@spec finalize_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

finalize_invoice!(inv, opts \\ [])

@spec finalize_invoice!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()

mark_uncollectible(inv, opts \\ [])

@spec mark_uncollectible(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

mark_uncollectible!(inv, opts \\ [])

@spec mark_uncollectible!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()

pay_invoice(inv, opts \\ [])

@spec pay_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) ::
  {:ok, Accrue.Billing.Invoice.t()}
  | {:ok, :requires_action, map()}
  | {:error, term()}

pay_invoice!(inv, opts \\ [])

send_invoice(inv, opts \\ [])

@spec send_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

send_invoice!(inv, opts \\ [])

@spec send_invoice!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()

void_invoice(inv, opts \\ [])

@spec void_invoice(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: {:ok, Accrue.Billing.Invoice.t()} | {:error, term()}

void_invoice!(inv, opts \\ [])

@spec void_invoice!(
  Accrue.Billing.Invoice.t(),
  keyword()
) :: Accrue.Billing.Invoice.t()