389 lines
13 KiB
PHP
389 lines
13 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 ZUGFeRD Import Module
|
|
*
|
|
* 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 class/importnotification.class.php
|
|
* \ingroup importzugferd
|
|
* \brief Email notification class for ZUGFeRD imports
|
|
*/
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
|
|
|
|
/**
|
|
* Class ImportNotification
|
|
* Handles email notifications for ZUGFeRD import events
|
|
*/
|
|
class ImportNotification
|
|
{
|
|
/**
|
|
* @var DoliDB Database handler
|
|
*/
|
|
public $db;
|
|
|
|
/**
|
|
* @var string Error message
|
|
*/
|
|
public $error = '';
|
|
|
|
/**
|
|
* @var array Error messages
|
|
*/
|
|
public $errors = array();
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param DoliDB $db Database handler
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* Check if notifications are enabled
|
|
*
|
|
* @return bool True if enabled
|
|
*/
|
|
public function isEnabled()
|
|
{
|
|
return getDolGlobalString('IMPORTZUGFERD_NOTIFY_ENABLED') && getDolGlobalString('IMPORTZUGFERD_NOTIFY_EMAIL');
|
|
}
|
|
|
|
/**
|
|
* Get notification email address
|
|
*
|
|
* @return string Email address
|
|
*/
|
|
public function getNotifyEmail()
|
|
{
|
|
return getDolGlobalString('IMPORTZUGFERD_NOTIFY_EMAIL');
|
|
}
|
|
|
|
/**
|
|
* Send notification for manual intervention required
|
|
*
|
|
* @param ZugferdImport $import Import object
|
|
* @param array $lines Import lines
|
|
* @return int 1 if sent, 0 if not needed, -1 on error
|
|
*/
|
|
public function sendManualInterventionNotification($import, $lines = array())
|
|
{
|
|
global $conf, $langs;
|
|
|
|
if (!$this->isEnabled() || !getDolGlobalString('IMPORTZUGFERD_NOTIFY_MANUAL')) {
|
|
return 0;
|
|
}
|
|
|
|
$langs->load('importzugferd@importzugferd');
|
|
|
|
$subject = $langs->trans('NotifySubjectManualIntervention', $import->invoice_number);
|
|
|
|
$body = $langs->trans('NotifyBodyManualIntervention', $import->invoice_number, $import->seller_name);
|
|
$body .= "\n\n";
|
|
$body .= $langs->trans('InvoiceNumber').': '.$import->invoice_number."\n";
|
|
$body .= $langs->trans('Supplier').': '.$import->seller_name."\n";
|
|
$body .= $langs->trans('InvoiceDate').': '.dol_print_date($import->invoice_date, 'day')."\n";
|
|
$body .= $langs->trans('TotalTTC').': '.price($import->total_ttc).' '.$import->currency."\n";
|
|
$body .= "\n";
|
|
|
|
// List issues
|
|
$missingProducts = 0;
|
|
$missingSupplier = ($import->fk_soc <= 0);
|
|
|
|
if (!empty($lines)) {
|
|
foreach ($lines as $line) {
|
|
if ($line->fk_product <= 0) {
|
|
$missingProducts++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($missingSupplier) {
|
|
$body .= "- ".$langs->trans('SupplierNotAssigned')."\n";
|
|
}
|
|
if ($missingProducts > 0) {
|
|
$body .= "- ".$missingProducts." ".$langs->trans('ProductsNotAssigned')."\n";
|
|
}
|
|
|
|
$body .= "\n";
|
|
$body .= $langs->trans('NotifyLinkToImport').': '.dol_buildpath('/importzugferd/import.php', 2).'?action=edit&id='.$import->id;
|
|
|
|
return $this->sendEmail($subject, $body);
|
|
}
|
|
|
|
/**
|
|
* Send notification for import error
|
|
*
|
|
* @param ZugferdImport $import Import object (may be partial)
|
|
* @param string $errorMessage Error message
|
|
* @param string $filename Original filename
|
|
* @return int 1 if sent, 0 if not needed, -1 on error
|
|
*/
|
|
public function sendErrorNotification($import, $errorMessage, $filename = '')
|
|
{
|
|
global $conf, $langs;
|
|
|
|
if (!$this->isEnabled() || !getDolGlobalString('IMPORTZUGFERD_NOTIFY_ERROR')) {
|
|
return 0;
|
|
}
|
|
|
|
$langs->load('importzugferd@importzugferd');
|
|
|
|
$invoiceNum = !empty($import->invoice_number) ? $import->invoice_number : $filename;
|
|
$subject = $langs->trans('NotifySubjectError', $invoiceNum);
|
|
|
|
$body = $langs->trans('NotifyBodyError', $invoiceNum);
|
|
$body .= "\n\n";
|
|
|
|
if (!empty($import->invoice_number)) {
|
|
$body .= $langs->trans('InvoiceNumber').': '.$import->invoice_number."\n";
|
|
}
|
|
if (!empty($import->seller_name)) {
|
|
$body .= $langs->trans('Supplier').': '.$import->seller_name."\n";
|
|
}
|
|
if (!empty($filename)) {
|
|
$body .= $langs->trans('File').': '.$filename."\n";
|
|
}
|
|
|
|
$body .= "\n";
|
|
$body .= $langs->trans('ErrorMessage').":\n";
|
|
$body .= $errorMessage."\n";
|
|
|
|
if ($import->id > 0) {
|
|
$body .= "\n";
|
|
$body .= $langs->trans('NotifyLinkToImport').': '.dol_buildpath('/importzugferd/import.php', 2).'?action=edit&id='.$import->id;
|
|
}
|
|
|
|
return $this->sendEmail($subject, $body);
|
|
}
|
|
|
|
/**
|
|
* Send notification for significant price differences
|
|
*
|
|
* @param ZugferdImport $import Import object
|
|
* @param array $priceDiffs Array of price differences: array of ['line' => ImportLine, 'product' => Product, 'old_price' => float, 'new_price' => float, 'diff_percent' => float]
|
|
* @return int 1 if sent, 0 if not needed, -1 on error
|
|
*/
|
|
public function sendPriceDifferenceNotification($import, $priceDiffs)
|
|
{
|
|
global $conf, $langs;
|
|
|
|
if (!$this->isEnabled() || !getDolGlobalString('IMPORTZUGFERD_NOTIFY_PRICE_DIFF')) {
|
|
return 0;
|
|
}
|
|
|
|
if (empty($priceDiffs)) {
|
|
return 0;
|
|
}
|
|
|
|
$langs->load('importzugferd@importzugferd');
|
|
|
|
$threshold = getDolGlobalInt('IMPORTZUGFERD_PRICE_DIFF_THRESHOLD', 10);
|
|
$subject = $langs->trans('NotifySubjectPriceDiff', $import->invoice_number, count($priceDiffs));
|
|
|
|
$body = $langs->trans('NotifyBodyPriceDiff', $import->invoice_number, $import->seller_name, $threshold);
|
|
$body .= "\n\n";
|
|
$body .= $langs->trans('InvoiceNumber').': '.$import->invoice_number."\n";
|
|
$body .= $langs->trans('Supplier').': '.$import->seller_name."\n";
|
|
$body .= $langs->trans('InvoiceDate').': '.dol_print_date($import->invoice_date, 'day')."\n";
|
|
$body .= "\n";
|
|
|
|
// Table header
|
|
$body .= str_pad($langs->trans('Product'), 40)." | ";
|
|
$body .= str_pad($langs->trans('OldPrice'), 12)." | ";
|
|
$body .= str_pad($langs->trans('NewPrice'), 12)." | ";
|
|
$body .= str_pad($langs->trans('Difference'), 10)."\n";
|
|
$body .= str_repeat('-', 80)."\n";
|
|
|
|
// List products with price differences
|
|
foreach ($priceDiffs as $diff) {
|
|
$productName = $diff['product']->ref.' - '.$diff['product']->label;
|
|
if (strlen($productName) > 38) {
|
|
$productName = substr($productName, 0, 35).'...';
|
|
}
|
|
|
|
$oldPrice = price($diff['old_price']).' '.$import->currency;
|
|
$newPrice = price($diff['new_price']).' '.$import->currency;
|
|
$diffPercent = ($diff['diff_percent'] > 0 ? '+' : '').number_format($diff['diff_percent'], 1).'%';
|
|
|
|
$body .= str_pad($productName, 40)." | ";
|
|
$body .= str_pad($oldPrice, 12)." | ";
|
|
$body .= str_pad($newPrice, 12)." | ";
|
|
$body .= str_pad($diffPercent, 10)."\n";
|
|
}
|
|
|
|
$body .= "\n";
|
|
$body .= $langs->trans('NotifyLinkToImport').': '.dol_buildpath('/importzugferd/import.php', 2).'?action=edit&id='.$import->id;
|
|
|
|
return $this->sendEmail($subject, $body);
|
|
}
|
|
|
|
/**
|
|
* Check for price differences and send notification if needed
|
|
*
|
|
* @param ZugferdImport $import Import object
|
|
* @param array $lines Import lines with fk_product set
|
|
* @return int 1 if notification sent, 0 if not needed, -1 on error
|
|
*/
|
|
public function checkAndNotifyPriceDifferences($import, $lines)
|
|
{
|
|
global $conf;
|
|
|
|
if (!$this->isEnabled() || !getDolGlobalString('IMPORTZUGFERD_NOTIFY_PRICE_DIFF')) {
|
|
return 0;
|
|
}
|
|
|
|
$threshold = getDolGlobalInt('IMPORTZUGFERD_PRICE_DIFF_THRESHOLD', 10);
|
|
$priceDiffs = array();
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
|
|
|
|
foreach ($lines as $line) {
|
|
if ($line->fk_product <= 0) {
|
|
continue;
|
|
}
|
|
|
|
// Get current supplier price
|
|
$productFourn = new ProductFournisseur($this->db);
|
|
$result = $productFourn->find_min_price_product_fournisseur($line->fk_product, 1, $import->fk_soc);
|
|
|
|
if ($result > 0 && $productFourn->fourn_price > 0) {
|
|
$oldPrice = $productFourn->fourn_price;
|
|
$newPrice = $line->unit_price;
|
|
|
|
// Calculate percentage difference
|
|
$diffPercent = (($newPrice - $oldPrice) / $oldPrice) * 100;
|
|
|
|
if (abs($diffPercent) >= $threshold) {
|
|
$product = new Product($this->db);
|
|
$product->fetch($line->fk_product);
|
|
|
|
$priceDiffs[] = array(
|
|
'line' => $line,
|
|
'product' => $product,
|
|
'old_price' => $oldPrice,
|
|
'new_price' => $newPrice,
|
|
'diff_percent' => $diffPercent
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($priceDiffs)) {
|
|
return $this->sendPriceDifferenceNotification($import, $priceDiffs);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Send test notification email
|
|
*
|
|
* @return int 1 if sent, -1 on error
|
|
*/
|
|
public function sendTestNotification()
|
|
{
|
|
global $conf, $langs;
|
|
|
|
if (!$this->isEnabled()) {
|
|
$this->error = $langs->trans('NotificationsNotEnabled');
|
|
return -1;
|
|
}
|
|
|
|
$langs->load('importzugferd@importzugferd');
|
|
|
|
$subject = $langs->trans('NotifySubjectTest');
|
|
|
|
$body = $langs->trans('NotifyBodyTest');
|
|
$body .= "\n\n";
|
|
$body .= $langs->trans('NotifyTestInfo')."\n\n";
|
|
|
|
// Show current notification settings
|
|
$body .= $langs->trans('CurrentSettings').":\n";
|
|
$body .= "- ".$langs->trans('NotifyEmail').": ".$this->getNotifyEmail()."\n";
|
|
$body .= "- ".$langs->trans('IMPORTZUGFERD_NOTIFY_MANUAL').": ".(getDolGlobalString('IMPORTZUGFERD_NOTIFY_MANUAL') ? $langs->trans('Yes') : $langs->trans('No'))."\n";
|
|
$body .= "- ".$langs->trans('IMPORTZUGFERD_NOTIFY_ERROR').": ".(getDolGlobalString('IMPORTZUGFERD_NOTIFY_ERROR') ? $langs->trans('Yes') : $langs->trans('No'))."\n";
|
|
$body .= "- ".$langs->trans('IMPORTZUGFERD_NOTIFY_PRICE_DIFF').": ".(getDolGlobalString('IMPORTZUGFERD_NOTIFY_PRICE_DIFF') ? $langs->trans('Yes') : $langs->trans('No'))."\n";
|
|
|
|
if (getDolGlobalString('IMPORTZUGFERD_NOTIFY_PRICE_DIFF')) {
|
|
$body .= "- ".$langs->trans('IMPORTZUGFERD_PRICE_DIFF_THRESHOLD').": ".getDolGlobalInt('IMPORTZUGFERD_PRICE_DIFF_THRESHOLD', 10)."%\n";
|
|
}
|
|
|
|
$body .= "\n";
|
|
$body .= $langs->trans('NotifyTestSuccess');
|
|
|
|
return $this->sendEmail($subject, $body);
|
|
}
|
|
|
|
/**
|
|
* Send email using Dolibarr's mail system
|
|
*
|
|
* @param string $subject Email subject
|
|
* @param string $body Email body (plain text)
|
|
* @return int 1 if sent, -1 on error
|
|
*/
|
|
protected function sendEmail($subject, $body)
|
|
{
|
|
global $conf, $langs, $mysoc;
|
|
|
|
$to = $this->getNotifyEmail();
|
|
if (empty($to)) {
|
|
$this->error = 'No notification email configured';
|
|
return -1;
|
|
}
|
|
|
|
// Get sender
|
|
$from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
|
|
if (empty($from)) {
|
|
$from = $mysoc->email;
|
|
}
|
|
if (empty($from)) {
|
|
$this->error = 'No sender email configured';
|
|
return -1;
|
|
}
|
|
|
|
// Add module prefix to subject
|
|
$subject = '[ZUGFeRD Import] '.$subject;
|
|
|
|
// Create mail object
|
|
$mailfile = new CMailFile(
|
|
$subject,
|
|
$to,
|
|
$from,
|
|
$body,
|
|
array(), // files
|
|
array(), // mimefiles
|
|
array(), // ccfiles
|
|
'', // cc
|
|
'', // bcc
|
|
0, // deliveryreceipt
|
|
0, // msgishtml
|
|
'', // errors_to
|
|
'', // css
|
|
'', // trackid
|
|
'', // moreinheader
|
|
'standard', // sendcontext
|
|
'' // replyto
|
|
);
|
|
|
|
$result = $mailfile->sendfile();
|
|
|
|
if ($result) {
|
|
dol_syslog("ImportNotification: Email sent to ".$to." - Subject: ".$subject, LOG_INFO);
|
|
return 1;
|
|
} else {
|
|
$this->error = $mailfile->error;
|
|
$this->errors = $mailfile->errors;
|
|
dol_syslog("ImportNotification: Failed to send email - ".$this->error, LOG_ERR);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|