<?php
/* Copyright (C) 2026 ZUGFeRD Import Module
 *
 * 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.
 */

/**
 * \file    datanorm_update.php
 * \ingroup importzugferd
 * \brief   Mass update products from Datanorm catalogs
 */

// Load Dolibarr environment
$res = 0;
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
    $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
}
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
$tmp2 = realpath(__FILE__);
$i = strlen($tmp) - 1;
$j = strlen($tmp2) - 1;
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
    $i--;
    $j--;
}
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
    $res = @include 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";
}
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.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
dol_include_once('/importzugferd/class/datanorm.class.php');
dol_include_once('/importzugferd/lib/importzugferd.lib.php');

// Load translations
$langs->loadLangs(array("importzugferd@importzugferd", "products", "bills"));

// Security check
if (!$user->hasRight('produit', 'creer')) {
    accessforbidden();
}

// Get parameters
$action = GETPOST('action', 'aZ09');
$fk_soc = GETPOSTINT('fk_soc');
$search_mode = GETPOST('search_mode', 'alpha') ?: 'supplier'; // supplier, manual
$search_term = GETPOST('search_term', 'alphanohtml');
$search_by_name = GETPOSTINT('search_by_name');
$search_by_ean = GETPOSTINT('search_by_ean');
$search_by_ref = GETPOSTINT('search_by_ref');

// Filters for what to update
// On first load (no action), default to price and description enabled
// On form submit, respect actual checkbox states
$isFormSubmitted = ($action == 'search' || GETPOSTISSET('fk_soc'));
if ($isFormSubmitted) {
    $filter_price = GETPOSTINT('filter_price');
    $filter_description = GETPOSTINT('filter_description');
    $filter_label = GETPOSTINT('filter_label');
    $only_differences = GETPOSTINT('only_differences');
    $hide_cables = GETPOSTINT('hide_cables');
    $filter_price_up = GETPOSTINT('filter_price_up');
    $filter_price_down = GETPOSTINT('filter_price_down');
} else {
    // Defaults for first page load
    $filter_price = 1;
    $filter_description = 1;
    $filter_label = 0;
    $only_differences = 0;
    $hide_cables = 0;
    $filter_price_up = 0;
    $filter_price_down = 0;
}

// Initialize objects
$form = new Form($db);
$formcompany = new FormCompany($db);
$datanorm = new Datanorm($db);

// Store pending changes in session
if (!isset($_SESSION['datanorm_pending_changes'])) {
    $_SESSION['datanorm_pending_changes'] = array();
}

/*
 * Actions
 */

// Apply single row update
if ($action == 'apply_single' && GETPOSTINT('product_id') && GETPOST('datanorm_key', 'alphanohtml')) {
    $product_id = GETPOSTINT('product_id');
    $datanorm_key = GETPOST('datanorm_key', 'alphanohtml');
    $apply_price = GETPOSTINT('apply_price');
    $apply_description = GETPOSTINT('apply_description');
    $apply_label = GETPOSTINT('apply_label');

    $result = applyDatanormUpdate($db, $user, $product_id, $datanorm_key, $fk_soc, $apply_price, $apply_description, $apply_label);

    if ($result > 0) {
        setEventMessages($langs->trans('ProductUpdated'), null, 'mesgs');
    } else {
        setEventMessages($langs->trans('ErrorUpdatingProduct'), null, 'errors');
    }

    // Redirect to same page with same parameters (preserve all filters)
    header('Location: '.$_SERVER['PHP_SELF'].'?fk_soc='.$fk_soc.'&search_mode='.$search_mode.'&search_term='.urlencode($search_term).'&filter_price='.$filter_price.'&filter_description='.$filter_description.'&filter_label='.$filter_label.'&only_differences='.$only_differences.'&hide_cables='.$hide_cables.'&filter_price_up='.$filter_price_up.'&filter_price_down='.$filter_price_down.'&action=search');
    exit;
}

// Add to pending changes
if ($action == 'add_pending') {
    $product_id = GETPOSTINT('product_id');
    $datanorm_key = GETPOST('datanorm_key', 'alphanohtml');
    $apply_fields = GETPOST('apply_fields', 'array');

    if ($product_id > 0 && !empty($datanorm_key)) {
        $_SESSION['datanorm_pending_changes'][$product_id] = array(
            'datanorm_key' => $datanorm_key,
            'fk_soc' => $fk_soc,
            'apply_fields' => $apply_fields
        );
        setEventMessages($langs->trans('AddedToPendingChanges'), null, 'mesgs');
    }

    // Redirect back with same parameters to preserve supplier selection and filters
    header('Location: '.$_SERVER['PHP_SELF'].'?fk_soc='.$fk_soc.'&search_mode='.$search_mode.'&search_term='.urlencode($search_term).'&filter_price='.$filter_price.'&filter_description='.$filter_description.'&filter_label='.$filter_label.'&only_differences='.$only_differences.'&hide_cables='.$hide_cables.'&filter_price_up='.$filter_price_up.'&filter_price_down='.$filter_price_down.'&action=search');
    exit;
}

// Remove from pending
if ($action == 'remove_pending') {
    $product_id = GETPOSTINT('product_id');
    unset($_SESSION['datanorm_pending_changes'][$product_id]);

    // Redirect back with same parameters
    header('Location: '.$_SERVER['PHP_SELF'].'?fk_soc='.$fk_soc.'&search_mode='.$search_mode.'&search_term='.urlencode($search_term).'&filter_price='.$filter_price.'&filter_description='.$filter_description.'&filter_label='.$filter_label.'&only_differences='.$only_differences.'&action=search');
    exit;
}

// Clear all pending
if ($action == 'clear_pending') {
    $_SESSION['datanorm_pending_changes'] = array();
    setEventMessages($langs->trans('PendingChangesCleared'), null, 'mesgs');

    // Redirect back
    header('Location: '.$_SERVER['PHP_SELF'].'?fk_soc='.$fk_soc);
    exit;
}

// Add all items with differences to pending
if ($action == 'add_all_pending') {
    $items_json = GETPOST('items_data', 'restricthtml');
    if (!empty($items_json)) {
        $items = json_decode($items_json, true);
        $added_count = 0;

        // Build apply_fields based on user's filter selection
        $apply_fields = array();
        if ($filter_price) $apply_fields[] = 'price';
        if ($filter_description) $apply_fields[] = 'description';
        if ($filter_label) $apply_fields[] = 'label';

        if (is_array($items) && !empty($apply_fields)) {
            foreach ($items as $item) {
                if (!empty($item['product_id']) && $item['product_id'] > 0 && !empty($item['datanorm_key'])) {
                    $_SESSION['datanorm_pending_changes'][$item['product_id']] = array(
                        'datanorm_key' => $item['datanorm_key'],
                        'fk_soc' => $fk_soc,
                        'apply_fields' => $apply_fields
                    );
                    $added_count++;
                }
            }
        }
        if ($added_count > 0) {
            setEventMessages($langs->trans('AddedAllToPendingChanges', $added_count), null, 'mesgs');
        }
    }

    // Redirect back with same parameters to preserve supplier selection and filters
    header('Location: '.$_SERVER['PHP_SELF'].'?fk_soc='.$fk_soc.'&search_mode='.$search_mode.'&search_term='.urlencode($search_term).'&filter_price='.$filter_price.'&filter_description='.$filter_description.'&filter_label='.$filter_label.'&only_differences='.$only_differences.'&hide_cables='.$hide_cables.'&filter_price_up='.$filter_price_up.'&filter_price_down='.$filter_price_down.'&action=search');
    exit;
}

// Show confirmation dialog
if ($action == 'confirm_apply_all') {
    // Will be handled in view section
}

// AJAX: Get raw Datanorm lines for debugging
if ($action == 'get_raw_lines' && GETPOST('article_number', 'alphanohtml')) {
    header('Content-Type: application/json');
    $article_number = GETPOST('article_number', 'alphanohtml');
    $ajax_fk_soc = GETPOSTINT('fk_soc');

    $result = array(
        'datanorm_line' => '',
        'datpreis_line' => '',
        'article_number' => $article_number
    );

    // Get the upload directory for this supplier
    $upload_dir = $conf->importzugferd->dir_output.'/datanorm/'.$ajax_fk_soc;

    if (is_dir($upload_dir)) {
        $allFiles = glob($upload_dir . '/*');

        // Search in DATANORM files
        foreach ($allFiles as $file) {
            $basename = strtoupper(basename($file));
            if (preg_match('/^DATANORM\.\d{3}$/', $basename)) {
                $handle = fopen($file, 'r');
                if ($handle) {
                    while (($line = fgets($handle)) !== false) {
                        // A-Satz starts with A; and contains the article number
                        if (preg_match('/^A;/', $line)) {
                            $parts = explode(';', $line);
                            if (isset($parts[2]) && trim($parts[2]) == $article_number) {
                                $result['datanorm_line'] = trim($line);
                                break;
                            }
                        }
                    }
                    fclose($handle);
                }
                if (!empty($result['datanorm_line'])) break;
            }
        }

        // Search in DATPREIS files
        foreach ($allFiles as $file) {
            $basename = strtoupper(basename($file));
            if (preg_match('/^DATPREIS\.\d{3}$/', $basename)) {
                $handle = fopen($file, 'r');
                if ($handle) {
                    while (($line = fgets($handle)) !== false) {
                        // P-Satz contains article numbers at various positions
                        if (preg_match('/^P;/', $line) && strpos($line, $article_number) !== false) {
                            $result['datpreis_line'] = trim($line);
                            break;
                        }
                    }
                    fclose($handle);
                }
                if (!empty($result['datpreis_line'])) break;
            }
        }

        $result['upload_dir'] = $upload_dir;
    } else {
        $result['error'] = 'Upload directory not found: ' . $upload_dir;
    }

    echo json_encode($result);
    exit;
}

// Apply all pending changes
if ($action == 'apply_all_confirmed' && GETPOST('confirm', 'alpha') == 'yes') {
    $success = 0;
    $errors = 0;

    // Generate batch ID for this mass update
    $batch_id = 'batch_'.date('Ymd_His').'_'.$user->id;

    foreach ($_SESSION['datanorm_pending_changes'] as $product_id => $change) {
        $apply_price = in_array('price', $change['apply_fields']) ? 1 : 0;
        $apply_description = in_array('description', $change['apply_fields']) ? 1 : 0;
        $apply_label = in_array('label', $change['apply_fields']) ? 1 : 0;

        $result = applyDatanormUpdate($db, $user, $product_id, $change['datanorm_key'], $change['fk_soc'], $apply_price, $apply_description, $apply_label, $batch_id);

        if ($result > 0) {
            $success++;
        } else {
            $errors++;
        }
    }

    $_SESSION['datanorm_pending_changes'] = array();
    setEventMessages($langs->trans('DatanormMassUpdateComplete', $success, $errors), null, 'mesgs');

    // Redirect to change log with batch filter
    header('Location: '.dol_buildpath('/importzugferd/datanorm_changelog.php', 1).'?batch_id='.urlencode($batch_id));
    exit;
}

/*
 * View
 */

$title = $langs->trans('DatanormMassUpdate');
llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-importzugferd page-datanorm-update');

print load_fiche_titre($title, '', 'fa-sync');

// Check if Datanorm data exists
$sql = "SELECT COUNT(*) as cnt FROM ".MAIN_DB_PREFIX."importzugferd_datanorm";
$resql = $db->query($sql);
$obj = $db->fetch_object($resql);
if ($obj->cnt == 0) {
    print '<div class="warning">'.$langs->trans('NoDatanormData').'</div>';
    print '<br><a href="'.dol_buildpath('/importzugferd/datanorm.php', 1).'" class="button">'.$langs->trans('UploadDatanorm').'</a>';
    llxFooter();
    $db->close();
    exit;
}

// Search form
print '<form method="GET" action="'.$_SERVER['PHP_SELF'].'" name="searchform">';
print '<input type="hidden" name="action" value="search">';

print '<div class="fichecenter">';
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';

// Supplier selection
print '<tr class="liste_titre">';
print '<td colspan="4">'.$langs->trans('SelectSupplier').'</td>';
print '</tr>';

print '<tr class="oddeven">';
print '<td class="titlefield">'.$langs->trans('Supplier').'</td>';
print '<td colspan="3">';
// Get suppliers with Datanorm data
$sql = "SELECT DISTINCT s.rowid, s.nom FROM ".MAIN_DB_PREFIX."societe s";
$sql .= " INNER JOIN ".MAIN_DB_PREFIX."importzugferd_datanorm d ON d.fk_soc = s.rowid";
$sql .= " WHERE s.fournisseur = 1";
$sql .= " ORDER BY s.nom";
$resql = $db->query($sql);
print '<select name="fk_soc" class="flat minwidth300" onchange="this.form.submit()">';
print '<option value="">'.$langs->trans('SelectASupplier').'</option>';
while ($obj = $db->fetch_object($resql)) {
    $selected = ($obj->rowid == $fk_soc) ? 'selected' : '';
    print '<option value="'.$obj->rowid.'" '.$selected.'>'.dol_escape_htmltag($obj->nom).'</option>';
}
print '</select>';
print '</td>';
print '</tr>';

// Search mode
print '<tr class="oddeven">';
print '<td>'.$langs->trans('SearchMode').'</td>';
print '<td colspan="3">';
print '<input type="radio" name="search_mode" value="supplier" id="mode_supplier" '.($search_mode == 'supplier' ? 'checked' : '').'>';
print '<label for="mode_supplier"> '.$langs->trans('SearchBySupplierProducts').'</label>';
print ' &nbsp; &nbsp; ';
print '<input type="radio" name="search_mode" value="manual" id="mode_manual" '.($search_mode == 'manual' ? 'checked' : '').'>';
print '<label for="mode_manual"> '.$langs->trans('ManualSearch').'</label>';
print '</td>';
print '</tr>';

// Manual search term
print '<tr class="oddeven" id="manual_search_row" style="'.($search_mode != 'manual' ? 'display:none;' : '').'">';
print '<td>'.$langs->trans('SearchTerm').'</td>';
print '<td colspan="3">';
print '<input type="text" name="search_term" value="'.dol_escape_htmltag($search_term).'" class="minwidth300" placeholder="'.$langs->trans('ArticleNumberOrName').'">';
print '</td>';
print '</tr>';

// Additional search options
print '<tr class="oddeven">';
print '<td>'.$langs->trans('AdditionalSearchOptions').'</td>';
print '<td colspan="3">';
print '<input type="checkbox" name="search_by_name" value="1" id="search_by_name" '.($search_by_name ? 'checked' : '').'>';
print '<label for="search_by_name"> '.$langs->trans('AlsoSearchByName').'</label>';
print ' &nbsp; ';
print '<input type="checkbox" name="search_by_ean" value="1" id="search_by_ean" '.($search_by_ean ? 'checked' : '').'>';
print '<label for="search_by_ean"> '.$langs->trans('AlsoSearchByEAN').'</label>';
print ' &nbsp; ';
print '<input type="checkbox" name="search_by_ref" value="1" id="search_by_ref" '.($search_by_ref ? 'checked' : '').'>';
print '<label for="search_by_ref"> '.$langs->trans('AlsoSearchByRef').'</label>';
print '</td>';
print '</tr>';

// Filter: What to compare/update
print '<tr class="liste_titre">';
print '<td colspan="4">'.$langs->trans('FieldsToCompare').'</td>';
print '</tr>';

print '<tr class="oddeven">';
print '<td>'.$langs->trans('Fields').'</td>';
print '<td colspan="3">';
print '<input type="checkbox" name="filter_price" value="1" id="filter_price" '.($filter_price ? 'checked' : '').'>';
print '<label for="filter_price"> '.$langs->trans('Price').'</label>';
print ' &nbsp; ';
print '<input type="checkbox" name="filter_description" value="1" id="filter_description" '.($filter_description ? 'checked' : '').'>';
print '<label for="filter_description"> '.$langs->trans('Description').'</label>';
print ' &nbsp; ';
print '<input type="checkbox" name="filter_label" value="1" id="filter_label" '.($filter_label ? 'checked' : '').'>';
print '<label for="filter_label"> '.$langs->trans('Label').'</label>';
print '</td>';
print '</tr>';

// Only show differences
print '<tr class="oddeven">';
print '<td>'.$langs->trans('Display').'</td>';
print '<td colspan="3">';
print '<input type="checkbox" name="only_differences" value="1" id="only_differences" '.($only_differences ? 'checked' : '').'>';
print '<label for="only_differences"> '.$langs->trans('OnlyShowDifferences').'</label>';
print ' &nbsp;&nbsp;&nbsp; ';
print '<input type="checkbox" name="hide_cables" value="1" id="hide_cables" '.($hide_cables ? 'checked' : '').'>';
print '<label for="hide_cables"> <i class="fas fa-eye-slash"></i> Keine Kabel anzeigen</label>';
print '</td>';
print '</tr>';

// Price direction filter
print '<tr class="oddeven">';
print '<td>Preisfilter</td>';
print '<td colspan="3">';
print '<input type="checkbox" name="filter_price_up" value="1" id="filter_price_up" '.($filter_price_up ? 'checked' : '').'>';
print '<label for="filter_price_up"> <i class="fas fa-arrow-up" style="color: #d9534f;"></i> Nur Preise rauf</label>';
print ' &nbsp;&nbsp;&nbsp; ';
print '<input type="checkbox" name="filter_price_down" value="1" id="filter_price_down" '.($filter_price_down ? 'checked' : '').'>';
print '<label for="filter_price_down"> <i class="fas fa-arrow-down" style="color: #5cb85c;"></i> Nur Preise runter</label>';
print '</td>';
print '</tr>';

print '</table>';
print '</div>';
print '</div>';

print '<div class="center" style="margin: 10px;">';
print '<input type="submit" class="button button-primary" value="'.$langs->trans('Search').'">';
if (!empty($_SESSION['datanorm_pending_changes'])) {
    print ' &nbsp; <a href="'.$_SERVER['PHP_SELF'].'?action=clear_pending&token='.newToken().'" class="button">'.$langs->trans('ClearPendingChanges').' ('.count($_SESSION['datanorm_pending_changes']).')</a>';
}
print '</div>';

print '</form>';

// Show pending changes section (always visible when there are pending changes)
if (!empty($_SESSION['datanorm_pending_changes'])) {
    $pendingCount = count($_SESSION['datanorm_pending_changes']);
    print '<br>';
    print '<div class="fichecenter">';
    print '<div class="div-table-responsive-no-min">';
    print '<table class="noborder centpercent">';
    print '<tr class="liste_titre">';
    print '<th colspan="2">'.$langs->trans('PendingChanges').' ('.$pendingCount.')</th>';
    print '</tr>';
    print '<tr class="oddeven">';
    print '<td class="center">';
    print '<a href="'.$_SERVER['PHP_SELF'].'?action=confirm_apply_all&token='.newToken().'" class="button button-primary">';
    print '<i class="fas fa-check-double paddingright"></i>'.$langs->trans('ApplyAllPendingChanges').' ('.$pendingCount.')';
    print '</a>';
    print '</td>';
    print '</tr>';
    print '</table>';
    print '</div>';
    print '</div>';
}

// JavaScript for toggling manual search and initializing state
print '<script>
(function() {
    // Toggle manual search row visibility
    function updateManualSearchVisibility() {
        var manualRow = document.getElementById("manual_search_row");
        var selectedMode = document.querySelector("input[name=search_mode]:checked");
        if (manualRow && selectedMode) {
            manualRow.style.display = (selectedMode.value == "manual") ? "" : "none";
        }
    }

    // Initialize on page load
    updateManualSearchVisibility();

    // Add event listeners for radio buttons
    document.querySelectorAll("input[name=search_mode]").forEach(function(radio) {
        radio.addEventListener("change", updateManualSearchVisibility);
    });
})();
</script>';

// Results
if ($fk_soc > 0 && ($action == 'search' || GETPOST('search_mode'))) {
    $comparison_results = array();

    if ($search_mode == 'supplier') {
        // Find all products linked to this supplier
        $comparison_results = findProductsForSupplier($db, $fk_soc, $search_by_name, $search_by_ean, $search_by_ref);
    } elseif ($search_mode == 'manual' && !empty($search_term)) {
        // Manual search in Datanorm
        $comparison_results = searchDatanormProducts($db, $fk_soc, $search_term, $search_by_name, $search_by_ean, $search_by_ref);
    }

    // Count differences before filtering
    $total_results = count($comparison_results);
    $diff_count = 0;
    foreach ($comparison_results as $item) {
        if (($filter_price && !empty($item['price_differs'])) ||
            ($filter_description && !empty($item['description_differs'])) ||
            ($filter_label && !empty($item['label_differs']))) {
            $diff_count++;
        }
    }

    // Filter results if needed
    if ($only_differences) {
        $comparison_results = array_filter($comparison_results, function($item) use ($filter_price, $filter_description, $filter_label) {
            return ($filter_price && $item['price_differs']) ||
                   ($filter_description && $item['description_differs']) ||
                   ($filter_label && $item['label_differs']);
        });
    }

    // Collect items with differences for "Add all" button
    $items_with_diff = array();
    foreach ($comparison_results as $item) {
        $has_difference = ($filter_price && $item['price_differs']) ||
                          ($filter_description && $item['description_differs']) ||
                          ($filter_label && $item['label_differs']);
        if ($has_difference && $item['product_id'] > 0) {
            $items_with_diff[] = array(
                'product_id' => $item['product_id'],
                'datanorm_key' => $item['datanorm_key']
            );
        }
    }

    // Show summary
    print '<br>';
    print '<div class="info" style="display: flex; justify-content: space-between; align-items: center;">';
    print '<div>';
    print '<strong>'.$langs->trans('Results').':</strong> ';
    print $total_results.' '.$langs->trans('Products');
    if ($diff_count > 0) {
        print ' | <span style="color: #d9534f;"><strong>'.$diff_count.'</strong> '.$langs->trans('WithDifferences').'</span>';
    }
    if ($only_differences) {
        print ' | <em>'.$langs->trans('OnlyShowingDifferences').'</em>';
    }
    print '</div>';

    // "Add all with differences" button
    if (!empty($items_with_diff)) {
        print '<div>';
        print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" style="display:inline;" id="addAllForm">';
        print '<input type="hidden" name="token" value="'.newToken().'">';
        print '<input type="hidden" name="action" value="add_all_pending">';
        print '<input type="hidden" name="fk_soc" value="'.$fk_soc.'">';
        print '<input type="hidden" name="filter_price" value="'.$filter_price.'">';
        print '<input type="hidden" name="filter_description" value="'.$filter_description.'">';
        print '<input type="hidden" name="filter_label" value="'.$filter_label.'">';
        print '<input type="hidden" name="search_mode" value="'.dol_escape_htmltag($search_mode).'">';
        print '<input type="hidden" name="search_term" value="'.dol_escape_htmltag($search_term).'">';
        print '<input type="hidden" name="only_differences" value="'.$only_differences.'">';
        print '<input type="hidden" name="items_data" id="items_data" value="'.dol_escape_htmltag(json_encode($items_with_diff)).'">';
        print '<button type="button" class="button" id="btnAddAllWithDiff">';
        print '<i class="fas fa-check-double"></i> '.$langs->trans('AddAllWithDifferences').' ('.$diff_count.')';
        print '</button>';
        print '</form>';
        print '</div>';

        // Hidden dialog for confirmation
        print '<div id="dialogConfirmAddAll" title="'.$langs->trans('Confirmation').'" style="display:none;">';
        print '<p><span class="ui-icon ui-icon-alert" style="float:left; margin:12px 12px 20px 0;"></span>';
        print $langs->trans('ConfirmAddAllToPending');
        print '</p>';
        print '<p class="opacitymedium"><strong>'.$diff_count.'</strong> '.$langs->trans('Products').'</p>';
        print '</div>';
    }
    print '</div>';

    if (!empty($comparison_results)) {
        print '<br>';
        print '<div class="div-table-responsive">';
        print '<table class="noborder centpercent">';

        // Header
        print '<tr class="liste_titre">';
        print '<th>'.$langs->trans('Product').'</th>';
        print '<th>'.$langs->trans('DatanormArticle').'</th>';
        if ($filter_price) {
            print '<th class="right">'.$langs->trans('CurrentPrice').'</th>';
            print '<th class="right">'.$langs->trans('DatanormPrice').'</th>';
        }
        if ($filter_description) {
            print '<th>'.$langs->trans('CurrentDescription').'</th>';
            print '<th>'.$langs->trans('DatanormDescription').'</th>';
        }
        if ($filter_label) {
            print '<th>'.$langs->trans('CurrentLabel').'</th>';
            print '<th>'.$langs->trans('DatanormLabel').'</th>';
        }
        print '<th class="center">'.$langs->trans('Actions').'</th>';
        print '</tr>';

        foreach ($comparison_results as $item) {
            // Filter cables if requested
            // Datanorm groups: 01-19, 101-119, 202, 205 are cables/wires
            if ($hide_cables && !empty($item['datanorm_product_group'])) {
                $pg = (int)$item['datanorm_product_group'];
                if (($pg >= 1 && $pg <= 19) || ($pg >= 101 && $pg <= 119) || $pg == 202 || $pg == 205) {
                    continue; // Skip cables
                }
            }

            // Calculate price difference for filtering
            $price_diff = 0;
            if ($item['product_id'] > 0 && $item['price_differs']) {
                $datanorm_pe = isset($item['datanorm_price_unit']) ? $item['datanorm_price_unit'] : 1;
                $datanorm_raw = isset($item['datanorm_price_raw']) ? $item['datanorm_price_raw'] : 0;
                $current_cu = isset($item['current_kupferzuschlag']) ? $item['current_kupferzuschlag'] : 0;
                $effective_qty = isset($item['current_effective_quantity']) ? $item['current_effective_quantity'] : 1;
                $current_total = isset($item['current_total_price']) ? $item['current_total_price'] : 0;

                // Scale Cu to Datanorm's price_unit
                $cu_per_unit = ($current_cu > 0 && $effective_qty > 0) ? $current_cu / $effective_qty : 0;
                $cu_for_pe = $cu_per_unit * $datanorm_pe;
                $datanorm_total = $datanorm_raw + $cu_for_pe;

                $current_unit = $effective_qty > 0 ? $current_total / $effective_qty : $current_total;
                $datanorm_unit = $datanorm_total / $datanorm_pe;
                $price_diff = $datanorm_unit - $current_unit;
            }

            // Filter by price direction
            if ($filter_price_up && !$filter_price_down && $price_diff <= 0) {
                continue; // Only show price increases
            }
            if ($filter_price_down && !$filter_price_up && $price_diff >= 0) {
                continue; // Only show price decreases
            }
            if ($filter_price_up && $filter_price_down && $price_diff == 0) {
                continue; // Both checked: show any change, skip unchanged
            }

            $has_difference = ($filter_price && $item['price_differs']) ||
                              ($filter_description && $item['description_differs']) ||
                              ($filter_label && $item['label_differs']);

            $rowClass = $has_difference ? 'oddeven highlighted' : 'oddeven';

            print '<tr class="'.$rowClass.'">';

            // Product
            print '<td>';
            if ($item['product_id'] > 0) {
                $product = new Product($db);
                $product->fetch($item['product_id']);
                print $product->getNomUrl(1, '', 0, 0, 0, 1, 1); // Open in new tab
                print '<br><span class="opacitymedium">'.$product->ref.'</span>';
            } else {
                print '<span class="opacitymedium">'.$langs->trans('ProductNotInDatabase').'</span>';
            }
            print '</td>';

            // Datanorm article
            print '<td>';
            print '<strong>'.dol_escape_htmltag($item['datanorm_ref']).'</strong>';
            print '<br><span class="opacitymedium">'.dol_escape_htmltag(dol_trunc($item['datanorm_name'], 50)).'</span>';
            // Show price_unit (PE = Preiseinheit)
            $pe = isset($item['datanorm_price_unit']) ? $item['datanorm_price_unit'] : 1;
            print '<br><span class="opacitymedium small">PE='.$pe.'</span>';
            print '</td>';

            // Price comparison
            if ($filter_price) {
                $priceStyle = $item['price_differs'] ? 'background-color: #fcf8e3;' : '';
                print '<td class="right nowraponall" style="'.$priceStyle.'">';
                if ($item['product_id'] > 0) {
                    $dolibarr_total = isset($item['current_total_price']) ? $item['current_total_price'] : $item['current_price'];
                    $dolibarr_qty = isset($item['current_quantity']) ? $item['current_quantity'] : 1;
                    $dolibarr_cu = isset($item['current_kupferzuschlag']) ? $item['current_kupferzuschlag'] : 0;

                    // IMPORTANT: Dolibarr price already includes Cu! Show as info only
                    if ($dolibarr_cu > 0) {
                        print '<span class="opacitymedium small">(davon '.price($dolibarr_cu).' Cu)</span><br>';
                    }

                    // Total price for minimum quantity (already includes Cu!)
                    print '<strong>'.price($dolibarr_total);
                    if ($dolibarr_qty > 1) {
                        print '/'.$dolibarr_qty;
                    }
                    print '</strong>';

                    // Unit price as secondary info
                    if ($dolibarr_qty > 1) {
                        $dolibarr_unit = $dolibarr_total / $dolibarr_qty;
                        print '<br><span class="opacitymedium small">('.price($dolibarr_unit).'/Stk.)</span>';
                    }
                } else {
                    print '-';
                }
                print '</td>';
                print '<td class="right nowraponall" style="'.$priceStyle.'">';
                $datanorm_pe = isset($item['datanorm_price_unit']) ? $item['datanorm_price_unit'] : 1;
                $datanorm_raw = isset($item['datanorm_price_raw']) ? $item['datanorm_price_raw'] : $item['datanorm_price'];
                $current_cu = isset($item['current_kupferzuschlag']) ? $item['current_kupferzuschlag'] : 0;
                $effective_qty = isset($item['current_effective_quantity']) ? $item['current_effective_quantity'] : 1;

                // Scale Cu from Dolibarr's quantity to Datanorm's price_unit
                // Example: Cu 254,55€ for 50m → for 100m = 509,10€
                $cu_per_unit = ($current_cu > 0 && $effective_qty > 0) ? $current_cu / $effective_qty : 0;
                $cu_for_pe = $cu_per_unit * $datanorm_pe;

                // Show breakdown if copper exists
                if ($current_cu > 0) {
                    print '<span class="opacitymedium small">'.price($datanorm_raw).' + '.price($cu_for_pe).' Cu</span><br>';
                }

                // Total price for Datanorm price_unit (with scaled Cu)
                $datanorm_total = $datanorm_raw + $cu_for_pe;
                print '<strong>'.price($datanorm_total);
                if ($datanorm_pe > 1) {
                    print '/'.$datanorm_pe;
                }
                print '</strong>';

                // Unit price as secondary info
                if ($datanorm_pe > 1) {
                    $datanorm_unit = $datanorm_total / $datanorm_pe;
                    print '<br><span class="opacitymedium small">('.price($datanorm_unit).'/Stk.)</span>';
                }
                if ($item['price_differs'] && $item['product_id'] > 0) {
                    // Calculate percentage difference using UNIT PRICE basis
                    $current_total = isset($item['current_total_price']) ? $item['current_total_price'] : $item['current_price'];

                    // Dolibarr: unit price (already includes Cu)
                    $current_compare = $effective_qty > 0 ? $current_total / $effective_qty : $current_total;

                    // Datanorm: unit price (material + scaled Cu)
                    $datanorm_compare = $datanorm_total / $datanorm_pe;

                    $diff = $datanorm_compare - $current_compare;
                    $diffPercent = ($current_compare > 0) ? ($diff / $current_compare * 100) : 0;
                    print '<br>';
                    if ($diff > 0) {
                        print '<span style="color: #d9534f;"><i class="fas fa-arrow-up"></i> +'.number_format($diffPercent, 1).'%</span>';
                    } else {
                        print '<span style="color: #5cb85c;"><i class="fas fa-arrow-down"></i> '.number_format($diffPercent, 1).'%</span>';
                    }
                }
                print '</td>';
            }

            // Description comparison
            if ($filter_description) {
                $descStyle = $item['description_differs'] ? 'background-color: #fcf8e3;' : '';
                print '<td style="'.$descStyle.'">';
                print dol_escape_htmltag(dol_trunc($item['current_description'], 80));
                print '</td>';
                print '<td style="'.$descStyle.'">';
                print dol_escape_htmltag(dol_trunc($item['datanorm_description'], 80));
                print '</td>';
            }

            // Label comparison
            if ($filter_label) {
                $labelStyle = $item['label_differs'] ? 'background-color: #fcf8e3;' : '';
                print '<td style="'.$labelStyle.'">';
                print dol_escape_htmltag($item['current_label']);
                print '</td>';
                print '<td style="'.$labelStyle.'">';
                print dol_escape_htmltag($item['datanorm_label']);
                print '</td>';
            }

            // Actions
            print '<td class="center nowraponall">';
            if ($item['product_id'] > 0 && $has_difference) {
                // Quick apply form
                print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" style="display:inline;">';
                print '<input type="hidden" name="token" value="'.newToken().'">';
                print '<input type="hidden" name="action" value="apply_single">';
                print '<input type="hidden" name="product_id" value="'.$item['product_id'].'">';
                print '<input type="hidden" name="datanorm_key" value="'.dol_escape_htmltag($item['datanorm_key']).'">';
                print '<input type="hidden" name="fk_soc" value="'.$fk_soc.'">';
                print '<input type="hidden" name="search_mode" value="'.$search_mode.'">';
                print '<input type="hidden" name="search_term" value="'.dol_escape_htmltag($search_term).'">';
                print '<input type="hidden" name="filter_price" value="'.$filter_price.'">';
                print '<input type="hidden" name="filter_description" value="'.$filter_description.'">';
                print '<input type="hidden" name="filter_label" value="'.$filter_label.'">';
                print '<input type="hidden" name="only_differences" value="'.$only_differences.'">';
                print '<input type="hidden" name="hide_cables" value="'.$hide_cables.'">';
                print '<input type="hidden" name="filter_price_up" value="'.$filter_price_up.'">';
                print '<input type="hidden" name="filter_price_down" value="'.$filter_price_down.'">';

                // Checkboxes for what to apply
                if ($filter_price && $item['price_differs']) {
                    print '<input type="checkbox" name="apply_price" value="1" checked title="'.$langs->trans('Price').'">';
                    print '<span class="opacitymedium">P</span> ';
                }
                if ($filter_description && $item['description_differs']) {
                    print '<input type="checkbox" name="apply_description" value="1" checked title="'.$langs->trans('Description').'">';
                    print '<span class="opacitymedium">D</span> ';
                }
                if ($filter_label && $item['label_differs']) {
                    print '<input type="checkbox" name="apply_label" value="1" checked title="'.$langs->trans('Label').'">';
                    print '<span class="opacitymedium">L</span> ';
                }

                print '<button type="submit" class="button smallpaddingimp" title="'.$langs->trans('ApplyChanges').'">';
                print '<i class="fas fa-check"></i>';
                print '</button>';
                print '</form>';

                // Add to pending
                $isPending = isset($_SESSION['datanorm_pending_changes'][$item['product_id']]);
                if (!$isPending) {
                    print ' <a href="'.$_SERVER['PHP_SELF'].'?action=add_pending&product_id='.$item['product_id'].'&datanorm_key='.urlencode($item['datanorm_key']).'&fk_soc='.$fk_soc.'&apply_fields[]=price&apply_fields[]=description&apply_fields[]=label&token='.newToken().'" class="button smallpaddingimp" title="'.$langs->trans('AddToPending').'">';
                    print '<i class="fas fa-plus"></i>';
                    print '</a>';
                } else {
                    print ' <span class="badge badge-status4">'.$langs->trans('Pending').'</span>';
                }
            } elseif ($item['product_id'] == 0) {
                // Create product link
                print '<a href="'.dol_buildpath('/product/card.php', 1).'?action=create&type=0" class="button smallpaddingimp" title="'.$langs->trans('CreateProduct').'" target="_blank">';
                print '<i class="fas fa-plus"></i>';
                print '</a>';
            } else {
                print '<span class="opacitymedium">'.$langs->trans('NoChanges').'</span>';
            }

            // Raw data button (always show)
            print ' <a href="#" class="button smallpaddingimp" onclick="showRawDatanorm(\''.dol_escape_js($item['datanorm_ref']).'\', '.$fk_soc.'); return false;" title="Rohdaten anzeigen">';
            print '<i class="fas fa-file-code"></i>';
            print '</a>';

            print '</td>';

            print '</tr>';
        }

        print '</table>';
        print '</div>';

        // Summary and mass apply button
        $pendingCount = count($_SESSION['datanorm_pending_changes']);
        if ($pendingCount > 0) {
            print '<br>';
            print '<div class="center">';
            print '<a href="'.$_SERVER['PHP_SELF'].'?action=confirm_apply_all&token='.newToken().'" class="button button-primary">';
            print '<i class="fas fa-check-double paddingright"></i>'.$langs->trans('ApplyAllPendingChanges').' ('.$pendingCount.')';
            print '</a>';
            print '</div>';
        }

    } else {
        print '<br><div class="opacitymedium center">'.$langs->trans('NoResultsFound').'</div>';
    }
}

// Confirmation dialog for mass apply
if ($action == 'confirm_apply_all' && !empty($_SESSION['datanorm_pending_changes'])) {
    print '<br><br>';
    print '<div class="confirmmessage">';
    print '<h3>'.$langs->trans('ConfirmMassUpdate').'</h3>';
    print '<p>'.$langs->trans('FollowingProductsWillBeUpdated').':</p>';

    print '<table class="noborder centpercent">';
    print '<tr class="liste_titre">';
    print '<th>'.$langs->trans('Product').'</th>';
    print '<th>'.$langs->trans('Changes').'</th>';
    print '</tr>';

    foreach ($_SESSION['datanorm_pending_changes'] as $product_id => $change) {
        $product = new Product($db);
        $product->fetch($product_id);

        print '<tr class="oddeven">';
        print '<td>'.$product->getNomUrl(1).' - '.$product->label.'</td>';
        print '<td>';
        $changes = array();
        if (in_array('price', $change['apply_fields'])) $changes[] = $langs->trans('Price');
        if (in_array('description', $change['apply_fields'])) $changes[] = $langs->trans('Description');
        if (in_array('label', $change['apply_fields'])) $changes[] = $langs->trans('Label');
        print implode(', ', $changes);
        print '</td>';
        print '</tr>';
    }

    print '</table>';

    print '<br>';
    print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
    print '<input type="hidden" name="token" value="'.newToken().'">';
    print '<input type="hidden" name="action" value="apply_all_confirmed">';
    print '<input type="hidden" name="confirm" value="yes">';
    print '<div class="center">';
    print '<input type="submit" class="button button-primary" value="'.$langs->trans('Confirm').'">';
    print ' &nbsp; ';
    print '<a href="'.$_SERVER['PHP_SELF'].'" class="button">'.$langs->trans('Cancel').'</a>';
    print '</div>';
    print '</form>';

    print '</div>';
}

print '<style>
.highlighted { background-color: #fff3cd !important; }
.confirmmessage { background: #f8f9fa; padding: 20px; border: 1px solid #dee2e6; border-radius: 5px; }
/* Modal styles */
.datanorm-modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
    z-index: 10000;
}
.datanorm-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: white;
    padding: 20px;
    border-radius: 8px;
    max-width: 90%;
    max-height: 80%;
    overflow: auto;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    z-index: 10001;
}
.datanorm-modal h3 {
    margin-top: 0;
    border-bottom: 1px solid #ddd;
    padding-bottom: 10px;
}
.datanorm-modal pre {
    background: #f5f5f5;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    overflow-x: auto;
    white-space: pre-wrap;
    word-wrap: break-word;
    font-size: 12px;
    font-family: monospace;
}
.datanorm-modal .close-btn {
    float: right;
    cursor: pointer;
    font-size: 20px;
    color: #999;
}
.datanorm-modal .close-btn:hover {
    color: #333;
}
</style>';

// Modal for raw data
print '<div id="datanormRawModal" class="datanorm-modal-overlay" onclick="closeRawDatanormModal(event)">';
print '<div class="datanorm-modal" onclick="event.stopPropagation()">';
print '<span class="close-btn" onclick="closeRawDatanormModal()">&times;</span>';
print '<h3><i class="fas fa-file-code"></i> Rohdaten: <span id="modalArticleNumber"></span></h3>';
print '<div id="modalContent">';
print '<p><em>Laden...</em></p>';
print '</div>';
print '</div>';
print '</div>';

print '<script>
function showRawDatanorm(articleNumber, fkSoc) {
    document.getElementById("datanormRawModal").style.display = "block";
    document.getElementById("modalArticleNumber").textContent = articleNumber;
    document.getElementById("modalContent").innerHTML = "<p><em>Laden...</em></p>";

    // AJAX request
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "'.$_SERVER['PHP_SELF'].'?action=get_raw_lines&article_number=" + encodeURIComponent(articleNumber) + "&fk_soc=" + fkSoc + "&token='.newToken().'", true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                try {
                    var data = JSON.parse(xhr.responseText);
                    var html = "";

                    html += "<h4>DATANORM (A-Satz)</h4>";
                    if (data.datanorm_line) {
                        html += "<pre>" + escapeHtml(data.datanorm_line) + "</pre>";
                        // Parse and show fields
                        var parts = data.datanorm_line.split(";");
                        html += "<table class=\"noborder\" style=\"margin-top:10px;font-size:12px;\">";
                        html += "<tr><td><b>0: Satzart</b></td><td>" + escapeHtml(parts[0] || "") + "</td></tr>";
                        html += "<tr><td><b>1: Aktionscode</b></td><td>" + escapeHtml(parts[1] || "") + "</td></tr>";
                        html += "<tr><td><b>2: Artikelnummer</b></td><td>" + escapeHtml(parts[2] || "") + "</td></tr>";
                        html += "<tr><td><b>3: Textkennzeichen</b></td><td>" + escapeHtml(parts[3] || "") + "</td></tr>";
                        html += "<tr><td><b>4: Kurztext 1</b></td><td>" + escapeHtml(parts[4] || "") + "</td></tr>";
                        html += "<tr><td><b>5: Kurztext 2</b></td><td>" + escapeHtml(parts[5] || "") + "</td></tr>";
                        html += "<tr><td><b>6: Preiskennzeichen</b></td><td>" + escapeHtml(parts[6] || "") + " (1=Brutto, 2=Netto)</td></tr>";
                        html += "<tr><td><b>7: PE-Code</b></td><td>" + escapeHtml(parts[7] || "") + " (0=1, 1=10, 2=100, 3=1000)</td></tr>";
                        html += "<tr><td><b>8: Mengeneinheit</b></td><td>" + escapeHtml(parts[8] || "") + "</td></tr>";
                        html += "<tr><td><b>9: Preis (Cent)</b></td><td>" + escapeHtml(parts[9] || "") + "</td></tr>";
                        html += "<tr><td><b>10: Rabattgruppe</b></td><td>" + escapeHtml(parts[10] || "") + "</td></tr>";
                        html += "<tr><td><b>11: Warengruppe</b></td><td>" + escapeHtml(parts[11] || "") + "</td></tr>";
                        html += "</table>";
                    } else {
                        html += "<p class=\"opacitymedium\">Keine DATANORM-Zeile gefunden</p>";
                    }

                    html += "<br><h4>DATPREIS (P-Satz)</h4>";
                    if (data.datpreis_line) {
                        html += "<pre>" + escapeHtml(data.datpreis_line) + "</pre>";
                        // Parse P-Satz
                        var pparts = data.datpreis_line.split(";");
                        html += "<table class=\"noborder\" style=\"margin-top:10px;font-size:12px;\">";
                        html += "<tr><td><b>0: Satzart</b></td><td>" + escapeHtml(pparts[0] || "") + "</td></tr>";
                        html += "<tr><td><b>1: Aktionscode</b></td><td>" + escapeHtml(pparts[1] || "") + "</td></tr>";
                        // Find the article in the P-Satz (multiple articles per line)
                        for (var i = 2; i < pparts.length; i += 9) {
                            if (pparts[i] && pparts[i].trim() === articleNumber) {
                                html += "<tr><td colspan=\"2\"><b>--- Artikel " + escapeHtml(pparts[i]) + " ---</b></td></tr>";
                                html += "<tr><td><b>" + i + ": Artikelnummer</b></td><td>" + escapeHtml(pparts[i] || "") + "</td></tr>";
                                html += "<tr><td><b>" + (i+1) + ": Preiskennzeichen</b></td><td>" + escapeHtml(pparts[i+1] || "") + " (2=Netto)</td></tr>";
                                html += "<tr><td><b>" + (i+2) + ": Preis (Cent)</b></td><td>" + escapeHtml(pparts[i+2] || "") + " = " + (parseFloat(pparts[i+2] || 0) / 100).toFixed(2) + " EUR</td></tr>";
                                html += "<tr><td><b>" + (i+3) + ": Unbekannt</b></td><td>" + escapeHtml(pparts[i+3] || "") + "</td></tr>";
                                html += "<tr><td><b>" + (i+4) + ": Metallzuschlag (Cent)</b></td><td>" + escapeHtml(pparts[i+4] || "") + " = " + (parseFloat(pparts[i+4] || 0) / 100).toFixed(2) + " EUR</td></tr>";
                                break;
                            }
                        }
                        html += "</table>";
                    } else {
                        html += "<p class=\"opacitymedium\">Keine DATPREIS-Zeile gefunden</p>";
                    }

                    if (data.error) {
                        html += "<br><p class=\"error\">" + escapeHtml(data.error) + "</p>";
                    }

                    document.getElementById("modalContent").innerHTML = html;
                } catch(e) {
                    document.getElementById("modalContent").innerHTML = "<p class=\"error\">Fehler beim Parsen: " + e.message + "</p><pre>" + escapeHtml(xhr.responseText) + "</pre>";
                }
            } else {
                document.getElementById("modalContent").innerHTML = "<p class=\"error\">HTTP Fehler: " + xhr.status + "</p>";
            }
        }
    };
    xhr.send();
}

function closeRawDatanormModal(event) {
    if (!event || event.target.id === "datanormRawModal") {
        document.getElementById("datanormRawModal").style.display = "none";
    }
}

function escapeHtml(text) {
    var div = document.createElement("div");
    div.appendChild(document.createTextNode(text || ""));
    return div.innerHTML;
}

// Close modal with ESC key
document.addEventListener("keydown", function(e) {
    if (e.key === "Escape") {
        closeRawDatanormModal();
    }
});

// Confirm dialog for adding all items using jQuery UI Dialog
$(document).ready(function() {
    $("#btnAddAllWithDiff").on("click", function() {
        $("#dialogConfirmAddAll").dialog({
            resizable: false,
            height: "auto",
            width: 450,
            modal: true,
            buttons: {
                "' . dol_escape_js($langs->trans('Validate')) . '": function() {
                    $(this).dialog("close");
                    document.getElementById("addAllForm").submit();
                },
                "' . dol_escape_js($langs->trans('Cancel')) . '": function() {
                    $(this).dialog("close");
                }
            }
        });
    });
});
</script>';

llxFooter();
$db->close();

/*
 * Helper functions
 */

/**
 * Find products linked to a supplier and compare with Datanorm
 *
 * @param object $db Database handler
 * @param int $fk_soc Supplier ID
 * @param int $search_by_name Search by name
 * @param int $search_by_ean Search by EAN
 * @param int $search_by_ref Search by reference
 * @return array Comparison results
 */
function findProductsForSupplier($db, $fk_soc, $search_by_name = 0, $search_by_ean = 0, $search_by_ref = 0)
{
    global $conf;

    $results = array();

    // Get all supplier products
    $sql = "SELECT DISTINCT pf.fk_product, pf.ref_fourn, pf.price as fourn_price, p.ref, p.label, p.description, p.barcode";
    $sql .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price pf";
    $sql .= " INNER JOIN ".MAIN_DB_PREFIX."product p ON p.rowid = pf.fk_product";
    $sql .= " WHERE pf.fk_soc = ".((int)$fk_soc);
    $sql .= " AND pf.entity IN (".getEntity('product').")";

    $resql = $db->query($sql);
    if ($resql) {
        while ($obj = $db->fetch_object($resql)) {
            // Try to find matching Datanorm article
            $datanorm = findDatanormMatch($db, $fk_soc, $obj->ref_fourn, $obj->label, $obj->barcode, $obj->ref, $search_by_name, $search_by_ean, $search_by_ref);

            if ($datanorm) {
                $results[] = buildComparisonResult($obj, $datanorm);
            }
        }
    }

    return $results;
}

/**
 * Search Datanorm products manually
 *
 * @param object $db Database handler
 * @param int $fk_soc Supplier ID
 * @param string $search_term Search term
 * @param int $search_by_name Search by name
 * @param int $search_by_ean Search by EAN
 * @param int $search_by_ref Search by reference
 * @return array Comparison results
 */
function searchDatanormProducts($db, $fk_soc, $search_term, $search_by_name = 0, $search_by_ean = 0, $search_by_ref = 0)
{
    global $conf;

    $results = array();

    // Search in Datanorm
    $sql = "SELECT d.* FROM ".MAIN_DB_PREFIX."importzugferd_datanorm d";
    $sql .= " WHERE d.fk_soc = ".((int)$fk_soc);
    $sql .= " AND (d.article_number LIKE '%".$db->escape($search_term)."%'";
    $sql .= " OR d.short_text1 LIKE '%".$db->escape($search_term)."%'";
    $sql .= " OR d.short_text2 LIKE '%".$db->escape($search_term)."%'";
    if ($search_by_ean) {
        $sql .= " OR d.ean LIKE '%".$db->escape($search_term)."%'";
    }
    $sql .= ")";
    $sql .= " ORDER BY d.article_number";
    $sql .= " LIMIT 100";

    $resql = $db->query($sql);
    if ($resql) {
        while ($datanorm = $db->fetch_object($resql)) {
            // Try to find matching product in database
            $product = findProductMatch($db, $fk_soc, $datanorm);

            // Calculate unit price (Datanorm price may be per price_unit pieces)
            $price_unit = (!empty($datanorm->price_unit) && $datanorm->price_unit > 1) ? $datanorm->price_unit : 1;
            $datanorm_metal_surcharge = !empty($datanorm->metal_surcharge) ? (float)$datanorm->metal_surcharge : 0;

            // Get current price and copper surcharge from extrafield
            $current_total_price = 0;
            $current_quantity = 1;
            $current_kaufmenge = 0;
            $current_kupferzuschlag = 0;
            $datanorm_price_unit_code = isset($datanorm->price_unit_code) ? $datanorm->price_unit_code : -1;

            if ($product) {
                $priceDetails = getSupplierPriceDetails($db, $product->rowid, $fk_soc);
                $current_total_price = $priceDetails['price'];
                $current_quantity = $priceDetails['quantity'];
                $current_kaufmenge = $priceDetails['kaufmenge'];
                $current_kupferzuschlag = $priceDetails['kupferzuschlag'];
            }

            // Use kaufmenge if set, otherwise fall back to quantity
            $effective_quantity = ($current_kaufmenge > 0) ? $current_kaufmenge : $current_quantity;

            // Priority for surcharge: 1) Invoice extrafield, 2) Datanorm
            if ($current_kupferzuschlag > 0) {
                $effective_surcharge = $current_kupferzuschlag;
                $surcharge_source = 'invoice';
            } else {
                $effective_surcharge = $datanorm_metal_surcharge;
                $surcharge_source = 'datanorm';
            }

            // Calculate prices for comparison - UNIT PRICE basis
            // IMPORTANT: Dolibarr price already INCLUDES kupferzuschlag! Don't add it again!
            // Datanorm price is WITHOUT kupferzuschlag, so add SCALED Cu for comparison
            //
            // Example: Kabel NYM-J 5x10
            // - Dolibarr: 331,27€ for 50m (includes 254,55€ Cu for 50m) → 6,63€/m
            // - Datanorm: 168,50€ for 100m (PE=100)
            // - Cu per unit: 254,55€ / 50m = 5,09€/m → for 100m = 509,10€
            // - Datanorm total: 168,50€ + 509,10€ = 677,60€ → 6,78€/m

            // Calculate Cu per unit (from Dolibarr's quantity basis)
            $cu_per_unit = ($current_kupferzuschlag > 0 && $effective_quantity > 0)
                ? $current_kupferzuschlag / $effective_quantity
                : 0;

            // Scale Cu to Datanorm's price_unit basis
            $cu_for_price_unit = $cu_per_unit * $price_unit;

            // Dolibarr: unit price (already includes Cu)
            $current_compare_price = $effective_quantity > 0 ? $current_total_price / $effective_quantity : $current_total_price;

            // Datanorm: material price + scaled Cu, then to unit price
            $datanorm_compare_price = ($datanorm->price + $cu_for_price_unit) / $price_unit;

            // For display: always show unit prices
            $datanorm_material_unit_price = $datanorm->price / $price_unit;
            $total_price_with_surcharge = $datanorm->price + $cu_for_price_unit;
            $datanorm_total_unit_price = $total_price_with_surcharge / $price_unit;
            $current_unit_price = $effective_quantity > 0 ? $current_total_price / $effective_quantity : $current_total_price;

            $results[] = array(
                'product_id' => $product ? $product->rowid : 0,
                'current_price' => $current_unit_price,
                'current_total_price' => $current_total_price,
                'current_quantity' => $current_quantity,
                'current_kaufmenge' => $current_kaufmenge,
                'current_effective_quantity' => $effective_quantity,
                'current_kupferzuschlag' => $current_kupferzuschlag,
                'current_description' => $product ? $product->description : '',
                'current_label' => $product ? $product->label : '',
                'datanorm_key' => $datanorm->article_number,
                'datanorm_ref' => $datanorm->article_number,
                'datanorm_name' => $datanorm->short_text1,
                'datanorm_price' => $datanorm_material_unit_price,
                'datanorm_price_with_surcharge' => $datanorm_total_unit_price,
                'datanorm_price_raw' => $datanorm->price,
                'datanorm_material_price' => $datanorm->price,
                'datanorm_metal_surcharge' => $datanorm_metal_surcharge,
                'datanorm_price_unit_code' => $datanorm_price_unit_code,
                'effective_surcharge' => $effective_surcharge,
                'surcharge_source' => $surcharge_source,
                'datanorm_price_unit' => $price_unit,
                'datanorm_product_group' => isset($datanorm->product_group) ? $datanorm->product_group : '',
                'datanorm_description' => trim($datanorm->short_text1.' '.$datanorm->short_text2),
                'datanorm_label' => $datanorm->short_text1,
                'price_differs' => $product && abs($current_compare_price - $datanorm_compare_price) > 0.01,
                'description_differs' => $product && $product->description != trim($datanorm->short_text1.' '.$datanorm->short_text2),
                'label_differs' => $product && $product->label != $datanorm->short_text1,
            );
        }
    }

    return $results;
}

/**
 * Find Datanorm match for a product
 */
function findDatanormMatch($db, $fk_soc, $ref_fourn, $label, $barcode, $ref, $search_by_name, $search_by_ean, $search_by_ref)
{
    // First try by supplier reference (article number)
    $sql = "SELECT * FROM ".MAIN_DB_PREFIX."importzugferd_datanorm";
    $sql .= " WHERE fk_soc = ".((int)$fk_soc);
    $sql .= " AND article_number = '".$db->escape($ref_fourn)."'";
    $sql .= " LIMIT 1";

    $resql = $db->query($sql);
    if ($resql && $db->num_rows($resql) > 0) {
        return $db->fetch_object($resql);
    }

    // Try by EAN if enabled
    if ($search_by_ean && !empty($barcode)) {
        $sql = "SELECT * FROM ".MAIN_DB_PREFIX."importzugferd_datanorm";
        $sql .= " WHERE fk_soc = ".((int)$fk_soc);
        $sql .= " AND ean = '".$db->escape($barcode)."'";
        $sql .= " LIMIT 1";

        $resql = $db->query($sql);
        if ($resql && $db->num_rows($resql) > 0) {
            return $db->fetch_object($resql);
        }
    }

    // Try by product ref if enabled
    if ($search_by_ref && !empty($ref)) {
        $sql = "SELECT * FROM ".MAIN_DB_PREFIX."importzugferd_datanorm";
        $sql .= " WHERE fk_soc = ".((int)$fk_soc);
        $sql .= " AND article_number = '".$db->escape($ref)."'";
        $sql .= " LIMIT 1";

        $resql = $db->query($sql);
        if ($resql && $db->num_rows($resql) > 0) {
            return $db->fetch_object($resql);
        }
    }

    // Try by product name/label if enabled
    if ($search_by_name && !empty($label)) {
        $sql = "SELECT * FROM ".MAIN_DB_PREFIX."importzugferd_datanorm";
        $sql .= " WHERE fk_soc = ".((int)$fk_soc);
        $sql .= " AND (short_text1 LIKE '%".$db->escape($label)."%'";
        $sql .= " OR short_text2 LIKE '%".$db->escape($label)."%')";
        $sql .= " LIMIT 1";

        $resql = $db->query($sql);
        if ($resql && $db->num_rows($resql) > 0) {
            return $db->fetch_object($resql);
        }
    }

    return null;
}

/**
 * Find product match for Datanorm article
 */
function findProductMatch($db, $fk_soc, $datanorm)
{
    // Try by supplier reference
    $sql = "SELECT p.* FROM ".MAIN_DB_PREFIX."product p";
    $sql .= " INNER JOIN ".MAIN_DB_PREFIX."product_fournisseur_price pf ON pf.fk_product = p.rowid";
    $sql .= " WHERE pf.fk_soc = ".((int)$fk_soc);
    $sql .= " AND pf.ref_fourn = '".$db->escape($datanorm->article_number)."'";
    $sql .= " LIMIT 1";

    $resql = $db->query($sql);
    if ($resql && $db->num_rows($resql) > 0) {
        return $db->fetch_object($resql);
    }

    // Try by EAN
    if (!empty($datanorm->ean)) {
        $sql = "SELECT * FROM ".MAIN_DB_PREFIX."product";
        $sql .= " WHERE barcode = '".$db->escape($datanorm->ean)."'";
        $sql .= " LIMIT 1";

        $resql = $db->query($sql);
        if ($resql && $db->num_rows($resql) > 0) {
            return $db->fetch_object($resql);
        }
    }

    return null;
}

/**
 * Get supplier price for a product
 */
function getSupplierPrice($db, $product_id, $fk_soc)
{
    // Use unitprice (price per 1 piece) for comparison, not price (which may be for a quantity)
    $sql = "SELECT unitprice, price, quantity FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
    $sql .= " WHERE fk_product = ".((int)$product_id);
    $sql .= " AND fk_soc = ".((int)$fk_soc);
    $sql .= " ORDER BY rowid DESC LIMIT 1";

    $resql = $db->query($sql);
    if ($resql && $db->num_rows($resql) > 0) {
        $obj = $db->fetch_object($resql);
        // Return unitprice if available, otherwise calculate from price/quantity
        if (!empty($obj->unitprice) && $obj->unitprice > 0) {
            return $obj->unitprice;
        }
        // Fallback: calculate unit price from price and quantity
        if (!empty($obj->quantity) && $obj->quantity > 0) {
            return $obj->price / $obj->quantity;
        }
        return $obj->price;
    }
    return 0;
}

/**
 * Get supplier price details including extrafields (Kupferzuschlag)
 */
function getSupplierPriceDetails($db, $product_id, $fk_soc)
{
    $result = array(
        'price' => 0,
        'quantity' => 1,
        'kaufmenge' => 0,
        'unitprice' => 0,
        'kupferzuschlag' => 0,
        'preiseinheit' => 1,
        'price_id' => 0,
    );

    // Get base price - ALWAYS load price + quantity, NOT unitprice alone!
    $sql = "SELECT pf.rowid, pf.price, pf.quantity";
    $sql .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price pf";
    $sql .= " WHERE pf.fk_product = ".((int)$product_id);
    $sql .= " AND pf.fk_soc = ".((int)$fk_soc);
    $sql .= " ORDER BY pf.rowid DESC LIMIT 1";

    $resql = $db->query($sql);
    if ($resql && $db->num_rows($resql) > 0) {
        $obj = $db->fetch_object($resql);
        $result['price_id'] = $obj->rowid;
        $result['price'] = (float)$obj->price;
        $result['quantity'] = max(1, (int)$obj->quantity);

        // Calculate unit price from price / quantity
        $result['unitprice'] = $result['quantity'] > 0 ? $result['price'] / $result['quantity'] : $result['price'];

        // Get extrafields (Kupferzuschlag, Preiseinheit, Kaufmenge)
        $sql_extra = "SELECT kupferzuschlag, preiseinheit, kaufmenge";
        $sql_extra .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price_extrafields";
        $sql_extra .= " WHERE fk_object = ".((int)$obj->rowid);

        $res_extra = $db->query($sql_extra);
        if ($res_extra && $db->num_rows($res_extra) > 0) {
            $extra = $db->fetch_object($res_extra);
            $result['kupferzuschlag'] = !empty($extra->kupferzuschlag) ? (float)$extra->kupferzuschlag : 0;
            $result['preiseinheit'] = !empty($extra->preiseinheit) ? (int)$extra->preiseinheit : 1;
            $result['kaufmenge'] = !empty($extra->kaufmenge) ? (int)$extra->kaufmenge : 0;
        }
    }

    return $result;
}

/**
 * Extract price unit from short text (e.g. "Ri100" = 100, "Tr.500" = 500)
 * This is a fallback when price_unit field is not properly filled
 *
 * @param string $short_text1 Short text 1
 * @param string $short_text2 Short text 2 (optional)
 * @return int Extracted price unit or 1 if not found
 */
function extractPriceUnitFromText($short_text1, $short_text2 = '')
{
    $text = $short_text1 . ' ' . $short_text2;

    // Patterns to match:
    // Ri100, Ri.100, Ri 100, Ri. 100 (Rolle = Roll)
    // Tr100, Tr.100, Tr 100, Tr. 100 (Trommel = Drum)
    // Ring 100, Ring100
    // /100, /50 (per unit indicator)
    // VPE100, VPE 100 (Verpackungseinheit)
    // 100er, 50er (German quantity suffix)

    $patterns = array(
        '/\bRi\.?\s*(\d+)\b/i',           // Ri100, Ri.100, Ri 100
        '/\bTr\.?\s*(\d+)\b/i',           // Tr100, Tr.100, Tr 500
        '/\bRing\.?\s*(\d+)\b/i',         // Ring 100
        '/\bRolle\.?\s*(\d+)\b/i',        // Rolle 100
        '/\bTrommel\.?\s*(\d+)\b/i',      // Trommel 500
        '/\/(\d+)\s*(?:Stk?|m|M)?\b/',    // /100, /100Stk, /100m
        '/\bVPE\.?\s*(\d+)\b/i',          // VPE100, VPE 100
        '/\b(\d+)er\b/',                  // 100er
        '/\bPE\s*(\d+)\b/i',              // PE100
    );

    foreach ($patterns as $pattern) {
        if (preg_match($pattern, $text, $matches)) {
            $unit = (int)$matches[1];
            if ($unit > 1 && $unit <= 10000) {
                return $unit;
            }
        }
    }

    return 1;
}

/**
 * Get effective price unit - uses database value if > 1, otherwise tries to extract from text
 *
 * @param object $datanorm Datanorm database object
 * @return int Effective price unit
 */
function getEffectivePriceUnit($datanorm)
{
    // If database has a valid price_unit > 1, use it
    if (!empty($datanorm->price_unit) && $datanorm->price_unit > 1) {
        return (int)$datanorm->price_unit;
    }

    // Otherwise try to extract from text
    $short_text2 = isset($datanorm->short_text2) ? $datanorm->short_text2 : '';
    return extractPriceUnitFromText($datanorm->short_text1, $short_text2);
}

/**
 * Build comparison result array
 *
 * @param object $product Product from supplier price
 * @param object $datanorm Datanorm data
 * @return array Comparison result
 */
function buildComparisonResult($product, $datanorm)
{
    global $db;

    $fk_soc = $datanorm->fk_soc;
    // Get supplier price details including extrafields (Kupferzuschlag, Kaufmenge)
    $priceDetails = getSupplierPriceDetails($db, $product->fk_product, $fk_soc);
    $current_total_price = $priceDetails['price'];
    $current_quantity = $priceDetails['quantity'];
    $current_kaufmenge = $priceDetails['kaufmenge']; // Actual purchase quantity (if set)
    $current_kupferzuschlag = $priceDetails['kupferzuschlag'];

    // Use kaufmenge if set, otherwise fall back to quantity
    $effective_quantity = ($current_kaufmenge > 0) ? $current_kaufmenge : $current_quantity;
    $current_unit_price = $effective_quantity > 0 ? $current_total_price / $effective_quantity : $current_total_price;

    // Calculate unit price (Datanorm price may be per price_unit pieces)
    // Datanorm metal_surcharge is usually 0 for Sonepar - use extrafield from invoice instead
    $price_unit = (!empty($datanorm->price_unit) && $datanorm->price_unit > 1) ? $datanorm->price_unit : 1;
    $datanorm_price_unit_code = isset($datanorm->price_unit_code) ? $datanorm->price_unit_code : -1;
    $datanorm_metal_surcharge = !empty($datanorm->metal_surcharge) ? (float)$datanorm->metal_surcharge : 0;

    // Priority for surcharge: 1) Invoice extrafield, 2) Datanorm
    if ($current_kupferzuschlag > 0) {
        $effective_surcharge = $current_kupferzuschlag;
        $surcharge_source = 'invoice';
    } else {
        $effective_surcharge = $datanorm_metal_surcharge;
        $surcharge_source = 'datanorm';
    }

    // Calculate prices for comparison
    // IMPORTANT: Dolibarr price already INCLUDES kupferzuschlag! Don't add it again!
    // Datanorm price is WITHOUT kupferzuschlag, so add it for comparison
    // Compare on UNIT PRICE basis (per 1 piece/meter)
    //
    // Example: Kabel NYM-J 5x10
    // - Dolibarr: 331,27€ for 50m (includes 254,55€ Cu for 50m) → 6,63€/m
    // - Datanorm: 168,50€ for 100m (PE=100) + Cu must be scaled to 100m
    // - Cu per unit: 254,55€ / 50m = 5,09€/m → for 100m = 509,10€
    // - Datanorm total for 100m: 168,50€ + 509,10€ = 677,60€ → 6,78€/m

    // Calculate Cu per unit (from Dolibarr's quantity basis)
    $cu_per_unit = ($current_kupferzuschlag > 0 && $effective_quantity > 0)
        ? $current_kupferzuschlag / $effective_quantity
        : 0;

    // Scale Cu to Datanorm's price_unit basis
    $cu_for_price_unit = $cu_per_unit * $price_unit;

    // Dolibarr: unit price (already includes Cu)
    $current_compare_price = $effective_quantity > 0 ? $current_total_price / $effective_quantity : $current_total_price;

    // Datanorm: material price + scaled Cu, then to unit price
    $datanorm_compare_price = ($datanorm->price + $cu_for_price_unit) / $price_unit;

    // For display: always show unit prices
    $datanorm_material_unit_price = $datanorm->price / $price_unit;
    $total_price_with_surcharge = $datanorm->price + $cu_for_price_unit;
    $datanorm_total_unit_price = $total_price_with_surcharge / $price_unit;

    return array(
        'product_id' => $product->fk_product,
        'current_price' => $current_unit_price,
        'current_total_price' => $current_total_price,
        'current_quantity' => $current_quantity,
        'current_kaufmenge' => $current_kaufmenge,
        'current_effective_quantity' => $effective_quantity,
        'current_kupferzuschlag' => $current_kupferzuschlag,
        'current_description' => $product->description,
        'current_label' => $product->label,
        'datanorm_key' => $datanorm->article_number,
        'datanorm_ref' => $datanorm->article_number,
        'datanorm_name' => $datanorm->short_text1,
        'datanorm_price' => $datanorm_material_unit_price,           // Material price per unit
        'datanorm_price_with_surcharge' => $datanorm_total_unit_price, // Total price including surcharge
        'datanorm_price_raw' => $datanorm->price,                    // Raw price from DATPREIS
        'datanorm_material_price' => $datanorm->price,
        'datanorm_metal_surcharge' => $datanorm_metal_surcharge,     // From Datanorm (usually 0)
        'datanorm_price_unit_code' => $datanorm_price_unit_code,
        'effective_surcharge' => $effective_surcharge,               // From invoice or Datanorm
        'surcharge_source' => $surcharge_source,                     // Source of surcharge (invoice/datanorm)
        'datanorm_price_unit' => $price_unit,
        'datanorm_product_group' => isset($datanorm->product_group) ? $datanorm->product_group : '',
        'datanorm_description' => trim($datanorm->short_text1.' '.$datanorm->short_text2),
        'datanorm_label' => $datanorm->short_text1,
        'price_differs' => abs($current_compare_price - $datanorm_compare_price) > 0.01,
        'description_differs' => $product->description != trim($datanorm->short_text1.' '.$datanorm->short_text2),
        'label_differs' => $product->label != $datanorm->short_text1,
    );
}

/**
 * Apply Datanorm update to a product and log changes
 */
function applyDatanormUpdate($db, $user, $product_id, $datanorm_key, $fk_soc, $apply_price, $apply_description, $apply_label, $batch_id = '')
{
    global $conf;

    // Get Datanorm data
    $sql = "SELECT * FROM ".MAIN_DB_PREFIX."importzugferd_datanorm";
    $sql .= " WHERE fk_soc = ".((int)$fk_soc);
    $sql .= " AND article_number = '".$db->escape($datanorm_key)."'";

    $resql = $db->query($sql);
    if (!$resql || $db->num_rows($resql) == 0) {
        return -1;
    }

    $datanorm = $db->fetch_object($resql);

    // Calculate unit price (Datanorm price may be per price_unit pieces)
    // Total price = material price + metal surcharge (for cables)
    $price_unit = (!empty($datanorm->price_unit) && $datanorm->price_unit > 1) ? $datanorm->price_unit : 1;

    // Get existing supplier price details to get kupferzuschlag and quantity from extrafield
    $priceDetails = getSupplierPriceDetails($db, $product_id, $fk_soc);
    $current_kupferzuschlag = $priceDetails['kupferzuschlag'];
    $current_quantity = $priceDetails['quantity'];
    $current_kaufmenge = $priceDetails['kaufmenge'];
    $effective_quantity = ($current_kaufmenge > 0) ? $current_kaufmenge : max(1, $current_quantity);

    // Priority for surcharge: 1) Dolibarr extrafield (from invoice), 2) Datanorm metal_surcharge
    $datanorm_metal_surcharge = !empty($datanorm->metal_surcharge) ? (float)$datanorm->metal_surcharge : 0;

    // Scale Cu from Dolibarr's quantity to Datanorm's price_unit
    // Example: Cu 152,73€ for 50m → per meter = 3,05€ → for 100m = 305,46€
    $cu_per_unit = ($current_kupferzuschlag > 0 && $effective_quantity > 0)
        ? $current_kupferzuschlag / $effective_quantity
        : 0;
    $cu_for_price_unit = $cu_per_unit * $price_unit;

    // Use scaled Cu, or fallback to Datanorm metal_surcharge
    $effective_surcharge = ($cu_for_price_unit > 0) ? $cu_for_price_unit : $datanorm_metal_surcharge;

    // Total price for price_unit includes scaled surcharge
    $total_price_for_pe = $datanorm->price + $effective_surcharge;
    // Unit price (per 1 piece/meter)
    $datanorm_unit_price = $total_price_for_pe / $price_unit;

    // Load product
    $product = new Product($db);
    $result = $product->fetch($product_id);
    if ($result <= 0) {
        return -2;
    }

    // Store original values for logging
    $old_label = $product->label;
    $old_description = $product->description;
    $old_price = getSupplierPrice($db, $product_id, $fk_soc);

    $updated = false;
    $changes = array();

    // Update label
    if ($apply_label && $product->label != $datanorm->short_text1) {
        $changes[] = array(
            'field' => 'label',
            'old' => $old_label,
            'new' => $datanorm->short_text1
        );
        $product->label = $datanorm->short_text1;
        $updated = true;
    }

    // Update label only (description goes to supplier price desc_fourn below)
    // Save product changes
    if ($updated) {
        $result = $product->update($product->id, $user);
        if ($result < 0) {
            return -3;
        }
    }

    // Update supplier price and/or description
    if ($apply_price || $apply_description) {
        $productFourn = new ProductFournisseur($db);
        $productFourn->fetch($product_id);

        // Load supplier object (required by update_buyprice - expects Societe object, not integer)
        $supplier = new Societe($db);
        $supplier->fetch($fk_soc);

        // Find existing supplier price
        $sql = "SELECT rowid, quantity, price, unitprice, desc_fourn FROM ".MAIN_DB_PREFIX."product_fournisseur_price";
        $sql .= " WHERE fk_product = ".((int)$product_id);
        $sql .= " AND fk_soc = ".((int)$fk_soc);
        $sql .= " ORDER BY rowid DESC LIMIT 1";

        $resql = $db->query($sql);
        if ($resql && $db->num_rows($resql) > 0) {
            $priceObj = $db->fetch_object($resql);
            $price_rowid = $priceObj->rowid;

            // Use effective_quantity (kaufmenge if set, otherwise quantity) for price comparison
            // This ensures consistent comparison with buildComparisonResult()
            $effective_qty = ($priceDetails['kaufmenge'] > 0) ? $priceDetails['kaufmenge'] : max(1, $priceObj->quantity);

            // Get the actual unit price from Dolibarr (price per 1 effective piece)
            $current_unit_price = $effective_qty > 0 ? $priceObj->price / $effective_qty : $priceObj->price;

            // Prepare new description if requested
            $new_desc_fourn = null;
            if ($apply_description) {
                $new_desc_fourn = trim($datanorm->short_text1.' '.$datanorm->short_text2);
                if ($priceObj->desc_fourn != $new_desc_fourn) {
                    $changes[] = array(
                        'field' => 'desc_fourn',
                        'old' => $priceObj->desc_fourn,
                        'new' => $new_desc_fourn
                    );
                } else {
                    $new_desc_fourn = null; // No change needed
                }
            }

            // Check if price needs update
            $price_changed = $apply_price && (abs($current_unit_price - $datanorm_unit_price) > 0.01);

            if ($price_changed) {
                $changes[] = array(
                    'field' => 'price',
                    'old' => $current_unit_price,
                    'new' => $datanorm_unit_price
                );
            }

            // Update only the fields that need changing (preserves all other fields!)
            if ($price_changed || $new_desc_fourn !== null) {
                $update_fields = array();

                if ($price_changed) {
                    // Calculate total price for the quantity, round to 2 decimals
                    $total_price_for_qty = round($datanorm_unit_price * $priceObj->quantity, 2);
                    $rounded_unit_price = round($datanorm_unit_price, 2);
                    $update_fields[] = "price = ".((float)$total_price_for_qty);
                    $update_fields[] = "unitprice = ".((float)$rounded_unit_price);
                }

                if ($new_desc_fourn !== null) {
                    $update_fields[] = "desc_fourn = '".$db->escape($new_desc_fourn)."'";
                }

                if (!empty($update_fields)) {
                    $sql_update = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price";
                    $sql_update .= " SET ".implode(", ", $update_fields);
                    $sql_update .= " WHERE rowid = ".(int)$price_rowid;

                    $result = $db->query($sql_update);
                    if (!$result) {
                        return -4;
                    }
                }
            }
        }
    }

    // Log all changes
    if (!empty($changes)) {
        $now = dol_now();
        $batch_id = $batch_id ?: uniqid('single_');

        foreach ($changes as $change) {
            $sql = "INSERT INTO ".MAIN_DB_PREFIX."importzugferd_datanorm_log";
            $sql .= " (fk_product, fk_soc, fk_user, datanorm_ref, field_changed, old_value, new_value, date_change, batch_id, entity)";
            $sql .= " VALUES (";
            $sql .= ((int)$product_id).", ";
            $sql .= ((int)$fk_soc).", ";
            $sql .= ((int)$user->id).", ";
            $sql .= "'".$db->escape($datanorm_key)."', ";
            $sql .= "'".$db->escape($change['field'])."', ";
            $sql .= "'".$db->escape($change['old'])."', ";
            $sql .= "'".$db->escape($change['new'])."', ";
            $sql .= "'".$db->idate($now)."', ";
            $sql .= "'".$db->escape($batch_id)."', ";
            $sql .= ((int)$conf->entity);
            $sql .= ")";

            $db->query($sql);
        }
    }

    return 1;
}
