- Add multi-invoice payment support (link one bank transaction to multiple invoices) - Add payment unlinking feature to correct wrong matches - Show linked payments, invoices and bank entries in transaction detail view - Allow linking already paid invoices to bank transactions - Update README with new features - Add CHANGELOG.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
174 lines
4.8 KiB
PHP
Executable file
174 lines
4.8 KiB
PHP
Executable file
<?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.
|
|
*/
|
|
|
|
/**
|
|
* JavaScript for browser push notifications about incoming payments
|
|
* Loaded on every Dolibarr page via module_parts['js']
|
|
*/
|
|
|
|
// Define MIME type
|
|
if (!defined('NOTOKENRENEWAL')) {
|
|
define('NOTOKENRENEWAL', '1');
|
|
}
|
|
if (!defined('NOREQUIREMENU')) {
|
|
define('NOREQUIREMENU', '1');
|
|
}
|
|
if (!defined('NOREQUIREHTML')) {
|
|
define('NOREQUIREHTML', '1');
|
|
}
|
|
if (!defined('NOREQUIREAJAX')) {
|
|
define('NOREQUIREAJAX', '1');
|
|
}
|
|
|
|
$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";
|
|
}
|
|
|
|
header('Content-Type: application/javascript; charset=UTF-8');
|
|
header('Cache-Control: max-age=3600');
|
|
|
|
if (!isModEnabled('bankimport') || empty($user->id) || !$user->hasRight('bankimport', 'read')) {
|
|
echo '/* bankimport: no access */';
|
|
exit;
|
|
}
|
|
|
|
$checkUrl = dol_buildpath('/bankimport/ajax/checkpending.php', 1);
|
|
$confirmUrl = dol_buildpath('/bankimport/confirm.php', 1).'?mainmenu=bank&leftmenu=bankimport';
|
|
$checkInterval = 5 * 60 * 1000; // 5 Minuten
|
|
?>
|
|
(function() {
|
|
'use strict';
|
|
|
|
var STORAGE_KEY = 'bankimport_last_pending';
|
|
var CHECK_URL = <?php echo json_encode($checkUrl); ?>;
|
|
var CONFIRM_URL = <?php echo json_encode($confirmUrl); ?>;
|
|
var CHECK_INTERVAL = <?php echo $checkInterval; ?>;
|
|
|
|
// Erst nach Seitenload starten
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
|
|
function init() {
|
|
// Berechtigung anfragen beim ersten Mal
|
|
if ('Notification' in window && Notification.permission === 'default') {
|
|
// Dezent um Berechtigung bitten - nicht sofort, sondern nach 10 Sekunden
|
|
setTimeout(function() {
|
|
Notification.requestPermission();
|
|
}, 10000);
|
|
}
|
|
|
|
// Sofort prüfen
|
|
checkPending();
|
|
|
|
// Regelmäßig prüfen
|
|
setInterval(checkPending, CHECK_INTERVAL);
|
|
}
|
|
|
|
function checkPending() {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open('GET', CHECK_URL, true);
|
|
xhr.timeout = 15000;
|
|
|
|
xhr.onload = function() {
|
|
if (xhr.status !== 200) return;
|
|
|
|
try {
|
|
var data = JSON.parse(xhr.responseText);
|
|
} catch(e) {
|
|
return;
|
|
}
|
|
|
|
var lastKnown = parseInt(localStorage.getItem(STORAGE_KEY) || '0', 10);
|
|
var currentPending = data.pending || 0;
|
|
var incoming = data.incoming || 0;
|
|
var incomingTotal = data.incoming_total || 0;
|
|
|
|
// Neue Buchungen seit letztem Check?
|
|
if (currentPending > lastKnown && currentPending > 0) {
|
|
var newCount = currentPending - lastKnown;
|
|
|
|
if (incoming > 0) {
|
|
showNotification(
|
|
'Zahlungseingang',
|
|
incoming + ' Zahlungseingang' + (incoming > 1 ? 'e' : '') + ' (' + formatAmount(incomingTotal) + ' €)\nBestätigung erforderlich',
|
|
'incoming'
|
|
);
|
|
} else {
|
|
showNotification(
|
|
'Bankimport',
|
|
newCount + ' neue Buchung' + (newCount > 1 ? 'en' : '') + ' warten auf Zuordnung',
|
|
'pending'
|
|
);
|
|
}
|
|
}
|
|
|
|
// Aktuellen Stand merken
|
|
localStorage.setItem(STORAGE_KEY, currentPending.toString());
|
|
};
|
|
|
|
xhr.onerror = function() {};
|
|
xhr.send();
|
|
}
|
|
|
|
function showNotification(title, body, type) {
|
|
if (!('Notification' in window)) return;
|
|
if (Notification.permission !== 'granted') return;
|
|
|
|
var icon = type === 'incoming'
|
|
? '/theme/common/mime/money.png'
|
|
: '/theme/common/mime/doc.png';
|
|
|
|
var notification = new Notification(title, {
|
|
body: body,
|
|
icon: icon,
|
|
tag: 'bankimport-' + type,
|
|
requireInteraction: true
|
|
});
|
|
|
|
notification.onclick = function() {
|
|
window.focus();
|
|
window.location.href = CONFIRM_URL;
|
|
notification.close();
|
|
};
|
|
|
|
// Nach 30 Sekunden automatisch schließen
|
|
setTimeout(function() {
|
|
notification.close();
|
|
}, 30000);
|
|
}
|
|
|
|
function formatAmount(amount) {
|
|
return parseFloat(amount).toFixed(2).replace('.', ',').replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
|
}
|
|
|
|
})();
|