Add production plan release checklist
This commit is contained in:
@@ -351,10 +351,57 @@ const mapProductionPlanRow = (row) => ({
|
||||
actualCutQuantity: Number(row.actual_cut_quantity || 0),
|
||||
finishedQuantity: Number(row.finished_quantity || 0),
|
||||
statusCounts: row.status_counts || {},
|
||||
review: row.review_id ? {
|
||||
id: Number(row.review_id),
|
||||
decision: row.review_decision || 'pending',
|
||||
checklist: row.review_checklist || {},
|
||||
notes: row.review_notes || '',
|
||||
reviewedBy: row.reviewed_by || '',
|
||||
reviewedAt: row.reviewed_at || null,
|
||||
updatedAt: row.review_updated_at || null
|
||||
} : null,
|
||||
createdAt: row.created_at || null,
|
||||
updatedAt: row.updated_at || null
|
||||
});
|
||||
|
||||
const REVIEW_DECISIONS = new Set(['pending', 'approved', 'adjustments_required']);
|
||||
const REVIEW_CHECKLIST_KEYS = new Set(['materialsReserved', 'cutSpecificationReady', 'sizeAndColorReviewed', 'responsibleAndDateDefined']);
|
||||
|
||||
const normalizeProductionPlanReview = (payload = {}) => {
|
||||
const decision = normalizeText(payload.decision).toLowerCase() || 'pending';
|
||||
if (!REVIEW_DECISIONS.has(decision)) {
|
||||
const error = new Error('Decisão de revisão inválida.');
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
const rawChecklist = payload.checklist && typeof payload.checklist === 'object' ? payload.checklist : {};
|
||||
const checklist = {};
|
||||
REVIEW_CHECKLIST_KEYS.forEach(key => { checklist[key] = Boolean(rawChecklist[key]); });
|
||||
return { decision, checklist, notes: normalizeText(payload.notes), reviewedBy: normalizeText(payload.reviewedBy) };
|
||||
};
|
||||
|
||||
const saveProductionPlanReview = async (id, payload = {}) => {
|
||||
const review = normalizeProductionPlanReview(payload);
|
||||
const planExists = await pool.query('SELECT id FROM production_plans WHERE id = $1', [id]);
|
||||
if (!planExists.rowCount) {
|
||||
const error = new Error('Plano de produção não encontrado.');
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
await pool.query(`
|
||||
INSERT INTO production_plan_reviews (production_plan_id, decision, checklist, notes, reviewed_by, reviewed_at, updated_at)
|
||||
VALUES ($1, $2, $3::jsonb, NULLIF($4, ''), NULLIF($5, ''), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (production_plan_id) DO UPDATE
|
||||
SET decision = EXCLUDED.decision,
|
||||
checklist = EXCLUDED.checklist,
|
||||
notes = EXCLUDED.notes,
|
||||
reviewed_by = EXCLUDED.reviewed_by,
|
||||
reviewed_at = CURRENT_TIMESTAMP,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
`, [id, review.decision, JSON.stringify(review.checklist), review.notes, review.reviewedBy]);
|
||||
return getProductionPlanById(id);
|
||||
};
|
||||
|
||||
const normalizePlanPayload = (payload = {}, fallback = {}) => ({
|
||||
id: normalizeInteger(payload.id),
|
||||
name: normalizeText(payload.name) || normalizeText(fallback.name),
|
||||
@@ -388,12 +435,16 @@ const getProductionPlanById = async (id, client = pool) => {
|
||||
WHERE production_plan_id = plan.id
|
||||
GROUP BY status
|
||||
) grouped_statuses
|
||||
), '{}'::json) AS status_counts
|
||||
), '{}'::json) AS status_counts,
|
||||
review.id AS review_id, review.decision AS review_decision,
|
||||
review.checklist AS review_checklist, review.notes AS review_notes,
|
||||
review.reviewed_by, review.reviewed_at, review.updated_at AS review_updated_at
|
||||
FROM production_plans plan
|
||||
LEFT JOIN production_orders po ON po.production_plan_id = plan.id
|
||||
LEFT JOIN app_users responsible ON responsible.id = plan.responsible_user_id
|
||||
LEFT JOIN production_plan_reviews review ON review.production_plan_id = plan.id
|
||||
WHERE plan.id = $1
|
||||
GROUP BY plan.id, responsible.id;
|
||||
GROUP BY plan.id, responsible.id, review.id;
|
||||
`, [id]);
|
||||
return result.rows[0] ? mapProductionPlanRow(result.rows[0]) : null;
|
||||
};
|
||||
@@ -789,11 +840,15 @@ const listProductionPlans = async () => {
|
||||
WHERE production_plan_id = plan.id
|
||||
GROUP BY status
|
||||
) grouped_statuses
|
||||
), '{}'::json) AS status_counts
|
||||
), '{}'::json) AS status_counts,
|
||||
review.id AS review_id, review.decision AS review_decision,
|
||||
review.checklist AS review_checklist, review.notes AS review_notes,
|
||||
review.reviewed_by, review.reviewed_at, review.updated_at AS review_updated_at
|
||||
FROM production_plans plan
|
||||
LEFT JOIN production_orders po ON po.production_plan_id = plan.id
|
||||
LEFT JOIN app_users responsible ON responsible.id = plan.responsible_user_id
|
||||
GROUP BY plan.id, responsible.id
|
||||
LEFT JOIN production_plan_reviews review ON review.production_plan_id = plan.id
|
||||
GROUP BY plan.id, responsible.id, review.id
|
||||
ORDER BY COALESCE(plan.planned_start_date, plan.created_at::date) DESC, plan.id DESC;
|
||||
`);
|
||||
return result.rows.map(mapProductionPlanRow);
|
||||
@@ -1942,6 +1997,7 @@ module.exports = {
|
||||
getProductComposition,
|
||||
importTinyProductCompositionExport,
|
||||
listProductionPlans,
|
||||
saveProductionPlanReview,
|
||||
listProductionWorkstations,
|
||||
listProductCompositions,
|
||||
listProductionOrders,
|
||||
|
||||
Reference in New Issue
Block a user