<?php
require_once '../../core/config.php';
require_once '../../core/session.php';
require_once '../../core/database.php';
require_once '../../core/auth.php';
require_once '../../core/permissions.php';
require_once '../../core/format.php';

require_login();

if (!user_can('billing.inbox') && !user_can('billing.payables') && !user_can('module.billing') && current_user_role() !== 'admin') {
    http_response_code(403);
    die('No tienes permisos para acceder a Cuentas por pagar.');
}

$page_title = 'Cuentas por pagar';
$billingEnvironment = defined('FACTO_DEFAULT_ENVIRONMENT') ? FACTO_DEFAULT_ENVIRONMENT : 'production';
$billingEnvironment = $billingEnvironment === 'sandbox' ? 'sandbox' : 'production';

require_once '../../layouts/header.php';
require_once '../../layouts/sidebar.php';
?>

<main class="app-main billing-integrated-main crm-ui-main" id="billingPayablesMain" data-spa-module="collections:payables">
    <?php require_once '../../layouts/topbar.php'; ?>
    <section id="payablesMain" class="content payables-main billing-page-shell billing-phase-content billing-payables-content billing-crm-content crm-ui-page-content crm-ui-page">
            <div class="payables-header page-header header-responsive billing-page-header crm-ui-page-header">
                <div class="page-header-content crm-ui-page-header-copy"><span class="label">FINANZAS</span><h2>Cuentas por pagar</h2><p class="text-muted">Facturas afectas y exentas que generan cuentas por pagar, con pago individual, parcial o masivo.</p></div>
                <div class="payables-header-actions cluster top-actions-cluster billing-page-header-actions crm-ui-page-actions">
                    <input type="hidden" id="payablesEnv" value="<?php echo htmlspecialchars($billingEnvironment, ENT_QUOTES, 'UTF-8'); ?>">
                    <details class="billing-actions-menu" data-billing-actions-scope="payables" data-close-outside><summary class="btn btn-outline"><i data-lucide="ellipsis-vertical"></i><span>Acciones</span></summary><div class="billing-actions-menu__panel"><button type="button" data-billing-bulk-action="xlsx"><i data-lucide="sheet"></i>Descargar XLSX</button><button type="button" data-billing-bulk-action="refresh"><i data-lucide="refresh-cw"></i>Recalcular estados</button></div></details>
                    <button type="button" class="btn btn-primary btn-icon-mobile" id="reloadPayablesBtn" title="Actualizar cuentas por pagar"><i data-lucide="refresh-cw"></i><span class="mobile-hidden">Actualizar</span></button>
                </div>
            </div>

            <section class="filter-bar-container payables-toolbar billing-toolbar billing-filter-bar billing-filter-bar--dates crm-ui-filter">
                <input type="search" id="payablesSearch" class="payables-input input" placeholder="Buscar proveedor, RUT o folio">
                <select id="payablesStatus" class="payables-input input"><option value="open" selected>Impagos / parciales</option><option value="all">Todos los documentos</option><option value="pending_cxp">Aprobadas sin CxP</option><option value="pending_approval">Pendientes de aprobación</option><option value="por_pagar">Por pagar</option><option value="vencido">Vencido</option><option value="parcial">Parcial</option><option value="pagado">Pagado</option><option value="rechazado">Rechazado</option><option value="anulado">Anulado</option></select>
                <div class="billing-filter-date-group crm-ui-date-group">
                    <label class="billing-date-filter-field crm-ui-date-field"><span>Desde</span><input type="date" id="payablesFrom" class="payables-input input" aria-label="Desde"><em>dd/mm/aaaa</em></label>
                    <label class="billing-date-filter-field crm-ui-date-field"><span>Hasta</span><input type="date" id="payablesTo" class="payables-input input" aria-label="Hasta"><em>dd/mm/aaaa</em></label>
                </div>
            </section>

            <section id="payablesIndicators" class="billing-table-indicators" aria-live="polite"></section>

            <section class="card payables-card billing-data-card crm-ui-table-card">
                <div class="payables-bulkbar billing-bulkbar crm-ui-bulkbar">
                    <div class="billing-selection-summary crm-ui-bulkbar-summary"><strong id="payablesSelectedCount">0 seleccionados</strong><span id="payablesSelectedTotal" class="payables-muted">Saldo seleccionado: $0</span></div>
                    <div class="payables-actions billing-selection-actions crm-ui-bulkbar-actions">
                        <button type="button" class="btn btn-primary" id="bulkPaymentBtn" disabled><i data-lucide="banknote-arrow-up"></i><span>Registrar pago</span></button>
                        <button type="button" class="btn btn-outline" id="clearSelectionBtn" disabled><i data-lucide="x"></i><span>Limpiar selección</span></button>
                    </div>
                </div>
            </section>
            <div id="payablesGroups" class="billing-document-groups" aria-live="polite"></div>
    </section>

<div id="payableModal" class="product-modal-backdrop crm-ui-modal-backdrop" data-crm-dismissable="true" style="display:none;" aria-hidden="true" role="dialog" aria-modal="true">
    <div class="product-modal-box payables-modal-box billing-modal-box crm-ui-modal-box">
        <div class="product-modal-header billing-modal-header billing-modal-header--no-back crm-ui-modal-header crm-ui-modal-header--no-back">
            <div class="billing-modal-heading crm-ui-modal-heading">
                <h2 id="payableModalTitle">Documento recibido</h2>
                <p id="payableModalSubtitle" class="payables-muted"></p>
            </div>
            <button type="button" class="btn btn-outline action-square-btn billing-modal-close crm-ui-modal-icon-btn" id="closePayableModalBtn" aria-label="Cerrar"><i data-lucide="x"></i></button>
        </div>
        <div class="product-modal-body crm-ui-modal-body" id="payableModalBody"></div>
        <div class="product-modal-footer billing-modal-footer crm-ui-modal-footer" id="payableModalFooter"></div>
    </div>
</div>

<div id="payableBulkModal" class="product-modal-backdrop crm-ui-modal-backdrop" data-crm-dismissable="true" style="display:none;" aria-hidden="true" role="dialog" aria-modal="true">
    <div class="product-modal-box payables-modal-box payables-bulk-modal-box billing-modal-box crm-ui-modal-box">
        <div class="product-modal-header billing-modal-header billing-modal-header--no-back crm-ui-modal-header crm-ui-modal-header--no-back">
            <div class="billing-modal-heading crm-ui-modal-heading">
                <h2>Pago masivo proveedores</h2>
                <p id="payableBulkSubtitle" class="payables-muted"></p>
            </div>
            <button type="button" class="btn btn-outline action-square-btn billing-modal-close crm-ui-modal-icon-btn" id="closeBulkPaymentBtn" aria-label="Cerrar"><i data-lucide="x"></i></button>
        </div>
        <div class="product-modal-body crm-ui-modal-body" id="payableBulkBody"></div>
        <div class="product-modal-footer billing-modal-footer crm-ui-modal-footer" id="payableBulkFooter"></div>
    </div>
</div>
<?php
$sortJsPath = dirname(__DIR__, 2) . '/assets/js/icy-table-sort.js';
$sortJsVersion = file_exists($sortJsPath) ? filemtime($sortJsPath) : time();
$referenceTableJsPath = dirname(__DIR__, 2) . '/assets/js/billing-table-reference.js';
$referenceTableJsVersion = file_exists($referenceTableJsPath) ? filemtime($referenceTableJsPath) : time();
?>
<script src="/assets/js/icy-table-sort.js?v=<?php echo (int)$sortJsVersion; ?>"></script>
<script src="/assets/js/billing-table-reference.js?v=<?php echo (int)$referenceTableJsVersion; ?>"></script>
<script>
(function(){
    const __pageBindings = [];
    const on = (target, eventName, handler, options) => {
        if (!target || typeof target.addEventListener !== 'function') return;
        target.addEventListener(eventName, handler, options || false);
        __pageBindings.push([target, eventName, handler, options || false]);
    };
    window.ICY_PAGE_CLEANUPS = window.ICY_PAGE_CLEANUPS || [];
    window.ICY_PAGE_CLEANUPS.push(() => {
        __pageBindings.forEach(([target, eventName, handler, options]) => {
            try { target.removeEventListener(eventName, handler, options); } catch (_) {}
        });
    });
    const state = { env: 'production', rows: [], options: null, current: null, currentPayments: [], selected: new Set(), paymentJustSaved: false, bulkSelection: [], bulkRequestKey: null, paging: window.IcyBillingTable.createPaging('10') };
    const $ = (id) => document.getElementById(id);
    const today = () => new Date().toISOString().slice(0,10);
    const refreshIcons = () => { if (window.lucide && typeof window.lucide.createIcons === 'function') window.lucide.createIcons(); };
    const paymentRequestKey = (prefix) => {
        const random = (window.crypto && typeof window.crypto.randomUUID === 'function')
            ? window.crypto.randomUUID()
            : (Date.now().toString(36) + '-' + Math.random().toString(36).slice(2) + '-' + Math.random().toString(36).slice(2));
        return String(prefix || 'payment') + ':' + random;
    };
    const fmtMoney = (v) => '$' + Math.round(Number(v || 0)).toLocaleString('es-CL');
    const esc = (s) => String(s == null ? '' : s).replace(/[&<>'"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[c]));
    const statusLabel = (s) => ({por_pagar:'Por pagar',vencido:'Vencido',parcial:'Parcial',pagado:'Pagado',rechazado:'Rechazado',anulado:'Anulado',pendiente_cxp:'Aprobada · CxP pendiente',pendiente_aprobacion:'Pendiente de aprobación',no_aplica:'No genera CxP'}[s] || s || 'Por pagar');
    const balanceOf = (r) => Number(r.balance_amount != null ? r.balance_amount : r.total_amount || 0);
    const payableBreakdown = (row) => {
        const rawNet = Math.max(0, Number(row.net_amount || 0));
        const total = Math.max(0, Number(row.total_amount || 0));
        const exemptDocument = Number(row.is_tax_exempt || 0) === 1
            || String(row.account_document_group || '') === 'invoice_exempt'
            || Number(row.document_type_id || 0) === 33;
        const reportedIva = Math.max(0, Number(row.taxes_amount || 0));
        const exempt = exemptDocument ? (rawNet > 0 ? rawNet : total) : 0;
        const net = exemptDocument ? 0 : rawNet;
        // FACTO Inbox entrega taxes_amount=0 en parte de las facturas afectas.
        // La diferencia total-neto corresponde al IVA verificable del DTE.
        const iva = exemptDocument ? 0 : (reportedIva > 0 ? reportedIva : Math.max(0, total - net));
        const other = Math.max(0, total - net - exempt - iva);
        const financial = Number(row.is_financial_document || 0) === 1;
        const hasCollection = Number(row.collection_id || 0) > 0;
        const paid = financial && hasCollection ? Math.min(total, Math.max(0, Number(row.paid_amount || 0))) : 0;
        const unpaid = financial ? Math.min(total, Math.max(0, hasCollection ? balanceOf(row) : total)) : 0;
        return {net, exempt, iva, other, total, paid, unpaid};
    };

    async function jsonFetch(url, options){
        const res = await fetch(url, options || {});
        const text = await res.text();
        let data;
        try { data = JSON.parse(text); } catch(e) { throw new Error(text || 'Respuesta no valida.'); }
        if (!res.ok || data.success === false) throw new Error(data.message || 'Error de solicitud.');
        return data;
    }

    function formData(obj){ const fd = new FormData(); Object.keys(obj).forEach(k => fd.append(k, obj[k] == null ? '' : obj[k])); return fd; }

    const documentGroups = [
        {key:'invoice_affected',title:'Facturas afectas',description:'Facturas electrónicas recibidas que generan cuenta por pagar.'},
        {key:'invoice_exempt',title:'Facturas exentas',description:'Facturas no afectas o exentas recibidas que generan cuenta por pagar.'}
    ];
    function groupKey(row){
        return Number(row.is_tax_exempt || 0) === 1
            || String(row.account_document_group || '') === 'invoice_exempt'
            || Number(row.document_type_id || 0) === 33
            ? 'invoice_exempt'
            : 'invoice_affected';
    }
    function sortValue(row,column){
        const amounts=['net','exempt','iva','other','total','paid','unpaid'].includes(column)?payableBreakdown(row):null;
        if(amounts)return amounts[column];
        if(column==='document_date')return String(row.document_date||'').slice(0,10);
        if(column==='folio')return row.document_number||'';
        if(column==='supplier')return row.supplier_name||row.issuer_name||'';
        if(column==='tax_id')return row.supplier_tax_id||row.issuer_tax_id_code||'';
        if(column==='due_date')return String(row.due_date||row.payment_due_date||'').slice(0,10);
        if(column==='status')return statusLabel(row.collection_status||row.payment_status||'por_pagar');
        return row[column]==null?'':row[column];
    }
    function sortHead(label,type,column,group,className=''){
        const sort=window.IcyBillingTable.sortState(state.paging,group.key);
        const ariaSort=sort&&sort.column===column?sort.direction:'none';
        return `<th class="${esc(className)}" aria-sort="${esc(ariaSort)}"><button type="button" class="icy-table-sort-button" data-sort-type="${esc(type)}" data-billing-sort-key="${esc(column)}"><span>${esc(label)}</span><span class="icy-table-sort-indicator" aria-hidden="true"></span></button></th>`;
    }
    function payableActions(row,canPay){
        const id=esc(row.id);
        return `<details class="billing-row-actions" data-billing-row-actions data-close-outside>
            <summary aria-label="Acciones de la cuenta por pagar ${esc(row.document_number||'')}"><i data-lucide="file-text"></i><i data-lucide="chevron-down"></i></summary>
            <div class="billing-row-actions__panel">
                <button type="button" data-payable-detail="${id}"><i data-lucide="eye"></i>Ver detalle</button>
                <a target="_blank" rel="noopener" href="payable_file.php?id=${id}&env=${esc(state.env)}&type=pdf"><i data-lucide="file-down"></i>PDF</a>
                <a target="_blank" rel="noopener" href="payable_file.php?id=${id}&env=${esc(state.env)}&type=xml"><i data-lucide="code-xml"></i>XML</a>
                ${canPay?`<button type="button" data-payable-pay="${id}"><i data-lucide="banknote-arrow-up"></i>Registrar pago</button>`:''}
            </div>
        </details>`;
    }
    function payableTotals(rows){
        return rows.reduce((totals,row)=>{const values=payableBreakdown(row);Object.keys(totals).forEach(key=>{totals[key]+=Number(values[key]||0);});return totals;},{net:0,exempt:0,iva:0,other:0,total:0,paid:0,unpaid:0});
    }
    function payableRow(r){
        const status=r.collection_status||r.payment_status||'por_pagar',balance=balanceOf(r),financial=Number(r.is_financial_document||0)===1,hasCollection=Number(r.collection_id||0)>0,approved=Number(r.is_approved_for_payable||0)===1;
        const canPay=financial&&hasCollection&&balance>0.5&&!['pagado','rechazado','anulado'].includes(status),checked=state.selected.has(String(r.id))?'checked':'';
        const statusText=financial?statusLabel(status):'No genera CxP';
        const disabledReason=!financial?'Documento informativo':(!approved&&!hasCollection?'Debe aprobarse antes de generar CxP':(!hasCollection?'CxP pendiente de materialización':(balance<=0.5?'Sin saldo pendiente':'No seleccionable')));
        const supplierName=r.supplier_name||r.issuer_name||'Sin proveedor';
        const documentName=r.document_label||r.document_type_taxbureau||r.document_type_id||'-';
        const dueDate=financial?(r.due_date||r.payment_due_date||''):'';
        const centerName=financial?(r.finance_center_name||'Sin centro'):'';
        const amounts=payableBreakdown(r);
        return `<tr><td class="col-select"><input type="checkbox" class="payable-select" data-payable-id="${esc(r.id)}" ${checked} ${canPay?'':'disabled'} title="${esc(canPay?'Seleccionar para pago':disabledReason)}" aria-label="${esc(canPay?'Seleccionar documento para pago':disabledReason)}"></td>
            <td class="col-date crm-mobile-hidden" data-sort-value="${esc(String(r.document_date||'').slice(0,10))}">${esc(r.document_date||'-')}</td>
            <td class="col-document crm-mobile-primary" data-sort-value="${esc(r.document_number||'')}"><div class="crm-ui-cell-stack"><strong>${esc(r.document_number||'-')}</strong><small>${esc(documentName)}</small></div></td>
            <td class="col-counterparty crm-mobile-primary" data-sort-value="${esc(supplierName)}"><div class="crm-ui-cell-stack"><strong>${esc(supplierName)}</strong><small>${esc(r.issuer_tax_id_code||'Sin RUT')} · Folio ${esc(r.document_number||'-')}</small></div></td>
            <td class="crm-mobile-hidden" data-sort-value="${esc(r.supplier_tax_id||r.issuer_tax_id_code||'')}">${esc(r.supplier_tax_id||r.issuer_tax_id_code||'-')}</td>
            <td class="payables-number crm-mobile-hidden" data-sort-value="${amounts.net}">${fmtMoney(amounts.net)}</td>
            <td class="payables-number crm-mobile-hidden" data-sort-value="${amounts.exempt}">${fmtMoney(amounts.exempt)}</td>
            <td class="payables-number crm-mobile-hidden" data-sort-value="${amounts.iva}">${fmtMoney(amounts.iva)}</td>
            <td class="payables-number crm-mobile-hidden" data-sort-value="${amounts.other}">${fmtMoney(amounts.other)}</td>
            <td class="col-total payables-number crm-mobile-hidden" data-sort-value="${amounts.total}"><strong>${fmtMoney(amounts.total)}</strong></td>
            <td class="payables-number crm-mobile-hidden" data-sort-value="${amounts.paid}">${fmtMoney(amounts.paid)}</td>
            <td class="col-balance payables-number crm-mobile-metric" data-sort-value="${amounts.unpaid}"><strong>${financial?fmtMoney(amounts.unpaid):'—'}</strong></td>
            <td class="col-due crm-mobile-hidden" data-sort-value="${esc(String(dueDate).slice(0,10))}">${financial?esc(dueDate||'-'):'—'}</td>
            <td class="col-status crm-mobile-hidden" data-sort-value="${esc(statusText)}"><span class="payables-status ${financial?esc(status):'no_aplica'}">${esc(statusText)}</span></td>
            <td class="col-action crm-ui-action-cell">${payableActions(r,canPay)}</td></tr>`;
    }
    function groupTable(rows,group){
        const orderedRows=window.IcyBillingTable.sortedRows(rows,state.paging,group.key,sortValue);
        const page=window.IcyBillingTable.pageFor(orderedRows,state.paging,group.key);
        const totals=payableTotals(orderedRows);
        const body=page.rows.length?page.rows.map(payableRow).join(''):'<tr class="empty-state-row"><td colspan="15" class="payables-muted">Sin documentos en esta categoría.</td></tr>';
        const headers=[
            `<th class="col-select"><input type="checkbox" class="payables-select-all-group" data-group="${esc(group.key)}"></th>`,
            sortHead('Emisión','date','document_date',group,'col-date crm-mobile-hidden'),
            sortHead('Folio','text','folio',group,'col-document crm-mobile-primary'),
            sortHead('Proveedor','text','supplier',group,'col-counterparty crm-mobile-primary'),
            sortHead('RUT','text','tax_id',group,'crm-mobile-hidden'),
            sortHead('Neto','number','net',group,'crm-mobile-hidden'),
            sortHead('Exento','number','exempt',group,'crm-mobile-hidden'),
            sortHead('IVA','number','iva',group,'crm-mobile-hidden'),
            sortHead('Otros','number','other',group,'crm-mobile-hidden'),
            sortHead('Total','number','total',group,'col-total crm-mobile-hidden'),
            sortHead('Pagado','number','paid',group,'crm-mobile-hidden'),
            sortHead('Impago','number','unpaid',group,'col-balance crm-mobile-metric'),
            sortHead('Vence','date','due_date',group,'col-due crm-mobile-hidden'),
            sortHead('Estado','text','status',group,'col-status crm-mobile-hidden'),
            '<th class="col-action crm-ui-action-cell">Acciones</th>'
        ].join('');
        const footer=`<tfoot><tr><th colspan="5">Total ${orderedRows.length.toLocaleString('es-CL')} documentos</th><td>${fmtMoney(totals.net)}</td><td>${fmtMoney(totals.exempt)}</td><td>${fmtMoney(totals.iva)}</td><td>${fmtMoney(totals.other)}</td><td>${fmtMoney(totals.total)}</td><td>${fmtMoney(totals.paid)}</td><td>${fmtMoney(totals.unpaid)}</td><td colspan="3"></td></tr></tfoot>`;
        return `<section class="card billing-document-group crm-ui-table-card" data-document-group="${esc(group.key)}"><header class="billing-document-group-header"><div><h3>${esc(group.title)}</h3><p>${esc(group.description)}</p></div><span class="billing-document-group-count">${orderedRows.length}</span></header>${window.IcyBillingTable.controls(page,group.title)}<div class="billing-document-group-scroll crm-ui-table-scroll billing-reference-table-scroll"><table class="table payables-table billing-responsive-table billing-crm-table crm-ui-table crm-ui-mobile-table billing-reference-table" data-billing-group="${esc(group.key)}"><thead><tr>${headers}</tr></thead><tbody>${body}</tbody>${footer}</table></div></section>`;
    }
    async function loadPayables(){
        const env=$('payablesEnv').value||'production'; state.env=env; state.selected.clear(); updateSelectionUi(); $('payablesGroups').innerHTML='<section class="card billing-document-group"><div class="billing-document-loading">Cargando documentos...</div></section>';
        const q=new URLSearchParams({env:env,status:$('payablesStatus').value,search:$('payablesSearch').value,date_from:$('payablesFrom').value,date_to:$('payablesTo').value,limit:'1000'});
        try{const data=await jsonFetch('payables_list_ajax.php?'+q.toString());state.rows=data.data.rows||[];state.options=data.data.options||null;window.IcyBillingTable.resetPaging(state.paging);renderIndicators();renderRows();}catch(e){$('payablesGroups').innerHTML='<section class="card billing-document-group"><div class="billing-document-loading">'+esc(e.message)+'</div></section>';}
    }
    function renderIndicators(){
        const overdue=state.rows.filter(r=>(r.collection_status||r.payment_status)==='vencido').reduce((sum,r)=>sum+payableBreakdown(r).unpaid,0);
        const unlinked=state.rows.filter(r=>!Number(r.supplier_customer_id||0)).length;
        const withoutCenter=state.rows.filter(r=>Number(r.is_financial_document||0)===1&&!Number(r.effective_finance_center_id||0)).length;
        $('payablesIndicators').innerHTML=`<span class="billing-table-indicator ${overdue>0?'is-danger':'is-neutral'}"><i data-lucide="triangle-alert"></i>Vencido <strong>${fmtMoney(overdue)}</strong></span><span class="billing-table-indicator ${unlinked>0?'is-warning':'is-neutral'}"><i data-lucide="user-round-x"></i>Sin proveedor <strong>${unlinked.toLocaleString('es-CL')}</strong></span><span class="billing-table-indicator ${withoutCenter>0?'is-warning':'is-neutral'}"><i data-lucide="landmark"></i>Sin centro <strong>${withoutCenter.toLocaleString('es-CL')}</strong></span>`;
        refreshIcons();
    }
    function renderRows(){ $('payablesGroups').innerHTML=documentGroups.map(group=>groupTable(state.rows.filter(row=>groupKey(row)===group.key),group)).join(''); updateSelectionUi(); refreshIcons(); }

    async function openDetail(id){
        const q = new URLSearchParams({id: id, env: state.env});
        const data = await jsonFetch('payable_detail_ajax.php?' + q.toString());
        state.current = data.document;
        state.currentPayments = data.payments || [];
        state.options = data.options || state.options;
        state.paymentJustSaved = false;
        renderDetail(data.document, state.currentPayments);
        $('payableModal').style.display = 'flex';
        $('payableModal').classList.add('show');
        $('payableModal').setAttribute('aria-hidden', 'false');
        document.body.classList.add('is-modal-open');
    }

    function optionHtml(items, idKey, labelKey, selected){
        return (items || []).map(i => `<option value="${esc(i[idKey])}" ${String(i[idKey]) === String(selected || '') ? 'selected' : ''}>${esc(i[labelKey])}</option>`).join('');
    }

    function renderPayments(payments){
        if (!payments || !payments.length) {
            return '<p class="payables-muted">No hay pagos registrados para este documento.</p>';
        }
        return `<div class="payables-history-wrap crm-ui-inner-table-wrap"><table class="payables-payment-history crm-ui-inner-table">
            <thead><tr><th>Fecha</th><th>Monto</th><th>Metodo</th><th>Caja/cuenta</th><th>Referencia</th><th>Nota</th></tr></thead>
            <tbody>${payments.map(p => `<tr>
                <td>${esc(p.payment_date || '-')}</td>
                <td><strong>${fmtMoney(p.amount)}</strong></td>
                <td>${esc(p.method_name || p.payment_method || '-')}</td>
                <td>${esc(p.account_name || '-')}</td>
                <td>${esc(p.external_reference || p.batch_reference || '-')}</td>
                <td>${esc(p.notes || '-')}</td>
            </tr>`).join('')}</tbody>
        </table></div>`;
    }

    function renderDetail(d, payments){
        const status = d.collection_status || d.payment_status || 'por_pagar';
        const balance = balanceOf(d);
        const isFinancialDocument = Number(d.is_financial_document || 0) === 1;
        if (!isFinancialDocument) {
            $('payableModalTitle').textContent = (d.document_label || 'Documento recibido') + ' ' + (d.document_number || '-');
            $('payableModalSubtitle').textContent = (d.issuer_name || 'Proveedor sin nombre') + ' · ' + (d.issuer_tax_id_code || 'Sin RUT');
            $('payableModalBody').innerHTML = `<div class="billing-alert billing-alert-info"><strong>Documento informativo.</strong><br>Las notas, guías y otros DTE no generan una cuenta por pagar independiente.</div><div class="payables-grid crm-ui-form-grid"><section class="payables-section crm-ui-section"><h3>Documento</h3><p><strong>Tipo:</strong> ${esc(d.document_label || d.document_type_taxbureau || d.document_type_id || '-')}</p><p><strong>Folio:</strong> ${esc(d.document_number || '-')}</p><p><strong>Fecha:</strong> ${esc(d.document_date || '-')}</p><p><strong>Total:</strong> ${fmtMoney(d.total_amount)}</p></section><section class="payables-section crm-ui-section"><h3>Proveedor</h3><p><strong>Razón social:</strong> ${esc(d.supplier_name || d.issuer_name || '-')}</p><p><strong>RUT:</strong> ${esc(d.issuer_tax_id_code || '-')}</p><p><strong>Estado recepción:</strong> ${esc(d.icylab_reception_status || d.inbox_document_status || '-')}</p></section></div>`;
            $('payableModalFooter').innerHTML = '<div class="modal-footer-actions-right"><button type="button" class="btn btn-outline" id="closePayableFooterBtn"><i data-lucide="x"></i><span>Cerrar</span></button></div>'; refreshIcons(); return;
        }
        const paid = Number(d.paid_amount || 0);
        const hasCollection = Number(d.collection_id || 0) > 0;
        const approvedForPayable = Number(d.is_approved_for_payable || 0) === 1;
        const resolution = d.supplier_resolution_status || (d.supplier_customer_id ? 'matched_existing' : 'pending');
        const resolutionLabel = {matched_existing:'Proveedor vinculado',created_from_dte:'Proveedor creado desde DTE',missing_rut:'Sin RUT usable',pending:'Pendiente',error:'Error'}[resolution] || resolution;
        const paymentTermLabel = d.supplier_payment_term_label || 'Contado';
        const paymentTermDays = d.supplier_payment_term_days != null ? Number(d.supplier_payment_term_days) : 0;
        $('payableModalTitle').textContent = 'DTE recibido ' + (d.document_number || '');
        $('payableModalSubtitle').textContent = (d.issuer_name || 'Proveedor sin nombre') + ' · ' + (d.issuer_tax_id_code || 'Sin RUT') + ' · ' + state.env;
        const centers = state.options ? state.options.finance_centers : [];
        const methods = state.options ? state.options.payment_methods : [];
        const accounts = state.options ? state.options.cash_accounts : [];
        $('payableModalBody').innerHTML = `
            <div class="payables-summary crm-ui-summary-grid">
                <div class="payables-summary-card crm-ui-summary-card"><span>Total documento</span><strong>${fmtMoney(d.total_amount)}</strong></div>
                <div class="payables-summary-card crm-ui-summary-card"><span>Total pagado</span><strong>${fmtMoney(paid)}</strong></div>
                <div class="payables-summary-card crm-ui-summary-card"><span>Saldo pendiente</span><strong>${fmtMoney(balance)}</strong></div>
            </div>
            <div class="payables-grid crm-ui-form-grid">
                <div class="payables-section crm-ui-section">
                    <h3>Documento</h3>
                    <p><strong>Proveedor:</strong> ${esc(d.supplier_name || d.issuer_name || 'Sin homologar')}</p>
                    <p><strong>RUT:</strong> ${esc(d.issuer_tax_id_code || '-')}</p>
                    <p><strong>Tipo:</strong> ${esc(d.document_type_taxbureau || d.document_type_id || '-')}</p>
                    <p><strong>Folio:</strong> ${esc(d.document_number || '-')}</p>
                    <p><strong>Fecha:</strong> ${esc(d.document_date || '-')}</p>
                    <p><strong>Neto:</strong> ${fmtMoney(d.net_amount)} · <strong>IVA:</strong> ${fmtMoney(d.taxes_amount)} · <strong>Total:</strong> ${fmtMoney(d.total_amount)}</p>
                    <p><strong>Estado:</strong> <span class="payables-status ${esc(status)}">${esc(statusLabel(status))}</span></p>
                    <p><strong>Proveedor:</strong> <span class="payables-resolution ${esc(resolution)}">${esc(resolutionLabel)}</span></p>
                    <p class="payables-muted">${esc(d.supplier_resolution_notes || '')}</p>
                    <div class="payables-actions">
                        <a class="btn btn-outline" target="_blank" rel="noopener" href="payable_file.php?id=${esc(d.id)}&env=${esc(state.env)}&type=pdf">Ver PDF</a>
                        <a class="btn btn-outline" target="_blank" rel="noopener" href="payable_file.php?id=${esc(d.id)}&env=${esc(state.env)}&type=xml">Ver XML</a>
                        ${d.supplier_customer_id ? '' : '<button type="button" class="btn btn-ghost" id="linkSupplierBtn">Resolver / crear proveedor por RUT</button>'}
                    </div>
                </div>
                <div class="payables-section crm-ui-section">
                    <h3>Cuenta por pagar</h3>
                    ${hasCollection ? '' : (approvedForPayable ? '<div class="billing-alert billing-alert-info"><strong>Factura aprobada sin CxP.</strong><br><span>La próxima sincronización con FINANCE activo la materializará. También puedes crearla explícitamente desde el pie.</span></div>' : '<div class="billing-alert billing-alert-warning"><strong>Factura pendiente de aprobación.</strong><br><span>No se puede crear ni pagar una CxP hasta aprobar la recepción de la factura.</span></div>')}
                    <form id="payableUpdateForm">
                        <div class="payables-field crm-ui-field"><label>Centro de costo</label><select name="finance_center_id"><option value="">${d.supplier_default_cost_center_id ? 'Usar centro del proveedor' : 'Sin centro'}</option>${optionHtml(centers, 'id', 'center_name', d.effective_finance_center_id || d.finance_center_id || d.supplier_default_cost_center_id)}</select><small>${d.finance_center_source === 'supplier_default' ? 'Valor heredado desde la ficha del proveedor. Puedes cambiarlo sólo para este DTE.' : (d.finance_center_source === 'document_override' ? 'Centro definido específicamente para este DTE.' : 'Proveedor sin centro de costo definido.')}</small></div>
                        <div class="payables-field crm-ui-field"><label>Condicion de pago proveedor</label><div class="payables-readonly-status"><span class="payables-resolution">${esc(paymentTermLabel)}</span><small>${paymentTermDays} dias. Se usa para calcular el vencimiento automaticamente.</small></div></div>
                        <div class="payables-field crm-ui-field"><label>Fecha vencimiento</label><div class="payables-readonly-status"><span>${esc(d.due_date || d.payment_due_date || 'Vencida / sin fecha calculable')}</span><small>Calculada desde la condicion de pago del proveedor. Si no hay proveedor o condicion, se usa contado; si no hay fecha base valida, queda vencida.</small></div></div>
                        <div class="payables-field crm-ui-field"><label>Estado financiero</label><div class="payables-readonly-status"><span class="payables-status ${esc(status)}">${esc(statusLabel(status))}</span><small>Calculado por saldo, pagos y vencimiento.</small></div></div>
                        <div class="payables-field crm-ui-field"><label>Nota interna</label><textarea name="notes">${esc(d.collection_notes || '')}</textarea></div>
                    </form>
                </div>
            </div>
            <div class="payables-section crm-ui-section" style="margin-top:12px;">
                <h3>Registrar pago individual / parcial</h3>
                <form id="payablePaymentForm" class="payables-grid-3 crm-ui-form-grid"><input type="hidden" name="idempotency_key" value="${esc(paymentRequestKey('cxp'))}"><input type="hidden" name="expected_collection_id" value="${esc(d.collection_id || '')}"><input type="hidden" name="expected_inbox_document_id" value="${esc(d.inbox_document_id || '')}"><input type="hidden" name="expected_balance" value="${esc(balance.toFixed(2))}">
                    <div class="payables-field crm-ui-field"><label>Fecha efectiva del pago</label><input type="date" name="payment_date" value="${today()}" required><small>Usa la fecha real del movimiento bancario, no la fecha de registro.</small></div>
                    <div class="payables-field crm-ui-field"><label>Monto a pagar</label><input type="number" name="amount" min="1" step="0.01" max="${esc(balance.toFixed(2))}" value="${esc(balance.toFixed(2))}" required ${!hasCollection || balance <= 0.5 ? 'disabled' : ''}></div>
                    <div class="payables-field crm-ui-field"><label>Metodo de pago</label><select name="payment_method_id" required ${!hasCollection || balance <= 0.5 ? 'disabled' : ''}><option value="">Seleccionar</option>${optionHtml(methods, 'id', 'method_name', '')}</select></div>
                    <div class="payables-field crm-ui-field"><label>Caja/cuenta origen</label><select name="cash_account_id" required ${!hasCollection || balance <= 0.5 ? 'disabled' : ''}><option value="">Seleccionar</option>${optionHtml(accounts, 'id', 'account_name', '')}</select></div>
                    <div class="payables-field crm-ui-field"><label>Referencia verificable</label><input type="text" name="external_reference" minlength="6" maxlength="120" placeholder="N° operación o referencia interna única" ${!hasCollection || balance <= 0.5 ? 'disabled' : ''} ${state.env === 'production' ? 'required' : ''}><small>No usar NA, N/A, sin referencia o no aplica.</small></div>
                    <div class="payables-field crm-ui-field"><label>Nota pago</label><input type="text" name="notes" placeholder="Observacion interna" ${!hasCollection || balance <= 0.5 ? 'disabled' : ''}></div>
                </form>
                ${hasCollection ? '<p class="payables-muted" style="margin-top:8px;">Puedes registrar pagos parciales. Si el saldo llega a $0, el documento queda como pagado.</p>' : (approvedForPayable ? '<p class="payables-muted" style="margin-top:8px;">Crea o sincroniza primero la cuenta por pagar para habilitar el registro de pagos.</p>' : '<p class="payables-muted" style="margin-top:8px;">Aprueba primero la factura desde Documentos recibidos.</p>')}
            </div>
            <div class="payables-section crm-ui-section" style="margin-top:12px;">
                <h3>Historial de pagos</h3>
                ${renderPayments(payments)}
            </div>
`;
        const primaryAdjustmentAction = hasCollection
            ? '<button type="button" class="btn btn-outline" id="savePayableBtn"><i data-lucide="save"></i><span>Guardar ajustes</span></button>'
            : (approvedForPayable ? '<button type="button" class="btn btn-success" id="savePayableBtn" data-materialize-collection="1"><i data-lucide="landmark"></i><span>Crear cuenta por pagar</span></button>' : '');
        const paymentActions = state.paymentJustSaved
            ? (hasCollection && balance > 0.5 ? '<button type="button" class="btn btn-success" id="registerAnotherPayablePaymentBtn"><i data-lucide="plus-circle"></i><span>Registrar otro pago</span></button>' : '<span class="billing-save-confirmation"><i data-lucide="circle-check"></i> Pago guardado</span>')
            : `${primaryAdjustmentAction}${hasCollection && balance > 0.5 ? '<button type="button" class="btn btn-success" id="registerPayablePaymentBtn" disabled><i data-lucide="badge-dollar-sign"></i><span>Registrar pago</span></button>' : ''}`;
        $('payableModalFooter').innerHTML = `
            <div class="modal-footer-actions-left">
                <button type="button" class="btn btn-outline" id="closePayableFooterBtn"><i data-lucide="x"></i><span>Cerrar</span></button>
            </div>
            <div class="modal-footer-actions-right">${paymentActions}</div>`;
        refreshIcons();
        bindPaymentValidation();
    }

    function bindPaymentValidation(){
        const form = $('payablePaymentForm');
        const btn = $('registerPayablePaymentBtn');
        if (!form || !btn) return;
        const validate = () => {
            const amount = Number(form.amount.value || 0);
            const max = Number(form.amount.getAttribute('max') || 0);
            const reference = String(form.external_reference.value || '').trim();
            const normalized = reference.toLowerCase().replace(/\s+/g, ' ');
            const weak = ['na','n/a','n.a.','no aplica','sin referencia','sin comprobante','s/r','sr','none','null','-','--'];
            const referenceOk = state.env !== 'production' || (reference.length >= 6 && !weak.includes(normalized));
            btn.disabled = !(form.payment_date.value && amount > 0 && amount <= max && form.payment_method_id.value && form.cash_account_id.value && referenceOk);
        };
        form.querySelectorAll('input,select').forEach(el => el.addEventListener('input', validate));
        form.querySelectorAll('select,input[type="date"]').forEach(el => el.addEventListener('change', validate));
        validate();
    }

    async function savePayable(){
        const materialize = !state.current || Number(state.current.collection_id || 0) <= 0;
        if (materialize) {
            const accepted = window.confirm('Se creará una cuenta por pagar productiva para este DTE. Esta acción no registra pagos ni acepta el documento en FACTO. ¿Continuar?');
            if (!accepted) return;
        }
        const fd = new FormData($('payableUpdateForm'));
        fd.append('id', state.current.id);
        fd.append('environment', state.env);
        fd.append('materialize_collection', materialize ? '1' : '0');
        fd.append('expected_inbox_document_id', state.current.inbox_document_id || '');
        const data = await jsonFetch('payable_update_ajax.php', {method:'POST', body:fd});
        state.current = data.result.document;
        await loadPayables();
        await openDetail(state.current.id);
    }

    async function registerPayment(){
        const form = $('payablePaymentForm');
        if (!form || !form.reportValidity()) return;
        const amount = Number(form.amount.value || 0);
        const reference = String(form.external_reference.value || '').trim();
        const paymentDate = form.payment_date.value || '';
        const methodLabel = form.payment_method_id.options[form.payment_method_id.selectedIndex]?.text || '-';
        const accountLabel = form.cash_account_id.options[form.cash_account_id.selectedIndex]?.text || '-';
        const accepted = window.confirm(`Registrar pago productivo por ${fmtMoney(amount)} para el folio ${state.current.document_number || '-'}?\nFecha efectiva: ${paymentDate}\nMétodo: ${methodLabel}\nCuenta: ${accountLabel}\nReferencia: ${reference || 'sin referencia'}\nEsta acción modificará saldos y no se puede repetir con otra clave.`);
        if (!accepted) return;
        const button = $('registerPayablePaymentBtn');
        if (button) button.disabled = true;
        try {
            const fd = new FormData(form);
            fd.append('id', state.current.id);
            fd.append('environment', state.env);
            const data = await jsonFetch('payable_register_payment_ajax.php', {method:'POST', body:fd});
            state.current = data.document;
            state.currentPayments = data.payments || [];
            state.paymentJustSaved = true;
            await loadPayables();
            renderDetail(state.current, state.currentPayments);
        } finally {
            if (button && document.body.contains(button)) button.disabled = false;
        }
    }

    async function linkSupplier(){
        const fd = formData({id: state.current.id, environment: state.env});
        const data = await jsonFetch('payable_link_supplier_ajax.php', {method:'POST', body:fd});
        state.current = data.result.document;
        await loadPayables();
        await openDetail(state.current.id);
    }

    async function inboxAction(action){
        if (!confirm('Confirmar accion FACTO: ' + action + '?')) return;
        const fd = formData({id: state.current.id, environment: state.env, action: action});
        const data = await jsonFetch('payable_accept_reject_ajax.php', {method:'POST', body:fd});
        state.current = data.document;
        await loadPayables();
        renderDetail(state.current, state.currentPayments);
    }

    function selectedRows(){
        return state.rows.filter(r => state.selected.has(String(r.id)) && balanceOf(r) > 0.5);
    }

    function updateSelectionUi(){
        const rows = selectedRows();
        const total = rows.reduce((sum, r) => sum + balanceOf(r), 0);
        $('payablesSelectedCount').textContent = rows.length + ' seleccionados';
        $('payablesSelectedTotal').textContent = 'Saldo seleccionado: ' + fmtMoney(total);
        $('bulkPaymentBtn').disabled = rows.length === 0;
        $('clearSelectionBtn').disabled = rows.length === 0;
        document.querySelectorAll('.payables-select-all-group').forEach(box => {
            const eligible = state.rows.filter(r => {
                const status = r.collection_status || r.payment_status || 'por_pagar';
                return groupKey(r) === box.dataset.group
                    && Number(r.is_financial_document || 0) === 1
                    && Number(r.collection_id || 0) > 0
                    && balanceOf(r) > 0.5
                    && !['pagado','rechazado','anulado'].includes(status);
            });
            const selected = eligible.filter(r => state.selected.has(String(r.id)));
            box.checked = eligible.length > 0 && selected.length === eligible.length;
            box.indeterminate = selected.length > 0 && selected.length < eligible.length;
            box.disabled = eligible.length === 0;
        });
    }

    function openBulkPayment(){
        const rows = selectedRows();
        if (!rows.length) return;
        const total = rows.reduce((sum, r) => sum + balanceOf(r), 0);
        state.bulkRequestKey = paymentRequestKey('payable-bulk');
        state.bulkSelection = rows.map(r => ({
            id: Number(r.id),
            collection_id: Number(r.collection_id),
            document_id: String(r.document_id || ''),
            inbox_document_id: String(r.inbox_document_id || ''),
            expected_balance: Number(balanceOf(r).toFixed(2)),
            due_date: String(r.due_date || r.payment_due_date || r.document_date || '9999-12-31'),
            folio: String(r.document_number || '')
        }));
        const methods = state.options ? state.options.payment_methods : [];
        const accounts = state.options ? state.options.cash_accounts : [];
        const referenceRequired = state.env === 'production' ? 'required minlength="6"' : '';
        $('payableBulkSubtitle').textContent = rows.length + ' documentos · saldo seleccionado ' + fmtMoney(total);
        $('payableBulkBody').innerHTML = `
            <div class="payables-summary crm-ui-summary-grid">
                <div class="payables-summary-card crm-ui-summary-card"><span>Documentos</span><strong>${rows.length}</strong></div>
                <div class="payables-summary-card crm-ui-summary-card"><span>Saldo seleccionado</span><strong>${fmtMoney(total)}</strong></div>
                <div class="payables-summary-card crm-ui-summary-card"><span>Modo</span><strong>Pago masivo atómico</strong></div>
            </div>
            <div class="payables-section crm-ui-section">
                <h3>Datos del pago</h3>
                <form id="bulkPaymentForm" class="payables-grid-3 crm-ui-form-grid">
                    <div class="payables-field crm-ui-field"><label>Fecha efectiva</label><input type="date" name="payment_date" value="${today()}" max="${today()}" required></div>
                    <div class="payables-field crm-ui-field"><label>Monto total a aplicar</label><input type="number" name="amount" min="0.01" step="0.01" max="${total.toFixed(2)}" value="${total.toFixed(2)}" required></div>
                    <div class="payables-field crm-ui-field"><label>Método de pago</label><select name="payment_method_id" required><option value="">Seleccionar</option>${optionHtml(methods, 'id', 'method_name', '')}</select></div>
                    <div class="payables-field crm-ui-field"><label>Caja/cuenta origen</label><select name="cash_account_id" required><option value="">Seleccionar</option>${optionHtml(accounts, 'id', 'account_name', '')}</select></div>
                    <div class="payables-field crm-ui-field"><label>Referencia verificable</label><input type="text" name="external_reference" maxlength="120" ${referenceRequired} placeholder="N.º operación, voucher o referencia interna"></div>
                    <div class="payables-field crm-ui-field"><label>Nota pago</label><input type="text" name="notes" placeholder="Observación interna"></div>
                    <label style="display:flex;align-items:center;gap:8px;font-weight:700;"><input type="checkbox" name="full_payment" value="1" checked> Pagar saldo completo de seleccionados</label>
                </form>
                <p class="payables-muted">El monto se distribuirá por fecha de vencimiento, desde el documento más antiguo. Si algún saldo cambia, el lote completo se rechazará.</p>
            </div>
            <div class="payables-section crm-ui-section" style="margin-top:12px;">
                <h3>Documentos seleccionados</h3>
                <div class="payables-bulk-result crm-ui-inner-table-wrap"><table>
                    <thead><tr><th>Proveedor</th><th>Folio</th><th>Saldo</th></tr></thead>
                    <tbody>${rows.map(r => `<tr><td>${esc(r.supplier_name || r.issuer_name || 'Sin proveedor')}</td><td>${esc(r.document_number || '-')}</td><td><strong>${fmtMoney(balanceOf(r))}</strong></td></tr>`).join('')}</tbody>
                </table></div>
            </div>`;
        $('payableBulkFooter').innerHTML = '<div class="modal-footer-actions-left"><button type="button" class="btn btn-outline" id="cancelBulkPaymentBtn"><i data-lucide="x"></i><span>Cancelar</span></button></div><div class="modal-footer-actions-right"><button type="button" class="btn btn-primary" id="confirmBulkPaymentBtn" disabled><i data-lucide="badge-dollar-sign"></i><span>Registrar pago</span></button></div>';
        refreshIcons();
        $('payableBulkModal').style.display = 'flex';
        $('payableBulkModal').classList.add('show');
        $('payableBulkModal').setAttribute('aria-hidden', 'false');
        document.body.classList.add('is-modal-open');
        bindBulkValidation(total);
    }

    function bindBulkValidation(total){
        const form = $('bulkPaymentForm');
        const btn = $('confirmBulkPaymentBtn');
        const amount = form.amount;
        const full = form.full_payment;
        const sync = () => {
            if (full.checked) { amount.value = total.toFixed(2); amount.disabled = true; }
            else { amount.disabled = false; }
            const value = Number(amount.value || 0);
            const referenceOk = state.env !== 'production' || String(form.external_reference.value || '').trim().length >= 6;
            btn.disabled = !(form.payment_date.value && value > 0 && value <= total + 0.01 && form.payment_method_id.value && form.cash_account_id.value && referenceOk);
        };
        form.querySelectorAll('input,select').forEach(el => el.addEventListener('input', sync));
        form.querySelectorAll('select,input[type="date"],input[type="checkbox"]').forEach(el => el.addEventListener('change', sync));
        sync();
    }

    async function submitBulkPayment(){
        const form = $('bulkPaymentForm');
        if (!form || !form.reportValidity() || !state.bulkSelection.length) return;
        const total = state.bulkSelection.reduce((sum,row) => sum + Number(row.expected_balance || 0), 0);
        const amount = form.full_payment.checked ? total : Number(form.amount.value || 0);
        const paymentDate = form.payment_date.value || '';
        const reference = String(form.external_reference.value || '').trim();
        const methodLabel = form.payment_method_id.options[form.payment_method_id.selectedIndex]?.text || '-';
        const accountLabel = form.cash_account_id.options[form.cash_account_id.selectedIndex]?.text || '-';
        const accepted = window.confirm(`Registrar pago masivo para ${state.bulkSelection.length} documentos por ${fmtMoney(amount)}?\nFecha efectiva: ${paymentDate}\nMétodo: ${methodLabel}\nCuenta: ${accountLabel}\nReferencia: ${reference || 'sin referencia'}\nEl lote es atómico: si un saldo cambió, no se registrará ningún pago.`);
        if (!accepted) return;
        const button = $('confirmBulkPaymentBtn');
        if (button) button.disabled = true;
        try {
            const fd = new FormData(form);
            if (form.full_payment.checked) fd.set('amount', total.toFixed(2));
            fd.append('environment', state.env);
            fd.append('ids', JSON.stringify(state.bulkSelection.map(r => r.id)));
            fd.append('selection_snapshot', JSON.stringify(state.bulkSelection));
            fd.append('idempotency_key', state.bulkRequestKey);
            if (!form.full_payment.checked) fd.set('full_payment', '0');
            const data = await jsonFetch('payable_bulk_payment_ajax.php', {method:'POST', body:fd});
            renderBulkResult(data.result);
            state.selected.clear();
            state.bulkSelection = [];
            await loadPayables();
        } finally {
            if (button && document.body.contains(button)) button.disabled = false;
        }
    }

    function renderBulkResult(result){
        $('payableBulkSubtitle').textContent = 'Batch #' + result.batch_id + ' · aplicado ' + fmtMoney(result.amount_allocated);
        $('payableBulkBody').innerHTML = `
            <div class="payables-summary crm-ui-summary-grid">
                <div class="payables-summary-card crm-ui-summary-card"><span>Monto informado</span><strong>${fmtMoney(result.amount_total)}</strong></div>
                <div class="payables-summary-card crm-ui-summary-card"><span>Monto aplicado</span><strong>${fmtMoney(result.amount_allocated)}</strong></div>
                <div class="payables-summary-card crm-ui-summary-card"><span>No asignado</span><strong>${fmtMoney(result.unallocated)}</strong></div>
            </div>
            <div class="payables-section crm-ui-section">
                <h3>Resultado por documento</h3>
                <div class="payables-bulk-result crm-ui-inner-table-wrap"><table>
                    <thead><tr><th>Proveedor</th><th>Folio</th><th>Aplicado</th><th>Saldo posterior</th><th>Estado</th></tr></thead>
                    <tbody>${(result.processed || []).map(r => `<tr><td>${esc(r.supplier || '-')}</td><td>${esc(r.folio || '-')}</td><td><strong>${fmtMoney(r.amount_applied)}</strong></td><td>${fmtMoney(r.balance_after)}</td><td>${esc(statusLabel(r.status_after))}</td></tr>`).join('')}</tbody>
                </table></div>
            </div>`;
        $('payableBulkFooter').innerHTML = '<div class="modal-footer-actions-right"><button type="button" class="btn btn-primary" id="closeBulkPaymentResultBtn"><i data-lucide="check"></i><span>Cerrar</span></button></div>';
        refreshIcons();
    }

    function syncModalBodyLock(){
        const hasOpenModal = Array.from(document.querySelectorAll('.product-modal-backdrop.show')).some((modal) => modal.getAttribute('aria-hidden') !== 'true');
        document.body.classList.toggle('is-modal-open', hasOpenModal);
    }
    function closeModal(){ $('payableModal').style.display = 'none'; $('payableModal').classList.remove('show'); $('payableModal').setAttribute('aria-hidden', 'true'); syncModalBodyLock(); }
    function closeBulk(){ $('payableBulkModal').style.display = 'none'; $('payableBulkModal').classList.remove('show'); $('payableBulkModal').setAttribute('aria-hidden', 'true'); syncModalBodyLock(); }

    on(document, 'click', function(e){
        if (e.target === $('payableBulkModal')) { closeBulk(); return; }
        if (e.target === $('payableModal')) { closeModal(); return; }
        const pageButton = e.target.closest('[data-billing-page]');
        if (pageButton && pageButton.closest('#payablesGroups')) { window.IcyBillingTable.movePage(state.paging,pageButton.dataset.billingPage,pageButton.dataset.direction,state.rows.filter(row=>groupKey(row)===pageButton.dataset.billingPage).length); renderRows(); return; }
        const payButton = e.target.closest('[data-payable-pay]');
        if (payButton) { state.selected.clear(); state.selected.add(String(payButton.dataset.payablePay)); updateSelectionUi(); openBulkPayment(); return; }
        const detailBtn = e.target.closest('[data-payable-detail]');
        if (detailBtn) { openDetail(detailBtn.getAttribute('data-payable-detail')).catch(err => alert(err.message)); return; }
        const action = e.target.closest('button, a');
        const actionId = action ? action.id : '';
        if (actionId === 'reloadPayablesBtn') { loadPayables(); return; }
        if (actionId === 'closePayableModalBtn' || actionId === 'closePayableFooterBtn') { closeModal(); return; }
        if (actionId === 'closeBulkPaymentBtn' || actionId === 'cancelBulkPaymentBtn' || actionId === 'closeBulkPaymentResultBtn') { closeBulk(); return; }
        if (actionId === 'savePayableBtn') { savePayable().catch(err => alert(err.message)); return; }
        if (actionId === 'registerPayablePaymentBtn') { registerPayment().catch(err => alert(err.message)); return; }
        if (actionId === 'registerAnotherPayablePaymentBtn') { state.paymentJustSaved = false; renderDetail(state.current, state.currentPayments); const amount = document.querySelector('#payablePaymentForm [name=amount]'); if (amount) { amount.focus(); amount.select(); } return; }
        if (actionId === 'linkSupplierBtn') { linkSupplier().catch(err => alert(err.message)); return; }
        if (actionId === 'bulkPaymentBtn') { openBulkPayment(); return; }
        if (actionId === 'confirmBulkPaymentBtn') { submitBulkPayment().catch(err => alert(err.message)); return; }
        if (actionId === 'clearSelectionBtn') { state.selected.clear(); renderRows(); return; }
        const inboxBtn = e.target.closest('[data-inbox-action]');
        if (inboxBtn) { inboxAction(inboxBtn.getAttribute('data-inbox-action')).catch(err => alert(err.message)); return; }
    });

    on(document, 'change', function(e){
        const pageSize = e.target.closest('[data-billing-page-size]');
        if (pageSize && pageSize.closest('#payablesGroups')) { window.IcyBillingTable.setPageSize(state.paging,pageSize.dataset.billingPageSize,pageSize.value); renderRows(); return; }
        if (e.target.classList.contains('payable-select')) {
            const id = String(e.target.getAttribute('data-payable-id'));
            if (e.target.checked) state.selected.add(id); else state.selected.delete(id);
            updateSelectionUi();
            return;
        }
        if (e.target.classList.contains('payables-select-all-group')) { const group=e.target.dataset.group,checked=e.target.checked; state.rows.forEach(r=>{const status=r.collection_status||r.payment_status||'por_pagar';if(groupKey(r)===group&&Number(r.is_financial_document||0)===1&&Number(r.collection_id||0)>0&&balanceOf(r)>0.5&&!['pagado','rechazado','anulado'].includes(status)){if(checked)state.selected.add(String(r.id));else state.selected.delete(String(r.id));}});renderRows();return; }
    });
    on(document, 'billing-reference-sort', function(e){
        if (!e.target.closest('#payablesGroups')) return;
        window.IcyBillingTable.toggleSort(state.paging,e.detail.group,e.detail.column,e.detail.type);
        renderRows();
    });

    ['payablesStatus','payablesFrom','payablesTo'].forEach(id => on($(id),'change', loadPayables));
    on($('payablesSearch'),'keydown', function(e){ if (e.key === 'Enter') loadPayables(); });
    on(document,'keydown', function(e){
        if (e.key !== 'Escape') return;
        if ($('payableBulkModal')?.classList.contains('show')) { closeBulk(); return; }
        if ($('payableModal')?.classList.contains('show')) closeModal();
    });
    loadPayables();
})();
</script>

</main>
<?php require_once '../../layouts/footer.php'; ?>
