feat: Multi-Invoice Skonto-Erkennung und automatische Skonto-Verarbeitung

- Toleranz für Multi-Invoice-Matching auf 3% des Betrags erhöht (statt 5€ fix)
- Automatische proportionale Verteilung der Zahlung bei Skonto-Abzug
- Rechnungen werden mit close_code='discount_vat' als bezahlt markiert
- Skonto-Betrag wird in der Notiz dokumentiert
- Version 3.1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-03-05 10:46:23 +01:00
parent 6e4b5b0de2
commit dbcad6a884
5 changed files with 68 additions and 4 deletions

0
admin/cronmonitor.php Normal file → Executable file
View file

View file

@ -829,7 +829,9 @@ class BankImportTransaction extends CommonObject
$socid = $this->findSupplierForMultiMatch($searchText);
if ($socid > 0) {
$multiMatch = $this->findMultipleSupplierInvoiceMatches($searchText, $socid, $absAmount, 5.00);
// Tolerance: 3% of amount (for cash discounts/skonto) or minimum 5 EUR
$tolerance = max($absAmount * 0.03, 5.00);
$multiMatch = $this->findMultipleSupplierInvoiceMatches($searchText, $socid, $absAmount, $tolerance);
if ($multiMatch && count($multiMatch['invoices']) > 1) {
// Add as a special "multi" match
@ -1603,6 +1605,26 @@ class BankImportTransaction extends CommonObject
return -2;
}
// Check for cash discount (Skonto): If paid amount is less than invoice sum
$actualAmount = abs($this->amount);
$hasDiscount = false;
$discountAmount = 0;
$discountFactor = 1.0;
if ($actualAmount < $totalPayment && $actualAmount > 0) {
$discountAmount = $totalPayment - $actualAmount;
// Only apply discount logic if within 5% (typical cash discount range)
if ($discountAmount <= ($totalPayment * 0.05)) {
$hasDiscount = true;
$discountFactor = $actualAmount / $totalPayment;
// Adjust amounts proportionally
foreach ($amounts as $invoiceId => $amount) {
$amounts[$invoiceId] = price2num($amount * $discountFactor, 'MT');
$multicurrency_amounts[$invoiceId] = $amounts[$invoiceId];
}
}
}
if ($isSupplier) {
// Supplier invoice payment
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
@ -1635,6 +1657,21 @@ class BankImportTransaction extends CommonObject
);
if ($bankLineId > 0) {
// If we had a cash discount, close the invoices with discount code
if ($hasDiscount) {
foreach ($invoices as $inv) {
$invObj = new FactureFournisseur($this->db);
if ($invObj->fetch($inv['id']) > 0) {
// Check if invoice still has remaining amount
$remaining = $invObj->getRemainToPay();
if ($remaining > 0 && $remaining <= $discountAmount) {
// Close with discount code
$invObj->setPaid($user, 'discount_vat', $langs->trans("CashDiscount").' ('.price($remaining).' '.$this->currency.')');
}
}
}
}
$this->fk_paiementfourn = $paymentId;
$this->fk_bank = $bankLineId;
$this->fk_societe = $socid;
@ -1643,7 +1680,8 @@ class BankImportTransaction extends CommonObject
$this->date_match = dol_now();
// Store first invoice as reference (or could store all in note)
$this->fk_facture_fourn = $invoices[0]['id'];
$this->note_private = ($this->note_private ? $this->note_private."\n" : '').'Multi-invoice payment: '.implode(', ', array_column($invoices, 'ref'));
$discountNote = $hasDiscount ? ' (Skonto: '.price($discountAmount).' '.$this->currency.')' : '';
$this->note_private = ($this->note_private ? $this->note_private."\n" : '').'Multi-invoice payment: '.implode(', ', array_column($invoices, 'ref')).$discountNote;
$this->update($user);
} else {
$this->error = 'Failed to add payment to bank';
@ -1682,6 +1720,21 @@ class BankImportTransaction extends CommonObject
);
if ($bankLineId > 0) {
// If we had a cash discount, close the invoices with discount code
if ($hasDiscount) {
foreach ($invoices as $inv) {
$invObj = new Facture($this->db);
if ($invObj->fetch($inv['id']) > 0) {
// Check if invoice still has remaining amount
$remaining = $invObj->getRemainToPay();
if ($remaining > 0 && $remaining <= $discountAmount) {
// Close with discount code
$invObj->setPaid($user, 'discount_vat', $langs->trans("CashDiscount").' ('.price($remaining).' '.$this->currency.')');
}
}
}
}
$this->fk_paiement = $paymentId;
$this->fk_bank = $bankLineId;
$this->fk_societe = $socid;
@ -1689,7 +1742,8 @@ class BankImportTransaction extends CommonObject
$this->fk_user_match = $user->id;
$this->date_match = dol_now();
$this->fk_facture = $invoices[0]['id'];
$this->note_private = ($this->note_private ? $this->note_private."\n" : '').'Multi-invoice payment: '.implode(', ', array_column($invoices, 'ref'));
$discountNote = $hasDiscount ? ' (Skonto: '.price($discountAmount).' '.$this->currency.')' : '';
$this->note_private = ($this->note_private ? $this->note_private."\n" : '').'Multi-invoice payment: '.implode(', ', array_column($invoices, 'ref')).$discountNote;
$this->update($user);
} else {
$this->error = 'Failed to add payment to bank';

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 = '2.9';
$this->version = '3.2';
// Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';

View file

@ -370,3 +370,8 @@ Repair = Reparieren
#
AdminTools = Admin-Werkzeuge
Open = Öffnen
#
# Cash Discount / Skonto
#
CashDiscount = Skonto

View file

@ -266,3 +266,8 @@ Repair = Repair
#
AdminTools = Admin Tools
Open = Open
#
# Cash Discount / Skonto
#
CashDiscount = Cash Discount