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'; 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 $filter_price = GETPOSTISSET('filter_price') ? GETPOSTINT('filter_price') : 1; $filter_description = GETPOSTISSET('filter_description') ? GETPOSTINT('filter_description') : 1; $filter_label = GETPOSTISSET('filter_label') ? GETPOSTINT('filter_label') : 0; // Only show differences $only_differences = GETPOSTINT('only_differences'); // 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 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); 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'); } } // Remove from pending if ($action == 'remove_pending') { $product_id = GETPOSTINT('product_id'); unset($_SESSION['datanorm_pending_changes'][$product_id]); } // Clear all pending if ($action == 'clear_pending') { $_SESSION['datanorm_pending_changes'] = array(); setEventMessages($langs->trans('PendingChangesCleared'), null, 'mesgs'); } // Show confirmation dialog if ($action == 'confirm_apply_all') { // Will be handled in view section } // Apply all pending changes if ($action == 'apply_all_confirmed' && GETPOST('confirm', 'alpha') == 'yes') { $success = 0; $errors = 0; 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); if ($result > 0) { $success++; } else { $errors++; } } $_SESSION['datanorm_pending_changes'] = array(); setEventMessages($langs->trans('DatanormMassUpdateComplete', $success, $errors), null, 'mesgs'); header('Location: '.$_SERVER['PHP_SELF']); 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 '
'.$langs->trans('NoDatanormData').'
'; print '
'.$langs->trans('UploadDatanorm').''; llxFooter(); $db->close(); exit; } // Search form print '
'; print ''; print ''; print '
'; print '
'; print ''; // Supplier selection print ''; print ''; print ''; print ''; print ''; print ''; print ''; // Search mode print ''; print ''; print ''; print ''; // Manual search term print ''; print ''; print ''; print ''; // Additional search options print ''; print ''; print ''; print ''; // Filter: What to compare/update print ''; print ''; print ''; print ''; print ''; print ''; print ''; // Only show differences print ''; print ''; print ''; print ''; print '
'.$langs->trans('SelectSupplier').'
'.$langs->trans('Supplier').''; // 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 ''; print '
'.$langs->trans('SearchMode').''; print ''; print ''; print '     '; print ''; print ''; print '
'.$langs->trans('SearchTerm').''; print ''; print '
'.$langs->trans('AdditionalSearchOptions').''; print ''; print ''; print '   '; print ''; print ''; print '   '; print ''; print ''; print '
'.$langs->trans('FieldsToCompare').'
'.$langs->trans('Fields').''; print ''; print ''; print '   '; print ''; print ''; print '   '; print ''; print ''; print '
'.$langs->trans('Display').''; print ''; print ''; print '
'; print '
'; print '
'; print '
'; print ''; if (!empty($_SESSION['datanorm_pending_changes'])) { print '   '.$langs->trans('ClearPendingChanges').' ('.count($_SESSION['datanorm_pending_changes']).')'; } print '
'; print '
'; // JavaScript for toggling manual search print ''; // 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); } // 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']); }); } if (!empty($comparison_results)) { print '
'; print '
'; print ''; // Header print ''; print ''; print ''; if ($filter_price) { print ''; print ''; } if ($filter_description) { print ''; print ''; } if ($filter_label) { print ''; print ''; } print ''; print ''; foreach ($comparison_results as $item) { $has_difference = ($filter_price && $item['price_differs']) || ($filter_description && $item['description_differs']) || ($filter_label && $item['label_differs']); $rowClass = $has_difference ? 'oddeven highlighted' : 'oddeven'; print ''; // Product print ''; // Datanorm article print ''; // Price comparison if ($filter_price) { $priceStyle = $item['price_differs'] ? 'background-color: #fcf8e3;' : ''; print ''; print ''; } // Description comparison if ($filter_description) { $descStyle = $item['description_differs'] ? 'background-color: #fcf8e3;' : ''; print ''; print ''; } // Label comparison if ($filter_label) { $labelStyle = $item['label_differs'] ? 'background-color: #fcf8e3;' : ''; print ''; print ''; } // Actions print ''; print ''; } print '
'.$langs->trans('Product').''.$langs->trans('DatanormArticle').''.$langs->trans('CurrentPrice').''.$langs->trans('DatanormPrice').''.$langs->trans('CurrentDescription').''.$langs->trans('DatanormDescription').''.$langs->trans('CurrentLabel').''.$langs->trans('DatanormLabel').''.$langs->trans('Actions').'
'; 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 '
'.$product->ref.''; } else { print ''.$langs->trans('ProductNotInDatabase').''; } print '
'; print ''.dol_escape_htmltag($item['datanorm_ref']).''; print '
'.dol_escape_htmltag(dol_trunc($item['datanorm_name'], 50)).''; print '
'; if ($item['product_id'] > 0) { print price($item['current_price']); } else { print '-'; } print ''; print price($item['datanorm_price']); if ($item['price_differs'] && $item['product_id'] > 0) { $diff = $item['datanorm_price'] - $item['current_price']; $diffPercent = ($item['current_price'] > 0) ? ($diff / $item['current_price'] * 100) : 0; print '
'; if ($diff > 0) { print ' +'.number_format($diffPercent, 1).'%'; } else { print ' '.number_format($diffPercent, 1).'%'; } } print '
'; print dol_escape_htmltag(dol_trunc($item['current_description'], 80)); print ''; print dol_escape_htmltag(dol_trunc($item['datanorm_description'], 80)); print ''; print dol_escape_htmltag($item['current_label']); print ''; print dol_escape_htmltag($item['datanorm_label']); print ''; if ($item['product_id'] > 0 && $has_difference) { // Quick apply form print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; // Checkboxes for what to apply if ($filter_price && $item['price_differs']) { print ''; print 'P '; } if ($filter_description && $item['description_differs']) { print ''; print 'D '; } if ($filter_label && $item['label_differs']) { print ''; print 'L '; } print ''; print '
'; // Add to pending $isPending = isset($_SESSION['datanorm_pending_changes'][$item['product_id']]); if (!$isPending) { print ' '; print ''; print ''; } else { print ' '.$langs->trans('Pending').''; } } elseif ($item['product_id'] == 0) { // Create product link print ''; print ''; print ''; } else { print ''.$langs->trans('NoChanges').''; } print '
'; print '
'; // Summary and mass apply button $pendingCount = count($_SESSION['datanorm_pending_changes']); if ($pendingCount > 0) { print '
'; print '
'; print ''; print ''.$langs->trans('ApplyAllPendingChanges').' ('.$pendingCount.')'; print ''; print '
'; } } else { print '
'.$langs->trans('NoResultsFound').'
'; } } // Confirmation dialog for mass apply if ($action == 'confirm_apply_all' && !empty($_SESSION['datanorm_pending_changes'])) { print '

'; print '
'; print '

'.$langs->trans('ConfirmMassUpdate').'

'; print '

'.$langs->trans('FollowingProductsWillBeUpdated').':

'; print ''; print ''; print ''; print ''; print ''; foreach ($_SESSION['datanorm_pending_changes'] as $product_id => $change) { $product = new Product($db); $product->fetch($product_id); print ''; print ''; print ''; print ''; } print '
'.$langs->trans('Product').''.$langs->trans('Changes').'
'.$product->getNomUrl(1).' - '.$product->label.''; $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 '
'; print '
'; print '
'; print ''; print ''; print ''; print '
'; print ''; print '   '; print ''.$langs->trans('Cancel').''; print '
'; print '
'; print '
'; } print ''; llxFooter(); $db->close(); /* * Helper functions */ /** * Find products linked to a supplier and compare with Datanorm */ 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 */ 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); $results[] = array( 'product_id' => $product ? $product->rowid : 0, 'current_price' => $product ? getSupplierPrice($db, $product->rowid, $fk_soc) : 0, '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->price, 'datanorm_description' => trim($datanorm->short_text1.' '.$datanorm->short_text2), 'datanorm_label' => $datanorm->short_text1, 'price_differs' => $product && abs(getSupplierPrice($db, $product->rowid, $fk_soc) - $datanorm->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); } } 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) { $sql = "SELECT price 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 $obj->price; } return 0; } /** * Build comparison result array */ function buildComparisonResult($product, $datanorm) { global $db; $fk_soc = $datanorm->fk_soc; $current_price = $product->fourn_price; return array( 'product_id' => $product->fk_product, 'current_price' => $current_price, '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->price, 'datanorm_description' => trim($datanorm->short_text1.' '.$datanorm->short_text2), 'datanorm_label' => $datanorm->short_text1, 'price_differs' => abs($current_price - $datanorm->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 */ function applyDatanormUpdate($db, $user, $product_id, $datanorm_key, $fk_soc, $apply_price, $apply_description, $apply_label) { // 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); // Load product $product = new Product($db); $result = $product->fetch($product_id); if ($result <= 0) { return -2; } $updated = false; // Update label if ($apply_label && $product->label != $datanorm->short_text1) { $product->label = $datanorm->short_text1; $updated = true; } // Update description if ($apply_description) { $new_desc = trim($datanorm->short_text1.' '.$datanorm->short_text2); if ($product->description != $new_desc) { $product->description = $new_desc; $updated = true; } } // Save product changes if ($updated) { $result = $product->update($product->id, $user); if ($result < 0) { return -3; } } // Update supplier price if ($apply_price) { $productFourn = new ProductFournisseur($db); $productFourn->fetch($product_id); // Find existing supplier price $sql = "SELECT rowid, 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) { $priceObj = $db->fetch_object($resql); // Update existing price $result = $productFourn->update_buyprice( $priceObj->quantity, $datanorm->price, $user, 'HT', $fk_soc, 0, // availability $datanorm->article_number, // ref_fourn 0, // tva_tx 0, // charges 0, // remise_percent 0, // remise 0, // newnpr 0, // delivery_time_days '', // supplier_reputation array(), // localtaxes '', // newdefaultvatcode 0, // multicurrency_buyprice '', // multicurrency_price_base_type 0, // multicurrency_tx '', // multicurrency_code '', // desc_fourn '', // barcode 0, // fk_barcode_type array() // options ); if ($result < 0) { return -4; } } } return 1; }