Version 3.4: Fehlende Lieferantenpreise und Bugfixes
- EAN-basierte Suche fuer Cross-Katalog Lieferantenpreise - Unique-Key auf Barcode entfernt (mehrere Lieferanten pro EAN) - Variable $extrafields Namenskollision mit Dolibarr-Core behoben - Duplikate bei gleichen Produkten auf mehreren Rechnungszeilen vermieden - select2-Suche auf allen Zeilen (eindeutige HTML-IDs) - Konsolidierter Bereich fuer fehlende Lieferantenpreise - Refresh-Button und Alle/Keine auswaehlen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1b2357a2aa
commit
82d4e8a323
5 changed files with 172 additions and 107 deletions
14
ChangeLog.md
14
ChangeLog.md
|
|
@ -1,5 +1,19 @@
|
||||||
# CHANGELOG MODULE IMPORTZUGFERD FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
# CHANGELOG MODULE IMPORTZUGFERD FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
|
||||||
|
|
||||||
|
## 3.4
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
- Fehlende Lieferantenpreise: EAN-basierte Suche nutzt jetzt Barcode aus Lieferantenpreis statt Artikelnummer
|
||||||
|
- Fehlende Lieferantenpreise: Unique-Key auf Barcode entfernt (mehrere Lieferanten koennen gleichen EAN haben)
|
||||||
|
- Fehlende Lieferantenpreise: Variable $extrafields Namenskollision mit Dolibarr-Core behoben
|
||||||
|
- Fehlende Lieferantenpreise: Duplikate bei gleichen Produkten auf mehreren Rechnungszeilen vermieden
|
||||||
|
- Produktauswahl: select2-Suche funktioniert jetzt auf allen Zeilen (eindeutige HTML-IDs)
|
||||||
|
|
||||||
|
### Verbesserungen
|
||||||
|
- Fehlende Lieferantenpreise in konsolidiertem Bereich unterhalb der Produkttabelle
|
||||||
|
- Refresh-Button fuer Produktlisten nach Anlage neuer Produkte
|
||||||
|
- Alle auswaehlen / Keine auswaehlen fuer fehlende Lieferantenpreise
|
||||||
|
|
||||||
## 3.3
|
## 3.3
|
||||||
|
|
||||||
### Sicherheit und Code-Qualitaet
|
### Sicherheit und Code-Qualitaet
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class modImportZugferd extends DolibarrModules
|
||||||
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@importzugferd'
|
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@importzugferd'
|
||||||
|
|
||||||
// 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 = '3.3';
|
$this->version = '3.4';
|
||||||
// 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';
|
||||||
|
|
||||||
|
|
|
||||||
257
import.php
257
import.php
|
|
@ -77,6 +77,13 @@ $supplier_id = GETPOST('supplier_id', 'int');
|
||||||
$line_id = GETPOST('line_id', 'int');
|
$line_id = GETPOST('line_id', 'int');
|
||||||
$product_id = GETPOST('product_id', 'int');
|
$product_id = GETPOST('product_id', 'int');
|
||||||
$template_product_id = GETPOST('template_product_id', 'int');
|
$template_product_id = GETPOST('template_product_id', 'int');
|
||||||
|
// Zeilenspezifische Produkt-IDs (wegen eindeutiger select2-IDs pro Zeile)
|
||||||
|
if (empty($product_id) && $line_id > 0) {
|
||||||
|
$product_id = GETPOST('product_id_'.$line_id, 'int');
|
||||||
|
}
|
||||||
|
if (empty($template_product_id) && $line_id > 0) {
|
||||||
|
$template_product_id = GETPOST('template_product_id_'.$line_id, 'int');
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize objects
|
// Initialize objects
|
||||||
$form = new Form($db);
|
$form = new Form($db);
|
||||||
|
|
@ -499,21 +506,34 @@ if ($action == 'removeproduct' && $line_id > 0) {
|
||||||
$import->fetch($id);
|
$import->fetch($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add missing supplier prices from other catalogs
|
// Fehlende Lieferantenpreise aus anderen Katalogen hinzufuegen
|
||||||
if ($action == 'addmissingprices' && $id > 0) {
|
if ($action == 'addmissingprices' && $id > 0) {
|
||||||
$import->fetch($id);
|
$import->fetch($id);
|
||||||
$fk_product = GETPOSTINT('fk_product');
|
$selectedPrices = GETPOST('add_prices', 'array');
|
||||||
$addSupplierPrices = GETPOST('add_supplier_prices', 'array');
|
|
||||||
|
|
||||||
if ($fk_product > 0 && !empty($addSupplierPrices)) {
|
|
||||||
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
|
|
||||||
require_once './class/datanorm.class.php';
|
|
||||||
require_once './class/productmapping.class.php';
|
|
||||||
|
|
||||||
|
if (!empty($selectedPrices)) {
|
||||||
$addedCount = 0;
|
$addedCount = 0;
|
||||||
|
$processedKeys = array();
|
||||||
|
|
||||||
|
foreach ($selectedPrices as $entry) {
|
||||||
|
// Duplikate ueberspringen
|
||||||
|
if (isset($processedKeys[$entry])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$processedKeys[$entry] = true;
|
||||||
|
|
||||||
|
$parts = explode(',', $entry);
|
||||||
|
if (count($parts) !== 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$productId = (int) $parts[0];
|
||||||
|
$socId = (int) $parts[1];
|
||||||
|
$datanormId = (int) $parts[2];
|
||||||
|
|
||||||
|
if ($productId <= 0 || $socId <= 0 || $datanormId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($addSupplierPrices as $socId => $datanormId) {
|
|
||||||
// Fetch the Datanorm article
|
|
||||||
$datanorm = new Datanorm($db);
|
$datanorm = new Datanorm($db);
|
||||||
if ($datanorm->fetch($datanormId) > 0) {
|
if ($datanorm->fetch($datanormId) > 0) {
|
||||||
$altSupplier = new Societe($db);
|
$altSupplier = new Societe($db);
|
||||||
|
|
@ -524,38 +544,16 @@ if ($action == 'addmissingprices' && $id > 0) {
|
||||||
$purchasePrice = $datanorm->price / $datanorm->price_unit;
|
$purchasePrice = $datanorm->price / $datanorm->price_unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare extrafields
|
$priceExtrafields = datanormBuildSupplierPriceExtrafields($datanorm);
|
||||||
$extrafields = array();
|
$result = datanormAddSupplierPrice($db, $productId, $datanorm, $altSupplier, $user, $purchasePrice, 19, $priceExtrafields);
|
||||||
if (!empty($datanorm->metal_surcharge) && $datanorm->metal_surcharge > 0) {
|
|
||||||
$extrafields['options_kupferzuschlag'] = $datanorm->metal_surcharge;
|
|
||||||
}
|
|
||||||
if (!empty($datanorm->price_unit) && $datanorm->price_unit > 1) {
|
|
||||||
$extrafields['options_preiseinheit'] = $datanorm->price_unit;
|
|
||||||
}
|
|
||||||
if (!empty($datanorm->product_group)) {
|
|
||||||
$extrafields['options_warengruppe'] = $datanorm->product_group;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add supplier price
|
|
||||||
$prodfourn = new ProductFournisseur($db);
|
|
||||||
$prodfourn->id = $fk_product;
|
|
||||||
$result = $prodfourn->update_buyprice(
|
|
||||||
1, $purchasePrice, $user, 'HT', $altSupplier, 0,
|
|
||||||
$datanorm->article_number, 19,
|
|
||||||
0, 0, 0, 0, 0, 0, array(), '',
|
|
||||||
0, 'HT', 1, '',
|
|
||||||
trim($datanorm->short_text1 . ($datanorm->short_text2 ? ' ' . $datanorm->short_text2 : '')),
|
|
||||||
!empty($datanorm->ean) ? $datanorm->ean : '',
|
|
||||||
!empty($datanorm->ean) ? 2 : 0,
|
|
||||||
$extrafields
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($result > 0) {
|
if ($result > 0) {
|
||||||
// Create product mapping
|
datanormInsertPriceExtrafields($db, $result, $priceExtrafields);
|
||||||
|
|
||||||
$mapping = new ProductMapping($db);
|
$mapping = new ProductMapping($db);
|
||||||
$mapping->fk_soc = $socId;
|
$mapping->fk_soc = $socId;
|
||||||
$mapping->supplier_ref = $datanorm->article_number;
|
$mapping->supplier_ref = $datanorm->article_number;
|
||||||
$mapping->fk_product = $fk_product;
|
$mapping->fk_product = $productId;
|
||||||
$mapping->ean = $datanorm->ean;
|
$mapping->ean = $datanorm->ean;
|
||||||
$mapping->manufacturer_ref = $datanorm->manufacturer_ref;
|
$mapping->manufacturer_ref = $datanorm->manufacturer_ref;
|
||||||
$mapping->description = $datanorm->short_text1;
|
$mapping->description = $datanorm->short_text1;
|
||||||
|
|
@ -1968,6 +1966,7 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
$allProductsMatched = true;
|
$allProductsMatched = true;
|
||||||
$matchedLinesCount = 0;
|
$matchedLinesCount = 0;
|
||||||
$totalLinesCount = count($lines);
|
$totalLinesCount = count($lines);
|
||||||
|
$allMissingPrices = array(); // Fehlende Lieferantenpreise sammeln
|
||||||
|
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$hasProduct = ($line->fk_product > 0);
|
$hasProduct = ($line->fk_product > 0);
|
||||||
|
|
@ -2069,44 +2068,48 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
print '<i class="fas fa-times"></i>';
|
print '<i class="fas fa-times"></i>';
|
||||||
print '</a>';
|
print '</a>';
|
||||||
|
|
||||||
// Check for missing supplier prices from other catalogs
|
// Fehlende Lieferantenpreise aus anderen Katalogen sammeln (Anzeige weiter unten)
|
||||||
if ($import->fk_soc > 0 && getDolGlobalString('IMPORTZUGFERD_DATANORM_SEARCH_ALL')) {
|
if ($import->fk_soc > 0 && getDolGlobalString('IMPORTZUGFERD_DATANORM_SEARCH_ALL')) {
|
||||||
// Get existing supplier prices for this product
|
// Alle vorhandenen Lieferantenpreise fuer dieses Produkt laden
|
||||||
$sqlExistingPrices = "SELECT fk_soc FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
|
$sqlExistingPrices = "SELECT fk_soc, price, barcode FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
|
||||||
$sqlExistingPrices .= " WHERE fk_product = ".(int)$line->fk_product;
|
$sqlExistingPrices .= " WHERE fk_product = ".(int)$line->fk_product;
|
||||||
$resExistingPrices = $db->query($sqlExistingPrices);
|
$resExistingPrices = $db->query($sqlExistingPrices);
|
||||||
$existingSupplierIds = array();
|
$existingSupplierIds = array();
|
||||||
|
$currentSupplierPrice = 0;
|
||||||
|
$supplierEan = '';
|
||||||
if ($resExistingPrices) {
|
if ($resExistingPrices) {
|
||||||
while ($objPrice = $db->fetch_object($resExistingPrices)) {
|
while ($objPrice = $db->fetch_object($resExistingPrices)) {
|
||||||
$existingSupplierIds[$objPrice->fk_soc] = true;
|
$existingSupplierIds[$objPrice->fk_soc] = true;
|
||||||
|
// Preis und EAN vom Rechnungslieferanten merken
|
||||||
|
if ($objPrice->fk_soc == $import->fk_soc) {
|
||||||
|
$currentSupplierPrice = $objPrice->price;
|
||||||
|
if (!empty($objPrice->barcode)) {
|
||||||
|
$supplierEan = $objPrice->barcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current supplier price for comparison
|
// EAN-Quellen: 1. Lieferantenpreis-Barcode, 2. Import-Zeile EAN
|
||||||
$currentSupplierPrice = 0;
|
$searchEan = !empty($supplierEan) ? $supplierEan : (!empty($line->ean) ? $line->ean : '');
|
||||||
if (isset($existingSupplierIds[$import->fk_soc])) {
|
|
||||||
$sqlCurrentPrice = "SELECT price FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
|
|
||||||
$sqlCurrentPrice .= " WHERE fk_product = ".(int)$line->fk_product;
|
|
||||||
$sqlCurrentPrice .= " AND fk_soc = ".(int)$import->fk_soc;
|
|
||||||
$sqlCurrentPrice .= " ORDER BY unitprice ASC LIMIT 1";
|
|
||||||
$resCurrentPrice = $db->query($sqlCurrentPrice);
|
|
||||||
if ($resCurrentPrice && $db->num_rows($resCurrentPrice) > 0) {
|
|
||||||
$objCurrentPrice = $db->fetch_object($resCurrentPrice);
|
|
||||||
$currentSupplierPrice = $objCurrentPrice->price;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search in all Datanorm catalogs for this article
|
|
||||||
$datanormSearch = new Datanorm($db);
|
$datanormSearch = new Datanorm($db);
|
||||||
$searchRef = !empty($line->supplier_ref) ? $line->supplier_ref : (!empty($line->ean) ? $line->ean : '');
|
$allCatalogResults = array();
|
||||||
if (!empty($searchRef)) {
|
|
||||||
$allCatalogResults = $datanormSearch->searchByArticleNumber($searchRef, $import->fk_soc, true, 10);
|
// Primaer: EAN-Suche in Datanorm-Katalogen (zuverlaessigste Methode)
|
||||||
|
if (!empty($searchEan)) {
|
||||||
|
$allCatalogResults = $datanormSearch->searchByArticleNumber($searchEan, $import->fk_soc, true, 10);
|
||||||
|
}
|
||||||
|
// Fallback: Lieferanten-Artikelnummer wenn EAN nichts fand
|
||||||
|
if (empty($allCatalogResults) && !empty($line->supplier_ref)) {
|
||||||
|
$allCatalogResults = $datanormSearch->searchByArticleNumber($line->supplier_ref, $import->fk_soc, true, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($allCatalogResults)) {
|
||||||
|
|
||||||
// Filter to only show suppliers without existing price
|
|
||||||
$missingSuppliers = array();
|
$missingSuppliers = array();
|
||||||
foreach ($allCatalogResults as $catalogResult) {
|
foreach ($allCatalogResults as $catalogResult) {
|
||||||
if (!isset($existingSupplierIds[$catalogResult['fk_soc']])) {
|
if (!isset($existingSupplierIds[$catalogResult['fk_soc']])) {
|
||||||
// Load supplier name
|
|
||||||
$altSupplier = new Societe($db);
|
$altSupplier = new Societe($db);
|
||||||
$altSupplier->fetch($catalogResult['fk_soc']);
|
$altSupplier->fetch($catalogResult['fk_soc']);
|
||||||
|
|
||||||
|
|
@ -2128,54 +2131,20 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show missing suppliers with checkboxes
|
|
||||||
if (!empty($missingSuppliers)) {
|
if (!empty($missingSuppliers)) {
|
||||||
print '<div style="margin-top: 8px; padding: 8px; background-color: #fcf8e3; border: 1px solid #faebcc; border-radius: 4px; font-size: 0.85em;">';
|
// Hinweis in der Zeile anzeigen
|
||||||
print '<div style="font-weight: bold; color: #8a6d3b; margin-bottom: 5px;">';
|
print ' <span class="badge badge-warning" title="'.$langs->trans('MissingSupplierPrices').'"><i class="fas fa-plus-circle"></i> '.count($missingSuppliers).'</span>';
|
||||||
print '<i class="fas fa-plus-circle"></i> '.$langs->trans('MissingSupplierPrices');
|
|
||||||
print '</div>';
|
|
||||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" style="margin: 0;">';
|
|
||||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
|
||||||
print '<input type="hidden" name="action" value="addmissingprices">';
|
|
||||||
print '<input type="hidden" name="id" value="'.$import->id.'">';
|
|
||||||
print '<input type="hidden" name="fk_product" value="'.$line->fk_product.'">';
|
|
||||||
|
|
||||||
foreach ($missingSuppliers as $missing) {
|
// Duplikate vermeiden (gleiches Produkt auf mehreren Rechnungszeilen)
|
||||||
$priceDiff = 0;
|
if (!isset($allMissingPrices[$line->fk_product])) {
|
||||||
$priceDiffPercent = 0;
|
$allMissingPrices[$line->fk_product] = array(
|
||||||
$diffHtml = '';
|
'product_id' => $line->fk_product,
|
||||||
|
'product_ref' => $product->ref,
|
||||||
if ($currentSupplierPrice > 0) {
|
'product_label' => $product->label,
|
||||||
$priceDiff = $missing['purchase_price'] - $currentSupplierPrice;
|
'current_price' => $currentSupplierPrice,
|
||||||
$priceDiffPercent = ($priceDiff / $currentSupplierPrice) * 100;
|
'missing' => $missingSuppliers,
|
||||||
|
);
|
||||||
if ($priceDiff < 0) {
|
|
||||||
$diffHtml = '<span style="color: #5cb85c;"><i class="fas fa-arrow-down"></i> '.number_format(abs($priceDiffPercent), 1).'%</span>';
|
|
||||||
} elseif ($priceDiff > 0) {
|
|
||||||
$diffHtml = '<span style="color: #d9534f;"><i class="fas fa-arrow-up"></i> +'.number_format($priceDiffPercent, 1).'%</span>';
|
|
||||||
} else {
|
|
||||||
$diffHtml = '<span class="opacitymedium">=</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print '<div style="padding: 3px 0;">';
|
|
||||||
print '<label style="cursor: pointer;">';
|
|
||||||
print '<input type="checkbox" name="add_supplier_prices['.$missing['fk_soc'].']" value="'.$missing['datanorm_id'].'" style="margin-right: 5px;">';
|
|
||||||
print '<strong>'.dol_escape_htmltag($missing['supplier_name']).'</strong>';
|
|
||||||
print ' <code style="font-size: 0.9em;">'.dol_escape_htmltag($missing['article_number']).'</code>';
|
|
||||||
print ' @ <strong>'.price($missing['purchase_price']).'</strong>';
|
|
||||||
if (!empty($diffHtml)) {
|
|
||||||
print ' '.$diffHtml;
|
|
||||||
}
|
|
||||||
print '</label>';
|
|
||||||
print '</div>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print '<button type="submit" class="button buttongen small" style="margin-top: 5px;">';
|
|
||||||
print '<i class="fas fa-plus"></i> '.$langs->trans('AddSelectedPrices');
|
|
||||||
print '</button>';
|
|
||||||
print '</form>';
|
|
||||||
print '</div>';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2186,7 +2155,7 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
print '<input type="hidden" name="action" value="assignproduct">';
|
print '<input type="hidden" name="action" value="assignproduct">';
|
||||||
print '<input type="hidden" name="line_id" value="'.$line->id.'">';
|
print '<input type="hidden" name="line_id" value="'.$line->id.'">';
|
||||||
print '<input type="hidden" name="id" value="'.$import->id.'">';
|
print '<input type="hidden" name="id" value="'.$import->id.'">';
|
||||||
print $form->select_produits('', 'product_id', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'minwidth150 maxwidth200', 1, '', 0);
|
print $form->select_produits('', 'product_id_'.$line->id, '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'minwidth150 maxwidth200', 1, '', 0);
|
||||||
print ' <button type="submit" class="button buttongen" title="'.$langs->trans('AssignProduct').'">';
|
print ' <button type="submit" class="button buttongen" title="'.$langs->trans('AssignProduct').'">';
|
||||||
print '<i class="fas fa-link"></i>';
|
print '<i class="fas fa-link"></i>';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
|
@ -2212,6 +2181,11 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
print '<i class="fas fa-plus-circle"></i> '.$langs->trans('CreateProduct');
|
print '<i class="fas fa-plus-circle"></i> '.$langs->trans('CreateProduct');
|
||||||
print '</a>';
|
print '</a>';
|
||||||
|
|
||||||
|
// Refresh-Button nach Produktanlage
|
||||||
|
print ' <a href="'.$_SERVER['PHP_SELF'].'?id='.$import->id.'" class="button buttongen margintoponlyshort" title="'.$langs->trans('RefreshProductListHelp').'">';
|
||||||
|
print '<i class="fas fa-sync-alt"></i>';
|
||||||
|
print '</a>';
|
||||||
|
|
||||||
// Product template
|
// Product template
|
||||||
print '<br>';
|
print '<br>';
|
||||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" class="inline-block margintoponlyshort">';
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" class="inline-block margintoponlyshort">';
|
||||||
|
|
@ -2219,7 +2193,7 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
print '<input type="hidden" name="action" value="duplicateproduct">';
|
print '<input type="hidden" name="action" value="duplicateproduct">';
|
||||||
print '<input type="hidden" name="line_id" value="'.$line->id.'">';
|
print '<input type="hidden" name="line_id" value="'.$line->id.'">';
|
||||||
print '<input type="hidden" name="id" value="'.$import->id.'">';
|
print '<input type="hidden" name="id" value="'.$import->id.'">';
|
||||||
print $form->select_produits('', 'template_product_id', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'minwidth100 maxwidth150', 1, '', 0);
|
print $form->select_produits('', 'template_product_id_'.$line->id, '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'minwidth100 maxwidth150', 1, '', 0);
|
||||||
print ' <button type="submit" class="button buttongen" title="'.$langs->trans('ProductTemplateHelp').'">';
|
print ' <button type="submit" class="button buttongen" title="'.$langs->trans('ProductTemplateHelp').'">';
|
||||||
print '<i class="fas fa-copy"></i>';
|
print '<i class="fas fa-copy"></i>';
|
||||||
print '</button>';
|
print '</button>';
|
||||||
|
|
@ -2324,6 +2298,77 @@ if ($action == 'edit' && $import->id > 0) {
|
||||||
print '</table>';
|
print '</table>';
|
||||||
print '</div>';
|
print '</div>';
|
||||||
|
|
||||||
|
// Fehlende Lieferantenpreise - konsolidierter Bereich
|
||||||
|
if (!empty($allMissingPrices)) {
|
||||||
|
print '<div style="margin-top: 15px; padding: 12px; background-color: #fcf8e3; border: 1px solid #faebcc; border-radius: 4px;">';
|
||||||
|
print '<div class="titre" style="margin-bottom: 10px; color: #8a6d3b;">';
|
||||||
|
print '<i class="fas fa-plus-circle paddingright"></i>'.$langs->trans('MissingSupplierPrices');
|
||||||
|
$totalMissing = 0;
|
||||||
|
foreach ($allMissingPrices as $mp) {
|
||||||
|
$totalMissing += count($mp['missing']);
|
||||||
|
}
|
||||||
|
print ' <span class="badge badge-warning">'.$totalMissing.'</span>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
|
||||||
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||||
|
print '<input type="hidden" name="action" value="addmissingprices">';
|
||||||
|
print '<input type="hidden" name="id" value="'.$import->id.'">';
|
||||||
|
|
||||||
|
foreach ($allMissingPrices as $mpData) {
|
||||||
|
print '<div style="margin-bottom: 10px; padding: 8px; background-color: #fff; border: 1px solid #eee; border-radius: 3px;">';
|
||||||
|
print '<div style="font-weight: bold; margin-bottom: 5px;">';
|
||||||
|
print '<i class="fas fa-cube paddingright" style="color: #555;"></i>';
|
||||||
|
print dol_escape_htmltag($mpData['product_ref'].' - '.$mpData['product_label']);
|
||||||
|
if ($mpData['current_price'] > 0) {
|
||||||
|
print ' <span class="opacitymedium">('.price($mpData['current_price']).')</span>';
|
||||||
|
}
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
foreach ($mpData['missing'] as $missing) {
|
||||||
|
$priceDiffHtml = '';
|
||||||
|
if ($mpData['current_price'] > 0) {
|
||||||
|
$pDiff = $missing['purchase_price'] - $mpData['current_price'];
|
||||||
|
$pDiffPercent = ($pDiff / $mpData['current_price']) * 100;
|
||||||
|
if ($pDiff < 0) {
|
||||||
|
$priceDiffHtml = ' <span style="color: #5cb85c;"><i class="fas fa-arrow-down"></i> '.number_format(abs($pDiffPercent), 1).'%</span>';
|
||||||
|
} elseif ($pDiff > 0) {
|
||||||
|
$priceDiffHtml = ' <span style="color: #d9534f;"><i class="fas fa-arrow-up"></i> +'.number_format($pDiffPercent, 1).'%</span>';
|
||||||
|
} else {
|
||||||
|
$priceDiffHtml = ' <span class="opacitymedium">=</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wert: productId,socId,datanormId
|
||||||
|
$cbValue = $mpData['product_id'].','.$missing['fk_soc'].','.$missing['datanorm_id'];
|
||||||
|
print '<div style="padding: 3px 0 3px 20px;">';
|
||||||
|
print '<label style="cursor: pointer; display: block;">';
|
||||||
|
print '<input type="checkbox" name="add_prices[]" value="'.dol_escape_htmltag($cbValue).'" checked class="cb-missing-price" style="margin-right: 5px;">';
|
||||||
|
print '<strong>'.dol_escape_htmltag($missing['supplier_name']).'</strong>';
|
||||||
|
print ' <code style="font-size: 0.9em;">'.dol_escape_htmltag($missing['article_number']).'</code>';
|
||||||
|
print ' @ <strong>'.price($missing['purchase_price']).'</strong>';
|
||||||
|
print $priceDiffHtml;
|
||||||
|
print '</label>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle auswaehlen / Keine auswaehlen + Submit
|
||||||
|
print '<div style="margin-top: 8px;">';
|
||||||
|
print '<a href="#" onclick="$(\'.cb-missing-price\').prop(\'checked\', true); return false;" class="small paddingright">'.$langs->trans('SelectAll').'</a>';
|
||||||
|
print ' / ';
|
||||||
|
print '<a href="#" onclick="$(\'.cb-missing-price\').prop(\'checked\', false); return false;" class="small paddingleft">'.$langs->trans('DeselectAll').'</a>';
|
||||||
|
print ' ';
|
||||||
|
print '<button type="submit" class="button buttongen">';
|
||||||
|
print '<i class="fas fa-plus paddingright"></i>'.$langs->trans('AddSelectedPrices');
|
||||||
|
print '</button>';
|
||||||
|
print '</div>';
|
||||||
|
|
||||||
|
print '</form>';
|
||||||
|
print '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
// Datanorm Preview Section (shown when preview action was triggered)
|
// Datanorm Preview Section (shown when preview action was triggered)
|
||||||
if (!empty($datanormPreviewMatches)) {
|
if (!empty($datanormPreviewMatches)) {
|
||||||
print '<div class="div-table-responsive-no-min" style="margin-top: 20px;">';
|
print '<div class="div-table-responsive-no-min" style="margin-top: 20px;">';
|
||||||
|
|
|
||||||
|
|
@ -486,3 +486,6 @@ AddSelectedPrices = Ausgewählte hinzufügen
|
||||||
SupplierPricesAdded = %s Lieferantenpreise hinzugefügt
|
SupplierPricesAdded = %s Lieferantenpreise hinzugefügt
|
||||||
CheaperBy = %s%% günstiger
|
CheaperBy = %s%% günstiger
|
||||||
MoreExpensiveBy = %s%% teurer
|
MoreExpensiveBy = %s%% teurer
|
||||||
|
RefreshProductListHelp = Produktlisten neu laden (nach Anlage neuer Produkte)
|
||||||
|
SelectAll = Alle auswählen
|
||||||
|
DeselectAll = Keine auswählen
|
||||||
|
|
|
||||||
|
|
@ -417,3 +417,6 @@ AddSelectedPrices = Add Selected
|
||||||
SupplierPricesAdded = %s supplier prices added
|
SupplierPricesAdded = %s supplier prices added
|
||||||
CheaperBy = %s%% cheaper
|
CheaperBy = %s%% cheaper
|
||||||
MoreExpensiveBy = %s%% more expensive
|
MoreExpensiveBy = %s%% more expensive
|
||||||
|
RefreshProductListHelp = Refresh product lists (after creating new products)
|
||||||
|
SelectAll = Select all
|
||||||
|
DeselectAll = Deselect all
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue