Files
graphs/backend/services/productionOrderService.js
Cauê Faleiros caea53b94d
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m20s
Add production orders page
2026-07-02 10:07:13 -03:00

140 lines
4.5 KiB
JavaScript

const { pool } = require('../db');
const STATUS_LABELS = {
open: 'Em aberto',
in_progress: 'Em andamento',
finished: 'Finalizada',
canceled: 'Cancelada'
};
const normalizeStatus = (status) => {
const normalizedStatus = String(status || 'open').trim().toLowerCase();
if (['open', 'em_aberto', 'em aberto', 'aberta'].includes(normalizedStatus)) return 'open';
if (['in_progress', 'andamento', 'em andamento'].includes(normalizedStatus)) return 'in_progress';
if (['finished', 'finalizada', 'finalizado'].includes(normalizedStatus)) return 'finished';
if (['canceled', 'cancelada', 'cancelado', 'cancelled'].includes(normalizedStatus)) return 'canceled';
return normalizedStatus || 'open';
};
const normalizeDateParam = (value) => {
if (!value) return null;
const date = new Date(`${value}T00:00:00`);
if (Number.isNaN(date.getTime())) return null;
return value;
};
const formatDate = (value) => {
if (!value) return null;
if (value instanceof Date && !Number.isNaN(value.getTime())) {
return value.toISOString().slice(0, 10);
}
return String(value).slice(0, 10);
};
const mapProductionOrderRow = (row) => {
const status = normalizeStatus(row.status);
return {
id: row.id,
tinyId: row.tiny_id || '',
number: row.number || '',
status,
statusLabel: STATUS_LABELS[status] || row.status || 'Em aberto',
orderReference: row.order_reference || '',
issueDate: formatDate(row.issue_date),
expectedDate: formatDate(row.expected_date),
productSku: row.product_sku || '',
productDescription: row.product_description || '',
quantity: Number(row.quantity || 0),
unit: row.unit || 'UN',
integrationStatus: row.integration_status || '',
markers: Array.isArray(row.markers) ? row.markers.filter(Boolean) : [],
createdAt: row.created_at || null,
updatedAt: row.updated_at || null
};
};
const listProductionOrders = async (filters = {}) => {
const params = [];
const where = [];
const normalizedStart = normalizeDateParam(filters.start);
const normalizedEnd = normalizeDateParam(filters.end);
const normalizedSearch = String(filters.search || '').trim();
if (normalizedStart) {
params.push(normalizedStart);
where.push(`COALESCE(po.issue_date, po.expected_date, po.created_at::date) >= $${params.length}::date`);
}
if (normalizedEnd) {
params.push(normalizedEnd);
where.push(`COALESCE(po.issue_date, po.expected_date, po.created_at::date) <= $${params.length}::date`);
}
if (normalizedSearch) {
params.push(`%${normalizedSearch}%`);
where.push(`(
po.number ILIKE $${params.length}
OR po.order_reference ILIKE $${params.length}
OR po.product_sku ILIKE $${params.length}
OR po.product_description ILIKE $${params.length}
)`);
}
const result = await pool.query(`
SELECT
po.id,
po.tiny_id,
po.number,
po.status,
po.order_reference,
po.issue_date,
po.expected_date,
po.product_sku,
po.product_description,
po.quantity,
po.unit,
po.integration_status,
po.created_at,
po.updated_at,
COALESCE(
JSON_AGG(
JSON_BUILD_OBJECT(
'label', pom.label,
'color', pom.color
)
ORDER BY pom.label
) FILTER (WHERE pom.id IS NOT NULL),
'[]'::json
) as markers
FROM production_orders po
LEFT JOIN production_order_markers pom ON pom.production_order_id = po.id
${where.length ? `WHERE ${where.join(' AND ')}` : ''}
GROUP BY po.id
ORDER BY
COALESCE(po.issue_date, po.created_at::date) DESC,
CASE WHEN po.number ~ '^\\d+$' THEN po.number::bigint ELSE NULL END DESC NULLS LAST,
po.id DESC;
`, params);
const orders = result.rows.map(mapProductionOrderRow);
const counts = orders.reduce((nextCounts, order) => {
nextCounts.all += 1;
nextCounts[order.status] = (nextCounts[order.status] || 0) + 1;
return nextCounts;
}, {
all: 0,
open: 0,
in_progress: 0,
finished: 0,
canceled: 0
});
return { orders, counts };
};
module.exports = {
listProductionOrders,
normalizeStatus
};