Version 1.4: Zahlungsabgleich, Bankkonto-Zuordnung, Dashboard-Integration

- Neue Seite confirm.php: FinTS-Buchungen mit Rechnungen abgleichen und
  Zahlungen in Dolibarr erstellen (Einzel- und Bulk-Bestätigung)
- confirmPayment() in banktransaction.class.php: Erstellt Paiement/PaiementFourn
  mit korrektem Betrag und verknüpft Bankzeile + Rechnung
- Bankkonto-Dropdown in admin/setup.php (BANKIMPORT_BANK_ACCOUNT_ID)
- Dashboard bankimportindex.php: Hinweis auf offene Zuordnungen + Warnung
  wenn kein Bankkonto konfiguriert
- Menüeintrag "Zahlungsabgleich" unter Bankimport

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-02-15 18:23:59 +01:00
parent be8a02e88e
commit c1c06e19ae
5 changed files with 625 additions and 2 deletions

View file

@ -199,6 +199,12 @@ if ($action == 'update') {
$error++;
}
// Dolibarr Bank Account mapping
$res = dolibarr_set_const($db, "BANKIMPORT_BANK_ACCOUNT_ID", GETPOSTINT('BANKIMPORT_BANK_ACCOUNT_ID'), 'chaine', 0, '', $conf->entity);
if (!($res > 0)) {
$error++;
}
// Reminder setting
$res = dolibarr_set_const($db, "BANKIMPORT_REMINDER_ENABLED", GETPOSTINT('BANKIMPORT_REMINDER_ENABLED'), 'chaine', 0, '', $conf->entity);
if (!($res > 0)) {
@ -433,6 +439,41 @@ print '</tr>';
print '</table>';
// Bank Account Mapping Section
print '<br>';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td colspan="2">'.$langs->trans("BankAccountMapping").'</td>';
print '</tr>';
// Bank Account Dropdown
$bankAccountId = getDolGlobalInt('BANKIMPORT_BANK_ACCOUNT_ID');
print '<tr class="oddeven">';
print '<td class="titlefield fieldrequired">'.$langs->trans("DolibarrBankAccount").'</td>';
print '<td>';
// Build select from llx_bank_account
$sql_ba = "SELECT rowid, label, iban_prefix FROM ".MAIN_DB_PREFIX."bank_account";
$sql_ba .= " WHERE entity = ".((int) $conf->entity);
$sql_ba .= " AND clos = 0";
$sql_ba .= " ORDER BY label ASC";
$bankAccounts = array('' => $langs->trans("SelectBankAccount"));
$resql_ba = $db->query($sql_ba);
if ($resql_ba) {
while ($obj_ba = $db->fetch_object($resql_ba)) {
$ibanDisplay = $obj_ba->iban_prefix ? ' ('.dol_trunc($obj_ba->iban_prefix, 20).')' : '';
$bankAccounts[$obj_ba->rowid] = $obj_ba->label.$ibanDisplay;
}
}
print $form->selectarray('BANKIMPORT_BANK_ACCOUNT_ID', $bankAccounts, $bankAccountId, 0, 0, 0, '', 0, 0, 0, '', 'minwidth300');
print '<br><span class="opacitymedium small">'.$langs->trans("DolibarrBankAccountHelp").'</span>';
print '</td>';
print '</tr>';
print '</table>';
// Automatic Import Section
print '<br>';
print '<table class="noborder centpercent">';

View file

@ -113,6 +113,36 @@ if ($reminderEnabled) {
}
}
// Payment matching notification
$bankAccountId = getDolGlobalInt('BANKIMPORT_BANK_ACCOUNT_ID');
if (!empty($bankAccountId)) {
$sqlNewCount = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX."bankimport_transaction";
$sqlNewCount .= " WHERE entity IN (".getEntity('banktransaction').")";
$sqlNewCount .= " AND status = 0";
$resNewCount = $db->query($sqlNewCount);
$newCount = 0;
if ($resNewCount) {
$objNewCount = $db->fetch_object($resNewCount);
$newCount = (int) $objNewCount->cnt;
}
if ($newCount > 0) {
print '<div class="info" style="border-left: 4px solid #2196F3; background: #e3f2fd; padding: 12px; margin-bottom: 15px;">';
print img_picto('', 'payment', 'class="pictofixedwidth"');
print '<strong>'.$langs->trans("PendingPaymentMatches", $newCount).'</strong>';
print '<br>'.$langs->trans("PendingPaymentMatchesDesc");
print ' <a class="butAction" style="margin-left: 10px;" href="'.dol_buildpath('/bankimport/confirm.php', 1).'?mainmenu=bank&leftmenu=bankimport">';
print $langs->trans("ReviewAndConfirm");
print '</a>';
print '</div>';
}
} else {
print '<div class="warning" style="margin-bottom: 15px;">';
print img_warning().' '.$langs->trans("NoBankAccountConfigured");
print ' <a href="'.dol_buildpath('/bankimport/admin/setup.php', 1).'">'.$langs->trans("GoToSetup").'</a>';
print '</div>';
}
print '<div class="fichecenter"><div class="fichethirdleft">';
// -----------------------------------------------

View file

@ -1019,6 +1019,193 @@ class BankImportTransaction extends CommonObject
return 0;
}
/**
* Confirm payment: create Paiement or PaiementFourn in Dolibarr
* Links the bankimport transaction to the created payment and bank line.
*
* @param User $user User performing the action
* @param string $type 'facture' or 'facture_fourn'
* @param int $invoiceId Invoice ID
* @param int $bankAccountId Dolibarr bank account ID (llx_bank_account.rowid)
* @return int >0 if OK (payment ID), <0 if error
*/
public function confirmPayment($user, $type, $invoiceId, $bankAccountId)
{
global $conf, $langs;
$error = 0;
$this->db->begin();
// Look up payment type ID for 'VIR' (bank transfer)
$paiementTypeId = 0;
$sql = "SELECT id FROM ".MAIN_DB_PREFIX."c_paiement WHERE code = 'VIR' AND active = 1";
$resql = $this->db->query($sql);
if ($resql && $this->db->num_rows($resql) > 0) {
$obj = $this->db->fetch_object($resql);
$paiementTypeId = (int) $obj->id;
}
if (empty($paiementTypeId)) {
$this->error = 'Payment type VIR not found in c_paiement';
$this->db->rollback();
return -1;
}
if ($type == 'facture') {
// Customer invoice payment
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
$invoice = new Facture($this->db);
if ($invoice->fetch($invoiceId) <= 0) {
$this->error = 'Invoice not found: '.$invoiceId;
$this->db->rollback();
return -2;
}
// Calculate remaining amount
$alreadyPaid = $invoice->getSommePaiement();
$creditnotes = $invoice->getSumCreditNotesUsed();
$deposits = $invoice->getSumDepositsUsed();
$remaintopay = price2num($invoice->total_ttc - $alreadyPaid - $creditnotes - $deposits, 'MT');
if ($remaintopay <= 0) {
$this->error = $langs->trans("InvoiceAlreadyPaid");
$this->db->rollback();
return -5;
}
// Use the lesser of: transaction amount or remain to pay
$payAmount = min(abs($this->amount), $remaintopay);
$paiement = new Paiement($this->db);
$paiement->datepaye = $this->date_trans;
$paiement->amounts = array($invoiceId => $payAmount);
$paiement->multicurrency_amounts = array($invoiceId => $payAmount);
$paiement->paiementid = $paiementTypeId;
$paiement->paiementcode = 'VIR';
$paiement->num_payment = $this->end_to_end_id ?: $this->ref;
$paiement->note_private = $langs->trans("PaymentCreatedByBankImport").' - '.$this->name.' - '.dol_trunc($this->description, 100);
$paymentId = $paiement->create($user, 1);
if ($paymentId < 0) {
$this->error = $paiement->error;
$this->errors = $paiement->errors;
$error++;
}
if (!$error) {
$bankLineId = $paiement->addPaymentToBank(
$user,
'payment',
'(CustomerInvoicePayment)',
$bankAccountId,
$this->name,
''
);
if ($bankLineId > 0) {
$this->fk_paiement = $paymentId;
$this->fk_bank = $bankLineId;
$this->fk_facture = $invoiceId;
$this->fk_societe = $invoice->socid;
$this->status = self::STATUS_MATCHED;
$this->fk_user_match = $user->id;
$this->date_match = dol_now();
$this->update($user);
} else {
$this->error = 'Failed to add payment to bank';
$error++;
}
}
if ($error) {
$this->db->rollback();
return -3;
}
$this->db->commit();
return $paymentId;
} elseif ($type == 'facture_fourn') {
// Supplier invoice payment
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php';
$invoice = new FactureFournisseur($this->db);
if ($invoice->fetch($invoiceId) <= 0) {
$this->error = 'Supplier invoice not found: '.$invoiceId;
$this->db->rollback();
return -2;
}
$alreadyPaid = $invoice->getSommePaiement();
$creditnotes = $invoice->getSumCreditNotesUsed();
$deposits = $invoice->getSumDepositsUsed();
$remaintopay = price2num($invoice->total_ttc - $alreadyPaid - $creditnotes - $deposits, 'MT');
if ($remaintopay <= 0) {
$this->error = $langs->trans("InvoiceAlreadyPaid");
$this->db->rollback();
return -5;
}
$payAmount = min(abs($this->amount), $remaintopay);
$paiementfourn = new PaiementFourn($this->db);
$paiementfourn->datepaye = $this->date_trans;
$paiementfourn->amounts = array($invoiceId => $payAmount);
$paiementfourn->multicurrency_amounts = array($invoiceId => $payAmount);
$paiementfourn->paiementid = $paiementTypeId;
$paiementfourn->paiementcode = 'VIR';
$paiementfourn->num_payment = $this->end_to_end_id ?: $this->ref;
$paiementfourn->note_private = $langs->trans("PaymentCreatedByBankImport").' - '.$this->name.' - '.dol_trunc($this->description, 100);
$paymentId = $paiementfourn->create($user, 1);
if ($paymentId < 0) {
$this->error = $paiementfourn->error;
$this->errors = $paiementfourn->errors;
$error++;
}
if (!$error) {
$bankLineId = $paiementfourn->addPaymentToBank(
$user,
'payment_supplier',
'(SupplierInvoicePayment)',
$bankAccountId,
$this->name,
''
);
if ($bankLineId > 0) {
$this->fk_paiementfourn = $paymentId;
$this->fk_bank = $bankLineId;
$this->fk_facture_fourn = $invoiceId;
$this->fk_societe = $invoice->socid;
$this->status = self::STATUS_MATCHED;
$this->fk_user_match = $user->id;
$this->date_match = dol_now();
$this->update($user);
} else {
$this->error = 'Failed to add payment to bank';
$error++;
}
}
if ($error) {
$this->db->rollback();
return -3;
}
$this->db->commit();
return $paymentId;
}
$this->error = 'Unknown type: '.$type;
$this->db->rollback();
return -4;
}
/**
* Link transaction to a Dolibarr object
*

350
confirm.php Normal file
View file

@ -0,0 +1,350 @@
<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
*
* 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/confirm.php
* \ingroup bankimport
* \brief Payment confirmation page - match bank transactions to invoices
*/
// 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"));
$action = GETPOST('action', 'aZ09');
// Security check
if (!$user->hasRight('bankimport', 'write')) {
accessforbidden();
}
$bankAccountId = getDolGlobalInt('BANKIMPORT_BANK_ACCOUNT_ID');
/*
* Actions
*/
// Confirm single payment
if ($action == 'confirmpayment' && !empty($bankAccountId)) {
$transid = GETPOSTINT('transid');
$matchtype = GETPOST('matchtype', 'alpha');
$matchid = GETPOSTINT('matchid');
if ($transid > 0 && !empty($matchtype) && $matchid > 0) {
$trans = new BankImportTransaction($db);
if ($trans->fetch($transid) > 0) {
if ($trans->status != BankImportTransaction::STATUS_NEW) {
setEventMessages($langs->trans("TransactionAlreadyProcessed"), null, 'warnings');
} else {
$result = $trans->confirmPayment($user, $matchtype, $matchid, $bankAccountId);
if ($result > 0) {
setEventMessages($langs->trans("PaymentCreatedSuccessfully", dol_escape_htmltag($trans->name), price(abs($trans->amount))), null, 'mesgs');
} else {
setEventMessages($trans->error, $trans->errors, 'errors');
}
}
}
}
header("Location: ".$_SERVER["PHP_SELF"]."?token=".newToken());
exit;
}
// Confirm all high-score matches
if ($action == 'confirmall' && !empty($bankAccountId)) {
$transaction = new BankImportTransaction($db);
$transactions = $transaction->fetchAll('date_trans', 'DESC', 0, 0, array('status' => BankImportTransaction::STATUS_NEW));
$created = 0;
$failed = 0;
if (is_array($transactions)) {
foreach ($transactions as $trans) {
$matches = $trans->findMatches();
if (!empty($matches) && $matches[0]['match_score'] >= 80) {
$bestMatch = $matches[0];
$result = $trans->confirmPayment($user, $bestMatch['type'], $bestMatch['id'], $bankAccountId);
if ($result > 0) {
$created++;
} else {
$failed++;
}
}
}
}
if ($created > 0 || $failed > 0) {
setEventMessages($langs->trans("PaymentsCreatedSummary", $created, $failed), null, $failed > 0 ? 'warnings' : 'mesgs');
} else {
setEventMessages($langs->trans("NoNewMatchesFound"), null, 'warnings');
}
header("Location: ".$_SERVER["PHP_SELF"]."?token=".newToken());
exit;
}
// Ignore transaction
if ($action == 'ignore') {
$transid = GETPOSTINT('transid');
if ($transid > 0) {
$trans = new BankImportTransaction($db);
if ($trans->fetch($transid) > 0 && $trans->status == BankImportTransaction::STATUS_NEW) {
$trans->setStatus(BankImportTransaction::STATUS_IGNORED, $user);
setEventMessages($langs->trans("StatusUpdated"), null, 'mesgs');
}
}
header("Location: ".$_SERVER["PHP_SELF"]."?token=".newToken());
exit;
}
/*
* View
*/
$form = new Form($db);
$title = $langs->trans("PaymentConfirmation");
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-bankimport page-confirm');
print load_fiche_titre($title, '', 'bank');
// Check if bank account is configured
if (empty($bankAccountId)) {
print '<div class="warning">';
print img_warning().' '.$langs->trans("ErrorNoBankAccountConfigured");
print ' <a href="'.dol_buildpath('/bankimport/admin/setup.php', 1).'">'.$langs->trans("GoToSetup").'</a>';
print '</div>';
llxFooter();
$db->close();
exit;
}
// Description
print '<div class="opacitymedium" style="margin-bottom: 15px;">'.$langs->trans("PaymentConfirmationDesc").'</div>';
// Fetch all new transactions and find matches
$transaction = new BankImportTransaction($db);
$transactions = $transaction->fetchAll('date_trans', 'DESC', 0, 0, array('status' => BankImportTransaction::STATUS_NEW));
$pendingMatches = array(); // transactions with matches
$noMatches = array(); // transactions without matches
if (is_array($transactions)) {
foreach ($transactions as $trans) {
$matches = $trans->findMatches();
if (!empty($matches)) {
$pendingMatches[] = array('transaction' => $trans, 'matches' => $matches);
} else {
$noMatches[] = $trans;
}
}
}
// Confirm all button (if high-score matches exist)
$highScoreCount = 0;
foreach ($pendingMatches as $pm) {
if ($pm['matches'][0]['match_score'] >= 80) {
$highScoreCount++;
}
}
if ($highScoreCount > 0) {
print '<div class="tabsAction">';
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=confirmall&token='.newToken().'">';
print $langs->trans("ConfirmAllHighScore").' ('.$highScoreCount.')';
print '</a>';
print '</div>';
}
// Match reasons translation
$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")
);
if (!empty($pendingMatches)) {
print '<div class="div-table-responsive">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<th>'.$langs->trans("Date").'</th>';
print '<th>'.$langs->trans("Counterparty").'</th>';
print '<th class="right">'.$langs->trans("Amount").' ('.$langs->trans("Transaction").')</th>';
print '<th style="text-align: center; width: 30px;"></th>';
print '<th>'.$langs->trans("Invoice").'</th>';
print '<th>'.$langs->trans("ThirdParty").'</th>';
print '<th class="right">'.$langs->trans("Amount").' ('.$langs->trans("Invoice").')</th>';
print '<th class="center">'.$langs->trans("Score").'</th>';
print '<th>'.$langs->trans("MatchReason").'</th>';
print '<th class="center">'.$langs->trans("Action").'</th>';
print '</tr>';
foreach ($pendingMatches as $pm) {
$trans = $pm['transaction'];
$bestMatch = $pm['matches'][0];
// Score color
$scoreColor = $bestMatch['match_score'] >= 80 ? '#4caf50' : ($bestMatch['match_score'] >= 60 ? '#ff9800' : '#9e9e9e');
print '<tr class="oddeven">';
// Transaction date
print '<td class="nowraponall">'.dol_print_date($trans->date_trans, 'day').'</td>';
// Counterparty name + description
print '<td class="tdoverflowmax200">';
print '<a href="'.dol_buildpath('/bankimport/card.php', 1).'?id='.$trans->id.'">'.dol_escape_htmltag(dol_trunc($trans->name, 30)).'</a>';
if ($trans->description) {
print '<br><span class="opacitymedium small">'.dol_escape_htmltag(dol_trunc($trans->description, 50)).'</span>';
}
print '</td>';
// Transaction amount
print '<td class="right nowraponall">';
if ($trans->amount >= 0) {
print '<span style="color: green; font-weight: bold;">+'.price($trans->amount, 0, $langs, 1, -1, 2, $trans->currency).'</span>';
} else {
print '<span style="color: red; font-weight: bold;">'.price($trans->amount, 0, $langs, 1, -1, 2, $trans->currency).'</span>';
}
print '</td>';
// Arrow
print '<td class="center" style="font-size: 1.3em;">&harr;</td>';
// Invoice reference
print '<td class="nowraponall">';
if ($bestMatch['type'] == 'facture') {
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
$inv = new Facture($db);
$inv->fetch($bestMatch['id']);
print $inv->getNomUrl(1);
} else {
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
$inv = new FactureFournisseur($db);
$inv->fetch($bestMatch['id']);
print $inv->getNomUrl(1);
}
print '</td>';
// Third party
print '<td class="tdoverflowmax150">';
if ($bestMatch['socid'] > 0) {
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
$soc = new Societe($db);
$soc->fetch($bestMatch['socid']);
print $soc->getNomUrl(1);
} else {
print dol_escape_htmltag($bestMatch['socname']);
}
print '</td>';
// Invoice amount
print '<td class="right nowraponall">'.price($bestMatch['amount'], 0, $langs, 1, -1, 2, 'EUR').'</td>';
// Score
print '<td class="center"><span style="color: '.$scoreColor.'; font-weight: bold; font-size: 1.1em;">'.$bestMatch['match_score'].'%</span></td>';
// Match reasons
print '<td>';
if (!empty($bestMatch['match_reasons'])) {
foreach ($bestMatch['match_reasons'] as $reason) {
$label = $reasonLabels[$reason] ?? $reason;
print '<span class="badge badge-secondary">'.$label.'</span> ';
}
}
print '</td>';
// Actions
print '<td class="center nowraponall">';
// Confirm payment button
print '<a class="butActionSmall" href="'.$_SERVER["PHP_SELF"].'?action=confirmpayment&transid='.$trans->id.'&matchtype='.urlencode($bestMatch['type']).'&matchid='.$bestMatch['id'].'&token='.newToken().'">';
print $langs->trans("ConfirmPayment");
print '</a>';
print '<br>';
// Ignore button
print '<a class="butActionSmall button-cancel" style="margin-top: 3px;" href="'.$_SERVER["PHP_SELF"].'?action=ignore&transid='.$trans->id.'&token='.newToken().'">';
print $langs->trans("SetAsIgnored");
print '</a>';
// Show alternatives if multiple matches
if (count($pm['matches']) > 1) {
print '<br><a class="small opacitymedium" style="margin-top: 3px;" href="'.dol_buildpath('/bankimport/card.php', 1).'?id='.$trans->id.'&action=findmatches&token='.newToken().'">';
print '+'.($count = count($pm['matches']) - 1).' '.$langs->trans("Alternatives");
print '</a>';
}
print '</td>';
print '</tr>';
}
print '</table>';
print '</div>';
} else {
print '<div class="opacitymedium" style="padding: 20px; text-align: center;">';
print $langs->trans("NoNewMatchesFound");
print '</div>';
}
// Show unmatched transactions count
if (!empty($noMatches)) {
print '<br>';
print '<div class="info">';
print img_picto('', 'info', 'class="pictofixedwidth"');
print $langs->trans("UnmatchedTransactions", count($noMatches));
print ' <a href="'.dol_buildpath('/bankimport/list.php', 1).'?search_status=0">'.$langs->trans("ShowAll").'</a>';
print '</div>';
}
llxFooter();
$db->close();

View file

@ -76,7 +76,7 @@ class modBankImport extends DolibarrModules
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@bankimport'
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
$this->version = '1.2';
$this->version = '1.4';
// Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
@ -369,6 +369,21 @@ class modBankImport extends DolibarrModules
'target' => '',
'user' => 2,
);
$this->menu[$r++] = array(
'fk_menu' => 'fk_mainmenu=bank,fk_leftmenu=bankimport',
'type' => 'left',
'titre' => 'PaymentConfirmation',
'prefix' => img_picto('', 'payment', 'class="pictofixedwidth valignmiddle paddingright"'),
'mainmenu' => 'bank',
'leftmenu' => 'bankimport_confirm',
'url' => '/bankimport/confirm.php?mainmenu=bank&leftmenu=bankimport',
'langs' => 'bankimport@bankimport',
'position' => 203,
'enabled' => 'isModEnabled("bankimport")',
'perms' => '$user->hasRight("bankimport", "write")',
'target' => '',
'user' => 2,
);
$this->menu[$r++] = array(
'fk_menu' => 'fk_mainmenu=bank,fk_leftmenu=bankimport',
'type' => 'left',
@ -378,7 +393,7 @@ class modBankImport extends DolibarrModules
'leftmenu' => 'bankimport_pdfstatements',
'url' => '/bankimport/pdfstatements.php?mainmenu=bank&leftmenu=bankimport',
'langs' => 'bankimport@bankimport',
'position' => 203,
'position' => 204,
'enabled' => 'isModEnabled("bankimport")',
'perms' => '$user->hasRight("bankimport", "read")',
'target' => '',