fix issue

This commit is contained in:
Marshal
2026-07-27 05:59:49 +00:00
parent 773456c256
commit 15d78e1d67
10 changed files with 410 additions and 36 deletions

View File

@@ -132,6 +132,24 @@ export class RatesService {
);
}
/**
* True when pricing resolves exactly ONE rate for this shape (base freight,
* customs clearance, lashing, empty-container return — all `find()`-based
* lookups). For those the unit is not part of the rate's identity: two rows
* for the same lane differing only by unit are a duplicate the engine cannot
* choose between.
*
* The additive surcharges are the opposite — the engine bills EVERY matching
* rate by its own unit, which is how hazard can be per-container for boxes
* and per-ton for bulk at the same time — so their unit stays part of the key.
*/
private resolvesSingleRate(
appliesTo: Rate['appliesTo'],
trigger: Rate['trigger'],
): boolean {
return this.isRouteScoped(appliesTo, trigger) || trigger === 'LASHING';
}
/**
* Which country each end of the leg must sit in, given what the rate is for.
* The railway only sells three shapes: import lands at the Djibouti ports and
@@ -326,10 +344,17 @@ export class RatesService {
* Reject a second rate with the same identity pattern (rateType + scope). With
* effective-date windows gone, two LIVE/DRAFT rates for the same pattern would
* make pricing ambiguous — so we allow exactly one per pattern.
*
* The UNIT is not part of that identity. Pricing resolves one rate per lane +
* scope and then applies whatever unit it carries; a per-container and a
* per-wagon row for the same 20ft lane are two answers to one question, and
* the engine silently picked one of them. Changing how a lane is billed means
* editing its rate, not adding a second.
*/
private async assertNoDuplicatePattern(pattern: {
rateType: string;
rateUnit: string;
/** Passed only for additive surcharges — see {@link resolvesSingleRate}. */
rateUnit?: string;
containerTypeId: string | null;
cargoTypeId: string | null;
tradeDirection: string | null;
@@ -415,7 +440,7 @@ export class RatesService {
await this.assertNoDuplicatePattern({
rateType,
rateUnit,
...(this.resolvesSingleRate(appliesTo, trigger) ? {} : { rateUnit }),
containerTypeId,
cargoTypeId,
tradeDirection,
@@ -600,7 +625,7 @@ export class RatesService {
// Guard the pattern uniqueness for the new identity, ignoring this row.
await this.assertNoDuplicatePattern({
rateType,
rateUnit,
...(this.resolvesSingleRate(appliesTo, trigger) ? {} : { rateUnit }),
containerTypeId: updates.containerTypeId,
cargoTypeId: updates.cargoTypeId,
tradeDirection: updates.tradeDirection,