- Drag & Drop Sortierung im Anlagenbaum (Geschwister-Ebene) - UNIQUE KEY uk_kundenkarte_societe_system um fk_contact erweitert - Automatische DB-Migration beim Modul-Aktivieren - Visueller Abstand zwischen Root-Elementen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
250 lines
8.2 KiB
PHP
Executable file
250 lines
8.2 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Alles Watt lauft
|
|
*
|
|
* AJAX endpoint for Bill of Materials (Stückliste) generation from schematic
|
|
*/
|
|
|
|
if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1');
|
|
if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1');
|
|
if (!defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1');
|
|
if (!defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1');
|
|
|
|
$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) die("Include of main fails");
|
|
|
|
header('Content-Type: application/json; charset=UTF-8');
|
|
|
|
$langs->loadLangs(array('kundenkarte@kundenkarte', 'products'));
|
|
|
|
$action = GETPOST('action', 'aZ09');
|
|
$anlageId = GETPOSTINT('anlage_id');
|
|
|
|
$response = array('success' => false, 'error' => '');
|
|
|
|
// Security check
|
|
if (!$user->hasRight('kundenkarte', 'read')) {
|
|
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
echo json_encode($response);
|
|
exit;
|
|
}
|
|
|
|
switch ($action) {
|
|
case 'generate':
|
|
// Generate BOM from all equipment in this installation (anlage)
|
|
if ($anlageId <= 0) {
|
|
$response['error'] = $langs->trans('ErrorRecordNotFound');
|
|
break;
|
|
}
|
|
|
|
// Get all equipment for this anlage through carriers and panels
|
|
$sql = "SELECT e.rowid as equipment_id, e.label as equipment_label, e.width_te, e.fk_product,";
|
|
$sql .= " et.rowid as type_id, et.ref as type_ref, et.label as type_label, et.fk_product as type_product,";
|
|
$sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.price, p.tva_tx,";
|
|
$sql .= " c.label as carrier_label, c.rowid as carrier_id,";
|
|
$sql .= " pan.label as panel_label, pan.rowid as panel_id";
|
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment e";
|
|
$sql .= " JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_carrier c ON e.fk_carrier = c.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_panel pan ON c.fk_panel = pan.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_type et ON e.fk_equipment_type = et.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product p ON COALESCE(e.fk_product, et.fk_product) = p.rowid";
|
|
$sql .= " WHERE (pan.fk_anlage = ".((int) $anlageId)." OR c.fk_anlage = ".((int) $anlageId).")";
|
|
$sql .= " AND e.status = 1";
|
|
$sql .= " ORDER BY pan.position ASC, c.position ASC, e.position_te ASC";
|
|
|
|
$resql = $db->query($sql);
|
|
if (!$resql) {
|
|
$response['error'] = $db->lasterror();
|
|
break;
|
|
}
|
|
|
|
$items = array();
|
|
$summary = array(); // Grouped by product
|
|
|
|
while ($obj = $db->fetch_object($resql)) {
|
|
$item = array(
|
|
'equipment_id' => $obj->equipment_id,
|
|
'equipment_label' => $obj->equipment_label ?: $obj->type_label,
|
|
'type_ref' => $obj->type_ref,
|
|
'type_label' => $obj->type_label,
|
|
'width_te' => $obj->width_te,
|
|
'carrier_label' => $obj->carrier_label,
|
|
'panel_label' => $obj->panel_label,
|
|
'product_id' => $obj->product_id,
|
|
'product_ref' => $obj->product_ref,
|
|
'product_label' => $obj->product_label,
|
|
'price' => $obj->price,
|
|
'tva_tx' => $obj->tva_tx
|
|
);
|
|
$items[] = $item;
|
|
|
|
// Group by product for summary
|
|
if ($obj->product_id) {
|
|
$key = $obj->product_id;
|
|
if (!isset($summary[$key])) {
|
|
$summary[$key] = array(
|
|
'product_id' => $obj->product_id,
|
|
'product_ref' => $obj->product_ref,
|
|
'product_label' => $obj->product_label,
|
|
'price' => $obj->price,
|
|
'tva_tx' => $obj->tva_tx,
|
|
'quantity' => 0,
|
|
'total' => 0
|
|
);
|
|
}
|
|
$summary[$key]['quantity']++;
|
|
$summary[$key]['total'] = $summary[$key]['quantity'] * $summary[$key]['price'];
|
|
} else {
|
|
// Group by type if no product linked
|
|
$key = 'type_'.$obj->type_id;
|
|
if (!isset($summary[$key])) {
|
|
$summary[$key] = array(
|
|
'product_id' => null,
|
|
'product_ref' => $obj->type_ref,
|
|
'product_label' => $obj->type_label.' (kein Produkt)',
|
|
'price' => 0,
|
|
'tva_tx' => 0,
|
|
'quantity' => 0,
|
|
'total' => 0
|
|
);
|
|
}
|
|
$summary[$key]['quantity']++;
|
|
}
|
|
}
|
|
$db->free($resql);
|
|
|
|
// Also include busbar types (connections with is_rail = 1)
|
|
$sql = "SELECT conn.rowid as connection_id, conn.rail_phases, conn.rail_start_te, conn.rail_end_te,";
|
|
$sql .= " bt.rowid as busbar_type_id, bt.ref as busbar_ref, bt.label as busbar_label, bt.fk_product,";
|
|
$sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.price, p.tva_tx,";
|
|
$sql .= " c.label as carrier_label, pan.label as panel_label";
|
|
$sql .= " FROM ".MAIN_DB_PREFIX."kundenkarte_equipment_connection conn";
|
|
$sql .= " JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_carrier c ON conn.fk_carrier = c.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_equipment_panel pan ON c.fk_panel = pan.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."kundenkarte_busbar_type bt ON conn.fk_busbar_type = bt.rowid";
|
|
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product p ON bt.fk_product = p.rowid";
|
|
$sql .= " WHERE (pan.fk_anlage = ".((int) $anlageId)." OR c.fk_anlage = ".((int) $anlageId).")";
|
|
$sql .= " AND conn.is_rail = 1";
|
|
$sql .= " AND conn.status = 1";
|
|
|
|
$resql = $db->query($sql);
|
|
if ($resql) {
|
|
while ($obj = $db->fetch_object($resql)) {
|
|
// Calculate busbar length in TE
|
|
$lengthTE = max(1, intval($obj->rail_end_te) - intval($obj->rail_start_te) + 1);
|
|
|
|
$item = array(
|
|
'equipment_id' => 'busbar_'.$obj->connection_id,
|
|
'equipment_label' => $obj->busbar_label ?: 'Sammelschiene '.$obj->rail_phases,
|
|
'type_ref' => $obj->busbar_ref ?: 'BUSBAR',
|
|
'type_label' => 'Sammelschiene',
|
|
'width_te' => $lengthTE,
|
|
'carrier_label' => $obj->carrier_label,
|
|
'panel_label' => $obj->panel_label,
|
|
'product_id' => $obj->product_id,
|
|
'product_ref' => $obj->product_ref,
|
|
'product_label' => $obj->product_label,
|
|
'price' => $obj->price,
|
|
'tva_tx' => $obj->tva_tx
|
|
);
|
|
$items[] = $item;
|
|
|
|
// Add to summary
|
|
if ($obj->product_id) {
|
|
$key = $obj->product_id;
|
|
if (!isset($summary[$key])) {
|
|
$summary[$key] = array(
|
|
'product_id' => $obj->product_id,
|
|
'product_ref' => $obj->product_ref,
|
|
'product_label' => $obj->product_label,
|
|
'price' => $obj->price,
|
|
'tva_tx' => $obj->tva_tx,
|
|
'quantity' => 0,
|
|
'total' => 0
|
|
);
|
|
}
|
|
$summary[$key]['quantity']++;
|
|
$summary[$key]['total'] = $summary[$key]['quantity'] * $summary[$key]['price'];
|
|
}
|
|
}
|
|
$db->free($resql);
|
|
}
|
|
|
|
// Calculate totals
|
|
$totalQuantity = 0;
|
|
$totalPrice = 0;
|
|
foreach ($summary as $s) {
|
|
$totalQuantity += $s['quantity'];
|
|
$totalPrice += $s['total'];
|
|
}
|
|
|
|
$response['success'] = true;
|
|
$response['items'] = $items;
|
|
$response['summary'] = array_values($summary);
|
|
$response['total_quantity'] = $totalQuantity;
|
|
$response['total_price'] = $totalPrice;
|
|
break;
|
|
|
|
case 'create_order':
|
|
// Create a Dolibarr order from the BOM
|
|
if (!$user->hasRight('kundenkarte', 'write')) {
|
|
$response['error'] = $langs->trans('ErrorPermissionDenied');
|
|
break;
|
|
}
|
|
|
|
$socid = GETPOSTINT('socid');
|
|
$productData = GETPOST('products', 'array');
|
|
|
|
if ($socid <= 0 || empty($productData)) {
|
|
$response['error'] = $langs->trans('ErrorMissingParameters');
|
|
break;
|
|
}
|
|
|
|
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
|
|
|
|
$order = new Commande($db);
|
|
$order->socid = $socid;
|
|
$order->date_commande = dol_now();
|
|
$order->note_private = 'Generiert aus Schaltplan-Stückliste';
|
|
$order->source = 1; // Web
|
|
|
|
$result = $order->create($user);
|
|
if ($result <= 0) {
|
|
$response['error'] = $order->error ?: 'Fehler beim Erstellen der Bestellung';
|
|
break;
|
|
}
|
|
|
|
// Add lines
|
|
$lineErrors = 0;
|
|
foreach ($productData as $prod) {
|
|
$productId = intval($prod['product_id']);
|
|
$qty = floatval($prod['quantity']);
|
|
|
|
if ($productId <= 0 || $qty <= 0) continue;
|
|
|
|
$result = $order->addline(
|
|
'', // Description (auto from product)
|
|
0, // Unit price (auto from product)
|
|
$qty,
|
|
0, // TVA rate (auto)
|
|
0, 0, // Remise
|
|
$productId
|
|
);
|
|
|
|
if ($result < 0) {
|
|
$lineErrors++;
|
|
}
|
|
}
|
|
|
|
$response['success'] = true;
|
|
$response['order_id'] = $order->id;
|
|
$response['order_ref'] = $order->ref;
|
|
$response['line_errors'] = $lineErrors;
|
|
break;
|
|
|
|
default:
|
|
$response['error'] = 'Unknown action';
|
|
}
|
|
|
|
echo json_encode($response);
|