import { BaseEntity } from '@edr/api-common'; import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; import { Rate } from './rate.entity'; export type RateChangeStatus = 'PENDING' | 'APPROVED' | 'REJECTED'; /** * One proposed edit to a LIVE rate, awaiting approval. * * A LIVE rate is what pricing actually charges, so it is never mutated in * place: the edit is filed here and the live row keeps its old value until an * approver applies it. `payload` holds only the changed fields (an * UpdateRateDto patch), `rateId` the rate being repriced. * * DRAFT rates are not covered — nothing prices off a draft, so those still * edit directly and reach LIVE through the existing submit/approve flow. */ @Entity({ schema: 'freight', name: 'rate_change_requests' }) @Index(['status']) export class RateChangeRequest extends BaseEntity { @Column({ name: 'rate_id', type: 'uuid' }) rateId!: string; @ManyToOne(() => Rate, { nullable: false }) @JoinColumn({ name: 'rate_id' }) rate?: Rate | null; /** Proposed field changes — an UpdateRateDto patch, changed keys only. */ @Column({ name: 'payload', type: 'jsonb' }) payload!: Record; /** * The rate's values at submit time, for the approver's before→after diff. * Snapshotted because the live row can move on between submit and decision. */ @Column({ name: 'previous_values', type: 'jsonb' }) previousValues!: Record; @Column({ name: 'status', type: 'varchar', length: 10, default: 'PENDING' }) status!: RateChangeStatus; @Column({ name: 'requested_by_user_id', type: 'uuid', nullable: true }) requestedByUserId?: string | null; @Column({ name: 'decided_by_user_id', type: 'uuid', nullable: true }) decidedByUserId?: string | null; @Column({ name: 'decided_at', type: 'timestamptz', nullable: true }) decidedAt?: Date | null; @Column({ name: 'decision_note', type: 'text', nullable: true }) decisionNote?: string | null; }