ComfortPlus Panel Produkcji
Panel zleceń produkcyjnych
Czytelny widok danych przeniesionych z Excela do formy programu.
| Nr | Zamawiający | Opis | m² | Kolor | Termin | Status | Uwagi |
|---|
| Rok | Miesiąc | Wzór | Kolor | Zamawiający | m² | Pozycji |
|---|
Ustawienia importu i eksportu
Eksport produkcyjny zachowuje układ Excela. Import historyczny zasila tylko statystyki, bez dopisywania do aktywnych zleceń.
Ustawienia konta
Aktualnie zalogowany użytkownik:
—
Słowniki produktów
Docelowo tutaj będą edytowane wzory: deska Natur/Standard/Rustic, jodełka francuska, jodełka angielska, kolory A1–F24, zamawiający i pracownicy.
Widok wewnętrzny ComfortPlus
Uwagi produkcyjne
Widok dla klienta
Tak klient widziałby status swojego zlecenia po zalogowaniu lub przez prywatny link.
1. Zaliczka—
2. Po dostawie—
3. Po realizacji—
';
const blob=new Blob(['\ufeff',html],{type:'application/vnd.ms-excel;charset=utf-8'});
const url=URL.createObjectURL(blob); const a=document.createElement('a'); a.href=url; a.download='comfortplus_panel_produkcji_export.xls'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(url),1000);
}
function valueOf(row, names){for(const n of names){if(row[n]!==undefined&&row[n]!==null&&String(row[n]).trim()!=='')return row[n];}return'';}
function importExcel(evt){
const file=evt.target.files&&evt.target.files[0]; if(!file)return;
const reader=new FileReader();
reader.onload=function(e){
try{
let rows=[];
if(file.name.toLowerCase().endsWith('.html')||file.name.toLowerCase().endsWith('.htm')||file.name.toLowerCase().endsWith('.xls')){
const text=e.target.result; const doc=new DOMParser().parseFromString(text,'text/html'); const table=doc.querySelector('table');
if(table){const trs=[...table.querySelectorAll('tr')]; const headerTr=trs.find(tr=>[...tr.children].some(td=>/Nr zlecenia|Zamawiający|Produkt/.test(td.textContent))); const headers=[...headerTr.children].map(td=>td.textContent.trim()); const start=trs.indexOf(headerTr)+1; rows=trs.slice(start).map(tr=>{const obj={}; [...tr.children].forEach((td,i)=>obj[headers[i]||('col'+i)]=td.textContent.trim()); return obj;}).filter(r=>Object.values(r).some(Boolean));}
}
if(!rows.length){
if(typeof XLSX==='undefined'){alert('Import .xlsx wymaga połączenia z biblioteką XLSX. Użyj eksportu .xls z panelu albo odśwież z internetem.'); evt.target.value=''; return;}
const data=new Uint8Array(e.target.result); const wb=XLSX.read(data,{type:'array',cellDates:false}); const ws=wb.Sheets[wb.SheetNames[0]]; rows=XLSX.utils.sheet_to_json(ws,{defval:''});
}
const imported=[];
rows.forEach((r,idx)=>{
const orderNo=String(valueOf(r,['Nr zlecenia','Nr','Zamówienie','order'])).trim();
const client=String(valueOf(r,['Zamawiający','Klient','client'])).trim();
const desc=String(valueOf(r,['Produkt / opis','Opis','Produkt','desc'])).trim();
if(!orderNo&&!client&&!desc)return;
const deadlineRaw=String(valueOf(r,['Data oddania','Termin','deadline'])).trim();
const deadlineIso=/^\d{4}-\d{2}-\d{2}$/.test(deadlineRaw)?deadlineRaw:isoFromDisplay(deadlineRaw);
const acceptedRaw=String(valueOf(r,['Data przyjęcia','Przyjęcie','acceptedDate'])).trim();
const acceptedIso=/^\d{4}-\d{2}-\d{2}$/.test(acceptedRaw)?acceptedRaw:isoFromDisplay(acceptedRaw);
const qtyRaw=String(valueOf(r,['Ilość','m²','m2'])).replace(',','.'); const qty=Number(qtyRaw);
const status=String(valueOf(r,['Status','STATUS PRODUKCJI','status'])).trim()||'Zlecenie';
const o={id:Date.now()+idx, month:monthFromIso(deadlineIso||deadlineRaw), row:0, order:orderNo||'Import Excel', client:client||'—', desc:desc||'—', m2:isNaN(qty)?qtyRaw:qty, notes:String(valueOf(r,['Uwagi','Uwagi produkcyjne','notes'])).trim(), color:String(valueOf(r,['Kolor','KOLOR','color'])).trim(), deadline:deadlineIso?displayFromIso(deadlineIso):deadlineRaw, deadlineIso, status, progress:progressFromStatus(status), adnot:'', urgent:/urgent|pilne|tak/i.test(String(valueOf(r,['Urgent','Priorytet']))), onStock:/tak|magazyn/i.test(String(valueOf(r,['Na magazynie']))), issued:/tak|wydano/i.test(String(valueOf(r,['Wydano']))), doneStages:stagesForStatus(status), position:String(valueOf(r,['Pozycja A-J','Pozycja','position'])).trim(), acceptedDate:acceptedIso, packQty:String(valueOf(r,['Ilość w paczce','packQty'])).trim(), responsibility:{}, createdBy:{login:CURRENT_USER?.login||'import',name:CURRENT_USER?.name||'Import Excel',role:CURRENT_USER?.role||'Import'}, updateLog:[], payments:{deposit:'brak danych',afterDelivery:'brak danych',final:'brak danych'}};
RESPONSIBILITY_STAGES.forEach(st=>{const val=String(valueOf(r,[st+' odpowiedzialny',st])).replace('GOTOWE','').trim(); o.responsibility[st]=val;});
addAudit(o,'Import','Zaimportowano z pliku Excel: '+file.name); imported.push(o);
});
if(!imported.length){alert('Nie znalazłem w pliku zleceń do importu.'); evt.target.value=''; return;}
const saved=getSavedOrders(); localStorage.setItem('cp_new_orders',JSON.stringify([...imported,...saved])); rebuildData(); refreshFilters(); render(); alert('Zaimportowano zlecenia: '+imported.length);
}catch(err){console.error(err); alert('Nie udało się zaimportować pliku. Sprawdź format arkusza.');}
evt.target.value='';
};
if(file.name.toLowerCase().endsWith('.html')||file.name.toLowerCase().endsWith('.htm')||file.name.toLowerCase().endsWith('.xls')) reader.readAsText(file,'utf-8'); else reader.readAsArrayBuffer(file);
}
// nadpisanie zapisu zlecenia: miesiąc wynika z daty oddania, np. 06.12.2026 => Grudzień i pojawia się w menu
submitNewOrder=function(e){e.preventDefault(); if(!CURRENT_USER){alert('Najpierw zaloguj się do panelu.');return;} const manualClient=document.getElementById('newClientManual').value.trim(); const client=manualClient||document.getElementById('newClient').value||'—'; const manualProduct=document.getElementById('newProductManual').value.trim(); const desc=manualProduct||document.getElementById('newProduct').value||'Własny wpis produktu'; const manualColor=document.getElementById('newColorManual').value.trim(); const color=manualColor||document.getElementById('newColor').value||''; const qtyRaw=document.getElementById('newQty').value.trim(); const qty=Number(qtyRaw.replace(',','.')); const accepted=document.getElementById('newAccepted').value; const deadlineIso=document.getElementById('newDeadline').value; const order={id:Date.now(), month:monthFromIso(deadlineIso), row:0, order:document.getElementById('newOrderNo').value.trim()||'Nowe zlecenie', client, desc, m2:isNaN(qty)?qtyRaw:qty, notes:document.getElementById('newNotes').value.trim(), color, deadline:displayFromIso(deadlineIso), deadlineIso, status:'Zlecenie', progress:16, adnot:'', urgent:document.getElementById('newUrgent').checked, onStock:false, issued:false, doneStages:['Zlecenie'], position:document.getElementById('newPosition').value, acceptedDate:accepted, packQty:document.getElementById('newPackQty').value.trim(), responsibility:readResponsibility('new'), createdBy:{login:CURRENT_USER.login,name:CURRENT_USER.name,role:CURRENT_USER.role}, updateLog:[], payments:{deposit:'brak danych',afterDelivery:'brak danych',final:'brak danych'}}; addAudit(order,'Utworzenie','Przyjęto nowe zlecenie do produkcji'); const saved=getSavedOrders(); saved.unshift(order); localStorage.setItem('cp_new_orders',JSON.stringify(saved)); rebuildData(); closeNewOrder(); e.target.reset(); initNewOrderForm(); refreshFilters(); selectedId=order.id; render(); openModal(order.id);};
const oldSaveEditOrderMonth=saveEditOrder;
saveEditOrder=function(e){oldSaveEditOrderMonth(e); DATA.forEach(normalizeOrderMonth); refreshFilters(); render();};
init();