From 62cf279ad41abb9ea8957d90ae499597b93e3c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Tue, 4 Aug 2026 13:01:31 -0300 Subject: [PATCH] Populate fabric planning from supply demand --- src/pages/Supplies.tsx | 94 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 13 deletions(-) diff --git a/src/pages/Supplies.tsx b/src/pages/Supplies.tsx index fdc9d03..876b980 100644 --- a/src/pages/Supplies.tsx +++ b/src/pages/Supplies.tsx @@ -1831,6 +1831,7 @@ const InventoryScreen = () => { const FabricPlanningScreen = () => { const [plans, setPlans] = useState([]); + const [summary, setSummary] = useState(emptySupplySummary); const [form, setForm] = useState({ material: '', color: '', @@ -1842,12 +1843,20 @@ const FabricPlanningScreen = () => { const [isBusy, setIsBusy] = useState(false); const [errorMessage, setErrorMessage] = useState(''); - const totalKg = plans.reduce((total, plan) => total + plan.quantityKg, 0); + const fabricNeeds = summary.purchaseNeeds.filter(need => ( + !need.missingReference && + !isServicePurchaseNeed(need) && + /\b(?:MALHA|TECIDO|RIBANA|MOLETOM)\b/i.test(need.material) + )); + const fabricNeedsToBuy = fabricNeeds.filter(need => need.purchaseKg > 0); + const fabricNeedUnits = new Set(fabricNeeds.flatMap(need => (need.products || []).map(product => product.productId).filter(Boolean))).size; + const plannedFabricKg = fabricNeeds.reduce((total, need) => total + Math.max(0, need.plannedKg), 0); const loadPlans = async () => { setErrorMessage(''); const summary = await fetchSupplySummary(); setPlans(summary.fabricPlans); + setSummary(summary); }; useEffect(() => { @@ -1857,7 +1866,10 @@ const FabricPlanningScreen = () => { setIsLoading(true); try { const summary = await fetchSupplySummary(); - if (isMounted) setPlans(summary.fabricPlans); + if (isMounted) { + setPlans(summary.fabricPlans); + setSummary(summary); + } } finally { if (isMounted) setIsLoading(false); } @@ -1910,7 +1922,7 @@ const FabricPlanningScreen = () => { return (
-
+
{errorMessage && (
{errorMessage} @@ -1918,24 +1930,80 @@ const FabricPlanningScreen = () => { )}
-

Planos ativos

+

Tecidos com necessidade

+

{fabricNeedsToBuy.length}

+

{formatNumber(fabricNeedUnits, 0)} SKUs dependem deles

+
+
+

Demanda de tecido

+

{formatNumber(plannedFabricKg)} kg

+

necessidade planejada para o período

+
+
+

Planos locais ativos

{plans.length}

+

{plans.filter(plan => plan.priority === 'Crítico').length} críticos

-
-

Kg planejado

-

{formatNumber(totalKg)} kg

-
-
-

Críticos

-

{plans.filter(plan => plan.priority === 'Crítico').length}

+
+ +
+
+
+

Fila de tecidos para abastecer o corte

+

Necessidade calculada pela composição, saldo sincronizado e recebimentos pendentes.

+
+ Abrir compra de materiais
+ {isLoading ? ( +
+ +

Calculando fila de tecidos...

+
+ ) : fabricNeeds.length ? ( +
+ {fabricNeeds.slice().sort((a, b) => b.purchaseKg - a.purchaseKg).map(need => { + const impactedSkus = new Set((need.products || []).map(product => product.productId).filter(Boolean)).size; + const isCovered = need.purchaseKg <= 0; + return ( +
+
+

{need.material}

+

{impactedSkus} SKUs impactados · {need.suppliers.length ? need.suppliers.join(' · ') : 'Fornecedor não informado'}

+
+
+ {[ + ['Necessário', need.plannedKg], + ['Em estoque', need.stockKg], + ['A receber', need.pendingKg], + ['Comprar', need.purchaseKg], + ].map(([label, value]) => ( +
+

{label}

+

{formatNumber(Number(value))} {need.unit || 'kg'}

+
+ ))} +
+
+ {isCovered ? 'Coberto' : 'Comprar tecido'} +
+
+ ); + })} +
+ ) : ( +
+ +

Nenhum tecido identificado na composição

+

Mapeie consumo de malha, tecido ou ribana para gerar esta fila.

+
+ )}
-

Planos de malha

-

Itens planejados para compra ou recebimento.

+

Planos locais de recebimento e alocação

+

Registros operacionais internos; não substituem a necessidade calculada acima.

{isLoading ? (