Known Behaviors¶
Behaviors preserved in calculatecost4 for backward compatibility with calculatecost2. Each behavior is documented with its origin and rationale for preservation.
1. float('inf') on Missing Price¶
Location: getPrice(cprcode, branchId)
When a product's price is not found in DynamoDB, getPrice returns float('inf').
This value propagates through multiplication:
The response contains Infinity in JSON-serialized numeric fields. Standard JSON
parsers reject this value. Python's json.dumps with default settings raises
ValueError; the legacy code uses a custom serializer that emits the raw token.
Impact: Silent corruption of rowTotal, grandTotal, and all downstream totals.
No error is raised. The caller receives a 200 response with unusable financial data.
Preserved because: Existing clients may rely on detecting Infinity to identify
missing-price conditions. Changing this to an error or zero would alter the response
contract.
2. getOriginalPrice Returns 0 on Missing Price¶
Location: getOriginalPrice(cprcode, branchId)
Unlike getPrice which returns float('inf') on a missing price, getOriginalPrice
returns 0. This asymmetry means a product with no price data will have:
Origin: Likely a bug. The two functions were written at different times and never
harmonized. getPrice was written defensively (return a value that makes totals
obviously wrong), while getOriginalPrice was written with a zero-default.
Preserved because: Changing either return value would alter the response for missing-price products.
3. grandTotal Does NOT Subtract two4Discount or percentageDiscount¶
Location: Grand total formula in Order.toDict()
The grand total formula is:
grandTotal = subTotal
+ discountedDeliveryFee
+ expressShippingCost
- cartDiscount
- bogoDiscount
- voucherDiscount
two4Discount and percentageDiscount are computed and present in the response
body but are not subtracted from grandTotal. They appear in totalDiscount
(which is purely informational) but do not affect the final price.
Rationale: The coupon3-checker Lambda may fold these discount types into
cartDiscount before returning them. The separation exists for reporting, not
for calculation. Subtracting them from grandTotal would double-count the discount.
Preserved because: Altering the formula would change grandTotal for orders
with percentage or 2-for-4 coupons.
4. Duplicate scheduleId in Product¶
Location: Legacy src/calculateCost2/schema/product.py
The old Product class declares scheduleId twice:
In Python dataclasses, the second declaration overwrites the first. The effective
default is 0, not None.
New code: Declares scheduleId once as int = 0. Behaviorally identical to
the legacy code's effective default.
Preserved because: Any test or client that relies on scheduleId defaulting
to 0 (rather than None) must continue to work.
5. Express Fee Hardcoded to 50 THB¶
Location: Shipping fee calculation in ShippingCalculator
The first schedule with mode=EXPRESS receives expressFee = 50. This value is
hardcoded, not configurable, and not read from any database or configuration table.
Only the first EXPRESS schedule gets the fee. Subsequent EXPRESS schedules in
the same order get expressFee = 0.
Preserved because: The 50 THB value is baked into test assertions and client expectations. Changing it requires coordination with the business team.
6. Weight Default is 0.25 Per Unit¶
Location: Product enrichment step
Products without weight data in DynamoDB default to 0.25 kg per unit. The weight
is then multiplied by quantity:
The or fallback triggers on both None (missing key) and 0 (explicit zero weight).
A product with an explicit weight of 0 in DynamoDB will be treated as 0.25.
Preserved because: Nationwide shipping fees depend on weight. Defaulting to zero would make nationwide shipping free for products with missing weight data.
7. shippingType Default is PICKUP¶
Location: Shipping.from_dict() and ShippingType enum default
When no shippingType is provided in the request, it defaults to PICKUP.
When shippingType is PICKUP, all schedule delivery fees are set to 0
regardless of schedule mode.
if shipping.shippingType == PICKUP:
for schedule in scheduleList:
schedule.deliveryFee = 0
schedule.expressFee = 0
Impact: An order submitted without an explicit shippingType field will have
zero delivery fees, even if schedules with REGULAR or EXPRESS modes are present.
Preserved because: Existing clients that omit shippingType expect zero fees.
Changing the default to DELIVERY would introduce unexpected charges.