fix: Datanorm-Preisvergleich mit Kupferzuschlag und Mindestmengen (v4.3)

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 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-03-03 08:44:59 +01:00
parent 848f24e7c5
commit 70354e824d

View file

@ -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,13 +1892,15 @@ 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;
// 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);
@ -1906,6 +1909,7 @@ if ($action == 'previewdatanorm' && $id > 0) {
}
}
}
}
foreach ($results as $altResult) {
// Skip if supplier already has a price for this product
@ -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 '<div id="'.$toggleId.'" class="missing-suppliers-content" style="margin-top: 5px;">';
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 '<input type="checkbox" name="add_prices[]" value="'.dol_escape_htmltag($cbValue).'" checked class="cb-missing-price" form="missing_prices_form" 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 ' @ <strong>'.price($totalPriceForUnit);
if ($priceUnit > 1) {
print '/'.$priceUnit;
}
print '</strong>';
if ($priceUnit > 1) {
print ' <span class="small opacitymedium">('.price($missing['purchase_price']).'/Stk.)</span>';
}
print $priceDiffHtml;
// Show current Dolibarr price for comparison
if ($currentSupplierPrice > 0) {
$currentTotalPrice = $currentSupplierPrice * $currentSupplierQty;
print ' <span class="small opacitymedium">| Aktuell: '.price($currentTotalPrice);
if ($currentSupplierQty > 1) {
print '/'.$currentSupplierQty;
}
print '</span>';
}
print '</label>';
print '</div>';
}
@ -3104,14 +3143,27 @@ if ($action == 'edit' && $import->id > 0) {
// Price
print '<td class="right nowraponall">';
if ($alt['price_unit'] > 1) {
print '<span class="small" style="color: #666;">'.price($alt['price']).'/'.$alt['price_unit'].'</span><br>';
// 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 '<span class="small" style="color: #666;">'.price($materialPriceForUnit).' + '.price($copperForUnit).' Kupfer</span><br>';
}
// Show breakdown: Datanorm base price + copper surcharge
if (!empty($alt['copper_surcharge']) && $alt['copper_surcharge'] > 0) {
print '<span class="small" style="color: #666;">'.price($alt['datanorm_base_price']).' + '.price($alt['copper_surcharge']).' Kupfer</span><br>';
// Show main price for minimum quantity
print '<strong>'.price($totalPriceForUnit).'</strong>';
if ($priceUnit > 1) {
print '<span class="small" style="color: #666;">/'.$priceUnit.'</span>';
}
// Show unit price as secondary info
if ($priceUnit > 1) {
print '<br><span class="small opacitymedium">'.price($alt['purchase_price']).'/Stk.</span>';
}
print '<strong>'.price($alt['purchase_price']).'</strong>';
print '</td>';
// Difference from invoice supplier price