Skip to content

Usage Guide

Calling the API

villaCalculateCost4 accepts POST requests with a JSON body and returns an enriched order with complete financial breakdowns.

Endpoint:

POST https://api.villaecommerce.com/calculatecost4
Content-Type: application/json

Minimal request:

curl -X POST https://api.villaecommerce.com/calculatecost4 \
  -H "Content-Type: application/json" \
  -d '{
    "productList": [
      {"cprcode": 57822, "productName": "Olive Oil", "quantity": 1}
    ],
    "branchId": "1049"
  }'

Minimal response structure:

{
  "productList": [
    {
      "cprcode": 57822,
      "productName": "Olive Oil",
      "quantity": 1,
      "scheduleId": 0,
      "isPreOrder": false,
      "originalPrice": 299.0,
      "price": 299.0,
      "rowTotal": 299.0,
      "discountedRowTotal": 299.0,
      "settlementPrice": 299.0,
      "weight": 0.75,
      "isControlledProduct": false
    }
  ],
  "branchId": "1049",
  "basketName": "",
  "basketId": "",
  "orderId": "",
  "orderDate": 0,
  "ownerId": "",
  "requestSubstitute": false,
  "specialComment": null,
  "noPlasticBags": false,
  "couponCodeList": [],
  "voucherId": [],
  "shipping": null,
  "subTotal": 299.0,
  "grandTotal": 299.0,
  "totalWeight": 0.75,
  "deliveryFee": 0,
  "discountedDeliveryFee": 0,
  "expressShippingCost": 0,
  "shippingFee": 0,
  "shippingPrice": 0,
  "cartDiscount": 0,
  "bogoDiscount": 0,
  "two4Discount": 0,
  "percentageDiscount": 0,
  "shippingDiscount": 0,
  "voucherDiscount": 0,
  "totalDiscount": 0,
  "totalExcludeControlledProducts": 299.0,
  "cartDiscountInfo": [],
  "validVouchers": [],
  "invalidVouchers": [],
  "discountResult": {},
  "summary": {
    "subTotal": 299.0,
    "grandTotal": 299.0,
    "voucherDiscount": 0,
    "cartDiscount": 0,
    "bogoDiscount": 0,
    "two4Discount": 0,
    "percentageDiscount": 0,
    "shippingDiscount": 0,
    "totalExcludeControlledProducts": 299.0,
    "expressShippingCost": 0,
    "deliveryFee": 0,
    "discountedDeliveryFee": 0,
    "totalWeight": 0.75,
    "discountResult": {}
  }
}

Request Body

Required Fields

Field Type Description
productList array List of product line items. Must contain at least one product.

Each product in productList requires:

Field Type Required Description
cprcode int Yes Product code used for DynamoDB price lookup
productName str Yes Display name of the product
quantity int Yes Number of units ordered

Optional Fields

Field Type Default Description
branchId str "1000" Branch code. Propagated to every product for price lookup.
orderId str "" Order identifier (passthrough)
orderDate float 0 Unix timestamp (passthrough)
ownerId str "" Owner identifier for voucher ownership validation
basketName str "" Basket name (passthrough)
basketId str "" Basket identifier (passthrough)
requestSubstitute bool false Substitution flag (passthrough)
specialComment str null Special instructions (passthrough)
noPlasticBags bool false Plastic bag preference (passthrough)
couponCodeList array[str] [] Coupon codes to validate and apply
voucherId array[str] [] Voucher IDs to validate and apply
shipping object null Shipping/delivery configuration (see below)

Optional Product Fields

Field Type Default Description
scheduleId int 0 Links product to a delivery schedule slot
isPreOrder bool false Pre-order flag (passthrough)
remark str "" Product-level remark (passthrough)

Shipping Object

Field Type Default Description
shippingType str "PICKUP" "DELIVERY" or "PICKUP"
scheduleList array [] List of delivery schedule slots
shippingPostcode str "10110" Postcode for nationwide rate lookup
shippingLat float 0 Latitude for Bangkok polygon check
shippingLon float 0 Longitude for Bangkok polygon check
shippingFirstName str "" Recipient first name (passthrough)
shippingLastName str "" Recipient last name (passthrough)
shippingAddress str "" Delivery address (passthrough)
shippingSubDistrict str "" Sub-district (passthrough)
shippingProvince str "" Province (passthrough)
shippingPhone str "" Contact phone (passthrough)
shippingEmail str "" Contact email (passthrough)
shippingDate float 0 Delivery date as Unix timestamp (passthrough)
brcode int 1000 Branch code for polygon lookup

Schedule Object (within scheduleList)

Field Type Default Description
mode str "NATIONWIDE" Shipping mode: "REGULAR", "EXPRESS", "NATIONWIDE", "PICKUP"
dateTime str -- ISO-8601 timestamp
scheduleId int 0 Links to products via Product.scheduleId
pickingStatus str -- Picking status (passthrough)
date_slot str -- Date slot (passthrough)
booking_hour int 0 Booking hour (passthrough)
nationwideConfig str -- Set to "none" for non-nationwide modes
nationwideMode str "DRY" Nationwide mode: "DRY", "FRESH", "MIX"

Response Body

The response is a flat JSON object with 28 top-level keys plus nested structures.

Scalar Fields

Field Type Description
branchId str Branch code from the request
basketName str Passthrough from request
basketId str Passthrough from request
orderId str Passthrough from request
orderDate float Passthrough from request
ownerId str Passthrough from request
requestSubstitute bool Passthrough from request
specialComment str Passthrough from request
noPlasticBags bool Passthrough from request
subTotal float Sum of all product rowTotal values
grandTotal float Final order total after all adjustments
totalWeight float Sum of all product weights (weight * quantity)
deliveryFee float Total delivery fee before discounts
discountedDeliveryFee float max(deliveryFee - shippingDiscount, 0)
expressShippingCost float Express surcharge (50 THB if EXPRESS mode, else 0)
shippingFee float Same as deliveryFee
shippingPrice float Same as deliveryFee
cartDiscount float Sum of flat cart-level coupon discounts
bogoDiscount float Sum of buy-one-get-one discounts
two4Discount float Sum of two-for discounts
percentageDiscount float Maximum percentage discount multiplied by subTotal
shippingDiscount float Total shipping discount from coupons
voucherDiscount float Sum of valid voucher values
totalDiscount float Sum of cartDiscount + bogoDiscount + two4Discount + voucherDiscount + percentageDiscount
totalExcludeControlledProducts float grandTotal minus controlled product rowTotals

Grand Total Formula

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

Nested Structures

productList (array)

Each product in the response contains enriched data:

Field Type Description
cprcode int Product code
productName str Display name
quantity int Units ordered
scheduleId int Linked schedule slot
isPreOrder bool Pre-order flag
originalPrice float Original/list price from price database
price float Current selling price from price database
rowTotal float max(price * quantity, 0)
discountedRowTotal float max(rowTotal - discountFromCoupon, 0)
settlementPrice float Same as rowTotal
weight float Product weight in kg (default 0.25 if not found)
isControlledProduct bool Whether product is on the controlled list

shipping (object or null)

Contains the shipping configuration with computed delivery fees. Each schedule in scheduleList includes a deliveryFee and expressFee set by the calculator.

couponCodeList (array)

Passthrough of the coupon codes from the request.

voucherId (array)

Passthrough of the voucher IDs from the request.

validVouchers (array)

List of voucher objects that passed all validity checks (active, unused, owner matches).

invalidVouchers (array)

List of voucher objects that failed one or more validity checks.

discountResult (object)

Full result from the coupon service. Contains:

Field Type Description
discounts array List of applied discount objects
failedCoupons array[str] Coupon codes that failed validation
error object Error details (empty dict on success)

Each discount object in discounts:

Field Type Description
coupon str The coupon code
discount float Flat cart-level discount amount
bogoDiscount float Buy-one-get-one discount amount
two4Discount float Two-for discount amount
percentageDiscount float Percentage discount multiplier
freeshipping bool Whether this coupon grants free shipping
shippingDiscount float Shipping discount amount

cartDiscountInfo (array)

Coupon application details. May contain JSON-encoded strings with per-coupon breakdown data.

summary (object)

Aggregated financial totals:

Field Type Description
subTotal float Sum of all product rowTotals
grandTotal float Final order total
voucherDiscount float Total voucher discount
cartDiscount float Total flat coupon discount
bogoDiscount float Total BOGO discount
two4Discount float Total two-for discount
percentageDiscount float Total percentage discount
shippingDiscount float Total shipping discount
totalExcludeControlledProducts float Grand total minus controlled products
expressShippingCost float Total express surcharges
deliveryFee float Total delivery fee before discounts
discountedDeliveryFee float Delivery fee after shipping discount
totalWeight float Total order weight
discountResult object Serialized DiscountResult

Example: Simple Order

A minimal order with a single product and no shipping.

curl -X POST https://api.villaecommerce.com/calculatecost4 \
  -H "Content-Type: application/json" \
  -d '{
    "productList": [
      {
        "cprcode": 57822,
        "productName": "Olive Oil",
        "quantity": 1
      }
    ],
    "branchId": "1049"
  }'

The response will include the product enriched with its current selling price, original price, weight, and computed totals. grandTotal equals subTotal since there is no shipping, coupons, or vouchers.


Example: With Shipping

A delivery order with REGULAR shipping to a Bangkok address.

curl -X POST https://api.villaecommerce.com/calculatecost4 \
  -H "Content-Type: application/json" \
  -d '{
    "productList": [
      {
        "cprcode": 146098,
        "productName": "Ice Cream",
        "quantity": 2,
        "scheduleId": 1
      }
    ],
    "branchId": "1023",
    "shipping": {
      "shippingType": "DELIVERY",
      "shippingLat": 13.73204799,
      "shippingLon": 100.56762289,
      "brcode": 1023,
      "scheduleList": [
        {
          "scheduleId": 1,
          "mode": "REGULAR",
          "dateTime": "2024-04-13T10:00:00.000Z",
          "pickingStatus": "",
          "date_slot": "",
          "nationwideConfig": "none",
          "nationwideMode": "DRY"
        }
      ]
    }
  }'

The response will include deliveryFee set by the Bangkok polygon API, shippingFee and shippingPrice matching the delivery fee, and grandTotal equal to subTotal + deliveryFee.


Example: With Coupons

An order with coupon codes applied.

curl -X POST https://api.villaecommerce.com/calculatecost4 \
  -H "Content-Type: application/json" \
  -d '{
    "productList": [
      {
        "cprcode": 57822,
        "productName": "Olive Oil",
        "quantity": 3
      }
    ],
    "branchId": "1049",
    "couponCodeList": ["SAVE20"],
    "ownerId": "user-12345"
  }'

The response will include the discountResult object with per-coupon discount breakdowns. If the coupon is valid, cartDiscount, bogoDiscount, shippingDiscount, or other discount fields will be populated. grandTotal will reflect the applied discounts. Invalid coupons appear in discountResult.failedCoupons.


Example: Pickup Order

An in-store pickup order with zero shipping cost.

curl -X POST https://api.villaecommerce.com/calculatecost4 \
  -H "Content-Type: application/json" \
  -d '{
    "productList": [
      {
        "cprcode": 57822,
        "productName": "Olive Oil",
        "quantity": 1
      },
      {
        "cprcode": 146098,
        "productName": "Ice Cream",
        "quantity": 2
      }
    ],
    "branchId": "1049",
    "shipping": {
      "shippingType": "PICKUP"
    }
  }'

The response will have deliveryFee, shippingFee, shippingPrice, and expressShippingCost all equal to 0. grandTotal equals subTotal.


Error Handling

The API currently returns errors as HTTP 500 responses with an error field in the JSON body.

{
  "error": "Branch 9999 is not available for delivery to the specified location"
}

There are no structured error codes in the current version. All error conditions produce a 500 status with a human-readable error message string.

Common error scenarios:

Scenario Error Message Pattern
Product price not found in database "No price for {cprcode}/{brcode}"
Branch not in delivery polygon "Branch {brcode} not in polygon" or "Branch {brcode} is not available"
Nationwide Lambda invocation failure Lambda error details in message
Coupon service failure Lambda error details in message
Voucher not found "Voucher {id} not found"
Invalid JSON body "Failed to parse request body"

Planned improvement: Future versions will add structured error codes (e.g., BRANCH_NOT_AVAILABLE, PRODUCT_PRICE_NOT_FOUND, VALIDATION_ERROR) with HTTP 400/422 status codes for client errors and 502 for downstream service failures.


Backward Compatibility

The /calculatecost2 and /calculatecost3 routes remain operational and produce identical behavior to their original implementations. Existing clients using those endpoints do not need to migrate.

All three endpoints accept the same request format with camelCase field names:

Endpoint Status
/calculatecost2 Active, original implementation
/calculatecost3 Active, original implementation
/calculatecost4 Active, new implementation (this project)

The /calculatecost4 endpoint accepts the same camelCase input format as the legacy endpoints and returns responses in the same structure. This was verified through 27 integration tests comparing field-by-field output between the new and legacy endpoints using 10 real paid production orders.


Using the Test UI

An interactive browser-based testing interface is available at https://test.villaecommerce.com.

The Test UI provides:

  • A JSON editor for constructing request payloads
  • Pre-built example orders for common scenarios
  • One-click submission to the /calculatecost4 endpoint
  • Formatted response display with syntax highlighting
  • Side-by-side comparison with legacy endpoint responses

Use the Test UI to experiment with different order configurations, shipping modes, coupon codes, and voucher IDs without writing any code or using the command line.