Clarify supply execution states
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m47s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m47s
This commit is contained in:
@@ -1838,6 +1838,14 @@ const getNeedPendingLabel = (need: SupplyPurchaseNeed) => {
|
||||
return /^Cadastrar rendimento:/i.test(need.material) ? 'Rendimento' : 'Referência';
|
||||
};
|
||||
|
||||
const hasNegativeMaterialBalance = (need: SupplyPurchaseNeed) => need.stockKg < 0;
|
||||
|
||||
const getNeedStatusLabel = (need: SupplyPurchaseNeed) => {
|
||||
if (hasNegativeMaterialBalance(need)) return 'Saldo negativo';
|
||||
if (need.missingReference) return 'Sem referência';
|
||||
return purchaseStatusLabels[need.status];
|
||||
};
|
||||
|
||||
type PurchaseNeedTab = 'materials' | 'services' | 'mapping';
|
||||
|
||||
const sumNeedProducts = (
|
||||
@@ -1887,7 +1895,13 @@ const PurchaseNeedsScreen = () => {
|
||||
const materialNeeds = summary.purchaseNeeds.filter(need => !need.missingReference && !isServicePurchaseNeed(need));
|
||||
const serviceNeeds = summary.purchaseNeeds.filter(need => !need.missingReference && isServicePurchaseNeed(need));
|
||||
const mappingNeeds = summary.purchaseNeeds.filter(need => need.missingReference);
|
||||
const activeNeeds = activeTab === 'materials' ? materialNeeds : activeTab === 'services' ? serviceNeeds : mappingNeeds;
|
||||
const activeNeeds = activeTab === 'materials'
|
||||
? [...materialNeeds].sort((a, b) => {
|
||||
if (hasNegativeMaterialBalance(a) !== hasNegativeMaterialBalance(b)) return hasNegativeMaterialBalance(a) ? -1 : 1;
|
||||
if (a.status !== b.status) return a.status === 'critical' ? -1 : b.status === 'critical' ? 1 : 0;
|
||||
return b.purchaseKg - a.purchaseKg;
|
||||
})
|
||||
: activeTab === 'services' ? serviceNeeds : mappingNeeds;
|
||||
const normalizedSearch = normalizeSearch(search);
|
||||
const visibleNeeds = activeNeeds.filter(need => (
|
||||
!normalizedSearch || normalizeSearch(`${need.material} ${getNeedDisplayName(need)} ${need.suppliers.join(' ')} ${need.colors.join(' ')} ${(need.products || []).map(product => `${product.productId} ${product.name}`).join(' ')}`).includes(normalizedSearch)
|
||||
@@ -1908,7 +1922,7 @@ const PurchaseNeedsScreen = () => {
|
||||
? 'Necessidades de beneficiamento e serviços. Não entram no total de compra de materiais.'
|
||||
: 'SKUs sem consumo confiável. Corrija o cadastro antes de usar estes valores para comprar.';
|
||||
const stats = [
|
||||
{ label: 'Materiais a comprar', value: `${purchaseItemCount}` },
|
||||
{ label: 'Itens para comprar', value: `${purchaseItemCount} materiais` },
|
||||
{ label: 'Compra sugerida', value: suggestedPurchaseSummary },
|
||||
{ label: 'Serviços a revisar', value: `${serviceNeeds.filter(need => need.purchaseKg > 0).length}` },
|
||||
{ label: 'SKUs sem mapeamento', value: `${impactedSkuCount || mappingNeeds.length}` },
|
||||
@@ -1969,9 +1983,9 @@ const PurchaseNeedsScreen = () => {
|
||||
</label>
|
||||
<div className="mt-4 inline-flex rounded-xl border border-dark-border bg-dark-input p-1">
|
||||
{[
|
||||
{ id: 'materials' as const, label: `Materiais (${materialNeeds.length})` },
|
||||
{ id: 'materials' as const, label: `Materiais (${materialNeeds.length} linhas)` },
|
||||
{ id: 'services' as const, label: `Serviços (${serviceNeeds.length})` },
|
||||
{ id: 'mapping' as const, label: `Mapear dados (${mappingNeeds.length})` },
|
||||
{ id: 'mapping' as const, label: `Mapear dados (${impactedSkuCount || mappingNeeds.length} SKUs)` },
|
||||
].map(tab => (
|
||||
<button key={tab.id} type="button" onClick={() => { setActiveTab(tab.id); setSearch(''); setCurrentPage(1); }} className={`rounded-lg px-3 py-2 text-xs font-bold transition-colors cursor-pointer ${activeTab === tab.id ? 'bg-brand-primary text-brand-contrast' : 'text-dark-muted hover:text-dark-text'}`}>
|
||||
{tab.label}
|
||||
@@ -2070,7 +2084,7 @@ const PurchaseNeedsScreen = () => {
|
||||
<th scope="col" className="px-4 py-3 text-right">{activeTab === 'services' ? 'Agendado' : 'Estoque atual'}</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">{activeTab === 'services' ? 'Em aberto' : 'Pendente'}</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">{activeTab === 'services' ? 'Contratar' : 'Comprar'}</th>
|
||||
<th scope="col" className="px-4 py-3 text-left">{activeTab === 'services' ? 'Ação' : 'Cobertura'}</th>
|
||||
<th scope="col" className="px-4 py-3 text-left">Situação</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-dark-border bg-dark-card">
|
||||
@@ -2083,7 +2097,10 @@ const PurchaseNeedsScreen = () => {
|
||||
<div>
|
||||
<p className="text-sm font-bold text-dark-text">{need.products.length} {need.products.length === 1 ? 'SKU impactado' : 'SKUs impactados'}</p>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">Demanda: {formatNumber(sumNeedProducts(need, 'suggestedQuantity'))} un.</p>
|
||||
<p className="mt-1 truncate font-mono text-[10px] text-dark-muted" title={need.products.map(product => product.productId).join(', ')}>{need.products.slice(0, 3).map(product => product.productId).join(', ')}{need.products.length > 3 ? ` +${need.products.length - 3}` : ''}</p>
|
||||
<p className="mt-1 flex flex-wrap gap-x-1.5 gap-y-1 font-mono text-[10px] text-dark-muted">
|
||||
{need.products.slice(0, 3).map(product => <RouterLink key={product.productId} to={`/products/${product.productId}`} className="hover:text-brand-primary hover:underline">#{product.productId}</RouterLink>)}
|
||||
{need.products.length > 3 && <span>+{need.products.length - 3}</span>}
|
||||
</p>
|
||||
</div>
|
||||
) : <span className="text-xs font-semibold text-dark-muted">Plano manual</span>}
|
||||
</td>
|
||||
@@ -2110,13 +2127,15 @@ const PurchaseNeedsScreen = () => {
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-4 text-right align-middle text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.plannedKg)}</td>
|
||||
<td className="px-4 py-4 text-right align-middle text-sm font-bold text-dark-text">{activeTab === 'services' ? '—' : formatNeedQuantity(need, need.stockKg)}</td>
|
||||
<td className={`px-4 py-4 text-right align-middle text-sm font-bold ${hasNegativeMaterialBalance(need) ? 'text-red-300' : 'text-dark-text'}`}>{activeTab === 'services' ? '—' : formatNeedQuantity(need, need.stockKg)}</td>
|
||||
<td className="px-4 py-4 text-right align-middle text-sm font-bold text-dark-text">{activeTab === 'services' ? '—' : formatNeedQuantity(need, need.pendingKg)}</td>
|
||||
<td className={`px-4 py-4 text-right align-middle text-sm font-bold ${need.purchaseKg > 0 ? 'text-amber-300' : 'text-emerald-300'}`}>{formatNeedQuantity(need, need.purchaseKg)}</td>
|
||||
<td className="px-4 py-4 align-middle">
|
||||
<span className={`inline-flex whitespace-nowrap rounded-full border px-2.5 py-1 text-xs font-bold ${
|
||||
activeTab === 'services'
|
||||
? 'border-sky-400/30 bg-sky-400/10 text-sky-300'
|
||||
: hasNegativeMaterialBalance(need)
|
||||
? 'border-red-400/30 bg-red-400/10 text-red-300'
|
||||
: need.missingReference
|
||||
? 'border-red-400/30 bg-red-400/10 text-red-300'
|
||||
: need.status === 'critical'
|
||||
@@ -2125,7 +2144,7 @@ const PurchaseNeedsScreen = () => {
|
||||
? 'border-amber-400/30 bg-amber-400/10 text-amber-300'
|
||||
: 'border-emerald-400/30 bg-emerald-400/10 text-emerald-300'
|
||||
}`}>
|
||||
{activeTab === 'services' ? 'Programar serviço' : need.missingReference ? 'Sem referência' : purchaseStatusLabels[need.status]}
|
||||
{activeTab === 'services' ? 'Programar serviço' : getNeedStatusLabel(need)}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user