/* Bonyan ERP — Invoices register (سجل الفواتير)
كل الفواتير (بيع + شراء) مرتّبة من الأحدث للأقدم، مع فلاتر:
النوع (بيع/شراء) · المدى الزمني (رينج) · رقم الفاتورة (رينج) · الحالة.
النقر على فاتورة يفتح النافذة الموحّدة (أصناف + تعديل + حذف). */
function InvoicesScreen({ onNav }) {
const I = window.BnyIcon;
const cur = window.BnyCur;
const fmtDate = window.BnyDate;
const status = window.BnyInvStatus;
const { Card, Button, Money, Badge } = window.BonyanDesignSystem_ad3ca0;
const isMobile = window.useBnyMobile();
const list = window.useBnyInvoices(); // يتحدّث تلقائيًا بعد أي تعديل/حذف
const D = window.BnyData;
const orgs = D.organizations || [];
// مؤسسة العميل المرتبط بالفاتورة (بمطابقة الاسم) — للعرض والبحث
const orgOfInvoice = (inv) => {
const c = (inv.custId != null ? (D.customers || []).find((x) => x.id === inv.custId) : null)
|| (D.customers || []).find((x) => x.name === inv.party);
if (!c || !c.org) return "";
const o = orgs.find((x) => x.id === c.org);
return o ? o.name : "";
};
const [textQ, setTextQ] = React.useState(""); // بحث بالعميل/المؤسسة
const [dupTarget, setDupTarget] = React.useState(null); // فاتورة قيد تأكيد النسخ
const [typeF, setTypeF] = React.useState("all"); // all | sale | purchase
const [statusF, setStatusF] = React.useState("all"); // all | paid | partial | unpaid
const [dateFrom, setDateFrom] = React.useState("");
const [dateTo, setDateTo] = React.useState("");
const [numFrom, setNumFrom] = React.useState("");
const [numTo, setNumTo] = React.useState("");
// نسخ فاتورة: توليد رقم جديد ثم إنشاء نسخة مطابقة كمسودة غير مسددة
const nextNoFor = (type) => {
const cfg = (D.numbering || {})[type] || { prefix: type === "purchase" ? "PINV-" : "" };
const maxNum = list.filter((i) => i.type === type).reduce((m, i) => Math.max(m, i.num || 0), 0) + 1;
return (cfg.prefix || "") + maxNum;
};
const duplicate = (inv) => {
const no = nextNoFor(inv.type);
const clone = { ...inv, no, num: parseInt(String(no).replace(/\D/g, ""), 10) || 0, paid: 0,
date: new Date().toISOString().slice(0, 10), items: (inv.items || []).map((it) => ({ ...it })) };
window.BnyInvoices.add(clone, { applyEffects: true }); // النسخة فاتورة فعلية: تؤثر على المخزون والأرصدة
if (window.BnyOps) window.BnyOps.log("invoice_create", "نسخ الفاتورة #" + inv.no + " إلى مسودة جديدة #" + no, no);
setDupTarget(null);
window.BnyOpenInvoice(no); // افتحها كمسودة للتعديل
};
const remaining = (inv) => Math.max(0, (inv.total || 0) - (inv.paid || 0));
const openInvoice = (inv) => window.BnyOpenInvoice(inv.no);
const rows = list
.filter((inv) => {
if (typeF !== "all" && inv.type !== typeF) return false;
if (statusF !== "all" && status(inv).key !== statusF) return false;
if (dateFrom && inv.date < dateFrom) return false;
if (dateTo && inv.date > dateTo) return false;
if (numFrom !== "" && inv.num < Number(numFrom)) return false;
if (numTo !== "" && inv.num > Number(numTo)) return false;
if (textQ) { const on = orgOfInvoice(inv); if (!((inv.party || "").includes(textQ) || on.includes(textQ))) return false; }
return true;
})
.slice()
.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : b.num - a.num));
const hasFilter = textQ || typeF !== "all" || statusF !== "all" || dateFrom || dateTo || numFrom !== "" || numTo !== "";
const reset = () => { setTextQ(""); setTypeF("all"); setStatusF("all"); setDateFrom(""); setDateTo(""); setNumFrom(""); setNumTo(""); };
const typeBadge = (t) => t === "sale" ?
| رقم الفاتورة | النوع | اسم العميل | المؤسسة | التاريخ | المبلغ الكلي | الحالة | المتبقي | خيارات |
|---|---|---|---|---|---|---|---|---|
| {inv.no} | {typeBadge(inv.type)} | {inv.party} | {orgOfInvoice(inv) || "—"} | {fmtDate(inv.date)} | {st.label} |
{rem > 0 ? |
سيتم إنشاء فاتورة جديدة مطابقة تمامًا كمسودة (غير مسدّدة) برقم جديد، ثم فتحها للتعديل. هل تريد المتابعة؟