/* global React, I, UI, SSSData, DocListPage */
const purchUI = window.UI;
const { useState: useStateP } = React;

// ---------- ผูกค่าใช้จ่ายกับโปรเจกต์/PO (persist) ----------
const EXL_KEY = "sss-expense-links";
function exlLoad() { try { return JSON.parse(localStorage.getItem(EXL_KEY) || "{}"); } catch { return {}; } }
function exlSave(all) { try { localStorage.setItem(EXL_KEY, JSON.stringify(all)); } catch {} }

function PurchaseOrders({ navigate }) {
  const D = window.SSSData;
  const { fmt0 } = window.UI;
  return (
    <window.DocListPage
      title="ใบสั่งซื้อ · Purchase Orders"
      sub="สั่งซื้อวัตถุดิบและบริการจาก vendor"
      docType="purchase-order" rows={D.PurchaseOrders}
      statusFilter={["รออนุมัติ", "อนุมัติแล้ว", "รับสินค้าแล้ว", "ยกเลิกแล้ว"]}
      createLabel="สร้างใบสั่งซื้อ"
      onCreate={() => window.openNewDoc && window.openNewDoc("purchase-order")}
      onRowClick={(r) => navigate("purchase-orders/" + r.no)}
      kpis={[
        { label: "ใบสั่งซื้อเปิดอยู่", value: D.PurchaseOrders.filter(p=>p.status!=='รับสินค้าแล้ว').length, sub: `฿${fmt0(D.PurchaseOrders.filter(p=>p.status!=='รับสินค้าแล้ว').reduce((s,p)=>s+p.amount,0))}`, icon: I.buy },
        { label: "รออนุมัติ", value: D.PurchaseOrders.filter(p=>p.status==='รออนุมัติ').length, sub: "ต้องการอนุมัติจาก Admin", icon: I.clock },
        { label: "รอรับสินค้า", value: D.PurchaseOrders.filter(p=>p.status==='อนุมัติแล้ว').length, sub: D.PurchaseOrders.filter(p=>p.status==='อนุมัติแล้ว').map(p=>p.no).slice(0,3).join(", ") || "—", icon: I.truck },
        { label: "ยอดซื้อเดือนนี้", value: `฿${fmt0(D.PurchaseOrders.reduce((s,p)=>s+p.amount,0))}`, sub: "รวมทุกสถานะ", icon: I.calc }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่" },
        { key: "vendor", label: "ผู้ขาย" },
        { key: "expected", label: "นัดรับ" },
        { key: "amount", label: "ยอดรวม", right: true, render: r => <span className="amount">฿{fmt0(r.amount)}</span> },
        { key: "status", label: "สถานะ", render: r => <window.UI.Badge>{r.status}</window.UI.Badge> }
      ]}
    />
  );
}

// ============ Expenses with attachments + workflow ============
function Expenses({ navigate }) {
  const D = window.SSSData;
  const { Btn, Badge, Card, PageHead, Tabs, Drawer, Workflow, fmt0, fmtM } = window.UI;
  const [open, setOpen] = useStateP(false);
  const [active, setActive] = useStateP(null);
  const [tab, setTab] = useStateP("all");
  const [q, setQ] = useStateP("");
  const [dateF, setDateF] = useStateP({ mode: "all" });
  const [allLinks, setAllLinks] = useStateP(exlLoad);
  const [showLink, setShowLink] = useStateP(false);

  const linksOf = (no) => allLinks[no] || ["PO-2569-0094", "JOB-2569-019"];
  const setLinks = (no, list) => { const next = { ...allLinks, [no]: list }; setAllLinks(next); exlSave(next); };
  const addLink = (code) => {
    if (!active) return;
    const cur = linksOf(active.no);
    if (cur.includes(code)) { window.toast && window.toast("ผูกรายการนี้ไว้แล้ว"); return; }
    setLinks(active.no, [...cur, code]);
    if (code.startsWith("JOB") && window.ProjX) {
      const x = window.ProjX.pxGet(code);
      const le = x.linkedExpenses || [];
      if (!le.includes(active.no)) window.ProjX.pxSet(code, { linkedExpenses: [...le, active.no] });
    }
    window.logActivity && window.logActivity("ผูกค่าใช้จ่าย", `${active.no} ↔ ${code}`, "update");
    window.toast && window.toast(`ผูก ${active.no} กับ ${code} แล้ว`);
  };
  const removeLink = (code) => {
    if (!active) return;
    setLinks(active.no, linksOf(active.no).filter(c => c !== code));
    if (code.startsWith("JOB") && window.ProjX) {
      const x = window.ProjX.pxGet(code);
      window.ProjX.pxSet(code, { linkedExpenses: (x.linkedExpenses || []).filter(n => n !== active.no) });
    }
    window.toast && window.toast(`ยกเลิกการผูก ${code} แล้ว`);
  };

  const tabs = [
    { key: "all", label: "ทั้งหมด", count: D.Expenses.length },
    { key: "รออนุมัติ", label: "รออนุมัติ", count: D.Expenses.filter(e=>e.status==='รออนุมัติ').length },
    { key: "อนุมัติแล้ว", label: "อนุมัติแล้ว", count: D.Expenses.filter(e=>e.status==='อนุมัติแล้ว').length }
  ];
  const byTab = tab === "all" ? D.Expenses : D.Expenses.filter(e => e.status === tab);
  const byQ = q.trim() ? byTab.filter(e => (e.no + " " + e.category + " " + e.vendor).toLowerCase().includes(q.trim().toLowerCase())) : byTab;
  const list = byQ.filter(e => !window.DocDates || window.DocDates.match(dateF, e.date));

  return (
    <>
      <PageHead
        title="ค่าใช้จ่าย · Expenses"
        sub="บันทึกค่าใช้จ่ายพร้อมแนบใบเสร็จ — ผ่าน approval workflow"
        right={<>
          <Btn icon={I.download} kind="ghost" onClick={() => window.exportRowsCSV("expenses", [
            { key: "no", label: "เลขที่" }, { key: "date", label: "วันที่" }, { key: "category", label: "หมวด" },
            { key: "vendor", label: "เจ้าหนี้" }, { key: "amount", label: "ยอดรวม" }, { key: "vat", label: "VAT" },
            { key: "wht", label: "หัก ณ ที่จ่าย" }, { key: "status", label: "สถานะ" }
          ], list)}>ส่งออก</Btn>
          <Btn icon={I.plus} kind="primary" onClick={() => setOpen(true)}>บันทึกค่าใช้จ่าย</Btn>
        </>}
      />

      <div className="grid cols-4 mb-lg">
        <window.UI.Stat label="ค่าใช้จ่ายเดือนนี้" value={`฿${fmt0(D.Expenses.reduce((s,e)=>s+e.amount,0))}`} sub="−8% vs เดือนก่อน" deltaDir="up" icon={I.receipt}/>
        <window.UI.Stat label="VAT ซื้อ (input)" value={`฿${fmt0(D.Expenses.reduce((s,e)=>s+e.vat,0))}`} sub="หักจาก VAT ขายได้" icon={I.tax}/>
        <window.UI.Stat label="หัก ณ ที่จ่ายเดือนนี้" value={`฿${fmt0(D.Expenses.reduce((s,e)=>s+e.wht,0))}`} sub="ภ.ง.ด.3 + ภ.ง.ด.53" icon={I.calc}/>
        <window.UI.Stat label="รออนุมัติ" value={D.Expenses.filter(e=>e.status==='รออนุมัติ').length} sub={`฿${fmt0(D.Expenses.filter(e=>e.status==='รออนุมัติ').reduce((s,e)=>s+e.amount,0))}`} icon={I.clock}/>
      </div>

      <Card flush>
        <div style={{padding: 12, borderBottom: "1px solid var(--line)", display: "flex", gap: 10, alignItems: "center"}}>
          <Tabs items={tabs} active={tab} onChange={setTab}/>
          <span className="spacer"/>
          <div className="search" style={{width: 240}}>
            <I.search size={14}/>
            <input placeholder="ค้นหาเลขที่/หมวด/vendor…" value={q} onChange={e => setQ(e.target.value)}/>
          </div>
          {window.DateFilter && <window.DateFilter value={dateF} onChange={setDateF}/>}
        </div>
        <div className="table-wrap">
          <table className="t">
            <thead>
              <tr>
                <th>เลขที่</th><th>วันที่</th><th>หมวด</th><th>เจ้าหนี้</th>
                <th className="right">ยอดรวม</th><th className="right">VAT</th><th className="right">หัก ณ ที่จ่าย</th>
                <th>แนบไฟล์</th><th>สถานะ</th><th></th>
              </tr>
            </thead>
            <tbody>
              {list.map(e => (
                <tr key={e.no} className="row-clickable" onClick={() => setActive(e)}>
                  <td className="mono">{e.no}</td>
                  <td>{e.date}</td>
                  <td>{e.category}</td>
                  <td className="muted">{e.vendor}</td>
                  <td className="right amount">฿{fmt0(e.amount)}</td>
                  <td className="right amount muted">{e.vat > 0 ? `฿${fmtM(e.vat)}` : "—"}</td>
                  <td className="right amount muted">{e.wht > 0 ? `฿${fmt0(e.wht)}` : "—"}</td>
                  <td>
                    {e.attachments > 0 && (
                      <span style={{display: "inline-flex", alignItems: "center", gap: 4, color: "var(--ink-3)", fontSize: 12}}>
                        <I.paperclip size={12}/>{e.attachments}
                      </span>
                    )}
                  </td>
                  <td><Badge>{e.status}</Badge></td>
                  <td><Btn kind="ghost" size="sm" icon={I.ellipsis}/></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Card>

      {/* Detail drawer */}
      <Drawer open={!!active} onClose={() => setActive(null)} title={active ? `ค่าใช้จ่าย ${active.no}` : ""} wide
        footer={<>
          <Btn kind="ghost">ปฏิเสธ</Btn>
          <Btn kind="primary" icon={I.check}>อนุมัติและออกใบสำคัญจ่าย</Btn>
        </>}>
        {active && (
          <div style={{display: "flex", flexDirection: "column", gap: 16}}>
            <div>
              <div className="mb"><Workflow steps={["บันทึก", "รออนุมัติ", "อนุมัติ", "ออกใบสำคัญจ่าย", "จ่ายแล้ว"]} current={active.status === "รออนุมัติ" ? 1 : 2}/></div>
            </div>

            <div className="field-row cols-2">
              <div className="field"><label>วันที่</label><div className="input" style={{display: "flex", alignItems: "center"}}>{active.date}</div></div>
              <div className="field"><label>หมวดค่าใช้จ่าย</label><div className="input" style={{display: "flex", alignItems: "center"}}>{active.category}</div></div>
            </div>
            <div className="field-row cols-2">
              <div className="field"><label>เจ้าหนี้/ผู้ขาย</label><div className="input" style={{display: "flex", alignItems: "center"}}>{active.vendor}</div></div>
              <div className="field"><label>วิธีจ่าย</label><div className="input" style={{display: "flex", alignItems: "center"}}>โอนผ่านธนาคาร · KBank</div></div>
            </div>
            <div className="field-row cols-3">
              <div className="field"><label>ยอดรวม (ก่อน VAT)</label><div className="input amount" style={{display: "flex", alignItems: "center"}}>฿{fmtM(active.amount - active.vat)}</div></div>
              <div className="field"><label>VAT 7%</label><div className="input amount" style={{display: "flex", alignItems: "center"}}>฿{fmtM(active.vat)}</div></div>
              <div className="field"><label>หัก ณ ที่จ่าย</label><div className="input amount" style={{display: "flex", alignItems: "center"}}>฿{fmtM(active.wht)}</div></div>
            </div>

            <div className="field">
              <label>คำอธิบาย/หมายเหตุ</label>
              <div className="textarea" style={{minHeight: 60}}>ค่าซ่อมเครื่องเลเซอร์ Fiber รุ่น L-3015 — เปลี่ยน lens และ calibrate</div>
            </div>

            <div>
              <div style={{display: "flex", justifyContent: "space-between", marginBottom: 8}}>
                <div style={{fontSize: 13, fontWeight: 500}}>เอกสารแนบ ({active.attachments})</div>
                <Btn kind="ghost" size="sm" icon={I.upload}>เพิ่มไฟล์</Btn>
              </div>
              <div className="attach-grid">
                {[...Array(active.attachments || 1)].map((_, i) => (
                  <div className="attach" key={i}>
                    <div className="ext">{["PDF", "JPG", "PDF"][i] || "PDF"}</div>
                    <div className="name">ใบเสร็จ-{active.no}-{i+1}</div>
                    <div className="meta">{["1.2 MB", "640 KB", "880 KB"][i] || "640 KB"}</div>
                  </div>
                ))}
                <div className="attach-dropzone">
                  <I.upload size={18}/>
                  <div>ลากไฟล์มาวาง<br/>หรือคลิก</div>
                </div>
              </div>
            </div>

            <div>
              <div style={{fontSize: 13, fontWeight: 500, marginBottom: 8}}>ผูกกับโปรเจกต์ / ใบสั่งซื้อ</div>
              <div style={{display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center"}}>
                {linksOf(active.no).map(code => (
                  <span key={code} className="badge outline" style={{display: "inline-flex", alignItems: "center", gap: 6}}>
                    <span className="mono" style={{fontSize: 11}}>{code}</span>
                    <button title="ยกเลิกการผูก" onClick={() => removeLink(code)} style={{border: 0, background: "transparent", cursor: "pointer", color: "var(--ink-3)", padding: 0, fontSize: 13, lineHeight: 1}}>×</button>
                  </span>
                ))}
                {linksOf(active.no).length === 0 && <span style={{fontSize: 12, color: "var(--ink-3)"}}>ยังไม่ได้ผูกกับรายการใด</span>}
                <Btn kind="ghost" size="sm" icon={I.plus} onClick={() => setShowLink(true)}>ผูก</Btn>
              </div>
            </div>

            <div>
              <div style={{fontSize: 13, fontWeight: 500, marginBottom: 8}}>ประวัติ</div>
              <div style={{borderLeft: "2px solid var(--line)", paddingLeft: 12, fontSize: 12.5, color: "var(--ink-3)", display: "flex", flexDirection: "column", gap: 4}}>
                {active.by && <div><b style={{color: "var(--ink-1)"}}>{active.by}</b> · บันทึก · {active.date}</div>}
                <div><b style={{color: "var(--ink-1)"}}>{active.status}</b></div>
              </div>
            </div>
          </div>
        )}
      </Drawer>

      <Drawer open={open} onClose={() => setOpen(false)} title="บันทึกค่าใช้จ่ายใหม่" wide
        footer={<><Btn>บันทึกร่าง</Btn><Btn kind="primary" icon={I.check} onClick={() => { setOpen(false); window.toast && window.toast("บันทึกค่าใช้จ่ายและส่งอนุมัติแล้ว"); }}>ส่งอนุมัติ</Btn></>}>
        <div style={{display: "flex", flexDirection: "column", gap: 14}}>
          <div className="field-row cols-2">
            <div className="field"><label>วันที่</label><input className="input" defaultValue="22 พ.ค. 2569"/></div>
            <div className="field"><label>หมวดค่าใช้จ่าย</label>
              <select className="input">
                <option>ค่าน้ำมัน รถส่งสินค้า</option>
                <option>ค่าไฟฟ้าโรงงาน</option>
                <option>ค่าเช่าโกดัง</option>
                <option>ค่ารับรอง / ประชุมลูกค้า</option>
                <option>ค่าโทรศัพท์/อินเทอร์เน็ต</option>
                <option>อุปกรณ์สำนักงาน</option>
                <option>ค่าซ่อมแซมและบำรุงรักษา</option>
              </select>
            </div>
          </div>
          <div className="field-row cols-2">
            <div className="field"><label>เจ้าหนี้/ผู้ขาย</label><input className="input" placeholder="เลือก vendor หรือพิมพ์ใหม่"/></div>
            <div className="field"><label>วิธีจ่าย</label>
              <select className="input">
                <option>โอนผ่านธนาคาร</option>
                <option>เช็ค</option>
                <option>เงินสด</option>
                <option>หักบัญชีอัตโนมัติ</option>
              </select>
            </div>
          </div>
          <div className="field-row cols-3">
            <div className="field"><label>ยอด (ก่อน VAT)</label><input className="input" placeholder="0.00"/></div>
            <div className="field"><label>VAT 7%</label><input className="input" placeholder="คำนวณอัตโนมัติ"/></div>
            <div className="field"><label>หัก ณ ที่จ่าย</label>
              <select className="input">
                <option>—</option>
                <option>ภ.ง.ด.3 · 1% บริการ</option>
                <option>ภ.ง.ด.3 · 3% บริการ</option>
                <option>ภ.ง.ด.3 · 5% ค่าเช่า</option>
                <option>ภ.ง.ด.53 · 1% บริการ</option>
                <option>ภ.ง.ด.53 · 3% บริการ</option>
              </select>
            </div>
          </div>
          <div className="field"><label>คำอธิบาย</label><textarea className="textarea" placeholder="รายละเอียดเพิ่มเติม…"/></div>
          <div>
            <label className="label" style={{display: "block", marginBottom: 6}}>แนบใบเสร็จ/เอกสาร (จำเป็น)</label>
            <div className="attach-dropzone">
              <I.upload size={24}/>
              <div style={{marginTop: 6}}>ลากไฟล์ใบเสร็จมาวางที่นี่ หรือ <b style={{color: "var(--ink-1)"}}>คลิกเพื่อเลือก</b></div>
              <div style={{fontSize: 11, marginTop: 4}}>รองรับ PDF, JPG, PNG · ขนาดไม่เกิน 10 MB ต่อไฟล์</div>
            </div>
          </div>
          <div className="field"><label>ผูกกับโปรเจกต์/PO (ถ้ามี)</label><input className="input" placeholder="ค้นหา JOB-... หรือ PO-..."/></div>
        </div>
      </Drawer>

      {showLink && active && (
        <ExpenseLinkPicker current={linksOf(active.no)} onPick={addLink} onClose={() => setShowLink(false)}/>
      )}
    </>
  );
}

// ---------- ตัวเลือกผูกโปรเจกต์ / ใบสั่งซื้อ ----------
function ExpenseLinkPicker({ current, onPick, onClose }) {
  const D = window.SSSData;
  const { Btn, Badge } = window.UI;
  const [q, setQ] = useStateP("");
  const users = window.ProjX ? window.ProjX.upLoad() : [];
  const projects = [...users, ...(D.Projects || []).filter(p => !users.some(u => u.code === p.code))];
  const pos = D.PurchaseOrders || [];
  const ql = q.trim().toLowerCase();
  const projList = projects.filter(p => !ql || (p.code + " " + p.name + " " + p.customer).toLowerCase().includes(ql));
  const poList = pos.filter(p => !ql || (p.no + " " + p.vendor).toLowerCase().includes(ql));

  const Row = ({ code, title, sub }) => {
    const linked = current.includes(code);
    return (
      <button onClick={() => !linked && onPick(code)} style={{
        display: "flex", alignItems: "center", gap: 10, width: "100%", textAlign: "left",
        padding: "8px 10px", background: linked ? "var(--success-bg, var(--surface-2))" : "transparent",
        border: 0, borderBottom: "1px solid var(--line-soft)", cursor: linked ? "default" : "pointer", borderRadius: 6
      }}>
        <span className="mono" style={{fontSize: 11.5, color: "var(--ink-3)", flexShrink: 0, width: 110}}>{code}</span>
        <span style={{flex: 1, minWidth: 0, fontSize: 12.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: "var(--ink-1)"}}>{title}<span style={{color: "var(--ink-3)", fontSize: 11.5}}> · {sub}</span></span>
        {linked
          ? <span style={{fontSize: 11, color: "var(--success)", fontWeight: 600, flexShrink: 0}}>✓ ผูกแล้ว</span>
          : <span style={{fontSize: 11, color: "var(--accent)", fontWeight: 600, flexShrink: 0}}>+ ผูก</span>}
      </button>
    );
  };

  return (
    <>
      <div className="scrim" style={{zIndex: 80}} onClick={onClose}></div>
      <div style={{position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", zIndex: 81, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 12, padding: 16, width: 480, maxWidth: "94vw", maxHeight: "78vh", display: "flex", flexDirection: "column", boxShadow: "var(--shadow-lg)"}}>
        <div style={{display: "flex", alignItems: "center", gap: 8, marginBottom: 10}}>
          <div style={{fontSize: 14, fontWeight: 600, flex: 1}}>ผูกกับโปรเจกต์ / ใบสั่งซื้อ</div>
          <button className="icon-btn" onClick={onClose}><I.close size={14}/></button>
        </div>
        <div className="search" style={{width: "100%", marginBottom: 10}}>
          <I.search size={14}/>
          <input placeholder="ค้นหา JOB-... / PO-... / ชื่องาน / ผู้ขาย…" value={q} onChange={e => setQ(e.target.value)} autoFocus/>
        </div>
        <div style={{overflowY: "auto", flex: 1, minHeight: 0}}>
          <div className="label" style={{margin: "4px 0 4px"}}>โปรเจกต์ ({projList.length})</div>
          {projList.map(p => <Row key={p.code} code={p.code} title={p.name} sub={p.customer}/>)}
          {projList.length === 0 && <div style={{padding: 10, fontSize: 12, color: "var(--ink-3)"}}>ไม่พบโปรเจกต์</div>}
          <div className="label" style={{margin: "12px 0 4px"}}>ใบสั่งซื้อ ({poList.length})</div>
          {poList.map(p => <Row key={p.no} code={p.no} title={p.vendor} sub={p.status}/>)}
          {poList.length === 0 && <div style={{padding: 10, fontSize: 12, color: "var(--ink-3)"}}>ไม่พบใบสั่งซื้อ</div>}
        </div>
        <div style={{display: "flex", justifyContent: "flex-end", marginTop: 12}}>
          <Btn kind="primary" onClick={onClose}>เสร็จสิ้น</Btn>
        </div>
      </div>
    </>
  );
}

// ============ Payment vouchers ============
function PaymentVouchers({ navigate }) {
  const D = window.SSSData;
  const { fmt0 } = window.UI;
  const pvs = D.PaymentVouchers || [];
  const pending = pvs.filter(p => p.status !== "จ่ายแล้ว" && p.status !== "ยกเลิกแล้ว");
  const cheques = pvs.filter(p => p.status === "ออกเช็คแล้ว" || /เช็ค/.test(p.method || ""));
  // กำหนดจ่าย: parse วันที่ไทย "22 ก.ค. 2569" → Date เพื่อเตือนใกล้ครบ/เลยกำหนด
  const TH_MON = { "ม.ค.": 0, "ก.พ.": 1, "มี.ค.": 2, "เม.ย.": 3, "พ.ค.": 4, "มิ.ย.": 5, "ก.ค.": 6, "ส.ค.": 7, "ก.ย.": 8, "ต.ค.": 9, "พ.ย.": 10, "ธ.ค.": 11 };
  const parseThaiDate = (s) => { const m = String(s || "").trim().match(/^(\d+)\s+(\S+)\s+(\d+)$/); if (!m || TH_MON[m[2]] === undefined) return null; return new Date((+m[3]) - 543, TH_MON[m[2]], +m[1]); };
  const today0 = new Date(); today0.setHours(0, 0, 0, 0);
  const isPVOverdue = (r) => { const d = parseThaiDate(r.due); return d && d < today0; };
  const dueSoon = pending.filter(p => { const d = parseThaiDate(p.due); if (!d) return false; return (d - today0) / 86400000 <= 7; });
  const [view, setView] = useStateP("list");
  const toggle = (
    <div className="payseg" style={{display: "inline-flex", marginBottom: 12}}>
      <button type="button" className={"payseg-btn" + (view === "list" ? " active" : "")} onClick={() => setView("list")}>📋 รายการ</button>
      <button type="button" className={"payseg-btn" + (view === "calendar" ? " active" : "")} onClick={() => setView("calendar")}>📅 ปฏิทินกำหนดจ่าย</button>
    </div>
  );
  if (view === "calendar") {
    return (
      <div>
        {toggle}
        <PVCalendar pvs={pvs} navigate={navigate} parseThaiDate={parseThaiDate}/>
      </div>
    );
  }
  return (
    <div>
    {toggle}
    <window.DocListPage
      title="ใบสำคัญจ่าย · Payment Vouchers"
      sub="เอกสารยืนยันการจ่ายเงิน (สำหรับการบัญชี)"
      docType="payment-voucher" rows={D.PaymentVouchers}
      statusFilter={["รอจ่าย", "ออกเช็คแล้ว", "จ่ายแล้ว", "ยกเลิกแล้ว"]}
      createLabel="ออกใบสำคัญจ่าย"
      filterField={{ key: "payee", label: "ผู้รับเงิน" }}
      onCreate={() => window.openNewDoc && window.openNewDoc("payment-voucher")}
      onRowClick={(r) => navigate("payment-vouchers/" + r.no)}
      kpis={[
        { label: "ใบสำคัญจ่ายเดือนนี้", value: pvs.length, sub: `รวม ฿${fmt0(pvs.reduce((s,p)=>s+p.amount,0))}`, icon: I.receipt },
        { label: "จ่ายแล้ว", value: pvs.filter(p=>p.status==='จ่ายแล้ว').length, sub: "หักจากบัญชีเรียบร้อย", dir: "up", icon: I.check },
        { label: "รอจ่าย", value: pending.length, sub: `฿${fmt0(pending.reduce((s,p)=>s+(p.amount||0),0))}`, icon: I.clock },
        { label: "ครบกำหนดจ่าย ≤7 วัน", value: dueSoon.length, sub: dueSoon.length ? `฿${fmt0(dueSoon.reduce((s,p)=>s+(p.amount||0),0))} อย่าลืมจ่าย` : "ไม่มีค้าง", icon: I.bell }
      ]}
      columns={[
        { key: "no", label: "เลขที่", render: r => <span className="mono">{r.no}</span> },
        { key: "date", label: "วันที่" },
        { key: "payee", label: "ผู้รับเงิน" },
        { key: "due", label: "กำหนดจ่าย", render: r => { if (!r.due) return <span className="muted" style={{fontSize: 12}}>—</span>; const od = isPVOverdue(r); const dd = (() => { const d = parseThaiDate(r.due); return d ? Math.round((d - today0) / 86400000) : null; })(); const soon = !od && dd != null && dd <= 1 && r.status !== "จ่ายแล้ว"; return <span style={{fontSize: 12.5, color: r.status === "จ่ายแล้ว" ? "var(--ink-3)" : od ? "var(--danger)" : soon ? "var(--warning)" : "var(--ink-1)", fontWeight: (od || soon) ? 700 : 400}}>{r.due}{od && r.status !== "จ่ายแล้ว" ? " ⚠️" : soon ? " ⏰" : ""}</span>; } },
        { key: "method", label: "วิธีจ่าย" },
        { key: "amount", label: "ยอดจ่าย", right: true, render: r => <span className="amount">฿{fmt0(r.amount)}</span> },
        { key: "status", label: "สถานะ", render: r => <window.UI.Badge>{r.status}</window.UI.Badge> }
      ]}
    />
    </div>
  );
}

// ---------- ปฏิทินใบสำคัญจ่ายรายเดือน — ดู/เตือนกำหนดจ่ายง่าย ----------
function PVCalendar({ pvs, navigate, parseThaiDate }) {
  const { fmt0 } = window.UI;
  const THAI_MONTHS = ["มกราคม", "กุมภาพันธ์", "มีนาคม", "เมษายน", "พฤษภาคม", "มิถุนายน", "กรกฎาคม", "สิงหาคม", "กันยายน", "ตุลาคม", "พฤศจิกายน", "ธันวาคม"];
  const DOW = ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส"];
  const now = new Date();
  const [cur, setCur] = useStateP({ y: now.getFullYear(), m: now.getMonth() });
  const today0 = new Date(); today0.setHours(0, 0, 0, 0);
  const isPaid = (p) => p.status === "จ่ายแล้ว";
  const isCancelled = (p) => p.status === "ยกเลิกแล้ว";

  // จัดกลุ่มใบสำคัญจ่ายตามวันครบกำหนด (due) ในเดือนที่เลือก
  const byDay = {};
  pvs.forEach(p => {
    const d = parseThaiDate(p.due);
    if (!d || isCancelled(p)) return;
    if (d.getFullYear() === cur.y && d.getMonth() === cur.m) {
      const day = d.getDate();
      (byDay[day] = byDay[day] || []).push(p);
    }
  });
  const monthList = Object.values(byDay).flat();
  const monthTotal = monthList.reduce((s, p) => s + (p.amount || 0), 0);
  const monthUnpaid = monthList.filter(p => !isPaid(p));
  const unpaidTotal = monthUnpaid.reduce((s, p) => s + (p.amount || 0), 0);

  // เตือน: เลยกำหนด + ที่ยังไม่จ่าย ทั้งหมด (ทุกเดือน)
  const overdueAll = pvs.filter(p => { if (isPaid(p) || isCancelled(p)) return false; const d = parseThaiDate(p.due); return d && d < today0; })
    .sort((a, b) => parseThaiDate(a.due) - parseThaiDate(b.due));

  const first = new Date(cur.y, cur.m, 1);
  const startDow = first.getDay();
  const daysInMonth = new Date(cur.y, cur.m + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < startDow; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);
  while (cells.length % 7 !== 0) cells.push(null);

  const shift = (delta) => setCur(c => { let m = c.m + delta, y = c.y; if (m < 0) { m = 11; y--; } if (m > 11) { m = 0; y++; } return { y, m }; });
  const goToday = () => setCur({ y: now.getFullYear(), m: now.getMonth() });
  const isToday = (d) => d && cur.y === today0.getFullYear() && cur.m === today0.getMonth() && d === today0.getDate();

  const dayColor = (list) => {
    const unpaid = list.filter(p => !isPaid(p));
    if (unpaid.length === 0) return { bg: "#f0fdf4", bd: "#86efac", ink: "#15803d" }; // จ่ายครบ
    const od = unpaid.some(p => { const dt = parseThaiDate(p.due); return dt && dt < today0; });
    if (od) return { bg: "#fef2f2", bd: "#fca5a5", ink: "#b91c1c" }; // เลยกำหนด
    return { bg: "#fff7ed", bd: "#fdba74", ink: "#c2410c" }; // รอจ่าย
  };

  return (
    <div className="card" style={{padding: 16}}>
      {/* ส่วนหัว: เดือน + เลื่อน */}
      <div style={{display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, flexWrap: "wrap", marginBottom: 12}}>
        <div style={{display: "flex", alignItems: "center", gap: 8}}>
          <button className="icon-btn" onClick={() => shift(-1)} title="เดือนก่อนหน้า"><I.chevronLeft size={16}/></button>
          <div style={{fontWeight: 800, fontSize: 17, minWidth: 168, textAlign: "center"}}>{THAI_MONTHS[cur.m]} {cur.y + 543}</div>
          <button className="icon-btn" onClick={() => shift(1)} title="เดือนถัดไป"><I.chevronRight size={16}/></button>
          <button className="btn sm ghost" onClick={goToday} style={{marginLeft: 4}}>วันนี้</button>
        </div>
        <div style={{display: "flex", gap: 14, fontSize: 12.5, flexWrap: "wrap"}}>
          <span>ครบกำหนดเดือนนี้ <b>{monthList.length}</b> ใบ · <b className="amount">฿{fmt0(monthTotal)}</b></span>
          {unpaidTotal > 0 && <span style={{color: "var(--danger)"}}>ยังไม่จ่าย <b>฿{fmt0(unpaidTotal)}</b></span>}
        </div>
      </div>

      {/* เตือนเลยกำหนด */}
      {overdueAll.length > 0 && (
        <div style={{background: "#fef2f2", border: "1px solid #fca5a5", borderRadius: 10, padding: "10px 14px", marginBottom: 12, fontSize: 12.5}}>
          <div style={{fontWeight: 800, color: "#b91c1c", marginBottom: 6}}>⚠️ เลยกำหนดจ่าย {overdueAll.length} ใบ — รวม ฿{fmt0(overdueAll.reduce((s, p) => s + (p.amount || 0), 0))}</div>
          <div style={{display: "flex", flexWrap: "wrap", gap: 6}}>
            {overdueAll.slice(0, 8).map(p => (
              <button key={p.no} className="chip" onClick={() => navigate("payment-vouchers/" + p.no)}
                style={{cursor: "pointer", background: "#fff", border: "1px solid #fca5a5", color: "#b91c1c", borderRadius: 999, padding: "3px 10px", fontSize: 12}}>
                {p.payee || p.no} · ฿{fmt0(p.amount)} · {p.due}
              </button>
            ))}
            {overdueAll.length > 8 && <span style={{color: "#b91c1c", alignSelf: "center"}}>+{overdueAll.length - 8} ใบ</span>}
          </div>
        </div>
      )}

      {/* หัวคอลัมน์วัน */}
      <div style={{display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 5, marginBottom: 5}}>
        {DOW.map((d, i) => <div key={i} style={{textAlign: "center", fontSize: 11.5, fontWeight: 700, color: i === 0 ? "var(--danger)" : "var(--ink-3)", padding: "2px 0"}}>{d}</div>)}
      </div>

      {/* ตารางวัน */}
      <div style={{display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 5}}>
        {cells.map((d, i) => {
          if (!d) return <div key={i} style={{minHeight: 78}}/>;
          const list = byDay[d] || [];
          const has = list.length > 0;
          const col = has ? dayColor(list) : null;
          const dayTotal = list.reduce((s, p) => s + (p.amount || 0), 0);
          return (
            <div key={i} style={{minHeight: 78, border: "1px solid " + (has ? col.bd : "var(--line)"), background: has ? col.bg : (isToday(d) ? "var(--surface-2)" : "var(--surface)"), borderRadius: 9, padding: 6, display: "flex", flexDirection: "column", gap: 3, outline: isToday(d) ? "2px solid var(--accent)" : "none"}}>
              <div style={{display: "flex", justifyContent: "space-between", alignItems: "center"}}>
                <span style={{fontSize: 12.5, fontWeight: isToday(d) ? 800 : 600, color: isToday(d) ? "var(--accent)" : "var(--ink-2)"}}>{d}</span>
                {has && <span style={{fontSize: 10, fontWeight: 700, color: col.ink}}>{list.length} ใบ</span>}
              </div>
              {has && <div style={{fontSize: 10.5, fontWeight: 800, color: col.ink}}>฿{fmt0(dayTotal)}</div>}
              <div style={{display: "flex", flexDirection: "column", gap: 2, overflow: "hidden"}}>
                {list.slice(0, 2).map(p => {
                  const paid = isPaid(p);
                  return (
                    <button key={p.no} onClick={() => navigate("payment-vouchers/" + p.no)} title={`${p.payee || p.no} · ฿${fmt0(p.amount)} · ${p.status}`}
                      style={{textAlign: "left", border: 0, borderRadius: 4, padding: "1px 5px", fontSize: 10, cursor: "pointer", background: paid ? "#dcfce7" : "rgba(255,255,255,0.7)", color: paid ? "#15803d" : col.ink, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis"}}>
                      {paid ? "✓ " : "• "}{p.payee || p.no}
                    </button>
                  );
                })}
                {list.length > 2 && <span style={{fontSize: 9.5, color: col.ink}}>+{list.length - 2} ใบ</span>}
              </div>
            </div>
          );
        })}
      </div>

      {/* คำอธิบายสี */}
      <div style={{display: "flex", gap: 14, flexWrap: "wrap", marginTop: 12, fontSize: 11.5, color: "var(--ink-3)"}}>
        <span><span style={{display: "inline-block", width: 11, height: 11, borderRadius: 3, background: "#fdba74", verticalAlign: -1, marginRight: 4}}/>รอจ่าย</span>
        <span><span style={{display: "inline-block", width: 11, height: 11, borderRadius: 3, background: "#fca5a5", verticalAlign: -1, marginRight: 4}}/>เลยกำหนด</span>
        <span><span style={{display: "inline-block", width: 11, height: 11, borderRadius: 3, background: "#86efac", verticalAlign: -1, marginRight: 4}}/>จ่ายครบแล้ว</span>
        <span style={{marginLeft: "auto"}}>แตะที่ใบเพื่อดู/จัดการ</span>
      </div>
    </div>
  );
}

window.PurchaseOrders = PurchaseOrders;
window.Expenses = Expenses;
window.PaymentVouchers = PaymentVouchers;
