recon_gen.common.money

Money as integer cents — the storage-domain currency (Phase AO.1).

SQLite stores DECIMAL columns as REAL (float64), introducing float-dust drift the L1 stored <> computed matview kept flagging (~700 false positives on the sasquatch_pr fixture). Postgres + Oracle DECIMAL stay exact but have to follow for uniform storage — the customer ETL feed contract must be dialect-agnostic.

Use Cents.from_dollars(Decimal('75.00')) at authoring boundaries, Cents.from_db(int_from_cursor) at read boundaries, .to_dollars() for display / rendering. Arithmetic is cents-on-cents only (type-safe); scalar multiplication for repeat-count semantics works (cents * 5).

Mixing with Decimal / float / bare int is intentionally NOT supported — silent coercion would re-introduce the float dust the type was created to eliminate. Pyright catches the cross-domain ops; at runtime they raise AttributeError when the foreign operand is dereferenced for .value.

Classes

Cents(value)

Integer cents — the storage-domain currency type.

class recon_gen.common.money.Cents(value)[source]

Bases: object

Integer cents — the storage-domain currency type.

Frozen + slotted for memory + immutability; order=True so comparisons (< / >= / min / sorted) work without extra boilerplate. The single field value carries the raw integer count of cents (signed; negative for debits / outflows).

Parameters:

value (int)

classmethod from_db(raw)[source]

Read-boundary constructor: int from a DB cursor → Cents.

Explicit int() coerce in case the dbapi driver returns a long-shaped numeric (Oracle’s NUMBER(19) cursors hand back int directly, but defensive). Pairs with cents_to_dollars_sql for the SQL-side projection.

Return type:

Cents

Parameters:

raw (int)

classmethod from_dollars(dollars)[source]

Authoring-boundary constructor: dollars (Decimal / str / int) → integer cents.

Quantizes via Decimal("1") to round-trip without losing sub-cent precision the float path would; e.g. from_dollars("0.01")Cents(1) (not Cents(0)). Always accepts str shape (avoids float-init Decimals like Decimal(0.01) == Decimal('0.01000000000000000020816...')).

Return type:

Cents

Parameters:

dollars (Decimal | str | int)

to_dollars()[source]

Display-boundary projection: cents → Decimal dollars.

Returns a Decimal (not float) so downstream formatters keep exact representation (e.g., f"${c.to_dollars():,.2f}" for $1,234.56 shape).

Return type:

Decimal

value: int