You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e19d114
Browse filesBrowse the repository at this point in the historyBrowse files
functionmodel(records:HistoricalRecord[]){constoperational=records.filter(record=>record.record_status==='operational');constguideKg=operational.reduce((sum,row)=>sum+n(row.guide_kg),0),receivedKg=operational.reduce((sum,row)=>sum+n(row.received_kg),0);constproviders=newSet(operational.map(row=>key(row.supplier_name??row.supplier_original)).filter(Boolean));constcritical=operational.filter(row=>n(row.guide_kg)>0&&Math.abs(n(row.guide_kg)-n(row.received_kg))/n(row.guide_kg)>=.15);constover=operational.filter(row=>row.guide_kg!=null&&row.received_kg!=null&&n(row.received_kg)>n(row.guide_kg));constmonths=newMap<number,{guide:number;received:number}>();for(constrowofoperational){constraw=row.event_date??row.production_date??row.process_date??row.reception_date;if(!raw)continue;constdate=newDate(raw);if(!Number.isFinite(date.getTime())||date.getFullYear()!==2025)continue;constmonth=date.getMonth(),entry=months.get(month)??{guide:0,received:0};entry.guide+=n(row.guide_kg);entry.received+=n(row.received_kg);months.set(month,entry)}constmonthly=[...months.entries()].sort(([a],[b])=>a-b).map(([month,value])=>({month:monthNames[month],...value,difference:value.guide>0?((value.guide-value.received)/value.guide)*100:0}));constproviderMap=newMap<string,{name:string;lots:number;guide:number;received:number;rejectionWeighted:number;rejectionWeight:number}>();for(constrowofoperational){constname=row.supplier_name??row.supplier_original??'Proveedor no identificado',id=key(name),entry=providerMap.get(id)??{name,lots:0,guide:0,received:0,rejectionWeighted:0,rejectionWeight:0};constguide=n(row.guide_kg),rejection=row.yields?.rechazo;entry.lots++;entry.guide+=guide;entry.received+=n(row.received_kg);if(rejection!=null&&Number.isFinite(Number(rejection))&&guide>0){entry.rejectionWeighted+=Number(rejection)*guide;entry.rejectionWeight+=guide}providerMap.set(id,entry)}constproviderPerformance=[...providerMap.values()].sort((a,b)=>b.guide-a.guide).slice(0,5).map(item=>({name:item.name,lots:item.lots,guide:item.guide,difference:item.guide>0?((item.guide-item.received)/item.guide)*100:0,rejection:item.rejectionWeight>0?(item.rejectionWeighted/item.rejectionWeight)*100:0}));constexceptions=[...operational].map(row=>{constguide=n(row.guide_kg),received=n(row.received_kg),ratio=guide>0?Math.abs(guide-received)/guide:0,text=(row.observations??'').toLowerCase();constunsafe=/(vidrio|tijereta|plastico|plástico)/.test(text),receivedOver=guide>0&&received>guide;return{row,score:unsafe?4:receivedOver?3:ratio>=.15?2:/(rechaz|respaldo|malacalidad|sinproducto)/.test(text)?1:0,type:unsafe?'Inocuidad':receivedOver?'Peso recibido mayor a guía':ratio>=.15?'Diferencia de peso ≥15%':'Observación de fuente'}}).filter(item=>item.score>0).sort((a,b)=>b.score-a.score||a.row.source_row-b.row.source_row).slice(0,4).map(item=>({id:`EX-${item.row.source_row}`,lot:item.row.lot_code,type:item.type,detail:`${item.row.guide_kg==null?'Sin kg guía':kg(n(item.row.guide_kg))} · ${item.row.received_kg==null?'sin kg recibidos':kg(n(item.row.received_kg))}${item.row.observations?` · ${item.row.observations}`:''}`,severity:item.score>=3?'Crítica':'Alta'}));return{records:records.length,operational:operational.length,guideKg,receivedKg,differenceKg:guideKg-receivedKg,providers:providers.size,duplicateGuides:duplicateValues(operational,row=>row.guide_number),duplicateLots:duplicateValues(operational,row=>row.lot_code),negativeDifferences:over.length,criticalDifferences:critical.length,monthly,providerPerformance,exceptions}}
13
-
exportfunctionCanonical2025(){const{records,loading,error}=useLot360();constdata=useMemo(()=>model(records),[records]);constmax=Math.max(1,...data.monthly.map(item=>item.guide));if(loading&&!records.length)return<><PageHeadereyebrow="Fuente canónica · Neon"title="Operación productiva real"description="Calculando el período directamente desde la base canónica."/><divclassName="panel empty-state"><Databasesize={28}/><h3>Sincronizando datos canónicos…</h3></div></>;if(error&&!records.length)return<><PageHeadereyebrow="Fuente canónica · Neon"title="Operación productiva real"description="No mostramos cifras estáticas cuando la fuente real no está disponible."/><divclassName="notice error">{error}</div></>;return<><PageHeadereyebrow="Fuente canónica · Neon"title="Operación productiva real"description="Todos los indicadores se calculan desde los registros canónicos vigentes; una reimportación validada actualiza esta vista sin mantener una copia paralela."/><sectionclassName="metric-grid canonical-metrics"><Metricicon={<Database/>}label="Registros"value={String(data.records)}note={`${data.operational} operacionales · ${data.records-data.operational} anulados`}/><Metricicon={<Scale/>}label="Kilos guía"value={kg(data.guideKg)}note={`${kg(data.receivedKg)} recibidos`}/><Metricicon={<FileWarning/>}label="Diferencia registrada"value={kg(data.differenceKg)}note={data.guideKg?`${((data.differenceKg/data.guideKg)*100).toFixed(1)}% sobre kilos guía`:'Sin base de kilos guía'}warning/><Metricicon={<Users/>}label="Proveedores"value={String(data.providers)}note="Derivados de la fuente canónica"/></section><sectionclassName="canonical-focus"><articleclassName="panel monthly-panel"><headerclassName="panel-header"><h2>Recepción mensual</h2><span>Guía vs. recibido</span></header>{data.monthly.length?<><divclassName="month-chart">{data.monthly.map(item=><divclassName="month-column"key={item.month}><divclassName="month-bars"><istyle={{height:`${item.guide/max*100}%`}}/><iclassName="received"style={{height:`${item.received/max*100}%`}}/></div><b>{item.month}</b><small>{item.difference.toFixed(1)}%</small></div>)}</div><divclassName="chart-legend"><span><i/>Kilos guía</span><span><iclassName="received"/>Kilos recibidos</span></div></>:<divclassName="empty-state"><span>Sin fechas válidas para construir la serie.</span></div>}</article></section><sectionclassName="analysis-disclosures"><Disclosuretitle="Calidad de datos"meta={`${data.criticalDifferences} diferencias críticas`}><divclassName="audit-panel"><Auditvalue={data.duplicateGuides}label="Guías repetidas"/><Auditvalue={data.duplicateLots}label="Lotes repetidos"/><Auditvalue={data.negativeDifferences}label="Recepciones sobre guía"/><Auditvalue={data.criticalDifferences}label="Diferencias de 15% o más"danger/></div></Disclosure><Disclosuretitle="Desempeño por proveedor"meta={`${data.providerPerformance.length} proveedores principales`}><divclassName="table-scroll"><tableclassName="data-table"><thead><tr><th>Proveedor</th><thclassName="numeric">Lotes</th><thclassName="numeric">Kilos guía</th><thclassName="numeric">Diferencia</th><thclassName="numeric">Rechazo / guía</th></tr></thead><tbody>{data.providerPerformance.map(provider=><trkey={provider.name}><td><b>{provider.name}</b></td><tdclassName="numeric">{provider.lots}</td><tdclassName="numeric">{kg(provider.guide)}</td><tdclassName={`numeric ${provider.difference>8?'negative':''}`}>{provider.difference>0?<ArrowUpRightsize={12}/>:<ArrowDownRightsize={12}/>}{Math.abs(provider.difference).toFixed(2)}%</td><tdclassName={`numeric ${provider.rejection>15?'negative':''}`}>{provider.rejection.toFixed(2)}%</td></tr>)}</tbody></table></div></Disclosure><Disclosuretitle="Excepciones documentadas"meta={`${data.exceptions.length} casos prioritarios`}><divclassName="canonical-exceptions">{data.exceptions.map(item=><divkey={item.id}><AlertTrianglesize={16}/><span><b>{item.type}</b><small>{item.lot} · {item.detail}</small></span><emclassName={item.severity==='Crítica'?'critical':''}>{item.severity}</em></div>)}</div></Disclosure></section></>}
13
+
functionmodel(records:HistoricalRecord[]){constperiod=records.filter(record=>yearOf(record)===2025);constoperational=period.filter(record=>record.record_status==='operational');constguideKg=operational.reduce((sum,row)=>sum+n(row.guide_kg),0),receivedKg=operational.reduce((sum,row)=>sum+n(row.received_kg),0);constproviders=newSet(operational.map(row=>key(row.supplier_name??row.supplier_original)).filter(Boolean));constcritical=operational.filter(row=>n(row.guide_kg)>0&&Math.abs(n(row.guide_kg)-n(row.received_kg))/n(row.guide_kg)>=.15);constover=operational.filter(row=>row.guide_kg!=null&&row.received_kg!=null&&n(row.received_kg)>n(row.guide_kg));constmonths=newMap<number,{guide:number;received:number}>();for(constrowofoperational){constraw=row.event_date??row.production_date??row.process_date??row.reception_date;if(!raw)continue;constdate=newDate(raw);if(!Number.isFinite(date.getTime()))continue;constmonth=date.getMonth(),entry=months.get(month)??{guide:0,received:0};entry.guide+=n(row.guide_kg);entry.received+=n(row.received_kg);months.set(month,entry)}constmonthly=[...months.entries()].sort(([a],[b])=>a-b).map(([month,value])=>({month:monthNames[month],...value,difference:value.guide>0?((value.guide-value.received)/value.guide)*100:0}));constproviderMap=newMap<string,{name:string;lots:number;guide:number;received:number;rejectionWeighted:number;rejectionWeight:number}>();for(constrowofoperational){constname=row.supplier_name??row.supplier_original??'Proveedor no identificado',id=key(name),entry=providerMap.get(id)??{name,lots:0,guide:0,received:0,rejectionWeighted:0,rejectionWeight:0};constguide=n(row.guide_kg),rejection=row.yields?.rechazo;entry.lots++;entry.guide+=guide;entry.received+=n(row.received_kg);if(rejection!=null&&Number.isFinite(Number(rejection))&&guide>0){entry.rejectionWeighted+=Number(rejection)*guide;entry.rejectionWeight+=guide}providerMap.set(id,entry)}constproviderPerformance=[...providerMap.values()].sort((a,b)=>b.guide-a.guide).slice(0,5).map(item=>({name:item.name,lots:item.lots,guide:item.guide,difference:item.guide>0?((item.guide-item.received)/item.guide)*100:0,rejection:item.rejectionWeight>0?(item.rejectionWeighted/item.rejectionWeight)*100:0}));constexceptions=[...operational].map(row=>{constguide=n(row.guide_kg),received=n(row.received_kg),ratio=guide>0?Math.abs(guide-received)/guide:0,text=(row.observations??'').toLowerCase();constunsafe=/(vidrio|tijereta|plastico|plástico)/.test(text),receivedOver=guide>0&&received>guide;return{row,score:unsafe?4:receivedOver?3:ratio>=.15?2:/(rechaz|respaldo|malacalidad|sinproducto)/.test(text)?1:0,type:unsafe?'Inocuidad':receivedOver?'Peso recibido mayor a guía':ratio>=.15?'Diferencia de peso ≥15%':'Observación de fuente'}}).filter(item=>item.score>0).sort((a,b)=>b.score-a.score||a.row.source_row-b.row.source_row).slice(0,4).map(item=>({id:`EX-${item.row.source_row}`,lot:item.row.lot_code,type:item.type,detail:`${item.row.guide_kg==null?'Sin kg guía':kg(n(item.row.guide_kg))} · ${item.row.received_kg==null?'sin kg recibidos':kg(n(item.row.received_kg))}${item.row.observations?` · ${item.row.observations}`:''}`,severity:item.score>=3?'Crítica':'Alta'}));return{records:period.length,operational:operational.length,guideKg,receivedKg,differenceKg:guideKg-receivedKg,providers:providers.size,duplicateGuides:duplicateValues(operational,row=>row.guide_number),duplicateLots:duplicateValues(operational,row=>row.lot_code),negativeDifferences:over.length,criticalDifferences:critical.length,monthly,providerPerformance,exceptions}}
14
+
exportfunctionCanonical2025(){const{records,loading,error}=useLot360();constdata=useMemo(()=>model(records),[records]);constmax=Math.max(1,...data.monthly.map(item=>item.guide));if(loading&&!records.length)return<><PageHeadereyebrow="Fuente canónica · Neon · 2025"title="Operación productiva real"description="Calculando el período 2025 directamente desde la base canónica."/><divclassName="panel empty-state"><Databasesize={28}/><h3>Sincronizando datos canónicos…</h3></div></>;if(error&&!records.length)return<><PageHeadereyebrow="Fuente canónica · Neon · 2025"title="Operación productiva real"description="No mostramos cifras estáticas cuando la fuente real no está disponible."/><divclassName="notice error">{error}</div></>;return<><PageHeadereyebrow="Fuente canónica · Neon · 2025"title="Operación productiva real"description="Todos los indicadores están cerrados al período 2025 y se calculan desde Neon; los nombres históricos de planta se conservan tal como vienen de la fuente."/><sectionclassName="metric-grid canonical-metrics"><Metricicon={<Database/>}label="Registros"value={String(data.records)}note={`${data.operational} operacionales · ${data.records-data.operational} anulados`}/><Metricicon={<Scale/>}label="Kilos guía"value={kg(data.guideKg)}note={`${kg(data.receivedKg)} recibidos`}/><Metricicon={<FileWarning/>}label="Diferencia registrada"value={kg(data.differenceKg)}note={data.guideKg?`${((data.differenceKg/data.guideKg)*100).toFixed(1)}% sobre kilos guía`:'Sin base de kilos guía'}warning/><Metricicon={<Users/>}label="Proveedores"value={String(data.providers)}note="Derivados de la fuente canónica"/></section><sectionclassName="canonical-focus"><articleclassName="panel monthly-panel"><headerclassName="panel-header"><h2>Recepción mensual</h2><span>Guía vs. recibido</span></header>{data.monthly.length?<><divclassName="month-chart">{data.monthly.map(item=><divclassName="month-column"key={item.month}><divclassName="month-bars"><istyle={{height:`${item.guide/max*100}%`}}/><iclassName="received"style={{height:`${item.received/max*100}%`}}/></div><b>{item.month}</b><small>{item.difference.toFixed(1)}%</small></div>)}</div><divclassName="chart-legend"><span><i/>Kilos guía</span><span><iclassName="received"/>Kilos recibidos</span></div></>:<divclassName="empty-state"><span>Sin fechas válidas para construir la serie.</span></div>}</article></section><sectionclassName="analysis-disclosures"><Disclosuretitle="Calidad de datos"meta={`${data.criticalDifferences} diferencias críticas`}><divclassName="audit-panel"><Auditvalue={data.duplicateGuides}label="Guías repetidas"/><Auditvalue={data.duplicateLots}label="Lotes repetidos"/><Auditvalue={data.negativeDifferences}label="Recepciones sobre guía"/><Auditvalue={data.criticalDifferences}label="Diferencias de 15% o más"danger/></div></Disclosure><Disclosuretitle="Desempeño por proveedor"meta={`${data.providerPerformance.length} proveedores principales`}><divclassName="table-scroll"><tableclassName="data-table"><thead><tr><th>Proveedor</th><thclassName="numeric">Lotes</th><thclassName="numeric">Kilos guía</th><thclassName="numeric">Diferencia</th><thclassName="numeric">Rechazo / guía</th></tr></thead><tbody>{data.providerPerformance.map(provider=><trkey={provider.name}><td><b>{provider.name}</b></td><tdclassName="numeric">{provider.lots}</td><tdclassName="numeric">{kg(provider.guide)}</td><tdclassName={`numeric ${provider.difference>8?'negative':''}`}>{provider.difference>0?<ArrowUpRightsize={12}/>:<ArrowDownRightsize={12}/>}{Math.abs(provider.difference).toFixed(2)}%</td><tdclassName={`numeric ${provider.rejection>15?'negative':''}`}>{provider.rejection.toFixed(2)}%</td></tr>)}</tbody></table></div></Disclosure><Disclosuretitle="Excepciones documentadas"meta={`${data.exceptions.length} casos prioritarios`}><divclassName="canonical-exceptions">{data.exceptions.map(item=><divkey={item.id}><AlertTrianglesize={16}/><span><b>{item.type}</b><small>{item.lot} · {item.detail}</small></span><emclassName={item.severity==='Crítica'?'critical':''}>{item.severity}</em></div>)}</div></Disclosure></section></>}
0 commit comments