Happy House - Ecommerce Docs
Developer ResourcesShipping

Shipping Module Overview

Flat per-district delivery fees — admin coverage, bulk pricing, and the customer quote.

Audience: Product owners, QA, frontend and backend developers Scope: Delivery pricing and serviceability per district

Shipping Module - Overview

1. What the module is

Shipping prices one flat delivery fee per district. An admin configures each of Nepal's 77 districts with a fee and an active flag; a customer quotes a district to learn the fee and whether delivery is available there.

The module deliberately knows nothing about the address table: the quote takes a districtId, which the client reads off an address's location.district.id. Shipping does not need to — and must not — read customer_address.

2. The two surfaces

SurfaceRoutesAuth
Admin coverage & rates/api/admin/shipping/* (5 routes)JWT + Shipping_* permission
Customer quoteGET /api/mobile/shipping/quote?districtId=JWT

3. The five rules that govern everything

  1. fee is minor units. NPR 120.00 is 12000. Never a decimal. 0 is free delivery, not a missing value.
  2. Unserviceable is a 200, not a 404. { "serviceable": false, "fee": null } is a successful answer — "we do not deliver here" is information, not an error. Only a districtId that names nothing is a 404. Branch on availability.serviceable, never on the status code.
  3. Pausing keeps the fee; deleting discards it. PUT … { "isActive": false } stops delivery while keeping the price for a one-toggle re-enable. DELETE removes the configuration entirely; the district returns to unconfigured.
  4. The rate is an upsert, keyed on the district. district_id is unique, so there is no separate create — calling PUT twice just replaces. The admin UI needs no create-vs-edit branch.
  5. Bulk is all-or-nothing, and a status-only bulk cannot create a configuration (there is no fee to insert). Skipped districts are named in the response — show that list.

4. The three availability flags

The admin coverage list returns all 77 districts whether configured or not:

configuredactiveserviceableMeansAdmin's next action
falsefalsefalseNever pricedSet a fee
truefalsefalsePaused; the fee is keptRe-enable — one toggle
truetruetrueDelivering

Both unserved states look identical to a customer. Only the admin screen distinguishes them.

5. How permissions get granted

Permissions have one source of truth: packages/db/src/authorization/permission-catalog.ts. Both the API's compile-time PermissionCode union and the seed's role grants derive from it, and a spec fails the build if they diverge.

Adding a module means adding its module name to that catalog, then running permissions:sync and db:seed — nothing is granted manually through a screen. The seed grants every module's permissions to admin except System_*; superadmin bypasses the check entirely.

Page guide

PageRead it for
Features and flowsActor journeys, bulk semantics, edge cases
BackendArchitecture, schema, serviceability, cache
APIAll six endpoints, DTOs and error codes