From 70354e824de4ef521d5b65de9078409905a5a999 Mon Sep 17 00:00:00 2001 From: data Date: Tue, 3 Mar 2026 08:44:59 +0100 Subject: [PATCH] fix: Datanorm-Preisvergleich mit Kupferzuschlag und Mindestmengen (v4.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: 1. Datanorm-Preise enthielten nur Materialpreis, kein Kupferzuschlag 2. Preise wurden als Stückpreis statt Preis für Mindestmenge angezeigt Lösung: 1. Kupferzuschlag aus Dolibarr-Lieferantenpreis-Extrafields wird zu Datanorm-Preis addiert 2. Hauptpreis zeigt jetzt Preis für Mindestmenge (z.B. 147,59€/100) 3. Stückpreis wird als sekundäre Info angezeigt (1,48€/Stk.) 4. Vergleich zwischen Datanorm und aktuellem Dolibarr-Preis für gleiche Menge Beispiel: - Datanorm: 102,29€ (Material) + 45,30€ (Kupfer) = 147,59€/100 - Dolibarr aktuell: 52,00€/100 - Differenz: +95,59€ (+183%) Co-Authored-By: Claude Sonnet 4.5 --- import.php | 140 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 44 deletions(-) diff --git a/import.php b/import.php index d7e6143..bf7085a 100755 --- a/import.php +++ b/import.php @@ -1881,9 +1881,10 @@ if ($action == 'previewdatanorm' && $id > 0) { // Load existing product's copper surcharge for price comparison $productCopperSurcharge = 0; + $currentSupplierPriceId = 0; if ($existingProductId > 0) { - $sqlExisting = "SELECT fk_soc, unitprice FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; - $sqlExisting .= " WHERE fk_product = ".(int)$existingProductId; + $sqlExisting = "SELECT pf.rowid, pf.fk_soc, pf.unitprice FROM ".MAIN_DB_PREFIX."product_fournisseur_price pf"; + $sqlExisting .= " WHERE pf.fk_product = ".(int)$existingProductId; $resExisting = $db->query($sqlExisting); if ($resExisting) { while ($objEx = $db->fetch_object($resExisting)) { @@ -1891,18 +1892,21 @@ if ($action == 'previewdatanorm' && $id > 0) { // Load current invoice supplier's Dolibarr price for comparison if ($objEx->fk_soc == $import->fk_soc) { $currentDolibarrPrice = (float)$objEx->unitprice; + $currentSupplierPriceId = $objEx->rowid; } } } - // Load copper surcharge from product extrafields - $sqlCopper = "SELECT kupferzuschlag FROM ".MAIN_DB_PREFIX."product_extrafields"; - $sqlCopper .= " WHERE fk_object = ".(int)$existingProductId; - $resCopper = $db->query($sqlCopper); - if ($resCopper && $db->num_rows($resCopper) > 0) { - $objCopper = $db->fetch_object($resCopper); - if (!empty($objCopper->kupferzuschlag) && $objCopper->kupferzuschlag > 0) { - $productCopperSurcharge = (float)$objCopper->kupferzuschlag; + // Load copper surcharge from supplier price extrafields (not product extrafields!) + if ($currentSupplierPriceId > 0) { + $sqlCopper = "SELECT kupferzuschlag FROM ".MAIN_DB_PREFIX."product_fournisseur_price_extrafields"; + $sqlCopper .= " WHERE fk_object = ".(int)$currentSupplierPriceId; + $resCopper = $db->query($sqlCopper); + if ($resCopper && $db->num_rows($resCopper) > 0) { + $objCopper = $db->fetch_object($resCopper); + if (!empty($objCopper->kupferzuschlag) && $objCopper->kupferzuschlag > 0) { + $productCopperSurcharge = (float)$objCopper->kupferzuschlag; + } } } } @@ -1916,15 +1920,19 @@ if ($action == 'previewdatanorm' && $id > 0) { $altSupplier = new Societe($db); $altSupplier->fetch($altResult['fk_soc']); - // Datanorm base price (per unit) - $altPurchasePrice = $altResult['price']; - if ($altResult['price_unit'] > 1) { - $altPurchasePrice = $altResult['price'] / $altResult['price_unit']; - } + // Calculate unit price from Datanorm + $priceUnit = ($altResult['price_unit'] > 0) ? $altResult['price_unit'] : 1; + $materialPrice = $altResult['price']; - // Add copper surcharge from existing product for comparison - // Datanorm has only material price, not copper surcharge - $altPurchasePriceWithCopper = $altPurchasePrice + $productCopperSurcharge; + // Datanorm has NO metal surcharge - use copper surcharge from Dolibarr supplier price instead + // $productCopperSurcharge is already loaded from extrafields above + $metalSurcharge = $productCopperSurcharge; + + // Total price for price unit (e.g. for 100 pieces) + $totalPriceForUnit = $materialPrice + $metalSurcharge; + + // Unit price (price per 1 piece/meter) + $altPurchasePrice = $totalPriceForUnit / $priceUnit; $supplierAlternatives[] = array( 'datanorm_id' => $altResult['id'], @@ -1934,9 +1942,10 @@ if ($action == 'previewdatanorm' && $id > 0) { 'short_text1' => $altResult['short_text1'], 'price' => $altResult['price'], 'price_unit' => $altResult['price_unit'], - 'purchase_price' => $altPurchasePriceWithCopper, - 'datanorm_base_price' => $altPurchasePrice, // Without copper - 'copper_surcharge' => $productCopperSurcharge, + 'effective_price_unit' => $priceUnit, // Actual divisor used + 'purchase_price' => $altPurchasePrice, // Calculated unit price (Datanorm + Copper) + 'datanorm_base_price' => $materialPrice / $priceUnit, // Material only per unit + 'metal_surcharge' => $metalSurcharge / $priceUnit, // Copper surcharge per unit (from Dolibarr) 'ean' => $altResult['ean'], 'manufacturer_ref' => $altResult['manufacturer_ref'], 'is_invoice_supplier' => ($altResult['fk_soc'] == $import->fk_soc), @@ -2590,18 +2599,20 @@ if ($action == 'edit' && $import->id > 0) { // Fehlende Lieferantenpreise aus anderen Katalogen sammeln (Anzeige weiter unten) if ($import->fk_soc > 0 && getDolGlobalString('IMPORTZUGFERD_DATANORM_SEARCH_ALL')) { // Alle vorhandenen Lieferantenpreise fuer dieses Produkt laden - $sqlExistingPrices = "SELECT fk_soc, price, unitprice, barcode FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; + $sqlExistingPrices = "SELECT fk_soc, price, unitprice, quantity, barcode FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; $sqlExistingPrices .= " WHERE fk_product = ".(int)$line->fk_product; $resExistingPrices = $db->query($sqlExistingPrices); $existingSupplierIds = array(); $currentSupplierPrice = 0; + $currentSupplierQty = 1; $supplierEan = ''; if ($resExistingPrices) { while ($objPrice = $db->fetch_object($resExistingPrices)) { $existingSupplierIds[$objPrice->fk_soc] = true; - // Stueckpreis und EAN vom Rechnungslieferanten merken + // Stueckpreis, Mindestmenge und EAN vom Rechnungslieferanten merken if ($objPrice->fk_soc == $import->fk_soc) { $currentSupplierPrice = $objPrice->unitprice; + $currentSupplierQty = max(1, $objPrice->quantity); if (!empty($objPrice->barcode)) { $supplierEan = $objPrice->barcode; } @@ -2613,10 +2624,12 @@ if ($action == 'edit' && $import->id > 0) { $datanormSearch = new Datanorm($db); $allCatalogResults = array(); - // Load copper surcharge from product extrafields for price comparison + // Load copper surcharge from current supplier price extrafields for price comparison $productCopperSurcharge = 0; - $sqlCopper = "SELECT kupferzuschlag FROM ".MAIN_DB_PREFIX."product_extrafields"; - $sqlCopper .= " WHERE fk_object = ".(int)$line->fk_product; + $sqlCopper = "SELECT pfe.kupferzuschlag FROM ".MAIN_DB_PREFIX."product_fournisseur_price pf"; + $sqlCopper .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price_extrafields pfe ON pfe.fk_object = pf.rowid"; + $sqlCopper .= " WHERE pf.fk_product = ".(int)$line->fk_product; + $sqlCopper .= " AND pf.fk_soc = ".(int)$import->fk_soc; $resCopper = $db->query($sqlCopper); if ($resCopper && $db->num_rows($resCopper) > 0) { $objCopper = $db->fetch_object($resCopper); @@ -2638,15 +2651,19 @@ if ($action == 'edit' && $import->id > 0) { $altSupplier = new Societe($db); $altSupplier->fetch($catalogResult['fk_soc']); - // Datanorm base price (per unit) - $altPurchasePrice = $catalogResult['price']; - if ($catalogResult['price_unit'] > 1) { - $altPurchasePrice = $catalogResult['price'] / $catalogResult['price_unit']; - } + // Calculate unit price from Datanorm (same logic as supplier_alternatives) + $priceUnit = ($catalogResult['price_unit'] > 0) ? $catalogResult['price_unit'] : 1; + $materialPrice = $catalogResult['price']; - // Add copper surcharge from existing product for comparison - // Datanorm has only material price, not copper surcharge - $altPurchasePriceWithCopper = $altPurchasePrice + $productCopperSurcharge; + // Datanorm has NO metal surcharge - use copper surcharge from Dolibarr supplier price + // $productCopperSurcharge is already loaded from extrafields above + $metalSurcharge = $productCopperSurcharge; + + // Total price for price unit + $totalPriceForUnit = $materialPrice + $metalSurcharge; + + // Unit price (price per 1 piece/meter) + $altPurchasePrice = $totalPriceForUnit / $priceUnit; $missingSuppliers[] = array( 'datanorm_id' => $catalogResult['id'], @@ -2655,9 +2672,10 @@ if ($action == 'edit' && $import->id > 0) { 'article_number' => $catalogResult['article_number'], 'price' => $catalogResult['price'], 'price_unit' => $catalogResult['price_unit'], - 'purchase_price' => $altPurchasePriceWithCopper, - 'datanorm_base_price' => $altPurchasePrice, // Without copper - 'copper_surcharge' => $productCopperSurcharge, + 'effective_price_unit' => $priceUnit, + 'purchase_price' => $altPurchasePrice, // Calculated unit price (Datanorm + Copper) + 'datanorm_base_price' => $materialPrice / $priceUnit, // Material only per unit + 'metal_surcharge' => $metalSurcharge / $priceUnit, // Copper surcharge per unit (from Dolibarr) 'ean' => $catalogResult['ean'], ); } @@ -2680,8 +2698,13 @@ if ($action == 'edit' && $import->id > 0) { print '
'; foreach ($missingSuppliers as $missing) { + // Calculate price for minimum quantity + $priceUnit = !empty($missing['effective_price_unit']) ? $missing['effective_price_unit'] : $missing['price_unit']; + $totalPriceForUnit = $missing['price'] + (!empty($missing['metal_surcharge']) ? ($missing['metal_surcharge'] * $priceUnit) : 0); + $priceDiffHtml = ''; if ($currentSupplierPrice > 0) { + // Compare unit prices (Stückpreise) $pDiff = $missing['purchase_price'] - $currentSupplierPrice; $pDiffPercent = ($pDiff / $currentSupplierPrice) * 100; if ($pDiff < 0) { @@ -2700,8 +2723,24 @@ if ($action == 'edit' && $import->id > 0) { print ''; print ''.dol_escape_htmltag($missing['supplier_name']).''; print ' '.dol_escape_htmltag($missing['article_number']).''; - print ' @ '.price($missing['purchase_price']).''; + print ' @ '.price($totalPriceForUnit); + if ($priceUnit > 1) { + print '/'.$priceUnit; + } + print ''; + if ($priceUnit > 1) { + print ' ('.price($missing['purchase_price']).'/Stk.)'; + } print $priceDiffHtml; + // Show current Dolibarr price for comparison + if ($currentSupplierPrice > 0) { + $currentTotalPrice = $currentSupplierPrice * $currentSupplierQty; + print ' | Aktuell: '.price($currentTotalPrice); + if ($currentSupplierQty > 1) { + print '/'.$currentSupplierQty; + } + print ''; + } print ''; print '
'; } @@ -3104,14 +3143,27 @@ if ($action == 'edit' && $import->id > 0) { // Price print ''; - if ($alt['price_unit'] > 1) { - print ''.price($alt['price']).'/'.$alt['price_unit'].'
'; + // Main price: Total price for minimum quantity (price_unit) + $priceUnit = !empty($alt['effective_price_unit']) ? $alt['effective_price_unit'] : $alt['price_unit']; + $totalPriceForUnit = $alt['price'] + (!empty($alt['metal_surcharge']) ? ($alt['metal_surcharge'] * $priceUnit) : 0); + + // Show breakdown if copper surcharge exists + if (!empty($alt['metal_surcharge']) && $alt['metal_surcharge'] > 0) { + $materialPriceForUnit = $alt['price']; + $copperForUnit = $alt['metal_surcharge'] * $priceUnit; + print ''.price($materialPriceForUnit).' + '.price($copperForUnit).' Kupfer
'; } - // Show breakdown: Datanorm base price + copper surcharge - if (!empty($alt['copper_surcharge']) && $alt['copper_surcharge'] > 0) { - print ''.price($alt['datanorm_base_price']).' + '.price($alt['copper_surcharge']).' Kupfer
'; + + // Show main price for minimum quantity + print ''.price($totalPriceForUnit).''; + if ($priceUnit > 1) { + print '/'.$priceUnit.''; + } + + // Show unit price as secondary info + if ($priceUnit > 1) { + print '
'.price($alt['purchase_price']).'/Stk.'; } - print ''.price($alt['purchase_price']).''; print ''; // Difference from invoice supplier price