Kritische Bugfixes: - FIX: Produktmatching korrigiert (qty→quantity, status→tosell in SQL) Vorher wurden alle Artikel als Freitext importiert, jetzt korrekte Verknüpfung mit Dolibarr-Produkten über product_fournisseur_price Neue Features: - FEAT: Sonepar NetPrice-Heuristik implementiert Erkennt automatisch ob NetPrice für PriceBasis (IDS-Standard) oder Order-Qty (Sonepar-Variante) ist. 2-Pass-Algorithmus vergleicht beide Interpretationen mit DB-Preis und wählt korrekte aus. Löst +100% Preisabweichungs-Problem bei Sonepar-Warenkörben. - FEAT: Preis-Vergleich in cart_review.php und tab_supplierorder.php Zeigt Abweichungen zwischen Shop-Preisen und gespeicherten Dolibarr-Preisen mit farbiger Markierung (rot >10%, gelb 2-10%, grün ≤2%) Manuelle Preis-Aktualisierung über Checkboxen (keine Automatik) - Admin-Option IDSCONNECT_PRICE_UPDATE_ENABLED mit konfigurierbarem Schwellwert - Übersetzungen für de_DE und en_US erweitert Dateien: - class/idsconnect.class.php: matchProducts() qty→quantity Fix - cart_review.php: Heuristik + Preis-Vergleich UI - tab_supplierorder.php: Preis-Vergleich für Bestellungen - admin/setup.php: Neue Preis-Update-Optionen - CHANGELOG.md: Dokumentation v2.9 und v3.1 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
475 lines
17 KiB
PHP
Executable file
475 lines
17 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* 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 idsconnect/cart_review.php
|
|
* \ingroup idsconnect
|
|
* \brief Empfangenen Warenkorb prüfen und als Lieferantenbestellung übernehmen
|
|
*/
|
|
|
|
// Dolibarr laden
|
|
$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) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
|
dol_include_once('/idsconnect/class/idslog.class.php');
|
|
dol_include_once('/idsconnect/class/idssupplier.class.php');
|
|
dol_include_once('/idsconnect/class/idsconnect.class.php');
|
|
dol_include_once('/idsconnect/lib/idsconnect.lib.php');
|
|
|
|
/**
|
|
* @var Conf $conf
|
|
* @var DoliDB $db
|
|
* @var Translate $langs
|
|
* @var User $user
|
|
*/
|
|
|
|
$langs->loadLangs(array("idsconnect@idsconnect", "orders", "bills"));
|
|
|
|
if (!$user->hasRight('idsconnect', 'use')) {
|
|
accessforbidden();
|
|
}
|
|
|
|
$log_id = GETPOSTINT('log_id');
|
|
$action = GETPOST('action', 'aZ09');
|
|
|
|
// Log laden
|
|
$log = new IdsLog($db);
|
|
if ($log_id > 0) {
|
|
$log->fetch($log_id);
|
|
}
|
|
|
|
// Großhändler laden
|
|
$supplier = new IdsSupplier($db);
|
|
if ($log->fk_supplier > 0) {
|
|
$supplier->fetch($log->fk_supplier);
|
|
}
|
|
|
|
// Artikel immer aus dem XML neu parsen (nutzt aktuelle Preis-Logik)
|
|
$idsconnect = new IdsConnect($db);
|
|
$items = array();
|
|
if (!empty($log->cart_xml)) {
|
|
$items = $idsconnect->parseCartXml($log->cart_xml);
|
|
if ($items === false) {
|
|
$items = array();
|
|
}
|
|
}
|
|
// Fallback: aus response_data wenn kein XML vorhanden
|
|
if (empty($items) && !empty($log->response_data)) {
|
|
$response = json_decode($log->response_data, true);
|
|
if (!empty($response['items'])) {
|
|
$items = $response['items'];
|
|
}
|
|
}
|
|
|
|
// Produktzuordnung: Lieferantenreferenzen gegen Dolibarr-Produkte matchen
|
|
$product_matches = array();
|
|
if (!empty($items) && $supplier->fk_soc > 0) {
|
|
$ref_list = array();
|
|
foreach ($items as $item) {
|
|
if (!empty($item['artikelnr'])) {
|
|
$ref_list[] = $item['artikelnr'];
|
|
}
|
|
}
|
|
if (!empty($ref_list)) {
|
|
$product_matches = $idsconnect->matchProducts($ref_list, $supplier->fk_soc);
|
|
}
|
|
}
|
|
|
|
// HEURISTIK: Sonepar sendet NetPrice manchmal für Qty statt PriceBasis
|
|
// Korrigiere einzelpreis wenn NetPrice/Qty näher am DB-Preis liegt
|
|
if (!empty($items) && !empty($product_matches)) {
|
|
foreach ($items as &$item) {
|
|
$match = $product_matches[$item['artikelnr']] ?? null;
|
|
if ($match && !empty($match['stored_price']) && !empty($item['raw_netprice']) && $item['menge'] > 0) {
|
|
// Zwei Kandidaten für Stückpreis
|
|
$price_from_pricebasis = $item['einzelpreis']; // NetPrice / PriceBasis
|
|
$price_from_qty = (float) $item['raw_netprice'] / (float) $item['menge']; // NetPrice / Qty
|
|
|
|
// Welcher ist näher am DB-Preis?
|
|
$diff_pricebasis = abs($price_from_pricebasis - (float) $match['stored_price']);
|
|
$diff_qty = abs($price_from_qty - (float) $match['stored_price']);
|
|
|
|
// Wenn Qty-basiert deutlich näher ist (mindestens 30% Unterschied), nutze das
|
|
if ($diff_qty < $diff_pricebasis * 0.7) {
|
|
$item['einzelpreis'] = $price_from_qty;
|
|
$item['preiseinheit'] = $item['menge']; // Korrektur: NetPrice gilt für Qty, nicht PriceBasis
|
|
$item['_price_heuristic'] = 'qty-based'; // Debug-Flag
|
|
} else {
|
|
$item['_price_heuristic'] = 'pricebasis-standard'; // Debug-Flag
|
|
}
|
|
}
|
|
}
|
|
unset($item); // Referenz aufheben
|
|
}
|
|
|
|
|
|
/*
|
|
* Actions
|
|
*/
|
|
|
|
// Lieferantenbestellung erstellen
|
|
if ($action == 'create_order' && $user->hasRight('fournisseur', 'commande', 'creer')) {
|
|
if (!verifCond(GETPOST('token', 'alpha') == newToken())) {
|
|
accessforbidden('Bad CSRF token');
|
|
}
|
|
|
|
if ($supplier->fk_soc > 0 && !empty($items)) {
|
|
$order = new CommandeFournisseur($db);
|
|
$order->socid = $supplier->fk_soc;
|
|
$order->note_private = 'Erstellt via IDS Connect aus Warenkorb vom '.dol_print_date($log->date_creation, 'dayhour').' ('.$supplier->label.')';
|
|
|
|
$db->begin();
|
|
$order_id = $order->create($user);
|
|
|
|
if ($order_id > 0) {
|
|
$line_errors = 0;
|
|
foreach ($items as $item) {
|
|
$vat_rate = !empty($item['mwst_satz']) ? $item['mwst_satz'] : 19;
|
|
|
|
// Produkt-Match prüfen
|
|
$fk_product = 0;
|
|
$fk_prod_fourn_price = 0;
|
|
if (!empty($item['artikelnr']) && isset($product_matches[$item['artikelnr']])) {
|
|
$match = $product_matches[$item['artikelnr']];
|
|
$fk_product = $match['fk_product'];
|
|
$fk_prod_fourn_price = $match['fk_prod_fourn_price'];
|
|
}
|
|
|
|
$result = $order->addline(
|
|
$item['bezeichnung'], // desc
|
|
$item['einzelpreis'], // pu_ht (Stückpreis)
|
|
$item['menge'], // qty
|
|
$vat_rate, // txtva
|
|
0, // txlocaltax1
|
|
0, // txlocaltax2
|
|
$fk_product, // fk_product (Dolibarr-Produkt oder 0)
|
|
$fk_prod_fourn_price, // fk_prod_fourn_price
|
|
$item['artikelnr'], // ref_supplier
|
|
0, // remise_percent
|
|
'HT' // price_base_type
|
|
);
|
|
if ($result < 0) {
|
|
$line_errors++;
|
|
}
|
|
}
|
|
|
|
if ($line_errors == 0) {
|
|
$db->commit();
|
|
// Log aktualisieren
|
|
$log->updateStatus('success', $log->response_data);
|
|
$log->updateCart($log->cart_xml, $order_id);
|
|
|
|
setEventMessages($langs->trans("IdsconnectCartImported"), null, 'mesgs');
|
|
header('Location: '.DOL_URL_ROOT.'/fourn/commande/card.php?id='.$order_id);
|
|
exit;
|
|
} else {
|
|
$db->rollback();
|
|
setEventMessages('Fehler beim Erstellen der Bestellpositionen', null, 'errors');
|
|
}
|
|
} else {
|
|
$db->rollback();
|
|
setEventMessages('Fehler beim Erstellen der Bestellung: '.$order->error, null, 'errors');
|
|
}
|
|
} else {
|
|
if ($supplier->fk_soc <= 0) {
|
|
setEventMessages('Großhändler hat keinen verknüpften Dolibarr-Lieferanten. Bitte zuerst in der Großhändler-Konfiguration zuweisen.', null, 'errors');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Manuelle Preis-Aktualisierung
|
|
if ($action == 'update_prices' && $user->hasRight('produit', 'creer')) {
|
|
if (!verifCond(GETPOST('token', 'alpha') == newToken())) {
|
|
accessforbidden('Bad CSRF token');
|
|
}
|
|
|
|
if (getDolGlobalInt('IDSCONNECT_PRICE_UPDATE_ENABLED')) {
|
|
$threshold = (float) getDolGlobalString('IDSCONNECT_PRICE_UPDATE_THRESHOLD', '5');
|
|
$update_prices = GETPOST('update_price', 'array'); // Array von rowids die aktualisiert werden sollen
|
|
$updated_count = 0;
|
|
|
|
if (!empty($update_prices) && !empty($items)) {
|
|
foreach ($items as $item) {
|
|
if (!empty($item['artikelnr']) && isset($product_matches[$item['artikelnr']])) {
|
|
$match = $product_matches[$item['artikelnr']];
|
|
$rowid = $match['fk_prod_fourn_price'];
|
|
|
|
// Prüfen ob diese Position aktualisiert werden soll
|
|
if (in_array($rowid, $update_prices)) {
|
|
// Mindestmengen-Preis (Basis-Preis) updaten, nicht Stückpreis
|
|
$old_basis_price = (float) $match['debug_price'];
|
|
$new_basis_price = !empty($item['raw_netprice']) ? (float) $item['raw_netprice'] : ($item['einzelpreis'] * (!empty($item['preiseinheit']) ? $item['preiseinheit'] : 1));
|
|
|
|
if ($old_basis_price > 0 && $new_basis_price > 0) {
|
|
$sql = "UPDATE ".$db->prefix()."product_fournisseur_price";
|
|
$sql .= " SET price = ".((float) $new_basis_price).", tms = NOW()";
|
|
$sql .= " WHERE rowid = ".((int) $rowid);
|
|
if ($db->query($sql)) {
|
|
$updated_count++;
|
|
dol_syslog("IDS Connect: Preis für ".$item['artikelnr']." manuell aktualisiert (Basis-Preis): ".$old_basis_price." → ".$new_basis_price." (".sprintf('%+.1f%%', (($new_basis_price - $old_basis_price) / $old_basis_price) * 100).")", LOG_INFO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($updated_count > 0) {
|
|
setEventMessages($updated_count.' '.$langs->trans("IdsconnectPricesUpdated"), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans("IdsconnectNoPricesSelected"), null, 'warnings');
|
|
}
|
|
} else {
|
|
setEventMessages($langs->trans("IdsconnectNoPricesSelected"), null, 'warnings');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* View
|
|
*/
|
|
|
|
$form = new Form($db);
|
|
|
|
llxHeader('', $langs->trans("IdsconnectCartReview"), '', '', 0, 0, '', '', '', 'mod-idsconnect page-cart_review');
|
|
|
|
print load_fiche_titre($langs->trans("IdsconnectCartReviewTitle"), '', 'fa-shopping-cart');
|
|
|
|
idsconnectShowTestModeBanner();
|
|
|
|
if (empty($items)) {
|
|
print '<div class="warning">'.$langs->trans("IdsconnectCartEmpty").'</div>';
|
|
print '<br><a href="'.DOL_URL_ROOT.'/custom/idsconnect/idsconnectindex.php" class="butAction">'.$langs->trans("Back").'</a>';
|
|
llxFooter();
|
|
$db->close();
|
|
exit;
|
|
}
|
|
|
|
// Info-Banner
|
|
$matched_count = 0;
|
|
foreach ($items as $item) {
|
|
if (!empty($item['artikelnr']) && isset($product_matches[$item['artikelnr']])) {
|
|
$matched_count++;
|
|
}
|
|
}
|
|
print '<div class="info">';
|
|
print '<strong>'.$langs->trans("IdsconnectCartReviewInfo").'</strong><br>';
|
|
print 'Großhändler: <strong>'.htmlspecialchars($supplier->label ?: '-').'</strong>';
|
|
print ' | Empfangen: '.dol_print_date($log->date_creation, 'dayhour');
|
|
print ' | Artikel: <strong>'.count($items).'</strong>';
|
|
if ($matched_count > 0) {
|
|
print ' | Produkte erkannt: <strong>'.$matched_count.'/'.count($items).'</strong>';
|
|
} else {
|
|
print ' | <span style="color:orange;">⚠ Keine Produkte erkannt</span>';
|
|
}
|
|
print '</div>';
|
|
|
|
// DEBUG: Produkt-Matching
|
|
if ($user->admin && $matched_count == 0) {
|
|
print '<div class="warning">';
|
|
print '<strong>DEBUG: Produkt-Matching</strong><br>';
|
|
print 'Sonepar fk_soc: <strong>'.$supplier->fk_soc.'</strong><br>';
|
|
print 'Gesuchte Artikelnummern: <strong>';
|
|
$search_refs = array();
|
|
foreach ($items as $item) {
|
|
if (!empty($item['artikelnr'])) {
|
|
$search_refs[] = $item['artikelnr'];
|
|
}
|
|
}
|
|
print implode(', ', $search_refs).'</strong><br>';
|
|
print 'Gefundene Matches: <strong>'.count($product_matches).'</strong><br>';
|
|
|
|
// Prüfen ob Artikel in DB existieren
|
|
if ($supplier->fk_soc > 0 && !empty($search_refs)) {
|
|
$sql = "SELECT ref_fourn, fk_product FROM ".$db->prefix()."product_fournisseur_price";
|
|
$sql .= " WHERE fk_soc = ".((int) $supplier->fk_soc);
|
|
$sql .= " LIMIT 5";
|
|
$resql = $db->query($sql);
|
|
if ($resql) {
|
|
$count = $db->num_rows($resql);
|
|
print 'Artikel für diesen Lieferanten in DB: <strong>'.$count.'+ Einträge</strong><br>';
|
|
if ($count > 0) {
|
|
print 'Beispiele: ';
|
|
while ($obj = $db->fetch_object($resql)) {
|
|
print $obj->ref_fourn.', ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
print '</div>';
|
|
}
|
|
|
|
// Formular für Preis-Updates
|
|
$has_price_changes = false;
|
|
$threshold = (float) getDolGlobalString('IDSCONNECT_PRICE_UPDATE_THRESHOLD', '5');
|
|
|
|
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'?log_id='.$log->id.'" name="price_update_form">';
|
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
|
print '<input type="hidden" name="action" value="update_prices">';
|
|
|
|
// Artikel-Tabelle
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>'.$langs->trans("IdsconnectCartArticleNr").'</th>';
|
|
print '<th>'.$langs->trans("IdsconnectCartDescription").'</th>';
|
|
print '<th>Dolibarr-Produkt</th>';
|
|
print '<th class="right">'.$langs->trans("IdsconnectCartQty").'</th>';
|
|
print '<th>'.$langs->trans("IdsconnectCartUnit").'</th>';
|
|
print '<th class="right">'.$langs->trans("IdsconnectCartUnitPrice").'</th>';
|
|
print '<th class="right">'.$langs->trans("IdsconnectPriceDeviation").'</th>';
|
|
print '<th class="right">'.$langs->trans("IdsconnectCartTotalPrice").'</th>';
|
|
print '</tr>';
|
|
|
|
$total = 0;
|
|
foreach ($items as $item) {
|
|
$line_total = $item['gesamtpreis'] ?: ($item['menge'] * $item['einzelpreis']);
|
|
$total += $line_total;
|
|
|
|
// Produkt-Match für Anzeige
|
|
$match = null;
|
|
if (!empty($item['artikelnr']) && isset($product_matches[$item['artikelnr']])) {
|
|
$match = $product_matches[$item['artikelnr']];
|
|
}
|
|
|
|
print '<tr class="oddeven">';
|
|
print '<td><code>'.htmlspecialchars($item['artikelnr']).'</code></td>';
|
|
print '<td>'.htmlspecialchars($item['bezeichnung']);
|
|
if (!empty($item['hersteller'])) {
|
|
print '<br><span class="opacitymedium" style="font-size:0.85em">'.htmlspecialchars($item['hersteller']).'</span>';
|
|
}
|
|
print '</td>';
|
|
// Dolibarr-Produkt
|
|
print '<td>';
|
|
if ($match) {
|
|
print '<a href="'.DOL_URL_ROOT.'/product/card.php?id='.$match['fk_product'].'">';
|
|
print img_picto('', 'product', 'class="paddingright"');
|
|
print htmlspecialchars($match['product_ref']).'</a>';
|
|
print '<br><span class="opacitymedium" style="font-size:0.85em">'.htmlspecialchars($match['product_label']).'</span>';
|
|
} else {
|
|
print '<span class="opacitymedium">Freitext</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td class="right">'.($item['menge']).'</td>';
|
|
print '<td>'.htmlspecialchars($item['einheit']).'</td>';
|
|
print '<td class="right">'.price($item['einzelpreis']);
|
|
// Preiseinheit-Info anzeigen wenn vorhanden (z.B. "Preis/100")
|
|
if (!empty($item['preiseinheit']) && $item['preiseinheit'] > 1) {
|
|
print '<br><span class="opacitymedium" style="font-size:0.85em">'.price($item['raw_netprice']).' / '.$item['preiseinheit'].' Stk</span>';
|
|
}
|
|
print '</td>';
|
|
// Preis-Abweichung
|
|
print '<td class="right">';
|
|
if ($match && !empty($match['stored_price']) && $match['stored_price'] > 0) {
|
|
// Stückpreis-Vergleich (einzelpreis bereits korrekt berechnet)
|
|
$shop_unit_price = (float) $item['einzelpreis'];
|
|
$db_unit_price = (float) $match['stored_price'];
|
|
|
|
// DEBUG für Admin: Zeige Rohdaten zur Nachvollziehbarkeit
|
|
if ($user->admin) {
|
|
print '<div style="font-size:0.7em; color:#666; text-align:left;">';
|
|
print 'Shop: '.price($item['raw_netprice']).' / '.$item['preiseinheit'].' Stk = '.price($shop_unit_price).'/Stk<br>';
|
|
print 'DB: '.price($match['debug_price']).' / '.$match['debug_qty'].' Stk = '.price($db_unit_price).'/Stk<br>';
|
|
print '</div>';
|
|
}
|
|
|
|
$deviation = (($shop_unit_price - $db_unit_price) / $db_unit_price) * 100;
|
|
$abs_deviation = abs($deviation);
|
|
// Farbcode: Grün ≤2%, Gelb 2-10%, Rot >10%
|
|
if ($abs_deviation <= 2) {
|
|
$color = '#28a745'; // Grün
|
|
} elseif ($abs_deviation <= 10) {
|
|
$color = '#ffc107'; // Gelb
|
|
} else {
|
|
$color = '#dc3545'; // Rot
|
|
}
|
|
print '<span style="color:'.$color.'; font-weight:bold;">'.sprintf('%+.1f%%', $deviation).'</span>';
|
|
print '<br><span class="opacitymedium" style="font-size:0.85em">DB: '.price($db_unit_price).'/Stk</span>';
|
|
|
|
// Checkbox für Preis-Update (nur bei Abweichung über Schwellwert UND wenn Feature aktiviert)
|
|
if ($abs_deviation > $threshold && getDolGlobalInt('IDSCONNECT_PRICE_UPDATE_ENABLED') && $user->hasRight('produit', 'creer')) {
|
|
$has_price_changes = true;
|
|
print '<br><input type="checkbox" name="update_price[]" value="'.$match['fk_prod_fourn_price'].'" id="price_'.$match['fk_prod_fourn_price'].'">';
|
|
print ' <label for="price_'.$match['fk_prod_fourn_price'].'" style="font-size:0.85em; cursor:pointer;">'.$langs->trans("IdsconnectUpdatePrice").'</label>';
|
|
}
|
|
} else {
|
|
print '<span class="opacitymedium">-</span>';
|
|
}
|
|
print '</td>';
|
|
print '<td class="right">'.price($line_total).'</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
// Summe
|
|
print '<tr class="liste_total">';
|
|
print '<td colspan="7" class="right"><strong>'.$langs->trans("Total").'</strong></td>';
|
|
print '<td class="right"><strong>'.price($total).'</strong></td>';
|
|
print '</tr>';
|
|
|
|
print '</table>';
|
|
|
|
print '</form>';
|
|
|
|
// Aktionsbuttons
|
|
print '<div class="tabsAction">';
|
|
|
|
// Preis-Update-Button (nur wenn es Änderungen gibt)
|
|
if ($has_price_changes) {
|
|
print '<a class="butAction" href="javascript:document.price_update_form.submit();">'.$langs->trans("IdsconnectUpdateSelectedPrices").'</a>';
|
|
}
|
|
|
|
if ($user->hasRight('fournisseur', 'commande', 'creer') && $supplier->fk_soc > 0) {
|
|
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?log_id='.$log->id.'&action=create_order&token='.newToken().'">'.$langs->trans("IdsconnectCartCreateOrder").'</a>';
|
|
} elseif ($supplier->fk_soc <= 0) {
|
|
print '<span class="butActionRefused" title="Kein Dolibarr-Lieferant verknüpft">'.$langs->trans("IdsconnectCartCreateOrder").'</span>';
|
|
}
|
|
|
|
print '<a class="butAction" href="'.DOL_URL_ROOT.'/custom/idsconnect/idsconnectindex.php">'.$langs->trans("Back").'</a>';
|
|
|
|
print '</div>';
|
|
|
|
// XML-Details (aufklappbar)
|
|
if (!empty($log->cart_xml)) {
|
|
print '<br>';
|
|
print '<details>';
|
|
print '<summary class="opacitymedium" style="cursor:pointer;">XML-Rohdaten anzeigen</summary>';
|
|
print '<div class="idsconnect-log-detail">';
|
|
print htmlspecialchars($log->cart_xml);
|
|
print '</div>';
|
|
print '</details>';
|
|
}
|
|
|
|
llxFooter();
|
|
$db->close();
|