/* Bonyan ERP — Suppliers list + account statement (multi-currency: د.ع + $) */ function SuppliersScreen() { const I = window.BnyIcon; const D = window.BnyData; const cur = window.BnyCur; const { Card, Button, Money, Badge, Tabs, Avatar, Input } = window.BonyanDesignSystem_ad3ca0; const isMobile = window.useBnyMobile(); window.useBnyInvoices(); // إعادة الرسم عند تعديل/حذف فاتورة شراء من أي مكان // المورد المطلوب من المساعد الصوتي، وإلا الأول — null لمشترك جديد بلا موردين const [sel, setSel] = React.useState(() => { const pid = window.BnyPendingSupplierId; if (pid != null) { window.BnyPendingSupplierId = null; const s = D.suppliers.find((x) => x.id === pid); if (s) return s; } return D.suppliers[0] || null; }); const [tab, setTab] = React.useState("stmt"); const [curFilter, setCurFilter] = React.useState("all"); // all | iqd | usd const [search, setSearch] = React.useState(""); const [sort, setSort] = React.useState("debt-desc"); // debt-desc|debt-asc|date-desc|date-asc const [filterVal, setFilterVal] = React.useState(""); // type:نقدي | type:آجل | "" const [printSup, setPrintSup] = React.useState(null); // المورد قيد سحب/طباعة الكشف const [showNew, setShowNew] = React.useState(false); // نافذة مورد جديد const [showPay, setShowPay] = React.useState(false); // نافذة تسديد دفعة const [showEdit, setShowEdit] = React.useState(false); // نافذة تعديل بيانات المورد const [payInv, setPayInv] = React.useState(null); // فاتورة شراء قيد التسديد const [delPay, setDelPay] = React.useState(null); // دفعة قيد الحذف (تأكيد المدير) const [printMode, setPrintMode] = React.useState("stmt"); // stmt | purch | pay const saveEdit = async (patch) => { await window.BnyOps.updateSupplierInfo(sel, patch); setShowEdit(false); forceRender(); }; const [, forceRender] = React.useReducer((x) => x + 1, 0); const doPayInvoice = async (amount) => { await window.BnyOps.payInvoice(payInv, amount); setPayInv(null); forceRender(); }; const confirmDeletePay = async (creds) => { await window.BnyOps.deleteSupplierPayment(delPay, creds); setDelPay(null); forceRender(); }; const doPrint = () => { if (sel) { setPrintMode(tab === "purch" ? "purch" : tab); setPrintSup(sel); } }; const addSupplier = async (data) => { const s = await window.BnyOps.addSupplier({ name: data.name.trim(), phone: (data.phone || "").trim(), type: data.type || "نقدي" }); setShowNew(false); setSel(s); forceRender(); }; const payOut = async (amount, curKey) => { await window.BnyOps.paySupplier(sel, amount, curKey); setShowPay(false); forceRender(); }; // فاتورة شراء جديدة للمورد المحدَّد: مسودة تُفتح للتحرير فورًا const newPurchase = async () => { const inv = await window.BnyOps.newPurchaseDraft(sel.name, sel.id); window.BnyOpenInvoice(inv.no); }; React.useEffect(() => { if (!printSup) return; document.body.classList.add("bny-printing-stmt"); const after = () => { document.body.classList.remove("bny-printing-stmt"); setPrintSup(null); }; window.addEventListener("afterprint", after); const t = setTimeout(() => window.print(), 120); return () => { clearTimeout(t); window.removeEventListener("afterprint", after); document.body.classList.remove("bny-printing-stmt"); }; }, [printSup]); // فواتير الشراء الفعلية للمورد (بالمتبقّي والتسديد) — الأحدث أولًا const purchases = !sel ? [] : window.BnySupPurchaseInvoices(sel) .filter((i) => curFilter === "all" || i.cur === curFilter) .slice().sort((a, b) => String(b.date || "").localeCompare(String(a.date || ""))); // كشف الحساب مشتقّ (فواتير شراء + دفعات صادرة) — الأقدم أعلى، أحدث دفعة أسفل const stmtRows = !sel ? [] : window.BnySupStatement(sel) .filter((s) => (curFilter === "all" || s.cur === curFilter) && (tab !== "pay" || s.debit > 0)); const CurFilter = () => (
); const supSortOptions = [ { key: "debt-desc", label: "المستحقّات: الأعلى أولًا" }, { key: "debt-asc", label: "المستحقّات: الأقل أولًا" }, { key: "date-desc", label: "الأحدث حركة" }, { key: "date-asc", label: "الأقدم حركة" }, ]; const supFilterOptions = [ { value: "type:نقدي", label: "النوع: نقدي" }, { value: "type:آجل", label: "النوع: آجل" }, ]; const toIqd = (s) => window.BnyToIqd(s.payable, s.payableUsd); const supRows = D.suppliers .filter((s) => { const q = search.trim(); if (!q) return true; return s.name.includes(q) || (s.phone || "").includes(q); }) .filter((s) => { if (!filterVal) return true; if (filterVal.indexOf("type:") === 0) return s.type === filterVal.slice(5); return true; }) .slice() .sort((a, b) => { if (sort === "debt-desc") return toIqd(b) - toIqd(a); if (sort === "debt-asc") return toIqd(a) - toIqd(b); const da = window.BnyLastMovement(a.name, a.id, "sup") || "", db = window.BnyLastMovement(b.name, b.id, "sup") || ""; if (da === db) return 0; if (!da) return 1; if (!db) return -1; return sort === "date-desc" ? (db < da ? -1 : 1) : (da < db ? -1 : 1); }); // ---- كشف ديون الموردين الإجمالي (المتبقّي مشتقّ من فواتير الشراء) ---- const iqdOf = (amt, c) => (c === "usd" ? (Number(amt) || 0) * (D.usdRate || 1500) : (Number(amt) || 0)); const supDebtRows = D.suppliers.map((s) => { const invs = window.BnySupPurchaseInvoices(s); const due = window.BnySupDue(s); return { id: s.id, name: s.name, total: invs.reduce((x, i) => x + iqdOf(i.total, i.cur), 0), paid: invs.reduce((x, i) => x + iqdOf(i.paid, i.cur), 0), remaining: window.BnyToIqd(due.iqd, due.usd), extra: s.lastPay || "", _s: s, }; }).sort((a, b) => b.remaining - a.remaining); const showSupDetail = (r) => { setSel(r._s); try { window.scrollTo({ top: 0, behavior: "smooth" }); } catch (e) {} }; // بيانات الطباعة حسب النمط: كشف / فواتير الشراء / الدفعات الصادرة const printData = (() => { if (!printSup) return { title: "", rows: [], cols: [] }; if (printMode === "purch") { const rows = window.BnySupPurchaseInvoices(printSup).slice().sort((a, b) => String(a.date || "").localeCompare(String(b.date || ""))); return { title: "فواتير شراء المورد", rows, cols: [ { label: "التاريخ", mono: true, render: (i) => window.BnyDate(i.date) }, { label: "الفاتورة", render: (i) => "#" + i.no }, { label: "العملة", align: "center", render: (i) => cur(i.cur) }, { label: "الإجمالي", align: "end", mono: true, render: (i) => Number(i.total || 0).toLocaleString("en-US") }, { label: "المسدَّد", align: "end", mono: true, render: (i) => Number(i.paid || 0).toLocaleString("en-US") }, { label: "المتبقّي", align: "end", mono: true, render: (i) => Math.max(0, (Number(i.total) || 0) - (Number(i.paid) || 0)).toLocaleString("en-US") }, ] }; } const all = window.BnySupStatement(printSup); const rows = printMode === "pay" ? all.filter((r) => r.kind === "payment") : all; return { title: printMode === "pay" ? "الدفعات الصادرة للمورد" : "كشف حساب مورد", rows, cols: [ { label: "التاريخ", render: (s) => s.date, mono: true }, { label: "البيان", render: (s) => s.ref }, { label: "العملة", align: "center", render: (s) => cur(s.cur) }, { label: "دفعة صادرة", align: "end", mono: true, render: (s) => s.debit ? Number(s.debit).toLocaleString("en-US") : "—" }, { label: "مشتريات", align: "end", mono: true, render: (s) => s.credit ? Number(s.credit).toLocaleString("en-US") : "—" }, ] }; })(); return (
{/* list */} } onClick={() => setShowNew(true)}>جديد}>
setSearch(e.target.value)} placeholder="ابحث عن مورد" icon={} />
{supRows.length === 0 &&
لا موردون مطابقون
} {supRows.map((s) => { const on = sel && s.id === sel.id; const settled = s.payable === 0 && s.payableUsd === 0; return ( ); })}
{/* detail */}
{!sel ? (
لا موردون بعد
أضف أول مورد لعرض حسابه وفواتير الشراء هنا.
) : (<>

{sel.name}

{sel.type}
{sel.phone} · آخر دفعة {sel.lastPay}
{!isMobile && (
)}
{isMobile && (
)} {/* المستحق للمورد — العملتان منفصلتان */}
0 ? "debt" : "credit"} style={{ fontSize: isMobile ? 19 : 22 }} />} /> 0 ? "debt" : "credit"} style={{ fontSize: isMobile ? 19 : 22 }} />} />
} />
{tab === "purch" ? (
{purchases.map((inv) => { const st = window.BnyInvStatus(inv); const rem = Math.max(0, (Number(inv.total) || 0) - (Number(inv.paid) || 0)); const u = inv.cur === "usd" ? "$" : "د.ع"; return (
window.BnyOpenInvoice(inv.no)}>
فاتورة #{inv.no} {st.label}
{window.BnyDate(inv.date)} · الإجمالي {Number(inv.total || 0).toLocaleString("en-US")} {u} · مسدَّد {Number(inv.paid || 0).toLocaleString("en-US")} {u}
المتبقّي
0 ? "debt" : "credit"} />
{rem > 0 ? : مسددة}
); })} {purchases.length === 0 &&
لا توجد فواتير شراء
}
) : ( isMobile ? (
{stmtRows.map((s, i) => { const canOpen = s.invNo && window.BnyInvoices && window.BnyInvoices.get(s.invNo); const isPay = s.kind === "payment"; return (
window.BnyOpenInvoice(s.invNo) : undefined}>
{s.ref}
{s.date} · {s.debit ? "دفعة صادرة" : "مشتريات"} · {cur(s.cur)}
{s.debit ? : } {isPay && s._pay && }
); })} {stmtRows.length === 0 &&
لا توجد حركات
}
) : (
{stmtRows.map((s, i) => { const canOpen = s.invNo && window.BnyInvoices && window.BnyInvoices.get(s.invNo); const isPay = s.kind === "payment"; return ( (e.currentTarget.style.background = "var(--surface-hover)") : undefined} onMouseLeave={canOpen ? (e) => (e.currentTarget.style.background = "transparent") : undefined} style={{ borderTop: "1px solid var(--border-subtle)", transition: "background var(--dur-fast,160ms) ease" }}> ); })} {stmtRows.length === 0 && }
التاريخ البيان العملة دفعة صادرة مشتريات
window.BnyOpenInvoice(s.invNo) : undefined} style={{ padding: "13px 0", fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-muted)", cursor: canOpen ? "pointer" : "default" }}>{s.date} window.BnyOpenInvoice(s.invNo) : undefined} style={{ padding: "13px 0", fontSize: 14, fontWeight: 500, color: canOpen ? "var(--text-link)" : "var(--text-strong)", cursor: canOpen ? "pointer" : "default" }}>{s.ref} {cur(s.cur)} {s.debit ? : } {s.credit ? : } {isPay && s._pay ? : null}
لا توجد حركات
) )}
)}
{/* نوافذ: مورد جديد + تسديد دفعة */} {showNew && setShowNew(false)} />} {showPay && sel && setShowPay(false)} />} {showEdit && sel && setShowEdit(false)} />} {payInv && ( doPayInvoice(amount)} onClose={() => setPayInv(null)} /> )} {delPay && ( setDelPay(null)} /> )} {/* كشف ديون الموردين الإجمالي انتقل إلى قسم «الحسابات» */} {/* ورقة الكشف للطباعة/PDF (مخفية على الشاشة) */} {printSup && (
t.label)} />
)} ); } function Mini({ label, node }) { return (
{label}
{node}
); } /* نافذة مورد جديد: اسم + هاتف + نوع */ function NewSupplierModal({ onSave, onClose }) { const I = window.BnyIcon; const { Button, Input } = window.BonyanDesignSystem_ad3ca0; const [name, setName] = React.useState(""); const [phone, setPhone] = React.useState(""); const [type, setType] = React.useState("نقدي"); const [busy, setBusy] = React.useState(false); const lbl = { fontSize: 12.5, fontWeight: 600, color: "var(--text-strong)", display: "block", marginBottom: 5 }; const save = async () => { if (!name.trim() || busy) return; setBusy(true); try { await onSave({ name, phone, type }); } catch (e) { console.error(e); } finally { setBusy(false); } }; return (
e.stopPropagation()} className="bny-card bny-card--pad" style={{ width: "100%", maxWidth: 400 }}>

مورد جديد

setName(e.target.value)} placeholder="مثال: مصنع حديد النجف" autoFocus />
setPhone(e.target.value)} placeholder="07XXXXXXXXX" icon={} />
); } /* نافذة تعديل بيانات مورد */ function EditSupplierModal({ s, onSave, onClose }) { const I = window.BnyIcon; const { Button, Input } = window.BonyanDesignSystem_ad3ca0; const [f, setF] = React.useState(() => ({ name: s.name || "", phone: s.phone || "", type: s.type || "نقدي" })); const [busy, setBusy] = React.useState(false); const set = (k, v) => setF((x) => ({ ...x, [k]: v })); const lbl = { fontSize: 12.5, fontWeight: 600, color: "var(--text-strong)", display: "block", marginBottom: 5 }; const save = async () => { if (!f.name.trim() || busy) return; setBusy(true); try { await onSave({ name: f.name.trim(), phone: f.phone.trim(), type: f.type }); } catch (e) { console.error(e); } finally { setBusy(false); } }; return (
e.stopPropagation()} className="bny-card bny-card--pad" style={{ width: "100%", maxWidth: 400 }}>

تعديل بيانات: {s.name}

set("name", e.target.value)} />
set("phone", e.target.value)} icon={} />
{f.name.trim() !== s.name &&

تغيير الاسم سينعكس على كل فواتير الشراء تلقائيًا.

}
); } window.SuppliersScreen = SuppliersScreen;