Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/plantasur-dev/ship-quote/llms.txt

Use this file to discover all available pages before exploring further.

Ship Quote stores its configuration in six MongoDB collections managed through Mongoose. Understanding how these models relate to each other is essential for seeding data correctly and for extending the system with new carriers. This page documents every field in every schema, the indexes that back the rate engine’s queries, and the relationships that tie the models together.

Model Relationships

The diagram below shows how the six collections are connected.
Agency (1) ──────────────────────── Zone (N)
  │                                   │
  │                                   └─── ZoneRules (N)  [postal-code overrides]

  ├──── PalletType (N)

  └──── Rate (N) ──── palletTypeId ──► PalletType (optional)
                  └── zoneName  (string FK to Zone.name)
  • An Agency owns one or more Zones, PalletTypes, and Rates.
  • A Zone has zero or more ZoneRules that refine province-level zone assignment by postal code range.
  • A Rate references its zone by the zoneName string (not an ObjectId) to allow fast composite-key cache lookups without joins.
  • A Rate optionally references a PalletType via palletTypeId — present for per-pallet-type pricing, absent (null) for weight-volume or parcel pricing where the zone-level key is sufficient.

Agency

The Agency model (api/src/lib/models/agency.model.js) is the root entity for every carrier registered in the system. It controls whether the carrier is active, which shipment types it supports, what scope it covers, and — for API carriers — how to connect to the external endpoint.
{
  "_id":    "ObjectId",
  "name":   "Dachser",
  "code":   "dachser",
  "type":   "api",
  "active": true,

  "rules": {
    "hasAndaluciaRule": false,
    "supportsPallets":  true,
    "supportsParcels":  false,
    "coverage":        ["national", "international"]
  },

  "supplements": {
    "fuelSurcharge": {
      "enabled": true,
      "type":    "percentage",
      "value":   5.5
    }
  },

  "apiConfig": {
    "baseUrlApi": "https://api.dachser.com",
    "timeout":    5000,
    "apiKey":     "sk_live_xxxxx",
    "endpoints": {
      "quotations":      "/v2/quotations",
      "transportOrders": "/v2/orders"
    }
  },

  "createdAt": "2026-06-18T10:30:00Z",
  "updatedAt": "2026-06-18T10:30:00Z"
}

Field Reference

FieldTypeDefaultNotes
nameStringrequired3–14 characters. Auto-title-cased on save.
codeStringautoDerived from name (lowercased, accents stripped, spaces → _). Unique index.
typeString enum"static""static" | "api" | "hybrid"
activeBooleantrueSet to false to disable without deleting.
rules.hasAndaluciaRuleBooleanfalseWhether the agency applies special Andalucía routing rules.
rules.supportsPalletsBooleantruePallet items are processed for this agency.
rules.supportsParcelsBooleanfalseParcel items are processed for this agency.
rules.coverage[String]["national"]Accepted values: "national", "international".
supplements.fuelSurcharge.enabledBooleanfalseToggle fuel surcharge on/off.
supplements.fuelSurcharge.typeString enum"percentage""percentage" or "fixed".
supplements.fuelSurcharge.valueNumber0Percentage (e.g. 5.5) or fixed euro amount (e.g. 2.50).
apiConfig.baseUrlApiStringRequired when type is "api".
apiConfig.timeoutNumber3000HTTP timeout in milliseconds for external carrier calls.
apiConfig.apiKeyStringRequired when type is "api". Passed via X-API-Key header.
apiConfig.endpoints.quotationsStringPath appended to baseUrlApi for rate queries.
apiConfig.endpoints.transportOrdersStringPath for order creation (future use).
apiConfig.baseUrlApi and apiConfig.apiKey have conditional required validators — they are only enforced when type === "api". Static agencies can omit the apiConfig block entirely.

Indexes

agencySchema.index({ code: 1 }, { unique: true });

Location

The Location model (api/src/lib/models/location.model.js) stores the geographical reference data: every province (and optionally country) that the system can route shipments to.
{
  "_id":            "ObjectId",
  "countryCode":    "ES",
  "countryName":    "Spain",
  "adminCode":      "J",
  "adminFullCode":  "ES-J",
  "name":           "Jaén",
  "normalizedName": "jaen",
  "postalCode":     "23",
  "type":           "province",
  "createdAt":      "2026-06-18T13:20:30Z",
  "updatedAt":      "2026-06-18T13:20:30Z"
}

Field Reference

FieldTypeNotes
countryCodeStringISO 3166-1 alpha-2, uppercase (max 2 chars).
countryNameStringHuman-readable country name (max 50 chars). Title-cased on save.
adminCodeStringShort province/region code, e.g. "J" for Jaén or "28" for Madrid. Uppercase.
adminFullCodeStringFully qualified code "<countryCode>-<adminCode>", e.g. "ES-J". Unique index.
nameStringProvince display name (max 50 chars). Title-cased on save.
normalizedNameStringAccent-stripped, lowercased, spaces → _ version of name. Used for search.
postalCodeStringTwo-digit postal code prefix (e.g. "23" for all 23xxx codes in Jaén).
typeString enum"province" | "state" | "region". Default "province".

Indexes

locationSchema.index({ countryCode: 1, normalizedName: 1 });
locationSchema.index({ adminFullCode: 1 }, { unique: true });
// normalizedName also carries a single-field index declared inline on the path
The adminFullCode unique index prevents duplicate province records. The composite { countryCode, normalizedName } index accelerates province look-ups by name when filtering by country.

PalletType

The PalletType model (api/src/lib/models/palletType.model.js) defines the pallet dimensions and weight limits recognised by a specific agency. The rate engine matches each inbound pallet item against the agency’s sorted pallet types to determine which rate bracket applies.
{
  "_id":      "ObjectId",
  "agencyId": "ObjectId → Agency",
  "name":     "Euro Pallet",
  "constraints": {
    "maxWeight": 1000,
    "maxHeight": 1600,
    "maxLength": 1200,
    "maxWidth":  800
  },
  "createdAt": "2026-06-18T10:00:00Z",
  "updatedAt": "2026-06-18T10:00:00Z"
}

Field Reference

FieldTypeNotes
agencyIdObjectIdReference to the owning Agency. Indexed.
nameStringPallet type label, e.g. "CUARTO", "QUARTER PALLET", "Euro Pallet" (max 60 chars).
constraints.maxWeightNumberMaximum gross weight in kg. Required, ≥ 0.
constraints.maxHeightNumberMaximum stacked height in cm. Defaults to 0 (unconstrained).
constraints.maxLengthNumberMaximum length in cm. Defaults to 0 (unconstrained).
constraints.maxWidthNumberMaximum width in cm. Defaults to 0 (unconstrained).
Constraint values of 0 mean the dimension is not checked during pallet matching. Empty strings and null are coerced to 0 by the sanitizeInput setter.
At startup, loadAgencyTariffs() sorts each agency’s pallet types by maxWeight ascending, then maxHeight ascending, so the matching algorithm picks the smallest valid type — ensuring customers are priced accurately rather than being over-charged.

Zone

The Zone model (api/src/lib/models/zone.model.js) maps a set of provinces to a named pricing zone for a given agency. The rate engine resolves which zone applies to a destination by searching the province list (or, via ZoneRules, by postal code range).
{
  "_id":             "ObjectId",
  "agencyId":        "ObjectId → Agency",
  "name":            "ZONA 3",
  "provinces":       ["ES-B", "ES-GI", "ES-L", "ES-T"],
  "calculationMode": "pallet",
  "volumetric": {
    "enabled": false,
    "factor":  200
  },
  "pricingMode": {
    "type": "weight",
    "tonnagePricingRule": {
      "enabled":   false,
      "threshold": 2001,
      "unit":      "€/ton"
    }
  },
  "createdAt": "2026-06-18T10:00:00Z",
  "updatedAt": "2026-06-18T10:00:00Z"
}

Field Reference

FieldTypeDefaultNotes
agencyIdObjectIdrequiredOwning agency.
nameStringrequiredZone label, e.g. "ZONA 1", "NACIONAL", "BALEARIC".
provinces[String][]Array of adminFullCode values, e.g. ["ES-B", "ES-GI"].
calculationModeString enum"pallet""pallet" or "parcel". Determines which calculator is invoked.
volumetric.enabledBooleanfalseWhether to apply volumetric weight conversion to pallet items.
volumetric.factorNumber200Divisor for volumetric weight: (L × W × H) / factor.
pricingMode.typeString enum"weight""weight" (per pallet type) or "weight_volume" (total weight across all items).
pricingMode.tonnagePricingRule.enabledBooleanfalseSwitch price unit to €/ton above threshold.
pricingMode.tonnagePricingRule.thresholdNumber2001Weight threshold in kg above which tonnage pricing activates.
pricingMode.tonnagePricingRule.unitString"€/ton"Label shown in the rate breakdown.

Indexes

zoneSchema.index({ agencyId: 1 });
zoneSchema.index({ agencyId: 1, provinces: 1 });
The multi-key index on { agencyId: 1, provinces: 1 } allows MongoDB to efficiently find the zone for a given agency + province combination.

ZoneRules

The ZoneRules model (api/src/lib/models/zone.rules.model.js) provides postal-code-level overrides for province-level zone assignment. Some agencies route a specific postal code range within a province to a different zone from the rest of that province — ZoneRules captures those exceptions.
{
  "_id":      "ObjectId",
  "agencyId": "ObjectId → Agency",
  "zoneId":   "ObjectId → Zone",
  "province": "ES-GR",
  "isDefault": true,
  "postalCodeRanges": [
    { "from": "18001", "to": "18099" },
    { "from": "18100", "to": "18199" }
  ],
  "createdAt": "2026-06-18T10:00:00Z",
  "updatedAt": "2026-06-18T10:00:00Z"
}

Field Reference

FieldTypeNotes
agencyIdObjectIdOwning agency.
zoneIdObjectIdThe zone this rule maps to.
provinceStringThe adminFullCode of the province this rule applies within, e.g. "ES-GR".
isDefaultBooleanExactly one rule per (agencyId, province) pair can be isDefault: true. It is the fallback when no postal code range matches.
postalCodeRangesArrayEach element has from and to (5-digit strings). The cache service pre-expands these ranges into individual postal code entries for O(1) look-ups.

Indexes

zoneRulesSchema.index({ agencyId: 1, province: 1 });
zoneRulesSchema.index({ agencyId: 1, zoneId: 1 });
// Partial unique index: only one default rule per (agencyId, province)
zoneRulesSchema.index(
    { agencyId: 1, province: 1, isDefault: 1 },
    { unique: true, partialFilterExpression: { isDefault: true } }
);

Rate

The Rate model (api/src/lib/models/rate.model.js) stores the pricing tariff for a specific combination of agency, shipment type, zone, and (optionally) pallet type. Each rate document contains an array of services (e.g. economy, premium) each with its own priceBreaks, surcharges, and limits.
{
  "_id":           "ObjectId",
  "agencyId":      "ObjectId → Agency",
  "type":          "pallet",
  "zoneName":      "ZONA 1",
  "palletTypeId":  "ObjectId → PalletType",
  "calculationType": "unit",
  "services": [
    {
      "service": "economy",
      "priceBreaks": [
        { "min": 1,  "max": 1,  "price": 43.50 },
        { "min": 2,  "max": 3,  "price": 38.00 },
        { "min": 4,  "max": 10, "price": 33.00 }
      ],
      "surcharges": {
        "extraKg": {
          "enabled":    false,
          "pricePerKg": 0
        },
        "dimensionRanges": [
          { "min": 160, "max": 200, "price": 8.50 }
        ],
        "multiParcelExcess": {
          "enabled":       false,
          "thresholdKg":   40,
          "divisor":       1,
          "pricePerBlock": 0
        }
      },
      "limits": {
        "maxWeight":        500,
        "maxLength":        0,
        "maxSumDimensions": 0
      }
    }
  ],
  "createdAt": "2026-06-18T10:00:00Z",
  "updatedAt": "2026-06-18T10:00:00Z"
}

Top-Level Fields

FieldTypeNotes
agencyIdObjectIdOwning agency. Indexed.
typeString enum"pallet" or "parcel". Part of the cache composite key.
zoneNameStringMust match Zone.name exactly. Part of the cache composite key.
palletTypeIdObjectId / nullPresent for per-pallet-type rates; null for weight-volume or parcel rates. Part of the cache composite key (stored as "none" when null).
calculationTypeString enum"unit" (price per item/pallet) or "quantity" (price per quantity bracket). Default "unit".

Service Fields

FieldTypeNotes
serviceString enum"economy" | "premium" | "express" | "basic".
priceBreaksArrayEach element { min, max, price } — the engine picks the bracket whose min ≤ quantity ≤ max.
surcharges.extraKg.enabledBooleanAdd pricePerKg for each kg over the base weight.
surcharges.dimensionRangesArray{ min, max, price } — adds a flat fee if the item’s longest dimension falls in the range.
surcharges.multiParcelExcess.thresholdKgNumberTotal weight above which the excess charge activates (default 40 kg).
surcharges.multiParcelExcess.divisorNumberExcess weight is divided by this value to count billable blocks.
surcharges.multiParcelExcess.pricePerBlockNumberEuro cost per block of excess weight.
limits.maxWeightNumberMaximum weight per item; 0 = unconstrained.
limits.maxLengthNumberMaximum single-dimension length; 0 = unconstrained.
limits.maxSumDimensionsNumberMaximum L + W + H; 0 = unconstrained.

Indexes

rateSchema.index({ agencyId: 1, type: 1, zoneName: 1 });
This compound index directly mirrors the ratesByKey cache structure. It also accelerates the bulk load query (Rate.find({ agencyId: { $in: agencyIds } })) used at startup.

Cache Key Structure

At server startup, loadAgencyTariffs() builds a composite string key for each rate document and stores it in a Map for O(1) retrieval at request time.
// api/src/api/services/cache.service.js
function buildRateKey({ type, zoneName, palletTypeId }) {
    return [
        type,                          // "pallet" | "parcel"
        zoneName,                      // e.g. "ZONA 1"
        palletTypeId ?? 'none'         // ObjectId string or "none"
    ].join('|');
}
"pallet|ZONA 1|507f1f77bcf86cd799439011"
The same key is constructed at query time in the calculators, so a rate lookup is a single Map.get(key) call with no database round-trip.
When adding a new rate document via the REST API or seed script, ensure zoneName exactly matches the corresponding Zone.name field — the lookup is case-sensitive. A mismatch will cause the rate engine to return a ZONE_NOT_FOUND incident for that agency.

Build docs developers (and LLMs) love