`);w.document.close();w.focus();setTimeout(()=>w.print(),250)}const $=id=>document.getElementById(id);const names={dashboard:'لوحة التحكم',products:'كارت الأصناف',rawStock:'مخزون المواد الأولية',finishedStock:'مخزون المنتجات النهائية',stockMoves:'حركات المخزون',purchases:'المشتريات',factories:'المصانع',factoryProducts:'منتجات المصانع',subcontract:'أوامر التصنيع',factoryStock:'خامات لدى المصانع',sales:'المبيعات',customers:'العملاء',ledger:'كشف حساب العميل',warehouses:'المخازن',users:'المستخدمون والصلاحيات',suppliers:'الموردون',supplierLedger:'كشف حساب المورد',companies:'الشركات والشركاء',finance:'المالية والأرباح'}; async function api(url,opts={}){const method=(opts.method||'GET').toUpperCase();let finalUrl=url;const headers={'Content-Type':'application/json',...(opts.headers||{})};let body=opts.body;if(method==='GET'&&!url.includes('/api/auth/')){const sep=url.includes('?')?'&':'?';finalUrl=url+sep+'companyId='+encodeURIComponent(state.companyId||'ATOM')}if(method!=='GET'&&body&&headers['Content-Type']?.includes('application/json')){try{const x=JSON.parse(body);if(x&&typeof x==='object'&&!Array.isArray(x)&&!x.companyId)x.companyId=state.companyId;body=JSON.stringify(x)}catch{}}const r=await fetch(finalUrl,{...opts,headers,body,credentials:'same-origin'});let d={};try{d=await r.json()}catch{}if(r.status===401){state.user=null;$('app')?.classList.add('hidden');$('login')?.classList.remove('hidden');throw new Error(d.error||'انتهت جلسة الدخول، سجل الدخول مرة أخرى')}if(!r.ok||d.ok===false)throw new Error(d.error||`HTTP ${r.status}`);return d.data}function esc(v){return String(v??'').replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]))}function money(v){return new Intl.NumberFormat('ar-EG',{minimumFractionDigits:0,maximumFractionDigits:0}).format(Math.round(Number(v||0)))+' ج.م'}function toast(m){$('toast').textContent=m;$('toast').classList.add('show');clearTimeout(window.__toast);window.__toast=setTimeout(()=>$('toast').classList.remove('show'),2800)}function openModal(h){$('modal').innerHTML=h;$('modalBack').classList.remove('hidden')}function closeModal(){$('modalBack').classList.add('hidden');$('modal').innerHTML=''}$('modalBack').addEventListener('click',e=>{if(e.target.id==='modalBack')closeModal()});function toggleSidebar(){$('sidebar').classList.toggle('open')} async function login(e){e.preventDefault();try{const u=await api('/api/auth/login',{method:'POST',body:JSON.stringify({email:$('email').value,password:$('password').value})});state.user=u;start()}catch(err){$('loginError').textContent=err.message;$('loginError').classList.remove('hidden')}}async function logout(){await api('/api/auth/logout',{method:'POST'}).catch(()=>{});location.reload()}async function boot(){try{state.user=await api('/api/auth/me');start()}catch{$('login').classList.remove('hidden')}}async function start(){$('login').classList.add('hidden');$('app').classList.remove('hidden');$('userName').textContent=state.user.name;$('userRole').textContent=state.user.roleName||state.user.role;$('avatar').textContent=(state.user.name||'م')[0];try{state.cache.companies=await api('/api/companies');if(!state.cache.companies.some(c=>c.id===state.companyId))state.companyId=state.cache.companies[0]?.id||'ATOM';renderCompanySelector()}catch{}await render();refreshAlerts()}function renderCompanySelector(){const el=$('companySelector');if(!el)return;el.innerHTML=(state.cache.companies||[]).map(c=>``).join('')}async function changeCompany(id){state.companyId=id;localStorage.setItem('zezo_company_id',id);state.cache={companies:state.cache.companies||[]};render()} const pagePermissions={dashboard:'DASHBOARD_VIEW',products:'PRODUCTS_VIEW',rawStock:'STOCK_VIEW',finishedStock:'STOCK_VIEW',stockMoves:'STOCK_VIEW',purchases:'PURCHASES_VIEW',factories:'FACTORIES_VIEW',subcontract:'MANUFACTURING_VIEW',factoryStock:'MANUFACTURING_VIEW',factoryWaste:'MANUFACTURING_VIEW',sales:'SALES_VIEW',customers:'SALES_VIEW',ledger:'SALES_VIEW',suppliers:'SUPPLIERS_VIEW',supplierLedger:'SUPPLIERS_VIEW',companies:'COMPANIES_MANAGE',finance:'FINANCE_VIEW',settlements:'FINANCE_MANAGE',monthlyReport:'REPORTS_VIEW',vaults:'FINANCE_VIEW',factoryLedger:'FACTORY_FINANCE',warehouses:'WAREHOUSES_MANAGE',users:'USERS_MANAGE'};function can(p){return state.user?.role==='SUPER_ADMIN'||(state.user?.permissions||[]).includes(p)}function setNav(){document.querySelectorAll('.nav button').forEach(b=>{const ok=can(pagePermissions[b.dataset.page]||'DASHBOARD_VIEW');b.style.display=ok?'':'none';b.classList.toggle('active',b.dataset.page===state.page)});if(!can(pagePermissions[state.page]||'DASHBOARD_VIEW'))state.page='dashboard';$('crumb').textContent=names[state.page]||state.page;$('sidebar').classList.remove('open')} document.querySelectorAll('.nav button').forEach(b=>b.onclick=()=>{state.page=b.dataset.page;render()});async function render(){setNav();$('content').innerHTML='
جارٍ التحميل...
';try{if(state.page==='dashboard')return dashboard();if(state.page==='factories')return factories();if(state.page==='products')return products();if(state.page==='rawStock')return stock('raw');if(state.page==='finishedStock')return stock('finished');if(state.page==='stockMoves')return moves();if(state.page==='warehouses')return warehouses();if(state.page==='factoryProducts')return factoryProducts();if(state.page==='factoryStock')return factoryStock();if(state.page==='subcontract')return subcontract();if(state.page==='customers')return customers();if(state.page==='ledger')return ledger();if(state.page==='sales')return sales();if(state.page==='purchases')return purchases();if(state.page==='users')return users();if(state.page==='suppliers')return suppliers();if(state.page==='supplierLedger')return supplierLedger();if(state.page==='companies')return companies();if(state.page==='finance')return finance();if(state.page==='monthlyReport')return monthlyReport()}catch(e){$('content').innerHTML=`

حدث خطأ

${esc(e.message)}

`}} async function dashboard(){const d=await api('/api/dashboard');$('content').innerHTML=`

لوحة التحكم

ملخص سريع لدورة الخامات والمصانع والمنتجات والمبيعات.

خامات في المخزن
${d.rawQty}
إجمالي الكميات
منتجات نهائية
${d.finishedQty}
جاهزة للبيع
أوامر تصنيع مفتوحة
${d.openOrders}
مع مصانع خارجية
مبيعات
${money(d.sales)}
إجمالي الفواتير
دورة العمل
مؤشرات
${d.factories}مصانع نشطة
${d.customers}عملاء
النظام مبني حول التصنيع التعاقدي: الخامات ملكك، والمصنع يستلمها للتصنيع ثم يرجع المنتج النهائي.
`} function go(p){state.page=p;render()} async function factories(){const data=await api('/api/factories');$('content').innerHTML=`

المصانع

إضافة وتعديل وتعطيل المصانع الثلاثة أو أي مصانع جديدة.

${data.length} مصنع
${data.map(x=>``).join('')}
الكودالمصنعالموقعالهاتفالمنتجاتالحالةإجراءات
${esc(x.code)}${esc(x.name_ar)}${esc(x.location)}${esc(x.phone)}${x.active?'نشط':'غير نشط'}
`} function factoryForm(x={}){openModal(`
`)}async function saveFactory(e,id){e.preventDefault();try{const b={code:$('f_code').value,nameAr:$('f_name').value,nameEn:$('f_en').value||$('f_name').value,location:$('f_loc').value,phone:$('f_phone').value,email:$('f_email').value};await api(id?'/api/factories/'+id:'/api/factories',{method:id?'PUT':'POST',body:JSON.stringify(b)});closeModal();toast('تم حفظ المصنع');factories()}catch(e){toast(e.message)}}async function deleteFactory(id){if(!confirm('تعطيل المصنع؟'))return;await api('/api/factories/'+id,{method:'DELETE'});factories()} async function products(){const [data,units,cats]=await Promise.all([api('/api/products'),api('/api/units'),api('/api/categories')]);state.cache={...state.cache,products:data,units,cats};$('content').innerHTML=`

كارت الأصناف

كل مادة خام ومنتج نهائي له كارت صنف مستقل وتكلفة وسعر جمهور.

الأصناف (${data.length})
${data.map(x=>``).join('')}
الكودالصنفالنوعالوحدةسعر الجمهورالتكلفةالحالةإجراءات
${esc(x.code)}${esc(x.name_ar)}${typeName(x.type)}${esc(x.unit_name)}${money(x.public_price)}${money(x.default_cost)}${x.active?'نشط':'غير نشط'}
`} function typeName(t){return ({RAW_MATERIAL:'مواد تصنيع',PACK:'مواد تعبئة وتغليف',FINISHED:'منتج نهائي'})[t]||t}function filterProducts(k,el){document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));el.classList.add('active');document.querySelectorAll('#prodTable tbody tr').forEach(r=>{const t=r.dataset.type;r.style.display=k==='all'||(k==='raw'&&t==='RAW_MATERIAL')||(k==='pack'&&t==='PACK')||(k==='finished'&&t==='FINISHED')?'':'none'})}function filterRows(q,id){const vars=searchVariants(q);document.querySelectorAll('#'+id+' tbody tr').forEach(r=>{const t=r.innerText.toLowerCase();r.style.display=vars.some(v=>t.includes(v))?'':'none'})} function productForm(x={}){const us=state.cache.units||[],cs=state.cache.cats||[];const isPack=x.type==='PACK',isFinished=x.type==='FINISHED';openModal(`
`);toggleProductPricing()} function toggleProductPricing(){const t=$('p_type')?.value,pack=t==='PACK',finished=t==='FINISHED';$('publicPriceField').style.display=pack?'none':'';$('p_cost').readOnly=finished;$('p_cost').title=finished?'يُحسب تلقائيًا من التصنيع':'';$('finishedCostNote').style.display=finished?'block':'none';$('packNote').style.display=pack?'block':'none';if(pack)$('p_price').value=0} async function saveProduct(e,id){e.preventDefault();try{const b={code:$('p_code').value,barcode:$('p_barcode').value.trim(),nameAr:$('p_name').value,nameEn:$('p_name').value,type:$('p_type').value,unitId:$('p_unit').value,categoryId:$('p_cat').value,publicPrice:+$('p_price').value,defaultCost:+$('p_cost').value,reorderLevel:+$('p_reorder').value};if(b.type==='FINISHED')b.defaultCost=0;if(b.type==='PACK')b.publicPrice=0;await api(id?'/api/products/'+id:'/api/products',{method:id?'PUT':'POST',body:JSON.stringify(b)});closeModal();toast('تم حفظ كارت الصنف');products()}catch(e){toast(e.message)}} async function deleteProduct(id){if(!confirm('حذف كارت الصنف؟ إذا كان عليه حركات سابقة سيُخفى للحفاظ على الحسابات.'))return;try{const d=await api('/api/products/'+id,{method:'DELETE'});toast(d?.archived?'تم إخفاء الصنف المرتبط بحركات سابقة':'تم حذف كارت الصنف');products()}catch(e){toast(e.message)}} function showProductQR(x){const value=String(x.barcode||x.code||'').trim();openModal(``);if(window.QRCode){QRCode.toCanvas(document.getElementById('productQR'),value,{width:220,margin:2},function(err){if(err)toast('تعذر إنشاء QR')})}else toast('مكتبة QR لم تُحمّل، تأكد من اتصال الإنترنت')} function printProductQR(value,name){const c=document.getElementById('productQR');if(!c)return;const w=window.open('','_blank');w.document.write(`QR - ${esc(name)}

${esc(name)}

${esc(value)}
`);w.document.close();setTimeout(()=>w.print(),300)} async function scanProductToPage(inputId,mode){const el=$(inputId);if(!el)return;const code=el.value.trim();if(!code)return;try{const p=await api('/api/products/scan/'+encodeURIComponent(code));if(mode==='stock'){closeModal();stockProductHistory(p.id)}else if(mode==='sale'){const idx=window.saleRows.findIndex(r=>r.productId===p.id);if(idx>=0)window.saleRows[idx].qty=Number(window.saleRows[idx].qty||0)+1;else window.saleRows.push({productId:p.id,qty:1,publicPrice:p.public_price||0,discount:0});renderSaleRows();toast(`تمت إضافة ${p.name_ar}`)}el.value='';el.focus()}catch(e){toast(e.message);el.select()}} async function stock(kind){const rows=await api('/api/stock?kind='+kind);const finished=kind==='finished';const action=finished?'':'';const title=finished?'مخزون المنتجات النهائية':'مخزون المواد الأولية';const desc=finished?'المنتج النهائي يدخل من المصنع ويمكن تحويله لمخازن البيع.':'يوضح المواد الأولية والكميات الحالية.';const rowHtml=rows.map(x=>{const click=finished?'':`onclick="stockProductHistory('${String(x.product_id||'').replace(/'/g,"\\'")}')" style="cursor:pointer"`;return `${esc(x.product_name)}${esc(x.code)}${num(x.quantity)}${esc(x.unit_name||'')}${esc(finished?(x.warehouse_name||'مخزن المنتجات النهائية'):(x.supplier_name||'غير محدد'))}${money(x.avg_cost)}${money(num(x.quantity)*num(x.avg_cost))}`}).join('')||'
لا توجد أرصدة بعد.
';$('content').innerHTML=`

${title}

${desc}

${action}
${rows.length} صنف
${rowHtml}
الصنفالكودالكميةالوحدة${finished?'المخزن':'المورد'}متوسط التكلفةالقيمة
`} async function stockProductHistory(pid){try{const d=await api('/api/stock/product/'+pid+'/movements');const rows=d.movements||[];openModal(``)}catch(e){toast(e.message)}} async function transferFinished(){const ps=state.cache.products||await api('/api/products');const allWh=state.cache.warehouses||await api('/api/warehouses');state.cache={...state.cache,products:ps,warehouses:allWh};const whs=allWh.filter(w=>w.kind==='SALES');const fin=allWh.find(w=>w.code==='FINISHED-SHARED');if(!fin){toast('مخزن المنتجات النهائية المشترك غير موجود');return}if(!whs.length){toast('لا يوجد مخزن مبيعات للشركة الحالية');return}openModal(`
`)}async function saveFinishedTransfer(e){e.preventDefault();try{await api('/api/stock/transfer',{method:'POST',body:JSON.stringify({fromWarehouseId:(state.cache.warehouses||[]).find(w=>w.code==='FINISHED-SHARED')?.id,toWarehouseId:$('tr_w').value,productId:$('tr_p').value,quantity:+$('tr_q').value,notes:$('tr_n').value})});closeModal();toast('تم تحويل المنتج إلى مخزن البيع');stock('finished')}catch(e){toast(e.message)}} async function moves(){const rows=await api('/api/stock/movements');$('content').innerHTML=`

حركات المخزون

سجل فعلي لكل دخول وخروج وتحويل في المخزون.

${rows.length} حركة
${rows.map(x=>``).join('')||''}
التاريخالصنفالمخزننوع الحركةالكميةتكلفة الوحدةالمرجعملاحظات
${esc(x.created_at||'')}${esc(x.product_name)}${esc(x.warehouse_name)}${esc(({PURCHASE:'شراء',SEND_TO_FACTORY:'إرسال للمصنع',FACTORY_RECEIPT:'استلام مصنع',TRANSFER_OUT:'تحويل خارج',TRANSFER_IN:'تحويل داخل',SALE:'بيع',ADJUSTMENT:'تسوية'}[x.type]||x.type))}${num(x.quantity)}${money(x.unit_cost)}${esc(x.ref_id||'—')}${esc(x.notes||'')}
لا توجد حركات.
`} async function warehouses(){const rows=await api('/api/warehouses');$('content').innerHTML=`

المخازن

مخازن الشركة الحالية. يمكنك إضافة مخزن جديد بدون التأثير على باقي الشركات.

${rows.map(w=>`
${esc(w.name_ar)}${w.kind==='RAW'?'مواد تصنيع':w.kind==='FIN'?'منتجات نهائية':'مبيعات'}

${esc(w.code)} — ${esc(w.location||'')}

`).join('')||'
لا توجد مخازن لهذه الشركة.
'}
`}function warehouseForm(w={}){openModal(`
`)}async function saveWarehouse(e,id){e.preventDefault();try{await api(id?'/api/warehouses/'+id:'/api/warehouses',{method:id?'PUT':'POST',body:JSON.stringify({code:$('wh_code').value,nameAr:$('wh_name').value,nameEn:$('wh_name').value,kind:$('wh_kind').value,location:$('wh_loc').value})});closeModal();toast('تم حفظ المخزن');warehouses()}catch(e){toast(e.message)}}async function deleteWarehouse(id){if(!confirm('تعطيل هذا المخزن؟'))return;try{await api('/api/warehouses/'+id,{method:'DELETE'});toast('تم تعطيل المخزن');warehouses()}catch(e){toast(e.message)}} async function factoryProducts(){const [rows,fs,ps]=await Promise.all([api('/api/factory-products'),api('/api/factories'),api('/api/products')]);state.cache={...state.cache,factories:fs,products:ps};$('content').innerHTML=`

منتجات المصانع

حدد أي منتج يتم تصنيعه في أي مصنع وسعر التصنيع المتفق عليه.

الربط الحالي
${rows.map(x=>``).join('')||''}
المصنعالمنتجكود المنتجسعر التصنيعإجراء
${esc(x.factory_name)}${esc(x.product_name)}${esc(x.product_code)}${money(x.subcontract_price)}
لم يتم ربط منتجات بالمصانع.
`}function factoryProductForm(){const fs=state.cache.factories||[],ps=(state.cache.products||[]).filter(p=>p.type==='FINISHED');openModal(`
`)}async function saveFactoryProduct(e){e.preventDefault();try{await api('/api/factory-products',{method:'POST',body:JSON.stringify({factoryId:$('fp_f').value,productId:$('fp_p').value,subcontractPrice:+$('fp_cost').value})});closeModal();toast('تم ربط المنتج بالمصنع');factoryProducts()}catch(e){toast(e.message)}}async function unlinkFactoryProduct(id){await api('/api/factory-products/'+id,{method:'DELETE'});factoryProducts()}async function showFactoryProducts(){state.page='factoryProducts';render()} async function factoryStock(){const rows=await api('/api/factory-stock');$('content').innerHTML=`

خامات لدى المصانع

رصيد كل خامة عند كل مصنع بعد الإرسال والاستهلاك والاستلام.

أرصدة المصانع
${rows.map(x=>``).join('')||''}
المصنعالخامةالكودالكمية المتبقيةمتوسط التكلفة
${esc(x.factory_name)}${esc(x.product_name)}${esc(x.code)}${x.quantity} ${esc(x.unit_name)}${money(x.avg_cost)}
لا توجد خامات لدى المصانع.
`} async function customers(){const [rows,wh]=await Promise.all([api('/api/customers'),api('/api/warehouses')]);state.cache={...state.cache,customers:rows,warehouses:wh};$('content').innerHTML=`

العملاء

كل عميل يمكن ربطه بشرم أو الغردقة وتحديد سعره وخصمه.

قائمة العملاء
${rows.map(x=>``).join('')}
الكودالعميلالهاتفالمخازنالرصيدالحالةإجراءات
${esc(x.code)}${esc(x.name)}${esc(x.phone)}${(x.warehouse_ids||'').split(',').map(id=>wh.find(w=>w.id===id)?.name_ar).filter(Boolean).join('، ')||'—'}${x.active?'نشط':'غير نشط'}
`} function customerForm(x={}){const wh=state.cache.warehouses||[],ids=String(x.warehouse_ids||'').split(',').filter(Boolean);openModal(`
`)}async function saveCustomer(e,id){e.preventDefault();try{const warehouseIds=[...document.querySelectorAll('.cwh:checked')].map(x=>x.value);const b={code:$('c_code').value,name:$('c_name').value,phone:$('c_phone').value,address:$('c_addr').value,creditLimit:+$('c_credit').value,warehouseIds};await api(id?'/api/customers/'+id:'/api/customers',{method:id?'PUT':'POST',body:JSON.stringify(b)});closeModal();toast('تم حفظ العميل');customers()}catch(e){toast(e.message)}}async function deleteCustomer(id){if(confirm('تعطيل العميل؟')){await api('/api/customers/'+id,{method:'DELETE'});customers()}} async function ledger(){const cs=state.cache.customers||await api('/api/customers');state.cache.customers=cs;$('content').innerHTML=`

كشف حساب العميل

اختر العميل لمشاهدة الفواتير والمدفوعات والرصيد المتراكم.

اختر عميلًا.
`}async function loadLedger(){const id=$('ledgerCustomer').value;if(!id)return;$('ledgerArea').innerHTML='
جارٍ التحميل...
';const rows=await api('/api/customer-ledger/'+id);let bal=rows.length?rows[rows.length-1].balance:0;$('ledgerArea').innerHTML=`
${money(bal)}الرصيد الحالي
${rows.map(r=>``).join('')||''}
التاريخالبيانمديندائنالرصيد
${esc(r.doc_date)}${esc(r.description)}${money(r.debit)}${money(r.credit)}${money(r.balance)}
لا توجد حركة.
`}function customerPaymentForm(customerId){const cs=state.cache.companies||[],c=state.cache.customers?.find(x=>x.id===customerId);openModal(`
`)}async function saveCustomerPayment(e,customerId){e.preventDefault();try{const f=$('cpp_file').files[0];let data='';if(f){if(f.size>4*1024*1024)throw new Error('حجم المرفق أكبر من 4MB');data=await new Promise((resolve,reject)=>{const r=new FileReader();r.onload=()=>resolve(r.result);r.onerror=reject;r.readAsDataURL(f)})}await api('/api/customer-payments',{method:'POST',body:JSON.stringify({customerId,companyId:$('cpp_c').value,amount:+$('cpp_a').value,date:$('cpp_d').value,description:$('cpp_desc').value,attachmentName:f?.name||'',attachmentData:data})});closeModal();toast('تم تسجيل التحصيل');loadLedger()}catch(e){toast(e.message)}}function openLedger(id){state.page='ledger';render().then(()=>{setTimeout(()=>{$('ledgerCustomer').value=id;loadLedger()},100)})} async function sales(){const [rows,cs,ps,wh,companies]=await Promise.all([api('/api/sales'),api('/api/customers'),api('/api/products'),api('/api/warehouses'),api('/api/companies')]);state.cache={...state.cache,customers:cs,products:ps,warehouses:wh,companies};$('content').innerHTML=`

المبيعات

بيع من مخزن شرم أو الغردقة. السعر النهائي يُحسب تلقائيًا من سعر الجمهور والخصم.

فواتير البيع
${rows.map(x=>``).join('')||''}
الفاتورةالتاريخالعميلالمخزنالخصمالإجماليالمدفوع
${esc(x.doc_date)}${esc(x.customer_name)}${esc(x.warehouse_name)}${money(x.discount)}${money(x.total)}${money(x.paid)}
لا توجد فواتير.
`} async function saleForm(){const cs=state.cache.customers||await api('/api/customers'),ps=state.cache.products||await api('/api/products'),wh=(state.cache.warehouses||await api('/api/warehouses')).filter(w=>w.kind==='SALES'),companies=state.cache.companies||await api('/api/companies');state.cache={...state.cache,customers:cs,products:ps,warehouses:wh,companies};window.saleRows=[{productId:ps.find(p=>p.type==='FINISHED')?.id||'',qty:1,publicPrice:0,discount:0}];openModal(`
`);refreshCustomerOptions();renderSaleRows()} function refreshCustomerOptions(){const wid=$('sale_wh')?.value;const cs=state.cache.customers||[];const ids=cs.filter(c=>String(c.warehouse_ids||'').split(',').includes(wid));$('sale_c').innerHTML=(ids.length?ids:cs).map(c=>``).join('')} function renderSaleRows(){const ps=(state.cache.products||[]).filter(p=>p.type==='FINISHED'&&p.active);$('saleRows').innerHTML=window.saleRows.map((r,i)=>{const p=ps.find(x=>x.id===r.productId)||ps[0];if(p&&!r.publicPrice)r.publicPrice=p.public_price;const net=num(r.publicPrice)*(1-num(r.discount)/100);return `${money(net)}${money(net*num(r.qty))}`}).join('')} function addSaleRow(){const ps=(state.cache.products||[]).filter(p=>p.type==='FINISHED'&&p.active);window.saleRows.push({productId:ps[0]?.id||'',qty:1,publicPrice:ps[0]?.public_price||0,discount:0});renderSaleRows()}async function saveSale(e){e.preventDefault();try{const items=window.saleRows.map(r=>({productId:r.productId,quantity:r.qty,publicPrice:r.publicPrice,discountPercent:r.discount}));const d=await api('/api/sales',{method:'POST',body:JSON.stringify({customerId:$('sale_c').value,warehouseId:$('sale_wh').value,companyId:$('sale_company').value,docDate:$('sale_date').value,paid:+$('sale_paid').value,items})});closeModal();toast(`تم ترحيل الفاتورة — ${money(d.total)}`);sales()}catch(e){toast(e.message)}} async function purchases(){const [rows,ps,wh,ss,cs]=await Promise.all([api('/api/purchases'),api('/api/products'),api('/api/warehouses'),api('/api/suppliers'),api('/api/companies')]);state.cache={...state.cache,products:ps,warehouses:wh,suppliers:ss,companies:cs};$('content').innerHTML=`

المشتريات

فواتير التوريد من الموردين فقط، وتدخل خامات ومواد التعبئة تلقائيًا إلى مخزن المواد الأولية للشركة المختارة.

الفواتير
${rows.map(x=>``).join('')||''}
رقم الفاتورةالتاريخالمخزنالموردالإجمالي
${esc(x.code)}${esc(x.doc_date)}${esc(x.warehouse_name)}${esc(x.supplier_name||'—')}${money(x.total)}
لا توجد فواتير.
`} async function purchaseForm(){const draft=loadDraft('purchase');const ss=state.cache.suppliers||await api('/api/suppliers');const cs=state.cache.companies||await api('/api/companies');state.cache={...state.cache,suppliers:ss,companies:cs};window.purRows=[{productId:'',quantity:1,unitCost:0}];window.purSupplierProducts=[];openModal(`
`);renderPurRows();if(draft){try{if(draft.supplierId){$('pur_supplier').value=draft.supplierId;await onPurchaseSupplierChange();window.purRows=draft.rows||window.purRows;if($('pur_setup'))$('pur_setup').value=draft.setupCost||0;renderPurRows();toast('تم استرجاع مسودة فاتورة الشراء')}}catch{}}}function savePurchaseDraft(){saveDraft('purchase',{supplierId:$('pur_supplier')?.value||'',rows:window.purRows||[],setupCost:+($('pur_setup')?.value||0)});}async function onPurchaseSupplierChange(){const sid=$('pur_supplier').value;if(!sid){window.purSupplierProducts=[];$('supplierSupplyBox').innerHTML='
اختر المورد لعرض الأصناف التي يوردها.
';renderPurRows();return}const s=(state.cache.suppliers||[]).find(x=>x.id===sid);if(s&&s.company_id)$('pur_company').value=s.company_id;$('supplierSupplyBox').innerHTML='
جارٍ تحميل أصناف المورد...
';window.purSupplierProducts=await api('/api/suppliers/'+sid+'/products');$('supplierSupplyBox').innerHTML=window.purSupplierProducts.length?`
المورد يورد:
${window.purSupplierProducts.map(p=>`${esc(p.name_ar)}`).join('')}
`:'
هذا المورد ليس له أصناف توريد محددة. عدّل كارت المورد أولًا.
';window.purRows=[{productId:window.purSupplierProducts[0]?.id||'',quantity:1,unitCost:window.purSupplierProducts[0]?.default_cost||0}];renderPurRows()}function renderPurRows(){savePurchaseDraft();const ps=window.purSupplierProducts||[];$('purRows').innerHTML=window.purRows.map((r,i)=>`${money(num(r.quantity)*num(r.unitCost))}`).join('')||`اختر المورد أولًا.`}function addPurRow(){if(!(window.purSupplierProducts||[]).length){toast('اختر المورد أولًا وحدد أصنافه');return}const ps=window.purSupplierProducts;window.purRows.push({productId:ps[0].id,quantity:1,unitCost:ps[0].default_cost||0});renderPurRows()}function togglePurchasePayment(){const mode=$('pur_payment')?.value;const inp=$('pur_paid');if(inp){inp.disabled=mode!=='CASH';if(mode!=='CASH')inp.value=0}} async function savePurchase(e){e.preventDefault();try{const sid=$('pur_supplier').value;if(!sid)throw new Error('اختر المورد أولًا');if(!window.purSupplierProducts.length)throw new Error('هذا المورد ليس له أصناف توريد محددة');const d=await api('/api/purchases',{method:'POST',body:JSON.stringify({supplierId:sid,companyId:$('pur_company').value,docDate:$('pur_date').value,paymentType:$('pur_payment')?.value||'CREDIT',paid:+($('pur_paid')?.value||0),setupCost:+($('pur_setup')?.value||0),items:window.purRows})});clearDraft('purchase');closeModal();toast(`تم ترحيل فاتورة التوريد إلى مخزن المواد الأولية — ${money(d.total)}`);purchases()}catch(e){toast(e.message)}} async function suppliers(){const [rows,companies]=await Promise.all([api('/api/suppliers'),api('/api/companies')]);state.cache={...state.cache,suppliers:rows,companies};$('content').innerHTML=`

الموردون

كل مورد له أصناف توريد محددة، وتظهر مباشرة عند عمل فاتورة التوريد.

إجمالي الموردين
${rows.length}
مورد مسجل
موردون لديهم أصناف
${rows.filter(x=>num(x.supplied_count)>0).length}
جاهزون للتوريد
${rows.length} مورد
${rows.map(x=>``).join('')||''}
الكودالموردأصناف التوريدالشركةالهاتفإجراءات
${esc(x.code)}${esc(x.name)}${num(x.supplied_count)} صنف${esc(x.company_name||'—')}${esc(x.phone||'—')}
لا يوجد موردون.
`} async function supplierForm(x={}){const [cs,ps,selected]=await Promise.all([api('/api/companies'),api('/api/products'),x.id?api('/api/suppliers/'+x.id+'/products'):Promise.resolve([])]);const eligible=ps.filter(p=>p.active&&['RAW_MATERIAL','PACK'].includes(p.type));const selectedIds=new Set(selected.map(p=>p.id));openModal(`
`)}async function deleteSupplier(id){if(!confirm('حذف المورد؟ سيتم إخفاؤه من القوائم الجديدة مع الاحتفاظ بحركاته السابقة.'))return;try{await api('/api/suppliers/'+id,{method:'DELETE'});toast('تم حذف المورد');suppliers()}catch(e){toast(e.message)}}async function saveSupplier(e,id){e.preventDefault();try{const productIds=[...document.querySelectorAll('.sp_product:checked')].map(x=>x.value);if(!productIds.length)throw new Error('حدد صنفًا واحدًا على الأقل يورده المورد');const b={code:$('sp_code').value,name:$('sp_name').value,companyId:$('sp_company').value,phone:$('sp_phone').value,email:$('sp_email').value,address:$('sp_addr').value,taxNumber:$('sp_tax').value,productIds};await api(id?'/api/suppliers/'+id:'/api/suppliers',{method:id?'PUT':'POST',body:JSON.stringify(b)});closeModal();suppliers();toast('تم حفظ المورد وأصناف التوريد')}catch(e){toast(e.message)}} async function printSaleInvoice(id){try{const d=await api('/api/sales/'+id),doc=d.doc,items=d.items||[];const rows=items.map(i=>`${esc(i.product_code)}${esc(i.product_name)}${num(i.quantity)}${money(i.unit_price)}${money(i.total)}`).join('');printDocument('فاتورة بيع '+doc.code,`

ZEZO SYSTEM

فاتورة بيع

العميل: ${esc(doc.customer_name||'—')}
التاريخ: ${esc(doc.doc_date)}
رقم الفاتورة: ${esc(doc.code)}
${rows}
الكودالصنفالكميةسعر الوحدةالإجمالي
الخصم: ${money(doc.discount)} — إجمالي الفاتورة: ${money(doc.total)}
`)}catch(e){toast(e.message)}} async function supplierLedger(){const rows=state.cache.suppliers||await api('/api/suppliers');state.cache.suppliers=rows;$('content').innerHTML=`

كشف حساب المورد

المشتريات والمدفوعات والرصيد المستحق أو الدائن.

اختر موردًا.
`}async function loadSupplierLedger(){const id=$('supplierLedgerSelect').value;if(!id)return;$('supplierLedgerArea').innerHTML='
جارٍ التحميل...
';const rows=await api('/api/supplier-ledger/'+id);const bal=rows.length?rows[rows.length-1].balance:0;$('supplierLedgerArea').innerHTML=`
${money(bal)}الرصيد
${rows.map(r=>``).join('')||''}
التاريخالبيانعلينالناالرصيد
${esc(r.doc_date)}${r.ref_type==='PURCHASE'&&r.ref_id?``:esc(r.description)}${money(r.debit)}${money(r.credit)}${money(r.balance)}
لا توجد حركة.
`}async function purchaseDetails(id){try{const d=await api('/api/purchases/'+id);const doc=d.doc,items=d.items||[],payments=d.payments||[];openModal(``)}catch(e){toast(e.message)}}async function printPurchaseInvoice(id){try{const d=await api('/api/purchases/'+id),doc=d.doc,items=d.items||[];const rows=items.map(i=>`${esc(i.product_code)}${esc(i.product_name)}${num(i.quantity)}${money(i.unit_cost)}${money(i.total)}`).join('');printDocument('فاتورة توريد '+doc.code,`

ZEZO SYSTEM

فاتورة توريد

المورد: ${esc(doc.supplier_name||'—')}
التاريخ: ${esc(doc.doc_date)}
رقم الفاتورة: ${esc(doc.code)}
${rows}
الكودالصنفالكميةسعر الوحدةالإجمالي
التجهيزات / قيمة إضافية: ${money(doc.setup_cost)} — إجمالي الفاتورة: ${money(doc.total)}
`)}catch(e){toast(e.message)}}function supplierPaymentForm(supplierId){const ss=state.cache.suppliers||[],cs=state.cache.companies||[],s=ss.find(x=>x.id===supplierId);openModal(`
`)}async function saveSupplierPayment(e,supplierId){e.preventDefault();try{const f=$('spp_file').files[0];let data='';if(f){if(f.size>4*1024*1024)throw new Error('حجم المرفق أكبر من 4MB');data=await new Promise((resolve,reject)=>{const r=new FileReader();r.onload=()=>resolve(r.result);r.onerror=reject;r.readAsDataURL(f)})}await api('/api/supplier-payments',{method:'POST',body:JSON.stringify({supplierId,companyId:$('spp_c').value,amount:+$('spp_a').value,date:$('spp_d').value,description:$('spp_desc').value,attachmentName:f?.name||'',attachmentData:data})});closeModal();toast('تم تسجيل التحويل');loadSupplierLedger()}catch(e){toast(e.message)}}function supplierLedgerOpen(id){state.page='supplierLedger';render().then(()=>setTimeout(()=>{$('supplierLedgerSelect').value=id;loadSupplierLedger()},100))} async function settlements(){const [rows,ss,fs,cs]=await Promise.all([api('/api/settlements'),api('/api/suppliers'),api('/api/factories'),api('/api/customers')]);state.cache={...state.cache,suppliers:ss,factories:fs,customers:cs};$('content').innerHTML=`

التسويات

زيادة أو نقص مستقلة مع تحديد تأثيرها على الخزنة والربحية وكشف الحساب.

التسويات
${rows.map(r=>``).join('')||''}
التاريخالنوعالقيمةالخزنةالربحيةالبيان
${esc(r.txn_date)}${esc(r.target)} / ${esc(r.direction)}${money(r.amount)}${r.affect_vault?'نعم':'لا'}${r.affect_profit?'نعم':'لا'}${esc(r.note)}
لا توجد تسويات.
`}function settlementForm(){const ss=state.cache.suppliers||[],fs=state.cache.factories||[],cs=state.cache.customers||[];openModal(`
`)}function toggleSettlementParty(){const t=$('set_target').value;['set_supplier','set_factory','set_customer'].forEach(id=>{$(id).closest('.field').style.display='none'});const m={SUPPLIER:'set_supplier',FACTORY:'set_factory',CUSTOMER:'set_customer'}[t];if(m)$(m).closest('.field').style.display='block'}async function saveSettlement(e){e.preventDefault();try{await api('/api/settlements',{method:'POST',body:JSON.stringify({target:$('set_target').value,direction:$('set_direction').value,amount:+$('set_amount').value,date:$('set_date').value,affectVault:$('set_vault').value==='1',affectProfit:$('set_profit').value==='1',supplierId:$('set_supplier').value||null,factoryId:$('set_factory').value||null,customerId:$('set_customer').value||null,batchNo:$('set_batch').value,note:$('set_note').value})});closeModal();toast('تم حفظ التسوية');settlements()}catch(e){toast(e.message)}} async function companies(){const cs=await api('/api/companies');state.cache.companies=cs;renderCompanySelector();$('content').innerHTML=`

الشركات والشركاء

اختر الشركة من أعلى النظام، وكل شركة لها موردون ومصانع ومخازن وحسابات مستقلة.

${cs.map(c=>`
${esc(c.name_ar)}EGP
${money(c.income)}إيرادات
${money(c.expense)}مصروفات
${money(num(c.income)-num(c.expense))}صافي
`).join('')}
`} async function monthlyReport(){ const now=new Date(), ym=now.toISOString().slice(0,7); const d=await api('/api/reports/monthly?month='+encodeURIComponent(window.monthlyReportMonth||ym)); const max=Math.max(1,...(d.topSales||[]).map(x=>Number(x.quantity||0))); const maxP=Math.max(1,...(d.topProfit||[]).map(x=>Number(x.profit||0))); const donutTotal=Math.max(1,Number(d.revenue||0)+Number(d.cogs||0)+Number(d.expenses||0)); const donut=[['إيرادات',d.revenue,'#16845b'],['تكلفة مبيعات',d.cogs,'#b97811'],['مصروفات',d.expenses,'#d63b4a']]; let acc=0; const donutSvg=donut.map(([label,val,color])=>{const v=Number(val||0)/donutTotal*360;const a1=acc*Math.PI/180,a2=(acc+v)*Math.PI/180;const x1=50+42*Math.cos(a1),y1=50+42*Math.sin(a1),x2=50+42*Math.cos(a2),y2=50+42*Math.sin(a2);const large=v>180?1:0;acc+=v;return ``}).join(''); const legends=donut.map(([label,val,color])=>`
${label}${money(val)}
`).join(''); $('content').innerHTML=`

تقرير الشهر

لوحة مالية وإدارية احترافية للشركة المختارة — ${esc(d.company)}

إجمالي المبيعات
${money(d.revenue)}
${d.sales.docs} فاتورة
مجمل الربح
${money(d.grossProfit)}
هامش ${d.revenue?Math.round(d.grossProfit/d.revenue*100):0}%
صافي الربح
${money(d.netProfit)}
بعد تكلفة المبيعات والمصروفات
صافي حركة النقدية
${money(d.cashNet)}
دخل ${money(d.cashIn)} — خرج ${money(d.cashOut)}
📊 توزيع النتائج المالية
${donutSvg}
${legends}
صافي الربح${money(d.netProfit)}
💵 حركة الأموال
${money(d.cashIn)}إجمالي الداخل
${money(d.cashOut)}إجمالي الخارج
تحصيل العملاء: ${money(d.customerCollections)}
سداد الموردين: ${money(d.supplierPayments)}
مصروفات نقدية: ${money(d.expensesCash)}
🏆 أكثر المنتجات مبيعًا
${(d.topSales||[]).map(x=>``).join('')||''}
المنتجالكميةالمبيعاتالرسم
${esc(x.product_name)}
${esc(x.code)}
${num(x.quantity)}${money(x.revenue)}
لا توجد مبيعات هذا الشهر.
💎 أكثر المنتجات ربحية
${(d.topProfit||[]).map(x=>{const margin=x.revenue?Number(x.profit)/Number(x.revenue)*100:0;return ``}).join('')||''}
المنتجالربحالهامشالرسم
${esc(x.product_name)}
${esc(x.code)}
${money(x.profit)}${Math.round(margin)}%
لا توجد بيانات ربحية.
📑 ملخص النشاط المالي
المشتريات
${money(d.purchases.total)}
${d.purchases.docs} فاتورة — مدفوع ${money(d.purchases.paid)}
الخصومات
${money(d.sales.discounts)}
من فواتير البيع
تكلفة البضاعة المباعة
${money(d.cogs)}
COGS
📈 حركة يومية مختصرة
${(d.daily||[]).map(x=>``).join('')||''}
اليومالمبيعاتالأموال الخارجةصافي
${esc(x.d)}${money(x.revenue)}${money(x.outflow)}${money(Number(x.revenue||0)-Number(x.outflow||0))}
لا توجد حركات.
`; } async function finance(){const [cs,rows]=await Promise.all([api('/api/companies'),api('/api/finance/transactions')]);state.cache.companies=cs;$('content').innerHTML=`

المالية والأرباح

ملخص مبسط لكل شركة، مع إمكانية تسجيل المصروفات والإيرادات وإرفاق إثبات.

${cs.map(c=>`
${esc(c.name_ar)}
${money(c.income)}إيرادات
${money(c.expense)}مصروفات
${money(num(c.income)-num(c.expense))}صافي
`).join('')}
آخر الحركات المالية
${rows.slice(0,100).map(r=>``).join('')||''}
التاريخالشركةالنوعالمبلغالبيانمرفق
${esc(r.txn_date)}${esc(r.company_name)}${esc(r.type)}${money(r.amount)}${esc(r.description)}${r.attachment_data?'📎 نعم':'—'}
لا توجد حركات.
`} function financeForm(){const cs=state.cache.companies||[];openModal(`
`)}async function saveFinance(e){e.preventDefault();try{const file=$('fin_file').files[0];let data='';if(file){if(file.size>4*1024*1024)throw new Error('حجم المرفق أكبر من 4MB');data=await new Promise((resolve,reject)=>{const r=new FileReader();r.onload=()=>resolve(r.result);r.onerror=reject;r.readAsDataURL(file)})}await api('/api/finance/transactions',{method:'POST',body:JSON.stringify({companyId:$('fin_c').value,type:$('fin_t').value,amount:+$('fin_a').value,date:$('fin_d').value,description:$('fin_desc').value,attachmentName:file?.name||'',attachmentData:data})});closeModal();toast('تم حفظ الحركة المالية');finance()}catch(e){toast(e.message)}} async function users(){const [rows,roles,companies]=await Promise.all([api('/api/users'),api('/api/roles'),api('/api/companies')]);state.cache={...state.cache,roles,companies};$('content').innerHTML=`

المستخدمون والصلاحيات

مدير النظام يحدد صلاحيات كل مستخدم بشكل مستقل، مع إمكانية استخدام الدور كنقطة بداية.

${rows.length} مستخدم
${rows.map(x=>``).join('')}
الاسمالبريدالدورالشركاتالحالةإجراء
${esc(x.name)}${esc(x.email)}${esc(x.role_name)}${x.active?'نشط':'غير نشط'}
`}function userForm(x={}){const rs=state.cache.roles||[];openModal(`
`)}async function saveUser(e,id){e.preventDefault();try{const b={name:$('u_name').value,email:$('u_email').value,roleId:$('u_role').value,password:$('u_pass').value,active:$('u_active').value==='1'};await api(id?'/api/users/'+id:'/api/users',{method:id?'PUT':'POST',body:JSON.stringify(b)});closeModal();toast('تم حفظ المستخدم');users()}catch(e){toast(e.message)}}async function editUserCompanies(uid){try{const [rows,companies]=await Promise.all([api('/api/users/'+uid+'/companies'),api('/api/companies')]);const selected=new Set(rows.map(x=>x.id));openModal(``)}catch(e){toast(e.message)}}async function saveUserCompanies(uid){try{const companyIds=[...document.querySelectorAll('.company-access:checked')].map(x=>x.value);if(!companyIds.length)throw new Error('اختر شركة واحدة على الأقل');await api('/api/users/'+uid+'/companies',{method:'PUT',body:JSON.stringify({companyIds})});closeModal();toast('تم حفظ شركات المستخدم');users()}catch(e){toast(e.message)}}async function permissionForm(uid,name){try{const d=await api('/api/users/'+uid+'/permissions');const groups={};for(const p of d.permissions){(groups[p.group_name] ||= []).push(p)}openModal(``)}catch(e){toast(e.message)}}async function savePermissions(uid){try{const overrides={};document.querySelectorAll('.perm-check').forEach(x=>overrides[x.dataset.code]=x.checked);await api('/api/users/'+uid+'/permissions',{method:'PUT',body:JSON.stringify({overrides})});closeModal();toast('تم حفظ الصلاحيات');if(uid===state.user?.id){state.user.permissions=Object.entries(overrides).filter(([k,v])=>v).map(([k])=>k);setNav()}users()}catch(e){toast(e.message)}} boot();