mahnung/ajax/createmahnung.php
Eduard Wisch 10cf41a687
All checks were successful
Deploy mahnung / deploy (push) Successful in 14s
i18n: Alle Texte über $langs->trans() — ~100 neue Sprachschlüssel de_DE + en_US [deploy]
Umlaute in allen lang-Dateien korrigiert. Alle hardcodierten deutschen Strings
in 22 PHP-Dateien durch $langs->trans('Key') ersetzt. Neue Schlüssel für
Cron-Meldungen, Dokument-Aktionen, Bonität, Vorschlag-Status, Template-Vars u.a.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 16:25:50 +02:00

213 lines
6.6 KiB
PHP

<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
*
* GPL v3 (siehe COPYING).
*/
/**
* \file htdocs/custom/mahnung/ajax/createmahnung.php
* \ingroup mahnung
* \brief AJAX-Endpoint: Mahnung(en) zu Rechnung(en) erzeugen + PDF generieren.
*
* Akzeptiert sowohl klassische Form-POSTs (Browser-Submit aus list.php)
* als auch AJAX-Calls. Antwortet je nach Accept-Header HTML-Redirect
* oder JSON.
*
* POST:
* - facture_ids[] Array Rechnungs-IDs (oder einzelne facture_id)
* - stufe Optional: Stufe erzwingen (sonst Vorschlag-Logik)
* - token CSRF
*/
if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1');
if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1');
ob_start();
require_once $_SERVER['DOCUMENT_ROOT'].'/main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnung.class.php';
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungstufe.class.php';
require_once DOL_DOCUMENT_ROOT.'/custom/mahnung/class/mahnungvorschlag.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
global $db, $user, $langs, $conf;
$langs->loadLangs(array('mahnung@mahnung'));
/**
* @param bool $success
* @param string $message
* @param array $extra
*/
function respond($success, $message, $extra = array())
{
$wantsJson = false;
if (!empty($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false) {
$wantsJson = true;
}
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
$wantsJson = true;
}
while (ob_get_level() > 0) {
ob_end_clean();
}
if ($wantsJson) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode(array_merge(array('success' => (bool) $success, 'message' => $message), $extra));
exit;
}
// Klassischer Submit -> Redirect zur Liste mit Flash-Message
global $user;
if (function_exists('setEventMessages')) {
setEventMessages($message, null, $success ? 'mesgs' : 'errors');
}
header('Location: '.DOL_URL_ROOT.'/custom/mahnung/list.php?mainmenu=billing&leftmenu=mahnung&mode=vorschlag');
exit;
}
// 1) CSRF
$postedToken = GETPOST('token', 'alphanohtml');
if (empty($postedToken) || empty($_SESSION['newtoken']) || $postedToken !== $_SESSION['newtoken']) {
respond(false, $langs->trans('MahnungCsrfFehler'), array('code' => 'csrf'));
}
// 2) Permission
if (!$user->hasRight('mahnung', 'write')) {
respond(false, $langs->transnoentities('NotEnoughPermissions') ?: $langs->trans('MahnungNichtBerechtigt'), array('code' => 'forbidden'));
}
// 3) Input
$factureIds = GETPOST('facture_ids', 'array:int');
if (empty($factureIds)) {
$single = GETPOSTINT('facture_id');
if (!empty($single)) {
$factureIds = array($single);
}
}
$factureIds = array_values(array_unique(array_map('intval', $factureIds)));
$factureIds = array_filter($factureIds, function ($v) {
return $v > 0;
});
if (empty($factureIds)) {
respond(false, $langs->trans('MahnungKeineRechnungenAusgewaehlt'), array('code' => 'noinput'));
}
$forceStufe = GETPOSTINT('stufe');
$forceStufe = ($forceStufe >= 1 && $forceStufe <= 3) ? $forceStufe : 0;
// 4) Verarbeitung — pro Rechnung Vorschlag holen, Mahnung erzeugen, PDF generieren
$service = new MahnungVorschlag($db);
$basiszins = (float) getDolGlobalString('MAHNUNG_BASISZINS', '1.27');
$created = 0;
$skipped = 0;
$failed = array();
foreach ($factureIds as $fid) {
$rows = $service->getVorschlaege(array('soc_id' => 0)); // ohne Filter holen
$row = null;
foreach ($rows as $r) {
if ((int) $r['facture_id'] === (int) $fid) {
$row = $r;
break;
}
}
if ($row === null) {
// Keine offene Mahnungs-Empfehlung — z.B. weil Wartefrist noch läuft
$skipped++;
continue;
}
$stufeNr = $forceStufe ?: (int) $row['vorgeschlagene_stufe'];
$stufe = $service->getStufe($stufeNr);
if ($stufe === null) {
$failed[] = $langs->trans('MahnungStufeNichtKonfiguriert', $fid, $stufeNr);
continue;
}
$mahnung = new Mahnung($db);
$mahnung->fk_facture = $fid;
$mahnung->fk_soc = (int) $row['soc_id'];
$mahnung->stufe = $stufeNr;
$mahnung->date_mahnung = dol_now();
$mahnung->date_lim_reglement_alt = $row['facture_date_lim_reglement'];
$mahnung->date_lim_reglement_neu = dol_time_plus_duree(dol_now(), (int) $stufe->neue_frist_tage, 'd');
$mahnung->betrag_offen = (float) $row['betrag_offen'];
$mahnung->customertype = $row['kundentyp'];
$mahnung->basiszins_snapshot = $basiszins;
$mahnung->versandart = $stufe->versandart_default ?: Mahnung::VERSAND_PDF;
// Gebühren + Pauschale
$mahnung->mahngebuehr = $stufe->getMahngebuehr($mahnung->customertype);
// §288 Abs. 5 Pauschale: nur einmal pro Rechnung B2B (in Stufe mit pauschale_b2b_einmalig=1)
if ($mahnung->customertype === Mahnung::KUNDENTYP_B2B && (int) $stufe->pauschale_b2b_einmalig === 1) {
$alreadyApplied = pauschaleBereitsAngewendet($db, (int) $fid);
if (!$alreadyApplied) {
$mahnung->pauschale_b2b = (float) getDolGlobalString('MAHNUNG_PAUSCHALE_B2B', '40.00');
}
}
// Verzugszinsen
$override = $stufe->getZinssatzOverride($mahnung->customertype);
$mahnung->verzugszinsen = Mahnung::berechneVerzugszinsen(
$mahnung->betrag_offen,
(int) $row['tage_verzug'],
$mahnung->customertype,
$basiszins,
$override
);
$mahnung->rechneSumme();
$mahnung->status = Mahnung::STATUS_ERSTELLT;
$newId = $mahnung->create($user);
if ($newId <= 0) {
$failed[] = 'Rechnung #'.$fid.': '.$mahnung->error;
continue;
}
$docResult = $mahnung->generateDocument('', $langs);
if ($docResult <= 0) {
$failed[] = 'Rechnung #'.$fid.' (Mahnung '.$mahnung->ref.'): Dokument-Fehler '.$mahnung->error;
continue;
}
$created++;
}
$msg = $langs->trans('MahnungMahnungErstellt', $created);
if ($skipped > 0) {
$msg .= $langs->trans('MahnungUebersprungen2', $skipped);
}
if (!empty($failed)) {
$msg .= $langs->trans('MahnungFehlerLabel', implode(' | ', $failed));
respond(false, $msg, array('created' => $created, 'failed' => $failed));
}
respond(true, $msg, array('created' => $created, 'skipped' => $skipped));
/**
* Prüft, ob für eine Rechnung bereits in einer aktiven Mahnung die §288-B2B-Pauschale gesetzt wurde.
*
* @param DoliDB $db
* @param int $factureId
* @return bool
*/
function pauschaleBereitsAngewendet($db, $factureId)
{
$sql = "SELECT 1 FROM ".MAIN_DB_PREFIX."mahnung_mahnung";
$sql .= " WHERE fk_facture = ".((int) $factureId);
$sql .= " AND status NOT IN (".Mahnung::STATUS_STORNIERT.")";
$sql .= " AND pauschale_b2b > 0";
$sql .= " LIMIT 1";
$resql = $db->query($sql);
if (!$resql) {
return false;
}
$has = (bool) $db->num_rows($resql);
$db->free($resql);
return $has;
}