diff --git a/apps/edr-freight-api/src/modules/rule-engine/repositories/cargo-types.repository.ts b/apps/edr-freight-api/src/modules/rule-engine/repositories/cargo-types.repository.ts index 4df961aaa..07993c267 100644 --- a/apps/edr-freight-api/src/modules/rule-engine/repositories/cargo-types.repository.ts +++ b/apps/edr-freight-api/src/modules/rule-engine/repositories/cargo-types.repository.ts @@ -59,26 +59,57 @@ export class CargoTypesRepository implements ICargoTypesRepository { } async create(data: Partial): Promise { - const entity = this.repo.create(data); - return this.repo.save(entity); + const { wagonTypes, ...columns } = data; + const entity = this.repo.create(columns); + const saved = await this.repo.save(entity); + if (wagonTypes?.length) { + await this.syncWagonTypes(saved.id, wagonTypes.map((wt) => wt.id), []); + } + return (await this.findById(saved.id)) ?? saved; } async update(id: string, data: Partial): Promise { - // Relation lists can't ride a column UPDATE — sync them via entity save. const { wagonTypes, ...columns } = data; if (Object.keys(columns).length) { await this.repo.update(id, columns as never); } if (wagonTypes) { - const entity = await this.repo.findOne({ where: { id } }); - if (entity) { - entity.wagonTypes = wagonTypes; - await this.repo.save(entity); + const current = await this.repo.findOne({ + where: { id }, + relations: { wagonTypes: true }, + }); + if (current) { + await this.syncWagonTypes( + id, + wagonTypes.map((wt) => wt.id), + (current.wagonTypes ?? []).map((wt) => wt.id), + ); } } return this.findById(id); } + /** + * Diffs the wagon-type links through the relation query builder rather than + * an entity save: junction-row inserts from save() broadcast afterInsert with + * no entity attached, which the @tria-plc/auditlog subscriber (deployed + * builds) dereferences and crashes the request on. + */ + private async syncWagonTypes( + id: string, + nextIds: string[], + currentIds: string[], + ): Promise { + const toAdd = nextIds.filter((x) => !currentIds.includes(x)); + const toRemove = currentIds.filter((x) => !nextIds.includes(x)); + if (!toAdd.length && !toRemove.length) return; + await this.repo + .createQueryBuilder() + .relation(CargoType, 'wagonTypes') + .of(id) + .addAndRemove(toAdd, toRemove); + } + async softDelete(id: string): Promise { await this.repo.softDelete(id); } diff --git a/apps/edr-freight-web/backoffice/src/components/fleet/FleetRecordActions.tsx b/apps/edr-freight-web/backoffice/src/components/fleet/FleetRecordActions.tsx index 4ab2c42f9..898982f34 100644 --- a/apps/edr-freight-web/backoffice/src/components/fleet/FleetRecordActions.tsx +++ b/apps/edr-freight-web/backoffice/src/components/fleet/FleetRecordActions.tsx @@ -126,6 +126,15 @@ const FleetRecordActions = ({ {removeLabel} ) : null} + {onPurge ? ( + onPurge(record)} + leftSection={} + > + Delete permanently + + ) : null} ); diff --git a/apps/edr-payment-api/src/modules/cbe-bill/cbe-bill.service.ts b/apps/edr-payment-api/src/modules/cbe-bill/cbe-bill.service.ts index 9389280c5..1e4c70d17 100644 --- a/apps/edr-payment-api/src/modules/cbe-bill/cbe-bill.service.ts +++ b/apps/edr-payment-api/src/modules/cbe-bill/cbe-bill.service.ts @@ -191,13 +191,12 @@ export class CbeBillService { return mapPaymentFailure(dto, "Payment is being processed."); } if (prior.failureClass === "BUSINESS") { - // Final — retrying cannot change the answer. Replay what we told CBE last time. - return ( - (prior.responsePayload as unknown as CbePaymentResponseDto) ?? - mapPaymentFailure( - dto, - prior.responseDescription ?? "Payment already failed.", - ) + // Final — retrying cannot change the answer. Same End_To_End_Txn_Id was already + // resolved (possibly against a different Bill_Id/amount); say so instead of + // echoing the original reason, which no longer describes this request. + return mapPaymentFailure( + dto, + `End_To_End_Txn_Id ${dto.End_To_End_Txn_Id} was already processed and failed: ${prior.responseDescription ?? "unknown reason"}.`, ); } // FAILED + TRANSIENT: allowed retry — fall through and re-run the settlement.