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

@@ -18,10 +18,17 @@ export class RatesRepository implements IRatesRepository {
return this.repo.findOne({ where: { id } });
}
/**
* Every LIVE rate, newest first. The ordering is load-bearing: pricing picks
* the first match for a pattern, so without it Postgres heap order decided
* which of two overlapping rates a booking was billed at. Newest-first also
* means the most recent configuration wins where legacy overlaps still exist.
*/
findLiveRates(): Promise<Rate[]> {
return this.repo
.createQueryBuilder('rate')
.where('rate.status = :status', { status: 'LIVE' })
.orderBy('rate.created_at', 'DESC')
.getMany();
}
@@ -47,9 +54,21 @@ export class RatesRepository implements IRatesRepository {
* insert so the admin gets a friendly error instead of a raw constraint fault.
* NULL scope columns are matched with IS NULL, mirroring the COALESCE index.
*/
/**
* The live/draft rate already covering a pricing pattern, if any.
*
* `rateUnit` is optional on purpose. Where pricing resolves ONE rate for a
* lane (base freight, customs, lashing, empty return) the unit is not part of
* the identity — a per-container and a per-wagon row for the same lane are
* two answers to one question and the engine picks whichever came back first,
* so the caller omits it and the second row is rejected. Additive surcharges
* (hazard, reefer, demurrage…) are the opposite: the engine bills every
* matching rate by its own unit, so one per freight shape is the design and
* the caller passes the unit to keep them apart.
*/
findByPattern(pattern: {
rateType: string;
rateUnit: string;
rateUnit?: string;
containerTypeId?: string | null;
cargoTypeId?: string | null;
tradeDirection?: string | null;
@@ -59,9 +78,12 @@ export class RatesRepository implements IRatesRepository {
const qb = this.repo
.createQueryBuilder('rate')
.where('rate.rate_type = :rateType', { rateType: pattern.rateType })
.andWhere('rate.rate_unit = :rateUnit', { rateUnit: pattern.rateUnit })
.andWhere('rate.status <> :superseded', { superseded: 'SUPERSEDED' });
if (pattern.rateUnit) {
qb.andWhere('rate.rate_unit = :rateUnit', { rateUnit: pattern.rateUnit });
}
if (pattern.containerTypeId) {
qb.andWhere('rate.container_type_id = :containerTypeId', { containerTypeId: pattern.containerTypeId });
} else {