Stabile 2.0 Version erweiter auf Kundenauftrag, Angebot, Lieferantenbestellung
Lager. Shoplink angepasst neuer Tab danach immer im selben Tab. Kundenauftrag Button für Bestellung aus dem Entwurf heraus
This commit is contained in:
parent
b970a66339
commit
941aae90f7
11 changed files with 1973 additions and 761 deletions
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.idea/SupplierLink3.iml
Normal file → Executable file
0
.idea/SupplierLink3.iml
Normal file → Executable file
239
README.md
239
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
|
## 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
|
||||||
{imgmd}
|
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).
|
### Supported Areas
|
||||||
|
- Customer Orders
|
||||||
## Translations
|
- Proposals/Quotes
|
||||||
|
- Supplier Orders
|
||||||
Translations can be completed manually by editing files in the module directories under `langs`.
|
- Stock Replenishment
|
||||||
|
- Product Card
|
||||||
<!--
|
|
||||||
This module contains also a sample configuration for Transifex, under the hidden directory [.tx](.tx), so it is possible to manage translation using this service.
|
|
||||||
|
|
||||||
For more information, see the [translator's documentation](https://wiki.dolibarr.org/index.php/Translator_documentation).
|
|
||||||
|
|
||||||
There is a [Transifex project](https://transifex.com/projects/p/dolibarr-module-template) for this module.
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
### 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
|
## Installation
|
||||||
|
|
||||||
Prerequisites: You must have Dolibarr ERP & CRM software installed. You can download it from [Dolistore.org](https://www.dolibarr.org).
|
### From ZIP file
|
||||||
You can also get a ready-to-use instance in the cloud from https://saas.dolibarr.org
|
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)),
|
### Admin Center
|
||||||
go to menu `Home> Setup> Modules> Deploy external module` and upload the zip file.
|
Settings under **Setup > Modules > SupplierLink3 > Settings**
|
||||||
|
|
||||||
<!--
|
#### Display Settings
|
||||||
|
Enable/disable the function for each area:
|
||||||
|
- Customer Orders
|
||||||
|
- Proposals
|
||||||
|
- Supplier Orders
|
||||||
|
- Stock Replenishment
|
||||||
|
- Product Card
|
||||||
|
|
||||||
Note: If this screen tells you that there is no "custom" directory, check that your setup is correct:
|
#### Icon Settings
|
||||||
|
Customizable FontAwesome icons:
|
||||||
|
- **Shop Icon**: Default `fas fa-store`
|
||||||
|
- **Stock Icon**: Optional
|
||||||
|
|
||||||
- In your Dolibarr installation directory, edit the `htdocs/conf/conf.php` file and check that following lines are not commented:
|
#### Debug Mode
|
||||||
|
- Disabled by default
|
||||||
|
- When enabled, writes to `/tmp/supplierlink3_debug.log`
|
||||||
|
|
||||||
```php
|
### Setting up Shop URL for Suppliers
|
||||||
//$dolibarr_main_url_root_alt ...
|
1. Open supplier > Edit
|
||||||
//$dolibarr_main_document_root_alt ...
|
2. Fill extrafield "Shop URL" (e.g. `https://shop.supplier.com/article/`)
|
||||||
```
|
3. The supplier article number is automatically appended
|
||||||
|
|
||||||
- Uncomment them if necessary (delete the leading `//`) and assign the proper value according to your Dolibarr installation
|
## Usage
|
||||||
|
|
||||||
For example :
|
### In Orders/Proposals
|
||||||
|
- Click shop icon next to stock level
|
||||||
|
- Multiple suppliers: Dropdown with price comparison
|
||||||
|
- Same supplier opens in same browser tab
|
||||||
|
|
||||||
- UNIX:
|
### Creating Supplier Order
|
||||||
```php
|
1. Open customer order
|
||||||
$dolibarr_main_url_root_alt = '/custom';
|
2. Click "Create Supplier Order" button
|
||||||
$dolibarr_main_document_root_alt = '/var/www/Dolibarr/htdocs/custom';
|
3. Select supplier
|
||||||
```
|
4. Select products
|
||||||
|
5. Create order
|
||||||
|
|
||||||
- Windows:
|
## Technical Details
|
||||||
```php
|
|
||||||
$dolibarr_main_url_root_alt = '/custom';
|
|
||||||
$dolibarr_main_document_root_alt = 'C:/My Web Sites/Dolibarr/htdocs/custom';
|
|
||||||
```
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
### Hooks
|
||||||
|
- `ordersuppliercard` - Supplier Orders
|
||||||
|
- `ordercard` - Customer Orders
|
||||||
|
- `propalcard` - Proposals
|
||||||
|
- `productcard` - Product Card
|
||||||
|
- `productpricecard` - Product Prices
|
||||||
|
- `stockreplenishlist` - Replenishment List
|
||||||
|
|
||||||
### From a GIT repository
|
### Database
|
||||||
|
Uses extrafield `shop_url` in `llx_societe_extrafields`.
|
||||||
|
|
||||||
Clone the repository in `$dolibarr_main_document_root_alt/supplierlink3`
|
### File Structure
|
||||||
|
```
|
||||||
```shell
|
supplierlink3/
|
||||||
cd ....../custom
|
├── admin/
|
||||||
git clone git@github.com:gitlogin/supplierlink3.git supplierlink3
|
│ ├── setup.php # Settings
|
||||||
|
│ └── about.php # About page
|
||||||
|
├── class/
|
||||||
|
│ └── actions_supplierlink3.class.php # Hook implementation
|
||||||
|
├── core/modules/
|
||||||
|
│ └── modSupplierLink3.class.php # Module definition
|
||||||
|
├── js/
|
||||||
|
│ └── replenish.js # JavaScript for replenishment
|
||||||
|
├── langs/
|
||||||
|
│ ├── de_DE/supplierlink3.lang
|
||||||
|
│ └── en_US/supplierlink3.lang
|
||||||
|
├── lib/
|
||||||
|
│ └── supplierlink3.lib.php
|
||||||
|
└── create_supplier_order.php # Create supplier order
|
||||||
```
|
```
|
||||||
|
|
||||||
-->
|
## 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
|
## Translations
|
||||||
- Go to "Setup"> "Modules"
|
|
||||||
- You should now be able to find and enable the module
|
|
||||||
|
|
||||||
|
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
|
## Licenses
|
||||||
|
|
||||||
### Main code
|
### Main Code
|
||||||
|
|
||||||
GPLv3 or (at your option) any later version. See file COPYING for more information.
|
GPLv3 or (at your option) any later version. See file COPYING for more information.
|
||||||
|
|
||||||
### Documentation
|
### 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`
|
||||||
|
|
|
||||||
116
admin/about.php
116
admin/about.php
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
/* Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
|
/* Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||||
* Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
|
|
||||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||||
|
* Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -25,11 +25,9 @@
|
||||||
|
|
||||||
// Load Dolibarr environment
|
// Load Dolibarr environment
|
||||||
$res = 0;
|
$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"])) {
|
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'];
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
||||||
$tmp2 = realpath(__FILE__);
|
$tmp2 = realpath(__FILE__);
|
||||||
$i = strlen($tmp) - 1;
|
$i = strlen($tmp) - 1;
|
||||||
|
|
@ -44,7 +42,6 @@ if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
||||||
if (!$res && $i > 0 && file_exists(dirname(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")) {
|
if (!$res && file_exists("../../main.inc.php")) {
|
||||||
$res = @include "../../main.inc.php";
|
$res = @include "../../main.inc.php";
|
||||||
}
|
}
|
||||||
|
|
@ -60,14 +57,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
|
||||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
|
||||||
require_once '../lib/supplierlink3.lib.php';
|
require_once '../lib/supplierlink3.lib.php';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Conf $conf
|
|
||||||
* @var DoliDB $db
|
|
||||||
* @var HookManager $hookmanager
|
|
||||||
* @var Translate $langs
|
|
||||||
* @var User $user
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
$langs->loadLangs(array("errors", "admin", "supplierlink3@supplierlink3"));
|
$langs->loadLangs(array("errors", "admin", "supplierlink3@supplierlink3"));
|
||||||
|
|
||||||
|
|
@ -76,43 +65,108 @@ if (!$user->admin) {
|
||||||
accessforbidden();
|
accessforbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameters
|
|
||||||
$action = GETPOST('action', 'aZ09');
|
|
||||||
$backtopage = GETPOST('backtopage', 'alpha');
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Actions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// None
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* View
|
* View
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$form = new Form($db);
|
$form = new Form($db);
|
||||||
|
|
||||||
$help_url = '';
|
$title = $langs->trans("SupplierLink3Setup");
|
||||||
$title = "SupplierLink3Setup";
|
|
||||||
|
|
||||||
llxHeader('', $langs->trans($title), $help_url, '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin_about');
|
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin_about');
|
||||||
|
|
||||||
// Subheader
|
// Subheader
|
||||||
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.$langs->trans("BackToModuleList").'</a>';
|
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
|
||||||
|
|
||||||
print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
|
print load_fiche_titre($title, $linkback, 'title_setup');
|
||||||
|
|
||||||
// Configuration header
|
// Configuration header
|
||||||
$head = supplierlink3AdminPrepareHead();
|
$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');
|
dol_include_once('/supplierlink3/core/modules/modSupplierLink3.class.php');
|
||||||
$tmpmodule = new modSupplierLink3($db);
|
$tmpmodule = new modSupplierLink3($db);
|
||||||
print $tmpmodule->getDescLong();
|
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
|
||||||
|
// Version
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th colspan="2">'.$langs->trans('SL3_Version').' & '.$langs->trans('SL3_Author').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td width="30%">'.$langs->trans('SL3_Version').'</td>';
|
||||||
|
print '<td><strong>'.$tmpmodule->version.'</strong></td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_Author').'</td>';
|
||||||
|
print '<td>Eduard Wisch <data@data-it-solution.de></td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_License').'</td>';
|
||||||
|
print '<td>GPL v3</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
print '<br>';
|
||||||
|
|
||||||
|
// Features
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th>'.$langs->trans('SL3_Features').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
$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 '<tr class="oddeven">';
|
||||||
|
print '<td><i class="fas '.$icon.'" style="color: #0077b6; margin-right: 10px; width: 20px;"></i>'.$langs->trans($key).'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
print '<br>';
|
||||||
|
|
||||||
|
// Stock badge legend
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th colspan="2">'.$langs->trans('SL3_StockBadges', 'Lagerbestand-Badges').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td width="150"><span class="badge" style="background-color: #dc3545; color: #fff;">0</span></td>';
|
||||||
|
print '<td>'.$langs->trans('SL3_StockCritical').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td><span class="badge" style="background-color: #fd7e14; color: #fff;">5</span></td>';
|
||||||
|
print '<td>'.$langs->trans('SL3_StockWarning').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td><span class="badge" style="background-color: #6c757d; color: #fff;">15</span></td>';
|
||||||
|
print '<td>'.$langs->trans('SL3_StockLow').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td><span class="badge" style="background-color: #28a745; color: #fff;">42</span></td>';
|
||||||
|
print '<td>'.$langs->trans('SL3_StockOk').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
// Page end
|
// Page end
|
||||||
print dol_get_fiche_end();
|
print dol_get_fiche_end();
|
||||||
|
|
||||||
llxFooter();
|
llxFooter();
|
||||||
$db->close();
|
$db->close();
|
||||||
|
|
|
||||||
908
admin/setup.php
908
admin/setup.php
|
|
@ -25,11 +25,9 @@
|
||||||
|
|
||||||
// Load Dolibarr environment
|
// Load Dolibarr environment
|
||||||
$res = 0;
|
$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"])) {
|
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'];
|
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
||||||
$tmp2 = realpath(__FILE__);
|
$tmp2 = realpath(__FILE__);
|
||||||
$i = strlen($tmp) - 1;
|
$i = strlen($tmp) - 1;
|
||||||
|
|
@ -44,7 +42,6 @@ if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
||||||
if (!$res && $i > 0 && file_exists(dirname(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")) {
|
if (!$res && file_exists("../../main.inc.php")) {
|
||||||
$res = @include "../../main.inc.php";
|
$res = @include "../../main.inc.php";
|
||||||
}
|
}
|
||||||
|
|
@ -58,263 +55,104 @@ if (!$res) {
|
||||||
// Libraries
|
// Libraries
|
||||||
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
||||||
require_once '../lib/supplierlink3.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
|
// Translations
|
||||||
$langs->loadLangs(array("admin", "supplierlink3@supplierlink3"));
|
$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
|
// Access control
|
||||||
/** @var HookManager $hookmanager */
|
if (!$user->admin) {
|
||||||
$hookmanager->initHooks(array('supplierlink3setup', 'globalsetup'));
|
accessforbidden();
|
||||||
|
}
|
||||||
|
|
||||||
// Parameters
|
// Parameters
|
||||||
$action = GETPOST('action', 'aZ09');
|
$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
|
* Actions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// For retrocompatibility Dolibarr < 15.0
|
// Toggle display settings
|
||||||
if (versioncompare(explode('.', DOL_VERSION), array(15)) < 0 && $action == 'update' && !empty($user->admin)) {
|
if ($action == 'set_enable_ordercard') {
|
||||||
$formSetup->saveConfFromPost();
|
$value = GETPOST('value', 'int');
|
||||||
}
|
dolibarr_set_const($db, 'SUPPLIERLINK3_ENABLE_ORDERCARD', $value, 'chaine', 0, '', $conf->entity);
|
||||||
|
|
||||||
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');
|
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||||
} else {
|
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||||
setEventMessages($langs->trans("Error"), null, 'errors');
|
exit;
|
||||||
}
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$action = 'edit';
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
$form = new Form($db);
|
||||||
|
|
||||||
$help_url = '';
|
$title = $langs->trans("SupplierLink3Setup");
|
||||||
$title = "SupplierLink3Setup";
|
|
||||||
|
|
||||||
llxHeader('', $langs->trans($title), $help_url, '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin');
|
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-supplierlink3 page-admin');
|
||||||
|
|
||||||
// Subheader
|
// Subheader
|
||||||
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1').'">'.$langs->trans("BackToModuleList").'</a>';
|
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
|
||||||
|
|
||||||
print load_fiche_titre($langs->trans($title), $linkback, 'title_setup');
|
print load_fiche_titre($title, $linkback, 'title_setup');
|
||||||
|
|
||||||
// Configuration header
|
// Configuration header
|
||||||
$head = supplierlink3AdminPrepareHead();
|
$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
|
print '<span class="opacitymedium">'.$langs->trans('SupplierLink3Description').'</span><br><br>';
|
||||||
echo '<span class="opacitymedium">'.$langs->trans("SupplierLink3SetupPage").'</span><br><br>';
|
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// DISPLAY SETTINGS
|
||||||
|
// =============================================================================
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th colspan="3">'.$langs->trans('SL3_DisplaySettings').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
/*if ($action == 'edit') {
|
print '<tr class="oddeven">';
|
||||||
print $formSetup->generateOutput(true);
|
print '<td colspan="3" class="opacitymedium">'.$langs->trans('SL3_DisplaySettingsDesc').'</td>';
|
||||||
print '<br>';
|
print '</tr>';
|
||||||
} elseif (!empty($formSetup->items)) {
|
|
||||||
print $formSetup->generateOutput();
|
|
||||||
print '<div class="tabsAction">';
|
|
||||||
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=edit&token='.newToken().'">'.$langs->trans("Modify").'</a>';
|
|
||||||
print '</div>';
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (!empty($formSetup->items)) {
|
|
||||||
print $formSetup->generateOutput(true);
|
|
||||||
print '<br>';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Customer Orders (ordercard)
|
||||||
foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) {
|
$enableOrderCard = getDolGlobalInt('SUPPLIERLINK3_ENABLE_ORDERCARD', 1);
|
||||||
if (!empty($myTmpObjectArray['includerefgeneration'])) {
|
print '<tr class="oddeven">';
|
||||||
// Numbering models
|
print '<td>'.$langs->trans('SL3_EnableOrderCard').'</td>';
|
||||||
|
print '<td class="center" width="80">';
|
||||||
$setupnotempty++;
|
if ($enableOrderCard) {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_ordercard&token='.newToken().'&value=0">';
|
||||||
print load_fiche_titre($langs->trans("NumberingModules", $myTmpObjectArray['label']), '', '');
|
|
||||||
|
|
||||||
print '<table class="noborder centpercent">';
|
|
||||||
print '<tr class="liste_titre">';
|
|
||||||
print '<td>'.$langs->trans("Name").'</td>';
|
|
||||||
print '<td>'.$langs->trans("Description").'</td>';
|
|
||||||
print '<td class="nowrap">'.$langs->trans("Example").'</td>';
|
|
||||||
print '<td class="center" width="60">'.$langs->trans("Status").'</td>';
|
|
||||||
print '<td class="center" width="16">'.$langs->trans("ShortInfo").'</td>';
|
|
||||||
print '</tr>'."\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 '<tr class="oddeven"><td>'.$module->getName($langs)."</td><td>\n";
|
|
||||||
print $module->info($langs);
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
// Show example of numbering model
|
|
||||||
print '<td class="nowrap">';
|
|
||||||
$tmp = $module->getExample();
|
|
||||||
if (preg_match('/^Error/', $tmp)) {
|
|
||||||
$langs->load("errors");
|
|
||||||
print '<div class="error">'.$langs->trans($tmp).'</div>';
|
|
||||||
} elseif ($tmp == 'NotConfigured') {
|
|
||||||
print $langs->trans($tmp);
|
|
||||||
} else {
|
|
||||||
print $tmp;
|
|
||||||
}
|
|
||||||
print '</td>'."\n";
|
|
||||||
|
|
||||||
print '<td class="center">';
|
|
||||||
$constforvar = 'SUPPLIERLINK3_'.strtoupper($myTmpObjectKey).'_ADDON';
|
|
||||||
$defaultifnotset = 'thevaluetousebydefault';
|
|
||||||
$activenumberingmodel = getDolGlobalString($constforvar, $defaultifnotset);
|
|
||||||
if ($activenumberingmodel == $file) {
|
|
||||||
print img_picto($langs->trans("Activated"), 'switch_on');
|
print img_picto($langs->trans("Activated"), 'switch_on');
|
||||||
} else {
|
print '</a>';
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setmod&token='.newToken().'&object='.strtolower($myTmpObjectKey).'&value='.urlencode($file).'">';
|
} else {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_ordercard&token='.newToken().'&value=1">';
|
||||||
print img_picto($langs->trans("Disabled"), 'switch_off');
|
print img_picto($langs->trans("Disabled"), 'switch_off');
|
||||||
print '</a>';
|
print '</a>';
|
||||||
}
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_EnableOrderCardDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
$className = $myTmpObjectArray['class'];
|
// Proposals (propalcard)
|
||||||
$mytmpinstance = new $className($db);
|
$enablePropalCard = getDolGlobalInt('SUPPLIERLINK3_ENABLE_PROPALCARD', 1);
|
||||||
'@phan-var-force MyObject $mytmpinstance';
|
print '<tr class="oddeven">';
|
||||||
$mytmpinstance->initAsSpecimen();
|
print '<td>'.$langs->trans('SL3_EnablePropalCard').'</td>';
|
||||||
|
print '<td class="center">';
|
||||||
// Info
|
if ($enablePropalCard) {
|
||||||
$htmltooltip = '';
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_propalcard&token='.newToken().'&value=0">';
|
||||||
$htmltooltip .= ''.$langs->trans("Version").': <b>'.$module->getVersion().'</b><br>';
|
print img_picto($langs->trans("Activated"), 'switch_on');
|
||||||
|
|
||||||
$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.'<br>';
|
|
||||||
} else {
|
|
||||||
$htmltooltip .= $langs->trans($module->error).'<br>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<td class="center">';
|
|
||||||
print $form->textwithpicto('', $htmltooltip, 1, 'info');
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
print "</tr>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir($handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print "</table><br>\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 '<table class="noborder centpercent">'."\n";
|
|
||||||
print '<tr class="liste_titre">'."\n";
|
|
||||||
print '<td>'.$langs->trans("Name").'</td>';
|
|
||||||
print '<td>'.$langs->trans("Description").'</td>';
|
|
||||||
print '<td class="center" width="60">'.$langs->trans("Status")."</td>\n";
|
|
||||||
print '<td class="center" width="60">'.$langs->trans("Default")."</td>\n";
|
|
||||||
print '<td class="center" width="38">'.$langs->trans("ShortInfo").'</td>';
|
|
||||||
print '<td class="center" width="38">'.$langs->trans("Preview").'</td>';
|
|
||||||
print "</tr>\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 '<tr class="oddeven"><td width="100">';
|
|
||||||
print(empty($module->name) ? $name : $module->name);
|
|
||||||
print "</td><td>\n";
|
|
||||||
if (method_exists($module, 'info')) {
|
|
||||||
print $module->info($langs); // @phan-suppress-current-line PhanUndeclaredMethod
|
|
||||||
} else {
|
|
||||||
print $module->description;
|
|
||||||
}
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
// Active
|
|
||||||
if (in_array($name, $def)) {
|
|
||||||
print '<td class="center">'."\n";
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=del&token='.newToken().'&value='.urlencode($name).'">';
|
|
||||||
print img_picto($langs->trans("Enabled"), 'switch_on');
|
|
||||||
print '</a>';
|
print '</a>';
|
||||||
print '</td>';
|
} else {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_propalcard&token='.newToken().'&value=1">';
|
||||||
|
print img_picto($langs->trans("Disabled"), 'switch_off');
|
||||||
|
print '</a>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_EnablePropalCardDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Supplier Orders (ordersuppliercard)
|
||||||
|
$enableSupplierOrder = getDolGlobalInt('SUPPLIERLINK3_ENABLE_SUPPLIERORDER', 1);
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_EnableSupplierOrder').'</td>';
|
||||||
|
print '<td class="center">';
|
||||||
|
if ($enableSupplierOrder) {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_supplierorder&token='.newToken().'&value=0">';
|
||||||
|
print img_picto($langs->trans("Activated"), 'switch_on');
|
||||||
|
print '</a>';
|
||||||
|
} else {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_supplierorder&token='.newToken().'&value=1">';
|
||||||
|
print img_picto($langs->trans("Disabled"), 'switch_off');
|
||||||
|
print '</a>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_EnableSupplierOrderDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Stock Replenishment (stockreplenishlist)
|
||||||
|
$enableReplenish = getDolGlobalInt('SUPPLIERLINK3_ENABLE_REPLENISH', 1);
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_EnableReplenish').'</td>';
|
||||||
|
print '<td class="center">';
|
||||||
|
if ($enableReplenish) {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_replenish&token='.newToken().'&value=0">';
|
||||||
|
print img_picto($langs->trans("Activated"), 'switch_on');
|
||||||
|
print '</a>';
|
||||||
|
} else {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_replenish&token='.newToken().'&value=1">';
|
||||||
|
print img_picto($langs->trans("Disabled"), 'switch_off');
|
||||||
|
print '</a>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_EnableReplenishDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Product Card (productcard)
|
||||||
|
$enableProductCard = getDolGlobalInt('SUPPLIERLINK3_ENABLE_PRODUCTCARD', 1);
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_EnableProductCard').'</td>';
|
||||||
|
print '<td class="center">';
|
||||||
|
if ($enableProductCard) {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_productcard&token='.newToken().'&value=0">';
|
||||||
|
print img_picto($langs->trans("Activated"), 'switch_on');
|
||||||
|
print '</a>';
|
||||||
|
} else {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set_enable_productcard&token='.newToken().'&value=1">';
|
||||||
|
print img_picto($langs->trans("Disabled"), 'switch_off');
|
||||||
|
print '</a>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_EnableProductCardDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
print '<br>';
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// ICON SETTINGS
|
||||||
|
// =============================================================================
|
||||||
|
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="action" value="save_icons">';
|
||||||
|
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th colspan="3">'.$langs->trans('SL3_IconSettings').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td colspan="3" class="opacitymedium">'.$langs->trans('SL3_IconSettingsDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Shop Icon
|
||||||
|
$shopIcon = getDolGlobalString('SUPPLIERLINK3_SHOP_ICON', 'fas fa-store');
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td width="30%">'.$langs->trans('SL3_ShopIcon').'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" name="shop_icon" value="'.dol_escape_htmltag($shopIcon).'" class="flat minwidth200" placeholder="fas fa-store">';
|
||||||
|
print ' <span class="sl3-icon-preview" style="font-size: 16px; margin-left: 10px;"><i class="'.$shopIcon.'" style="color: #0077b6;"></i></span>';
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_ShopIconDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Stock Icon (optional, for future use)
|
||||||
|
$stockIcon = getDolGlobalString('SUPPLIERLINK3_STOCK_ICON', '');
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_StockIcon').'</td>';
|
||||||
|
print '<td>';
|
||||||
|
print '<input type="text" name="stock_icon" value="'.dol_escape_htmltag($stockIcon).'" class="flat minwidth200" placeholder="fas fa-box">';
|
||||||
|
if (!empty($stockIcon)) {
|
||||||
|
print ' <span class="sl3-icon-preview" style="font-size: 16px; margin-left: 10px;"><i class="'.$stockIcon.'"></i></span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_StockIconDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Icon preview row
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_IconPreview').'</td>';
|
||||||
|
print '<td colspan="2">';
|
||||||
|
print '<div style="display: flex; gap: 20px; align-items: center; padding: 10px; background: #f9f9f9; border-radius: 4px;">';
|
||||||
|
|
||||||
|
// Preview: Shop link with stock badge
|
||||||
|
print '<div style="display: inline-flex; align-items: center;">';
|
||||||
|
print '<span style="display: inline-block; width: 28px; text-align: center;">';
|
||||||
|
print '<a href="#" style="color: #0077b6; font-size: 14px;"><i class="'.$shopIcon.'"></i></a>';
|
||||||
|
print '</span>';
|
||||||
|
print '<span style="display: inline-block; min-width: 55px; text-align: right;">';
|
||||||
|
print '<span class="badge badge-success" style="background-color: #28a745; color: #fff;">42</span>';
|
||||||
|
print '</span>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '<div style="display: inline-flex; align-items: center;">';
|
||||||
|
print '<span style="display: inline-block; width: 28px; text-align: center;">';
|
||||||
|
print '<a href="#" style="color: #0077b6; font-size: 14px;"><i class="'.$shopIcon.'"></i><i class="fas fa-caret-down" style="font-size:8px;margin-left:2px;"></i></a>';
|
||||||
|
print '</span>';
|
||||||
|
print '<span style="display: inline-block; min-width: 55px; text-align: right;">';
|
||||||
|
print '<span class="badge badge-warning" style="background-color: #fd7e14; color: #fff;">8</span>';
|
||||||
|
print '</span>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '<div style="display: inline-flex; align-items: center;">';
|
||||||
|
print '<span style="display: inline-block; width: 28px; text-align: center;"></span>';
|
||||||
|
print '<span style="display: inline-block; min-width: 55px; text-align: right;">';
|
||||||
|
print '<span class="badge badge-danger" style="background-color: #dc3545; color: #fff;">0</span>';
|
||||||
|
print '</span>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '</div>';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td colspan="3" class="center">';
|
||||||
|
print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
print '</form>';
|
||||||
|
|
||||||
|
print '<br>';
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// DEBUG SETTINGS
|
||||||
|
// =============================================================================
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th colspan="3">'.$langs->trans('SL3_DebugSettings').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Debug-Modus (default: OFF)
|
||||||
|
$debugMode = getDolGlobalInt('SUPPLIERLINK3_DEBUG_MODE', 0);
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_DebugMode').'</td>';
|
||||||
|
print '<td class="center" width="80">';
|
||||||
|
if ($debugMode) {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setdebug&token='.newToken().'&value=0">';
|
||||||
|
print img_picto($langs->trans("Activated"), 'switch_on');
|
||||||
|
print '</a>';
|
||||||
|
} else {
|
||||||
|
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setdebug&token='.newToken().'&value=1">';
|
||||||
|
print img_picto($langs->trans("Disabled"), 'switch_off');
|
||||||
|
print '</a>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '<td class="opacitymedium">'.$langs->trans('SL3_DebugModeDesc').'</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Debug-Log anzeigen
|
||||||
|
$logFile = '/tmp/supplierlink3_debug.log';
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
print '<td>'.$langs->trans('SL3_DebugLog').'</td>';
|
||||||
|
print '<td colspan="2">';
|
||||||
|
if (file_exists($logFile)) {
|
||||||
|
$logContent = file_get_contents($logFile);
|
||||||
|
$logSize = filesize($logFile);
|
||||||
|
print '<strong>'.$langs->trans('SL3_DebugLogFile').':</strong> '.$logFile.' ('.$langs->trans('SL3_DebugLogSize').': '.round($logSize/1024, 2).' KB)<br>';
|
||||||
|
print '<a class="button" href="'.$_SERVER["PHP_SELF"].'?action=cleardebuglog&token='.newToken().'">'.$langs->trans('SL3_DebugLogClear').'</a>';
|
||||||
|
print '<br><br>';
|
||||||
|
print '<strong>'.$langs->trans('SL3_DebugLogLastEntries').':</strong><br>';
|
||||||
|
print '<textarea readonly style="width:100%; height:200px; font-family:monospace; font-size:11px;">';
|
||||||
|
// Zeige letzte 100 Zeilen
|
||||||
|
$lines = explode("\n", $logContent);
|
||||||
|
$lastLines = array_slice($lines, -100);
|
||||||
|
print htmlspecialchars(implode("\n", $lastLines));
|
||||||
|
print '</textarea>';
|
||||||
|
} else {
|
||||||
|
print '<span class="opacitymedium">'.$langs->trans('SL3_DebugLogEmpty').'</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
|
||||||
|
print '<br>';
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// REGISTERED HOOKS
|
||||||
|
// =============================================================================
|
||||||
|
print '<table class="noborder centpercent">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th colspan="3">'.$langs->trans('SL3_RegisteredHooks').'</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
$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 '<tr class="oddeven">';
|
||||||
|
print '<td width="200"><code>'.$hook.'</code></td>';
|
||||||
|
print '<td>'.$desc.'</td>';
|
||||||
|
print '<td class="center" width="60">';
|
||||||
|
if ($isEnabled) {
|
||||||
|
print '<span class="badge badge-status4">'.$langs->trans("Activated").'</span>';
|
||||||
} else {
|
} else {
|
||||||
print '<td class="center">'."\n";
|
print '<span class="badge badge-status8">'.$langs->trans("Disabled").'</span>';
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=set&token='.newToken().'&value='.urlencode($name).'&scan_dir='.urlencode($module->scandir).'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"), 'switch_off').'</a>';
|
|
||||||
print "</td>";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default
|
|
||||||
print '<td class="center">';
|
|
||||||
$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 '<a href="'.$_SERVER["PHP_SELF"].'?action=unsetdoc&token='.newToken().'&object='.urlencode(strtolower($myTmpObjectKey)).'&value='.urlencode($name).'&scan_dir='.urlencode($module->scandir).'&label='.urlencode($module->name).'&type='.urlencode($type).'" alt="'.$langs->trans("Disable").'">'.img_picto($langs->trans("Enabled"), 'on').'</a>';
|
|
||||||
} else {
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=setdoc&token='.newToken().'&object='.urlencode(strtolower($myTmpObjectKey)).'&value='.urlencode($name).'&scan_dir='.urlencode($module->scandir).'&label='.urlencode($module->name).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').'</a>';
|
|
||||||
}
|
}
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
print '</tr>';
|
||||||
// Info
|
|
||||||
$htmltooltip = ''.$langs->trans("Name").': '.$module->name;
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Type").': '.($module->type ? $module->type : $langs->trans("Unknown"));
|
|
||||||
if ($module->type == 'pdf') {
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur;
|
|
||||||
}
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Path").': '.preg_replace('/^\//', '', $realpath).'/'.$file;
|
|
||||||
|
|
||||||
$htmltooltip .= '<br><br><u>'.$langs->trans("FeaturesSupported").':</u>';
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("Logo").': '.yn($module->option_logo, 1, 1);
|
|
||||||
$htmltooltip .= '<br>'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang, 1, 1);
|
|
||||||
|
|
||||||
print '<td class="center">';
|
|
||||||
print $form->textwithpicto('', $htmltooltip, 1, 'info');
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
// Preview
|
|
||||||
print '<td class="center">';
|
|
||||||
if ($module->type == 'pdf') {
|
|
||||||
$newname = preg_replace('/_'.preg_quote(strtolower($myTmpObjectKey), '/').'/', '', $name);
|
|
||||||
print '<a href="'.$_SERVER["PHP_SELF"].'?action=specimen&module='.urlencode($newname).'&object='.urlencode($myTmpObjectKey).'">'.img_object($langs->trans("Preview"), 'pdf').'</a>';
|
|
||||||
} else {
|
|
||||||
print img_object($langs->transnoentitiesnoconv("PreviewNotAvailable"), 'generic');
|
|
||||||
}
|
|
||||||
print '</td>';
|
|
||||||
|
|
||||||
print "</tr>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print '</table>';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($setupnotempty)) {
|
print '</table>';
|
||||||
print '<br>'.$langs->trans("NothingToSetup");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Page end
|
// Page end
|
||||||
print dol_get_fiche_end();
|
print dol_get_fiche_end();
|
||||||
|
|
||||||
|
// JavaScript for live icon preview
|
||||||
|
print '<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("input[name=\'shop_icon\']").on("input", function() {
|
||||||
|
var iconClass = $(this).val();
|
||||||
|
$(this).siblings(".sl3-icon-preview").find("i").attr("class", iconClass);
|
||||||
|
});
|
||||||
|
$("input[name=\'stock_icon\']").on("input", function() {
|
||||||
|
var iconClass = $(this).val();
|
||||||
|
var $preview = $(this).siblings(".sl3-icon-preview");
|
||||||
|
if ($preview.length === 0) {
|
||||||
|
$(this).after(\' <span class="sl3-icon-preview" style="font-size: 16px; margin-left: 10px;"><i class="\' + iconClass + \'"></i></span>\');
|
||||||
|
} else {
|
||||||
|
$preview.find("i").attr("class", iconClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>';
|
||||||
|
|
||||||
llxFooter();
|
llxFooter();
|
||||||
$db->close();
|
$db->close();
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,16 @@ class ActionsSupplierLink3 extends CommonHookActions
|
||||||
*/
|
*/
|
||||||
public $db;
|
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)
|
* @var string Error code (or message)
|
||||||
*/
|
*/
|
||||||
|
|
@ -63,6 +73,16 @@ class ActionsSupplierLink3 extends CommonHookActions
|
||||||
public $priority;
|
public $priority;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param DoliDB $db Database handler
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @var string Shop icon class (FontAwesome)
|
||||||
|
*/
|
||||||
|
private $shopIcon = 'fas fa-store';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
|
|
@ -71,6 +91,38 @@ class ActionsSupplierLink3 extends CommonHookActions
|
||||||
public function __construct($db)
|
public function __construct($db)
|
||||||
{
|
{
|
||||||
$this->db = $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,116 +453,448 @@ class ActionsSupplierLink3 extends CommonHookActions
|
||||||
return 1;
|
return 1;
|
||||||
}*/
|
}*/
|
||||||
/* Add other hook methods here... */
|
/* Add other hook methods here... */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = '<span class="badge '.$badgeClass.' classfortooltip" title="'.dol_escape_htmltag($tooltip).'" style="font-size: 11px;">';
|
||||||
|
$html .= '<i class="fas '.$icon.'" style="margin-right: 4px;"></i>';
|
||||||
|
$html .= number_format($qtyStock, 0, ',', '.');
|
||||||
|
$html .= '</span>';
|
||||||
|
|
||||||
|
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 = '<a href="'.dol_escape_htmltag($supplier['full_url']).'" ';
|
||||||
|
$html .= 'target="supplier_'.$supplier['supplier_id'].'" ';
|
||||||
|
$html .= 'class="classfortooltip" ';
|
||||||
|
$html .= 'title="'.dol_escape_htmltag('Im Shop ansehen: '.$supplier['supplier_name']).'" ';
|
||||||
|
$html .= 'style="color: #0077b6; font-size: 14px;">';
|
||||||
|
$html .= '<i class="'.$this->shopIcon.'"></i>';
|
||||||
|
$html .= '</a>';
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall 2: Mehrere Lieferanten - Modal-Trigger
|
||||||
|
$modalId = 'supplier_modal_'.$lineId;
|
||||||
|
|
||||||
|
// Trigger-Button mit Dropdown-Pfeil
|
||||||
|
$html = '<a href="#" class="supplier-modal-trigger classfortooltip" ';
|
||||||
|
$html .= 'data-modal="'.$modalId.'" ';
|
||||||
|
$html .= 'title="'.dol_escape_htmltag('Lieferanten-Shops ('.count($suppliers).')').'" ';
|
||||||
|
$html .= 'style="color: #0077b6; font-size: 14px;">';
|
||||||
|
$html .= '<i class="'.$this->shopIcon.'"></i>';
|
||||||
|
$html .= '<i class="fas fa-caret-down" style="font-size: 8px; margin-left: 2px;"></i>';
|
||||||
|
$html .= '</a>';
|
||||||
|
|
||||||
|
// Modal-Container (versteckt)
|
||||||
|
$html .= '<div id="'.$modalId.'" class="supplier-modal-content" style="display: none;" title="Lieferanten-Shops">';
|
||||||
|
|
||||||
|
$isFirst = true;
|
||||||
|
foreach ($suppliers as $supplier) {
|
||||||
|
$isCurrentSupplier = ($supplier['supplier_id'] == $currentSupplierId);
|
||||||
|
|
||||||
|
$rowStyle = '';
|
||||||
|
if ($isCurrentSupplier) {
|
||||||
|
$rowStyle = 'background-color: #e8f4fd;';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '<div class="supplier-modal-item" style="padding: 10px; border-bottom: 1px solid #eee; '.$rowStyle.'">';
|
||||||
|
|
||||||
|
// Lieferanten-Name mit Stern für günstigsten
|
||||||
|
$html .= '<div style="font-weight: '.($isFirst ? 'bold' : 'normal').'; margin-bottom: 5px;">';
|
||||||
|
if ($isFirst) {
|
||||||
|
$html .= '<i class="fas fa-star" style="color: gold; margin-right: 5px;" title="Günstigster Lieferant"></i>';
|
||||||
|
}
|
||||||
|
$html .= dol_escape_htmltag($supplier['supplier_name']);
|
||||||
|
if ($isCurrentSupplier) {
|
||||||
|
$html .= ' <span style="font-size: 10px; color: #666;">(aktueller Lieferant)</span>';
|
||||||
|
}
|
||||||
|
$html .= '</div>';
|
||||||
|
|
||||||
|
// Preis und Mindestmenge
|
||||||
|
$html .= '<div style="font-size: 12px; color: #666; margin-bottom: 8px;">';
|
||||||
|
$html .= '<strong>'.number_format($supplier['price'], 2, ',', '.').' EUR</strong>';
|
||||||
|
$html .= ' · Mindestmenge: '.(int)$supplier['min_qty'].' Stk.';
|
||||||
|
$html .= ' · Art.-Nr: '.dol_escape_htmltag($supplier['ref_fourn']);
|
||||||
|
$html .= '</div>';
|
||||||
|
|
||||||
|
// Shop-Link Button
|
||||||
|
$html .= '<a href="'.dol_escape_htmltag($supplier['full_url']).'" ';
|
||||||
|
$html .= 'target="supplier_'.$supplier['supplier_id'].'" ';
|
||||||
|
$html .= 'class="button small" style="font-size: 11px; padding: 4px 10px;">';
|
||||||
|
$html .= '<i class="fas fa-external-link-alt" style="margin-right: 5px;"></i>Im Shop öffnen';
|
||||||
|
$html .= '</a>';
|
||||||
|
|
||||||
|
$html .= '</div>';
|
||||||
|
|
||||||
|
$isFirst = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
public function printObjectLine($parameters, &$object, &$action, $hookmanager)
|
public function printObjectLine($parameters, &$object, &$action, $hookmanager)
|
||||||
{
|
{
|
||||||
static $css_added = false;
|
static $css_js_added = false;
|
||||||
|
|
||||||
$isSupplierOrder = ($parameters['currentcontext'] == 'ordersuppliercard');
|
$isSupplierOrder = ($parameters['currentcontext'] == 'ordersuppliercard');
|
||||||
$isCustomerOrder = ($parameters['currentcontext'] == 'ordercard');
|
$isCustomerOrder = ($parameters['currentcontext'] == 'ordercard');
|
||||||
|
$isProposal = ($parameters['currentcontext'] == 'propalcard');
|
||||||
|
|
||||||
if (!$isSupplierOrder && !$isCustomerOrder) {
|
if (!$isSupplierOrder && !$isCustomerOrder && !$isProposal) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS nur einmal hinzufügen
|
// Prüfen ob diese Funktion für den jeweiligen Kontext aktiviert ist
|
||||||
if (!$css_added) {
|
if ($isSupplierOrder && !getDolGlobalInt('SUPPLIERLINK3_ENABLE_SUPPLIERORDER', 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ($isCustomerOrder && !getDolGlobalInt('SUPPLIERLINK3_ENABLE_ORDERCARD', 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ($isProposal && !getDolGlobalInt('SUPPLIERLINK3_ENABLE_PROPALCARD', 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS und JavaScript nur einmal hinzufügen
|
||||||
|
if (!$css_js_added) {
|
||||||
?>
|
?>
|
||||||
<style>
|
<style>
|
||||||
/* Verstecke Info-Icons innerhalb der Lagerbestand-Anzeige */
|
/* Verstecke Info-Icons innerhalb der Lagerbestand-Anzeige */
|
||||||
.stock-display-wrapper .fa-info-circle {
|
.stock-display-wrapper .fa-info-circle,
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tablelines tbody .fa-info-circle {
|
#tablelines tbody .fa-info-circle {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Badge-Styling - 4 Zustände */
|
||||||
|
.badge.badge-danger {
|
||||||
|
background-color: #dc3545 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.badge.badge-warning {
|
||||||
|
background-color: #fd7e14 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.badge.badge-secondary {
|
||||||
|
background-color: #6c757d !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.badge.badge-success {
|
||||||
|
background-color: #28a745 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal-Item Styling */
|
||||||
|
.supplier-modal-item:last-child {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
.supplier-modal-item:hover {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button Styling */
|
||||||
|
.supplier-modal-item .button.small {
|
||||||
|
background: linear-gradient(to bottom, #f8f9fa 0%, #e9ecef 100%);
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #495057;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.supplier-modal-item .button.small:hover {
|
||||||
|
background: linear-gradient(to bottom, #e9ecef 0%, #dee2e6 100%);
|
||||||
|
color: #212529;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* jQuery UI Dialog Anpassungen */
|
||||||
|
.ui-dialog .supplier-modal-content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Modal-Dialog bei Klick auf Trigger öffnen
|
||||||
|
$(document).on('click', '.supplier-modal-trigger', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var modalId = $(this).data('modal');
|
||||||
|
var $modal = $('#' + modalId);
|
||||||
|
|
||||||
|
if ($modal.length) {
|
||||||
|
$modal.dialog({
|
||||||
|
modal: true,
|
||||||
|
width: 450,
|
||||||
|
maxHeight: 400,
|
||||||
|
buttons: {
|
||||||
|
'Schließen': function() {
|
||||||
|
$(this).dialog('close');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
open: function() {
|
||||||
|
// Styling für Dialog-Buttons
|
||||||
|
$(this).parent().find('.ui-dialog-buttonpane button').css({
|
||||||
|
'font-size': '12px',
|
||||||
|
'padding': '6px 15px'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<?php
|
<?php
|
||||||
$css_added = true;
|
$css_js_added = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$line = $parameters['line'];
|
$line = $parameters['line'];
|
||||||
|
|
||||||
$line = $parameters['line'];
|
// Lagerbestand, Wunschbestand und Mindestbestand abfragen
|
||||||
|
$sqlStock = "SELECT stock, desiredstock, seuil_stock_alerte
|
||||||
// Lagerbestand und Wunschbestand abfragen
|
|
||||||
$sqlStock = "SELECT stock, desiredstock
|
|
||||||
FROM ".MAIN_DB_PREFIX."product
|
FROM ".MAIN_DB_PREFIX."product
|
||||||
WHERE rowid = ".(int)$line->fk_product;
|
WHERE rowid = ".(int)$line->fk_product;
|
||||||
|
|
||||||
$resStock = $this->db->query($sqlStock);
|
$resStock = $this->db->query($sqlStock);
|
||||||
$qtyStock = 0;
|
$qtyStock = 0;
|
||||||
$desiredQty = 0;
|
$desiredQty = 0;
|
||||||
|
$alertQty = 0;
|
||||||
if ($resStock && $this->db->num_rows($resStock) > 0) {
|
if ($resStock && $this->db->num_rows($resStock) > 0) {
|
||||||
$objStock = $this->db->fetch_object($resStock);
|
$objStock = $this->db->fetch_object($resStock);
|
||||||
$qtyStock = (float) $objStock->stock;
|
$qtyStock = (float) $objStock->stock;
|
||||||
$desiredQty = (float) $objStock->desiredstock;
|
$desiredQty = (float) $objStock->desiredstock;
|
||||||
|
$alertQty = (float) $objStock->seuil_stock_alerte;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Farbe setzen
|
// Lieferanten-Shop-Link Logik (nur für Lieferantenbestellungen)
|
||||||
$stockColor = 'ffffff';
|
|
||||||
if ($qtyStock < 1) {
|
|
||||||
$stockColor = 'red'; // ausverkauft
|
|
||||||
} elseif ($qtyStock < $desiredQty) {
|
|
||||||
$stockColor = 'orange'; // kleiner als Wunschbestand
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lieferanten-ID aus dem Bestell-Objekt holen
|
|
||||||
if (!empty($object->socid) && $isSupplierOrder) {
|
if (!empty($object->socid) && $isSupplierOrder) {
|
||||||
$fk_supplier = $object->socid;
|
$fk_supplier = $object->socid;
|
||||||
|
|
||||||
|
// Alle Lieferanten für dieses Produkt abrufen
|
||||||
|
$allSuppliers = $this->getAllSuppliersForProduct($line->fk_product);
|
||||||
|
|
||||||
|
// Shop-Icon/Modal generieren
|
||||||
|
$lineId = !empty($line->id) ? $line->id : uniqid();
|
||||||
|
$shopLinkHtml = $this->getShopLinkHtml($allSuppliers, $fk_supplier, $line->ref_fourn, $lineId);
|
||||||
|
|
||||||
// Extrafeld-Wert vom Lieferanten laden
|
// Lagerbestand-Badge generieren (4 Zustände: rot/orange/grau/grün)
|
||||||
$sql = "SELECT shop_url FROM ".MAIN_DB_PREFIX."societe_extrafields";
|
$stockBadgeHtml = $this->getStockBadgeHtml($qtyStock, $desiredQty, $alertQty);
|
||||||
$sql .= " WHERE fk_object = ".(int)$fk_supplier;
|
|
||||||
|
|
||||||
$resql = $this->db->query($sql);
|
// Neue ref_fourn zusammenbauen:
|
||||||
|
// [Shop-Icon feste Breite] [Lagerbestand feste Breite] [Artikel-Nummer]
|
||||||
|
$newref = '<div style="display: inline-flex; align-items: center;">';
|
||||||
|
|
||||||
if ($resql && $this->db->num_rows($resql) > 0) {
|
// Shop-Icon mit fester Breite (damit alle untereinander stehen)
|
||||||
$obj = $this->db->fetch_object($resql);
|
$newref .= '<span class="supplier-shop-col" style="display: inline-block; width: 28px; text-align: center;">';
|
||||||
|
if (!empty($shopLinkHtml)) {
|
||||||
|
$newref .= $shopLinkHtml;
|
||||||
|
}
|
||||||
|
$newref .= '</span>';
|
||||||
|
|
||||||
if (isset($obj->shop_url) && $obj->shop_url !== '' && $obj->shop_url !== null) {
|
// Lagerbestand-Badge mit fester Breite (rechtsbündig für gleichmäßige Ausrichtung)
|
||||||
|
$newref .= '<span class="supplier-stock-col" style="display: inline-block; min-width: 55px; text-align: right; margin-right: 8px;">';
|
||||||
|
$newref .= $stockBadgeHtml;
|
||||||
|
$newref .= '</span>';
|
||||||
|
|
||||||
$shop_url = trim($obj->shop_url);
|
// Artikel-Nummer als normaler Text
|
||||||
|
$newref .= '<span>' . dol_escape_htmltag($line->ref_fourn) . '</span>';
|
||||||
|
|
||||||
if (!empty($shop_url)) {
|
$newref .= '</div>';
|
||||||
|
|
||||||
// Artikel-Link
|
|
||||||
$full_url = rtrim($shop_url, '/') . '/' . $line->ref_fourn;
|
|
||||||
$newref = '<a href="'.$full_url.'" target="_blank" class="classfortooltip" title="'.dol_escape_htmltag('Artikel im Shop ansehen').'">';
|
|
||||||
$newref .= $line->ref_fourn;
|
|
||||||
$newref .= '</a>';
|
|
||||||
|
|
||||||
// Lagerbestand daneben mit Farbe
|
|
||||||
$newref .= ' <span class="fas fa-box-open em080 pictofixedwidth" title="Lagerbestand" style="color: '.$stockColor.'"></span><b> <span style="color: '.$stockColor.'">'.$qtyStock.'</span></b>';
|
|
||||||
|
|
||||||
$line->ref_fourn = $newref;
|
$line->ref_fourn = $newref;
|
||||||
|
|
||||||
}
|
} elseif ($isCustomerOrder || $isProposal) {
|
||||||
}
|
// Kundenauftrag oder Angebot - Shop-Link und Lagerbestand anzeigen
|
||||||
}
|
|
||||||
} elseif ($isCustomerOrder) {
|
|
||||||
// NUR für Produkte, NICHT für Dienstleistungen
|
// NUR für Produkte, NICHT für Dienstleistungen
|
||||||
if (!empty($line->fk_product) && $line->product_type == 0) {
|
if (!empty($line->fk_product) && $line->product_type == 0) {
|
||||||
|
|
||||||
// Lagerbestand rechtsbündig mit float
|
// Alle Lieferanten für dieses Produkt abrufen
|
||||||
$stockInfo = '<span style="float: right; margin-left: 20px;">';
|
$allSuppliers = $this->getAllSuppliersForProduct($line->fk_product);
|
||||||
$stockInfo .= '<span class="fas fa-box-open" style="color: ' . $stockColor . ';"></span>';
|
|
||||||
$stockInfo .= ' <span style="color: ' . $stockColor . '; font-weight: bold;">' . $qtyStock . '</span>';
|
// Shop-Icon/Modal generieren (ohne aktuellen Lieferanten, da Kundenauftrag/Angebot)
|
||||||
|
$lineId = !empty($line->id) ? $line->id : uniqid();
|
||||||
|
$shopLinkHtml = $this->getShopLinkHtml($allSuppliers, 0, '', $lineId);
|
||||||
|
|
||||||
|
// Lagerbestand-Badge mit neuem System (4 Zustände: rot/orange/grau/grün)
|
||||||
|
$stockBadgeHtml = $this->getStockBadgeHtml($qtyStock, $desiredQty, $alertQty);
|
||||||
|
|
||||||
|
// Rechtsbündig: [Shop-Icon feste Breite] [Lagerbestand feste Breite]
|
||||||
|
$stockInfo = '<span style="float: right; margin-left: 15px; display: inline-flex; align-items: center;">';
|
||||||
|
|
||||||
|
// Shop-Icon mit fester Breite
|
||||||
|
$stockInfo .= '<span class="supplier-shop-col" style="display: inline-block; width: 28px; text-align: center;">';
|
||||||
|
if (!empty($shopLinkHtml)) {
|
||||||
|
$stockInfo .= $shopLinkHtml;
|
||||||
|
}
|
||||||
$stockInfo .= '</span>';
|
$stockInfo .= '</span>';
|
||||||
|
|
||||||
// An verschiedene mögliche Felder anhängen
|
// Lagerbestand-Badge mit fester Breite (rechtsbündig für gleichmäßige Ausrichtung)
|
||||||
|
$stockInfo .= '<span class="supplier-stock-col" style="display: inline-block; min-width: 55px; text-align: right;">';
|
||||||
|
$stockInfo .= $stockBadgeHtml;
|
||||||
|
$stockInfo .= '</span>';
|
||||||
|
|
||||||
|
$stockInfo .= '</span>';
|
||||||
|
|
||||||
|
// An Produktlabel anhängen
|
||||||
if (isset($line->product_label)) {
|
if (isset($line->product_label)) {
|
||||||
$line->product_label .= $stockInfo;
|
$line->product_label .= $stockInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook für zusätzliche Buttons auf der Kundenauftrags-Seite
|
||||||
|
* WICHTIG: Dolibarr gibt $hookmanager->resPrint bei addMoreActionsButtons NICHT aus!
|
||||||
|
* Daher verwenden wir JavaScript um den Button dynamisch einzufügen (wie SubtotalTitle)
|
||||||
|
*/
|
||||||
|
public function addMoreActionsButtons($parameters, &$object, &$action, $hookmanager)
|
||||||
|
{
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$this->debugLog('Hook aufgerufen - context: '.(isset($parameters['currentcontext']) ? $parameters['currentcontext'] : 'nicht gesetzt'), 'addMoreActionsButtons');
|
||||||
|
$this->debugLog('Object element: '.(is_object($object) ? $object->element : 'kein Objekt'), 'addMoreActionsButtons');
|
||||||
|
$this->debugLog('Object id: '.(is_object($object) && isset($object->id) ? $object->id : 'keine ID'), 'addMoreActionsButtons');
|
||||||
|
|
||||||
|
// Nur für Kundenaufträge (Commande)
|
||||||
|
if (!is_object($object) || $object->element != 'commande') {
|
||||||
|
$this->debugLog('Abbruch: Kein Commande-Objekt', 'addMoreActionsButtons');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen ob Produktzeilen vorhanden sind
|
||||||
|
$sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."commandedet
|
||||||
|
WHERE fk_commande = ".(int)$object->id."
|
||||||
|
AND fk_product > 0
|
||||||
|
AND product_type = 0";
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
$hasProducts = false;
|
||||||
|
if ($resql) {
|
||||||
|
$obj = $db->fetch_object($resql);
|
||||||
|
$hasProducts = ($obj->nb > 0);
|
||||||
|
$this->debugLog('Produktzeilen gefunden: '.$obj->nb, 'addMoreActionsButtons');
|
||||||
|
} else {
|
||||||
|
$this->debugLog('SQL Fehler: '.$db->lasterror(), 'addMoreActionsButtons');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasProducts) {
|
||||||
|
$buttonUrl = dol_buildpath('/custom/supplierlink3/create_supplier_order.php', 1).'?origin=commande&originid='.$object->id;
|
||||||
|
|
||||||
|
// Button per JavaScript einfügen (da Dolibarr resPrint bei diesem Hook nicht ausgibt)
|
||||||
|
print '<script>$(document).ready(function() {';
|
||||||
|
print ' if ($(".tabsAction").length > 0 && $("#btnSupplierLink3").length === 0) {';
|
||||||
|
print ' $(".tabsAction").append(\'<a id="btnSupplierLink3" class="butAction" href="'.dol_escape_js($buttonUrl).'"><i class="fas fa-truck-loading" style="margin-right: 5px;"></i>Lieferantenbestellung erstellen</a>\');';
|
||||||
|
print ' }';
|
||||||
|
print '});</script>'."\n";
|
||||||
|
|
||||||
|
$this->debugLog('Button per JavaScript eingefügt für URL: '.$buttonUrl, 'addMoreActionsButtons');
|
||||||
|
} else {
|
||||||
|
$this->debugLog('Kein Button - keine Produktzeilen', 'addMoreActionsButtons');
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public function formObjectOptions($parameters, &$object, &$action)
|
public function formObjectOptions($parameters, &$object, &$action)
|
||||||
{
|
{
|
||||||
global $db;
|
global $db;
|
||||||
|
|
@ -520,6 +904,11 @@ class ActionsSupplierLink3 extends CommonHookActions
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfen ob Produktkarte aktiviert ist
|
||||||
|
if (!getDolGlobalInt('SUPPLIERLINK3_ENABLE_PRODUCTCARD', 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if ($action != 'create' && $action != 'edit') {
|
if ($action != 'create' && $action != 'edit') {
|
||||||
|
|
||||||
$sql = "SELECT DISTINCT pfp.ref_fourn, pfp.quantity, pfp.price, s.nom, se.shop_url
|
$sql = "SELECT DISTINCT pfp.ref_fourn, pfp.quantity, pfp.price, s.nom, se.shop_url
|
||||||
|
|
@ -575,4 +964,113 @@ class ActionsSupplierLink3 extends CommonHookActions
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook für Footer der Liste - JavaScript einfügen das die Stock-Spalte ersetzt
|
||||||
|
* KEINE zusätzliche Spalte - nur die bestehende Stock-Spalte wird modifiziert
|
||||||
|
*/
|
||||||
|
public function printFieldListFooter($parameters, &$object, &$action, $hookmanager)
|
||||||
|
{
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
if (strpos($parameters['currentcontext'], 'stockreplenishlist') === false) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen ob Nachbestellung aktiviert ist
|
||||||
|
if (!getDolGlobalInt('SUPPLIERLINK3_ENABLE_REPLENISH', 1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Produkte mit Shop-URLs abrufen
|
||||||
|
$sql = "SELECT DISTINCT pfp.fk_product,
|
||||||
|
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 se.shop_url IS NOT NULL AND se.shop_url != ''
|
||||||
|
ORDER BY pfp.fk_product, pfp.price ASC";
|
||||||
|
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
$productSuppliers = array();
|
||||||
|
if ($resql) {
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$productId = $obj->fk_product;
|
||||||
|
if (!isset($productSuppliers[$productId])) {
|
||||||
|
$productSuppliers[$productId] = array();
|
||||||
|
}
|
||||||
|
$productSuppliers[$productId][] = array(
|
||||||
|
'supplier_id' => $obj->supplier_id,
|
||||||
|
'supplier_name' => $obj->supplier_name,
|
||||||
|
'ref_fourn' => $obj->ref_fourn,
|
||||||
|
'price' => $obj->price,
|
||||||
|
'min_qty' => $obj->min_qty,
|
||||||
|
'full_url' => rtrim($obj->shop_url, '/').'/'.$obj->ref_fourn
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$suppliersJson = json_encode($productSuppliers);
|
||||||
|
$jsUrl = dol_buildpath('/supplierlink3/js/replenish.js', 1);
|
||||||
|
$shopIcon = getDolGlobalString('SUPPLIERLINK3_SHOP_ICON', 'fas fa-store');
|
||||||
|
|
||||||
|
// Daten und Script direkt in den Footer schreiben (nach der Seite)
|
||||||
|
// Verwende print statt resprints um außerhalb der Tabelle zu landen
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
window.sl3Data = <?php echo $suppliersJson; ?>;
|
||||||
|
window.sl3JsUrl = "<?php echo $jsUrl; ?>";
|
||||||
|
window.sl3ShopIcon = "<?php echo dol_escape_js($shopIcon); ?>";
|
||||||
|
console.log("SL3: Init data loaded for", Object.keys(window.sl3Data).length, "products");
|
||||||
|
|
||||||
|
// Externes Script laden wenn DOM fertig
|
||||||
|
(function() {
|
||||||
|
var loadScript = function() {
|
||||||
|
var s = document.createElement("script");
|
||||||
|
s.src = window.sl3JsUrl + "?v=" + Date.now();
|
||||||
|
document.body.appendChild(s);
|
||||||
|
console.log("SL3: Loading external script", s.src);
|
||||||
|
};
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", loadScript);
|
||||||
|
} else {
|
||||||
|
loadScript();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook für Zeilenwerte - KEIN Output, keine zusätzliche Spalte
|
||||||
|
*/
|
||||||
|
public function printFieldListValue($parameters, &$object, &$action, $hookmanager)
|
||||||
|
{
|
||||||
|
// Kein Output - wir ersetzen die bestehende Stock-Spalte per JavaScript
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook für Spaltenüberschrift - KEIN Output, keine zusätzliche Spalte
|
||||||
|
*/
|
||||||
|
public function printFieldListTitle($parameters, &$object, &$action, $hookmanager)
|
||||||
|
{
|
||||||
|
// Kein Output
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook für Filter-Zeile - KEIN Output, keine zusätzliche Spalte
|
||||||
|
*/
|
||||||
|
public function printFieldListOption($parameters, &$object, &$action, $hookmanager)
|
||||||
|
{
|
||||||
|
// Kein Output
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class modSupplierLink3 extends DolibarrModules
|
||||||
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@supplierlink3'
|
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@supplierlink3'
|
||||||
|
|
||||||
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
|
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
|
||||||
$this->version = '1.0';
|
$this->version = '2.0';
|
||||||
// Url to the file with your last numberversion of this module
|
// Url to the file with your last numberversion of this module
|
||||||
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
||||||
|
|
||||||
|
|
@ -120,13 +120,12 @@ class modSupplierLink3 extends DolibarrModules
|
||||||
// Set here all hooks context managed by module. To find available hook context, make a "grep -r '>initHooks(' *" on source code. You can also set hook context to 'all'
|
// Set here all hooks context managed by module. To find available hook context, make a "grep -r '>initHooks(' *" on source code. You can also set hook context to 'all'
|
||||||
/* BEGIN MODULEBUILDER HOOKSCONTEXTS */
|
/* BEGIN MODULEBUILDER HOOKSCONTEXTS */
|
||||||
'hooks' => array(
|
'hooks' => array(
|
||||||
'data' => array(
|
|
||||||
'ordersuppliercard',
|
'ordersuppliercard',
|
||||||
'ordercard',
|
'ordercard',
|
||||||
|
'propalcard',
|
||||||
'productcard',
|
'productcard',
|
||||||
'productpricecard ',
|
'productpricecard',
|
||||||
),
|
'stockreplenishlist',
|
||||||
'entity' => '0',
|
|
||||||
),
|
),
|
||||||
/* END MODULEBUILDER HOOKSCONTEXTS */
|
/* END MODULEBUILDER HOOKSCONTEXTS */
|
||||||
// Set this to 1 if features of module are opened to external users
|
// Set this to 1 if features of module are opened to external users
|
||||||
|
|
@ -509,8 +508,6 @@ class modSupplierLink3 extends DolibarrModules
|
||||||
'isModEnabled("supplierlink3")' // Enabled condition
|
'isModEnabled("supplierlink3")' // Enabled condition
|
||||||
);
|
);
|
||||||
|
|
||||||
print_r($result0);
|
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
$this->remove($options);
|
$this->remove($options);
|
||||||
|
|
||||||
|
|
|
||||||
359
create_supplier_order.php
Executable file
359
create_supplier_order.php
Executable file
|
|
@ -0,0 +1,359 @@
|
||||||
|
<?php
|
||||||
|
/* Copyright (C) 2025 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load Dolibarr environment
|
||||||
|
$res = 0;
|
||||||
|
if (!$res && file_exists("../main.inc.php")) $res = @include "../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";
|
||||||
|
if (!$res && file_exists("../../../../main.inc.php")) $res = @include "../../../../main.inc.php";
|
||||||
|
if (!$res) die("Include of main fails");
|
||||||
|
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
||||||
|
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php';
|
||||||
|
|
||||||
|
$langs->loadLangs(array('orders', 'sendings', 'companies', 'suppliers'));
|
||||||
|
|
||||||
|
// Parameter
|
||||||
|
$origin = GETPOST('origin', 'alpha');
|
||||||
|
$originid = GETPOST('originid', 'int');
|
||||||
|
$action = GETPOST('action', 'alpha');
|
||||||
|
|
||||||
|
// Berechtigungen prüfen
|
||||||
|
if (empty($user->rights->fournisseur->commande->creer)) {
|
||||||
|
accessforbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kundenauftrag laden
|
||||||
|
$objectsrc = new Commande($db);
|
||||||
|
$objectsrc->fetch($originid);
|
||||||
|
$objectsrc->fetch_lines();
|
||||||
|
|
||||||
|
// Kunde laden für Lieferantenreferenz
|
||||||
|
$customer = new Societe($db);
|
||||||
|
$customer->fetch($objectsrc->socid);
|
||||||
|
|
||||||
|
// Lieferantenreferenz Format: "Kundenname - Auftragsnummer / Kundenzeichen"
|
||||||
|
$supplier_ref_default = $customer->name.' - '.$objectsrc->ref;
|
||||||
|
if (!empty($objectsrc->ref_client)) {
|
||||||
|
$supplier_ref_default .= ' / '.$objectsrc->ref_client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ($action == 'create_supplier_orders') {
|
||||||
|
$toselect = GETPOST('toselect', 'array');
|
||||||
|
$supplier_id = GETPOST('supplier_id', 'int');
|
||||||
|
$ref_supplier = GETPOST('ref_supplier', 'alpha');
|
||||||
|
|
||||||
|
if (empty($toselect)) {
|
||||||
|
setEventMessages('Bitte wählen Sie mindestens eine Zeile aus.', null, 'errors');
|
||||||
|
} elseif (empty($supplier_id)) {
|
||||||
|
setEventMessages('Bitte wählen Sie einen Lieferanten aus.', null, 'errors');
|
||||||
|
} else {
|
||||||
|
// Lieferantenbestellung erstellen
|
||||||
|
$supplierorder = new CommandeFournisseur($db);
|
||||||
|
|
||||||
|
$supplierorder->socid = $supplier_id;
|
||||||
|
$supplierorder->ref_supplier = !empty($ref_supplier) ? $ref_supplier : $supplier_ref_default;
|
||||||
|
$supplierorder->cond_reglement_id = 0;
|
||||||
|
$supplierorder->mode_reglement_id = 0;
|
||||||
|
$supplierorder->origin = 'commande';
|
||||||
|
$supplierorder->origin_id = $originid;
|
||||||
|
|
||||||
|
$db->begin();
|
||||||
|
|
||||||
|
$result = $supplierorder->create($user);
|
||||||
|
|
||||||
|
if ($result > 0) {
|
||||||
|
// Zeilen hinzufügen
|
||||||
|
$errors = 0;
|
||||||
|
foreach ($toselect as $lineid) {
|
||||||
|
// Zeile aus Kundenauftrag finden
|
||||||
|
$srcline = null;
|
||||||
|
foreach ($objectsrc->lines as $line) {
|
||||||
|
if ($line->id == $lineid) {
|
||||||
|
$srcline = $line;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($srcline && !empty($srcline->fk_product)) {
|
||||||
|
// Günstigsten Lieferantenpreis für diesen Lieferanten finden
|
||||||
|
$sql = "SELECT pfp.rowid, pfp.ref_fourn, pfp.price, pfp.quantity as min_qty
|
||||||
|
FROM ".MAIN_DB_PREFIX."product_fournisseur_price pfp
|
||||||
|
WHERE pfp.fk_product = ".(int)$srcline->fk_product."
|
||||||
|
AND pfp.fk_soc = ".(int)$supplier_id."
|
||||||
|
ORDER BY pfp.price ASC
|
||||||
|
LIMIT 1";
|
||||||
|
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
$fournprice = 0;
|
||||||
|
$fournref = '';
|
||||||
|
$fournpricerowid = 0;
|
||||||
|
|
||||||
|
if ($resql && $db->num_rows($resql) > 0) {
|
||||||
|
$objprice = $db->fetch_object($resql);
|
||||||
|
$fournprice = $objprice->price;
|
||||||
|
$fournref = $objprice->ref_fourn;
|
||||||
|
$fournpricerowid = $objprice->rowid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeile zur Lieferantenbestellung hinzufügen
|
||||||
|
$result_line = $supplierorder->addline(
|
||||||
|
$srcline->desc, // Description
|
||||||
|
$fournprice, // Unit price
|
||||||
|
$srcline->qty, // Quantity
|
||||||
|
$srcline->tva_tx, // VAT rate
|
||||||
|
$srcline->localtax1_tx, // Local tax 1
|
||||||
|
$srcline->localtax2_tx, // Local tax 2
|
||||||
|
$srcline->fk_product, // Product ID
|
||||||
|
$fournpricerowid, // Supplier price ID
|
||||||
|
$fournref, // Supplier ref
|
||||||
|
$srcline->remise_percent, // Discount
|
||||||
|
'HT', // Price base type
|
||||||
|
0, // pu_ht_devise
|
||||||
|
0, // type
|
||||||
|
0, // info_bits
|
||||||
|
false, // notrigger
|
||||||
|
null, // date_start
|
||||||
|
null, // date_end
|
||||||
|
0, // array_options
|
||||||
|
'', // fk_unit
|
||||||
|
0, // origin
|
||||||
|
0, // origin_id
|
||||||
|
1 // rang
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($result_line < 0) {
|
||||||
|
$errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors == 0) {
|
||||||
|
$db->commit();
|
||||||
|
setEventMessages('Lieferantenbestellung '.$supplierorder->ref.' wurde erstellt.', null, 'mesgs');
|
||||||
|
header('Location: '.DOL_URL_ROOT.'/fourn/commande/card.php?id='.$supplierorder->id);
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$db->rollback();
|
||||||
|
setEventMessages('Fehler beim Hinzufügen der Zeilen.', null, 'errors');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$db->rollback();
|
||||||
|
setEventMessages('Fehler beim Erstellen der Lieferantenbestellung: '.$supplierorder->error, null, 'errors');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* View
|
||||||
|
*/
|
||||||
|
|
||||||
|
$title = 'Lieferantenbestellung aus Kundenauftrag erstellen';
|
||||||
|
llxHeader('', $title);
|
||||||
|
|
||||||
|
print load_fiche_titre($title, '', 'order');
|
||||||
|
|
||||||
|
// Formular für Auswahl
|
||||||
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="action" value="create_supplier_orders">';
|
||||||
|
print '<input type="hidden" name="origin" value="'.$origin.'">';
|
||||||
|
print '<input type="hidden" name="originid" value="'.$originid.'">';
|
||||||
|
|
||||||
|
// Zwei Boxen nebeneinander
|
||||||
|
print '<div class="fichecenter">';
|
||||||
|
|
||||||
|
// Linke Box: Kundenauftrag-Info
|
||||||
|
print '<div class="fichehalfleft">';
|
||||||
|
print '<div class="underbanner clearboth"></div>';
|
||||||
|
print '<table class="border centpercent tableforfield">';
|
||||||
|
print '<tr class="liste_titre"><th colspan="2">Kundenauftrag</th></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td class="titlefield">'.$langs->trans("Ref").'</td>';
|
||||||
|
print '<td>'.$objectsrc->getNomUrl(1).'</td></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td>'.$langs->trans("Customer").'</td>';
|
||||||
|
print '<td>'.$customer->getNomUrl(1).'</td></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td>'.$langs->trans("RefCustomer").'</td>';
|
||||||
|
print '<td>'.dol_escape_htmltag($objectsrc->ref_client).'</td></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td>'.$langs->trans("Status").'</td>';
|
||||||
|
print '<td>'.$objectsrc->getLibStatut(4).'</td></tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
// Rechte Box: Lieferant und Referenz
|
||||||
|
print '<div class="fichehalfright">';
|
||||||
|
print '<div class="underbanner clearboth"></div>';
|
||||||
|
print '<table class="border centpercent tableforfield">';
|
||||||
|
print '<tr class="liste_titre"><th colspan="2">Neue Lieferantenbestellung</th></tr>';
|
||||||
|
|
||||||
|
// Alle Lieferanten mit Shop-URL abrufen, die Produkte aus diesem Auftrag liefern können
|
||||||
|
$sql = "SELECT DISTINCT s.rowid, s.nom
|
||||||
|
FROM ".MAIN_DB_PREFIX."societe s
|
||||||
|
INNER JOIN ".MAIN_DB_PREFIX."product_fournisseur_price pfp ON pfp.fk_soc = s.rowid
|
||||||
|
WHERE pfp.fk_product IN (
|
||||||
|
SELECT cd.fk_product FROM ".MAIN_DB_PREFIX."commandedet cd
|
||||||
|
WHERE cd.fk_commande = ".(int)$originid." AND cd.fk_product > 0
|
||||||
|
)
|
||||||
|
AND s.fournisseur = 1
|
||||||
|
ORDER BY s.nom ASC";
|
||||||
|
|
||||||
|
$resql = $db->query($sql);
|
||||||
|
$suppliers = array();
|
||||||
|
if ($resql) {
|
||||||
|
while ($obj = $db->fetch_object($resql)) {
|
||||||
|
$suppliers[$obj->rowid] = $obj->nom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td class="titlefield fieldrequired">Lieferant</td>';
|
||||||
|
print '<td><select name="supplier_id" id="supplier_id" class="flat maxwidth300" required>';
|
||||||
|
print '<option value="">-- Lieferant wählen --</option>';
|
||||||
|
foreach ($suppliers as $id => $name) {
|
||||||
|
$selected = (GETPOST('supplier_id', 'int') == $id) ? ' selected' : '';
|
||||||
|
print '<option value="'.$id.'"'.$selected.'>'.dol_escape_htmltag($name).'</option>';
|
||||||
|
}
|
||||||
|
print '</select></td></tr>';
|
||||||
|
|
||||||
|
print '<tr class="oddeven"><td>Lieferanten-Referenz</td>';
|
||||||
|
print '<td><input type="text" name="ref_supplier" value="'.dol_escape_htmltag($supplier_ref_default).'" class="flat quatrevingtpercent" placeholder="Kunde - Auftrag / Zeichen">';
|
||||||
|
print '</td></tr>';
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
print '</div>'; // Ende fichehalfright
|
||||||
|
|
||||||
|
print '</div>'; // Ende fichecenter
|
||||||
|
print '<div class="clearboth"></div>';
|
||||||
|
|
||||||
|
print '<br>';
|
||||||
|
|
||||||
|
// Zeilen-Auswahl mit Checkboxen
|
||||||
|
print '<div class="div-table-responsive-no-min">';
|
||||||
|
print '<table class="noborder centpercent" id="tablelines">';
|
||||||
|
print '<tr class="liste_titre">';
|
||||||
|
print '<th class="center" style="width: 30px;"><input type="checkbox" id="checkall" onclick="toggleAll(this)"></th>';
|
||||||
|
print '<th>Produkt</th>';
|
||||||
|
print '<th class="right">Menge</th>';
|
||||||
|
print '<th class="right">Lagerbestand</th>';
|
||||||
|
print '<th colspan="2">Lieferanten</th>';
|
||||||
|
print '</tr>';
|
||||||
|
|
||||||
|
// Produkte aus Kundenauftrag auflisten
|
||||||
|
foreach ($objectsrc->lines as $line) {
|
||||||
|
// Nur Produkte, keine Dienstleistungen
|
||||||
|
if (empty($line->fk_product) || $line->product_type != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produkt laden
|
||||||
|
$product = new Product($db);
|
||||||
|
$product->fetch($line->fk_product);
|
||||||
|
|
||||||
|
// Lagerbestand
|
||||||
|
$qtyStock = $product->stock_reel;
|
||||||
|
|
||||||
|
// Stock-Badge Farbe
|
||||||
|
$stockClass = 'badge-success';
|
||||||
|
if ($qtyStock <= 0) {
|
||||||
|
$stockClass = 'badge-danger';
|
||||||
|
} elseif ($product->seuil_stock_alerte > 0 && $qtyStock < $product->seuil_stock_alerte) {
|
||||||
|
$stockClass = 'badge-warning';
|
||||||
|
} elseif ($product->desiredstock > 0 && $qtyStock < $product->desiredstock) {
|
||||||
|
$stockClass = 'badge-secondary';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lieferanten für dieses Produkt
|
||||||
|
$sql_suppliers = "SELECT pfp.fk_soc, pfp.price, pfp.ref_fourn, s.nom
|
||||||
|
FROM ".MAIN_DB_PREFIX."product_fournisseur_price pfp
|
||||||
|
INNER JOIN ".MAIN_DB_PREFIX."societe s ON s.rowid = pfp.fk_soc
|
||||||
|
WHERE pfp.fk_product = ".(int)$line->fk_product."
|
||||||
|
ORDER BY pfp.price ASC";
|
||||||
|
$ressuppliers = $db->query($sql_suppliers);
|
||||||
|
$supplierList = array();
|
||||||
|
if ($ressuppliers) {
|
||||||
|
while ($objsup = $db->fetch_object($ressuppliers)) {
|
||||||
|
$supplierList[] = dol_escape_htmltag($objsup->nom).' ('.number_format($objsup->price, 2, ',', '.').' EUR)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print '<tr class="oddeven">';
|
||||||
|
|
||||||
|
// Checkbox
|
||||||
|
print '<td class="center">';
|
||||||
|
print '<input type="checkbox" name="toselect[]" value="'.$line->id.'" class="linecheckbox">';
|
||||||
|
print '</td>';
|
||||||
|
|
||||||
|
// Produkt
|
||||||
|
print '<td>';
|
||||||
|
print $product->getNomUrl(1);
|
||||||
|
print ' - '.dol_escape_htmltag($product->label);
|
||||||
|
print '</td>';
|
||||||
|
|
||||||
|
// Menge
|
||||||
|
print '<td class="right">'.(int)$line->qty.'</td>';
|
||||||
|
|
||||||
|
// Lagerbestand
|
||||||
|
print '<td class="right">';
|
||||||
|
print '<span class="badge '.$stockClass.'">'.(int)$qtyStock.'</span>';
|
||||||
|
print '</td>';
|
||||||
|
|
||||||
|
// Lieferanten
|
||||||
|
print '<td>';
|
||||||
|
if (!empty($supplierList)) {
|
||||||
|
print implode('<br>', $supplierList);
|
||||||
|
} else {
|
||||||
|
print '<span class="opacitymedium">Keine Lieferanten</span>';
|
||||||
|
}
|
||||||
|
print '</td>';
|
||||||
|
|
||||||
|
print '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
print '</table>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
print '<div class="center" style="margin-top: 20px;">';
|
||||||
|
print '<input type="submit" class="button button-save" value="Lieferantenbestellung erstellen">';
|
||||||
|
print ' ';
|
||||||
|
print '<a class="button button-cancel" href="'.DOL_URL_ROOT.'/commande/card.php?id='.$originid.'">Abbrechen</a>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '</form>';
|
||||||
|
|
||||||
|
// JavaScript für "Alle auswählen"
|
||||||
|
print '<script type="text/javascript">
|
||||||
|
function toggleAll(checkbox) {
|
||||||
|
var checkboxes = document.getElementsByClassName("linecheckbox");
|
||||||
|
for (var i = 0; i < checkboxes.length; i++) {
|
||||||
|
checkboxes[i].checked = checkbox.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>';
|
||||||
|
|
||||||
|
// Badge-Styles
|
||||||
|
print '<style>
|
||||||
|
.badge.badge-danger { background-color: #dc3545 !important; color: #fff !important; }
|
||||||
|
.badge.badge-warning { background-color: #fd7e14 !important; color: #fff !important; }
|
||||||
|
.badge.badge-secondary { background-color: #6c757d !important; color: #fff !important; }
|
||||||
|
.badge.badge-success { background-color: #28a745 !important; color: #fff !important; }
|
||||||
|
</style>';
|
||||||
|
|
||||||
|
llxFooter();
|
||||||
|
$db->close();
|
||||||
167
js/replenish.js
Executable file
167
js/replenish.js
Executable file
|
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
* SupplierLink3 - Replenish page enhancements
|
||||||
|
* Ersetzt die "Aktueller Lagerbestand"-Spalte mit Badge + Shop-Link
|
||||||
|
*/
|
||||||
|
console.log('SL3: replenish.js loaded');
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
console.log('SL3: DOM ready, sl3Data exists:', typeof window.sl3Data !== 'undefined');
|
||||||
|
|
||||||
|
if (typeof window.sl3Data === 'undefined') {
|
||||||
|
console.log('SL3: No data found, exiting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var productSuppliers = window.sl3Data;
|
||||||
|
var shopIcon = window.sl3ShopIcon || 'fas fa-store';
|
||||||
|
console.log('SL3: Processing', Object.keys(productSuppliers).length, 'products');
|
||||||
|
|
||||||
|
// CSS einfügen
|
||||||
|
$('head').append('<style>' +
|
||||||
|
'.badge.badge-danger { background-color: #dc3545 !important; color: #fff !important; }' +
|
||||||
|
'.badge.badge-warning { background-color: #fd7e14 !important; color: #fff !important; }' +
|
||||||
|
'.badge.badge-secondary { background-color: #6c757d !important; color: #fff !important; }' +
|
||||||
|
'.badge.badge-success { background-color: #28a745 !important; color: #fff !important; }' +
|
||||||
|
'.sl3-stock-wrapper { display: inline-flex; align-items: center; justify-content: flex-end; }' +
|
||||||
|
'.sl3-shop-col { display: inline-block; width: 28px; text-align: center; }' +
|
||||||
|
'.sl3-badge-col { display: inline-block; min-width: 55px; text-align: right; }' +
|
||||||
|
'.sl3-modal-item { padding: 10px; border-bottom: 1px solid #eee; }' +
|
||||||
|
'.sl3-modal-item:hover { background-color: #f5f5f5; }' +
|
||||||
|
'.sl3-modal-item:last-child { border-bottom: none; }' +
|
||||||
|
'</style>');
|
||||||
|
|
||||||
|
// Modal-Handler
|
||||||
|
$(document).on('click', '.sl3-modal-trigger', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var suppliers = $(this).data('suppliers');
|
||||||
|
if (!suppliers || suppliers.length === 0) return;
|
||||||
|
|
||||||
|
var content = '';
|
||||||
|
for (var i = 0; i < suppliers.length; i++) {
|
||||||
|
var sup = suppliers[i];
|
||||||
|
var isFirst = (i === 0);
|
||||||
|
var bgStyle = isFirst ? 'background-color: #e8f4fd;' : '';
|
||||||
|
var star = isFirst ? '<i class="fas fa-star" style="color: gold; margin-right: 5px;"></i>' : '';
|
||||||
|
|
||||||
|
content += '<div class="sl3-modal-item" style="' + bgStyle + '">';
|
||||||
|
content += '<div style="font-weight: ' + (isFirst ? 'bold' : 'normal') + '; margin-bottom: 5px;">' + star + sup.supplier_name + '</div>';
|
||||||
|
content += '<div style="font-size: 12px; color: #666; margin-bottom: 8px;">';
|
||||||
|
content += '<strong>' + parseFloat(sup.price).toFixed(2).replace('.', ',') + ' EUR</strong>';
|
||||||
|
content += ' - Art.-Nr: ' + sup.ref_fourn;
|
||||||
|
if (sup.min_qty > 1) content += ' (ab ' + sup.min_qty + ' St.)';
|
||||||
|
content += '</div>';
|
||||||
|
content += '<a href="' + sup.full_url + '" target="supplier_' + sup.supplier_id + '" class="button small">Im Shop</a>';
|
||||||
|
content += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('<div>').html(content).dialog({
|
||||||
|
modal: true,
|
||||||
|
title: 'Lieferanten-Shops',
|
||||||
|
width: 400,
|
||||||
|
buttons: { 'Schließen': function() { $(this).dialog('close'); } }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spalten-Indizes aus der Header-Zeile ermitteln
|
||||||
|
var colIndex = { stock: -1, desired: -1, alert: -1 };
|
||||||
|
$('table.liste tr.liste_titre th, table.liste tr.liste_titre td').each(function(idx) {
|
||||||
|
var text = $(this).text().trim().toLowerCase();
|
||||||
|
// Stock-Spalte (Aktueller Lagerbestand / Physical stock / Physischer Bestand)
|
||||||
|
if (text.indexOf('lagerbestand') >= 0 || text.indexOf('physical stock') >= 0 ||
|
||||||
|
text.indexOf('stock physique') >= 0 || text.indexOf('physischer bestand') >= 0) {
|
||||||
|
colIndex.stock = idx;
|
||||||
|
}
|
||||||
|
// Desired Stock (Gewünschter Lagerbestand / Wunschbestand)
|
||||||
|
if (text.indexOf('gewünscht') >= 0 || text.indexOf('wunsch') >= 0 ||
|
||||||
|
text.indexOf('desired') >= 0 || text.indexOf('souhaité') >= 0) {
|
||||||
|
colIndex.desired = idx;
|
||||||
|
}
|
||||||
|
// Alert Stock (Grenzwert für Warnung / Mindestbestand / Stock limit)
|
||||||
|
if (text.indexOf('grenzwert') >= 0 || text.indexOf('warnung') >= 0 ||
|
||||||
|
text.indexOf('mindest') >= 0 || text.indexOf('limit') >= 0 ||
|
||||||
|
text.indexOf('alerte') >= 0 || text.indexOf('alert') >= 0) {
|
||||||
|
colIndex.alert = idx;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('SL3: Column indices found:', colIndex);
|
||||||
|
|
||||||
|
// Fallback auf feste Indizes wenn nicht gefunden (für Produkte ohne Service-Spalte)
|
||||||
|
if (colIndex.stock < 0) colIndex.stock = 5;
|
||||||
|
if (colIndex.desired < 0) colIndex.desired = 3;
|
||||||
|
if (colIndex.alert < 0) colIndex.alert = 4;
|
||||||
|
|
||||||
|
// Jede Datenzeile in der Tabelle durchgehen
|
||||||
|
var rowsFound = 0;
|
||||||
|
var rowsProcessed = 0;
|
||||||
|
$('table.liste tr').not('.liste_titre').each(function() {
|
||||||
|
rowsFound++;
|
||||||
|
var $row = $(this);
|
||||||
|
|
||||||
|
// Produkt-Link finden um die Produkt-ID zu extrahieren
|
||||||
|
var $productLink = $row.find('td a[href*="product/card.php?id="]').first();
|
||||||
|
if ($productLink.length === 0) {
|
||||||
|
// Versuche alternative Selektoren
|
||||||
|
$productLink = $row.find('td a[href*="product.php?id="]').first();
|
||||||
|
}
|
||||||
|
if ($productLink.length === 0) {
|
||||||
|
console.log('SL3: Row', rowsFound, '- no product link found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var href = $productLink.attr('href');
|
||||||
|
var match = href.match(/id=(\d+)/);
|
||||||
|
if (!match) {
|
||||||
|
console.log('SL3: Row', rowsFound, '- no ID in href:', href);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var productId = match[1];
|
||||||
|
rowsProcessed++;
|
||||||
|
|
||||||
|
// Stock-Spalte dynamisch finden
|
||||||
|
var $stockCell = $row.find('td').eq(colIndex.stock);
|
||||||
|
if ($stockCell.length === 0) return;
|
||||||
|
|
||||||
|
// Stock-Wert aus der Zelle lesen
|
||||||
|
var stock = parseFloat($stockCell.text().trim()) || 0;
|
||||||
|
|
||||||
|
// Desired-Stock und Alert-Stock
|
||||||
|
var desired = parseFloat($row.find('td').eq(colIndex.desired).text().trim()) || 0;
|
||||||
|
var alert = parseFloat($row.find('td').eq(colIndex.alert).text().trim()) || 0;
|
||||||
|
|
||||||
|
// Badge-Klasse bestimmen
|
||||||
|
var badgeClass = 'badge-success';
|
||||||
|
if (stock < 1) {
|
||||||
|
badgeClass = 'badge-danger';
|
||||||
|
} else if (alert > 0 && stock <= alert) {
|
||||||
|
badgeClass = 'badge-warning';
|
||||||
|
} else if (desired > 0 && stock < desired) {
|
||||||
|
badgeClass = 'badge-secondary';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shop-Link erstellen (immer mit Container für feste Breite)
|
||||||
|
var shopIconHtml = '';
|
||||||
|
var suppliers = productSuppliers[productId];
|
||||||
|
if (suppliers && suppliers.length > 0) {
|
||||||
|
if (suppliers.length === 1) {
|
||||||
|
var sup = suppliers[0];
|
||||||
|
shopIconHtml = '<a href="' + sup.full_url + '" target="supplier_' + sup.supplier_id + '" ' +
|
||||||
|
'class="classfortooltip" title="' + sup.supplier_name + '" style="color: #0077b6; font-size: 14px;">' +
|
||||||
|
'<i class="' + shopIcon + '"></i></a>';
|
||||||
|
} else {
|
||||||
|
shopIconHtml = '<a href="#" class="sl3-modal-trigger" data-suppliers=\'' + JSON.stringify(suppliers) + '\' ' +
|
||||||
|
'title="' + suppliers.length + ' Lieferanten" style="color: #0077b6; font-size: 14px;">' +
|
||||||
|
'<i class="' + shopIcon + '"></i><i class="fas fa-caret-down" style="font-size:8px;margin-left:2px;"></i></a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stock-Zelle ersetzen: inline-flex wie im Kundenauftrag
|
||||||
|
var html = '<div class="sl3-stock-wrapper">' +
|
||||||
|
'<span class="sl3-shop-col">' + shopIconHtml + '</span>' +
|
||||||
|
'<span class="sl3-badge-col"><span class="badge ' + badgeClass + '">' + Math.floor(stock) + '</span></span>' +
|
||||||
|
'</div>';
|
||||||
|
$stockCell.html(html).addClass('right').css('text-align', 'right');
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('SL3: Processing complete - rows found:', rowsFound, 'rows processed:', rowsProcessed);
|
||||||
|
});
|
||||||
97
langs/de_DE/supplierlink3.lang
Executable file
97
langs/de_DE/supplierlink3.lang
Executable file
|
|
@ -0,0 +1,97 @@
|
||||||
|
# SupplierLink3 - German translation
|
||||||
|
# Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generic
|
||||||
|
#
|
||||||
|
ModuleSupplierLink3Name = SupplierLink3
|
||||||
|
ModuleSupplierLink3Desc = Lieferantenverknüpfung mit Shop-Links und Lagerbestand-Anzeige
|
||||||
|
|
||||||
|
#
|
||||||
|
# Admin page
|
||||||
|
#
|
||||||
|
SupplierLink3Setup = SupplierLink3 Einstellungen
|
||||||
|
Settings = Einstellungen
|
||||||
|
SupplierLink3SetupPage = SupplierLink3 Einstellungsseite
|
||||||
|
SupplierLink3Description = Zeigt Shop-Links und Lagerbestände in Aufträgen, Angeboten und Bestellungen an
|
||||||
|
|
||||||
|
# Settings sections
|
||||||
|
SL3_DisplaySettings = Anzeige-Einstellungen
|
||||||
|
SL3_DisplaySettingsDesc = Wählen Sie, wo Shop-Links und Lagerbestände angezeigt werden sollen
|
||||||
|
SL3_IconSettings = Symbol-Einstellungen
|
||||||
|
SL3_IconSettingsDesc = Passen Sie die verwendeten Symbole an
|
||||||
|
SL3_DebugSettings = Debug-Einstellungen
|
||||||
|
SL3_RegisteredHooks = Registrierte Hooks
|
||||||
|
|
||||||
|
# Display areas
|
||||||
|
SL3_EnableOrderCard = Kundenaufträge
|
||||||
|
SL3_EnableOrderCardDesc = Shop-Links und Lagerbestand in Kundenaufträgen anzeigen
|
||||||
|
SL3_EnablePropalCard = Angebote
|
||||||
|
SL3_EnablePropalCardDesc = Shop-Links und Lagerbestand in Angeboten anzeigen
|
||||||
|
SL3_EnableSupplierOrder = Lieferantenbestellungen
|
||||||
|
SL3_EnableSupplierOrderDesc = Shop-Links und Lagerbestand in Lieferantenbestellungen anzeigen
|
||||||
|
SL3_EnableReplenish = Nachbestellung (Lager)
|
||||||
|
SL3_EnableReplenishDesc = Shop-Links und Lagerbestand in der Nachbestellungsliste anzeigen
|
||||||
|
SL3_EnableProductCard = Produktkarte
|
||||||
|
SL3_EnableProductCardDesc = Shop-Links auf der Produktkarte anzeigen
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
SL3_ShopIcon = Shop-Symbol
|
||||||
|
SL3_ShopIconDesc = FontAwesome-Klasse für das Shop-Symbol (z.B. fa-store, fa-shopping-cart, fa-external-link-alt)
|
||||||
|
SL3_StockIcon = Lagerbestand-Symbol
|
||||||
|
SL3_StockIconDesc = FontAwesome-Klasse für das Lagerbestand-Symbol (z.B. fa-box, fa-warehouse, fa-cubes)
|
||||||
|
SL3_IconPreview = Vorschau
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
SL3_DebugMode = Debug-Modus
|
||||||
|
SL3_DebugModeDesc = Schreibt Debug-Meldungen in die Log-Datei
|
||||||
|
SL3_DebugLog = Debug-Log
|
||||||
|
SL3_DebugLogFile = Log-Datei
|
||||||
|
SL3_DebugLogSize = Größe
|
||||||
|
SL3_DebugLogClear = Log löschen
|
||||||
|
SL3_DebugLogCleared = Debug-Log wurde gelöscht
|
||||||
|
SL3_DebugLogEmpty = Keine Log-Datei vorhanden. Aktivieren Sie den Debug-Modus und führen Sie eine Aktion aus.
|
||||||
|
SL3_DebugLogLastEntries = Letzte Einträge
|
||||||
|
|
||||||
|
# Hooks
|
||||||
|
SL3_HookOrderSupplier = Lieferantenbestellungen - Shop-Links und Lagerbestand
|
||||||
|
SL3_HookOrderCard = Kundenaufträge - Shop-Links, Lagerbestand und Button "Lieferantenbestellung erstellen"
|
||||||
|
SL3_HookPropalCard = Angebote - Shop-Links und Lagerbestand
|
||||||
|
SL3_HookProductCard = Produktkarte - Shop-Links
|
||||||
|
SL3_HookProductPrice = Produktpreise - Shop-Links
|
||||||
|
SL3_HookReplenish = Nachbestellung - Shop-Links und Lagerbestand
|
||||||
|
|
||||||
|
# Messages
|
||||||
|
SetupSaved = Einstellungen gespeichert
|
||||||
|
|
||||||
|
#
|
||||||
|
# About page
|
||||||
|
#
|
||||||
|
About = Über
|
||||||
|
SupplierLink3About = Über SupplierLink3
|
||||||
|
SupplierLink3AboutPage = SupplierLink3 Informationen
|
||||||
|
SL3_Version = Version
|
||||||
|
SL3_Author = Autor
|
||||||
|
SL3_License = Lizenz
|
||||||
|
SL3_Features = Funktionen
|
||||||
|
SL3_Feature1 = Shop-Links zu Lieferanten-Webshops
|
||||||
|
SL3_Feature2 = Lagerbestand-Anzeige mit farbigen Badges
|
||||||
|
SL3_Feature3 = Schnelle Lieferantenbestellung aus Kundenaufträgen
|
||||||
|
SL3_Feature4 = Multi-Lieferanten-Unterstützung mit Popup-Auswahl
|
||||||
|
SL3_Documentation = Dokumentation
|
||||||
|
|
||||||
|
#
|
||||||
|
# Stock badges
|
||||||
|
#
|
||||||
|
SL3_StockCritical = Kritisch (< 1)
|
||||||
|
SL3_StockWarning = Warnung (unter Mindestbestand)
|
||||||
|
SL3_StockLow = Niedrig (unter Wunschbestand)
|
||||||
|
SL3_StockOk = Ausreichend
|
||||||
|
|
||||||
|
#
|
||||||
|
# Supplier order creation
|
||||||
|
#
|
||||||
|
SL3_CreateSupplierOrder = Lieferantenbestellung erstellen
|
||||||
|
SL3_SelectSupplier = Lieferant wählen
|
||||||
|
SL3_SelectProducts = Produkte auswählen
|
||||||
|
SL3_NoSuppliers = Keine Lieferanten verfügbar
|
||||||
|
|
@ -1,42 +1,97 @@
|
||||||
# Translation file
|
# SupplierLink3 - English translation
|
||||||
|
# Copyright (C) 2025 Eduard Wisch <data@data-it-solution.de>
|
||||||
|
|
||||||
#
|
#
|
||||||
# Generic
|
# Generic
|
||||||
#
|
#
|
||||||
|
|
||||||
# Module label 'ModuleSupplierLink3Name'
|
|
||||||
ModuleSupplierLink3Name = SupplierLink3
|
ModuleSupplierLink3Name = SupplierLink3
|
||||||
# Module description 'ModuleSupplierLink3Desc'
|
ModuleSupplierLink3Desc = Supplier linking with shop links and stock display
|
||||||
ModuleSupplierLink3Desc = SupplierLink3 description
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Admin page
|
# Admin page
|
||||||
#
|
#
|
||||||
SupplierLink3Setup = SupplierLink3 setup
|
SupplierLink3Setup = SupplierLink3 Setup
|
||||||
Settings = Settings
|
Settings = Settings
|
||||||
SupplierLink3SetupPage = SupplierLink3 setup page
|
SupplierLink3SetupPage = SupplierLink3 setup page
|
||||||
NewSection=New section
|
SupplierLink3Description = Displays shop links and stock levels in orders, proposals and supplier orders
|
||||||
SUPPLIERLINK3_MYPARAM1 = My param 1
|
|
||||||
SUPPLIERLINK3_MYPARAM1Tooltip = My param 1 tooltip
|
|
||||||
SUPPLIERLINK3_MYPARAM2=My param 2
|
|
||||||
SUPPLIERLINK3_MYPARAM2Tooltip=My param 2 tooltip
|
|
||||||
|
|
||||||
|
# Settings sections
|
||||||
|
SL3_DisplaySettings = Display Settings
|
||||||
|
SL3_DisplaySettingsDesc = Choose where shop links and stock levels should be displayed
|
||||||
|
SL3_IconSettings = Icon Settings
|
||||||
|
SL3_IconSettingsDesc = Customize the icons used
|
||||||
|
SL3_DebugSettings = Debug Settings
|
||||||
|
SL3_RegisteredHooks = Registered Hooks
|
||||||
|
|
||||||
|
# Display areas
|
||||||
|
SL3_EnableOrderCard = Customer Orders
|
||||||
|
SL3_EnableOrderCardDesc = Show shop links and stock in customer orders
|
||||||
|
SL3_EnablePropalCard = Proposals
|
||||||
|
SL3_EnablePropalCardDesc = Show shop links and stock in proposals
|
||||||
|
SL3_EnableSupplierOrder = Supplier Orders
|
||||||
|
SL3_EnableSupplierOrderDesc = Show shop links and stock in supplier orders
|
||||||
|
SL3_EnableReplenish = Stock Replenishment
|
||||||
|
SL3_EnableReplenishDesc = Show shop links and stock in the replenishment list
|
||||||
|
SL3_EnableProductCard = Product Card
|
||||||
|
SL3_EnableProductCardDesc = Show shop links on the product card
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
SL3_ShopIcon = Shop Icon
|
||||||
|
SL3_ShopIconDesc = FontAwesome class for the shop icon (e.g. fa-store, fa-shopping-cart, fa-external-link-alt)
|
||||||
|
SL3_StockIcon = Stock Icon
|
||||||
|
SL3_StockIconDesc = FontAwesome class for the stock icon (e.g. fa-box, fa-warehouse, fa-cubes)
|
||||||
|
SL3_IconPreview = Preview
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
SL3_DebugMode = Debug Mode
|
||||||
|
SL3_DebugModeDesc = Writes debug messages to the log file
|
||||||
|
SL3_DebugLog = Debug Log
|
||||||
|
SL3_DebugLogFile = Log file
|
||||||
|
SL3_DebugLogSize = Size
|
||||||
|
SL3_DebugLogClear = Clear log
|
||||||
|
SL3_DebugLogCleared = Debug log cleared
|
||||||
|
SL3_DebugLogEmpty = No log file exists. Enable debug mode and perform an action.
|
||||||
|
SL3_DebugLogLastEntries = Last entries
|
||||||
|
|
||||||
|
# Hooks
|
||||||
|
SL3_HookOrderSupplier = Supplier Orders - Shop links and stock
|
||||||
|
SL3_HookOrderCard = Customer Orders - Shop links, stock and "Create supplier order" button
|
||||||
|
SL3_HookPropalCard = Proposals - Shop links and stock
|
||||||
|
SL3_HookProductCard = Product Card - Shop links
|
||||||
|
SL3_HookProductPrice = Product Prices - Shop links
|
||||||
|
SL3_HookReplenish = Replenishment - Shop links and stock
|
||||||
|
|
||||||
|
# Messages
|
||||||
|
SetupSaved = Settings saved
|
||||||
|
|
||||||
#
|
#
|
||||||
# About page
|
# About page
|
||||||
#
|
#
|
||||||
About = About
|
About = About
|
||||||
SupplierLink3About = About SupplierLink3
|
SupplierLink3About = About SupplierLink3
|
||||||
SupplierLink3AboutPage = SupplierLink3 about page
|
SupplierLink3AboutPage = SupplierLink3 information
|
||||||
|
SL3_Version = Version
|
||||||
|
SL3_Author = Author
|
||||||
|
SL3_License = License
|
||||||
|
SL3_Features = Features
|
||||||
|
SL3_Feature1 = Shop links to supplier webshops
|
||||||
|
SL3_Feature2 = Stock display with colored badges
|
||||||
|
SL3_Feature3 = Quick supplier order creation from customer orders
|
||||||
|
SL3_Feature4 = Multi-supplier support with popup selection
|
||||||
|
SL3_Documentation = Documentation
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sample page
|
# Stock badges
|
||||||
#
|
#
|
||||||
SupplierLink3Area = Home SupplierLink3
|
SL3_StockCritical = Critical (< 1)
|
||||||
MyPageName = My page name
|
SL3_StockWarning = Warning (below minimum stock)
|
||||||
|
SL3_StockLow = Low (below desired stock)
|
||||||
|
SL3_StockOk = Sufficient
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sample widget
|
# Supplier order creation
|
||||||
#
|
#
|
||||||
MyWidget = My widget
|
SL3_CreateSupplierOrder = Create Supplier Order
|
||||||
MyWidgetDescription = My widget description
|
SL3_SelectSupplier = Select supplier
|
||||||
|
SL3_SelectProducts = Select products
|
||||||
|
SL3_NoSuppliers = No suppliers available
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue