(() => { 'use strict'; const $ = id => document.getElementById(id); const esc = value => String(value ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); let state = {}, products = [], refs = [], offset = 0, saveForm; const labels = {pending:'Afventer',applying:'Udfører',approved:'Godkendt og udført',rejected:'Afvist',cancelled:'Annulleret',running:'Synkroniserer',retry:'Prøver igen',done:'Gennemført',failed:'Fejl',uncertain:'Uafklaret',synced:'Synkroniseret',local_only:'Kun i Hub',unlinked:'Ikke koblet',conflict:'Konflikt'}; async function api(path, method='GET', body) { const res = await fetch('/api/v1' + path, {method,headers:{'Content-Type':'application/json'},body:body === undefined ? undefined : JSON.stringify(body)}); const data = await res.json(); if (!res.ok) throw Error(typeof data.detail === 'string' ? data.detail : `Kaldet fejlede (${res.status})`); return data; } function feedback(message,error=false) { $('ecoFeedback').textContent=message; $('ecoFeedback').className='alert '+(error?'alert-danger':'alert-success'); } const connection = () => Number($('ecoConnection').value); function field(name,label,type='text',value='') { return ``; } function select(name,label,items) { return ``; } function modal(title,fields,save) { $('ecoDialogTitle').textContent=title; $('ecoFields').innerHTML=fields; $('ecoFormError').textContent=''; saveForm=save; $('ecoDialog').showModal(); } async function refresh() { state=await api('/economic/catalog/status'); const chosen=$('ecoConnection').value; $('ecoConnection').innerHTML=state.connections.map(c=>``).join(''); if(chosen) $('ecoConnection').value=chosen; const active=state.connections.find(c=>c.id===connection())?.enabled; $('ecoMode').textContent=state.read_only||state.dry_run?'Safety mode: kun læsning':!active?'Integration ikke aktiveret':`Live-skrivning aktiveret${state.order_read_only||state.order_dry_run?' · Ordreeksport er dog blokeret af modulindstilling':''}`; $('ecoJobRows').innerHTML=state.jobs.map(j=>`
Hub opretter kun en ordrekladde. Fakturakladder, bogføring og afsendelse ændres aldrig fra Hub.
',async d=>{ const draft=await api(`/ordre/drafts/${Number(d.draft_id)}`); const body={document_key:`order-draft:${draft.id}`,customer_id:draft.customer_id,lines:draft.lines_json,layout_number:draft.layout_number,currency:'DKK',notes:draft.notes}; const preview=await api('/economic/documents/order/preview','POST',body); const total=preview.lines.reduce((sum,l)=>sum+Number(l.net_total),0).toFixed(2); if(!window.confirm(`${preview.lines.length} linjer · ${total} DKK ekskl. moms.\nOpret ordrekladde i e-conomic?`))throw Error('Eksport annulleret; intet sendt.'); await api('/economic/documents/order/export','POST',body); }); } if(name==='preview'||name==='import') { const j=await api(`/economic/catalog/connections/${connection()}/sync?preview=${name==='preview'}`,'POST');feedback('Job oprettet: '+j.id);return refresh(); } if(name==='config') { const c=state.connections.find(x=>x.id===connection()); await loadRefs(); modal('Automatisk synkronisering',`Forslaget ændrer intet nu. En anden godkender skal kontrollere før/efter og godkende.
',d=>{let value=d.value;if(d.field==='salesPrice')value=Number(value);if(d.field==='barred')value=String(value).toLowerCase()==='true';return api('/economic/change-requests','POST',{connection_id:connection(),target_type:d.target_type,target_number:d.target_number,changes:{[d.field]:value},reason:d.reason});}); } $('economicCatalog').addEventListener('click',async event=>{try { const b=event.target.closest('button');if(!b)return; if(b.dataset.tab) {for(const t of ['products','groups','rules','changes','jobs']) $('eco'+t[0].toUpperCase()+t.slice(1)).hidden=t!==b.dataset.tab;document.querySelectorAll('[data-tab]').forEach(n=>n.classList.toggle('active',n===b));if(b.dataset.tab==='groups')await loadRefs();if(b.dataset.tab==='rules')await loadRules();} if(b.dataset.action)await action(b.dataset.action); if(b.dataset.retry){await api(`/economic/catalog/jobs/${b.dataset.retry}/retry`,'POST');await refresh();} if(b.dataset.reconcileProduct){await api(`/economic/catalog/jobs/${b.dataset.reconcileProduct}/reconcile`,'POST');await refresh();await loadProducts();} if(b.dataset.reconcileExport){await api(`/economic/documents/exports/${b.dataset.reconcileExport}/reconcile`,'POST');await refresh();} if(b.dataset.retryExport){await api(`/economic/documents/exports/${b.dataset.retryExport}/retry`,'POST');await refresh();} if(b.dataset.rebind){const p=products.find(x=>x.id===Number(b.dataset.rebind));modal('Administrativ omkobling: '+p.name,`Dette er en særskilt ændring af varens permanente kobling. Historiske dokumenter ændres ikke. Nuværende nummer: ${esc(p.economic_product_number)}
`+field('confirmation_number','Skriv det nuværende nummer for at bekræfte')+field('number','Nyt eksisterende varenummer på den valgte aftale')+field('reason','Begrundelse (mindst 10 tegn)'),d=>api(`/products/${p.id}/economic-rebind`,'POST',{...d,connection_id:connection()}));} if(b.dataset.price){const p=products.find(x=>x.id===Number(b.dataset.price));modal('Hub-pris: '+p.name,field('sales_price','Standardpris ekskl. moms','number',p.sales_price??'')+field('sales_currency','Valuta','text',p.sales_currency||'DKK')+'Dette ændrer ikke referenceprisen i e-conomic eller eksisterende aftalepriser.
',d=>api(`/products/${p.id}/economic-pricing`,'PATCH',d));} if(b.dataset.state){const p=products.find(x=>x.id===Number(b.dataset.state));modal('Intern status: '+p.name,''+field('category_id','Kategori-ID','number',p.category_id||'')+field('subcategory_id','Underkategori-ID','number',p.subcategory_id||''),d=>api(`/products/${p.id}/economic-state`,'PATCH',{...d,category_id:Number(d.category_id)||null,subcategory_id:Number(d.subcategory_id)||null}));} if(b.dataset.rule){await api(`/economic/catalog/price-rules/${b.dataset.rule}`,'DELETE');await loadRules();} if(b.dataset.group){const r=refs.find(x=>x.kind==='product-groups'&&x.number===Number(b.dataset.group));modal('Varegruppe '+r.name,field('category_id','Standardkategori-ID','number',r.category_id||'')+`Låst e-conomic-varenummer: ${esc(p.economic_product_number)}
Navnet gemmes kun i Hub. Det ændrer aldrig den eksisterende e-conomic-vare.
`+(p.economic_sync_status==='conflict'?'':''),d=>api(`/products/${p.id}`,'PATCH',{name:d.name}));else modal(p.name,''+field('number','Eksisterende varenummer (ved tilknytning)')+select('group','Varegruppe (ved oprettelse)',refs.filter(r=>r.kind==='product-groups'&&r.allow_new&&!r.phasing_out))+select('unit','Enhed (ved oprettelse)',refs.filter(r=>r.kind==='units')),d=>api(`/products/${p.id}/economic-${d.operation}`,'POST',d.operation==='link'?{connection_id:connection(),number:d.number}:{connection_id:connection(),group_number:Number(d.group),unit_number:Number(d.unit)||null}));} if(b.dataset.acceptEconomic){await api(`/products/${b.dataset.acceptEconomic}/economic-resolve`,'POST',{choice:'economic'});await loadProducts();} if(b.dataset.approveChange){if(window.confirm('Godkend og udfør denne ændring i e-conomic?')){await api(`/economic/change-requests/${b.dataset.approveChange}/approve`,'POST',{note:'Godkendt i Hub'});await refresh();}} if(b.dataset.rejectChange){const note=window.prompt('Hvorfor afvises ændringen?','Afvist efter kontrol');if(note!==null){await api(`/economic/change-requests/${b.dataset.rejectChange}/reject`,'POST',{note});await refresh();}} }catch(e){feedback(e.message,true);}}); $('ecoForm').addEventListener('submit',async e=>{e.preventDefault();const button=e.submitter||e.target.querySelector('[type="submit"]');button.disabled=true;try{await saveForm(Object.fromEntries(new FormData(e.target)));$('ecoDialog').close();feedback('Gemt. Eventuel synkronisering kan følges under Sync og fejl.');await refresh();await loadProducts();await loadRefs();if(!$('ecoRules').hidden)await loadRules();}catch(error){$('ecoFormError').textContent=error.message;}finally{button.disabled=false;}}); $('ecoCancel').onclick=()=>$('ecoDialog').close(); $('ecoConnection').onchange=()=>loadRefs().catch(e=>feedback(e.message,true)); $('ecoInactive').onchange=()=>loadProducts().catch(e=>feedback(e.message,true)); refresh().then(loadProducts).catch(e=>feedback(e.message,true)); setInterval(()=>{if(!document.hidden&&!$('ecoDialog').open)refresh().catch(e=>feedback(e.message,true));},15000); })();