fix: Verschachtelte Forms, Stückpreis-Anzeige und DATPREIS-Kommentare (v4.0)

- Verschachtelte HTML-Forms behoben: "Ausgewählte Preise hinzufügen" funktionierte
  nicht, weil die missing_prices_form um die Tabelle mit inneren Forms gewickelt war.
  Lösung: Form ausgelagert mit HTML5 form-Attribut auf Checkboxen und Submit-Button.
- Einkaufspreise zeigen jetzt Stückpreis (unitprice) statt Gesamtpreis (price)
- Preisvergleich für fehlende Lieferantenpreise nutzt jetzt Stückpreis
- DATPREIS-Parser: Kommentare korrigiert - Feld ist Rabattkennzeichen, nicht PE-Code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-03-01 20:19:05 +01:00
parent 745fc68fc9
commit 578120eca9
3 changed files with 19 additions and 14 deletions

View file

@ -975,13 +975,13 @@ class Datanorm extends CommonObject
// P;A format - multiple articles per line // P;A format - multiple articles per line
// Format: P;A;ArtNr;PreisKz;Preis;PE;Zuschlag;x;x;x;ArtNr2;... // Format: P;A;ArtNr;PreisKz;Preis;PE;Zuschlag;x;x;x;ArtNr2;...
// PE is the price unit code from DATPREIS (may differ from A-record!) // Rabattkennzeichen aus DATPREIS (wird gespeichert aber nicht fuer price_unit verwendet)
if ($recordType === 'P' && isset($parts[1]) && $parts[1] === 'A') { if ($recordType === 'P' && isset($parts[1]) && $parts[1] === 'A') {
$i = 2; $i = 2;
while ($i < count($parts) - 2) { while ($i < count($parts) - 2) {
$articleNumber = trim($parts[$i] ?? ''); $articleNumber = trim($parts[$i] ?? '');
$priceRaw = trim($parts[$i + 2] ?? '0'); $priceRaw = trim($parts[$i + 2] ?? '0');
$datpreisPeCode = (int)trim($parts[$i + 3] ?? '0'); // PE code from DATPREIS $datpreisPeCode = (int)trim($parts[$i + 3] ?? '0'); // Rabattkennzeichen (nicht PE!)
$metalSurchargeRaw = trim($parts[$i + 4] ?? '0'); $metalSurchargeRaw = trim($parts[$i + 4] ?? '0');
$price = (float)$priceRaw / 100; // Convert cents to euros $price = (float)$priceRaw / 100; // Convert cents to euros
$metalSurcharge = (float)$metalSurchargeRaw / 100; // Convert cents to euros $metalSurcharge = (float)$metalSurchargeRaw / 100; // Convert cents to euros
@ -1000,7 +1000,7 @@ class Datanorm extends CommonObject
// Simple format: P;ArtNr;PreisKz;Preis;PE;... // Simple format: P;ArtNr;PreisKz;Preis;PE;...
$articleNumber = trim($parts[1] ?? ''); $articleNumber = trim($parts[1] ?? '');
$priceRaw = trim($parts[3] ?? '0'); $priceRaw = trim($parts[3] ?? '0');
$datpreisPeCode = (int)trim($parts[4] ?? '0'); // PE code if available $datpreisPeCode = (int)trim($parts[4] ?? '0'); // Rabattkennzeichen (nicht PE!)
if (strpos($priceRaw, ',') === false && strpos($priceRaw, '.') === false) { if (strpos($priceRaw, ',') === false && strpos($priceRaw, '.') === false) {
$price = (float)$priceRaw / 100; $price = (float)$priceRaw / 100;

View file

@ -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.8'; $this->version = '4.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';

View file

@ -2356,10 +2356,12 @@ if ($action == 'edit' && $import->id > 0) {
// Line items // Line items
print '<br>'; print '<br>';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" id="missing_prices_form">'; // Form fuer fehlende Lieferantenpreise (ausserhalb der Tabelle, um verschachtelte Forms zu vermeiden)
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" id="missing_prices_form" style="display:none;">';
print '<input type="hidden" name="token" value="'.newToken().'">'; print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="addmissingprices">'; print '<input type="hidden" name="action" value="addmissingprices">';
print '<input type="hidden" name="id" value="'.$import->id.'">'; print '<input type="hidden" name="id" value="'.$import->id.'">';
print '</form>';
print '<div class="div-table-responsive">'; print '<div class="div-table-responsive">';
print '<table class="noborder centpercent">'; print '<table class="noborder centpercent">';
print '<tr class="liste_titre">'; print '<tr class="liste_titre">';
@ -2480,11 +2482,11 @@ if ($action == 'edit' && $import->id > 0) {
print ' <i class="fas fa-check-circle" style="color: green;"></i>'; print ' <i class="fas fa-check-circle" style="color: green;"></i>';
// Alle Einkaufspreise des Produktes anzeigen // Alle Einkaufspreise des Produktes anzeigen
$sqlPrices = "SELECT pfp.fk_soc, pfp.price, pfp.ref_fourn, pfp.quantity, s.nom as supplier_name"; $sqlPrices = "SELECT pfp.fk_soc, pfp.price, pfp.unitprice, pfp.ref_fourn, pfp.quantity, s.nom as supplier_name";
$sqlPrices .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price pfp"; $sqlPrices .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price pfp";
$sqlPrices .= " LEFT JOIN ".MAIN_DB_PREFIX."societe s ON s.rowid = pfp.fk_soc"; $sqlPrices .= " LEFT JOIN ".MAIN_DB_PREFIX."societe s ON s.rowid = pfp.fk_soc";
$sqlPrices .= " WHERE pfp.fk_product = ".(int)$line->fk_product; $sqlPrices .= " WHERE pfp.fk_product = ".(int)$line->fk_product;
$sqlPrices .= " ORDER BY pfp.price ASC"; $sqlPrices .= " ORDER BY pfp.unitprice ASC";
$resPrices = $db->query($sqlPrices); $resPrices = $db->query($sqlPrices);
if ($resPrices && $db->num_rows($resPrices) > 0) { if ($resPrices && $db->num_rows($resPrices) > 0) {
print '<div style="margin-top: 4px; font-size: 0.85em; line-height: 1.4;">'; print '<div style="margin-top: 4px; font-size: 0.85em; line-height: 1.4;">';
@ -2493,7 +2495,10 @@ if ($action == 'edit' && $import->id > 0) {
$style = $isInvoiceSupplier ? 'font-weight: bold;' : 'color: #666;'; $style = $isInvoiceSupplier ? 'font-weight: bold;' : 'color: #666;';
print '<div style="'.$style.'">'; print '<div style="'.$style.'">';
print dol_escape_htmltag($objP->supplier_name); print dol_escape_htmltag($objP->supplier_name);
print ': <strong>'.price($objP->price).'</strong>'; print ': <strong>'.price($objP->unitprice).'</strong>';
if ($objP->quantity > 1) {
print ' <span class="opacitymedium">('.price($objP->price).'/'.(int)$objP->quantity.'Stk.)</span>';
}
if (!empty($objP->ref_fourn)) { if (!empty($objP->ref_fourn)) {
print ' <span class="opacitymedium">('.dol_escape_htmltag($objP->ref_fourn).')</span>'; print ' <span class="opacitymedium">('.dol_escape_htmltag($objP->ref_fourn).')</span>';
} }
@ -2519,7 +2524,7 @@ if ($action == 'edit' && $import->id > 0) {
// Fehlende Lieferantenpreise aus anderen Katalogen sammeln (Anzeige weiter unten) // 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')) {
// Alle vorhandenen Lieferantenpreise fuer dieses Produkt laden // Alle vorhandenen Lieferantenpreise fuer dieses Produkt laden
$sqlExistingPrices = "SELECT fk_soc, price, barcode FROM ".MAIN_DB_PREFIX."product_fournisseur_price"; $sqlExistingPrices = "SELECT fk_soc, price, unitprice, 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();
@ -2528,9 +2533,9 @@ if ($action == 'edit' && $import->id > 0) {
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 // Stueckpreis und EAN vom Rechnungslieferanten merken
if ($objPrice->fk_soc == $import->fk_soc) { if ($objPrice->fk_soc == $import->fk_soc) {
$currentSupplierPrice = $objPrice->price; $currentSupplierPrice = $objPrice->unitprice;
if (!empty($objPrice->barcode)) { if (!empty($objPrice->barcode)) {
$supplierEan = $objPrice->barcode; $supplierEan = $objPrice->barcode;
} }
@ -2607,7 +2612,7 @@ if ($action == 'edit' && $import->id > 0) {
$cbValue = $line->fk_product.','.$missing['fk_soc'].','.$missing['datanorm_id']; $cbValue = $line->fk_product.','.$missing['fk_soc'].','.$missing['datanorm_id'];
print '<div style="padding: 3px 0 3px 10px;">'; print '<div style="padding: 3px 0 3px 10px;">';
print '<label style="cursor: pointer; display: block;">'; 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 '<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 '<strong>'.dol_escape_htmltag($missing['supplier_name']).'</strong>';
print ' <code style="font-size: 0.9em;">'.dol_escape_htmltag($missing['article_number']).'</code>'; print ' <code style="font-size: 0.9em;">'.dol_escape_htmltag($missing['article_number']).'</code>';
print ' @ <strong>'.price($missing['purchase_price']).'</strong>'; print ' @ <strong>'.price($missing['purchase_price']).'</strong>';
@ -2799,7 +2804,7 @@ if ($action == 'edit' && $import->id > 0) {
// Submit Button // Submit Button
print '<span style="border-left: 1px solid #ccc; padding-left: 10px;">'; print '<span style="border-left: 1px solid #ccc; padding-left: 10px;">';
print '<button type="submit" class="button buttongen">'; print '<button type="submit" form="missing_prices_form" class="button buttongen">';
print '<i class="fas fa-plus paddingright"></i>'.$langs->trans('AddSelectedPrices'); print '<i class="fas fa-plus paddingright"></i>'.$langs->trans('AddSelectedPrices');
print '</button>'; print '</button>';
print '</span>'; print '</span>';
@ -2808,7 +2813,7 @@ if ($action == 'edit' && $import->id > 0) {
print '</div>'; print '</div>';
} }
print '</form>'; // End missing_prices_form // missing_prices_form ist bereits oben geschlossen (ausgelagert wegen verschachtelter Forms)
// Datanorm Preview Section (shown when preview action was triggered) // Datanorm Preview Section (shown when preview action was triggered)
if (!empty($datanormPreviewMatches)) { if (!empty($datanormPreviewMatches)) {