Merge pull request #1128 from Tria-plc/freight_feature/usermanagement

Freight feature/usermanagement
This commit is contained in:
marshal
2026-08-05 23:05:39 +03:00
committed by GitHub
3 changed files with 53 additions and 14 deletions

View File

@@ -59,26 +59,57 @@ export class CargoTypesRepository implements ICargoTypesRepository {
}
async create(data: Partial<CargoType>): Promise<CargoType> {
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<CargoType>): Promise<CargoType | null> {
// 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<void> {
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<void> {
await this.repo.softDelete(id);
}

View File

@@ -126,6 +126,15 @@ const FleetRecordActions = ({
{removeLabel}
</MenuItem>
) : null}
{onPurge ? (
<MenuItem
color="red"
onClick={() => onPurge(record)}
leftSection={<ShieldAlert size={14} strokeWidth={2} />}
>
Delete permanently
</MenuItem>
) : null}
</Menu.Dropdown>
</Menu>
);

View File

@@ -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.