feat: PaymentVarious - Zahlung anlegen für Transaktionen ohne Rechnung
Neuer Button "Zahlung anlegen" auf der Transaktions-Detailseite (card.php) für Bankbuchungen ohne zugehörige Rechnung (z.B. Steuer-Erstattungen, private Umbuchungen, sonstige Zahlungen). - Inline-Formular mit Buchungskonto (Select2), Nebenbuchkonto, Buchungsseite (Soll/Haben) und Label - Buchungsseite wird automatisch aus Vorzeichen ermittelt - Erstellt PaymentVarious mit Bank-Eintrag und bank_url-Verknüpfung - Transaktion wird auf MATCHED gesetzt - Anzeige der sonstigen Zahlung mit Link bei gematchten Transaktionen - Fix: update() speichert jetzt fk_user_match und date_match korrekt - Fix: Leeres Nebenbuchkonto wird nicht mehr als -1 gespeichert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
33ecb49fc4
commit
b552b90303
5 changed files with 261 additions and 2 deletions
147
card.php
147
card.php
|
|
@ -337,6 +337,32 @@ if ($action == 'searchinvoice' && $object->id > 0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
* View
|
||||||
*/
|
*/
|
||||||
|
|
@ -563,6 +589,36 @@ if ($object->id > 0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 '<tr>';
|
||||||
|
print '<td>'.$langs->trans("Payment").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<a href="'.DOL_URL_ROOT.'/compta/bank/various_payment/card.php?id='.$various->id.'">';
|
||||||
|
print img_picto('', 'payment', 'class="pictofixedwidth"');
|
||||||
|
print $langs->trans("VariousPayment").' #'.$various->id;
|
||||||
|
print '</a>';
|
||||||
|
print ' <span class="opacitymedium">('.dol_print_date($various->datep, 'day').' - '.price($various->amount, 0, $langs, 1, -1, 2, 'EUR').')</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
if (!empty($various->accountancy_code)) {
|
||||||
|
print '<tr>';
|
||||||
|
print '<td>'.$langs->trans("AccountingAccount").'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($various->accountancy_code).'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If no payment link but invoice link exists (edge case)
|
// If no payment link but invoice link exists (edge case)
|
||||||
if (empty($object->fk_paiement) && empty($object->fk_paiementfourn)) {
|
if (empty($object->fk_paiement) && empty($object->fk_paiementfourn)) {
|
||||||
if ($object->fk_facture_fourn > 0) {
|
if ($object->fk_facture_fourn > 0) {
|
||||||
|
|
@ -677,6 +733,9 @@ if ($object->id > 0) {
|
||||||
// Find matches button
|
// Find matches button
|
||||||
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=findmatches&token='.newToken().'">'.$langs->trans("FindMatches").'</a>';
|
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=findmatches&token='.newToken().'">'.$langs->trans("FindMatches").'</a>';
|
||||||
|
|
||||||
|
// Create various payment button
|
||||||
|
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=createvarious&token='.newToken().'">'.$langs->trans("CreateVariousPayment").'</a>';
|
||||||
|
|
||||||
// Set as ignored
|
// Set as ignored
|
||||||
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=setstatus&status='.BankImportTransaction::STATUS_IGNORED.'&token='.newToken().'">'.$langs->trans("SetAsIgnored").'</a>';
|
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=setstatus&status='.BankImportTransaction::STATUS_IGNORED.'&token='.newToken().'">'.$langs->trans("SetAsIgnored").'</a>';
|
||||||
}
|
}
|
||||||
|
|
@ -693,6 +752,94 @@ if ($object->id > 0) {
|
||||||
|
|
||||||
print '</div>';
|
print '</div>';
|
||||||
|
|
||||||
|
// 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 '<br>';
|
||||||
|
print load_fiche_titre($langs->trans("CreateVariousPayment"), '', 'object_payment');
|
||||||
|
|
||||||
|
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="id" value="'.$object->id.'">';
|
||||||
|
print '<input type="hidden" name="action" value="confirmcreatevarious">';
|
||||||
|
|
||||||
|
print '<table class="border centpercent">';
|
||||||
|
|
||||||
|
// Amount (read-only)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td class="titlefield fieldrequired">'.$langs->trans("Amount").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
$amountColor = ($object->amount >= 0) ? 'green' : 'red';
|
||||||
|
print '<span style="font-weight: bold; color: '.$amountColor.';">'.price($object->amount, 0, $langs, 1, -1, 2, 'EUR').'</span>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Date (read-only)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td>'.$langs->trans("Date").'</td>';
|
||||||
|
print '<td>'.dol_print_date($object->date_trans, 'day').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Counterparty (read-only)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td>'.$langs->trans("Counterparty").'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($object->name).'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Accounting account (Select2 search)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td class="fieldrequired">'.$langs->trans("AccountingAccount").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print $formaccounting->select_account(GETPOST('accountancy_code', 'alpha'), 'accountancy_code', 1, array(), 0, 0, 'minwidth200 maxwidth500', '', 1);
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Subledger account (Select2 search)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td>'.$langs->trans("SubledgerAccount").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print $formaccounting->select_auxaccount(GETPOST('subledger_account', 'alpha'), 'subledger_account', 1, 'minwidth200 maxwidth500');
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Debit/Credit (auto-selected)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td class="fieldrequired">'.$langs->trans("DebitCredit").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
$sensValue = GETPOSTISSET('sens') ? GETPOSTINT('sens') : $defaultSens;
|
||||||
|
print '<select name="sens" class="flat minwidth100">';
|
||||||
|
print '<option value="0"'.($sensValue == 0 ? ' selected' : '').'>'.$langs->trans("Debit").' ('.$langs->trans("Expense").')</option>';
|
||||||
|
print '<option value="1"'.($sensValue == 1 ? ' selected' : '').'>'.$langs->trans("Credit").' ('.$langs->trans("Income").')</option>';
|
||||||
|
print '</select>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Label (editable, pre-filled)
|
||||||
|
print '<tr>';
|
||||||
|
print '<td class="fieldrequired">'.$langs->trans("Label").'</td>';
|
||||||
|
print '<td>';
|
||||||
|
$labelValue = GETPOSTISSET('various_label') ? GETPOST('various_label', 'alphanohtml') : $defaultLabel;
|
||||||
|
print '<input type="text" name="various_label" class="flat minwidth300 maxwidth500" value="'.dol_escape_htmltag($labelValue).'">';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
print '<div class="center" style="margin-top: 10px;">';
|
||||||
|
print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
|
||||||
|
print ' ';
|
||||||
|
print '<a class="button button-cancel" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("Cancel").'</a>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '</form>';
|
||||||
|
}
|
||||||
|
|
||||||
// Manual invoice selection (only for new transactions)
|
// Manual invoice selection (only for new transactions)
|
||||||
if ($object->status == BankImportTransaction::STATUS_NEW) {
|
if ($object->status == BankImportTransaction::STATUS_NEW) {
|
||||||
// Determine if this is likely a supplier payment (negative amount) or customer (positive)
|
// Determine if this is likely a supplier payment (negative amount) or customer (positive)
|
||||||
|
|
|
||||||
|
|
@ -455,7 +455,9 @@ class BankImportTransaction extends CommonObject
|
||||||
$sql .= " status = ".((int) $this->status).",";
|
$sql .= " status = ".((int) $this->status).",";
|
||||||
$sql .= " fk_user_modif = ".((int) $user->id).",";
|
$sql .= " fk_user_modif = ".((int) $user->id).",";
|
||||||
$sql .= " note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL").",";
|
$sql .= " note_private = ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "NULL").",";
|
||||||
$sql .= " note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "NULL");
|
$sql .= " note_public = ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "NULL").",";
|
||||||
|
$sql .= " fk_user_match = ".($this->fk_user_match > 0 ? ((int) $this->fk_user_match) : "NULL").",";
|
||||||
|
$sql .= " date_match = ".($this->date_match ? "'".$this->db->idate($this->date_match)."'" : "NULL");
|
||||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||||
|
|
||||||
dol_syslog(get_class($this)."::update", LOG_DEBUG);
|
dol_syslog(get_class($this)."::update", LOG_DEBUG);
|
||||||
|
|
@ -1538,6 +1540,88 @@ class BankImportTransaction extends CommonObject
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a various payment (PaymentVarious) for this transaction
|
||||||
|
* Used for transactions without invoices (tax refunds, internal transfers, etc.)
|
||||||
|
*
|
||||||
|
* @param User $user User performing the action
|
||||||
|
* @param int $bankAccountId Dolibarr bank account ID
|
||||||
|
* @param string $accountancyCode Accounting account code (e.g. '1780')
|
||||||
|
* @param int $sens 0=debit, 1=credit
|
||||||
|
* @param string $label Payment label
|
||||||
|
* @param string $subledgerAccount Subledger account (optional)
|
||||||
|
* @param int $typePayment Payment type ID (0 = auto-detect VIR)
|
||||||
|
* @return int >0 if OK (PaymentVarious ID), <0 if error
|
||||||
|
*/
|
||||||
|
public function createVariousPayment($user, $bankAccountId, $accountancyCode, $sens, $label, $subledgerAccount = '', $typePayment = 0)
|
||||||
|
{
|
||||||
|
global $conf, $langs;
|
||||||
|
|
||||||
|
$error = 0;
|
||||||
|
$this->db->begin();
|
||||||
|
|
||||||
|
// Look up payment type ID for 'VIR' (bank transfer) if not provided
|
||||||
|
if (empty($typePayment)) {
|
||||||
|
$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);
|
||||||
|
$typePayment = (int) $obj->id;
|
||||||
|
}
|
||||||
|
if (empty($typePayment)) {
|
||||||
|
$this->error = 'Payment type VIR not found in c_paiement';
|
||||||
|
$this->db->rollback();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
|
||||||
|
|
||||||
|
$various = new PaymentVarious($this->db);
|
||||||
|
$various->datep = $this->date_trans;
|
||||||
|
$various->datev = $this->date_value ?: $this->date_trans;
|
||||||
|
$various->sens = $sens;
|
||||||
|
$various->amount = abs($this->amount);
|
||||||
|
$various->type_payment = $typePayment;
|
||||||
|
$various->num_payment = $this->end_to_end_id ?: $this->ref;
|
||||||
|
$various->label = $label;
|
||||||
|
$various->accountancy_code = $accountancyCode;
|
||||||
|
$various->subledger_account = !empty($subledgerAccount) ? $subledgerAccount : '';
|
||||||
|
$various->fk_account = $bankAccountId;
|
||||||
|
$various->note = $langs->trans("PaymentCreatedByBankImport").' - '.$this->name.' - '.dol_trunc($this->description, 100);
|
||||||
|
|
||||||
|
$variousId = $various->create($user);
|
||||||
|
if ($variousId < 0) {
|
||||||
|
$this->error = $various->error;
|
||||||
|
$this->errors = $various->errors;
|
||||||
|
$error++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$error) {
|
||||||
|
// Get the bank line ID created by PaymentVarious
|
||||||
|
$sql = "SELECT fk_bank FROM ".MAIN_DB_PREFIX."payment_various WHERE rowid = ".((int) $variousId);
|
||||||
|
$resql = $this->db->query($sql);
|
||||||
|
if ($resql && $this->db->num_rows($resql) > 0) {
|
||||||
|
$obj = $this->db->fetch_object($resql);
|
||||||
|
$this->fk_bank = (int) $obj->fk_bank;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update transaction status to MATCHED
|
||||||
|
$this->status = self::STATUS_MATCHED;
|
||||||
|
$this->fk_user_match = $user->id;
|
||||||
|
$this->date_match = dol_now();
|
||||||
|
$this->update($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($error) {
|
||||||
|
$this->db->rollback();
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->commit();
|
||||||
|
return $variousId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm payment for multiple invoices (batch payment)
|
* Confirm payment for multiple invoices (batch payment)
|
||||||
* Creates a single payment that covers multiple invoices
|
* Creates a single payment that covers multiple invoices
|
||||||
|
|
|
||||||
|
|
@ -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'
|
$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'
|
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
|
||||||
$this->version = '3.5';
|
$this->version = '3.7';
|
||||||
// Url to the file with your last numberversion of this module
|
// Url to the file with your last numberversion of this module
|
||||||
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -404,3 +404,17 @@ StatementsNotPdfFormat = Kontoauszüge empfangen, aber nicht im PDF-Format
|
||||||
StatementsUsingHKEKA = Nutze HKEKA (generischer Kontoauszug) statt HKEKP
|
StatementsUsingHKEKA = Nutze HKEKA (generischer Kontoauszug) statt HKEKP
|
||||||
StatementsUsingHKEKP = Nutze HKEKP (PDF-Kontoauszug)
|
StatementsUsingHKEKP = Nutze HKEKP (PDF-Kontoauszug)
|
||||||
NeitherHKEKPnorHKEKA = Die Bank unterstützt weder HKEKP noch HKEKA für elektronische Kontoauszüge
|
NeitherHKEKPnorHKEKA = Die Bank unterstützt weder HKEKP noch HKEKA für elektronische Kontoauszüge
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sonstige Zahlung (PaymentVarious)
|
||||||
|
#
|
||||||
|
CreateVariousPayment = Zahlung anlegen
|
||||||
|
AccountingAccount = Buchungskonto
|
||||||
|
SubledgerAccount = Nebenbuchkonto
|
||||||
|
DebitCredit = Buchungsseite
|
||||||
|
Debit = Soll
|
||||||
|
Credit = Haben
|
||||||
|
Expense = Ausgabe
|
||||||
|
Income = Einnahme
|
||||||
|
VariousPaymentCreated = Sonstige Zahlung erfolgreich erstellt
|
||||||
|
VariousPayment = Sonstige Zahlung
|
||||||
|
|
|
||||||
|
|
@ -301,3 +301,17 @@ StatementsUsingHKEKP = Using HKEKP (PDF statement)
|
||||||
NeitherHKEKPnorHKEKA = The bank supports neither HKEKP nor HKEKA for electronic statements
|
NeitherHKEKPnorHKEKA = The bank supports neither HKEKP nor HKEKA for electronic statements
|
||||||
BankImportAutoFetch = Automatic bank import (transactions)
|
BankImportAutoFetch = Automatic bank import (transactions)
|
||||||
BankImportFetchPdfStatements = Automatic PDF statement retrieval
|
BankImportFetchPdfStatements = Automatic PDF statement retrieval
|
||||||
|
|
||||||
|
#
|
||||||
|
# Various Payment (PaymentVarious)
|
||||||
|
#
|
||||||
|
CreateVariousPayment = Create Payment
|
||||||
|
AccountingAccount = Accounting Account
|
||||||
|
SubledgerAccount = Subledger Account
|
||||||
|
DebitCredit = Debit/Credit
|
||||||
|
Debit = Debit
|
||||||
|
Credit = Credit
|
||||||
|
Expense = Expense
|
||||||
|
Income = Income
|
||||||
|
VariousPaymentCreated = Various payment created successfully
|
||||||
|
VariousPayment = Various Payment
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue