Shipping Device DB Reference Migration Plan
This document continues from the Shipping Device DB Reference Plan into the SQL migration design level. The focus is the order of migration files, the contents of each file, the primary constraints, and the design decisions that must be preserved before final implementation under kesles_reference/reference_database/sql/ (the standalone reference repo, db_reference).
Status (2026-06-23): shipped. Migrations
015–022(plus themerchantschema, seeds, and later coverage/rate expansion migrations) are applied inkesles_reference/reference_database/sql/.
Companion documents:
- Shipping Device DB Reference Plan
- Shipping Device Seed Checklist (moved →
merchant_docs/docs/development/shipping-device-seed-checklist.md)
Goal
The goal of this migration plan is to:
- translate the device shipping policy into a reference table structure ready to implement
- keep naming and schema patterns consistent with the
merchantschema indb_reference - ensure that rate, coverage, and fallback data can be managed without hardcoding
General Migration Principles
- All new tables live in the
merchantschema. - Table names follow the
merchant.ref_*pattern. - Migrations are additive and backward-compatible.
- Initial seed data is separated from the schema migration as much as possible.
- Constraints are used to block invalid data from the start.
Recommended File Numbering
After the latest current file 014_create_ref_mcc_code.sql, the recommended file order is:
015_create_ref_shipping_partner.sql016_create_ref_shipping_service.sql017_create_ref_shipping_origin.sql018_create_ref_shipping_destination_region_group.sql019_create_ref_shipping_partner_service.sql020_create_ref_shipping_partner_coverage.sql021_create_ref_shipping_rate.sql022_create_ref_shipping_fallback_rule.sql
If we want to separate the basic reference seeds, the seed files can start after that, for example:
023_seed_ref_shipping_service.sql024_seed_ref_shipping_partner.sql
But if this repo has historically mixed create and seed in a single file for certain references, the final approach can be adjusted. For the shipping use case, I still recommend keeping schema and seed separate.
015 Create Shipping Partner
Table name
merchant.ref_shipping_partner
Function
Stores the master list of courier partners allowed by the system.
Core columns
shipping_partner_ididentity primary keypartner_codevarchar uniquepartner_namevarcharpriority_ranksmallintis_primarybooleansupports_regularbooleansupports_cargobooleanis_activebooleannotestextcreated_atupdated_at
Primary constraints
partner_codeuniquepriority_rank > 0- if
is_primary = true, only one active primary partner is allowed at a time
Implementation notes:
- the uniqueness for a single active primary can be handled with a partial unique index
priority_rankis still stored even though there is a fallback table, because it is useful for default ordering
016 Create Shipping Service
Table name
merchant.ref_shipping_service
Function
Stores the internal service categories the system recognizes.
Initial values
regularcargo
Core columns
shipping_service_ididentity primary keyservice_codevarchar uniqueservice_namevarcharsort_ordersmallintis_activebooleancreated_atupdated_at
Primary constraints
service_codeuniqueservice_coderestricted to lowercase snake_case or lowercase plain code
017 Create Shipping Origin
Table name
merchant.ref_shipping_origin
Function
Stores shipping origin nodes. The initial phase still uses Makassar, but this model is prepared for possible multi-origin support later.
Core columns
shipping_origin_ididentity primary keyorigin_codevarchar uniqueorigin_namevarcharcountry_codeprovince_codeor a foreign key reference to the region table when availablecity_codeor a foreign key reference to the region table when availableis_defaultbooleanis_activebooleancreated_atupdated_at
Primary constraints
origin_codeunique- at most one active default origin
018 Create Shipping Destination Region Group
Table name
merchant.ref_shipping_destination_region_group
Function
Stores destination groupings for the shipping policy.
Initial values
SULAWESINON_SULAWESI
Core columns
shipping_destination_region_group_ididentity primary keydestination_city_codeor FK to the city referenceregion_group_coderegion_group_nameis_activecreated_atupdated_at
Primary constraints
- a single destination may only have one active
region_group_code region_group_codeis restricted by an initial enum check:SULAWESI,NON_SULAWESI
Notes:
- the choice between
destination_city_codeand a final FK depends on the source-of-truth region table already available indb_reference
019 Create Shipping Partner Service
Table name
merchant.ref_shipping_partner_service
Function
Stores the mapping between an internal service and the partner's service code.
Core columns
shipping_partner_service_ididentity primary keyshipping_partner_idFK toref_shipping_partnershipping_service_idFK toref_shipping_servicepartner_service_codepartner_service_nameis_activecreated_atupdated_at
Primary constraints
- the combination
shipping_partner_id + partner_service_codeis unique - the combination
shipping_partner_id + shipping_service_id + partner_service_codeis unique if we want to be more explicit
020 Create Shipping Partner Coverage
Table name
merchant.ref_shipping_partner_coverage
Function
Determines whether a given partner serves a given destination for a given service from a given origin.
Core columns
shipping_partner_coverage_ididentity primary keyshipping_partner_idFKshipping_service_idFKshipping_origin_idFKdestination_city_codeor final FKregion_group_codeis_availablesla_min_dayssla_max_dayseffective_fromeffective_tois_activecoverage_notescreated_atupdated_at
Primary constraints
sla_min_days >= 0sla_max_days >= sla_min_days- active periods may not overlap for the same combination of partner, service, origin, and destination
Notes:
- active overlap usually requires an exclusion constraint or must be enforced at the application layer
- to keep things simple at the start, use a unique combination on active data + disciplined update process
021 Create Shipping Rate
Table name
merchant.ref_shipping_rate
Function
Stores the master shipping rate for a combination of partner, service, origin, and destination.
Core columns
shipping_rate_ididentity primary keyshipping_partner_idFKshipping_service_idFKshipping_origin_idFKdestination_city_codeor final FKcurrencyprice_amountprice_unitmin_billable_weight_kgweight_increment_kgrounding_rule_codesla_min_dayssla_max_dayseffective_fromeffective_tois_activerate_sourcerate_notescreated_atupdated_at
Primary constraints
price_amount >= 0min_billable_weight_kg > 0weight_increment_kg > 0sla_max_days >= sla_min_daysprice_unitis restricted to initial values, e.g.per_kg,flat_per_shipmentrounding_rule_codeis restricted to initial values, e.g.ceil_kg,floor_kg,nearest_kg
Design notes
For the initial phase, the safest design:
- all rates use
currency = 'IDR' - the majority of rates use
price_unit = per_kg - weight rounding uses
ceil_kg
This model is still flexible enough without being overly complex.
022 Create Shipping Fallback Rule
Table name
merchant.ref_shipping_fallback_rule
Function
Stores the fallback partner ordering by origin, region group, and internal service.
Core columns
shipping_fallback_rule_ididentity primary keyshipping_origin_idFKregion_group_codeshipping_service_idFKsequence_noshipping_partner_idFKfallback_reason_codeis_activecreated_atupdated_at
Primary constraints
sequence_no > 0- the combination
origin + region_group_code + service + sequence_nois unique region_group_codeis restricted to the initial enum
Design notes
fallback_reason_code can be optional in the initial phase. The value is more useful as a policy label than as a logic determiner.
Region Reference
Before final SQL implementation, we must first decide the source of truth for the region data:
Realistic options:
- refer directly to the city/regency table already in
db_reference - store
destination_city_codebased on the canonical region code
This decision affects:
- the FK column type
- city/regency seeds
- the shipping resolver query
While the final decision is pending, all designs in this document deliberately write destination_city_code or final FK.
Recommended Indexes
Minimum indexes:
- partner:
partner_code,is_active,priority_rank - service:
service_code,is_active - origin:
origin_code,is_default,is_active - region group:
destination_city_code,region_group_code - coverage:
(origin, destination, service, partner, is_active) - rate:
(origin, destination, service, partner, effective_from, effective_to, is_active) - fallback:
(origin, region_group_code, service, sequence_no, is_active)
Seed vs. Migration Rules
Belongs in the basic seed:
regular,cargo- origin
Makassar - the three initial partners
- the initial region-group mapping
- the initial fallback rules
Can be deferred to a phased operational seed:
- complete coverage of all cities/regencies
- complete rates for all destinations
- per-destination SLA updates
Resolver Queries the Design Must Support
This migration design must support queries such as:
- find the active rate for
Makassar -> destination X -> cargo service - order partners by fallback sequence
- check the active coverage of a partner for a specific destination
- fetch the rate effective on a specific date
- return merchant-facing values without exposing partner data to the merchant UI
Implementation Risks
- locking FKs to the wrong region table will complicate later seeds
- adding too many enum checks now can complicate later expansion
- not separating the basic seed from the bulk rate seed will make the migration too large
- not preparing the effective date from the start will complicate auditing rate changes
Deliverables After This Document
Once this migration plan is approved, the next deliverables are:
- SQL draft files
015through022 - basic seeds for
service,origin,partner,fallback - initial coverage and rate seeds
- draft resolver queries for the backend
Status
Current status: shipped — the SQL migrations described here (015–022) are implemented and applied in kesles_reference/reference_database/sql/.