* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ /** * \file bankimport/card.php * \ingroup bankimport * \brief Card page for a single bank transaction */ // Load Dolibarr environment $res = 0; if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; } $tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1; while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; } if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) { $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; } if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; } if (!$res && file_exists("../main.inc.php")) { $res = @include "../main.inc.php"; } if (!$res && file_exists("../../main.inc.php")) { $res = @include "../../main.inc.php"; } if (!$res) { die("Include of main fails"); } require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; dol_include_once('/bankimport/class/banktransaction.class.php'); dol_include_once('/bankimport/lib/bankimport.lib.php'); /** * @var Conf $conf * @var DoliDB $db * @var Translate $langs * @var User $user */ $langs->loadLangs(array("bankimport@bankimport", "banks", "bills")); $id = GETPOSTINT('id'); $ref = GETPOST('ref', 'alpha'); $action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); // Security check if (!$user->hasRight('bankimport', 'read')) { accessforbidden(); } /* * Actions */ $object = new BankImportTransaction($db); if ($id > 0 || !empty($ref)) { $result = $object->fetch($id, $ref); if ($result <= 0) { setEventMessages($langs->trans("RecordNotFound"), null, 'errors'); } } // Set status if ($action == 'setstatus' && $object->id > 0) { $newstatus = GETPOSTINT('status'); $result = $object->setStatus($newstatus, $user); if ($result > 0) { setEventMessages($langs->trans("StatusUpdated"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } else { setEventMessages($object->error, null, 'errors'); } } // Unlink payment (reset to NEW status) if ($action == 'unlink' && $object->id > 0) { if ($object->status == BankImportTransaction::STATUS_MATCHED) { $result = $object->unlinkPayment($user); if ($result > 0) { setEventMessages($langs->trans("PaymentUnlinked"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } else { setEventMessages($object->error, null, 'errors'); } } else { setEventMessages($langs->trans("CannotUnlinkThisStatus"), null, 'warnings'); } } // Find matches if ($action == 'findmatches' && $object->id > 0) { $matches = $object->findMatches(); if (count($matches) > 0) { $_SESSION['bankimport_matches_'.$object->id] = $matches; setEventMessages($langs->trans("MatchesFound", count($matches)), null, 'mesgs'); } else { setEventMessages($langs->trans("NoMatchesFound"), null, 'warnings'); } } // Link to object (old method - just link without payment) if ($action == 'linkto' && $object->id > 0) { $linktype = GETPOST('linktype', 'alpha'); $linkid = GETPOSTINT('linkid'); if ($linktype && $linkid > 0) { $result = $object->linkTo($linktype, $linkid, $user); if ($result > 0) { setEventMessages($langs->trans("LinkCreated"), null, 'mesgs'); unset($_SESSION['bankimport_matches_'.$object->id]); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } else { setEventMessages($object->error, null, 'errors'); } } } // Confirm payment (creates payment in Dolibarr) $bankAccountId = getDolGlobalInt('BANKIMPORT_BANK_ACCOUNT_ID'); if ($action == 'confirmpayment' && $object->id > 0 && !empty($bankAccountId)) { $matchtype = GETPOST('matchtype', 'alpha'); $matchid = GETPOSTINT('matchid'); if ($matchtype && $matchid > 0) { if ($object->status != BankImportTransaction::STATUS_NEW) { setEventMessages($langs->trans("TransactionAlreadyProcessed"), null, 'warnings'); } else { $result = $object->confirmPayment($user, $matchtype, $matchid, $bankAccountId); if ($result > 0) { setEventMessages($langs->trans("PaymentCreatedSuccessfully", dol_escape_htmltag($object->name), price(abs($object->amount))), null, 'mesgs'); unset($_SESSION['bankimport_matches_'.$object->id]); } else { setEventMessages($object->error, $object->errors, 'errors'); } } header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } } // Confirm multiple invoices payment if ($action == 'confirmmulti' && $object->id > 0 && !empty($bankAccountId)) { $invoiceIds = GETPOST('invoices', 'array'); if (!empty($invoiceIds)) { if ($object->status != BankImportTransaction::STATUS_NEW) { setEventMessages($langs->trans("TransactionAlreadyProcessed"), null, 'warnings'); } else { // Build invoices array with amounts require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $invoices = array(); foreach ($invoiceIds as $invId) { $invId = (int) $invId; if ($invId > 0) { $invoice = new FactureFournisseur($db); if ($invoice->fetch($invId) > 0) { $alreadyPaid = $invoice->getSommePaiement(); $creditnotes = $invoice->getSumCreditNotesUsed(); $deposits = $invoice->getSumDepositsUsed(); $remainToPay = price2num($invoice->total_ttc - $alreadyPaid - $creditnotes - $deposits, 'MT'); if ($remainToPay > 0) { $invoices[] = array( 'type' => 'facture_fourn', 'id' => $invId, 'ref' => $invoice->ref, 'amount' => $remainToPay ); } } } } if (!empty($invoices)) { $result = $object->confirmMultiplePayment($user, $invoices, $bankAccountId); if ($result > 0) { setEventMessages($langs->trans("PaymentCreatedSuccessfully", dol_escape_htmltag($object->name), price(abs($object->amount))).' ('.count($invoices).' '.$langs->trans("Invoices").')', null, 'mesgs'); unset($_SESSION['bankimport_matches_'.$object->id]); } else { setEventMessages($object->error, $object->errors, 'errors'); } } else { setEventMessages($langs->trans("NoInvoicesSelected"), null, 'errors'); } } header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } } // Link existing payment to bank entry (for already paid invoices) if ($action == 'linkpayment' && $object->id > 0 && !empty($bankAccountId)) { $invoiceType = GETPOST('invoicetype', 'alpha'); $invoiceId = GETPOSTINT('invoiceid'); if ($invoiceType && $invoiceId > 0) { if ($object->status != BankImportTransaction::STATUS_NEW) { setEventMessages($langs->trans("TransactionAlreadyProcessed"), null, 'warnings'); } else { $result = $object->linkExistingPayment($user, $invoiceType, $invoiceId, $bankAccountId); if ($result > 0) { setEventMessages($langs->trans("PaymentLinkedSuccessfully"), null, 'mesgs'); } else { setEventMessages($object->error, $object->errors, 'errors'); } } header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } } // Link multiple existing payments to bank entry (for already paid invoices) if ($action == 'linkpaymentmulti' && $object->id > 0 && !empty($bankAccountId)) { $paidInvoiceIds = GETPOST('paid_invoice', 'array'); $paidInvoiceType = GETPOST('paid_invoice_type', 'alpha'); if (!empty($paidInvoiceIds) && !empty($paidInvoiceType)) { if ($object->status != BankImportTransaction::STATUS_NEW) { setEventMessages($langs->trans("TransactionAlreadyProcessed"), null, 'warnings'); } else { // Build invoices array $invoices = array(); foreach ($paidInvoiceIds as $invId) { $invoices[] = array( 'type' => $paidInvoiceType, 'id' => (int) $invId ); } $result = $object->linkMultipleExistingPayments($user, $invoices, $bankAccountId); if ($result > 0) { setEventMessages($langs->trans("PaymentLinkedSuccessfully"), null, 'mesgs'); } else { setEventMessages($object->error, $object->errors, 'errors'); } } header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } else { setEventMessages($langs->trans("NoInvoicesSelected"), null, 'warnings'); } } // Search for invoice manually if ($action == 'searchinvoice' && $object->id > 0) { $searchInvoiceRef = GETPOST('search_invoice_ref', 'alpha'); $searchInvoiceType = GETPOST('search_invoice_type', 'alpha'); // 'customer' or 'supplier' if (!empty($searchInvoiceRef)) { $manualMatches = array(); if ($searchInvoiceType == 'customer' || empty($searchInvoiceType)) { // Search customer invoices require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; $sql = "SELECT f.rowid, f.ref, f.ref_client, f.total_ttc, f.date_lim_reglement, s.nom as socname, s.rowid as socid"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid"; $sql .= " WHERE f.entity = ".$conf->entity; $sql .= " AND f.fk_statut = 1"; // Unpaid $sql .= " AND (f.ref LIKE '%".$db->escape($searchInvoiceRef)."%' OR f.ref_client LIKE '%".$db->escape($searchInvoiceRef)."%')"; $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { $manualMatches[] = array( 'type' => 'facture', 'id' => $obj->rowid, 'ref' => $obj->ref, 'ref_client' => $obj->ref_client, 'amount' => $obj->total_ttc, 'socname' => $obj->socname, 'socid' => $obj->socid, 'match_score' => 0, 'match_reasons' => array('manual'), 'date_due' => $obj->date_lim_reglement ); } } } if ($searchInvoiceType == 'supplier' || empty($searchInvoiceType)) { // Search supplier invoices require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $sql = "SELECT f.rowid, f.ref, f.ref_supplier, f.total_ttc, f.date_lim_reglement, s.nom as socname, s.rowid as socid"; $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid"; $sql .= " WHERE f.entity = ".$conf->entity; $sql .= " AND f.fk_statut = 1"; // Unpaid $sql .= " AND (f.ref LIKE '%".$db->escape($searchInvoiceRef)."%' OR f.ref_supplier LIKE '%".$db->escape($searchInvoiceRef)."%')"; $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { $manualMatches[] = array( 'type' => 'facture_fourn', 'id' => $obj->rowid, 'ref' => $obj->ref, 'ref_supplier' => $obj->ref_supplier, 'amount' => $obj->total_ttc, 'socname' => $obj->socname, 'socid' => $obj->socid, 'match_score' => 0, 'match_reasons' => array('manual'), 'date_due' => $obj->date_lim_reglement ); } } } if (!empty($manualMatches)) { $_SESSION['bankimport_matches_'.$object->id] = $manualMatches; setEventMessages($langs->trans("MatchesFound", count($manualMatches)), null, 'mesgs'); } else { setEventMessages($langs->trans("NoMatchesFound"), null, 'warnings'); } } } // Create various payment (for transactions without invoices) if ($action == 'confirmcreatevarious' && $object->id > 0 && $object->status == BankImportTransaction::STATUS_NEW) { $accountancyCode = GETPOST('accountancy_code', 'alpha'); $subledgerAccount = GETPOST('subledger_account', 'alpha'); $sens = GETPOSTINT('sens'); $variousLabel = GETPOST('various_label', 'alphanohtml'); if (empty($accountancyCode)) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("AccountingAccount")), null, 'errors'); } else { $bankAccountId = getDolGlobalInt('BANKIMPORT_BANK_ACCOUNT_ID'); if (empty($bankAccountId)) { setEventMessages($langs->trans("ErrorNoBankAccountConfigured"), null, 'errors'); } else { $result = $object->createVariousPayment($user, $bankAccountId, $accountancyCode, $sens, $variousLabel, $subledgerAccount); if ($result > 0) { setEventMessages($langs->trans("VariousPaymentCreated"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); exit; } else { setEventMessages($object->error, null, 'errors'); } } } } /* * View */ $form = new Form($db); $title = $langs->trans("Transaction").' - '.$object->ref; llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-bankimport page-card'); if ($object->id > 0) { print '
'; // Title print load_fiche_titre($langs->trans("Transaction"), '', 'bank'); // Card header print '
'; print ''; // Reference print ''; print ''; print ''; print ''; // IBAN print ''; print ''; print ''; print ''; // Date print ''; print ''; print ''; print ''; // Value date if ($object->date_value) { print ''; print ''; print ''; print ''; } // Name print ''; print ''; print ''; print ''; // Counterparty IBAN if ($object->counterparty_iban) { print ''; print ''; print ''; print ''; } // Amount print ''; print ''; print ''; print ''; // Label if ($object->label) { print ''; print ''; print ''; print ''; } // Description print ''; print ''; print ''; print ''; // End-to-End ID if ($object->end_to_end_id) { print ''; print ''; print ''; print ''; } // Mandate ID if ($object->mandate_id) { print ''; print ''; print ''; print ''; } // Status print ''; print ''; print ''; print ''; // Linked payment and invoices (for matched transactions) if ($object->status == BankImportTransaction::STATUS_MATCHED || $object->status == BankImportTransaction::STATUS_RECONCILED) { // Customer payment if ($object->fk_paiement > 0) { require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; $payment = new Paiement($db); $payment->fetch($object->fk_paiement); print ''; print ''; print ''; print ''; // Find all invoices linked to this payment $sql = "SELECT pf.fk_facture, pf.amount, f.ref, f.total_ttc"; $sql .= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON pf.fk_facture = f.rowid"; $sql .= " WHERE pf.fk_paiement = ".((int) $object->fk_paiement); $resql = $db->query($sql); if ($resql && $db->num_rows($resql) > 0) { print ''; print ''; print ''; print ''; } } // Supplier payment if ($object->fk_paiementfourn > 0) { require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $payment = new PaiementFourn($db); $payment->fetch($object->fk_paiementfourn); print ''; print ''; print ''; print ''; // Find all invoices linked to this payment $sql = "SELECT pf.fk_facturefourn, pf.amount, f.ref, f.ref_supplier, f.total_ttc"; $sql .= " FROM ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as f ON pf.fk_facturefourn = f.rowid"; $sql .= " WHERE pf.fk_paiementfourn = ".((int) $object->fk_paiementfourn); $resql = $db->query($sql); $invoicesFound = false; if ($resql && $db->num_rows($resql) > 0) { $invoicesFound = true; print ''; print ''; print ''; print ''; } // Also check note_private for multi-invoice links (format: "Multi-invoice link: SI2602-0146, SI2602-0147") if (!$invoicesFound && !empty($object->note_private) && strpos($object->note_private, 'Multi-invoice link:') !== false) { if (preg_match('/Multi-invoice link:\s*(.+)$/m', $object->note_private, $matches)) { $invoiceRefs = array_map('trim', explode(',', $matches[1])); print ''; print ''; print ''; print ''; $invoicesFound = true; } } // Fallback: Show linked invoice from transaction if (!$invoicesFound && $object->fk_facture_fourn > 0) { $inv = new FactureFournisseur($db); $inv->fetch($object->fk_facture_fourn); print ''; print ''; print ''; print ''; } } // Various payment (no invoice, linked via bank_url) if (empty($object->fk_paiement) && empty($object->fk_paiementfourn) && $object->fk_bank > 0) { $sql = "SELECT url_id FROM ".MAIN_DB_PREFIX."bank_url WHERE fk_bank = ".((int) $object->fk_bank)." AND type = 'payment_various'"; $resql = $db->query($sql); if ($resql && $db->num_rows($resql) > 0) { $obj = $db->fetch_object($resql); require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php'; $various = new PaymentVarious($db); $various->fetch($obj->url_id); print ''; print ''; print ''; print ''; if (!empty($various->accountancy_code)) { print ''; print ''; print ''; print ''; } } } // If no payment link but invoice link exists (edge case) if (empty($object->fk_paiement) && empty($object->fk_paiementfourn)) { if ($object->fk_facture_fourn > 0) { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $inv = new FactureFournisseur($db); $inv->fetch($object->fk_facture_fourn); print ''; print ''; print ''; print ''; } if ($object->fk_facture > 0) { require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; $inv = new Facture($db); $inv->fetch($object->fk_facture); print ''; print ''; print ''; print ''; } } // Bank entry if ($object->fk_bank > 0) { require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; $bankline = new AccountLine($db); $bankline->fetch($object->fk_bank); print ''; print ''; print ''; print ''; } } else { // For non-matched transactions, show simple links if they exist // Linked invoice if ($object->fk_facture > 0) { require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; $invoice = new Facture($db); $invoice->fetch($object->fk_facture); print ''; print ''; print ''; print ''; } // Linked supplier invoice if ($object->fk_facture_fourn > 0) { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $invoice = new FactureFournisseur($db); $invoice->fetch($object->fk_facture_fourn); print ''; print ''; print ''; print ''; } } // Linked PDF statement if (!empty($object->fk_statement)) { dol_include_once('/bankimport/class/bankstatement.class.php'); $stmt = new BankImportStatement($db); $stmt->fetch($object->fk_statement); print ''; print ''; print ''; print ''; } // Linked third party if ($object->fk_societe > 0) { require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; $soc = new Societe($db); $soc->fetch($object->fk_societe); print ''; print ''; print ''; print ''; } // Import date print ''; print ''; print ''; print ''; print '
'.$langs->trans("Ref").''.dol_escape_htmltag($object->ref).'
'.$langs->trans("AccountIBAN").''.dol_escape_htmltag($object->iban).'
'.$langs->trans("Date").''.dol_print_date($object->date_trans, 'day').'
'.$langs->trans("DateValue").''.dol_print_date($object->date_value, 'day').'
'.$langs->trans("Counterparty").''.dol_escape_htmltag($object->name).'
'.$langs->trans("CounterpartyIBAN").''.dol_escape_htmltag($object->counterparty_iban).'
'.$langs->trans("Amount").''; if ($object->amount >= 0) { print '+'.price($object->amount, 0, $langs, 1, -1, 2, $object->currency).''; } else { print ''.price($object->amount, 0, $langs, 1, -1, 2, $object->currency).''; } print '
'.$langs->trans("Label").''.dol_escape_htmltag($object->label).'
'.$langs->trans("Description").''.nl2br(dol_escape_htmltag($object->description)).'
'.$langs->trans("EndToEndId").''.dol_escape_htmltag($object->end_to_end_id).'
'.$langs->trans("MandateId").''.dol_escape_htmltag($object->mandate_id).'
'.$langs->trans("Status").''.$object->getLibStatut(4).'
'.$langs->trans("Payment").''.$payment->getNomUrl(1).' ('.dol_print_date($payment->datepaye, 'day').')
'.$langs->trans("Invoices").''; while ($obj = $db->fetch_object($resql)) { $inv = new Facture($db); $inv->fetch($obj->fk_facture); print $inv->getNomUrl(1); print ' ('.price($obj->amount, 0, $langs, 1, -1, 2, 'EUR').')'; print '
'; } print '
'.$langs->trans("Payment").''.$payment->getNomUrl(1).' ('.dol_print_date($payment->datepaye, 'day').')
'.$langs->trans("SupplierInvoices").''; while ($obj = $db->fetch_object($resql)) { $inv = new FactureFournisseur($db); $inv->fetch($obj->fk_facturefourn); print $inv->getNomUrl(1); if (!empty($obj->ref_supplier)) { print ' ('.$obj->ref_supplier.')'; } print ' ('.price($obj->amount, 0, $langs, 1, -1, 2, 'EUR').')'; print '
'; } print '
'.$langs->trans("SupplierInvoices").''; foreach ($invoiceRefs as $invRef) { // Try to find invoice by ref $sql2 = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture_fourn WHERE ref = '".$db->escape($invRef)."' AND entity = ".$conf->entity; $resql2 = $db->query($sql2); if ($resql2 && $db->num_rows($resql2) > 0) { $obj2 = $db->fetch_object($resql2); $inv = new FactureFournisseur($db); $inv->fetch($obj2->rowid); print $inv->getNomUrl(1); if (!empty($inv->ref_supplier)) { print ' ('.$inv->ref_supplier.')'; } } else { print dol_escape_htmltag($invRef); } print '
'; } print '
'.$langs->trans("SupplierInvoice").''.$inv->getNomUrl(1); if (!empty($inv->ref_supplier)) { print ' ('.$inv->ref_supplier.')'; } print '
'.$langs->trans("Payment").''; print ''; print img_picto('', 'payment', 'class="pictofixedwidth"'); print $langs->trans("VariousPayment").' #'.$various->id; print ''; print ' ('.dol_print_date($various->datep, 'day').' - '.price($various->amount, 0, $langs, 1, -1, 2, 'EUR').')'; print '
'.$langs->trans("AccountingAccount").''.dol_escape_htmltag($various->accountancy_code).'
'.$langs->trans("SupplierInvoice").''.$inv->getNomUrl(1); if (!empty($inv->ref_supplier)) { print ' ('.$inv->ref_supplier.')'; } print '
'.$langs->trans("Invoice").''.$inv->getNomUrl(1).'
'.$langs->trans("BankEntry").''; print ''; print img_picto('', 'bank_account', 'class="pictofixedwidth"'); print $bankline->ref ?: '#'.$object->fk_bank; print ''; print ' ('.dol_print_date($bankline->dateo, 'day').' - '.price($bankline->amount, 0, $langs, 1, -1, 2, 'EUR').')'; print '
'.$langs->trans("Invoice").''.$invoice->getNomUrl(1).'
'.$langs->trans("SupplierInvoice").''.$invoice->getNomUrl(1).'
'.$langs->trans("PDFStatement").''; print ''; print img_picto($langs->trans("ViewPDFStatement"), 'pdf').' '; print $langs->trans("StatementNumber").' '.$stmt->statement_number.'/'.$stmt->statement_year; print ''; if ($stmt->date_from && $stmt->date_to) { print ' ('.dol_print_date($stmt->date_from, 'day').' - '.dol_print_date($stmt->date_to, 'day').')'; } print '
'.$langs->trans("ThirdParty").''.$soc->getNomUrl(1).'
'.$langs->trans("DateCreation").''.dol_print_date($object->datec, 'dayhour').'
'; print '
'; // Actions buttons print '
'; if ($object->status == BankImportTransaction::STATUS_NEW) { // Find matches button print ''.$langs->trans("FindMatches").''; // Create various payment button print ''.$langs->trans("CreateVariousPayment").''; // Set as ignored print ''.$langs->trans("SetAsIgnored").''; } if ($object->status == BankImportTransaction::STATUS_MATCHED) { // Edit/Unlink - allows correcting wrong matches print ''.$langs->trans("UnlinkPayment").''; } if ($object->status == BankImportTransaction::STATUS_IGNORED) { // Reopen print ''.$langs->trans("Reopen").''; } print '
'; // Various payment inline form if ($action == 'createvarious' && $object->status == BankImportTransaction::STATUS_NEW) { // Auto-detect sens from amount sign: positive = credit (1), negative = debit (0) $defaultSens = ($object->amount >= 0) ? 1 : 0; $defaultLabel = $object->name.' - '.dol_trunc($object->description, 80); require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; $formaccounting = new FormAccounting($db); print '
'; print load_fiche_titre($langs->trans("CreateVariousPayment"), '', 'object_payment'); print '
'; print ''; print ''; print ''; print ''; // Amount (read-only) print ''; print ''; print ''; print ''; // Date (read-only) print ''; print ''; print ''; print ''; // Counterparty (read-only) print ''; print ''; print ''; print ''; // Accounting account (Select2 search) print ''; print ''; print ''; print ''; // Subledger account (Select2 search) print ''; print ''; print ''; print ''; // Debit/Credit (auto-selected) print ''; print ''; print ''; print ''; // Label (editable, pre-filled) print ''; print ''; print ''; print ''; print '
'.$langs->trans("Amount").''; $amountColor = ($object->amount >= 0) ? 'green' : 'red'; print ''.price($object->amount, 0, $langs, 1, -1, 2, 'EUR').''; print '
'.$langs->trans("Date").''.dol_print_date($object->date_trans, 'day').'
'.$langs->trans("Counterparty").''.dol_escape_htmltag($object->name).'
'.$langs->trans("AccountingAccount").''; print $formaccounting->select_account(GETPOST('accountancy_code', 'alpha'), 'accountancy_code', 1, array(), 0, 0, 'minwidth200 maxwidth500', '', 1); print '
'.$langs->trans("SubledgerAccount").''; print $formaccounting->select_auxaccount(GETPOST('subledger_account', 'alpha'), 'subledger_account', 1, 'minwidth200 maxwidth500'); print '
'.$langs->trans("DebitCredit").''; $sensValue = GETPOSTISSET('sens') ? GETPOSTINT('sens') : $defaultSens; print ''; print '
'.$langs->trans("Label").''; $labelValue = GETPOSTISSET('various_label') ? GETPOST('various_label', 'alphanohtml') : $defaultLabel; print ''; print '
'; print '
'; print ''; print '   '; print ''.$langs->trans("Cancel").''; print '
'; print '
'; } // Manual invoice selection (only for new transactions) if ($object->status == BankImportTransaction::STATUS_NEW) { // Determine if this is likely a supplier payment (negative amount) or customer (positive) $defaultType = ($object->amount < 0) ? 'supplier' : 'customer'; $selectedType = GETPOST('invoice_type', 'alpha') ?: $defaultType; $searchFilter = GETPOST('search_filter', 'alpha'); $showPaid = GETPOSTINT('show_paid'); print '
'; print load_fiche_titre($langs->trans("SelectInvoicesManually"), '', 'object_invoice'); // Type selector and search filter print '
'; print ''; print '
'; print ''; print ''; print '
'; print '
'; print ''; print '
'; print '
'; print ''; print ''; print ' '; if (!empty($searchFilter)) { print ' '.$langs->trans("RemoveFilter").''; } print '
'; print '
'; // Fetch invoices $invoiceList = array(); $absAmount = abs($object->amount); if ($selectedType == 'supplier') { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $sql = "SELECT f.rowid, f.ref, f.ref_supplier, f.total_ttc, f.datef, f.date_lim_reglement, f.fk_statut,"; $sql .= " s.nom as socname, s.rowid as socid"; $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid"; $sql .= " WHERE f.entity = ".$conf->entity; if ($showPaid) { $sql .= " AND f.fk_statut IN (1, 2)"; // 1=Unpaid, 2=Paid } else { $sql .= " AND f.fk_statut = 1"; // Unpaid only } if (!empty($searchFilter)) { $sql .= " AND (f.ref LIKE '%".$db->escape($searchFilter)."%'"; $sql .= " OR f.ref_supplier LIKE '%".$db->escape($searchFilter)."%'"; $sql .= " OR s.nom LIKE '%".$db->escape($searchFilter)."%')"; } $sql .= " ORDER BY f.datef DESC"; $sql .= " LIMIT 100"; $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { $inv = new FactureFournisseur($db); $inv->fetch($obj->rowid); $alreadyPaid = $inv->getSommePaiement(); $creditnotes = $inv->getSumCreditNotesUsed(); $deposits = $inv->getSumDepositsUsed(); $remainToPay = price2num($inv->total_ttc - $alreadyPaid - $creditnotes - $deposits, 'MT'); $isPaid = ($obj->fk_statut == 2); // For paid invoices, check if already linked via BankImport transaction $isLinkedViaBankImport = false; if ($isPaid) { $sqlBi = "SELECT rowid FROM ".MAIN_DB_PREFIX."bankimport_transaction"; $sqlBi .= " WHERE fk_facture_fourn = ".((int) $obj->rowid); $sqlBi .= " AND status > 0"; $resqlBi = $db->query($sqlBi); if ($resqlBi && $db->num_rows($resqlBi) > 0) { $isLinkedViaBankImport = true; } } // Show if unpaid OR if paid but not yet linked via BankImport if ($remainToPay > 0 || ($isPaid && !$isLinkedViaBankImport)) { $invoiceList[] = array( 'id' => $obj->rowid, 'ref' => $obj->ref, 'ref_supplier' => $obj->ref_supplier, 'amount' => $isPaid ? $obj->total_ttc : $remainToPay, 'total_ttc' => $obj->total_ttc, 'socname' => $obj->socname, 'socid' => $obj->socid, 'datef' => $obj->datef, 'date_due' => $obj->date_lim_reglement, 'type' => 'facture_fourn', 'object' => $inv, 'is_paid' => $isPaid ); } } } } else { require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; $sql = "SELECT f.rowid, f.ref, f.ref_client, f.total_ttc, f.datef, f.date_lim_reglement, f.fk_statut,"; $sql .= " s.nom as socname, s.rowid as socid"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid"; $sql .= " WHERE f.entity = ".$conf->entity; if ($showPaid) { $sql .= " AND f.fk_statut IN (1, 2)"; // 1=Unpaid, 2=Paid } else { $sql .= " AND f.fk_statut = 1"; // Unpaid only } if (!empty($searchFilter)) { $sql .= " AND (f.ref LIKE '%".$db->escape($searchFilter)."%'"; $sql .= " OR f.ref_client LIKE '%".$db->escape($searchFilter)."%'"; $sql .= " OR s.nom LIKE '%".$db->escape($searchFilter)."%')"; } $sql .= " ORDER BY f.datef DESC"; $sql .= " LIMIT 100"; $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { $inv = new Facture($db); $inv->fetch($obj->rowid); $alreadyPaid = $inv->getSommePaiement(); $creditnotes = $inv->getSumCreditNotesUsed(); $deposits = $inv->getSumDepositsUsed(); $remainToPay = price2num($inv->total_ttc - $alreadyPaid - $creditnotes - $deposits, 'MT'); $isPaid = ($obj->fk_statut == 2); // For paid invoices, check if already linked via BankImport transaction $isLinkedViaBankImport = false; if ($isPaid) { $sqlBi = "SELECT rowid FROM ".MAIN_DB_PREFIX."bankimport_transaction"; $sqlBi .= " WHERE fk_facture = ".((int) $obj->rowid); $sqlBi .= " AND status > 0"; $resqlBi = $db->query($sqlBi); if ($resqlBi && $db->num_rows($resqlBi) > 0) { $isLinkedViaBankImport = true; } } // Show if unpaid OR if paid but not yet linked via BankImport if ($remainToPay > 0 || ($isPaid && !$isLinkedViaBankImport)) { $invoiceList[] = array( 'id' => $obj->rowid, 'ref' => $obj->ref, 'ref_client' => $obj->ref_client, 'amount' => $isPaid ? $obj->total_ttc : $remainToPay, 'total_ttc' => $obj->total_ttc, 'socname' => $obj->socname, 'socid' => $obj->socid, 'datef' => $obj->datef, 'date_due' => $obj->date_lim_reglement, 'type' => 'facture', 'object' => $inv, 'is_paid' => $isPaid ); } } } } // Separate paid and unpaid invoices $unpaidInvoices = array_filter($invoiceList, function($inv) { return empty($inv['is_paid']); }); $paidInvoices = array_filter($invoiceList, function($inv) { return !empty($inv['is_paid']); }); // Display unpaid invoices table with checkboxes if (!empty($unpaidInvoices)) { print '
'; print ''; print ''; print ''; // Table without div-table-responsive for compact layout print ''; // Info header row print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; foreach ($unpaidInvoices as $inv) { $amountMatch = (abs($inv['amount'] - $absAmount) < 1.00); $rowClass = $amountMatch ? 'oddeven highlight' : 'oddeven'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; } // Footer row with button print ''; print ''; print ''; print ''; print '
'; print $langs->trans("TransactionAmount").': '.price(abs($object->amount), 0, $langs, 1, -1, 2, 'EUR').''; print '  |  '.$langs->trans("Selected").': 0,00 €'; print '  |  '.$langs->trans("Difference").': '.price(abs($object->amount), 0, $langs, 1, -1, 2, 'EUR').''; print '
'.$langs->trans("Ref").''.($selectedType == 'supplier' ? $langs->trans("SupplierRef") : $langs->trans("CustomerRef")).''.$langs->trans("ThirdParty").''.$langs->trans("Date").''.$langs->trans("DateDue").''.$langs->trans("AmountRemaining").'
'; print ''; print ''.$inv['object']->getNomUrl(1).''.dol_escape_htmltag($inv['ref_supplier'] ?? $inv['ref_client'] ?? '').''.dol_escape_htmltag($inv['socname']).''.dol_print_date($inv['datef'], 'day').''.($inv['date_due'] ? dol_print_date($inv['date_due'], 'day') : '-').''.price($inv['amount'], 0, $langs, 1, -1, 2, 'EUR').'
'; if (!empty($bankAccountId)) { print ''; } else { print ''.$langs->trans("ConfirmPayment").''; } print '
'; print '
'; } // Display paid invoices table (link to existing payment) if (!empty($paidInvoices)) { print '
'; print load_fiche_titre($langs->trans("PaidInvoices"), '', 'object_invoice'); print '
'.$langs->trans("PaidInvoicesInfo").'
'; // Determine invoice type for all paid invoices (should be same type) $paidInvoiceType = $paidInvoices[0]['type'] ?? 'facture'; print ''; } if (empty($unpaidInvoices) && empty($paidInvoices)) { print '
'.$langs->trans("NoUnpaidInvoices").'
'; } // JavaScript for sum calculation (only if we have unpaid invoices) if (!empty($unpaidInvoices)) { print ''; } // JavaScript for paid invoices multi-select (only if we have paid invoices) if (!empty($paidInvoices)) { print ''; } // CSS for highlighting print ''; } // Show matches if found $matches = $_SESSION['bankimport_matches_'.$object->id] ?? array(); if (!empty($matches) && $object->status == BankImportTransaction::STATUS_NEW) { print '
'; print load_fiche_titre($langs->trans("MatchesFound", count($matches)), '', 'object_invoice'); // Translate match reasons $reasonLabels = array( 'ref' => $langs->trans("MatchByRef"), 'ref_client' => $langs->trans("MatchByClientRef"), 'ref_supplier' => $langs->trans("MatchBySupplierRef"), 'amount' => $langs->trans("MatchByAmount"), 'amount_close' => $langs->trans("MatchByAmountClose"), 'name_exact' => $langs->trans("MatchByNameExact"), 'name_similar' => $langs->trans("MatchByNameSimilar"), 'iban' => $langs->trans("MatchByIBAN"), 'multi_invoice' => $langs->trans("MatchByMultiInvoice"), 'manual' => $langs->trans("ManualSearch") ); // Check for multi-invoice matches $hasMultiMatch = false; foreach ($matches as $match) { if ($match['type'] == 'multi_facture_fourn') { $hasMultiMatch = true; break; } } // If we have multi-invoice matches, show them first with selection form if ($hasMultiMatch) { foreach ($matches as $match) { if ($match['type'] == 'multi_facture_fourn' && !empty($match['invoices'])) { print '
'; print ''.$langs->trans("MatchByMultiInvoice").': '; print count($match['invoices']).' '.$langs->trans("Invoices").' = '.price($match['total'] ?? $match['amount'], 0, $langs, 1, -1, 2, 'EUR'); if (isset($match['difference']) && abs($match['difference']) > 0.01) { print ' ('.$langs->trans("Difference").': '.price($match['difference'], 0, $langs, 1, -1, 2, 'EUR').')'; } print '
'; print '
'; print ''; print ''; print ''; print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; foreach ($match['invoices'] as $invData) { $inv = new FactureFournisseur($db); $inv->fetch($invData['id']); print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; } print '
'.$langs->trans("Ref").''.$langs->trans("SupplierRef").''.$langs->trans("ThirdParty").''.$langs->trans("Amount").''.$langs->trans("DateDue").'
'.$inv->getNomUrl(1).''.dol_escape_htmltag($invData['ref_supplier']).''.dol_escape_htmltag($invData['socname']).''.price($invData['amount'], 0, $langs, 1, -1, 2, 'EUR').''.($invData['date_due'] ? dol_print_date($invData['date_due'], 'day') : '-').'
'; print '
'; print '
'; print ''; print '
'; print '
'; print '
'; // JavaScript for toggle all print ''; } } } // Show single invoice matches $singleMatches = array_filter($matches, function($m) { return $m['type'] != 'multi_facture_fourn'; }); if (!empty($singleMatches)) { if ($hasMultiMatch) { print '
'; print load_fiche_titre($langs->trans("Alternatives"), '', ''); } print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; foreach ($singleMatches as $match) { print ''; print ''; // Get invoice link if ($match['type'] == 'facture') { require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; $inv = new Facture($db); $inv->fetch($match['id']); print ''; } else { require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; $inv = new FactureFournisseur($db); $inv->fetch($match['id']); print ''; } // Third party with link if ($match['socid'] > 0) { require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; $soc = new Societe($db); $soc->fetch($match['socid']); print ''; } else { print ''; } print ''; print ''; // Score with color $scoreColor = $match['match_score'] >= 80 ? 'green' : ($match['match_score'] >= 60 ? 'orange' : 'gray'); print ''; // Match reasons as badges print ''; // Action buttons print ''; print ''; } print '
'.$langs->trans("Type").''.$langs->trans("Ref").''.$langs->trans("ThirdParty").''.$langs->trans("Amount").''.$langs->trans("DateDue").''.$langs->trans("Score").''.$langs->trans("MatchReason").'
'.($match['type'] == 'facture' ? $langs->trans("CustomerInvoice") : $langs->trans("SupplierInvoice")).''.$inv->getNomUrl(1).''.$inv->getNomUrl(1).''.$soc->getNomUrl(1).''.dol_escape_htmltag($match['socname']).''.price($match['amount'], 0, $langs, 1, -1, 2, 'EUR').''.($match['date_due'] ? dol_print_date($match['date_due'], 'day') : '-').''.$match['match_score'].'%'; if (!empty($match['match_reasons'])) { foreach ($match['match_reasons'] as $reason) { $label = $reasonLabels[$reason] ?? $reason; print ''.$label.' '; } } print ''; if (!empty($bankAccountId)) { // Confirm payment button (creates payment in Dolibarr) print ''.$langs->trans("ConfirmPayment").''; } else { // Just link (no bank account configured) print ''.$langs->trans("Link").''; } print '
'; print '
'; } } } else { print '
'.$langs->trans("RecordNotFound").'
'; } // Back link print '
'; print ''.$langs->trans("BackToList").''; print '
'; llxFooter(); $db->close();