Adiciona percentual de progresso e data do ultimo apontamento no relatorio de pecas por processo
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { X, FileText, Printer } from 'lucide-react';
|
||||
import { useOFs } from '@/hooks/useOFs';
|
||||
import { useRelatorioPecasProcesso, PecaComStatus } from '@/hooks/useRelatorioPecasProcesso';
|
||||
import { useDashboardProducaoOtimizado } from '@/hooks/useDashboardProducaoOtimizado';
|
||||
import { RelatorioPecasProcessoPDF } from './RelatorioPecasProcessoPDF';
|
||||
import { RelatorioPecasProcessoPrint } from './RelatorioPecasProcessoPrint';
|
||||
|
||||
@@ -22,6 +23,7 @@ export const RelatorioPecasProcessoModal: React.FC<RelatorioPecasProcessoModalPr
|
||||
const [selectedFase, setSelectedFase] = useState('todas');
|
||||
const { ofs, loading: ofsLoading } = useOFs();
|
||||
const { pecasComStatus, resumoProcessos, loading } = useRelatorioPecasProcesso(selectedOF);
|
||||
const { dashboardData } = useDashboardProducaoOtimizado(selectedOF);
|
||||
|
||||
// Filtrar peças por fase selecionada
|
||||
const pecasFiltradas = selectedFase === 'todas'
|
||||
@@ -37,6 +39,33 @@ export const RelatorioPecasProcessoModal: React.FC<RelatorioPecasProcessoModalPr
|
||||
pesoTotalExpedicao: pecasFiltradas.filter(p => p.processos.expedicao).reduce((sum, p) => sum + p.peso_total, 0)
|
||||
};
|
||||
|
||||
// Calcular percentual de progresso conforme índices da tela de Dashboard de Produção
|
||||
const percentuais = useMemo(() => {
|
||||
if (selectedFase === 'todas' && dashboardData?.processos && dashboardData.processos.length > 0) {
|
||||
const pCorte = dashboardData.processos.find(p => p.nome.toLowerCase().includes('corte'))?.progressoReal ?? 0;
|
||||
const pSolda = dashboardData.processos.find(p => p.nome.toLowerCase().includes('solda'))?.progressoReal ?? 0;
|
||||
const pPintura = dashboardData.processos.find(p => p.nome.toLowerCase().includes('pint') || p.nome.toLowerCase().includes('galv'))?.progressoReal ?? 0;
|
||||
const pExp = dashboardData.processos.find(p => p.nome.toLowerCase().includes('exped'))?.progressoReal ?? 0;
|
||||
|
||||
return {
|
||||
corte: pCorte,
|
||||
solda: pSolda,
|
||||
pintura: pPintura,
|
||||
expedicao: pExp
|
||||
};
|
||||
}
|
||||
|
||||
const pesoTotalGeral = pecasFiltradas.reduce((sum, p) => sum + p.peso_total, 0);
|
||||
const pesoTotalSoldavel = pecasFiltradas.filter(p => p.tem_componentes).reduce((sum, p) => sum + p.peso_total, 0) || pesoTotalGeral;
|
||||
|
||||
return {
|
||||
corte: pesoTotalGeral > 0 ? (estatisticasFiltradas.pesoTotalCorte / pesoTotalGeral) * 100 : 0,
|
||||
solda: pesoTotalSoldavel > 0 ? (estatisticasFiltradas.pesoTotalSolda / pesoTotalSoldavel) * 100 : 0,
|
||||
pintura: pesoTotalGeral > 0 ? (estatisticasFiltradas.pesoTotalPintura / pesoTotalGeral) * 100 : 0,
|
||||
expedicao: pesoTotalGeral > 0 ? (estatisticasFiltradas.pesoTotalExpedicao / pesoTotalGeral) * 100 : 0,
|
||||
};
|
||||
}, [selectedFase, dashboardData, pecasFiltradas, estatisticasFiltradas]);
|
||||
|
||||
// Obter fases únicas das peças
|
||||
const fasesUnicas = [...new Set(pecasComStatus.map(peca => peca.etapa_fase))].sort((a, b) => {
|
||||
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
||||
@@ -95,12 +124,14 @@ export const RelatorioPecasProcessoModal: React.FC<RelatorioPecasProcessoModalPr
|
||||
<RelatorioPecasProcessoPDF
|
||||
pecasComStatus={pecasFiltradas}
|
||||
estatisticas={estatisticasFiltradas}
|
||||
percentuais={percentuais}
|
||||
selectedOF={selectedOF}
|
||||
selectedFase={selectedFase}
|
||||
/>
|
||||
<RelatorioPecasProcessoPrint
|
||||
pecasComStatus={pecasFiltradas}
|
||||
estatisticas={estatisticasFiltradas}
|
||||
percentuais={percentuais}
|
||||
selectedOF={selectedOF}
|
||||
selectedFase={selectedFase}
|
||||
/>
|
||||
@@ -115,7 +146,7 @@ export const RelatorioPecasProcessoModal: React.FC<RelatorioPecasProcessoModalPr
|
||||
Preview - OF {selectedOF} {selectedFase !== 'todas' ? `- Fase ${selectedFase}` : ''}
|
||||
</h3>
|
||||
|
||||
{/* Estatísticas */}
|
||||
{/* Estatísticas com Percentual de Progresso */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
|
||||
<div className="bg-muted/50 p-3 rounded text-center">
|
||||
<p className="text-sm text-muted-foreground">Total de Peças</p>
|
||||
@@ -123,19 +154,39 @@ export const RelatorioPecasProcessoModal: React.FC<RelatorioPecasProcessoModalPr
|
||||
</div>
|
||||
<div className="bg-muted/50 p-3 rounded text-center">
|
||||
<p className="text-sm text-muted-foreground">Peso Corte</p>
|
||||
<p className="text-xl font-bold text-card-foreground">{estatisticasFiltradas.pesoTotalCorte.toFixed(0)} kg</p>
|
||||
<p className="text-xl font-bold text-card-foreground">
|
||||
{estatisticasFiltradas.pesoTotalCorte.toFixed(0)} kg{' '}
|
||||
<span className="text-sm font-semibold text-blue-600 dark:text-blue-400">
|
||||
({percentuais.corte.toFixed(1)}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-muted/50 p-3 rounded text-center">
|
||||
<p className="text-sm text-muted-foreground">Peso Solda</p>
|
||||
<p className="text-xl font-bold text-card-foreground">{estatisticasFiltradas.pesoTotalSolda.toFixed(0)} kg</p>
|
||||
<p className="text-xl font-bold text-card-foreground">
|
||||
{estatisticasFiltradas.pesoTotalSolda.toFixed(0)} kg{' '}
|
||||
<span className="text-sm font-semibold text-blue-600 dark:text-blue-400">
|
||||
({percentuais.solda.toFixed(1)}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-muted/50 p-3 rounded text-center">
|
||||
<p className="text-sm text-muted-foreground">Peso Pintura</p>
|
||||
<p className="text-xl font-bold text-card-foreground">{estatisticasFiltradas.pesoTotalPintura.toFixed(0)} kg</p>
|
||||
<p className="text-xl font-bold text-card-foreground">
|
||||
{estatisticasFiltradas.pesoTotalPintura.toFixed(0)} kg{' '}
|
||||
<span className="text-sm font-semibold text-blue-600 dark:text-blue-400">
|
||||
({percentuais.pintura.toFixed(1)}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-muted/50 p-3 rounded text-center">
|
||||
<p className="text-sm text-muted-foreground">Peso Expedição</p>
|
||||
<p className="text-xl font-bold text-card-foreground">{estatisticasFiltradas.pesoTotalExpedicao.toFixed(0)} kg</p>
|
||||
<p className="text-xl font-bold text-card-foreground">
|
||||
{estatisticasFiltradas.pesoTotalExpedicao.toFixed(0)} kg{' '}
|
||||
<span className="text-sm font-semibold text-blue-600 dark:text-blue-400">
|
||||
({percentuais.expedicao.toFixed(1)}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -161,21 +212,67 @@ export const RelatorioPecasProcessoModal: React.FC<RelatorioPecasProcessoModalPr
|
||||
<tr key={`${peca.id}-${index}`} className="hover:bg-muted/20">
|
||||
<td className="border border-border p-2 text-xs text-card-foreground">{peca.of_number}</td>
|
||||
<td className="border border-border p-2 text-xs text-card-foreground">{peca.etapa_fase}</td>
|
||||
<td className="border border-border p-2 text-xs text-card-foreground">{peca.marca}</td>
|
||||
<td className="border border-border p-2 text-xs text-card-foreground font-semibold">{peca.marca}</td>
|
||||
<td className="border border-border p-2 text-xs text-center text-card-foreground">{peca.quantidade}</td>
|
||||
<td className="border border-border p-2 text-xs text-right text-card-foreground">{peca.peso_unitario.toFixed(2)} kg</td>
|
||||
<td className="border border-border p-2 text-xs text-right text-card-foreground">{peca.peso_total.toFixed(2)} kg</td>
|
||||
|
||||
{/* Corte com check e data */}
|
||||
<td className="border border-border p-2 text-xs text-center text-card-foreground">
|
||||
{peca.processos.corte ? '✓' : ''}
|
||||
{peca.processos.corte ? (
|
||||
<div className="flex flex-col items-center justify-center leading-tight">
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-bold text-sm">✓</span>
|
||||
{peca.datasProcessos?.corte && (
|
||||
<span className="text-[10px] text-muted-foreground font-medium">
|
||||
{peca.datasProcessos.corte}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : ''}
|
||||
</td>
|
||||
|
||||
{/* Solda com check e data */}
|
||||
<td className="border border-border p-2 text-xs text-center text-card-foreground">
|
||||
{!peca.tem_componentes ? 'S/M' : (peca.processos.solda ? '✓' : '')}
|
||||
{!peca.tem_componentes ? (
|
||||
<span className="text-purple-600 dark:text-purple-400 font-semibold">S/M</span>
|
||||
) : peca.processos.solda ? (
|
||||
<div className="flex flex-col items-center justify-center leading-tight">
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-bold text-sm">✓</span>
|
||||
{peca.datasProcessos?.solda && (
|
||||
<span className="text-[10px] text-muted-foreground font-medium">
|
||||
{peca.datasProcessos.solda}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : ''}
|
||||
</td>
|
||||
|
||||
{/* Pintura com check e data */}
|
||||
<td className="border border-border p-2 text-xs text-center text-card-foreground">
|
||||
{peca.processos.pintura ? '✓' : ''}
|
||||
{peca.processos.pintura ? (
|
||||
<div className="flex flex-col items-center justify-center leading-tight">
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-bold text-sm">✓</span>
|
||||
{peca.datasProcessos?.pintura && (
|
||||
<span className="text-[10px] text-muted-foreground font-medium">
|
||||
{peca.datasProcessos.pintura}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : ''}
|
||||
</td>
|
||||
|
||||
{/* Expedição com check e data */}
|
||||
<td className="border border-border p-2 text-xs text-center text-card-foreground">
|
||||
{peca.processos.expedicao ? '✓' : ''}
|
||||
{peca.processos.expedicao ? (
|
||||
<div className="flex flex-col items-center justify-center leading-tight">
|
||||
<span className="text-emerald-600 dark:text-emerald-400 font-bold text-sm">✓</span>
|
||||
{peca.datasProcessos?.expedicao && (
|
||||
<span className="text-[10px] text-muted-foreground font-medium">
|
||||
{peca.datasProcessos.expedicao}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : ''}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -16,6 +16,12 @@ interface RelatorioPecasProcessoPDFProps {
|
||||
pesoTotalPintura: number;
|
||||
pesoTotalExpedicao: number;
|
||||
};
|
||||
percentuais?: {
|
||||
corte: number;
|
||||
solda: number;
|
||||
pintura: number;
|
||||
expedicao: number;
|
||||
};
|
||||
selectedOF: string;
|
||||
selectedFase: string;
|
||||
}
|
||||
@@ -23,6 +29,7 @@ interface RelatorioPecasProcessoPDFProps {
|
||||
export const RelatorioPecasProcessoPDF: React.FC<RelatorioPecasProcessoPDFProps> = ({
|
||||
pecasComStatus,
|
||||
estatisticas,
|
||||
percentuais,
|
||||
selectedOF,
|
||||
selectedFase
|
||||
}) => {
|
||||
@@ -47,38 +54,53 @@ export const RelatorioPecasProcessoPDF: React.FC<RelatorioPecasProcessoPDFProps>
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`OF: ${selectedOF}${faseText} | Gerado em: ${new Date().toLocaleString('pt-BR')}`, 14, 23);
|
||||
|
||||
// Resumo por Processo em linha mais compacta
|
||||
let yPos = 32;
|
||||
doc.setFontSize(9);
|
||||
// Resumo por Processo em box estilizado e alinhado
|
||||
let yPos = 29;
|
||||
doc.setFillColor(248, 250, 252);
|
||||
doc.roundedRect(14, yPos, 182, 10, 1, 1, 'F');
|
||||
doc.setDrawColor(226, 232, 240);
|
||||
doc.setLineWidth(0.3);
|
||||
doc.roundedRect(14, yPos, 182, 10, 1, 1, 'S');
|
||||
|
||||
doc.setFontSize(7.5);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`Total Peças:`, 14, yPos);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`${estatisticas.totalPecas}`, 36, yPos);
|
||||
doc.setTextColor(71, 85, 105);
|
||||
doc.text('Total Peças:', 17, yPos + 6.5);
|
||||
doc.setTextColor(15, 23, 42);
|
||||
doc.text(`${estatisticas.totalPecas}`, 33, yPos + 6.5);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`Corte:`, 50, yPos);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`${estatisticas.pesoTotalCorte.toFixed(0)} kg`, 62, yPos);
|
||||
doc.setTextColor(71, 85, 105);
|
||||
doc.text('Corte:', 44, yPos + 6.5);
|
||||
doc.setTextColor(15, 43, 92);
|
||||
doc.text(`${estatisticas.pesoTotalCorte.toFixed(0)} kg`, 54, yPos + 6.5);
|
||||
doc.setTextColor(37, 99, 235);
|
||||
doc.text(`(${(percentuais?.corte ?? 0).toFixed(1)}%)`, 70, yPos + 6.5);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`Solda:`, 90, yPos);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`${estatisticas.pesoTotalSolda.toFixed(0)} kg`, 102, yPos);
|
||||
doc.setTextColor(71, 85, 105);
|
||||
doc.text('Solda:', 86, yPos + 6.5);
|
||||
doc.setTextColor(15, 43, 92);
|
||||
doc.text(`${estatisticas.pesoTotalSolda.toFixed(0)} kg`, 96, yPos + 6.5);
|
||||
doc.setTextColor(37, 99, 235);
|
||||
doc.text(`(${(percentuais?.solda ?? 0).toFixed(1)}%)`, 112, yPos + 6.5);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`Pintura:`, 130, yPos);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`${estatisticas.pesoTotalPintura.toFixed(0)} kg`, 145, yPos);
|
||||
doc.setTextColor(71, 85, 105);
|
||||
doc.text('Pintura:', 127, yPos + 6.5);
|
||||
doc.setTextColor(15, 43, 92);
|
||||
doc.text(`${estatisticas.pesoTotalPintura.toFixed(0)} kg`, 138, yPos + 6.5);
|
||||
doc.setTextColor(37, 99, 235);
|
||||
doc.text(`(${(percentuais?.pintura ?? 0).toFixed(1)}%)`, 150, yPos + 6.5);
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`Expedição:`, 175, yPos);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`${estatisticas.pesoTotalExpedicao.toFixed(0)} kg`, 192, yPos);
|
||||
doc.setTextColor(71, 85, 105);
|
||||
doc.text('Exp.:', 165, yPos + 6.5);
|
||||
doc.setTextColor(15, 43, 92);
|
||||
doc.text(`${estatisticas.pesoTotalExpedicao.toFixed(0)} kg`, 173, yPos + 6.5);
|
||||
doc.setTextColor(37, 99, 235);
|
||||
doc.text(`(${(percentuais?.expedicao ?? 0).toFixed(1)}%)`, 185, yPos + 6.5);
|
||||
|
||||
yPos = 38; // Ajusta yPos para o início da tabela
|
||||
yPos = 42; // Ajusta yPos para o início da tabela
|
||||
|
||||
// Preparar dados para a tabela
|
||||
// Preparar dados para a tabela com indicação de data abaixo do check
|
||||
const tableData = pecasComStatus.map(peca => [
|
||||
peca.of_number,
|
||||
peca.etapa_fase,
|
||||
@@ -86,10 +108,10 @@ export const RelatorioPecasProcessoPDF: React.FC<RelatorioPecasProcessoPDFProps>
|
||||
peca.quantidade.toString(),
|
||||
`${peca.peso_unitario.toFixed(2)} kg`,
|
||||
`${peca.peso_total.toFixed(2)} kg`,
|
||||
peca.processos.corte ? 'OK' : '',
|
||||
!peca.tem_componentes ? 'S/M' : (peca.processos.solda ? 'OK' : ''),
|
||||
peca.processos.pintura ? 'OK' : '',
|
||||
peca.processos.expedicao ? 'OK' : ''
|
||||
peca.processos.corte ? (peca.datasProcessos?.corte ? `✓\n${peca.datasProcessos.corte}` : '✓') : '',
|
||||
!peca.tem_componentes ? 'S/M' : (peca.processos.solda ? (peca.datasProcessos?.solda ? `✓\n${peca.datasProcessos.solda}` : '✓') : ''),
|
||||
peca.processos.pintura ? (peca.datasProcessos?.pintura ? `✓\n${peca.datasProcessos.pintura}` : '✓') : '',
|
||||
peca.processos.expedicao ? (peca.datasProcessos?.expedicao ? `✓\n${peca.datasProcessos.expedicao}` : '✓') : ''
|
||||
]);
|
||||
|
||||
// Cabeçalhos da tabela
|
||||
|
||||
@@ -14,6 +14,12 @@ interface RelatorioPecasProcessoPrintProps {
|
||||
pesoTotalPintura: number;
|
||||
pesoTotalExpedicao: number;
|
||||
};
|
||||
percentuais?: {
|
||||
corte: number;
|
||||
solda: number;
|
||||
pintura: number;
|
||||
expedicao: number;
|
||||
};
|
||||
selectedOF: string;
|
||||
selectedFase: string;
|
||||
}
|
||||
@@ -21,6 +27,7 @@ interface RelatorioPecasProcessoPrintProps {
|
||||
export const RelatorioPecasProcessoPrint: React.FC<RelatorioPecasProcessoPrintProps> = ({
|
||||
pecasComStatus,
|
||||
estatisticas,
|
||||
percentuais,
|
||||
selectedOF,
|
||||
selectedFase
|
||||
}) => {
|
||||
@@ -128,10 +135,10 @@ export const RelatorioPecasProcessoPrint: React.FC<RelatorioPecasProcessoPrintPr
|
||||
<section style="margin-bottom: 12px;">
|
||||
<div style="display: flex; flex-wrap: wrap; justify-content: space-between; font-size: 11px; color: #4b5563; padding: 6px 12px; border: 1px solid #e5e7eb; border-radius: 6px; background-color: #f9fafb;" class="summary-info">
|
||||
<div><strong>Total Peças:</strong> ${estatisticas.totalPecas}</div>
|
||||
<div><strong>Corte:</strong> ${estatisticas.pesoTotalCorte.toFixed(0)} kg</div>
|
||||
<div><strong>Solda:</strong> ${estatisticas.pesoTotalSolda.toFixed(0)} kg</div>
|
||||
<div><strong>Pintura:</strong> ${estatisticas.pesoTotalPintura.toFixed(0)} kg</div>
|
||||
<div><strong>Expedição:</strong> ${estatisticas.pesoTotalExpedicao.toFixed(0)} kg</div>
|
||||
<div><strong>Corte:</strong> ${estatisticas.pesoTotalCorte.toFixed(0)} kg <span style="color: #2563eb; font-weight: bold;">(${(percentuais?.corte ?? 0).toFixed(1)}%)</span></div>
|
||||
<div><strong>Solda:</strong> ${estatisticas.pesoTotalSolda.toFixed(0)} kg <span style="color: #2563eb; font-weight: bold;">(${(percentuais?.solda ?? 0).toFixed(1)}%)</span></div>
|
||||
<div><strong>Pintura:</strong> ${estatisticas.pesoTotalPintura.toFixed(0)} kg <span style="color: #2563eb; font-weight: bold;">(${(percentuais?.pintura ?? 0).toFixed(1)}%)</span></div>
|
||||
<div><strong>Expedição:</strong> ${estatisticas.pesoTotalExpedicao.toFixed(0)} kg <span style="color: #2563eb; font-weight: bold;">(${(percentuais?.expedicao ?? 0).toFixed(1)}%)</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -157,14 +164,34 @@ export const RelatorioPecasProcessoPrint: React.FC<RelatorioPecasProcessoPrintPr
|
||||
<tr>
|
||||
<td>${peca.of_number}</td>
|
||||
<td>${peca.etapa_fase}</td>
|
||||
<td>${peca.marca}</td>
|
||||
<td style="font-weight: 600;">${peca.marca}</td>
|
||||
<td>${peca.quantidade}</td>
|
||||
<td class="text-right">${peca.peso_unitario.toFixed(2)} kg</td>
|
||||
<td class="text-right">${peca.peso_total.toFixed(2)} kg</td>
|
||||
<td>${peca.processos.corte ? 'OK' : ''}</td>
|
||||
<td>${!peca.tem_componentes ? 'S/M' : (peca.processos.solda ? 'OK' : '')}</td>
|
||||
<td>${peca.processos.pintura ? 'OK' : ''}</td>
|
||||
<td>${peca.processos.expedicao ? 'OK' : ''}</td>
|
||||
<td>
|
||||
${peca.processos.corte ? `
|
||||
<div style="font-weight: bold; color: #16a34a; font-size: 9pt; line-height: 1;">✓</div>
|
||||
${peca.datasProcessos?.corte ? `<div style="font-size: 6.5pt; color: #6b7280; margin-top: 1px;">${peca.datasProcessos.corte}</div>` : ''}
|
||||
` : ''}
|
||||
</td>
|
||||
<td>
|
||||
${!peca.tem_componentes ? '<span style="color: #9333ea; font-weight: 600;">S/M</span>' : (peca.processos.solda ? `
|
||||
<div style="font-weight: bold; color: #16a34a; font-size: 9pt; line-height: 1;">✓</div>
|
||||
${peca.datasProcessos?.solda ? `<div style="font-size: 6.5pt; color: #6b7280; margin-top: 1px;">${peca.datasProcessos.solda}</div>` : ''}
|
||||
` : '')}
|
||||
</td>
|
||||
<td>
|
||||
${peca.processos.pintura ? `
|
||||
<div style="font-weight: bold; color: #16a34a; font-size: 9pt; line-height: 1;">✓</div>
|
||||
${peca.datasProcessos?.pintura ? `<div style="font-size: 6.5pt; color: #6b7280; margin-top: 1px;">${peca.datasProcessos.pintura}</div>` : ''}
|
||||
` : ''}
|
||||
</td>
|
||||
<td>
|
||||
${peca.processos.expedicao ? `
|
||||
<div style="font-weight: bold; color: #16a34a; font-size: 9pt; line-height: 1;">✓</div>
|
||||
${peca.datasProcessos?.expedicao ? `<div style="font-size: 6.5pt; color: #6b7280; margin-top: 1px;">${peca.datasProcessos.expedicao}</div>` : ''}
|
||||
` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
|
||||
@@ -18,6 +18,12 @@ export interface PecaComStatus {
|
||||
pintura: boolean;
|
||||
expedicao: boolean;
|
||||
};
|
||||
datasProcessos?: {
|
||||
corte?: string | null;
|
||||
solda?: string | null;
|
||||
pintura?: string | null;
|
||||
expedicao?: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResumoProcesso {
|
||||
@@ -26,6 +32,29 @@ export interface ResumoProcesso {
|
||||
quantidadePecas: number;
|
||||
}
|
||||
|
||||
// Formata data ISO ou YYYY-MM-DD para 'dd/mm'
|
||||
export const formatarDiaMes = (dataStr?: string | null): string => {
|
||||
if (!dataStr) return '';
|
||||
try {
|
||||
const raw = dataStr.split('T')[0];
|
||||
const partes = raw.split('-');
|
||||
if (partes.length === 3) {
|
||||
const dia = partes[2].padStart(2, '0');
|
||||
const mes = partes[1].padStart(2, '0');
|
||||
return `${dia}/${mes}`;
|
||||
}
|
||||
const d = new Date(dataStr);
|
||||
if (!isNaN(d.getTime())) {
|
||||
const dia = d.getDate().toString().padStart(2, '0');
|
||||
const mes = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||
return `${dia}/${mes}`;
|
||||
}
|
||||
} catch (e) {
|
||||
// fallback
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// Função para ordenação numérica natural
|
||||
const naturalSort = (a: string, b: string): number => {
|
||||
const collator = new Intl.Collator(undefined, {
|
||||
@@ -86,39 +115,103 @@ export const useRelatorioPecasProcesso = (ofNumber: string) => {
|
||||
const processosMap = new Map(processosData?.map(p => [p.nome, p.id]) || []);
|
||||
console.log('Processos mapeados:', Object.fromEntries(processosMap));
|
||||
|
||||
// Buscar apontamentos para todas as peças da OF
|
||||
// Buscar apontamentos para todas as peças da OF incluindo data_apontamento e created_at
|
||||
const pecaIds = pecasData.map(p => p.id);
|
||||
const { data: apontamentosData, error: apontamentosError } = await supabase
|
||||
.from('apontamentos_producao')
|
||||
.select(`
|
||||
peca_id,
|
||||
processo_id,
|
||||
quantidade_produzida,
|
||||
processos_fabricacao!apontamentos_producao_processo_id_fkey(nome)
|
||||
`)
|
||||
.eq('of_number', ofNumber)
|
||||
.in('peca_id', pecaIds);
|
||||
const [resApontamentos, resRomaneios] = await Promise.all([
|
||||
supabase
|
||||
.from('apontamentos_producao')
|
||||
.select(`
|
||||
peca_id,
|
||||
processo_id,
|
||||
quantidade_produzida,
|
||||
data_apontamento,
|
||||
created_at,
|
||||
processos_fabricacao!apontamentos_producao_processo_id_fkey(nome)
|
||||
`)
|
||||
.eq('of_number', ofNumber)
|
||||
.in('peca_id', pecaIds),
|
||||
|
||||
if (apontamentosError) {
|
||||
console.error('Erro ao buscar apontamentos:', apontamentosError);
|
||||
throw apontamentosError;
|
||||
// Buscar romaneios de expedição para garantir datas e status de expedição
|
||||
supabase
|
||||
.from('itens_romaneio_pecas')
|
||||
.select(`
|
||||
marca,
|
||||
peca_id,
|
||||
quantidade_expedida,
|
||||
created_at,
|
||||
romaneios_expedicao!inner(of_number, data_saida, data_emissao, created_at)
|
||||
`)
|
||||
.eq('romaneios_expedicao.of_number', ofNumber)
|
||||
]);
|
||||
|
||||
if (resApontamentos.error) {
|
||||
console.error('Erro ao buscar apontamentos:', resApontamentos.error);
|
||||
throw resApontamentos.error;
|
||||
}
|
||||
|
||||
console.log('Apontamentos encontrados:', apontamentosData?.length || 0);
|
||||
const apontamentosData = resApontamentos.data || [];
|
||||
const romaneiosData = resRomaneios.data || [];
|
||||
|
||||
// Mapear status dos processos por peça
|
||||
console.log('Apontamentos encontrados:', apontamentosData.length);
|
||||
|
||||
// Mapear status dos processos por peça e guardar a ÚLTIMA data apontada de cada processo
|
||||
const statusPorPeca = new Map<string, Set<string>>();
|
||||
const datasPorPeca = new Map<string, { corte: string; solda: string; pintura: string; expedicao: string }>();
|
||||
|
||||
apontamentosData?.forEach(apt => {
|
||||
apontamentosData.forEach(apt => {
|
||||
if (!statusPorPeca.has(apt.peca_id)) {
|
||||
statusPorPeca.set(apt.peca_id, new Set());
|
||||
}
|
||||
statusPorPeca.get(apt.peca_id)!.add(apt.processos_fabricacao.nome);
|
||||
const nomeProc = apt.processos_fabricacao?.nome || '';
|
||||
statusPorPeca.get(apt.peca_id)!.add(nomeProc);
|
||||
|
||||
if (!datasPorPeca.has(apt.peca_id)) {
|
||||
datasPorPeca.set(apt.peca_id, { corte: '', solda: '', pintura: '', expedicao: '' });
|
||||
}
|
||||
const datas = datasPorPeca.get(apt.peca_id)!;
|
||||
const dataApt = apt.data_apontamento || apt.created_at || '';
|
||||
const nomeLower = nomeProc.toLowerCase();
|
||||
|
||||
if (dataApt) {
|
||||
if (nomeLower.includes('corte')) {
|
||||
if (!datas.corte || dataApt > datas.corte) datas.corte = dataApt;
|
||||
} else if (nomeLower.includes('solda')) {
|
||||
if (!datas.solda || dataApt > datas.solda) datas.solda = dataApt;
|
||||
} else if (nomeLower.includes('pint') || nomeLower.includes('galv')) {
|
||||
if (!datas.pintura || dataApt > datas.pintura) datas.pintura = dataApt;
|
||||
} else if (nomeLower.includes('exped')) {
|
||||
if (!datas.expedicao || dataApt > datas.expedicao) datas.expedicao = dataApt;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Construir array de peças com status
|
||||
// Adicionar informações vindas dos Romaneios de Expedição
|
||||
romaneiosData.forEach((item: any) => {
|
||||
const pecaAlvo = pecasData.find(p => p.id === item.peca_id || p.marca === item.marca);
|
||||
if (pecaAlvo) {
|
||||
if (!statusPorPeca.has(pecaAlvo.id)) {
|
||||
statusPorPeca.set(pecaAlvo.id, new Set());
|
||||
}
|
||||
statusPorPeca.get(pecaAlvo.id)!.add('Expedicao');
|
||||
|
||||
if (!datasPorPeca.has(pecaAlvo.id)) {
|
||||
datasPorPeca.set(pecaAlvo.id, { corte: '', solda: '', pintura: '', expedicao: '' });
|
||||
}
|
||||
const datas = datasPorPeca.get(pecaAlvo.id)!;
|
||||
const dataExp = item.romaneios_expedicao?.data_saida ||
|
||||
item.romaneios_expedicao?.data_emissao ||
|
||||
item.romaneios_expedicao?.created_at ||
|
||||
item.created_at || '';
|
||||
if (dataExp && (!datas.expedicao || dataExp > datas.expedicao)) {
|
||||
datas.expedicao = dataExp;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Construir array de peças com status e última data apontada
|
||||
const pecasComStatusFormatadas: PecaComStatus[] = pecasData.map(peca => {
|
||||
const processosRealizados = statusPorPeca.get(peca.id) || new Set();
|
||||
const datas = datasPorPeca.get(peca.id) || { corte: '', solda: '', pintura: '', expedicao: '' };
|
||||
|
||||
return {
|
||||
id: peca.id,
|
||||
@@ -136,6 +229,12 @@ export const useRelatorioPecasProcesso = (ofNumber: string) => {
|
||||
processosRealizados.has('Pintura/Galv') ||
|
||||
processosRealizados.has('Pintura'),
|
||||
expedicao: processosRealizados.has('Expedicao')
|
||||
},
|
||||
datasProcessos: {
|
||||
corte: formatarDiaMes(datas.corte),
|
||||
solda: formatarDiaMes(datas.solda),
|
||||
pintura: formatarDiaMes(datas.pintura),
|
||||
expedicao: formatarDiaMes(datas.expedicao)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user