|null $meta Meta. * @param DateTimeImmutable $created_at Created UTC. * @param DateTimeImmutable $updated_at Updated UTC. * * @throws \Bookline\Domain\Shared\DomainException When invariants fail. */ public function __construct( private ?int $id, private Uuid $uuid, private int $customer_id, private OrderStatus $status, private Money $subtotal, private Money $discount, private Money $tax, private Money $total, private Money $paid, private ?int $coupon_id, private ?OrderSource $source, private ?string $ip_hash, private ?string $user_agent_hash, private ?array $meta, private DateTimeImmutable $created_at, private DateTimeImmutable $updated_at, ) { if ( $this->customer_id < 1 ) { throw new DomainException( 'order.customer_id', 'customer_id must be positive.' ); } $currency = $this->total->currency(); foreach ( [ $this->subtotal, $this->discount, $this->tax, $this->paid ] as $m ) { if ( $m->currency() !== $currency ) { throw new DomainException( 'order.currency_mismatch', 'All order money fields must share a currency.' ); } } if ( $this->subtotal->is_negative() || $this->discount->is_negative() || $this->tax->is_negative() || $this->total->is_negative() || $this->paid->is_negative() ) { throw new DomainException( 'order.negative_money', 'Order money fields must not be negative.' ); } if ( $this->paid->amount_minor() > $this->total->amount_minor() ) { throw new DomainException( 'order.paid_exceeds_total', 'paid must not exceed total.' ); } } /** * Database id. * * @since 0.1.0 */ public function id(): ?int { return $this->id; } /** * Public UUID. * * @since 0.1.0 */ public function uuid(): Uuid { return $this->uuid; } /** * Customer id. * * @since 0.1.0 */ public function customer_id(): int { return $this->customer_id; } /** * Status. * * @since 0.1.0 */ public function status(): OrderStatus { return $this->status; } /** * Subtotal. * * @since 0.1.0 */ public function subtotal(): Money { return $this->subtotal; } /** * Discount. * * @since 0.1.0 */ public function discount(): Money { return $this->discount; } /** * Tax. * * @since 0.1.0 */ public function tax(): Money { return $this->tax; } /** * Total. * * @since 0.1.0 */ public function total(): Money { return $this->total; } /** * Paid. * * @since 0.1.0 */ public function paid(): Money { return $this->paid; } /** * ISO currency code. * * @since 0.1.0 */ public function currency(): string { return $this->total->currency(); } /** * Coupon id. * * @since 0.1.0 */ public function coupon_id(): ?int { return $this->coupon_id; } /** * Source. * * @since 0.1.0 */ public function source(): ?OrderSource { return $this->source; } /** * Ip hash. * * @since 0.1.0 */ public function ip_hash(): ?string { return $this->ip_hash; } /** * User agent hash. * * @since 0.1.0 */ public function user_agent_hash(): ?string { return $this->user_agent_hash; } /** * Opaque meta payload. * * @return array|null * * @since 0.1.0 */ public function meta(): ?array { return $this->meta; } /** * Created at. * * @since 0.1.0 */ public function created_at(): DateTimeImmutable { return $this->created_at; } /** * Updated at. * * @since 0.1.0 */ public function updated_at(): DateTimeImmutable { return $this->updated_at; } /** * Assign the database id after insert. * * @param int $id New id. * * @throws \Bookline\Domain\Shared\DomainException When id is already set or invalid. * * @since 0.1.0 */ public function assign_id( int $id ): void { if ( null !== $this->id ) { throw new DomainException( 'order.id_assigned', 'Order id is already assigned.' ); } $this->id = $id; } }