mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 01:15:03 +00:00
fix rate edit
This commit is contained in:
@@ -119,12 +119,62 @@ export class RatesService {
|
||||
});
|
||||
}
|
||||
|
||||
/** Update a DRAFT rate. */
|
||||
/**
|
||||
* Update a DRAFT rate in place. Nothing prices off a draft, so a direct edit
|
||||
* is safe. A LIVE rate cannot take this path — see `applyApprovedUpdate`.
|
||||
*/
|
||||
async update(id: string, dto: UpdateRateDto): Promise<Rate> {
|
||||
const existing = await this.findById(id);
|
||||
if (existing.status !== 'DRAFT') {
|
||||
throw new BadRequestException('Only DRAFT rates can be updated');
|
||||
throw new BadRequestException(
|
||||
existing.status === 'LIVE'
|
||||
? 'A LIVE rate cannot be edited directly — file a rate change request so an approver can apply it.'
|
||||
: 'Only DRAFT rates can be updated',
|
||||
);
|
||||
}
|
||||
return this.applyUpdate(existing, dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an approved change request to a LIVE rate. Same validation as a
|
||||
* DRAFT edit — it just skips the DRAFT guard, because a LIVE rate reaching
|
||||
* here has already been through approval. Only ever called by
|
||||
* RateChangeRequestsService.approve.
|
||||
*/
|
||||
async applyApprovedUpdate(id: string, dto: UpdateRateDto): Promise<Rate> {
|
||||
const existing = await this.findById(id);
|
||||
if (existing.status !== 'LIVE') {
|
||||
throw new BadRequestException(
|
||||
`Rate change requests apply to LIVE rates only — this rate is ${existing.status}.`,
|
||||
);
|
||||
}
|
||||
return this.applyUpdate(existing, dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a proposed patch against a rate without writing anything — lets a
|
||||
* change request be refused at submit time instead of surprising the
|
||||
* approver. Throws exactly what applying it would throw.
|
||||
*/
|
||||
async assertUpdateValid(id: string, dto: UpdateRateDto): Promise<void> {
|
||||
await this.buildUpdate(await this.findById(id), dto);
|
||||
}
|
||||
|
||||
private async applyUpdate(existing: Rate, dto: UpdateRateDto): Promise<Rate> {
|
||||
const updates = await this.buildUpdate(existing, dto);
|
||||
const updated = await this.repository.update(existing.id, updates);
|
||||
if (!updated) throw new NotFoundException(`Rate ${existing.id} not found`);
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* The shared edit body: re-derives rateType, re-validates the unit against
|
||||
* the (possibly changed) shape, and guards pattern uniqueness. Status is
|
||||
* never touched — an approved edit to a LIVE rate stays LIVE. Pure apart
|
||||
* from the uniqueness read, so it doubles as the dry-run validator.
|
||||
*/
|
||||
private async buildUpdate(existing: Rate, dto: UpdateRateDto): Promise<Partial<Rate>> {
|
||||
const id = existing.id;
|
||||
const updates: Partial<Rate> = {};
|
||||
|
||||
const appliesTo = (dto.appliesTo as Rate['appliesTo']) ?? existing.appliesTo;
|
||||
@@ -179,9 +229,7 @@ export class RatesService {
|
||||
|
||||
updates.currency = dto.currency ?? existing.currency ?? 'USD';
|
||||
if (dto.rateValue !== undefined) updates.rateValue = dto.rateValue;
|
||||
const updated = await this.repository.update(id, updates);
|
||||
if (!updated) throw new NotFoundException(`Rate ${id} not found`);
|
||||
return updated;
|
||||
return updates;
|
||||
}
|
||||
|
||||
/** Submit a DRAFT rate for CEO approval. */
|
||||
|
||||
Reference in New Issue
Block a user