From 941aae90f7a7b95f4c48ce0c0bcaaa7e889360d4 Mon Sep 17 00:00:00 2001 From: data Date: Fri, 30 Jan 2026 10:25:03 +0100 Subject: [PATCH] =?UTF-8?q?Stabile=202.0=20Version=20erweiter=20auf=20Kund?= =?UTF-8?q?enauftrag,=20Angebot,=20Lieferantenbestellung=20Lager.=20Shopli?= =?UTF-8?q?nk=20angepasst=20neuer=20Tab=20danach=20immer=20im=20selben=20T?= =?UTF-8?q?ab.=20Kundenauftrag=20Button=20f=C3=BCr=20Bestellung=20aus=20de?= =?UTF-8?q?m=20Entwurf=20heraus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 0 .idea/SupplierLink3.iml | 0 README.md | 239 ++++-- admin/about.php | 134 +++- admin/setup.php | 942 ++++++++++-------------- class/actions_supplierlink3.class.php | 688 ++++++++++++++--- core/modules/modSupplierLink3.class.php | 17 +- create_supplier_order.php | 359 +++++++++ js/replenish.js | 167 +++++ langs/de_DE/supplierlink3.lang | 97 +++ langs/en_US/supplierlink3.lang | 91 ++- 11 files changed, 1973 insertions(+), 761 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .idea/SupplierLink3.iml create mode 100755 create_supplier_order.php create mode 100755 js/replenish.js create mode 100755 langs/de_DE/supplierlink3.lang diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.idea/SupplierLink3.iml b/.idea/SupplierLink3.iml old mode 100644 new mode 100755 diff --git a/README.md b/README.md index 5725b14..455ffa6 100755 --- a/README.md +++ b/README.md @@ -1,96 +1,217 @@ -# SUPPLIERLINK3 FOR [DOLIBARR ERP & CRM](https://www.dolibarr.org) +# SupplierLink3 for [Dolibarr ERP & CRM](https://www.dolibarr.org) + +**Version 2.0** | **License: GPL v3** | **Author: Eduard Wisch** + +Dolibarr module for linking supplier webshops with products and displaying stock levels. ## Features -Description of the module... +### Shop Links +- Direct access to supplier webshops from within Dolibarr +- Automatic URL generation with supplier article number +- Multi-supplier support with popup selection (sorted by price) +- Configurable shop icon (FontAwesome) +- Same supplier opens in same browser tab - +### Stock Display +Colored badges for quick overview: +- **Red**: Out of stock (< 1) +- **Orange**: Below minimum stock (alert threshold) +- **Gray**: Below desired stock +- **Green**: Sufficient stock -Other external modules are available on [Dolistore.com](https://www.dolistore.com). - -## Translations - -Translations can be completed manually by editing files in the module directories under `langs`. - - +### Supported Areas +- Customer Orders +- Proposals/Quotes +- Supplier Orders +- Stock Replenishment +- Product Card +### Quick Supplier Order Creation +- "Create Supplier Order" button in customer orders +- Automatic product and quantity transfer +- Supplier reference with customer name and order number ## Installation -Prerequisites: You must have Dolibarr ERP & CRM software installed. You can download it from [Dolistore.org](https://www.dolibarr.org). -You can also get a ready-to-use instance in the cloud from https://saas.dolibarr.org +### From ZIP file +1. Go to menu **Home > Setup > Modules > Deploy external module** +2. Upload the zip file +### Manual Installation +1. Copy module folder to `/custom/supplierlink3/` +2. In Dolibarr: **Setup > Modules > SupplierLink3** - activate +3. On activation, extrafield `shop_url` is created for suppliers -### From the ZIP file and GUI interface +## Configuration -If the module is a ready-to-deploy zip file, so with a name `module_xxx-version.zip` (e.g., when downloading it from a marketplace like [Dolistore](https://www.dolistore.com)), -go to menu `Home> Setup> Modules> Deploy external module` and upload the zip file. +### Admin Center +Settings under **Setup > Modules > SupplierLink3 > Settings** - +## Technical Details - +## Changelog -### Final steps +### Version 2.0 +- Admin center completely redesigned +- Settings per area (enable/disable) +- Configurable icons (FontAwesome) +- Debug mode disabled by default +- Bilingual (German/English) +- Stock replenishment list integrated +- Improved icon alignment -Using your browser: +### Version 1.0 +- Initial release +- Shop links in orders +- Stock badges +- Supplier order from customer order - - Log into Dolibarr as a super-administrator - - Go to "Setup"> "Modules" - - You should now be able to find and enable the module +## Translations +Translations are available in: +- German (de_DE) +- English (en_US) +Additional translations can be added by creating files in `langs/xx_XX/supplierlink3.lang` + +## Support + +For questions or issues: data@data-it-solution.de ## Licenses -### Main code - +### Main Code GPLv3 or (at your option) any later version. See file COPYING for more information. ### Documentation +All texts and readmes are licensed under [GFDL](https://www.gnu.org/licenses/fdl-1.3.en.html). -All texts and readme's are licensed under [GFDL](https://www.gnu.org/licenses/fdl-1.3.en.html). +--- + +# SupplierLink3 für [Dolibarr ERP & CRM](https://www.dolibarr.org) (Deutsch) + +**Version 2.0** | **Lizenz: GPL v3** | **Autor: Eduard Wisch** + +Dolibarr-Modul zur Verknüpfung von Lieferanten-Webshops mit Produkten und Anzeige von Lagerbeständen. + +## Funktionen + +### Shop-Links +- Direkter Zugang zu Lieferanten-Webshops aus Dolibarr heraus +- Automatische URL-Generierung mit Lieferanten-Artikelnummer +- Multi-Lieferanten-Unterstützung mit Popup-Auswahl (sortiert nach Preis) +- Konfigurierbares Shop-Symbol (FontAwesome) +- Gleicher Lieferant öffnet sich im gleichen Browser-Tab + +### Lagerbestand-Anzeige +Farbige Badges für schnelle Übersicht: +- **Rot**: Nicht auf Lager (< 1) +- **Orange**: Unter Mindestbestand (Alarm-Schwelle) +- **Grau**: Unter Wunschbestand +- **Grün**: Ausreichend auf Lager + +### Unterstützte Bereiche +- Kundenaufträge +- Angebote +- Lieferantenbestellungen +- Nachbestellung (Lager) +- Produktkarte + +### Schnelle Lieferantenbestellung +- Button "Lieferantenbestellung erstellen" im Kundenauftrag +- Automatische Übernahme von Produkten und Mengen +- Lieferanten-Referenz mit Kundenname und Auftragsnummer + +## Konfiguration + +### Shop-URL für Lieferanten einrichten +1. Lieferant öffnen > Bearbeiten +2. Extrafeld "Shop URL" ausfüllen (z.B. `https://shop.lieferant.de/artikel/`) +3. Die Lieferanten-Artikelnummer wird automatisch angehängt + +### Admin-Center +Einstellungen unter **Einstellungen > Module > SupplierLink3 > Einstellungen** + +#### Anzeige-Einstellungen +Aktivieren/Deaktivieren pro Bereich: +- Kundenaufträge +- Angebote +- Lieferantenbestellungen +- Nachbestellung (Lager) +- Produktkarte + +#### Symbol-Einstellungen +- **Shop-Symbol**: Standard `fas fa-store` +- **Lagerbestand-Symbol**: Optional + +#### Debug-Modus +- Standardmäßig deaktiviert +- Schreibt bei Aktivierung nach `/tmp/supplierlink3_debug.log` diff --git a/admin/about.php b/admin/about.php index 0be5fcd..0632ca4 100755 --- a/admin/about.php +++ b/admin/about.php @@ -1,7 +1,7 @@ - * Copyright (C) 2025 Eduard Wisch * Copyright (C) 2024 Frédéric France + * Copyright (C) 2025 Eduard Wisch * * 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 @@ -25,34 +25,31 @@ // Load Dolibarr environment $res = 0; -// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined) if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { - $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; + $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; } -// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME $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--; + $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"; + $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"; + $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; } -// Try main.inc.php using relative path if (!$res && file_exists("../../main.inc.php")) { - $res = @include "../../main.inc.php"; + $res = @include "../../main.inc.php"; } if (!$res && file_exists("../../../main.inc.php")) { - $res = @include "../../../main.inc.php"; + $res = @include "../../../main.inc.php"; } if (!$res) { - die("Include of main fails"); + die("Include of main fails"); } // Libraries @@ -60,59 +57,116 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; require_once '../lib/supplierlink3.lib.php'; -/** - * @var Conf $conf - * @var DoliDB $db - * @var HookManager $hookmanager - * @var Translate $langs - * @var User $user - */ - // Translations $langs->loadLangs(array("errors", "admin", "supplierlink3@supplierlink3")); // Access control if (!$user->admin) { - accessforbidden(); + accessforbidden(); } -// Parameters -$action = GETPOST('action', 'aZ09'); -$backtopage = GETPOST('backtopage', 'alpha'); - - -/* - * Actions - */ - -// None - - /* * View */ $form = new Form($db); -$help_url = ''; -$title = "SupplierLink3Setup"; +$title = $langs->trans("SupplierLink3Setup"); -llxHeader('', $langs->trans($title), $help_url, '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin_about'); +llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin_about'); // Subheader -$linkback = ''.$langs->trans("BackToModuleList").''; +$linkback = ''.$langs->trans("BackToModuleList").''; -print load_fiche_titre($langs->trans($title), $linkback, 'title_setup'); +print load_fiche_titre($title, $linkback, 'title_setup'); // Configuration header $head = supplierlink3AdminPrepareHead(); -print dol_get_fiche_head($head, 'about', $langs->trans($title), 0, 'supplierlink3@supplierlink3'); +print dol_get_fiche_head($head, 'about', $title, -1, 'supplierlink3@supplierlink3'); +// Module info dol_include_once('/supplierlink3/core/modules/modSupplierLink3.class.php'); $tmpmodule = new modSupplierLink3($db); -print $tmpmodule->getDescLong(); + +print ''; + +// Version +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print '
'.$langs->trans('SL3_Version').' & '.$langs->trans('SL3_Author').'
'.$langs->trans('SL3_Version').''.$tmpmodule->version.'
'.$langs->trans('SL3_Author').'Eduard Wisch <data@data-it-solution.de>
'.$langs->trans('SL3_License').'GPL v3
'; + +print '
'; + +// Features +print ''; +print ''; +print ''; +print ''; + +$features = array( + 'SL3_Feature1' => 'fa-store', + 'SL3_Feature2' => 'fa-boxes', + 'SL3_Feature3' => 'fa-truck-loading', + 'SL3_Feature4' => 'fa-users', +); + +foreach ($features as $key => $icon) { + print ''; + print ''; + print ''; +} + +print '
'.$langs->trans('SL3_Features').'
'.$langs->trans($key).'
'; + +print '
'; + +// Stock badge legend +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print '
'.$langs->trans('SL3_StockBadges', 'Lagerbestand-Badges').'
0'.$langs->trans('SL3_StockCritical').'
5'.$langs->trans('SL3_StockWarning').'
15'.$langs->trans('SL3_StockLow').'
42'.$langs->trans('SL3_StockOk').'
'; // Page end print dol_get_fiche_end(); + llxFooter(); $db->close(); diff --git a/admin/setup.php b/admin/setup.php index cbd6a78..50cc4d7 100755 --- a/admin/setup.php +++ b/admin/setup.php @@ -1,7 +1,7 @@ * Copyright (C) 2024 Frédéric France - * Copyright (C) 2025 Eduard Wisch + * Copyright (C) 2025 Eduard Wisch * * 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 @@ -25,296 +25,134 @@ // Load Dolibarr environment $res = 0; -// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined) if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { - $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; + $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; } -// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME $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--; + $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"; + $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"; + $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; } -// Try main.inc.php using relative path if (!$res && file_exists("../../main.inc.php")) { - $res = @include "../../main.inc.php"; + $res = @include "../../main.inc.php"; } if (!$res && file_exists("../../../main.inc.php")) { - $res = @include "../../../main.inc.php"; + $res = @include "../../../main.inc.php"; } if (!$res) { - die("Include of main fails"); + die("Include of main fails"); } // Libraries require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; require_once '../lib/supplierlink3.lib.php'; -//require_once "../class/myclass.class.php"; - -/** - * @var Conf $conf - * @var DoliDB $db - * @var HookManager $hookmanager - * @var Translate $langs - * @var User $user - */ // Translations $langs->loadLangs(array("admin", "supplierlink3@supplierlink3")); -// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context -/** @var HookManager $hookmanager */ -$hookmanager->initHooks(array('supplierlink3setup', 'globalsetup')); +// Access control +if (!$user->admin) { + accessforbidden(); +} // Parameters $action = GETPOST('action', 'aZ09'); -$backtopage = GETPOST('backtopage', 'alpha'); -$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php - -$value = GETPOST('value', 'alpha'); -$label = GETPOST('label', 'alpha'); -$scandir = GETPOST('scan_dir', 'alpha'); -$type = 'myobject'; - -$error = 0; -$setupnotempty = 0; - -// Access control -if (!$user->admin) { - accessforbidden(); -} - - -// Set this to 1 to use the factory to manage constants. Warning, the generated module will be compatible with version v15+ only -$useFormSetup = 1; - -if (!class_exists('FormSetup')) { - require_once DOL_DOCUMENT_ROOT.'/core/class/html.formsetup.class.php'; -} -$formSetup = new FormSetup($db); - -// Access control -if (!$user->admin) { - accessforbidden(); -} - - -// Enter here all parameters in your setup page - -// Setup conf for selection of an URL -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM1'); -$item->fieldParams['isMandatory'] = 1; -$item->fieldAttr['placeholder'] = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST']; -$item->cssClass = 'minwidth500'; - -// Setup conf for selection of a simple string input -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM2'); -$item->defaultFieldValue = 'default value'; -$item->fieldAttr['placeholder'] = 'A placeholder here'; -$item->helpText = 'Tooltip text'; - -// Setup conf for selection of a simple textarea input but we replace the text of field title -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM3'); -$item->nameText = $item->getNameText().' more html text '; - -// Setup conf for a selection of a Thirdparty -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM4'); -$item->setAsThirdpartyType(); - -// Setup conf for a selection of a boolean -$formSetup->newItem('SUPPLIERLINK3_MYPARAM5')->setAsYesNo(); - -// Setup conf for a selection of an Email template of type thirdparty -$formSetup->newItem('SUPPLIERLINK3_MYPARAM6')->setAsEmailTemplate('thirdparty'); - -// Setup conf for a selection of a secured key -//$formSetup->newItem('SUPPLIERLINK3_MYPARAM7')->setAsSecureKey(); - -// Setup conf for a selection of a Product -$formSetup->newItem('SUPPLIERLINK3_MYPARAM8')->setAsProduct(); - -// Add a title for a new section -$formSetup->newItem('NewSection')->setAsTitle(); - -$TField = array( - 'test01' => $langs->trans('test01'), - 'test02' => $langs->trans('test02'), - 'test03' => $langs->trans('test03'), - 'test04' => $langs->trans('test04'), - 'test05' => $langs->trans('test05'), - 'test06' => $langs->trans('test06'), -); - -// Setup conf for a simple combo list -$formSetup->newItem('SUPPLIERLINK3_MYPARAM9')->setAsSelect($TField); - -// Setup conf for a multiselect combo list -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM10'); -$item->setAsMultiSelect($TField); -$item->helpText = $langs->transnoentities('SUPPLIERLINK3_MYPARAM10'); - -// Setup conf for a category selection -$formSetup->newItem('SUPPLIERLINK3_CATEGORY_ID_XXX')->setAsCategory('product'); - -// Setup conf SUPPLIERLINK3_MYPARAM10 -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM10'); -$item->setAsColor(); -$item->defaultFieldValue = '#FF0000'; -//$item->fieldValue = ''; -//$item->fieldAttr = array() ; // fields attribute only for compatible fields like input text -//$item->fieldOverride = false; // set this var to override field output will override $fieldInputOverride and $fieldOutputOverride too -//$item->fieldInputOverride = false; // set this var to override field input -//$item->fieldOutputOverride = false; // set this var to override field output - -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM11')->setAsHtml(); -$item->nameText = $item->getNameText().' more html text '; -$item->fieldInputOverride = ''; -$item->helpText = $langs->transnoentities('HelpMessage'); -$item->cssClass = 'minwidth500'; - -$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM12'); -$item->fieldOverride = "Value forced, can't be modified"; -$item->cssClass = 'minwidth500'; - -//$item = $formSetup->newItem('SUPPLIERLINK3_MYPARAM13')->setAsDate(); // Not yet implemented - -// End of definition of parameters - - -$setupnotempty += count($formSetup->items); - - -$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); - -$moduledir = 'supplierlink3'; -$myTmpObjects = array(); -// TODO Scan list of objects to fill this array -$myTmpObjects['myobject'] = array('label' => 'MyObject', 'includerefgeneration' => 0, 'includedocgeneration' => 0, 'class' => 'MyObject'); - -$tmpobjectkey = GETPOST('object', 'aZ09'); -if ($tmpobjectkey && !array_key_exists($tmpobjectkey, $myTmpObjects)) { - accessforbidden('Bad value for object. Hack attempt ?'); -} - /* * Actions */ -// For retrocompatibility Dolibarr < 15.0 -if (versioncompare(explode('.', DOL_VERSION), array(15)) < 0 && $action == 'update' && !empty($user->admin)) { - $formSetup->saveConfFromPost(); +// Toggle display settings +if ($action == 'set_enable_ordercard') { + $value = GETPOST('value', 'int'); + dolibarr_set_const($db, 'SUPPLIERLINK3_ENABLE_ORDERCARD', $value, 'chaine', 0, '', $conf->entity); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; } -include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; - -if ($action == 'updateMask') { - $maskconst = GETPOST('maskconst', 'aZ09'); - $maskvalue = GETPOST('maskvalue', 'alpha'); - - if ($maskconst && preg_match('/_MASK$/', $maskconst)) { - $res = dolibarr_set_const($db, $maskconst, $maskvalue, 'chaine', 0, '', $conf->entity); - if (!($res > 0)) { - $error++; - } - } - - if (!$error) { - setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); - } else { - setEventMessages($langs->trans("Error"), null, 'errors'); - } -} elseif ($action == 'specimen' && $tmpobjectkey) { - $modele = GETPOST('module', 'alpha'); - - $className = $myTmpObjects[$tmpobjectkey]['class']; - $tmpobject = new $className($db); - '@phan-var-force MyObject $tmpobject'; - $tmpobject->initAsSpecimen(); - - // Search template files - $file = ''; - $className = ''; - $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); - foreach ($dirmodels as $reldir) { - $file = dol_buildpath($reldir."core/modules/supplierlink3/doc/pdf_".$modele."_".strtolower($tmpobjectkey).".modules.php", 0); - if (file_exists($file)) { - $className = "pdf_".$modele."_".strtolower($tmpobjectkey); - break; - } - } - - if ($className !== '') { - require_once $file; - - $module = new $className($db); - '@phan-var-force ModelePDFMyObject $module'; - - '@phan-var-force ModelePDFMyObject $module'; - - if ($module->write_file($tmpobject, $langs) > 0) { - header("Location: ".DOL_URL_ROOT."/document.php?modulepart=supplierlink3-".strtolower($tmpobjectkey)."&file=SPECIMEN.pdf"); - return; - } else { - setEventMessages($module->error, null, 'errors'); - dol_syslog($module->error, LOG_ERR); - } - } else { - setEventMessages($langs->trans("ErrorModuleNotFound"), null, 'errors'); - dol_syslog($langs->trans("ErrorModuleNotFound"), LOG_ERR); - } -} elseif ($action == 'setmod') { - // TODO Check if numbering module chosen can be activated by calling method canBeActivated - if (!empty($tmpobjectkey)) { - $constforval = 'SUPPLIERLINK3_'.strtoupper($tmpobjectkey)."_ADDON"; - dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity); - } -} elseif ($action == 'set') { - // Activate a model - $ret = addDocumentModel($value, $type, $label, $scandir); -} elseif ($action == 'del') { - $ret = delDocumentModel($value, $type); - if ($ret > 0) { - if (!empty($tmpobjectkey)) { - $constforval = 'SUPPLIERLINK3_'.strtoupper($tmpobjectkey).'_ADDON_PDF'; - if (getDolGlobalString($constforval) == "$value") { - dolibarr_del_const($db, $constforval, $conf->entity); - } - } - } -} elseif ($action == 'setdoc') { - // Set or unset default model - if (!empty($tmpobjectkey)) { - $constforval = 'SUPPLIERLINK3_'.strtoupper($tmpobjectkey).'_ADDON_PDF'; - if (dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity)) { - // The constant that was read before the new set - // We therefore requires a variable to have a coherent view - $conf->global->{$constforval} = $value; - } - - // We disable/enable the document template (into llx_document_model table) - $ret = delDocumentModel($value, $type); - if ($ret > 0) { - $ret = addDocumentModel($value, $type, $label, $scandir); - } - } -} elseif ($action == 'unsetdoc') { - if (!empty($tmpobjectkey)) { - $constforval = 'SUPPLIERLINK3_'.strtoupper($tmpobjectkey).'_ADDON_PDF'; - dolibarr_del_const($db, $constforval, $conf->entity); - } +if ($action == 'set_enable_propalcard') { + $value = GETPOST('value', 'int'); + dolibarr_set_const($db, 'SUPPLIERLINK3_ENABLE_PROPALCARD', $value, 'chaine', 0, '', $conf->entity); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; } -$action = 'edit'; +if ($action == 'set_enable_supplierorder') { + $value = GETPOST('value', 'int'); + dolibarr_set_const($db, 'SUPPLIERLINK3_ENABLE_SUPPLIERORDER', $value, 'chaine', 0, '', $conf->entity); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} + +if ($action == 'set_enable_replenish') { + $value = GETPOST('value', 'int'); + dolibarr_set_const($db, 'SUPPLIERLINK3_ENABLE_REPLENISH', $value, 'chaine', 0, '', $conf->entity); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} + +if ($action == 'set_enable_productcard') { + $value = GETPOST('value', 'int'); + dolibarr_set_const($db, 'SUPPLIERLINK3_ENABLE_PRODUCTCARD', $value, 'chaine', 0, '', $conf->entity); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} + +// Save icon settings +if ($action == 'save_icons') { + $shopIcon = GETPOST('shop_icon', 'alpha'); + $stockIcon = GETPOST('stock_icon', 'alpha'); + + // Validate FontAwesome class format + if (!empty($shopIcon) && preg_match('/^fa[srb]?\s+fa-[\w-]+$/', $shopIcon)) { + dolibarr_set_const($db, 'SUPPLIERLINK3_SHOP_ICON', $shopIcon, 'chaine', 0, '', $conf->entity); + } elseif (empty($shopIcon)) { + dolibarr_del_const($db, 'SUPPLIERLINK3_SHOP_ICON', $conf->entity); + } + + if (!empty($stockIcon) && preg_match('/^fa[srb]?\s+fa-[\w-]+$/', $stockIcon)) { + dolibarr_set_const($db, 'SUPPLIERLINK3_STOCK_ICON', $stockIcon, 'chaine', 0, '', $conf->entity); + } elseif (empty($stockIcon)) { + dolibarr_del_const($db, 'SUPPLIERLINK3_STOCK_ICON', $conf->entity); + } + + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} + +// Debug mode +if ($action == 'setdebug') { + $value = GETPOST('value', 'int'); + dolibarr_set_const($db, 'SUPPLIERLINK3_DEBUG_MODE', $value, 'chaine', 0, '', $conf->entity); + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} + +if ($action == 'cleardebuglog') { + $logFile = '/tmp/supplierlink3_debug.log'; + if (file_exists($logFile)) { + unlink($logFile); + setEventMessages($langs->trans('SL3_DebugLogCleared'), null, 'mesgs'); + } + header("Location: ".$_SERVER["PHP_SELF"]); + exit; +} /* @@ -323,307 +161,333 @@ $action = 'edit'; $form = new Form($db); -$help_url = ''; -$title = "SupplierLink3Setup"; +$title = $langs->trans("SupplierLink3Setup"); -llxHeader('', $langs->trans($title), $help_url, '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin'); +llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin'); // Subheader -$linkback = ''.$langs->trans("BackToModuleList").''; +$linkback = ''.$langs->trans("BackToModuleList").''; -print load_fiche_titre($langs->trans($title), $linkback, 'title_setup'); +print load_fiche_titre($title, $linkback, 'title_setup'); // Configuration header $head = supplierlink3AdminPrepareHead(); -print dol_get_fiche_head($head, 'settings', $langs->trans($title), -1, "supplierlink3@supplierlink3"); +print dol_get_fiche_head($head, 'settings', $title, -1, "supplierlink3@supplierlink3"); -// Setup page goes here -echo ''.$langs->trans("SupplierLink3SetupPage").'

'; +print ''.$langs->trans('SupplierLink3Description').'

'; +// ============================================================================= +// DISPLAY SETTINGS +// ============================================================================= +print ''; +print ''; +print ''; +print ''; -/*if ($action == 'edit') { - print $formSetup->generateOutput(true); - print '
'; - } elseif (!empty($formSetup->items)) { - print $formSetup->generateOutput(); - print '
'; - print ''.$langs->trans("Modify").''; - print '
'; - } - */ -if (!empty($formSetup->items)) { - print $formSetup->generateOutput(true); - print '
'; +print ''; +print ''; +print ''; + +// Customer Orders (ordercard) +$enableOrderCard = getDolGlobalInt('SUPPLIERLINK3_ENABLE_ORDERCARD', 1); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Proposals (propalcard) +$enablePropalCard = getDolGlobalInt('SUPPLIERLINK3_ENABLE_PROPALCARD', 1); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Supplier Orders (ordersuppliercard) +$enableSupplierOrder = getDolGlobalInt('SUPPLIERLINK3_ENABLE_SUPPLIERORDER', 1); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Stock Replenishment (stockreplenishlist) +$enableReplenish = getDolGlobalInt('SUPPLIERLINK3_ENABLE_REPLENISH', 1); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Product Card (productcard) +$enableProductCard = getDolGlobalInt('SUPPLIERLINK3_ENABLE_PRODUCTCARD', 1); +print ''; +print ''; +print ''; +print ''; +print ''; + +print '
'.$langs->trans('SL3_DisplaySettings').'
'.$langs->trans('SL3_DisplaySettingsDesc').'
'.$langs->trans('SL3_EnableOrderCard').''; +if ($enableOrderCard) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''.$langs->trans('SL3_EnableOrderCardDesc').'
'.$langs->trans('SL3_EnablePropalCard').''; +if ($enablePropalCard) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''.$langs->trans('SL3_EnablePropalCardDesc').'
'.$langs->trans('SL3_EnableSupplierOrder').''; +if ($enableSupplierOrder) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''.$langs->trans('SL3_EnableSupplierOrderDesc').'
'.$langs->trans('SL3_EnableReplenish').''; +if ($enableReplenish) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''.$langs->trans('SL3_EnableReplenishDesc').'
'.$langs->trans('SL3_EnableProductCard').''; +if ($enableProductCard) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''.$langs->trans('SL3_EnableProductCardDesc').'
'; + +print '
'; + +// ============================================================================= +// ICON SETTINGS +// ============================================================================= +print '
'; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; + +// Shop Icon +$shopIcon = getDolGlobalString('SUPPLIERLINK3_SHOP_ICON', 'fas fa-store'); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Stock Icon (optional, for future use) +$stockIcon = getDolGlobalString('SUPPLIERLINK3_STOCK_ICON', ''); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Icon preview row +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; + +print '
'.$langs->trans('SL3_IconSettings').'
'.$langs->trans('SL3_IconSettingsDesc').'
'.$langs->trans('SL3_ShopIcon').''; +print ''; +print ' '; +print ''.$langs->trans('SL3_ShopIconDesc').'
'.$langs->trans('SL3_StockIcon').''; +print ''; +if (!empty($stockIcon)) { + print ' '; +} +print ''.$langs->trans('SL3_StockIconDesc').'
'.$langs->trans('SL3_IconPreview').''; +print '
'; + +// Preview: Shop link with stock badge +print '
'; +print ''; +print ''; +print ''; +print ''; +print '42'; +print ''; +print '
'; + +print '
'; +print ''; +print ''; +print ''; +print ''; +print '8'; +print ''; +print '
'; + +print '
'; +print ''; +print ''; +print '0'; +print ''; +print '
'; + +print '
'; +print '
'; +print ''; +print '
'; +print '
'; + +print '
'; + +// ============================================================================= +// DEBUG SETTINGS +// ============================================================================= +print ''; +print ''; +print ''; +print ''; + +// Debug-Modus (default: OFF) +$debugMode = getDolGlobalInt('SUPPLIERLINK3_DEBUG_MODE', 0); +print ''; +print ''; +print ''; +print ''; +print ''; + +// Debug-Log anzeigen +$logFile = '/tmp/supplierlink3_debug.log'; +print ''; +print ''; +print ''; +print ''; + +print '
'.$langs->trans('SL3_DebugSettings').'
'.$langs->trans('SL3_DebugMode').''; +if ($debugMode) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''.$langs->trans('SL3_DebugModeDesc').'
'.$langs->trans('SL3_DebugLog').''; +if (file_exists($logFile)) { + $logContent = file_get_contents($logFile); + $logSize = filesize($logFile); + print ''.$langs->trans('SL3_DebugLogFile').': '.$logFile.' ('.$langs->trans('SL3_DebugLogSize').': '.round($logSize/1024, 2).' KB)
'; + print ''.$langs->trans('SL3_DebugLogClear').''; + print '

'; + print ''.$langs->trans('SL3_DebugLogLastEntries').':
'; + print ''; +} else { + print ''.$langs->trans('SL3_DebugLogEmpty').''; +} +print '
'; + +print '
'; + +// ============================================================================= +// REGISTERED HOOKS +// ============================================================================= +print ''; +print ''; +print ''; +print ''; + +$hooks = array( + 'ordersuppliercard' => $langs->trans('SL3_HookOrderSupplier'), + 'ordercard' => $langs->trans('SL3_HookOrderCard'), + 'propalcard' => $langs->trans('SL3_HookPropalCard'), + 'productcard' => $langs->trans('SL3_HookProductCard'), + 'productpricecard' => $langs->trans('SL3_HookProductPrice'), + 'stockreplenishlist' => $langs->trans('SL3_HookReplenish'), +); + +// Check which hooks are enabled +$enabledHooks = array( + 'ordersuppliercard' => getDolGlobalInt('SUPPLIERLINK3_ENABLE_SUPPLIERORDER', 1), + 'ordercard' => getDolGlobalInt('SUPPLIERLINK3_ENABLE_ORDERCARD', 1), + 'propalcard' => getDolGlobalInt('SUPPLIERLINK3_ENABLE_PROPALCARD', 1), + 'productcard' => getDolGlobalInt('SUPPLIERLINK3_ENABLE_PRODUCTCARD', 1), + 'productpricecard' => getDolGlobalInt('SUPPLIERLINK3_ENABLE_PRODUCTCARD', 1), + 'stockreplenishlist' => getDolGlobalInt('SUPPLIERLINK3_ENABLE_REPLENISH', 1), +); + +foreach ($hooks as $hook => $desc) { + $isEnabled = $enabledHooks[$hook] ?? 1; + print ''; + print ''; + print ''; + print ''; + print ''; } - -foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) { - if (!empty($myTmpObjectArray['includerefgeneration'])) { - // Numbering models - - $setupnotempty++; - - print load_fiche_titre($langs->trans("NumberingModules", $myTmpObjectArray['label']), '', ''); - - print '
'.$langs->trans('SL3_RegisteredHooks').'
'.$hook.''.$desc.''; + if ($isEnabled) { + print ''.$langs->trans("Activated").''; + } else { + print ''.$langs->trans("Disabled").''; + } + print '
'; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''; - print ''."\n"; - - clearstatcache(); - - foreach ($dirmodels as $reldir) { - $dir = dol_buildpath($reldir."core/modules/".$moduledir); - - if (is_dir($dir)) { - $handle = opendir($dir); - if (is_resource($handle)) { - while (($file = readdir($handle)) !== false) { - if (strpos($file, 'mod_'.strtolower($myTmpObjectKey).'_') === 0 && substr($file, dol_strlen($file) - 3, 3) == 'php') { - $file = substr($file, 0, dol_strlen($file) - 4); - - require_once $dir.'/'.$file.'.php'; - - $module = new $file($db); - '@phan-var-force ModeleNumRefMyObject $module'; - - // Show modules according to features level - if ($module->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) { - continue; - } - if ($module->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) { - continue; - } - - if ($module->isEnabled()) { - dol_include_once('/'.$moduledir.'/class/'.strtolower($myTmpObjectKey).'.class.php'); - - print ''; - - // Show example of numbering model - print ''."\n"; - - print ''; - - $className = $myTmpObjectArray['class']; - $mytmpinstance = new $className($db); - '@phan-var-force MyObject $mytmpinstance'; - $mytmpinstance->initAsSpecimen(); - - // Info - $htmltooltip = ''; - $htmltooltip .= ''.$langs->trans("Version").': '.$module->getVersion().'
'; - - $nextval = $module->getNextValue($mytmpinstance); - if ("$nextval" != $langs->trans("NotAvailable")) { // Keep " on nextval - $htmltooltip .= ''.$langs->trans("NextValue").': '; - if ($nextval) { - if (preg_match('/^Error/', $nextval) || $nextval == 'NotConfigured') { - $nextval = $langs->trans($nextval); - } - $htmltooltip .= $nextval.'
'; - } else { - $htmltooltip .= $langs->trans($module->error).'
'; - } - } - - print ''; - - print "\n"; - } - } - } - closedir($handle); - } - } - } - print "
'.$langs->trans("Name").''.$langs->trans("Description").''.$langs->trans("Example").''.$langs->trans("Status").''.$langs->trans("ShortInfo").'
'.$module->getName($langs)."\n"; - print $module->info($langs); - print ''; - $tmp = $module->getExample(); - if (preg_match('/^Error/', $tmp)) { - $langs->load("errors"); - print '
'.$langs->trans($tmp).'
'; - } elseif ($tmp == 'NotConfigured') { - print $langs->trans($tmp); - } else { - print $tmp; - } - print '
'; - $constforvar = 'SUPPLIERLINK3_'.strtoupper($myTmpObjectKey).'_ADDON'; - $defaultifnotset = 'thevaluetousebydefault'; - $activenumberingmodel = getDolGlobalString($constforvar, $defaultifnotset); - if ($activenumberingmodel == $file) { - print img_picto($langs->trans("Activated"), 'switch_on'); - } else { - print ''; - print img_picto($langs->trans("Disabled"), 'switch_off'); - print ''; - } - print ''; - print $form->textwithpicto('', $htmltooltip, 1, 'info'); - print '

\n"; - } - - if (!empty($myTmpObjectArray['includedocgeneration'])) { - /* - * Document templates generators - */ - $setupnotempty++; - $type = strtolower($myTmpObjectKey); - - print load_fiche_titre($langs->trans("DocumentModules", $myTmpObjectKey), '', ''); - - // Load array def with activated templates - $def = array(); - $sql = "SELECT nom"; - $sql .= " FROM ".$db->prefix()."document_model"; - $sql .= " WHERE type = '".$db->escape($type)."'"; - $sql .= " AND entity = ".$conf->entity; - $resql = $db->query($sql); - if ($resql) { - $i = 0; - $num_rows = $db->num_rows($resql); - while ($i < $num_rows) { - $array = $db->fetch_array($resql); - array_push($def, $array[0]); - $i++; - } - } else { - dol_print_error($db); - } - - print ''."\n"; - print ''."\n"; - print ''; - print ''; - print '\n"; - print '\n"; - print ''; - print ''; - print "\n"; - - clearstatcache(); - - foreach ($dirmodels as $reldir) { - foreach (array('', '/doc') as $valdir) { - $realpath = $reldir."core/modules/".$moduledir.$valdir; - $dir = dol_buildpath($realpath); - - if (is_dir($dir)) { - $handle = opendir($dir); - if (is_resource($handle)) { - $filelist = array(); - while (($file = readdir($handle)) !== false) { - $filelist[] = $file; - } - closedir($handle); - arsort($filelist); - - foreach ($filelist as $file) { - if (preg_match('/\.modules\.php$/i', $file) && preg_match('/^(pdf_|doc_)/', $file)) { - if (file_exists($dir.'/'.$file)) { - $name = substr($file, 4, dol_strlen($file) - 16); - $className = substr($file, 0, dol_strlen($file) - 12); - - require_once $dir.'/'.$file; - $module = new $className($db); - '@phan-var-force ModelePDFMyObject $module'; - - $modulequalified = 1; - if ($module->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) { - $modulequalified = 0; - } - if ($module->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) { - $modulequalified = 0; - } - - if ($modulequalified) { - print ''; - - // Active - if (in_array($name, $def)) { - print ''; - } else { - print '"; - } - - // Default - print ''; - - // Info - $htmltooltip = ''.$langs->trans("Name").': '.$module->name; - $htmltooltip .= '
'.$langs->trans("Type").': '.($module->type ? $module->type : $langs->trans("Unknown")); - if ($module->type == 'pdf') { - $htmltooltip .= '
'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur; - } - $htmltooltip .= '
'.$langs->trans("Path").': '.preg_replace('/^\//', '', $realpath).'/'.$file; - - $htmltooltip .= '

'.$langs->trans("FeaturesSupported").':'; - $htmltooltip .= '
'.$langs->trans("Logo").': '.yn($module->option_logo, 1, 1); - $htmltooltip .= '
'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang, 1, 1); - - print ''; - - // Preview - print ''; - - print "\n"; - } - } - } - } - } - } - } - } - - print '
'.$langs->trans("Name").''.$langs->trans("Description").''.$langs->trans("Status")."'.$langs->trans("Default")."'.$langs->trans("ShortInfo").''.$langs->trans("Preview").'
'; - print(empty($module->name) ? $name : $module->name); - print "\n"; - if (method_exists($module, 'info')) { - print $module->info($langs); // @phan-suppress-current-line PhanUndeclaredMethod - } else { - print $module->description; - } - print ''."\n"; - print ''; - print img_picto($langs->trans("Enabled"), 'switch_on'); - print ''; - print ''."\n"; - print 'scandir).'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"), 'switch_off').''; - print "'; - $constforvar = 'SUPPLIERLINK3_'.strtoupper($myTmpObjectKey).'_ADDON_PDF'; - if (getDolGlobalString($constforvar) == $name) { - //print img_picto($langs->trans("Default"), 'on'); - // Even if choice is the default value, we allow to disable it. Replace this with previous line if you need to disable unset - print 'scandir).'&label='.urlencode($module->name).'&type='.urlencode($type).'" alt="'.$langs->trans("Disable").'">'.img_picto($langs->trans("Enabled"), 'on').''; - } else { - print 'scandir).'&label='.urlencode($module->name).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').''; - } - print ''; - print $form->textwithpicto('', $htmltooltip, 1, 'info'); - print ''; - if ($module->type == 'pdf') { - $newname = preg_replace('/_'.preg_quote(strtolower($myTmpObjectKey), '/').'/', '', $name); - print ''.img_object($langs->trans("Preview"), 'pdf').''; - } else { - print img_object($langs->transnoentitiesnoconv("PreviewNotAvailable"), 'generic'); - } - print '
'; - } -} - -if (empty($setupnotempty)) { - print '
'.$langs->trans("NothingToSetup"); -} +print ''; // Page end print dol_get_fiche_end(); +// JavaScript for live icon preview +print ''; + llxFooter(); $db->close(); diff --git a/class/actions_supplierlink3.class.php b/class/actions_supplierlink3.class.php index 7634dc6..c7572b6 100755 --- a/class/actions_supplierlink3.class.php +++ b/class/actions_supplierlink3.class.php @@ -36,6 +36,16 @@ class ActionsSupplierLink3 extends CommonHookActions */ public $db; + /** + * @var int Debug mode (0=off, 1=on) + */ + private $debug = 0; + + /** + * @var string Debug log file path + */ + private $debugLogFile = '/tmp/supplierlink3_debug.log'; + /** * @var string Error code (or message) */ @@ -63,6 +73,16 @@ class ActionsSupplierLink3 extends CommonHookActions public $priority; + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + /** + * @var string Shop icon class (FontAwesome) + */ + private $shopIcon = 'fas fa-store'; + /** * Constructor * @@ -71,6 +91,38 @@ class ActionsSupplierLink3 extends CommonHookActions public function __construct($db) { $this->db = $db; + + // Debug-Modus aus Dolibarr-Konfiguration laden (Standard: AUS) + $this->debug = getDolGlobalInt('SUPPLIERLINK3_DEBUG_MODE', 0); + + // Shop-Icon aus Konfiguration laden + $this->shopIcon = getDolGlobalString('SUPPLIERLINK3_SHOP_ICON', 'fas fa-store'); + + // Nur loggen wenn Debug aktiviert + if ($this->debug) { + error_log('['.date('Y-m-d H:i:s').'] [CONSTRUCTOR] ActionsSupplierLink3 instanziiert'."\n", 3, $this->debugLogFile); + } + } + + /** + * Debug-Log schreiben + * + * @param string $message Nachricht + * @param string $method Methodenname + */ + private function debugLog($message, $method = '') + { + if (!$this->debug) { + return; + } + + $logLine = '['.date('Y-m-d H:i:s').']'; + if ($method) { + $logLine .= ' ['.$method.']'; + } + $logLine .= ' '.$message."\n"; + + error_log($logLine, 3, $this->debugLogFile); } @@ -401,111 +453,443 @@ class ActionsSupplierLink3 extends CommonHookActions return 1; }*/ /* Add other hook methods here... */ - public function printObjectLine($parameters, &$object, &$action, $hookmanager) + + /** + * Holt alle Lieferanten mit Shop-URL für ein Produkt + * + * @param int $fk_product Produkt-ID + * @return array Array mit Lieferanten-Daten, sortiert nach Preis aufsteigend + */ + private function getAllSuppliersForProduct($fk_product) + { + $suppliers = array(); + + $sql = "SELECT DISTINCT + pfp.fk_soc as supplier_id, + pfp.ref_fourn, + pfp.price, + pfp.quantity as min_qty, + s.nom as supplier_name, + se.shop_url + FROM ".MAIN_DB_PREFIX."product_fournisseur_price pfp + INNER JOIN ".MAIN_DB_PREFIX."societe s ON s.rowid = pfp.fk_soc + LEFT JOIN ".MAIN_DB_PREFIX."societe_extrafields se ON se.fk_object = pfp.fk_soc + WHERE pfp.fk_product = ".(int)$fk_product." + AND se.shop_url IS NOT NULL + AND se.shop_url != '' + ORDER BY pfp.price ASC"; + + $resql = $this->db->query($sql); + if ($resql) { + while ($obj = $this->db->fetch_object($resql)) { + $suppliers[] = array( + 'supplier_id' => $obj->supplier_id, + 'ref_fourn' => $obj->ref_fourn, + 'price' => $obj->price, + 'min_qty' => $obj->min_qty, + 'supplier_name' => $obj->supplier_name, + 'shop_url' => rtrim($obj->shop_url, '/'), + 'full_url' => rtrim($obj->shop_url, '/') . '/' . $obj->ref_fourn + ); + } + } + + return $suppliers; + } + + /** + * Generiert HTML-Badge für Lagerbestand + * + * 4 Zustände: + * - ROT: stock <= 0 (Nicht verfügbar) + * - ORANGE: stock < seuil_stock_alerte (Unter Mindestmenge) + * - GRAU: stock < desiredstock (Unter Wunschmenge) + * - GRÜN: stock >= desiredstock (Ausreichend) + * + * @param float $qtyStock Aktueller Lagerbestand + * @param float $desiredQty Wunschbestand + * @param float $alertQty Mindestbestand (seuil_stock_alerte) + * @return string HTML-Badge + */ + private function getStockBadgeHtml($qtyStock, $desiredQty, $alertQty = 0) + { + if ($qtyStock <= 0) { + // ROT: Nicht verfügbar (0 oder negativ) + $badgeClass = 'badge-danger'; + $icon = 'fa-times-circle'; + $tooltip = 'Nicht auf Lager'; + } elseif ($alertQty > 0 && $qtyStock < $alertQty) { + // ORANGE: Unter Mindestmenge (Alarm-Schwelle) + $badgeClass = 'badge-warning'; + $icon = 'fa-exclamation-triangle'; + $tooltip = 'Unter Mindestmenge ('.(int)$alertQty.')'; + } elseif ($desiredQty > 0 && $qtyStock < $desiredQty) { + // GRAU: Unter Wunschmenge + $badgeClass = 'badge-secondary'; + $icon = 'fa-box-open'; + $tooltip = 'Unter Wunschmenge ('.(int)$desiredQty.')'; + } else { + // GRÜN: Ausreichend auf Lager + $badgeClass = 'badge-success'; + $icon = 'fa-check-circle'; + $tooltip = 'Ausreichend auf Lager'; + } + + $html = ''; + $html .= ''; + $html .= number_format($qtyStock, 0, ',', '.'); + $html .= ''; + + return $html; + } + + /** + * Generiert Shop-Link HTML (Icon oder Modal-Trigger für mehrere Lieferanten) + * + * @param array $suppliers Array der Lieferanten + * @param int $currentSupplierId Aktueller Lieferant der Bestellung + * @param string $refFourn Lieferanten-Artikelnummer + * @param int $lineId Zeilen-ID für eindeutige Modal-ID + * @return string HTML für Shop-Link + */ + private function getShopLinkHtml($suppliers, $currentSupplierId, $refFourn, $lineId) + { + if (empty($suppliers)) { + return ''; + } + + // Fall 1: Nur ein Lieferant mit Shop-URL - direkter Link + if (count($suppliers) == 1) { + $supplier = $suppliers[0]; + $html = 'shopIcon.'">'; + $html .= ''; + return $html; + } + + // Fall 2: Mehrere Lieferanten - Modal-Trigger + $modalId = 'supplier_modal_'.$lineId; + + // Trigger-Button mit Dropdown-Pfeil + $html = 'shopIcon.'">'; + $html .= ''; + $html .= ''; + + // Modal-Container (versteckt) + $html .= '