"use strict"; /** * InspectionService - Servicio de inspecciones de calidad * * Gestión de inspecciones con checklists y resultados. * * @module Quality */ Object.defineProperty(exports, "__esModule", { value: true }); exports.InspectionService = void 0; class InspectionService { inspectionRepository; resultRepository; nonConformityRepository; checklistRepository; constructor(inspectionRepository, resultRepository, nonConformityRepository, checklistRepository) { this.inspectionRepository = inspectionRepository; this.resultRepository = resultRepository; this.nonConformityRepository = nonConformityRepository; this.checklistRepository = checklistRepository; } generateInspectionNumber() { 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, 8).toUpperCase(); return `INS-${year}${month}-${random}`; } generateNCNumber() { 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 `NC-${year}${month}-${random}`; } async findWithFilters(ctx, filters = {}, page = 1, limit = 20) { const skip = (page - 1) * limit; const queryBuilder = this.inspectionRepository .createQueryBuilder('insp') .leftJoinAndSelect('insp.checklist', 'checklist') .leftJoinAndSelect('insp.inspector', 'inspector') .where('insp.tenant_id = :tenantId', { tenantId: ctx.tenantId }); if (filters.checklistId) { queryBuilder.andWhere('insp.checklist_id = :checklistId', { checklistId: filters.checklistId }); } if (filters.loteId) { queryBuilder.andWhere('insp.lote_id = :loteId', { loteId: filters.loteId }); } if (filters.inspectorId) { queryBuilder.andWhere('insp.inspector_id = :inspectorId', { inspectorId: filters.inspectorId }); } if (filters.status) { queryBuilder.andWhere('insp.status = :status', { status: filters.status }); } if (filters.dateFrom) { queryBuilder.andWhere('insp.inspection_date >= :dateFrom', { dateFrom: filters.dateFrom }); } if (filters.dateTo) { queryBuilder.andWhere('insp.inspection_date <= :dateTo', { dateTo: filters.dateTo }); } queryBuilder .orderBy('insp.inspection_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.inspectionRepository.findOne({ where: { id, tenantId: ctx.tenantId, }, }); } async findWithDetails(ctx, id) { return this.inspectionRepository.findOne({ where: { id, tenantId: ctx.tenantId, }, relations: ['checklist', 'checklist.items', 'inspector', 'results', 'results.checklistItem', 'nonConformities'], }); } async create(ctx, dto) { // Validate checklist exists const checklist = await this.checklistRepository.findOne({ where: { id: dto.checklistId, tenantId: ctx.tenantId, isActive: true, deletedAt: null, }, relations: ['items'], }); if (!checklist) { throw new Error('Checklist not found or inactive'); } const inspection = this.inspectionRepository.create({ tenantId: ctx.tenantId, createdById: ctx.userId, checklistId: dto.checklistId, loteId: dto.loteId, inspectionNumber: this.generateInspectionNumber(), inspectionDate: dto.inspectionDate, inspectorId: dto.inspectorId, notes: dto.notes, status: 'pending', totalItems: checklist.items?.filter(i => i.isActive).length || 0, }); return this.inspectionRepository.save(inspection); } async startInspection(ctx, id) { const inspection = await this.findById(ctx, id); if (!inspection) { return null; } if (inspection.status !== 'pending') { throw new Error('Can only start pending inspections'); } inspection.status = 'in_progress'; inspection.updatedById = ctx.userId || ''; return this.inspectionRepository.save(inspection); } async recordResult(ctx, inspectionId, dto) { const inspection = await this.findById(ctx, inspectionId); if (!inspection) { throw new Error('Inspection not found'); } if (inspection.status !== 'in_progress') { throw new Error('Can only record results for in-progress inspections'); } // Check if result already exists let result = await this.resultRepository.findOne({ where: { inspectionId, checklistItemId: dto.checklistItemId, tenantId: ctx.tenantId, }, }); if (result) { // Update existing result.result = dto.result; result.observations = dto.observations || result.observations; result.photoUrl = dto.photoUrl || result.photoUrl; result.inspectedAt = new Date(); } else { // Create new result = this.resultRepository.create({ tenantId: ctx.tenantId, inspectionId, checklistItemId: dto.checklistItemId, result: dto.result, observations: dto.observations, photoUrl: dto.photoUrl, inspectedAt: new Date(), }); } const savedResult = await this.resultRepository.save(result); // If failed, create non-conformity automatically if (dto.result === 'failed') { const nc = this.nonConformityRepository.create({ tenantId: ctx.tenantId, createdById: ctx.userId, inspectionId, loteId: inspection.loteId, ncNumber: this.generateNCNumber(), detectionDate: new Date(), category: 'Inspection Finding', severity: 'minor', description: dto.observations || 'Failed inspection item', photoUrl: dto.photoUrl, status: 'open', }); await this.nonConformityRepository.save(nc); } return savedResult; } async completeInspection(ctx, id) { const inspection = await this.findWithDetails(ctx, id); if (!inspection) { return null; } if (inspection.status !== 'in_progress') { throw new Error('Can only complete in-progress inspections'); } // Calculate pass/fail counts const results = inspection.results || []; const passed = results.filter(r => r.result === 'passed').length; const failed = results.filter(r => r.result === 'failed').length; const total = results.filter(r => r.result !== 'not_applicable').length; inspection.passedItems = passed; inspection.failedItems = failed; inspection.passRate = total > 0 ? (passed / total) * 100 : 0; inspection.status = 'completed'; inspection.completedAt = new Date(); inspection.updatedById = ctx.userId || ''; return this.inspectionRepository.save(inspection); } async approveInspection(ctx, id) { const inspection = await this.findWithDetails(ctx, id); if (!inspection) { return null; } if (inspection.status !== 'completed') { throw new Error('Can only approve completed inspections'); } // Check for open critical non-conformities const criticalNCs = inspection.nonConformities?.filter(nc => nc.severity === 'critical' && nc.status !== 'verified'); if (criticalNCs && criticalNCs.length > 0) { throw new Error('Cannot approve inspection with open critical non-conformities'); } inspection.status = 'approved'; inspection.approvedById = ctx.userId || ''; inspection.approvedAt = new Date(); inspection.updatedById = ctx.userId || ''; return this.inspectionRepository.save(inspection); } async rejectInspection(ctx, id, reason) { const inspection = await this.findById(ctx, id); if (!inspection) { return null; } if (inspection.status !== 'completed') { throw new Error('Can only reject completed inspections'); } inspection.status = 'rejected'; inspection.rejectionReason = reason; inspection.updatedById = ctx.userId || ''; return this.inspectionRepository.save(inspection); } } exports.InspectionService = InspectionService; //# sourceMappingURL=inspection.service.js.map