Skip to content

Business Logic: Processing Pipeline

Complete processing pipeline for calculatecost4 with exact formulas from source code.


1. Parse Input

Event body is parsed from JSON string to dict. Order.fromDict filters input keys to only those matching dataclass fields.

  • Products deserialized via Product.from_dict
  • Shipping deserialized via Shipping.from_dict
  • branchId from order is propagated to every product

2. Enrich Products

For each product in the order:

price = PricingService.get_price(cprcode, branchId)
        # If not found: float('inf')

originalPrice = PricingService.get_original_price(cprcode, branchId)
                # If not found: 0

weight = ProductLookupService.get_product(cprcode).get('weight') or 0.25
weight = weight * quantity

isControlledProduct = cprcode in ControlledProductService.get_controlled_product_list()

rowTotal = max(price * quantity, 0)

settlementPrice = rowTotal

discountedRowTotal = max(rowTotal - discountFromCoupon, 0)

3. Calculate Weights

Weights are accumulated per schedule, then summed for the total.

weights[scheduleId] = sum(product.weight for products in that schedule)

totalWeight = sum(weights)

4. Calculate Shipping

For each schedule in scheduleList:

if mode == EXPRESS and schedule is first:
    expressFee = 50  # THB

if mode != NATIONWIDE:
    nationwideConfig = "none"

Per-schedule fee via ShippingCalculator:

PICKUP:     fee = 0
REGULAR:    fee = ShippingPriceService.get_regular_price(lat, lon, brcode)
EXPRESS:    fee = ShippingPriceService.get_regular_price(lat, lon, brcode)
NATIONWIDE: fee = ShippingPriceService.get_nationwide_price(postcode, weight, nationwideMode)

The regular/express price uses the Bangkok polygon API.

Aggregation:

deliveryFee = sum(schedule fees)

expressShippingCost = first schedule's expressFee
                      # 0 if no schedules

shippingPrice = shippingFee = sum(schedule fees)

5. Apply Coupons

Runs only if couponCodeList is non-empty.

Build CouponOrder payload:

CouponOrder(
    products      = [product.toDict() for each product],
    branchId      = order.branchId,
    ownerId       = order.ownerId,
    shipping      = order.shipping,
    couponCodeList = order.couponCodeList,
    subTotal      = 0,
    grandTotal    = 0,
    totalExcludeControlledProducts = 0,
    expressShippingCost = expressShippingCost,
    deliveryFee   = 0,
    totalWeight   = totalWeight,
)

Call CouponService.check_coupons(order_dict) which returns a DiscountResult.

DiscountResult.discounts is a list of Discount, each containing:

Field Type
coupon str
discount float
bogoDiscount float
two4Discount float
percentageDiscount float
freeshipping bool
shippingDiscount float

Aggregation formulas:

cartDiscount       = sum(d.discount for d in discounts)
bogoDiscount       = sum(d.bogoDiscount for d in discounts)
two4Discount       = sum(d.two4Discount for d in discounts)

percentageDiscount = max(d.percentageDiscount for d in discounts) * subTotal
                     # 0 if no discounts

freeShipping       = any(d.freeshipping for d in discounts)

calculatedShippingDiscount = sum(d.shippingDiscount for d in discounts)

shippingDiscount = deliveryFee                              if freeShipping
                   min(calculatedShippingDiscount, deliveryFee)  otherwise

6. Apply Vouchers

For each voucherId in the order:

voucher = VoucherService.get_voucher(voucherId)
voucher.usedOnOwner = order.ownerId

Validity check (all must be true):

voucher.isActive == True
voucher.usedOnOrder is empty       # not already redeemed
owner matches                      # if both voucher.usedOnOwner and order.ownerId are set

Aggregation:

voucherDiscount = sum(abs(voucher.value) for valid vouchers)

7. Calculate Totals

subTotal = max(sum(product.rowTotal for all products), 0)

discountedDeliveryFee = max(deliveryFee - shippingDiscount, 0)

grandTotal = max(
    subTotal
    + discountedDeliveryFee
    + expressShippingCost
    - cartDiscount
    - bogoDiscount
    - voucherDiscount,
    0
)

totalExcludeControlledProducts = grandTotal - sum(
    product.rowTotal for controlled products
)

totalDiscount = cartDiscount
              + bogoDiscount
              + two4Discount
              + voucherDiscount
              + percentageDiscount

8. Serialize

Order.toDict() outputs a flat dict containing all computed scalar fields plus nested structures:

  • productList -- enriched product dicts
  • shipping -- shipping object
  • validVouchers -- list of vouchers that passed validity
  • invalidVouchers -- list of vouchers that failed validity
  • discountResult -- full DiscountResult from coupon service
  • summary -- TotalSummary with final totals