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; } } }