dolibarr.stundenzettel/lib/stundenzettel.lib.php

268 lines
10 KiB
PHP

<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
*
* Stundenzettel - Library functions
*/
/**
* Prepare array of tabs for Stundenzettel card
*
* @param Stundenzettel $object Object
* @return array Array of tabs
*/
function stundenzettel_prepare_head($object)
{
global $db, $langs, $conf, $user;
$langs->load("stundenzettel@stundenzettel");
$h = 0;
$head = array();
// Tab 1: Kundenauftrag (Link zum Auftrag) - immer am Anfang
if ($object->fk_commande > 0) {
$head[$h][0] = DOL_URL_ROOT.'/commande/card.php?id='.$object->fk_commande;
$head[$h][1] = $langs->trans("Order");
$head[$h][2] = 'order';
$h++;
}
// Tab 2: Produktliste (Link zu stundenzettel_commande.php mit Produktliste aus Auftrag)
if ($object->fk_commande > 0) {
$head[$h][0] = dol_buildpath('/stundenzettel/stundenzettel_commande.php', 1).'?id='.$object->fk_commande.'&tab=products&noredirect=1&stundenzettel_id='.$object->id;
$head[$h][1] = $langs->trans("ProductList");
$head[$h][2] = 'productlist';
$h++;
}
// Tab 3: Stundenzettel (Link zum aktiven Stundenzettel - card.php)
$nbLeistungen = 0;
if (!empty($object->leistungen)) {
$nbLeistungen = count($object->leistungen);
}
$nbProducts = 0;
if (!empty($object->products)) {
$nbProducts = count($object->products);
}
$head[$h][0] = dol_buildpath('/stundenzettel/card.php', 1).'?id='.$object->id;
$head[$h][1] = $langs->trans("Stundenzettel");
$totalItems = $nbLeistungen + $nbProducts;
if ($totalItems > 0) {
$head[$h][1] .= '<span class="badge marginleftonlyshort">'.$totalItems.'</span>';
}
$head[$h][2] = 'card';
$h++;
// Tab 4: Alle Stundenzettel (Liste aller Stundenzettel für diesen Auftrag)
if ($object->fk_commande > 0) {
// Anzahl Stundenzettel für diesen Auftrag zählen
$sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."stundenzettel WHERE fk_commande = ".((int)$object->fk_commande);
$resql = $db->query($sql);
$nbStundenzettel = 0;
if ($resql && ($obj = $db->fetch_object($resql))) {
$nbStundenzettel = $obj->nb;
}
$head[$h][0] = dol_buildpath('/stundenzettel/stundenzettel_commande.php', 1).'?id='.$object->fk_commande.'&tab=stundenzettel&noredirect=1&stundenzettel_id='.$object->id;
$head[$h][1] = $langs->trans("StundenzettelList");
if ($nbStundenzettel > 0) {
$head[$h][1] .= '<span class="badge marginleftonlyshort">'.$nbStundenzettel.'</span>';
}
$head[$h][2] = 'stundenzettel_list';
$h++;
}
// Tab 5: Lieferauflistung (Tracking)
if ($object->fk_commande > 0) {
$head[$h][0] = dol_buildpath('/stundenzettel/stundenzettel_commande.php', 1).'?id='.$object->fk_commande.'&tab=tracking&noredirect=1&stundenzettel_id='.$object->id;
$head[$h][1] = $langs->trans("DeliveryTracking");
$head[$h][2] = 'tracking';
$h++;
}
// Tab 6: Notizen
$head[$h][0] = dol_buildpath('/stundenzettel/card.php', 1).'?id='.$object->id.'&tab=notes';
$head[$h][1] = $langs->trans("Notes");
$head[$h][2] = 'notes';
$h++;
return $head;
}
/**
* Prepare array of tabs for Stundenzettel Commande page (order-level view)
* Diese Tabs werden immer angezeigt, unabhängig davon ob ein Stundenzettel ausgewählt ist
*
* @param Commande $order Das Auftrags-Objekt
* @param int $stundenzettel_id Optional: ID des ausgewählten Stundenzettels
* @return array Array of tabs
*/
function stundenzettel_commande_prepare_head($order, $stundenzettel_id = 0)
{
global $db, $langs, $conf, $user;
$langs->load("stundenzettel@stundenzettel");
$h = 0;
$head = array();
// Tab 1: Kundenauftrag (Link zurück zum Auftrag)
$head[$h][0] = DOL_URL_ROOT.'/commande/card.php?id='.$order->id;
$head[$h][1] = $langs->trans("Order");
$head[$h][2] = 'order';
$h++;
// Tab 2: Produktliste
$head[$h][0] = dol_buildpath('/stundenzettel/stundenzettel_commande.php', 1).'?id='.$order->id.'&tab=products&noredirect=1'.($stundenzettel_id > 0 ? '&stundenzettel_id='.$stundenzettel_id : '');
$head[$h][1] = $langs->trans("ProductList");
$head[$h][2] = 'products';
$h++;
// Tab 3: Stundenzettel (aktueller/ausgewählter Stundenzettel - card.php)
// Wenn kein Stundenzettel ausgewählt, den letzten offenen für diesen Auftrag suchen
$activeStundenzettelId = $stundenzettel_id;
if ($activeStundenzettelId <= 0) {
$sqlActive = "SELECT rowid FROM ".MAIN_DB_PREFIX."stundenzettel";
$sqlActive .= " WHERE fk_commande = ".((int)$order->id);
$sqlActive .= " AND status = 0"; // Nur Entwürfe
$sqlActive .= " ORDER BY date_stundenzettel DESC, rowid DESC";
$sqlActive .= " LIMIT 1";
$resqlActive = $db->query($sqlActive);
if ($resqlActive && $db->num_rows($resqlActive) > 0) {
$objActive = $db->fetch_object($resqlActive);
$activeStundenzettelId = $objActive->rowid;
}
}
if ($activeStundenzettelId > 0) {
// Lade Stundenzettel für Badge-Berechnung
require_once dol_buildpath('/stundenzettel/class/stundenzettel.class.php', 0);
$tmpStz = new Stundenzettel($db);
$tmpStz->fetch($activeStundenzettelId);
$tmpStz->fetchLeistungen();
$tmpStz->fetchProducts();
$nbItems = count($tmpStz->leistungen) + count($tmpStz->products);
$head[$h][0] = dol_buildpath('/stundenzettel/card.php', 1).'?id='.$activeStundenzettelId;
$head[$h][1] = $langs->trans("Stundenzettel");
if ($nbItems > 0) {
$head[$h][1] .= '<span class="badge marginleftonlyshort">'.$nbItems.'</span>';
}
$head[$h][2] = 'card';
$h++;
}
// Tab 4: Alle Stundenzettel (Liste aller Stundenzettel für diesen Auftrag)
$sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."stundenzettel WHERE fk_commande = ".((int)$order->id);
$resql = $db->query($sql);
$nbStundenzettel = 0;
if ($resql && ($obj = $db->fetch_object($resql))) {
$nbStundenzettel = $obj->nb;
}
$head[$h][0] = dol_buildpath('/stundenzettel/stundenzettel_commande.php', 1).'?id='.$order->id.'&tab=stundenzettel&noredirect=1'.($stundenzettel_id > 0 ? '&stundenzettel_id='.$stundenzettel_id : '');
$head[$h][1] = $langs->trans("StundenzettelList");
if ($nbStundenzettel > 0) {
$head[$h][1] .= '<span class="badge marginleftonlyshort">'.$nbStundenzettel.'</span>';
}
$head[$h][2] = 'stundenzettel';
$h++;
// Tab 4: Lieferauflistung (Tracking)
$head[$h][0] = dol_buildpath('/stundenzettel/stundenzettel_commande.php', 1).'?id='.$order->id.'&tab=tracking&noredirect=1'.($stundenzettel_id > 0 ? '&stundenzettel_id='.$stundenzettel_id : '');
$head[$h][1] = $langs->trans("DeliveryTracking");
$head[$h][2] = 'tracking';
$h++;
return $head;
}
/**
* Holt den kundenspezifischen Preis für ein Produkt
* Falls kein kundenspezifischer Preis existiert, wird der Standardpreis zurückgegeben
*
* @param DoliDB $db Datenbankverbindung
* @param int $fk_product Produkt-ID
* @param int $fk_soc Kunden-ID (Societe)
* @param Product|null $product Optional: bereits geladenes Produkt-Objekt
* @return array Array mit 'price' (HT), 'price_ttc', 'tva_tx', 'price_base_type', 'is_customer_price'
*/
function getCustomerPrice($db, $fk_product, $fk_soc, $product = null) {
global $conf;
$now = dol_now();
// Suche kundenspezifischen Preis in der Tabelle product_customer_price
$sql = "SELECT price, price_ttc, tva_tx, price_base_type";
$sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price";
$sql .= " WHERE fk_product = ".((int)$fk_product);
$sql .= " AND fk_soc = ".((int)$fk_soc);
$sql .= " AND entity IN (".getEntity('productprice').")";
// Prüfe Gültigkeitszeitraum (date_begin <= now und (date_end IS NULL oder date_end >= now))
$sql .= " AND date_begin <= '".$db->idate($now)."'";
$sql .= " AND (date_end IS NULL OR date_end >= '".$db->idate($now)."')";
$sql .= " ORDER BY date_begin DESC";
$sql .= " LIMIT 1";
$resql = $db->query($sql);
if ($resql && $db->num_rows($resql) > 0) {
$obj = $db->fetch_object($resql);
return array(
'price' => (float)$obj->price,
'price_ttc' => (float)$obj->price_ttc,
'tva_tx' => (float)$obj->tva_tx,
'price_base_type' => $obj->price_base_type,
'is_customer_price' => true
);
}
// Kein kundenspezifischer Preis gefunden - lade Standardpreis vom Produkt
if ($product === null) {
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
$product = new Product($db);
$product->fetch($fk_product);
}
return array(
'price' => (float)$product->price,
'price_ttc' => (float)$product->price_ttc,
'tva_tx' => (float)$product->tva_tx,
'price_base_type' => $product->price_base_type,
'is_customer_price' => false
);
}
/**
* Holt den effektiven Stundenpreis für einen Stundenzettel
* Berücksichtigt: 1. Manuell gesetzter Preis im Stundenzettel
* 2. Kundenspezifischer Preis
* 3. Standard-Produktpreis
*
* @param DoliDB $db Datenbankverbindung
* @param Stundenzettel $stundenzettel Das Stundenzettel-Objekt
* @param int $defaultServiceId ID der Standard-Leistung
* @return array Array mit 'price', 'source' ('custom', 'customer', 'standard')
*/
function getEffectiveHourlyRate($db, $stundenzettel, $defaultServiceId) {
// 1. Prüfe ob manueller Preis im Stundenzettel gesetzt
if ($stundenzettel->hourly_rate_is_custom && $stundenzettel->hourly_rate !== null) {
return array(
'price' => (float)$stundenzettel->hourly_rate,
'source' => 'custom'
);
}
// 2. Hole kundenspezifischen oder Standard-Preis
if ($defaultServiceId > 0) {
$priceInfo = getCustomerPrice($db, $defaultServiceId, $stundenzettel->fk_soc);
return array(
'price' => $priceInfo['price'],
'source' => $priceInfo['is_customer_price'] ? 'customer' : 'standard'
);
}
// 3. Kein Preis verfügbar
return array(
'price' => 0,
'source' => 'none'
);
}