Documentación técnica del plugin PagosMultiples

Documento para desarrolladores que necesiten mantener el plugin, ampliarlo o integrarlo con otros. Describe los modelos, los procesos, los asientos que genera y los puntos de extensión.

🗃️ Modelos y tablas

  • Base\PaymentReceiptGroup: base de las agrupaciones. CustomerReceiptGroup (ppmm_customer_receipts) y SupplierReceiptGroup (ppmm_supplier_receipts).
    • status: STATUS_PENDING (0) o STATUS_CHARGED (1, aprobado).
    • groupdate: fecha de pago de los recibos y del asiento.
    • idbank: cuenta bancaria; el asiento usa su codsubcuenta.
    • idcompany, idcurrency e idserie filtran los recibos que se pueden añadir. La serie aporta además el canal del asiento.
    • groupreceipts: una línea por cliente o proveedor (true) o por recibo (false).
    • noentry: aprobar sin asiento.
    • identry: asiento generado al aprobar.
    • receipts y total: recuento e importe, que recalcula updateTotal() al añadir o quitar recibos.
    • idagent: solo en la de clientes.
  • Extension/Table/recibospagoscli y recibospagosprov: añaden idmultiple a los recibos, con ON DELETE SET NULL. Es el único enlace entre recibo y agrupación.
  • Base\BankCheck: base de los talones. CustomerBankCheck (ppmm_customer_bankchecks, prefijo TAL-, idcustomer) y SupplierBankCheck (ppmm_supplier_bankchecks, prefijo TALP-, idsupplier).
    • idmultiple: agrupación del talón, con ON DELETE RESTRICT: una agrupación con talones no se puede borrar.
    • codsubaccount: subcuenta de cartera.
    • idbank: banco de depósito o cobro. Si está vacío, test() toma el de la agrupación.
    • identry: asiento del cobro o cargo.
    • Fechas delivered, expiration, deposited, charged y returned.
    • type: enum BankCheckType (check, promissory).
    • status: enum BankCheckStatus (UNTRACKED 1, PENDING 10, DEPOSITED 30, RETURNED 50, CHARGED 90). En los emitidos, PENDING se muestra como «Emitido» y CHARGED como «Cargado» (selectIssued()).
    • expired: atributo calculado en loadFromData(), no se guarda: PENDING o DEPOSITED con expiration hasta hoy.
    • El cliente o proveedor es obligatorio solo sin idmultiple.
  • AgentSettlement (ppmm_agent_settlement): liquidación de agente, con la misma id que el cobro múltiple (ON DELETE CASCADE). Guarda el recuento de billetes y monedas, total_diets, bankchecks, total y difference. Con automatic, test() recalcula el total y difference = total + total_diets + bankchecks - total del grupo.
  • Extension/Table/agentes: codsubaccount_wallet y codsubaccount_difference, que usa la contabilización del cobro múltiple, y codsubaccount_payment, que solo existe en la tabla y en el XMLView y el código no usa.
  • Join\ReciboClienteAdd y Join\ReciboProveedorAdd: recibos con su factura y su cliente o proveedor, para la pestaña Añadir. Join\AccountingDetail: partidas con su asiento, para las pestañas de asiento.

🚦 Estado del talón

El estado se calcula siempre en test() mediante updateStatus(), salvo que ya sea RETURNED, a partir de los datos del talón y por este orden:

  • Con identry, CHARGED.
  • Con deposited, DEPOSITED.
  • Con codsubaccount, PENDING.
  • Con idmultiple, CHARGED con charged = delivered: se cobra con el asiento de la agrupación.
  • En otro caso, UNTRACKED.

Sin identry se vacía charged. Por eso las acciones no fijan el estado, sino los datos de los que depende; la excepción es cancelar, que pone RETURNED.

isLocked() es true con identry, en DEPOSITED, en RETURNED con subcuenta de cartera o si la agrupación no está pendiente. Bloqueado, delete() falla y checkLocked() rechaza el guardado si cambia algún campo de lockedFields() (codsubaccount, delivered, expiration, issuerbank, number, total, type y el cliente o proveedor). idbank no está en esa lista porque las acciones lo escriben; solo se bloquea en pantalla.

🔄 Procesos

  • Lib/PagosMultiples/EditPaymentReceiptGroup: base de EditCustomerReceiptGroup y EditSupplierReceiptGroup. Crea las pestañas y los botones según el estado, y delega las acciones add-receipts, remove-receipts, charged-receipts (aprobar) y reopen-receipts en PaymentReceiptGroupActions.
  • PaymentReceiptGroupActions, dentro de una transacción:
    • Aprobar marca cada recibo como pagado con disablePaymentGeneration(). Crea un pago (PagoCliente/PagoProveedor) con customid = id de la agrupación y disableAccountingGeneration(), genera el asiento salvo noentry y pone STATUS_CHARGED. Falla si algún recibo ya está pagado.
    • Reabrir borra el asiento (con editable = true en memoria), crea un pago negativo por recibo, los deja sin pagar dentro de la agrupación y vuelve a STATUS_PENDING.
  • PaymentReceiptGroup::delete(): borra el asiento, deja los recibos sin pagar y sin idmultiple, y borra los pagos con su customid.
  • EditCustomerReceiptGroup: añade la pestaña de liquidación, solo con agente, y la acción AJAX cash-calculate que usa Assets/JS/EditCustomerReceiptGroup.js para recalcular en pantalla.
  • Lib/PagosMultiples/EditBankCheck: base de EditCustomerBankCheck y EditSupplierBankCheck. Las fichas hijas solo definen addActionButtons() y subjectColumn(). Acciones:
    • deposit-bankcheck y undo-deposit-bankcheck escriben o vacían deposited e idbank.
    • charged-bankcheck, refund-bankcheck y cancel-bankcheck delegan en BankCheckToAccounting.
    • En UNTRACKED, cancelar solo pone RETURNED y returned.
    • La pestaña de asiento muestra las partidas con asientos.documento igual a la referencia del talón o del asiento identry.
  • Lib/PagosMultiples/BankCheckBatch: depósito y cobro o cargo en lote desde ListFacturaCliente y ListFacturaProveedor. Descarta los talones en un estado no válido y avisa con bankchecks-skipped.
    • El core no envía los codes[] marcados a los modales, así que Assets/JS/BankCheckBatch.js (PMultiples.BankCheckBatch) los copia al formulario del modal.
    • Se carga con AssetLoader, que añade ?v=filemtime.
  • BankCheck::notifyExpiring(): aviso con Tools::log() al cargar la vista principal de los listados de facturas. Usa whereExpiring(): PENDING o DEPOSITED con vencimiento hasta DAYS_TO_EXPIRE (15) días.

📒 Asientos

Las clases de Lib/Accounting extienden AccountingClass del core. Comprueban que el ejercicio de la fecha exista y esté abierto, y guardan el asiento con editable = false. Si la cuenta bancaria no tiene subcuenta, usan la especial SPECIAL_PAYMENT_ACCOUNT.

  • ReceiptGroupToAccounting, con sus hijas CustomerReceiptGroupToAccounting y SupplierReceiptGroupToAccounting: un asiento por agrupación con la fecha groupdate e importe total. Informa identry.
    • Clientes: debe el banco por el total, haber la subcuenta de cada cliente (por recibo o agrupada). Por cada talón con cartera, debe cartera y haber banco.
    • Proveedores: haber el banco por el total, debe la subcuenta de cada proveedor. Por cada talón con cartera, debe banco y haber cartera.
    • Con agente: si tiene codsubaccount_wallet, sustituye al banco en todas las líneas. Con diferencia, debe codsubaccount_difference y haber el banco o la cartera por -difference; sin esa subcuenta, la aprobación falla.
  • BankCheckToAccounting: asientos del talón, con documento igual a getAccountingReference(). El sentido se invierte para los emitidos (instanceof CustomerBankCheck).
    • charge(): agrupa por banco; los DEPOSITED van a su idbank y el resto al indicado. Genera un asiento por banco con una línea de banco y una de cartera por talón, e informa identry, charged e idbank. Con varios talones, el asiento no lleva referencia y el enlace es solo identry.
    • refund(): cartera contra banco más los gastos, que llevan su propia subcuenta. Vacía deposited e identry, así que el talón vuelve a PENDING.
    • cancel(): cliente o proveedor (u otra subcuenta indicada) contra cartera. Pone RETURNED y returned.
    • La empresa del ejercicio es la de la cuenta bancaria; sin cuenta, la de la agrupación o la empresa por defecto.

🧩 Extensiones y puntos de extensión

Se registran en Init::init().

  • Extension/Model/ReciboCliente y ReciboProveedor: impiden borrar un recibo con idmultiple y normalizan idmultiple vacío a null.
  • Extension/Controller/ListFacturaCliente y ListFacturaProveedor: pestañas de agrupaciones y de talones, botones de lote y aviso de vencimientos.
  • Extension/Controller/EditCliente y EditProveedor: pestaña de talones del cliente o proveedor.
  • Extension/XMLView: EditAgente (subcuentas del agente) y EditReciboCliente/EditReciboProveedor (enlace a la agrupación).

ReceiptGroupToAccounting usa ExtensionsTrait, y otro plugin puede añadir líneas al asiento de la agrupación con pipeFalse(). Si alguna extensión devuelve false, el asiento se descarta:

  • addHeaderLine($document, $entry, $line): tras la línea del banco.
  • addLinesAgrupped($document, $entry, $receipts) y addLinesWithoutGrouping($document, $entry, $receipt): tras las líneas de clientes o proveedores.
  • addLines($document, $entry, $groupreceipts): tras todas las líneas de recibos.

🛠️ Migración

Migration/FillBankCheckStatus (pagosmultiples_fill_bankcheck_status_2026_10) se ejecuta una vez desde Init::update(). Rellena status en los talones de clientes anteriores a la 2.05 (CHARGED con charged, PENDING sin ella) y pone type = check donde estaba vacío. En el XML, type no tiene valor por defecto, para que la migración pueda distinguir los registros antiguos.

Cookies
Usamos cookies necesarias para el funcionamiento del sitio y cookies opcionales para recordar tus preferencias y mejorar tu experiencia. Puedes aceptarlas todas, rechazarlas o configurar tus preferencias

Soporte