calculatecost4 Data Models¶
Data model classes in calculatecost4/core/models/. Each class is a plain Python dataclass
replacing the ad-hoc dict manipulation in the legacy src/calculateCost2/schema/ code.
Product¶
Module: core/models/product.py
Represents a single line item. Input fields come from the request; injected fields are set by the calculator after price lookup.
Input Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
cprcode |
int |
Yes | -- | Product code; DynamoDB lookup key |
productName |
str |
Yes | -- | Display name |
quantity |
int |
Yes | -- | Units ordered |
branchId |
str |
No | "1000" |
Propagated from Order |
scheduleId |
int |
No | 0 |
Links to shipping schedule slot |
isPreOrder |
bool |
No | false |
Pre-order flag |
remark |
str |
No | "" |
Passthrough |
discountFromCoupon |
float |
No | 0 |
Coupon discount applied to this product |
usedForCoupon |
str \| None |
No | None |
Coupon code that claimed this product |
Injected Fields (set by calculator)¶
| Field | Type | Default | Notes |
|---|---|---|---|
_price |
float |
-- | Current selling price from price table |
_original_price |
float |
-- | Original price before promotions |
_weight |
float |
0.25 |
Product weight in kg |
_is_controlled |
bool |
-- | Whether product is controlled (excluded from discounts) |
Computed Properties¶
| Property | Return Type | Computation |
|---|---|---|
price |
float |
Returns _price |
originalPrice |
float |
Returns _original_price |
rowTotal |
float |
max(price * quantity, 0) |
settlementPrice |
float |
Same as rowTotal |
discountedRowTotal |
float |
max(rowTotal - discountFromCoupon, 0) |
weight |
float |
Returns _weight |
isControlledProduct |
bool |
Returns _is_controlled |
Methods¶
| Method | Behaviour |
|---|---|
clearDiscount() |
Sets discountFromCoupon = 0, usedForCoupon = None |
Serialization: _asdict()¶
Returns:
{
"cprcode": int,
"productName": str,
"quantity": int,
"scheduleId": int,
"isPreOrder": bool,
"originalPrice": float,
"price": float,
"rowTotal": float,
"discountedRowTotal": float,
"settlementPrice": float,
"weight": float,
"isControlledProduct": bool,
}
Input-only fields (branchId, remark, discountFromCoupon, usedForCoupon)
and injected fields (_price, _original_price, _weight, _is_controlled)
are excluded from serialization output.
Order¶
Module: core/models/order.py
Top-level request container. Holds the product list, shipping info, and passthrough metadata fields.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
productList |
list[Product] |
Yes | -- | Line items |
voucherId |
list[str] |
No | [] |
Voucher IDs to apply |
basketName |
str |
No | "" |
Passthrough |
basketId |
str |
No | "" |
Passthrough |
branchId |
str |
No | "1000" |
Propagated to all products |
orderId |
str |
No | "" |
Passthrough |
orderDate |
float |
No | 0 |
Unix timestamp |
ownerId |
str |
No | "" |
Used for voucher ownership checks |
requestSubstitute |
bool |
No | false |
Passthrough |
specialComment |
str \| None |
No | None |
Passthrough |
noPlasticBags |
bool |
No | false |
Passthrough |
shipping |
Shipping \| None |
No | None |
Delivery/pickup configuration |
cartDiscountInfo |
list |
No | [] |
Populated during discount calculation |
couponCodeList |
list[str] |
No | [] |
Coupon codes to validate and apply |
Class Method: from_dict(input_dict)¶
Deserializes a raw request dict into an Order instance:
1. Filters input_dict keys to known field names (unknown keys are dropped).
2. Deserializes shipping value into a Shipping instance.
3. Deserializes each entry in productList into a Product instance.
4. Propagates branchId from Order to every Product in the list.
Method: to_dict()¶
Returns a flat dict containing:
- All input fields
- All computed totals (subTotal, grandTotal, etc.)
- Nested productList serialized via Product._asdict()
- Nested shipping serialized to dict
Shipping¶
Module: core/models/shipping.py
Delivery/pickup configuration attached to an Order.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
scheduleList |
list[Schedule] |
No | [] |
Delivery schedule slots |
shippingType |
ShippingType |
No | PICKUP |
DELIVERY or PICKUP |
shippingPostcode |
str |
No | "10110" |
Used for nationwide rate lookup |
shippingLat |
float |
No | 0 |
Latitude for Bangkok polygon check |
shippingLon |
float |
No | 0 |
Longitude for Bangkok polygon check |
shippingFirstName |
str |
No | "" |
Passthrough |
shippingLastName |
str |
No | "" |
Passthrough |
shippingAddress |
str |
No | "" |
Passthrough |
shippingSubDistrict |
str |
No | "" |
Passthrough |
shippingProvince |
str |
No | "" |
Passthrough |
shippingPhone |
str |
No | "" |
Passthrough |
shippingEmail |
str |
No | "" |
Passthrough |
shippingDate |
float |
No | 0 |
Unix timestamp; passthrough |
shippingPrice |
float |
No | 0 |
Total shipping price (computed) |
weight |
float |
No | 0 |
Total weight of shipment |
brcode |
int |
No | 1000 |
Branch code for polygon lookup |
Schedule¶
Module: core/models/shipping.py
A single delivery schedule slot within a Shipping object.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
mode |
ShippingMode |
No | NATIONWIDE |
Determines fee calculator path |
dateTime |
str |
Yes | -- | ISO-8601 timestamp |
scheduleId |
int |
No | 0 |
Links to Product.scheduleId |
deliveryFee |
float |
No | 0 |
Overwritten by fee calculation |
pickingStatus |
str |
Yes | -- | Passthrough |
date_slot |
str |
Yes | -- | Passthrough |
booking_hour |
int |
No | 0 |
Passthrough |
expressFee |
int |
No | 0 |
Overwritten; 50 THB if EXPRESS and first schedule |
nationwideConfig |
str |
Yes | -- | Set to "none" for non-nationwide modes |
nationwideMode |
NationwideMode |
No | DRY |
Affects nationwide rate table lookup |
Enums¶
Module: core/models/shipping.py
ShippingMode¶
| Value | Usage |
|---|---|
NATIONWIDE |
Nationwide delivery; uses weight-based rate table |
REGULAR |
Standard local delivery |
EXPRESS |
Express delivery; adds 50 THB surcharge |
PICKUP |
In-store pickup; zero delivery fee |
ShippingType¶
| Value | Usage |
|---|---|
DELIVERY |
Order is delivered to address |
PICKUP |
Order is picked up in store |
NationwideMode¶
| Value | Usage |
|---|---|
FRESH |
Fresh/chilled products; higher shipping rate |
DRY |
Dry goods; standard shipping rate |
MIX |
Mixed fresh and dry; uses higher rate |
NationwideCost¶
Module: core/models/shipping.py
A single row from the nationwide shipping rate table.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
maxW |
float |
Yes | -- | Maximum weight threshold for this rate band |
cost |
float |
Yes | -- | Shipping cost for this weight band |
nationwideMode |
NationwideMode |
Yes | -- | Which mode this rate applies to |
Voucher¶
Module: core/models/voucher.py
Represents a voucher (gift card / store credit) that can be applied to an order.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
usedOnOwner |
str \| None |
No | None |
Owner who used the voucher |
usedOnOrder |
list |
No | [] |
Orders this voucher was used on |
isActive |
bool |
No | true |
Whether voucher is active |
isRefund |
bool |
No | false |
Whether this is a refund voucher |
ownerId |
str |
No | "" |
Owner ID for ownership validation |
voucherId |
str |
No | "" |
Unique voucher identifier |
value |
float |
No | 0 |
Monetary value of voucher |
creationDate |
float \| None |
No | None |
Unix timestamp |
useDate |
float \| None |
No | None |
Unix timestamp |
Computed Properties¶
| Property | Return Type | Computation |
|---|---|---|
isUsed |
bool |
len(usedOnOrder) > 0 |
isValid |
bool |
isActive AND NOT isUsed AND checkOwner() |
Validation Methods¶
| Method | Behaviour |
|---|---|
checkValidity() |
Returns false if isActive is false |
checkUsage() |
Returns false if usedOnOrder is non-empty |
checkOwner() |
Returns false if both usedOnOwner and ownerId are set and they differ |
isValid is the composite check: all three validations must pass.
Discount¶
Module: core/models/discount.py
Result of applying a single coupon code. Each field tracks a different discount category.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
coupon |
str |
Yes | -- | The coupon code that produced this discount |
bogoDiscount |
float |
No | 0 |
Buy-one-get-one discount amount |
two4Discount |
float |
No | 0 |
2-for-4 type promotion discount |
discount |
float |
No | 0 |
Flat cart-level discount |
percentageDiscount |
float |
No | 0 |
Percentage-based discount amount |
freeshipping |
bool |
No | false |
Whether this coupon grants free shipping |
shippingDiscount |
float |
No | 0 |
Discount applied to shipping fee |
DiscountResult¶
Module: core/models/discount.py
Aggregated result of all coupon processing for an order.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
discounts |
list[Discount] |
Yes | -- | Successfully applied discounts |
failedCoupons |
list[str] |
Yes | -- | Coupon codes that failed validation |
error |
dict |
Yes | -- | Error details (empty dict on success) |
TotalSummary¶
Module: core/models/summary.py
Final computed totals for the order. Embedded in the response as the summary object.
Fields¶
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
subTotal |
float |
Yes | -- | Sum of all product.rowTotal |
grandTotal |
float |
Yes | -- | Final order total after all adjustments |
voucherDiscount |
float |
Yes | -- | Total voucher discount applied |
cartDiscount |
float |
Yes | -- | Total flat coupon discount |
bogoDiscount |
float |
Yes | -- | Total BOGO discount |
two4Discount |
float |
Yes | -- | Total 2-for-4 discount |
percentageDiscount |
float |
Yes | -- | Total percentage discount |
shippingDiscount |
float |
Yes | -- | Total shipping discount |
totalExcludeControlledProducts |
float |
Yes | -- | Grand total minus controlled product value |
expressShippingCost |
float |
Yes | -- | Total express surcharges |
deliveryFee |
float |
Yes | -- | Total delivery fee before discounts |
discountedDeliveryFee |
float |
Yes | -- | max(deliveryFee - shippingDiscount, 0) |
totalWeight |
float |
Yes | -- | Sum of all product.weight * quantity |
discountResult |
dict |
Yes | -- | Serialized DiscountResult |
Type Aliases¶
Module: core/models/types.py
Semantic type aliases for documentation clarity and type checking.
| Alias | Underlying Type | Usage |
|---|---|---|
Cprcode |
int |
Product code |
Brcode |
int |
Branch code |
CouponCode |
str |
Coupon code string |
CouponId |
int |
Coupon database ID |
Weight |
float |
Weight in kg |
Lat |
float |
Latitude coordinate |
Lon |
float |
Longitude coordinate |
Cost |
float |
Monetary cost value |
Postcode |
int |
Postal code |
Model Dependency Graph¶
Order
+-- productList: list[Product]
+-- shipping: Shipping
+-- scheduleList: list[Schedule]
| +-- mode: ShippingMode (enum)
| +-- nationwideMode: NationwideMode (enum)
+-- shippingType: ShippingType (enum)
DiscountResult
+-- discounts: list[Discount]
TotalSummary
+-- discountResult: dict (serialized DiscountResult)
Voucher (standalone, looked up by voucherId)
NationwideCost (standalone, loaded from rate table)