"use strict";
/**
 * RequisicionService - Servicio de requisiciones de obra
 *
 * Gestión de requisiciones de material con workflow:
 * draft -> submitted -> approved -> partially_served -> served
 *
 * @module Inventory
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.RequisicionService = void 0;
class RequisicionService {
    requisicionRepository;
    lineaRepository;
    constructor(requisicionRepository, lineaRepository) {
        this.requisicionRepository = requisicionRepository;
        this.lineaRepository = lineaRepository;
    }
    generateNumber() {
        const now = new Date();
        const year = now.getFullYear().toString().slice(-2);
        const month = (now.getMonth() + 1).toString().padStart(2, '0');
        const random = Math.random().toString(36).substring(2, 6).toUpperCase();
        return `REQ-${year}${month}-${random}`;
    }
    async findWithFilters(ctx, filters = {}, page = 1, limit = 20) {
        const skip = (page - 1) * limit;
        const queryBuilder = this.requisicionRepository
            .createQueryBuilder('req')
            .leftJoinAndSelect('req.fraccionamiento', 'fraccionamiento')
            .leftJoinAndSelect('req.requestedBy', 'requestedBy')
            .where('req.tenant_id = :tenantId', { tenantId: ctx.tenantId })
            .andWhere('req.deleted_at IS NULL');
        if (filters.fraccionamientoId) {
            queryBuilder.andWhere('req.fraccionamiento_id = :fraccionamientoId', {
                fraccionamientoId: filters.fraccionamientoId,
            });
        }
        if (filters.status) {
            queryBuilder.andWhere('req.status = :status', { status: filters.status });
        }
        if (filters.priority) {
            queryBuilder.andWhere('req.priority = :priority', { priority: filters.priority });
        }
        if (filters.dateFrom) {
            queryBuilder.andWhere('req.requisition_date >= :dateFrom', { dateFrom: filters.dateFrom });
        }
        if (filters.dateTo) {
            queryBuilder.andWhere('req.requisition_date <= :dateTo', { dateTo: filters.dateTo });
        }
        queryBuilder
            .orderBy('req.requisition_date', 'DESC')
            .skip(skip)
            .take(limit);
        const [data, total] = await queryBuilder.getManyAndCount();
        return {
            data,
            meta: {
                total,
                page,
                limit,
                totalPages: Math.ceil(total / limit),
            },
        };
    }
    async findById(ctx, id) {
        return this.requisicionRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
                deletedAt: null,
            },
        });
    }
    async findWithDetails(ctx, id) {
        return this.requisicionRepository.findOne({
            where: {
                id,
                tenantId: ctx.tenantId,
                deletedAt: null,
            },
            relations: [
                'fraccionamiento',
                'requestedBy',
                'approvedBy',
                'lineas',
                'lineas.concepto',
                'lineas.lote',
            ],
        });
    }
    async create(ctx, dto) {
        if (!ctx.userId) {
            throw new Error('User ID is required to create a requisition');
        }
        const requisicion = this.requisicionRepository.create({
            tenantId: ctx.tenantId,
            createdById: ctx.userId,
            requestedById: ctx.userId,
            requisitionNumber: this.generateNumber(),
            fraccionamientoId: dto.fraccionamientoId,
            requisitionDate: dto.requisitionDate,
            requiredDate: dto.requiredDate,
            priority: dto.priority || 'medium',
            destinationWarehouseId: dto.destinationWarehouseId,
            notes: dto.notes,
            status: 'draft',
        });
        return this.requisicionRepository.save(requisicion);
    }
    async addLinea(ctx, requisicionId, dto) {
        const requisicion = await this.findById(ctx, requisicionId);
        if (!requisicion) {
            throw new Error('Requisicion not found');
        }
        if (requisicion.status !== 'draft') {
            throw new Error('Can only add lines to draft requisitions');
        }
        const linea = this.lineaRepository.create({
            tenantId: ctx.tenantId,
            createdById: ctx.userId,
            requisicionId,
            productId: dto.productId,
            conceptoId: dto.conceptoId,
            loteId: dto.loteId,
            quantityRequested: dto.quantityRequested,
            unitId: dto.unitId,
            notes: dto.notes,
        });
        return this.lineaRepository.save(linea);
    }
    async removeLinea(ctx, requisicionId, lineaId) {
        const requisicion = await this.findById(ctx, requisicionId);
        if (!requisicion) {
            return false;
        }
        if (requisicion.status !== 'draft') {
            throw new Error('Can only remove lines from draft requisitions');
        }
        const result = await this.lineaRepository.delete({
            id: lineaId,
            requisicionId,
            tenantId: ctx.tenantId,
        });
        return (result.affected ?? 0) > 0;
    }
    async submit(ctx, id) {
        const requisicion = await this.findWithDetails(ctx, id);
        if (!requisicion) {
            return null;
        }
        if (requisicion.status !== 'draft') {
            throw new Error('Can only submit draft requisitions');
        }
        if (!requisicion.lineas || requisicion.lineas.length === 0) {
            throw new Error('Cannot submit requisition without lines');
        }
        requisicion.status = 'submitted';
        return this.requisicionRepository.save(requisicion);
    }
    async approve(ctx, id) {
        const requisicion = await this.findWithDetails(ctx, id);
        if (!requisicion) {
            return null;
        }
        if (requisicion.status !== 'submitted') {
            throw new Error('Can only approve submitted requisitions');
        }
        requisicion.status = 'approved';
        requisicion.approvedById = ctx.userId || '';
        requisicion.approvedAt = new Date();
        // Set approved quantities to requested quantities by default
        for (const linea of requisicion.lineas) {
            linea.quantityApproved = linea.quantityRequested;
            await this.lineaRepository.save(linea);
        }
        return this.requisicionRepository.save(requisicion);
    }
    async reject(ctx, id, reason) {
        const requisicion = await this.findById(ctx, id);
        if (!requisicion) {
            return null;
        }
        if (requisicion.status !== 'submitted') {
            throw new Error('Can only reject submitted requisitions');
        }
        requisicion.status = 'cancelled';
        requisicion.rejectionReason = reason;
        return this.requisicionRepository.save(requisicion);
    }
    async cancel(ctx, id) {
        const requisicion = await this.findById(ctx, id);
        if (!requisicion) {
            return null;
        }
        if (requisicion.status === 'served' || requisicion.status === 'cancelled') {
            throw new Error('Cannot cancel served or already cancelled requisitions');
        }
        requisicion.status = 'cancelled';
        return this.requisicionRepository.save(requisicion);
    }
    async updateServedQuantity(ctx, lineaId, quantityServed) {
        const linea = await this.lineaRepository.findOne({
            where: { id: lineaId, tenantId: ctx.tenantId },
            relations: ['requisicion'],
        });
        if (!linea || !linea.requisicion) {
            return null;
        }
        if (linea.requisicion.status !== 'approved' && linea.requisicion.status !== 'partially_served') {
            throw new Error('Can only serve approved or partially served requisitions');
        }
        linea.quantityServed = quantityServed;
        await this.lineaRepository.save(linea);
        // Update requisition status based on all lines
        await this.updateRequisitionStatus(ctx, linea.requisicion.id);
        return linea;
    }
    async updateRequisitionStatus(ctx, requisicionId) {
        const requisicion = await this.findWithDetails(ctx, requisicionId);
        if (!requisicion || !requisicion.lineas) {
            return;
        }
        const allServed = requisicion.lineas.every((l) => l.quantityServed >= (l.quantityApproved || l.quantityRequested));
        const someServed = requisicion.lineas.some((l) => l.quantityServed > 0);
        if (allServed) {
            requisicion.status = 'served';
        }
        else if (someServed) {
            requisicion.status = 'partially_served';
        }
        await this.requisicionRepository.save(requisicion);
    }
    async softDelete(ctx, id) {
        const requisicion = await this.findById(ctx, id);
        if (!requisicion) {
            return false;
        }
        if (requisicion.status !== 'draft' && requisicion.status !== 'cancelled') {
            throw new Error('Can only delete draft or cancelled requisitions');
        }
        await this.requisicionRepository.update({ id, tenantId: ctx.tenantId }, { deletedAt: new Date(), deletedById: ctx.userId });
        return true;
    }
}
exports.RequisicionService = RequisicionService;
//# sourceMappingURL=requisicion.service.js.map