import { Injectable } from '@nestjs/common';
import { randomUUID } from 'node:crypto';
import { DatabaseService } from '../../../infrastructure/database/database.service';
import { StockMovementService } from './stock-movement.service';
import { OutboxService } from '../../../infrastructure/outbox/outbox.service';
import { assertWasteInput } from './inventory-input-validation';
@Injectable()
export class WasteService {
constructor(
private readonly database:DatabaseService,
private readonly movements:StockMovementService,
private readonly outbox:OutboxService,
) {}
async record(input:{
organizationId:string;locationId:string;stockItemId:string;storageLocationId:string;
quantity:string;unitCode:string;normalizedQuantity:string;normalizedUnitCode:string;
reason:string;businessDate:string;notes?:string;sourceType?:string;sourceId?:string;
employeeId?:string;
}) {
assertWasteInput(input);
return this.database.transaction(async(tx)=>{
const id=randomUUID();
await tx.query(
`insert into waste_records(
id,organization_id,location_id,stock_item_id,storage_location_id,
quantity,unit_code,normalized_quantity,reason,notes,
source_type,source_id,recorded_by_employee_id,business_date
) values($1,$2,$3,$4,$5,$6::numeric,$7,$8::numeric,$9,$10,$11,$12,$13,$14::date)`,
[
id,input.organizationId,input.locationId,input.stockItemId,input.storageLocationId,
input.quantity,input.unitCode,input.normalizedQuantity,input.reason,input.notes??null,
input.sourceType??null,input.sourceId??null,input.employeeId??null,input.businessDate,
],
);
const movementType=input.reason==='breakage'?'breakage':'waste';
const movement=await this.movements.append({
organizationId:input.organizationId,locationId:input.locationId,stockItemId:input.stockItemId,
storageLocationId:input.storageLocationId,movementType,
quantityDelta:`-${input.quantity}`,unitCode:input.unitCode,
normalizedQuantityDelta:`-${input.normalizedQuantity}`,normalizedUnitCode:input.normalizedUnitCode,
businessDate:input.businessDate,sourceType:'waste_record',sourceId:id,
reason:input.reason,employeeId:input.employeeId,
},tx);
await this.outbox.append({
organizationId:input.organizationId,aggregateType:'WasteRecord',aggregateId:id,
eventType:'WasteRecorded',
payload:{wasteRecordId:id,stockMovementId:movement.id,stockItemId:input.stockItemId},
},tx);
return {id,stockMovementId:movement.id};
},{lockTimeoutMs:3000,statementTimeoutMs:10000});
}
}
