Skip to content

Payment case

Payments often use different representations in different parts of an application. This example keeps the amount in cents on one side and converts it to dollars on the other. The reverse direction has its own conversion back to cents. Both directions receive the keyword-only ctx context, which supplies the minor-unit policy and currency.

Example

from dataclasses import dataclass

from betwixt import Betwixt, field_refs, map_leftward, map_rightward, reduce_rightward

@dataclass
class Payment:
    cents: int

@dataclass
class PaymentView:
    dollars: float
    currency: str

left, right = field_refs(Payment, PaymentView)

class PaymentMapping(Betwixt):
    left = Payment
    right = PaymentView
    dollars = map_rightward(left=left.cents, right=right.dollars,
                            rightward=lambda cents, *, ctx: cents / ctx["minor_units"])
    currency = reduce_rightward(right=right.currency,
                                rightward=lambda _payment, *, ctx: ctx["currency"])
    cents = map_leftward(right=right.dollars, left=left.cents,
                         leftward=lambda dollars, *, ctx: round(dollars * ctx["minor_units"]))

mapping = PaymentMapping()
context = {"minor_units": 100, "currency": "USD"}
right = mapping.rightward(Payment(1210), context=context)
left = mapping.leftward(PaymentView(12.10, "USD"), context=context)
patch = mapping.leftward_partial({"dollars": 12.10}, context=context)

assert right.dollars == 12.10
assert right.currency == "USD"
assert left == Payment(1210)
assert patch == {"cents": 1210}