db = $db; } /** * Create object in database * * @param User $user User that creates * @param bool $notrigger false=launch triggers, true=disable triggers * @return int Return integer <0 if KO, Id of created object if OK */ public function create($user, $notrigger = false) { global $conf; $error = 0; $now = dol_now(); // Check parameters if (empty($this->fk_soc) || empty($this->fk_product)) { $this->error = 'ErrorMissingParameters'; return -1; } // Check if already exists if ($this->alreadyExists($this->fk_soc, $this->fk_product, $this->fk_contact)) { $this->error = 'ErrorRecordAlreadyExists'; return -2; } $this->db->begin(); // Get max rang for this customer/contact to add at end of list $maxRang = 0; $sqlRang = "SELECT MAX(rang) as maxrang FROM ".MAIN_DB_PREFIX.$this->table_element; $sqlRang .= " WHERE fk_soc = ".((int) $this->fk_soc); if ($this->fk_contact > 0) { $sqlRang .= " AND fk_contact = ".((int) $this->fk_contact); } else { $sqlRang .= " AND (fk_contact IS NULL OR fk_contact = 0)"; } $sqlRang .= " AND entity = ".((int) $conf->entity); $resRang = $this->db->query($sqlRang); if ($resRang) { $objRang = $this->db->fetch_object($resRang); $maxRang = ($objRang->maxrang !== null) ? ((int) $objRang->maxrang + 1) : 0; } $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." ("; $sql .= "entity, fk_soc, fk_contact, fk_product, qty, rang, note, active, date_creation, fk_user_creat"; $sql .= ") VALUES ("; $sql .= ((int) $conf->entity); $sql .= ", ".((int) $this->fk_soc); $sql .= ", ".($this->fk_contact > 0 ? ((int) $this->fk_contact) : "NULL"); $sql .= ", ".((int) $this->fk_product); $sql .= ", ".((float) ($this->qty > 0 ? $this->qty : 1)); $sql .= ", ".((int) $maxRang); $sql .= ", ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL"); $sql .= ", ".((int) ($this->active !== null ? $this->active : 1)); $sql .= ", '".$this->db->idate($now)."'"; $sql .= ", ".((int) $user->id); $sql .= ")"; $resql = $this->db->query($sql); if (!$resql) { $error++; $this->errors[] = "Error ".$this->db->lasterror(); } if (!$error) { $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element); $this->date_creation = $now; $this->fk_user_creat = $user->id; } if ($error) { $this->db->rollback(); return -1 * $error; } else { $this->db->commit(); return $this->id; } } /** * Load object from database * * @param int $id ID of record * @return int Return integer <0 if KO, 0 if not found, >0 if OK */ public function fetch($id) { global $conf; $sql = "SELECT rowid, entity, fk_soc, fk_contact, fk_product, qty, rang, note, active,"; $sql .= " date_creation, tms, fk_user_creat, fk_user_modif"; $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element; $sql .= " WHERE rowid = ".((int) $id); $sql .= " AND entity = ".((int) $conf->entity); $resql = $this->db->query($sql); if ($resql) { if ($this->db->num_rows($resql)) { $obj = $this->db->fetch_object($resql); $this->id = $obj->rowid; $this->entity = $obj->entity; $this->fk_soc = $obj->fk_soc; $this->fk_contact = $obj->fk_contact; $this->fk_product = $obj->fk_product; $this->qty = $obj->qty; $this->rang = $obj->rang; $this->note = $obj->note; $this->active = $obj->active; $this->date_creation = $this->db->jdate($obj->date_creation); $this->tms = $this->db->jdate($obj->tms); $this->fk_user_creat = $obj->fk_user_creat; $this->fk_user_modif = $obj->fk_user_modif; $this->db->free($resql); return 1; } else { $this->db->free($resql); return 0; } } else { $this->error = $this->db->lasterror(); return -1; } } /** * Update object in database * * @param User $user User that modifies * @param bool $notrigger false=launch triggers, true=disable triggers * @return int Return integer <0 if KO, >0 if OK */ public function update($user, $notrigger = false) { $error = 0; $this->db->begin(); $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET"; $sql .= " qty = ".((float) $this->qty); $sql .= ", rang = ".((int) $this->rang); $sql .= ", note = ".($this->note ? "'".$this->db->escape($this->note)."'" : "NULL"); $sql .= ", active = ".((int) $this->active); $sql .= ", fk_user_modif = ".((int) $user->id); $sql .= " WHERE rowid = ".((int) $this->id); $resql = $this->db->query($sql); if (!$resql) { $error++; $this->errors[] = "Error ".$this->db->lasterror(); } if ($error) { $this->db->rollback(); return -1 * $error; } else { $this->db->commit(); return 1; } } /** * Delete object in database * * @param User $user User that deletes * @param bool $notrigger false=launch triggers, true=disable triggers * @return int Return integer <0 if KO, >0 if OK */ public function delete($user, $notrigger = false) { $error = 0; $this->db->begin(); $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element; $sql .= " WHERE rowid = ".((int) $this->id); $resql = $this->db->query($sql); if (!$resql) { $error++; $this->errors[] = "Error ".$this->db->lasterror(); } if ($error) { $this->db->rollback(); return -1 * $error; } else { $this->db->commit(); return 1; } } /** * Check if a product is already a favorite for a customer/contact * * @param int $socid Customer ID * @param int $productid Product ID * @param int $contactid Contact ID (optional) * @return bool */ public function alreadyExists($socid, $productid, $contactid = 0) { global $conf; $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$this->table_element; $sql .= " WHERE fk_soc = ".((int) $socid); $sql .= " AND fk_product = ".((int) $productid); if ($contactid > 0) { $sql .= " AND fk_contact = ".((int) $contactid); } else { $sql .= " AND (fk_contact IS NULL OR fk_contact = 0)"; } $sql .= " AND entity = ".((int) $conf->entity); $resql = $this->db->query($sql); if ($resql) { return ($this->db->num_rows($resql) > 0); } return false; } /** * Get all favorite products for a customer * * @param int $socid Customer ID * @param int $activeonly Only active favorites * @return array|int Array of FavoriteProduct objects or -1 if error */ public function fetchAllBySociete($socid, $activeonly = 1) { global $conf; $results = array(); $sql = "SELECT fp.rowid, fp.fk_soc, fp.fk_product, fp.qty, fp.rang, fp.note, fp.active,"; $sql .= " p.ref as product_ref, p.label as product_label, p.price, p.price_ttc, p.tva_tx"; $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as fp"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON fp.fk_product = p.rowid"; $sql .= " WHERE fp.fk_soc = ".((int) $socid); // Only thirdparty-level favorites, not contact-specific $sql .= " AND (fp.fk_contact IS NULL OR fp.fk_contact = 0)"; $sql .= " AND fp.entity = ".((int) $conf->entity); if ($activeonly) { $sql .= " AND fp.active = 1"; } $sql .= " ORDER BY fp.rang ASC, p.label ASC"; $resql = $this->db->query($sql); if ($resql) { while ($obj = $this->db->fetch_object($resql)) { $fav = new FavoriteProduct($this->db); $fav->id = $obj->rowid; $fav->fk_soc = $obj->fk_soc; $fav->fk_product = $obj->fk_product; $fav->qty = $obj->qty; $fav->rang = $obj->rang; $fav->note = $obj->note; $fav->active = $obj->active; // Product info $fav->product_ref = $obj->product_ref; $fav->product_label = $obj->product_label; $fav->product_price = $obj->price; $fav->product_price_ttc = $obj->price_ttc; $fav->product_tva_tx = $obj->tva_tx; $results[] = $fav; } $this->db->free($resql); return $results; } else { $this->error = $this->db->lasterror(); return -1; } } /** * Move a favorite product up in the list * * @param int $id Favorite ID to move * @param int $socid Customer ID * @return int 1 if OK, <0 if KO */ public function moveUp($id, $socid) { return $this->movePosition($id, $socid, 'up'); } /** * Move a favorite product down in the list * * @param int $id Favorite ID to move * @param int $socid Customer ID * @return int 1 if OK, <0 if KO */ public function moveDown($id, $socid) { return $this->movePosition($id, $socid, 'down'); } /** * Move a favorite product position * * @param int $id Favorite ID to move * @param int $socid Customer ID * @param string $direction 'up' or 'down' * @return int 1 if OK, <0 if KO */ private function movePosition($id, $socid, $direction) { global $conf; // Get all favorites ordered by rang $sql = "SELECT rowid, rang FROM ".MAIN_DB_PREFIX.$this->table_element; $sql .= " WHERE fk_soc = ".((int) $socid); $sql .= " AND entity = ".((int) $conf->entity); $sql .= " ORDER BY rang ASC, rowid ASC"; $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return -1; } $items = array(); $currentIndex = -1; $i = 0; while ($obj = $this->db->fetch_object($resql)) { $items[$i] = array('id' => $obj->rowid, 'rang' => $i); if ($obj->rowid == $id) { $currentIndex = $i; } $i++; } $this->db->free($resql); if ($currentIndex < 0) { return 0; // Item not found } // Calculate new positions $swapIndex = -1; if ($direction == 'up' && $currentIndex > 0) { $swapIndex = $currentIndex - 1; } elseif ($direction == 'down' && $currentIndex < count($items) - 1) { $swapIndex = $currentIndex + 1; } if ($swapIndex < 0) { return 0; // Cannot move } // Swap positions $this->db->begin(); $sql1 = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET rang = ".((int) $swapIndex); $sql1 .= " WHERE rowid = ".((int) $items[$currentIndex]['id']); $sql2 = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET rang = ".((int) $currentIndex); $sql2 .= " WHERE rowid = ".((int) $items[$swapIndex]['id']); if ($this->db->query($sql1) && $this->db->query($sql2)) { $this->db->commit(); return 1; } else { $this->error = $this->db->lasterror(); $this->db->rollback(); return -1; } } /** * Generate an order from selected favorite products * * @param User $user User creating the order * @param int $socid Customer ID * @param array $selectedIds Array of favorite product IDs to include * @param array $quantities Optional array of quantities (id => qty) * @return int Order ID if OK, <0 if KO */ public function generateOrder($user, $socid, $selectedIds, $quantities = array()) { global $conf, $langs, $mysoc; require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; if (empty($selectedIds)) { $this->error = 'NoProductsSelected'; return -1; } // Load thirdparty (required for VAT calculation) $societe = new Societe($this->db); if ($societe->fetch($socid) <= 0) { $this->error = 'ErrorLoadingThirdparty'; return -1; } // Fetch selected favorites $favorites = $this->fetchAllBySociete($socid); if (!is_array($favorites)) { return -1; } // Filter to selected only $toAdd = array(); foreach ($favorites as $fav) { if (in_array($fav->id, $selectedIds)) { $qty = isset($quantities[$fav->id]) ? (float) $quantities[$fav->id] : $fav->qty; if ($qty > 0) { $toAdd[] = array( 'product_id' => $fav->fk_product, 'qty' => $qty ); } } } if (empty($toAdd)) { $this->error = 'NoValidProductsToAdd'; return -2; } // Create order $order = new Commande($this->db); $order->socid = $socid; $order->thirdparty = $societe; // Required for VAT calculation $order->date = dol_now(); $order->ref_client = $societe->name.' - '.$langs->trans('FavoriteProducts'); // Ihr Zeichen $order->note_private = $langs->trans('OrderGeneratedFromFavorites'); $this->db->begin(); // First create the order header $result = $order->create($user); if ($result <= 0) { $this->error = $order->error; $this->errors = $order->errors; $this->db->rollback(); return -3; } // Now add products to the created order foreach ($toAdd as $item) { $product = new Product($this->db); $product->fetch($item['product_id']); // Get VAT rate for this product and customer (mysoc = seller, societe = buyer) $tva_tx = get_default_tva($mysoc, $societe, $product->id); $localtax1_tx = get_default_localtax($mysoc, $societe, 1, $product->id); $localtax2_tx = get_default_localtax($mysoc, $societe, 2, $product->id); $lineResult = $order->addline( $product->label, // Description $product->price, // Unit price HT $item['qty'], // Quantity $tva_tx, // VAT rate $localtax1_tx, // Local tax 1 $localtax2_tx, // Local tax 2 $product->id, // Product ID 0, // Discount 0, // Info bits 0, // fk_remise_except 'HT', // Price base type 0, // Unit price TTC '', // Date start '', // Date end 0, // Type (0=product) -1, // Rang 0, // Special code 0, // fk_parent_line 0, // fk_fournprice 0, // pa_ht $product->label, // Label array(), // Array options $product->fk_unit // Unit ); if ($lineResult < 0) { $this->error = $order->error; $this->errors = $order->errors; $this->db->rollback(); return -4; } } $this->db->commit(); return $order->id; } /** * Get all favorite products for a contact/address * * @param int $contactid Contact ID * @param int $activeonly Only active favorites * @return array|int Array of FavoriteProduct objects or -1 if error */ public function fetchAllByContact($contactid, $activeonly = 1) { global $conf; $results = array(); $sql = "SELECT fp.rowid, fp.fk_soc, fp.fk_contact, fp.fk_product, fp.qty, fp.rang, fp.note, fp.active,"; $sql .= " p.ref as product_ref, p.label as product_label, p.price, p.price_ttc, p.tva_tx"; $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as fp"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON fp.fk_product = p.rowid"; $sql .= " WHERE fp.fk_contact = ".((int) $contactid); $sql .= " AND fp.entity = ".((int) $conf->entity); if ($activeonly) { $sql .= " AND fp.active = 1"; } $sql .= " ORDER BY fp.rang ASC, p.label ASC"; $resql = $this->db->query($sql); if ($resql) { while ($obj = $this->db->fetch_object($resql)) { $fav = new FavoriteProduct($this->db); $fav->id = $obj->rowid; $fav->fk_soc = $obj->fk_soc; $fav->fk_contact = $obj->fk_contact; $fav->fk_product = $obj->fk_product; $fav->qty = $obj->qty; $fav->rang = $obj->rang; $fav->note = $obj->note; $fav->active = $obj->active; // Product info $fav->product_ref = $obj->product_ref; $fav->product_label = $obj->product_label; $fav->product_price = $obj->price; $fav->product_price_ttc = $obj->price_ttc; $fav->product_tva_tx = $obj->tva_tx; $results[] = $fav; } $this->db->free($resql); return $results; } else { $this->error = $this->db->lasterror(); return -1; } } /** * Move a favorite product up in the list (for contact) * * @param int $id Favorite ID to move * @param int $contactid Contact ID * @return int 1 if OK, <0 if KO */ public function moveUpByContact($id, $contactid) { return $this->movePositionByContact($id, $contactid, 'up'); } /** * Move a favorite product down in the list (for contact) * * @param int $id Favorite ID to move * @param int $contactid Contact ID * @return int 1 if OK, <0 if KO */ public function moveDownByContact($id, $contactid) { return $this->movePositionByContact($id, $contactid, 'down'); } /** * Move a favorite product position (for contact) * * @param int $id Favorite ID to move * @param int $contactid Contact ID * @param string $direction 'up' or 'down' * @return int 1 if OK, <0 if KO */ private function movePositionByContact($id, $contactid, $direction) { global $conf; $sql = "SELECT rowid, rang FROM ".MAIN_DB_PREFIX.$this->table_element; $sql .= " WHERE fk_contact = ".((int) $contactid); $sql .= " AND entity = ".((int) $conf->entity); $sql .= " ORDER BY rang ASC, rowid ASC"; $resql = $this->db->query($sql); if (!$resql) { $this->error = $this->db->lasterror(); return -1; } $items = array(); $currentIndex = -1; $i = 0; while ($obj = $this->db->fetch_object($resql)) { $items[$i] = array('id' => $obj->rowid, 'rang' => $i); if ($obj->rowid == $id) { $currentIndex = $i; } $i++; } $this->db->free($resql); if ($currentIndex < 0) { return 0; } $swapIndex = -1; if ($direction == 'up' && $currentIndex > 0) { $swapIndex = $currentIndex - 1; } elseif ($direction == 'down' && $currentIndex < count($items) - 1) { $swapIndex = $currentIndex + 1; } if ($swapIndex < 0) { return 0; } $this->db->begin(); $sql1 = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET rang = ".((int) $swapIndex); $sql1 .= " WHERE rowid = ".((int) $items[$currentIndex]['id']); $sql2 = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET rang = ".((int) $currentIndex); $sql2 .= " WHERE rowid = ".((int) $items[$swapIndex]['id']); if ($this->db->query($sql1) && $this->db->query($sql2)) { $this->db->commit(); return 1; } else { $this->error = $this->db->lasterror(); $this->db->rollback(); return -1; } } /** * Generate an order from selected favorite products (for contact) * * @param User $user User creating the order * @param int $socid Customer ID * @param int $contactid Contact ID * @param array $selectedIds Array of favorite product IDs to include * @param array $quantities Optional array of quantities (id => qty) * @return int Order ID if OK, <0 if KO */ public function generateOrderByContact($user, $socid, $contactid, $selectedIds, $quantities = array()) { global $conf, $langs, $mysoc; require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; if (empty($selectedIds)) { $this->error = 'NoProductsSelected'; return -1; } // Load thirdparty $societe = new Societe($this->db); if ($societe->fetch($socid) <= 0) { $this->error = 'ErrorLoadingThirdparty'; return -1; } // Load contact $contact = new Contact($this->db); if ($contact->fetch($contactid) <= 0) { $this->error = 'ErrorLoadingContact'; return -1; } // Fetch selected favorites $favorites = $this->fetchAllByContact($contactid); if (!is_array($favorites)) { return -1; } // Filter to selected only $toAdd = array(); foreach ($favorites as $fav) { if (in_array($fav->id, $selectedIds)) { $qty = isset($quantities[$fav->id]) ? (float) $quantities[$fav->id] : $fav->qty; if ($qty > 0) { $toAdd[] = array( 'product_id' => $fav->fk_product, 'qty' => $qty ); } } } if (empty($toAdd)) { $this->error = 'NoValidProductsToAdd'; return -2; } // Create order $order = new Commande($this->db); $order->socid = $socid; $order->thirdparty = $societe; $order->date = dol_now(); // Ihr Zeichen: Kunde - Kontakt/Adresse - Favoriten $order->ref_client = $societe->name.' - '.$contact->getFullName($langs).' - '.$langs->trans('FavoriteProducts'); $order->note_private = $langs->trans('OrderGeneratedFromFavorites'); $this->db->begin(); $result = $order->create($user); if ($result <= 0) { $this->error = $order->error; $this->errors = $order->errors; $this->db->rollback(); return -3; } // Add products foreach ($toAdd as $item) { $product = new Product($this->db); $product->fetch($item['product_id']); $tva_tx = get_default_tva($mysoc, $societe, $product->id); $localtax1_tx = get_default_localtax($mysoc, $societe, 1, $product->id); $localtax2_tx = get_default_localtax($mysoc, $societe, 2, $product->id); $lineResult = $order->addline( $product->label, $product->price, $item['qty'], $tva_tx, $localtax1_tx, $localtax2_tx, $product->id, 0, 0, 0, 'HT', 0, '', '', 0, -1, 0, 0, 0, 0, $product->label, array(), $product->fk_unit ); if ($lineResult < 0) { $this->error = $order->error; $this->errors = $order->errors; $this->db->rollback(); return -4; } } $this->db->commit(); return $order->id; } }