## Quagga2 Scanner - Reader-Reihenfolge optimiert: CODE128/CODE39 vor EAN - Verhindert Fehlerkennungen bei alphanumerischen Codes (z.B. P20260030) - EAN-Reader haben niedrigere Priorität ## Brother PT-E560BT Android App - Native Kotlin App für Bluetooth-Druck auf Brother PT-E560BT - Intent-Schema: brotherprint://print?barcode=XXX&ref=REF - 90° Rotation für Längs-Druck auf 24mm TZe-Band - Produkt-Referenz (fett), Barcode-Strichen, Barcode-Wert - Erweiterte Error-Handling (SetLabelsizeError, NoCoverError, etc.) - Build: Gradle 9.3.1, Kotlin 2.1.0, Brother SDK v4 ## Bestelllogik - ref_supplier = "Direkt" (ohne Datum) für dauerhafte Direktbestellungen - Pro Lieferant eine durchgängige Direkt-Bestellung statt tägliche neue ## PWA Updates - Service Worker v8.1 - CSS/JS Cache-Invalidierung (?v=81) - localStorage Migration für alte Keys ## Dokumentation - README.md aktualisiert mit Brother-App und PWA-Details - Dateistruktur erweitert um android-app/ - .gitignore für Test-Dateien Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
212 lines
5.5 KiB
PHP
Executable file
212 lines
5.5 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* AJAX: Add product to supplier order (Direktbestellung)
|
|
* Creates or uses existing draft order per supplier
|
|
*/
|
|
|
|
if (!defined('NOTOKENRENEWAL')) {
|
|
define('NOTOKENRENEWAL', '1');
|
|
}
|
|
if (!defined('NOREQUIREMENU')) {
|
|
define('NOREQUIREMENU', '1');
|
|
}
|
|
if (!defined('NOREQUIREHTML')) {
|
|
define('NOREQUIREHTML', '1');
|
|
}
|
|
if (!defined('NOREQUIREAJAX')) {
|
|
define('NOREQUIREAJAX', '1');
|
|
}
|
|
|
|
// Load Dolibarr environment
|
|
$res = 0;
|
|
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(json_encode(['success' => false, 'error' => 'Failed to load Dolibarr']));
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
|
|
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// Security check
|
|
if (!$user->hasRight('fournisseur', 'commande', 'creer') && !$user->hasRight('supplier_order', 'creer')) {
|
|
echo json_encode(['success' => false, 'error' => 'Access denied']);
|
|
exit;
|
|
}
|
|
|
|
// Get parameters
|
|
$productId = GETPOSTINT('product_id');
|
|
$supplierId = GETPOSTINT('supplier_id');
|
|
$qty = GETPOSTINT('qty');
|
|
$price = GETPOSTFLOAT('price');
|
|
|
|
if (empty($productId) || empty($supplierId) || empty($qty)) {
|
|
echo json_encode(['success' => false, 'error' => 'Missing parameters']);
|
|
exit;
|
|
}
|
|
|
|
// Load supplier
|
|
$supplier = new Societe($db);
|
|
if ($supplier->fetch($supplierId) <= 0) {
|
|
echo json_encode(['success' => false, 'error' => 'Supplier not found']);
|
|
exit;
|
|
}
|
|
|
|
// Load product
|
|
$product = new Product($db);
|
|
if ($product->fetch($productId) <= 0) {
|
|
echo json_encode(['success' => false, 'error' => 'Product not found']);
|
|
exit;
|
|
}
|
|
|
|
// Build ref_supplier (Lieferanten-Best.-Nr.): "Direkt" ohne Datum für dauerhafte Direktbestellung
|
|
$refSupplier = 'Direkt';
|
|
|
|
// Search for existing draft order for this supplier with ref_supplier = "Direkt"
|
|
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."commande_fournisseur";
|
|
$sql .= " WHERE fk_soc = ".((int) $supplierId);
|
|
$sql .= " AND fk_statut = 0"; // Draft status
|
|
$sql .= " AND ref_supplier = '".$db->escape($refSupplier)."'";
|
|
$sql .= " AND entity IN (".getEntity('supplier_order').")";
|
|
$sql .= " ORDER BY rowid DESC LIMIT 1";
|
|
|
|
$resql = $db->query($sql);
|
|
$existingOrderId = 0;
|
|
|
|
if ($resql && $db->num_rows($resql) > 0) {
|
|
$obj = $db->fetch_object($resql);
|
|
$existingOrderId = $obj->rowid;
|
|
}
|
|
|
|
$order = new CommandeFournisseur($db);
|
|
|
|
if ($existingOrderId > 0) {
|
|
// Use existing draft order
|
|
$order->fetch($existingOrderId);
|
|
} else {
|
|
// Create new order - ref is auto-generated by Dolibarr, ref_supplier is our custom reference
|
|
$order->socid = $supplierId;
|
|
$order->ref_supplier = $refSupplier; // Lieferanten-Best.-Nr.: "Direkt" (dauerhafte Direktbestellung)
|
|
$order->cond_reglement_id = $supplier->cond_reglement_supplier_id ?: getDolGlobalInt('FOURN_COND_REGLEMENT_ID_DEFAULT', 1);
|
|
$order->mode_reglement_id = $supplier->mode_reglement_supplier_id ?: getDolGlobalInt('FOURN_MODE_REGLEMENT_ID_DEFAULT', 1);
|
|
$order->date = dol_now();
|
|
$order->date_livraison = dol_now() + (7 * 24 * 60 * 60); // +7 days default
|
|
|
|
$result = $order->create($user);
|
|
|
|
if ($result < 0) {
|
|
echo json_encode(['success' => false, 'error' => 'Failed to create order: ' . $order->error]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Get supplier price info
|
|
$productFourn = new ProductFournisseur($db);
|
|
$supplierPrices = $productFourn->list_product_fournisseur_price($productId, '', '', 0, 0, $supplierId);
|
|
|
|
$supplierRef = '';
|
|
$unitPrice = $price;
|
|
$tva_tx = 19; // Default VAT
|
|
|
|
if (!empty($supplierPrices)) {
|
|
foreach ($supplierPrices as $sp) {
|
|
if ($sp->fourn_id == $supplierId) {
|
|
$supplierRef = $sp->ref_fourn;
|
|
if ($unitPrice <= 0) {
|
|
$unitPrice = $sp->fourn_price;
|
|
}
|
|
$tva_tx = $sp->tva_tx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if product already in order
|
|
$existingLine = null;
|
|
foreach ($order->lines as $line) {
|
|
if ($line->fk_product == $productId) {
|
|
$existingLine = $line;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($existingLine) {
|
|
// Update quantity
|
|
$newQty = $existingLine->qty + $qty;
|
|
$result = $order->updateline(
|
|
$existingLine->id,
|
|
$existingLine->desc,
|
|
$unitPrice,
|
|
$newQty,
|
|
$existingLine->remise_percent,
|
|
$tva_tx,
|
|
0, // localtax1
|
|
0, // localtax2
|
|
'HT',
|
|
0,
|
|
0,
|
|
GETPOST('product_type', 'int') ?: 0,
|
|
false,
|
|
null,
|
|
null,
|
|
0,
|
|
$supplierRef
|
|
);
|
|
} else {
|
|
// Add new line
|
|
$result = $order->addline(
|
|
$product->description ?: $product->label,
|
|
$unitPrice,
|
|
$qty,
|
|
$tva_tx,
|
|
0, // localtax1
|
|
0, // localtax2
|
|
$productId,
|
|
0, // fourn_price_id
|
|
$supplierRef,
|
|
0, // remise_percent
|
|
'HT',
|
|
0, // pu_devise
|
|
$product->type,
|
|
0, // rang
|
|
0, // special_code
|
|
null, // array_options
|
|
null, // fk_unit
|
|
0, // origin
|
|
0 // origin_id
|
|
);
|
|
}
|
|
|
|
if ($result < 0) {
|
|
echo json_encode(['success' => false, 'error' => 'Failed to add line: ' . $order->error]);
|
|
exit;
|
|
}
|
|
|
|
// Count items in order
|
|
$order->fetch($order->id);
|
|
$totalItems = count($order->lines);
|
|
$totalQty = 0;
|
|
foreach ($order->lines as $line) {
|
|
$totalQty += $line->qty;
|
|
}
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'order_id' => $order->id,
|
|
'order_ref' => $order->ref,
|
|
'total_items' => $totalItems,
|
|
'total_qty' => $totalQty,
|
|
'message' => 'Product added to ' . $order->ref
|
|
]);
|