๐Ÿฝ
MPOS
Kassensystem
PIN vergessen? Recovery-Code eingeben
Kasse / POS
<
>
๐Ÿ›’ Warenkorb
Gesamt 0,00 โ‚ฌ
๐ŸŒ Online-Bestellungen
Keine Online-Bestellungen
"; return h; } catch(e){ return "
A4-Fehler: "+e.message+"
"+e.stack+"
"; } } // โ•โ• SCHNELLE PREISBEARBEITUNG โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• var quickPriceDraft = {}; var quickPriceSelected = {}; function _qpNum(v){ if(typeof v==="number") return isFinite(v)?v:0; v=String(v==null?"":v).trim().replace(/\s/g,"").replace(",","."); var n=parseFloat(v); return isFinite(n)?n:0; } function _qpFmt(v){ return _qpNum(v).toFixed(2).replace(".",","); } function openQuickPriceEditor(){ quickPriceDraft={}; quickPriceSelected={}; products.forEach(function(p){ quickPriceDraft[p.id]={priceRest:_qpNum(p.priceRest),priceTake:_qpNum(p.priceTake),priceDel:_qpNum(p.priceDel)}; }); var cat=document.getElementById("qp-cat"); if(cat){ var cats=typeof allCats==="function"?allCats():Array.from(new Set(products.map(function(p){return p.cat||"Sonstiges";}))).sort(); cat.innerHTML=''+cats.map(function(c){ return ''; }).join(""); } var s=document.getElementById("qp-search"); if(s)s.value=""; renderQuickPrices(); openModal("modal-quick-prices"); } function _qpVisibleProducts(){ var q=((document.getElementById("qp-search")||{}).value||"").toLowerCase().trim(); var cat=(document.getElementById("qp-cat")||{}).value||""; return products.filter(function(p){ if(cat&&p.cat!==cat)return false; return !q||String(p.id).indexOf(q)>=0||String(p.name||"").toLowerCase().indexOf(q)>=0; }); } function renderQuickPrices(){ var body=document.getElementById("qp-tbody"); if(!body)return; var list=_qpVisibleProducts(); body.innerHTML=list.map(function(p){ var d=quickPriceDraft[p.id]||{priceRest:_qpNum(p.priceRest),priceTake:_qpNum(p.priceTake),priceDel:_qpNum(p.priceDel)}; return ''+ ''+ ''+p.id+''+ ''+String(p.name||"").replace(/'+ ''+String(p.cat||"").replace(/'+ ''+ ''+ ''+ ''+ ''; }).join(""); renderQuickPriceCount(); } function renderQuickPriceCount(){ var c=document.getElementById("qp-count"); if(!c)return; var selected=Object.keys(quickPriceSelected).filter(function(id){return quickPriceSelected[id];}).length; c.textContent=_qpVisibleProducts().length+" Artikel angezeigt ยท "+selected+" markiert"; } function quickPriceToggleAll(on){ _qpVisibleProducts().forEach(function(p){quickPriceSelected[p.id]=!!on;}); renderQuickPrices(); } function quickPriceCopyRest(id){ var d=quickPriceDraft[id]; if(!d)return; d.priceTake=d.priceRest; d.priceDel=d.priceRest; renderQuickPrices(); } function _qpSelectedIds(){ var ids=Object.keys(quickPriceSelected).filter(function(id){return quickPriceSelected[id];}).map(Number); return ids.length?ids:_qpVisibleProducts().map(function(p){return p.id;}); } function _qpFields(){ var v=(document.getElementById("qp-bulk-field")||{}).value||"all"; return v==="all"?["priceRest","priceTake","priceDel"]:[v]; } function applyQuickPricePercent(){ var raw=(document.getElementById("qp-percent")||{}).value; if(String(raw||"").trim()===""){notify("Prozentwert eingeben","var(--err)");return;} var n=_qpNum(raw), factor=1+n/100; if(factor<0){notify("ร„nderung zu groรŸ","var(--err)");return;} var ids=_qpSelectedIds(), fields=_qpFields(); ids.forEach(function(id){var d=quickPriceDraft[id];if(d)fields.forEach(function(f){d[f]=Math.round(_qpNum(d[f])*factor*100)/100;});}); renderQuickPrices(); notify("โœ“ "+(n>=0?"+":"")+n+"% auf "+ids.length+" Artikel angewendet"); } function applyQuickPriceFixed(){ var raw=(document.getElementById("qp-fixed")||{}).value; if(String(raw||"").trim()===""){notify("Preis eingeben","var(--err)");return;} var price=_qpNum(raw); if(price<0){notify("Ungรผltiger Preis","var(--err)");return;} var ids=_qpSelectedIds(), fields=_qpFields(); ids.forEach(function(id){var d=quickPriceDraft[id];if(d)fields.forEach(function(f){d[f]=price;});}); renderQuickPrices(); notify("โœ“ Preis auf "+ids.length+" Artikel gesetzt"); } function saveQuickPrices(){ var changed=0; products=products.map(function(p){ var d=quickPriceDraft[p.id]; if(!d)return p; var nr=Math.max(0,_qpNum(d.priceRest)),nt=Math.max(0,_qpNum(d.priceTake)),nd=Math.max(0,_qpNum(d.priceDel)); if(nr!==_qpNum(p.priceRest)||nt!==_qpNum(p.priceTake)||nd!==_qpNum(p.priceDel))changed++; return Object.assign({},p,{priceRest:nr,priceTake:nt,priceDel:nd,price:nr}); }); save(); renderProductsPage(); if(typeof renderPOSProducts==="function")renderPOSProducts(); try{if(typeof mobRenderProducts==="function")mobRenderProducts();}catch(e){} closeModal("modal-quick-prices"); notify("โœ“ "+changed+" Artikelpreise gespeichert","var(--ok)"); } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // UNIVERSAL PRODUCT IMPORT โ€” CSV / TXT / TSV / XLSX / XLS // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• var UIMP = { headers: [], rows: [], map: {}, fileName: "", fileType: "", parsed: false }; var UIMP_FIELDS = [ {key:"id", label:"Artikelnummer", aliases:["artikelnummer","artikelnr","artikel nr","nr","plu","sku","product id","productid","item id","itemid","code","artikelcode","product code"]}, {key:"name", label:"Name *", required:true, aliases:["name","artikelname","produktname","bezeichnung","artikel","produkt","item name","itemname","product name","productname"]}, {key:"cat", label:"Kategorie", aliases:["kategorie","warengruppe","gruppe","category","product group","productgroup","item group","department","family"]}, {key:"vat", label:"MwSt", aliases:["mwst","ust","umsatzsteuer","steuer","steuersatz","vat","tax","tax rate","taxrate"]}, {key:"priceRest", label:"Restaurantpreis", aliases:["restaurantpreis","restaurant preis","restaurant","preis","verkaufspreis","vk","price","sales price","selling price","unit price","unitprice"]}, {key:"priceTake", label:"Takeawaypreis", aliases:["takeawaypreis","takeaway preis","takeaway","take away","abholpreis","abholung","pickup price","pickup"]}, {key:"priceDel", label:"Lieferpreis", aliases:["lieferpreis","lieferung","deliverypreis","delivery price","delivery"]}, {key:"priceLieferando", label:"Lieferando", aliases:["lieferando","lieferando preis","lieferandopreis"]}, {key:"priceWolt", label:"Wolt", aliases:["wolt","wolt preis","woltpreis"]}, {key:"priceUberEats", label:"Uber Eats", aliases:["uber eats","ubereats","uber preis","ubereatspreis"]}, {key:"active", label:"Aktiv", aliases:["aktiv","active","enabled","sichtbar","status"]}, {key:"desc", label:"Beschreibung", aliases:["beschreibung","description","details","info","produktbeschreibung","artikelbeschreibung"]} ]; function uimpProfileKey(){ var rid = (typeof mposActiveRestaurantId==="function") ? mposActiveRestaurantId() : "default"; return "mpos_import_profiles_"+rid; } function uimpGetProfiles(){ try{ var x=JSON.parse(localStorage.getItem(uimpProfileKey())||"[]"); return Array.isArray(x)?x:[]; }catch(e){return [];} } function uimpSetProfiles(a){ try{localStorage.setItem(uimpProfileKey(),JSON.stringify(a||[]));}catch(e){} } function uimpRenderProfiles(){ var el=document.getElementById("uimp-profile"); if(!el)return; var cur=el.value; var a=uimpGetProfiles(); el.innerHTML=''+a.map(function(p,i){ return ''; }).join(""); if(cur!=="" && a[Number(cur)]) el.value=cur; } function uimpSaveProfile(){ if(!UIMP.headers.length){notify("Bitte zuerst eine Datei laden","var(--err)");return;} uimpReadMappingFromUI(); var name=prompt("Name fรผr dieses Importprofil:", UIMP.fileName ? UIMP.fileName.replace(/\.[^.]+$/,"") : "Importprofil"); if(!name || !name.trim())return; var arr=uimpGetProfiles(); arr.push({name:name.trim(),map:Object.assign({},UIMP.map),savedAt:new Date().toISOString()}); uimpSetProfiles(arr);uimpRenderProfiles(); var sel=document.getElementById("uimp-profile");if(sel)sel.value=String(arr.length-1); notify("โœ“ Importprofil gespeichert","var(--ok)"); } function uimpDeleteProfile(){ var sel=document.getElementById("uimp-profile");if(!sel||sel.value==="")return; var arr=uimpGetProfiles(), idx=Number(sel.value); if(!arr[idx])return; if(!confirm('Importprofil "'+(arr[idx].name||"")+'" lรถschen?'))return; arr.splice(idx,1);uimpSetProfiles(arr);uimpRenderProfiles(); notify("Importprofil gelรถscht"); } function uimpLoadSelectedProfile(){ var sel=document.getElementById("uimp-profile");if(!sel||sel.value==="")return; var p=uimpGetProfiles()[Number(sel.value)];if(!p)return; UIMP.map=Object.assign({},p.map||{}); uimpRenderMapping(); uimpRefreshPreview(); } function uimpEsc(v){ return String(v==null?"":v).replace(/&/g,"&").replace(//g,">").replace(/"/g,"""); } function uimpNorm(v){ return String(v==null?"":v).trim().toLowerCase() .normalize("NFD").replace(/[\u0300-\u036f]/g,"") .replace(/[_\-./\\()[\]{}]+/g," ").replace(/\s+/g," ").trim(); } function uimpNum(v){ if(typeof v==="number")return isFinite(v)?v:null; var s=String(v==null?"":v).trim(); if(!s)return null; s=s.replace(/[โ‚ฌ$ยฃ\s]/g,""); if(s.indexOf(",")>=0 && s.indexOf(".")>=0){ if(s.lastIndexOf(",")>s.lastIndexOf("."))s=s.replace(/\./g,"").replace(",","."); else s=s.replace(/,/g,""); }else if(s.indexOf(",")>=0){ s=s.replace(",","."); } var n=parseFloat(s); return isFinite(n)?n:null; } function uimpVat(v){ if(v===null||v===undefined||String(v).trim()==="")return 0.07; var n=uimpNum(String(v).replace("%","")); if(n===null)return 0.07; if(n>1)n=n/100; if(Math.abs(n-.19)<.03)return .19; if(Math.abs(n-.07)<.03)return .07; return n>=.13?.19:.07; } function uimpBool(v){ var s=uimpNorm(v); if(!s)return true; if(["0","nein","no","false","inaktiv","inactive","disabled","aus"].indexOf(s)>=0)return false; return true; } function uimpDetectDelimiter(line){ var chars=[";",",","\t","|"],best=";",bestCount=-1; chars.forEach(function(d){ var q=false,c=0; for(var i=0;ibestCount){bestCount=c;best=d;} }); return best; } function uimpParseDelimited(txt){ txt=String(txt||"").replace(/^\uFEFF/,""); var first=(txt.split(/\r?\n/).find(function(l){return l.trim();})||""); var delim=uimpDetectDelimiter(first); var rows=[],row=[],field="",q=false; for(var i=0;i1 ? b+" ("+seen[b]+")" : b; }); } function uimpAutoMap(){ var norms=UIMP.headers.map(uimpNorm), used={}; UIMP.map={}; UIMP_FIELDS.forEach(function(f){ var best=-1; for(var a=0;a=0 || al.indexOf(norms[j])>=0){best=j;break;} } } } if(best>=0){UIMP.map[f.key]=UIMP.headers[best];used[best]=true;} else UIMP.map[f.key]=""; }); } function openUniversalImport(){ UIMP={headers:[],rows:[],map:{},fileName:"",fileType:"",parsed:false}; uimpRenderProfiles(); var nf=document.getElementById("uimp-no-file"),ws=document.getElementById("uimp-workspace"); if(nf)nf.style.display="block";if(ws)ws.style.display="none"; var fn=document.getElementById("uimp-file-name");if(fn)fn.textContent="Noch keine Datei gewรคhlt"; var btn=document.getElementById("uimp-import-btn");if(btn)btn.disabled=true; var inp=document.getElementById("universal-product-import");if(inp)inp.value=""; openModal("modal-universal-import"); } function handleUniversalImportFile(e){ var file=e.target.files&&e.target.files[0];if(!file)return; UIMP.fileName=file.name||"Import"; UIMP.fileType=(file.name.split(".").pop()||"").toLowerCase(); var fn=document.getElementById("uimp-file-name");if(fn)fn.textContent="๐Ÿ“„ "+UIMP.fileName; if(["xlsx","xls"].indexOf(UIMP.fileType)>=0){ if(typeof XLSX==="undefined"){ notify("Excel-Bibliothek nicht geladen. Bitte Internetverbindung prรผfen oder CSV verwenden.","var(--err)"); return; } var r=new FileReader(); r.onload=function(ev){ try{ var wb=XLSX.read(ev.target.result,{type:"array"}); var sheet=wb.Sheets[wb.SheetNames[0]]; var arr=XLSX.utils.sheet_to_json(sheet,{header:1,raw:false,defval:""}); uimpAcceptMatrix(arr); }catch(err){notify("Excel-Fehler: "+err.message,"var(--err)");} }; r.readAsArrayBuffer(file); }else{ var reader=new FileReader(); reader.onload=function(ev){ try{uimpAcceptMatrix(uimpParseDelimited(ev.target.result));} catch(err){notify("Importdatei konnte nicht gelesen werden: "+err.message,"var(--err)");} }; reader.readAsText(file,"UTF-8"); } } function uimpAcceptMatrix(arr){ arr=(arr||[]).filter(function(r){return Array.isArray(r)&&r.some(function(x){return String(x).trim()!=="";});}); if(arr.length<2){notify("Die Datei enthรคlt keine Produktzeilen","var(--err)");return;} UIMP.headers=uimpUniqueHeaders(arr[0]); UIMP.rows=arr.slice(1).map(function(r){ var x=[];for(var i=0;i'+uimpEsc(x)+''; }); return h; } function uimpRenderMapping(){ var el=document.getElementById("uimp-mapping");if(!el)return; el.innerHTML=UIMP_FIELDS.map(function(f){ return '
'+uimpEsc(f.label)+(f.required?' *':'')+'
'+ '
'; }).join(""); } function uimpReadMappingFromUI(){ document.querySelectorAll("[data-uimp-field]").forEach(function(s){UIMP.map[s.getAttribute("data-uimp-field")]=s.value||"";}); } function uimpMappingChanged(){uimpReadMappingFromUI();uimpRefreshPreview();} function uimpColIndex(field){ var h=UIMP.map[field]||"";return h?UIMP.headers.indexOf(h):-1; } function uimpCell(row,field){ var i=uimpColIndex(field);return i>=0?(row[i]===undefined?"":row[i]):""; } function uimpMapped(field){return uimpColIndex(field)>=0;} function uimpFindExisting(id,name){ if(id!==null){ var byId=products.find(function(p){return Number(p.id)===Number(id);}); if(byId)return byId; } var useName=document.getElementById("uimp-match-name"); if((id===null||!uimpMapped("id")) && useName && useName.checked && name){ var n=uimpNorm(name); return products.find(function(p){return uimpNorm(p.name)===n;})||null; } return null; } function uimpRowToProduct(row, generatedId){ var rawId=uimpCell(row,"id"), id=null; if(String(rawId).trim()!==""){ var ni=parseInt(String(rawId).replace(/[^\d-]/g,""),10); if(isFinite(ni)&&ni>0)id=ni; } if(id===null && generatedId)id=generatedId; var name=String(uimpCell(row,"name")||"").trim(); var rest=uimpMapped("priceRest")?uimpNum(uimpCell(row,"priceRest")):null; var take=uimpMapped("priceTake")?uimpNum(uimpCell(row,"priceTake")):null; var del=uimpMapped("priceDel")?uimpNum(uimpCell(row,"priceDel")):null; var copy=document.getElementById("uimp-copy-price"); if(copy&©.checked&&rest!==null){ if(take===null)take=rest;if(del===null)del=rest; } return { id:id,name:name, cat:uimpMapped("cat")?String(uimpCell(row,"cat")||"").trim():"", vat:uimpMapped("vat")?uimpVat(uimpCell(row,"vat")):.07, priceRest:rest,priceTake:take,priceDel:del, priceLieferando:uimpMapped("priceLieferando")?uimpNum(uimpCell(row,"priceLieferando")):null, priceWolt:uimpMapped("priceWolt")?uimpNum(uimpCell(row,"priceWolt")):null, priceUberEats:uimpMapped("priceUberEats")?uimpNum(uimpCell(row,"priceUberEats")):null, active:uimpMapped("active")?uimpBool(uimpCell(row,"active")):true, desc:uimpMapped("desc")?String(uimpCell(row,"desc")||"").trim():"" }; } function uimpActionFor(p){ if(!p.name)return "รผberspringen"; var ex=uimpFindExisting(p.id,p.name); var mode=(document.getElementById("uimp-mode")||{}).value||"upsert"; if(mode==="new")return ex?"รผberspringen":"neu"; if(mode==="update")return ex?"aktualisieren":"รผberspringen"; if(mode==="replace")return "neu"; return ex?"aktualisieren":"neu"; } function uimpRefreshPreview(){ if(!UIMP.parsed)return; uimpReadMappingFromUI(); var nameOk=uimpMapped("name"); var prev=document.getElementById("uimp-preview"),warn=document.getElementById("uimp-warnings"); if(!nameOk){ if(warn){warn.style.display="block";warn.textContent="Bitte die Spalte fรผr โ€žNameโ€œ zuordnen.";} if(prev)prev.innerHTML=""; var btn=document.getElementById("uimp-import-btn");if(btn)btn.disabled=true; return; } var maxId=products.reduce(function(m,p){return Math.max(m,Number(p.id)||0);},0),next=maxId+1; var rows=UIMP.rows.slice(0,10), missing=0, badIds=0, fileIds={},dups=0; var html=rows.map(function(r){ var p=uimpRowToProduct(r,next++); if(!p.name)missing++; if(uimpMapped("id") && String(uimpCell(r,"id")).trim()!=="" && p.id===null)badIds++; if(p.id!==null){if(fileIds[p.id])dups++;fileIds[p.id]=true;} var a=uimpActionFor(p); var badge=a==="neu"?"badge-ok":a==="aktualisieren"?"badge-blue":"badge-gray"; return ''+(p.id===null?"auto":p.id)+''+uimpEsc(p.name||"โ€”")+''+ ''+uimpEsc(p.cat||"Import")+''+Math.round((p.vat||.07)*100)+'%'+ ''+((p.priceRest===null)?"โ€”":p.priceRest.toFixed(2).replace(".",",")+" โ‚ฌ")+''+ ''+((p.priceTake===null)?"โ€”":p.priceTake.toFixed(2).replace(".",",")+" โ‚ฌ")+''+ ''+((p.priceDel===null)?"โ€”":p.priceDel.toFixed(2).replace(".",",")+" โ‚ฌ")+''+ ''+a+''; }).join(""); if(prev)prev.innerHTML=html||'Keine Daten'; // Full-file stats var newC=0,updC=0,skipC=0,emptyC=0; var seen={}; UIMP.rows.forEach(function(r){ var p=uimpRowToProduct(r,null); if(!p.name){emptyC++;return;} if(p.id!==null&&seen[p.id]){skipC++;return;} if(p.id!==null)seen[p.id]=1; var a=uimpActionFor(p);if(a==="neu")newC++;else if(a==="aktualisieren")updC++;else skipC++; }); var warnings=[]; if(emptyC)warnings.push(emptyC+" Zeile(n) ohne Name"); if(dups)warnings.push("Doppelte Artikelnummern in der Vorschau erkannt"); if(badIds)warnings.push("Ungรผltige Artikelnummer(n)"); if(warn){ warn.style.display=warnings.length?"block":"none"; warn.textContent=warnings.join(" ยท "); } var sb=document.getElementById("uimp-summary-badge");if(sb)sb.textContent=UIMP.rows.length+" Zeilen"; var stats=document.getElementById("uimp-stats"); if(stats)stats.innerHTML=""+newC+" neu ยท "+updC+" aktualisieren ยท "+skipC+" รผberspringen"+ (UIMP.rows.length>10?" ยท Vorschau zeigt die ersten 10 Zeilen":""); var btn2=document.getElementById("uimp-import-btn");if(btn2)btn2.disabled=false; } function uimpMergeProduct(existing,p,newId){ var x=existing?Object.assign({},existing):{ id:newId,name:"",cat:"Import",vat:.07,priceRest:0,priceTake:0,priceDel:0, priceLieferando:null,priceWolt:null,priceUberEats:null,active:true,desc:"" }; x.id=existing?existing.id:(p.id||newId); x.name=p.name||x.name; if(uimpMapped("cat"))x.cat=p.cat||"Import"; else if(!existing)x.cat="Import"; if(uimpMapped("vat"))x.vat=p.vat; else if(!existing)x.vat=.07; ["priceRest","priceTake","priceDel","priceLieferando","priceWolt","priceUberEats"].forEach(function(k){ if((uimpMapped(k) || (["priceTake","priceDel"].indexOf(k)>=0 && p[k]!==null)) && p[k]!==null)x[k]=Math.max(0,p[k]); }); if(uimpMapped("active"))x.active=p.active; else if(!existing)x.active=true; if(uimpMapped("desc"))x.desc=p.desc; else if(!existing)x.desc=""; x.price=Number(x.priceRest)||0; return x; } function uimpDoImport(){ if(!UIMP.parsed||!uimpMapped("name")){notify("Name-Spalte fehlt","var(--err)");return;} var mode=(document.getElementById("uimp-mode")||{}).value||"upsert"; if(mode==="replace"){ if(!confirm("โš ๏ธ Komplettes Menรผ ersetzen?\n\nDie aktuelle Produktliste dieses Restaurants wird durch den Import ersetzt.\nBestellungen/Rechnungen bleiben erhalten."))return; } // Automatic in-browser backup before menu import. try{ var b={restaurant:(typeof mposActiveRestaurant==="function"?mposActiveRestaurant():null),products:products,createdAt:new Date().toISOString(),source:UIMP.fileName}; var a=document.createElement("a"); a.href=URL.createObjectURL(new Blob([JSON.stringify(b,null,2)],{type:"application/json"})); a.download="mpos_produkte_vor_import_"+new Date().toISOString().slice(0,10)+".json"; document.body.appendChild(a);a.click();document.body.removeChild(a);setTimeout(function(){URL.revokeObjectURL(a.href);},1000); }catch(e){} var target=(mode==="replace")?[]:products.slice(); var maxId=target.reduce(function(m,p){return Math.max(m,Number(p.id)||0);},0); if(mode==="replace")maxId=0; var nextId=maxId+1, added=0,updated=0,skipped=0,seen={}; UIMP.rows.forEach(function(r){ var p=uimpRowToProduct(r,null); if(!p.name){skipped++;return;} if(p.id!==null&&seen[p.id]){skipped++;return;} if(p.id!==null)seen[p.id]=1; var existing=null,idx=-1; if(mode!=="replace"){ existing=uimpFindExisting(p.id,p.name); if(existing)idx=target.findIndex(function(x){return Number(x.id)===Number(existing.id);}); } if(mode==="new"&&existing){skipped++;return;} if(mode==="update"&&!existing){skipped++;return;} if(existing&&idx>=0){ target[idx]=uimpMergeProduct(existing,p,existing.id);updated++; }else{ var useId=p.id; if(useId===null || target.some(function(x){return Number(x.id)===Number(useId);})){ while(target.some(function(x){return Number(x.id)===Number(nextId);}))nextId++; useId=nextId++; } target.push(uimpMergeProduct(null,p,useId));added++; } }); products=target.sort(function(a,b){return (Number(a.id)||0)-(Number(b.id)||0);}); save(); if(typeof renderProductsPage==="function")renderProductsPage(); if(typeof renderPOSCats==="function")renderPOSCats(); if(typeof renderPOSProducts==="function")renderPOSProducts(); try{if(typeof mobRenderCats==="function")mobRenderCats();}catch(e){} try{if(typeof mobRenderProducts==="function")mobRenderProducts();}catch(e){} closeModal("modal-universal-import"); notify("โœ“ Import fertig: "+added+" neu, "+updated+" aktualisiert, "+skipped+" รผbersprungen","var(--ok)"); var inp=document.getElementById("universal-product-import");if(inp)inp.value=""; } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // OPTIONEN PRO โ€” professionelle Optionsgruppen, Aufpreise, Pflicht, // Min/Max, Standardauswahl und Mehrfachzuweisung an Produkte. // Bestehende optionGroups/catPopupConfig bleiben kompatibel. // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• function opEsc(v){ return String(v==null?"":v).replace(/&/g,"&").replace(//g,">").replace(/"/g,"""); } function opParseOption(opt){ var raw=String(opt==null?"":opt); var m=raw.match(/\(([+-])\s*(\d+(?:[,.]\d+)?)\s*โ‚ฌ\)\s*$/); var price=0; if(m){ price=parseFloat(m[2].replace(",","."))||0; if(m[1]==="-") price=-price; } var name=raw.replace(/\s*\([+-]\s*\d+(?:[,.]\d+)?\s*โ‚ฌ\)\s*$/,"").trim(); return {raw:raw,name:name,price:price}; } function opFormatOption(name,price){ name=String(name||"").trim(); price=parseFloat(String(price==null?0:price).replace(",","."))||0; if(!price)return name; var sign=price>=0?"+":"-"; return name+" ("+sign+Math.abs(price).toFixed(2).replace(".",",")+"โ‚ฌ)"; } function opGroupNormalize(g){ if(!g)return null; if(!Array.isArray(g.options))g.options=[]; if(typeof g.multi!=="boolean")g.multi=false; if(g.required===undefined)g.required=false; if(g.multi){ var min=parseInt(g.min,10); if(!isFinite(min)||min<0)min=g.required?1:0; var max=parseInt(g.max,10); if(!isFinite(max)||max<0)max=0; g.min=min; g.max=max; }else{ g.min=g.required?1:0; g.max=1; if(g.defaultOpt===undefined || g.options.indexOf(g.defaultOpt)<0) g.defaultOpt=g.options[0]||null; } return g; } function opNormalizeAllGroups(){ Object.keys(optionGroups||{}).forEach(function(k){opGroupNormalize(optionGroups[k]);}); } opNormalizeAllGroups(); function opSelectedCount(gk){ var g=optionGroups[gk],v=currentOptSelections[gk]; if(!g)return 0; if(g.multi)return v&&v.size?v.size:0; return v?1:0; } function opDefaultForGroup(g){ if(!g)return null; if(g.defaultOpt && g.options.indexOf(g.defaultOpt)>=0)return g.defaultOpt; return g.options[0]||null; } function opGroupRuleText(g){ if(!g)return ""; if(!g.multi){ return g.required ? "Pflicht ยท genau 1 Auswahl" : "Optional ยท max. 1"; } var min=Math.max(0,parseInt(g.min,10)||0),max=Math.max(0,parseInt(g.max,10)||0); if(min>0&&max>0)return "Pflicht ยท "+min+"โ€“"+max+" auswรคhlen"; if(min>0)return "Mind. "+min+" auswรคhlen"; if(max>0)return "Optional ยท max. "+max; return "Mehrfachauswahl"; } function opPriceForRaw(raw){return opParseOption(raw).price;} function opRenderItemGroup(gk, basePrice){ var g=opGroupNormalize(optionGroups[gk]); if(!g)return null; var wrap=document.createElement("div"); wrap.className="op-live-group"; wrap.setAttribute("data-group-key",gk); wrap.style.cssText="margin-bottom:16px;padding:11px 12px;border:1px solid #e5e7eb;border-radius:11px;background:#fff;"; var head=document.createElement("div"); head.style.cssText="display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;"; var title=document.createElement("div"); title.innerHTML="
"+opEsc(g.label||gk)+"
" +"
"+opEsc(opGroupRuleText(g))+"
"; head.appendChild(title); if(g.required || (g.multi && (parseInt(g.min,10)||0)>0)){ var badge=document.createElement("span"); badge.textContent="PFLICHT"; badge.style.cssText="background:#fee2e2;color:#b91c1c;border-radius:8px;padding:2px 7px;font-size:9px;font-weight:900;white-space:nowrap;"; head.appendChild(badge); } wrap.appendChild(head); var row=document.createElement("div"); row.id="optgrp-"+gk; row.style.cssText="display:flex;gap:7px;flex-wrap:wrap;"; g.options.forEach(function(raw){ var parsed=opParseOption(raw); var selected; if(g.multi){ selected=currentOptSelections[gk] && currentOptSelections[gk].has(raw); }else{ selected=currentOptSelections[gk]===raw; } var btn=document.createElement("button"); btn.type="button"; btn.setAttribute("data-group",gk); btn.setAttribute("data-opt",raw); btn.setAttribute("data-multi",g.multi?"1":"0"); btn.className="opt-btn"+(g.multi?" opt-multi":"")+(selected?(g.multi?" selected-multi":" selected"):""); btn.style.cssText += "min-height:38px;padding:8px 11px;display:flex;align-items:center;gap:6px;"; var label=document.createElement("span"); label.textContent=parsed.name+(selected&&!g.multi?" โœ“":""); btn.appendChild(label); if(parsed.price!==0){ var pr=document.createElement("span"); pr.textContent=(parsed.price>0?"+":"โˆ’")+Math.abs(parsed.price).toFixed(2).replace(".",",")+" โ‚ฌ"; pr.style.cssText="font-size:10px;font-weight:900;color:"+(selected?"inherit":"var(--brand)")+";background:"+(selected?"rgba(255,255,255,.2)":"#fff7ed")+";border-radius:8px;padding:2px 5px;"; btn.appendChild(pr); } btn.onclick=function(){ if(g.multi){ var set=currentOptSelections[gk]; if(!set || !(set instanceof Set)){set=new Set();currentOptSelections[gk]=set;} if(set.has(raw)){ set.delete(raw); }else{ var mx=Math.max(0,parseInt(g.max,10)||0); if(mx>0 && set.size>=mx){ notify("Maximal "+mx+" Auswahl(en) bei "+(g.label||"dieser Gruppe"),"var(--err)"); return; } set.add(raw); } }else{ if(currentOptSelections[gk]===raw && !g.required){ currentOptSelections[gk]=null; }else{ currentOptSelections[gk]=raw; } } opRefreshLiveGroup(gk,basePrice); updateOptPreview(); }; row.appendChild(btn); }); wrap.appendChild(row); var status=document.createElement("div"); status.id="op-rule-"+gk; status.style.cssText="font-size:10px;color:#9ca3af;margin-top:7px;"; wrap.appendChild(status); setTimeout(function(){opUpdateRuleStatus(gk);},0); return wrap; } function opRefreshLiveGroup(gk,basePrice){ var old=document.querySelector(".op-live-group[data-group-key='"+gk.replace(/'/g,"\\'")+"']"); if(!old)return; var fresh=opRenderItemGroup(gk,basePrice); old.parentNode.replaceChild(fresh,old); } function opUpdateRuleStatus(gk){ var g=optionGroups[gk],el=document.getElementById("op-rule-"+gk);if(!g||!el)return; var n=opSelectedCount(gk); if(g.multi){ var min=Math.max(0,parseInt(g.min,10)||0),max=Math.max(0,parseInt(g.max,10)||0); el.textContent=n+" ausgewรคhlt"+(max>0?" / max. "+max:"")+(min>0?" ยท mind. "+min:""); el.style.color=(n"; body.appendChild(note); var preview=document.createElement("div"); preview.id="opt-preview"; preview.style.cssText="display:none;background:var(--brand-light);border-radius:var(--radius-sm);padding:10px 12px;border:1px solid var(--border2);"; preview.innerHTML="
Auf Bon / Kรผchenbon
" +"
"; body.appendChild(preview); openModal("modal-item-options"); updateOptPreview(); if(typeof isMob==="function"&&isMob()){ var fab=document.getElementById("item-opt-fab"); if(!fab){ fab=document.createElement("button");fab.id="item-opt-fab";fab.innerHTML="โœ“"; fab.style.cssText="position:fixed;right:10px;top:40%;z-index:10001;width:50px;height:50px;border-radius:50%;border:none;background:var(--brand);color:#fff;font-size:22px;font-weight:900;box-shadow:0 3px 14px rgba(232,93,4,.4);cursor:pointer;display:flex;align-items:center;justify-content:center;"; fab.onclick=function(){confirmItemOptions();};document.body.appendChild(fab); }else fab.style.display="flex"; } }; window.buildOptParts = function(includeDefaults){ var parts=[]; currentActiveGroups.forEach(function(gk){ var g=optionGroups[gk];if(!g)return; var val=currentOptSelections[gk]; if(g.multi){ if(val)val.forEach(function(raw){parts.push(opParseOption(raw).name);}); }else if(val){ var def=opDefaultForGroup(g); // Pflichtgruppen werden immer auf Bon/Kรผche gezeigt. Bei optionalen Gruppen // wird die Standardauswahl nur in der Vorschau angezeigt. if(includeDefaults || g.required || val!==def) parts.push(opParseOption(val).name); } }); var note=(document.getElementById("opt-note")||{}).value||""; if(note.trim())parts.push(note.trim()); return parts; }; window.updateOptPreview = function(){ var parts=buildOptParts(true),preview=document.getElementById("opt-preview"),txt=document.getElementById("opt-preview-text"); if(preview){ preview.style.display=parts.length?"block":"none"; if(txt)txt.textContent=parts.length?"โ†’ "+parts.join(" ยท "):""; } currentActiveGroups.forEach(opUpdateRuleStatus); }; function opValidateSelections(){ for(var i=0;i0&&n>max)return {ok:false,msg:"Bei โ€ž"+g.label+"โ€œ sind maximal "+max+" Auswahlen erlaubt.",group:gk}; } } return {ok:true}; } function opSelectedSurcharge(){ var sum=0; currentActiveGroups.forEach(function(gk){ var g=optionGroups[gk],val=currentOptSelections[gk];if(!g||!val)return; if(g.multi){val.forEach(function(raw){sum+=opPriceForRaw(raw);});} else sum+=opPriceForRaw(val); }); return Math.round(sum*100)/100; } window.confirmItemOptions = function(){ var check=opValidateSelections(); if(!check.ok){ notify(check.msg,"var(--err)"); var el=document.querySelector(".op-live-group[data-group-key='"+check.group.replace(/'/g,"\\'")+"']"); if(el){el.style.borderColor="#fca5a5";el.style.background="#fff7f7";el.scrollIntoView({behavior:"smooth",block:"center"});} return; } var p=products.find(function(x){return x.id===currentOptProductId;});if(!p)return; var surcharge=opSelectedSurcharge(); var finalPrice=Math.max(0,Math.round((getProductPrice(p)+surcharge)*100)/100); var parts=buildOptParts(false); var displayText=parts.join(" ยท "); closeModal("modal-item-options"); var fab=document.getElementById("item-opt-fab");if(fab)fab.style.display="none"; addToCartDirect(currentOptProductId,displayText||null,finalPrice,Math.max(1,parseInt(mposItemPopupQty,10)||1)); var nr=document.getElementById("mob-nr-input");if(nr)setTimeout(function(){nr.focus();nr.value="";},200); }; // ---------------------- OPTIONS STUDIO ------------------------------- var OP_STUDIO = {selectedProducts:{}}; function opStudioEnsure(){ var old=document.getElementById("modal-options-studio");if(old)return; var div=document.createElement("div"); div.innerHTML = ` `; document.body.appendChild(div.firstElementChild); var style=document.createElement("style"); style.textContent="@media(max-width:850px){#op-studio-grid{grid-template-columns:1fr!important;}}"; document.head.appendChild(style); } function openOptionsStudio(){ opNormalizeAllGroups();opStudioEnsure();OP_STUDIO.selectedProducts={}; var cats=typeof allCats==="function"?allCats():Array.from(new Set(products.map(function(p){return p.cat;}))).filter(Boolean); var bc=document.getElementById("op-bulk-cat"),ca=document.getElementById("op-cat-assign"); var opts=''+cats.map(function(c){return '';}).join(""); if(bc)bc.innerHTML=opts; if(ca)ca.innerHTML=cats.map(function(c){return '';}).join(""); opRenderStudioGroups();opRenderBulkProducts();opRenderBulkGroups();opRenderCategoryAssignment(); openModal("modal-options-studio"); } function opRenderStudioGroups(){ var el=document.getElementById("op-group-list");if(!el)return; var keys=Object.keys(optionGroups); if(!keys.length){el.innerHTML="
Noch keine Optionsgruppen
";return;} el.innerHTML=keys.map(function(k){ var g=opGroupNormalize(optionGroups[k]),priceCount=g.options.filter(function(o){return opPriceForRaw(o)!==0;}).length; return "
" +"
"+opEsc(g.label||k)+"
" +"
"+opEsc(opGroupRuleText(g))+" ยท "+g.options.length+" Optionen"+(priceCount?" ยท "+priceCount+" mit Aufpreis":"")+"
" +"
" +"
"+opEsc(g.options.slice(0,5).map(function(o){return opParseOption(o).name;}).join(" ยท "))+(g.options.length>5?" โ€ฆ":"")+"
"; }).join(""); } function opNewGroup(){opEditGroup(null);} function opEditGroup(key){ var creating=!key; var g=creating?{label:"Neue Optionsgruppe",multi:false,required:false,min:0,max:1,defaultOpt:null,options:["Option 1"]}:JSON.parse(JSON.stringify(opGroupNormalize(optionGroups[key]))); var pop=document.createElement("div");pop.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:12000;display:flex;align-items:center;justify-content:center;padding:16px;overflow:auto;"; var rows=g.options.map(function(raw,i){var p=opParseOption(raw);return opOptionEditorRow(p.name,p.price,i);}).join(""); pop.innerHTML="
" +"
" +"
"+(creating?"๏ผ‹ Optionsgruppe":"โœ Optionsgruppe bearbeiten")+"
Aufpreise, Pflichtauswahl, Min/Max und Standard
" +"
" +"
" +"
" +"
" +"
" +"" +"" +"
" +"
" +"
Optionen
" +"
NameAufpreis โ‚ฌStandard
" +"
"+rows+"
" +"
Aufpreis kann auch negativ sein, z.B. โˆ’1,00 โ‚ฌ. Bei Einfachauswahl kann genau eine Standardoption gewรคhlt werden.
" +"
" +(creating?"":"") +"
"; document.body.appendChild(pop); window._opgRuleChanged=function(){ var multi=document.getElementById("opg-multi").checked; var req=document.getElementById("opg-required").checked; var mi=document.getElementById("opg-min"),ma=document.getElementById("opg-max"); mi.disabled=!multi;ma.disabled=!multi; if(!multi){mi.value=req?1:0;ma.value=1;} else if(req && parseInt(mi.value||"0",10)<1)mi.value=1; }; window._opgRuleChanged(); document.getElementById("opg-close").onclick=function(){pop.remove();}; pop.onclick=function(e){if(e.target===pop)pop.remove();}; document.getElementById("opg-add").onclick=function(){ document.getElementById("opg-rows").insertAdjacentHTML("beforeend",opOptionEditorRow("",0,Date.now())); }; if(!creating){ document.getElementById("opg-delete").onclick=function(){ if(!confirm("Optionsgruppe wirklich lรถschen? Sie wird auch aus Kategorien und Produkt-Zuweisungen entfernt."))return; delete optionGroups[key]; Object.keys(catPopupConfig).forEach(function(c){catPopupConfig[c]=(catPopupConfig[c]||[]).filter(function(x){return x!==key;});}); products=products.map(function(p){if(Array.isArray(p.optionOverride))p.optionOverride=p.optionOverride.filter(function(x){return x!==key;});return p;}); saveItemOptions();save();pop.remove();opRenderStudioGroups();opRenderBulkGroups();opRenderCategoryAssignment();notify("Optionsgruppe gelรถscht","var(--err)"); }; } document.getElementById("opg-save").onclick=function(){ var k=String(document.getElementById("opg-key").value||"").trim().replace(/\s+/g,"_").replace(/[^\wรครถรผร„ร–รœรŸ-]/g,"_"); var label=String(document.getElementById("opg-label").value||"").trim(); if(!k||!label){notify("Schlรผssel und Name sind erforderlich","var(--err)");return;} if(creating&&optionGroups[k]){notify("Dieser Schlรผssel existiert bereits","var(--err)");return;} var multi=document.getElementById("opg-multi").checked,required=document.getElementById("opg-required").checked; var opts=[],defaultOpt=null; document.querySelectorAll("#opg-rows .opg-row").forEach(function(r){ var name=r.querySelector("[data-opg-name]").value.trim(); var price=parseFloat((r.querySelector("[data-opg-price]").value||"0").replace(",","."))||0; if(!name)return; var raw=opFormatOption(name,price);opts.push(raw); var rd=r.querySelector("[data-opg-default]");if(rd&&rd.checked)defaultOpt=raw; }); if(!opts.length){notify("Mindestens eine Option anlegen","var(--err)");return;} var min=multi?Math.max(0,parseInt(document.getElementById("opg-min").value,10)||0):(required?1:0); var max=multi?Math.max(0,parseInt(document.getElementById("opg-max").value,10)||0):1; if(required&&multi&&min<1)min=1; if(max>0&&min>max){notify("Min. darf nicht grรถรŸer als Max. sein","var(--err)");return;} if(!multi&&!defaultOpt)defaultOpt=opts[0]; optionGroups[k]={label:label,multi:multi,required:required,min:min,max:max,defaultOpt:defaultOpt,options:opts}; saveItemOptions();pop.remove();opRenderStudioGroups();opRenderBulkGroups();opRenderCategoryAssignment();notify("โœ“ Optionsgruppe gespeichert","var(--ok)"); }; } function opOptionEditorRow(name,price,idx){ return "
" +"" +"" +"" +"
"; } function opVisibleBulkProducts(){ var q=((document.getElementById("op-bulk-search")||{}).value||"").toLowerCase().trim(); var cat=(document.getElementById("op-bulk-cat")||{}).value||""; return products.filter(function(p){return (!cat||p.cat===cat)&&(!q||String(p.id).indexOf(q)>=0||String(p.name||"").toLowerCase().indexOf(q)>=0);}); } function opRenderBulkProducts(){ var el=document.getElementById("op-bulk-products");if(!el)return; var list=opVisibleBulkProducts(); el.innerHTML=list.map(function(p){ var groups=getCatGroups(p.cat,p.id)||[]; return ""; }).join("")||"
Keine Produkte
"; opBulkUpdateCount(); } function opBulkSelectVisible(on){opVisibleBulkProducts().forEach(function(p){OP_STUDIO.selectedProducts[p.id]=!!on;});opRenderBulkProducts();} function opBulkUpdateCount(){ var n=Object.keys(OP_STUDIO.selectedProducts).filter(function(id){return OP_STUDIO.selectedProducts[id];}).length; var el=document.getElementById("op-bulk-count");if(el)el.textContent=n+" gewรคhlt"; } function opRenderBulkGroups(){ var el=document.getElementById("op-bulk-groups");if(!el)return; el.innerHTML=Object.keys(optionGroups).map(function(k){return "";}).join(""); } function opBulkApply(mode){ var ids=Object.keys(OP_STUDIO.selectedProducts).filter(function(id){return OP_STUDIO.selectedProducts[id];}).map(Number); if(!ids.length){notify("Bitte zuerst Produkte markieren","var(--err)");return;} var chosen=[];document.querySelectorAll("[data-opbulk-group]:checked").forEach(function(x){chosen.push(x.getAttribute("data-opbulk-group"));}); if(["set","add","remove"].indexOf(mode)>=0 && !chosen.length){notify("Bitte mindestens eine Optionsgruppe markieren","var(--err)");return;} products=products.map(function(p){ if(ids.indexOf(Number(p.id))<0)return p; if(mode==="inherit")return Object.assign({},p,{optionOverride:null}); if(mode==="none")return Object.assign({},p,{optionOverride:[]}); var cur=Array.isArray(p.optionOverride)?p.optionOverride.slice():(getCatGroups(p.cat)||[]).slice(); if(mode==="set")cur=chosen.slice(); if(mode==="add")chosen.forEach(function(k){if(cur.indexOf(k)<0)cur.push(k);}); if(mode==="remove")cur=cur.filter(function(k){return chosen.indexOf(k)<0;}); return Object.assign({},p,{optionOverride:cur}); }); save();renderProductsPage();opRenderBulkProducts(); notify("โœ“ Optionen bei "+ids.length+" Produkten aktualisiert","var(--ok)"); } function opRenderCategoryAssignment(){ var cat=(document.getElementById("op-cat-assign")||{}).value||""; var el=document.getElementById("op-cat-groups");if(!el)return; var active=(catPopupConfig[cat]||[]); el.innerHTML=Object.keys(optionGroups).map(function(k){ return ""; }).join(""); } function opSaveCategoryAssignment(){ var cat=(document.getElementById("op-cat-assign")||{}).value||"";if(!cat)return; var arr=[];document.querySelectorAll("[data-opcat-group]:checked").forEach(function(x){arr.push(x.getAttribute("data-opcat-group"));}); catPopupConfig[cat]=arr;saveItemOptions();opRenderBulkProducts();notify("โœ“ Kategorie-Zuweisung gespeichert","var(--ok)"); } // Make old "manage options" button lead to the professional studio. window.manageOptions=function(){closeModal("modal-item-options");openOptionsStudio();}; // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ARTIKELPOPUP PRO โ€” mobile Bedienung wie moderne Gastro-POS: // vorhandene Optionen bleiben unverรคndert, Kopf + Footer bleiben fest, // Menge kann vor dem Hinzufรผgen mit โˆ’ / Zahl / + gewรคhlt werden. // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• var itemOptionQty = 1; var itemOptionBasePrice = 0; function itemOptCurrentUnitPrice(){ var p=products.find(function(x){return x.id===currentOptProductId;}); if(!p)return 0; var surcharge=(typeof opSelectedSurcharge==="function")?opSelectedSurcharge():0; return Math.max(0,Math.round((getProductPrice(p)+surcharge)*100)/100); } function itemOptSetQty(n){ n=Math.max(1,Math.min(99,parseInt(n,10)||1)); itemOptionQty=n; itemOptUpdateFooter(); } function itemOptQty(delta){ itemOptSetQty(itemOptionQty+delta); } function itemOptQtyPrompt(){ var v=prompt("Menge eingeben:",String(itemOptionQty)); if(v===null)return; itemOptSetQty(v); } function itemOptUpdateFooter(){ var q=document.getElementById("item-opt-qty-value");if(q)q.textContent=itemOptionQty; var unit=itemOptCurrentUnitPrice(); var total=Math.round(unit*itemOptionQty*100)/100; var b=document.getElementById("confirm-item-btn"); if(b){ b.innerHTML="Hinzufรผgen"+fmt(total)+""; b.setAttribute("aria-label",itemOptionQty+" Artikel hinzufรผgen, "+fmt(total)); } var price=document.getElementById("item-opt-price"); if(price){ price.textContent=(itemOptionQty>1?itemOptionQty+" ร— ":"")+fmt(unit); } } function itemOptClose(){ closeModal("modal-item-options"); var fab=document.getElementById("item-opt-fab");if(fab)fab.style.display="none"; } function itemOptInstallShell(){ var modal=document.querySelector("#modal-item-options .modal");if(!modal)return; modal.classList.add("item-options-pro"); var head=modal.querySelector(".modal-head"); if(head){ head.innerHTML= ""+ "
"+ ""; } var foot=modal.querySelector(".modal-footer"); if(foot){ foot.className="modal-footer item-opt-footer"; foot.innerHTML= "
"+ ""+ ""+ ""+ "
"+ ""; } var oldFab=document.getElementById("item-opt-fab");if(oldFab)oldFab.style.display="none"; } (function(){ var st=document.createElement("style"); st.id="item-options-pro-css"; st.textContent=` #modal-item-options{padding:10px!important;align-items:center!important;} #modal-item-options .item-options-pro{ width:min(620px,calc(100vw - 20px))!important; max-width:620px!important; max-height:min(94dvh,900px)!important; height:auto!important; border-radius:22px!important; overflow:hidden!important; display:flex!important; flex-direction:column!important; } #modal-item-options .item-options-pro .modal-head{ flex:none!important; min-height:62px!important; padding:10px 12px!important; background:#fff!important; border-bottom:1px solid #eee!important; position:relative!important; } .item-opt-head-center{flex:1;text-align:center;min-width:0;padding:0 8px;} .item-opt-head-center .modal-title{font-size:16px!important;font-weight:900!important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} .item-opt-head-price{font-size:11px;color:var(--text3);font-weight:700;margin-top:2px;} .item-opt-back,.item-opt-close{ width:38px;height:38px;border-radius:11px;border:1px solid #e5e7eb;background:#fff; display:flex;align-items:center;justify-content:center;cursor:pointer;font-family:inherit;color:#444;flex:none; } .item-opt-back{font-size:30px;line-height:1;padding-bottom:4px;} .item-opt-close{font-size:16px;} #modal-item-options .item-options-pro .modal-body{ padding:12px 14px 22px!important; overflow-y:auto!important; overscroll-behavior:contain; -webkit-overflow-scrolling:touch; flex:1 1 auto!important; } #modal-item-options .op-live-group{ margin-bottom:12px!important;padding:10px!important;border-radius:13px!important; } #modal-item-options .op-live-group .opt-btn{ min-height:44px!important;border-radius:11px!important;padding:9px 12px!important; } #modal-item-options .item-opt-footer{ flex:none!important; display:grid!important; grid-template-columns:170px 1fr!important; gap:10px!important; padding:10px 12px calc(10px + env(safe-area-inset-bottom))!important; background:rgba(255,255,255,.98)!important; border-top:1px solid #e5e7eb!important; box-shadow:0 -8px 20px rgba(0,0,0,.06)!important; z-index:8!important; } .item-opt-qty-stepper{ height:52px;border:1.5px solid #d1d5db;border-radius:14px;display:grid; grid-template-columns:1fr 1fr 1fr;overflow:hidden;background:#fff; } .item-opt-qty-stepper button{ border:none;background:#fff;font-family:inherit;font-size:22px;font-weight:700;color:#333;cursor:pointer; touch-action:manipulation; } .item-opt-qty-stepper button:active{background:#f3f4f6;} .item-opt-qty-stepper .item-opt-qty-value{ font-size:17px;background:#f7f7f8;border-left:1px solid #e5e7eb;border-right:1px solid #e5e7eb; } .item-opt-add{ min-height:52px;border:none;border-radius:14px;background:var(--brand);color:#fff; display:flex;align-items:center;gap:10px;padding:0 18px;font-family:inherit;font-size:15px;cursor:pointer; touch-action:manipulation; } .item-opt-add:active{filter:brightness(.94);} @media(max-width:768px){ #modal-item-options{ padding:0!important; align-items:flex-end!important; background:rgba(0,0,0,.34)!important; } #modal-item-options .item-options-pro{ width:100vw!important; max-width:none!important; height:calc(100dvh - max(12px,env(safe-area-inset-top)))!important; max-height:none!important; border-radius:22px 22px 0 0!important; margin:0!important; } #modal-item-options .item-options-pro .modal-head{ padding-top:max(10px,env(safe-area-inset-top))!important; min-height:68px!important; } #modal-item-options .item-options-pro .modal-body{ padding:12px 14px 20px!important; } #modal-item-options .item-opt-footer{ grid-template-columns:minmax(145px,36%) 1fr!important; } } @media(max-width:390px){ #modal-item-options .item-opt-footer{grid-template-columns:138px 1fr!important;gap:7px!important;padding-left:8px!important;padding-right:8px!important;} .item-opt-add{padding:0 12px!important;font-size:14px!important;} }`; document.head.appendChild(st); })(); // Preserve Options Pro behavior, but with the improved mobile shell and quantity stepper. window.openItemOptions = function(id){ mposItemPopupQty=1; setTimeout(function(){mposItemQtySet(1);},0); var p=products.find(function(x){return x.id===id;});if(!p)return; currentOptProductId=id; itemOptionQty=1; var groups=getCatGroups(p.cat,p.id)||[]; currentActiveGroups=groups; currentOptSelections={}; groups.forEach(function(gk){ var g=opGroupNormalize(optionGroups[gk]);if(!g)return; if(g.multi)currentOptSelections[gk]=new Set(); else currentOptSelections[gk]=opDefaultForGroup(g); }); itemOptInstallShell(); var basePrice=getProductPrice(p); itemOptionBasePrice=basePrice; var nameEl=document.getElementById("item-opt-name");if(nameEl)nameEl.textContent=p.name; var body=document.querySelector("#modal-item-options .modal-body");if(!body)return; body.innerHTML=""; groups.forEach(function(gk){ var el=opRenderItemGroup(gk,basePrice);if(el)body.appendChild(el); }); var note=document.createElement("div"); note.style.marginBottom="12px"; note.innerHTML="
๐Ÿ“ Sonderwunsch / Kรผchenhinweis
"+ ""; body.appendChild(note); var preview=document.createElement("div"); preview.id="opt-preview"; preview.style.cssText="display:none;background:var(--brand-light);border-radius:var(--radius-sm);padding:10px 12px;border:1px solid var(--border2);margin-bottom:4px;"; preview.innerHTML="
Auf Bon / Kรผchenbon
"+ "
"; body.appendChild(preview); openModal("modal-item-options"); updateOptPreview(); itemOptUpdateFooter(); var oldFab=document.getElementById("item-opt-fab");if(oldFab)oldFab.style.display="none"; }; // Keep price/selection preview and refresh the fixed footer whenever options change. var _oldUpdateOptPreview = window.updateOptPreview; window.updateOptPreview = function(){ if(typeof _oldUpdateOptPreview==="function")_oldUpdateOptPreview(); itemOptUpdateFooter(); }; window.confirmItemOptions = function(){ var check=opValidateSelections(); if(!check.ok){ notify(check.msg,"var(--err)"); var el=document.querySelector(".op-live-group[data-group-key='"+check.group.replace(/'/g,"\\'")+"']"); if(el){el.style.borderColor="#fca5a5";el.style.background="#fff7f7";el.scrollIntoView({behavior:"smooth",block:"center"});} return; } var p=products.find(function(x){return x.id===currentOptProductId;});if(!p)return; var surcharge=opSelectedSurcharge(); var finalPrice=Math.max(0,Math.round((getProductPrice(p)+surcharge)*100)/100); var parts=buildOptParts(false); var displayText=parts.join(" ยท "); var qty=Math.max(1,parseInt(itemOptionQty,10)||1); itemOptClose(); addToCartDirect(currentOptProductId,displayText||null,finalPrice,qty); if(typeof notify==="function"){ notify("โœ“ "+qty+"ร— "+p.name+" hinzugefรผgt","var(--ok)"); } var nr=document.getElementById("mob-nr-input"); if(nr)setTimeout(function(){nr.focus();nr.value="";},180); }; // Back button / browser back while the options popup is open closes only the popup. (function(){ var pushed=false; var oldOpenModal=window.openModal; if(typeof oldOpenModal==="function"){ window.openModal=function(id){ oldOpenModal(id); if(id==="modal-item-options" && history && history.pushState){ try{history.pushState({mposItemOptions:true},"",location.href);pushed=true;}catch(e){} } }; } window.addEventListener("popstate",function(){ var m=document.getElementById("modal-item-options"); if(m&&m.classList.contains("open")){ itemOptClose(); pushed=false; } }); })();