243 lines
9.4 KiB
JavaScript
243 lines
9.4 KiB
JavaScript
"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
|