dolibarr  19.0.0-dev
card.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2020 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005 Eric Seigne <eric.seigne@ryxeo.com>
5  * Copyright (C) 2005-2018 Regis Houssin <regis.houssin@inodbox.com>
6  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
7  * Copyright (C) 2011-2014 Juanjo Menent <jmenent@2byte.es>
8  * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
9  * Copyright (C) 2023 Benjamin Falière <benjamin.faliere@altairis.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <https://www.gnu.org/licenses/>.
23  */
24 
31 // Load Dolibarr environment
32 require '../../main.inc.php';
33 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
34 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
35 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
36 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
37 
38 // Load translation files required by the page
39 $langs->loadLangs(array('bills', 'products', 'stocks'));
40 
41 $id = GETPOST('id', 'int');
42 $ref = GETPOST('ref', 'alpha');
43 $action = GETPOST('action', 'aZ09');
44 $confirm = GETPOST('confirm', 'alpha');
45 $cancel = GETPOST('cancel', 'alpha');
46 $key = GETPOST('key');
47 $parent = GETPOST('parent');
48 
49 // Security check
50 if (!empty($user->socid)) {
51  $socid = $user->socid;
52 }
53 $fieldvalue = (!empty($id) ? $id : (!empty($ref) ? $ref : ''));
54 $fieldtype = (!empty($ref) ? 'ref' : 'rowid');
55 
56 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
57 $hookmanager->initHooks(array('productcompositioncard', 'globalcard'));
58 
59 $object = new Product($db);
60 $objectid = 0;
61 if ($id > 0 || !empty($ref)) {
62  $result = $object->fetch($id, $ref);
63  $objectid = $object->id;
64  $id = $object->id;
65 }
66 
67 $result = restrictedArea($user, 'produit|service', $fieldvalue, 'product&product', '', '', $fieldtype);
68 
69 if ($object->id > 0) {
70  if ($object->type == $object::TYPE_PRODUCT) {
71  restrictedArea($user, 'produit', $object->id, 'product&product', '', '');
72  }
73  if ($object->type == $object::TYPE_SERVICE) {
74  restrictedArea($user, 'service', $object->id, 'product&product', '', '');
75  }
76 } else {
77  restrictedArea($user, 'produit|service', $fieldvalue, 'product&product', '', '', $fieldtype);
78 }
79 $usercanread = (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->lire) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'lire')));
80 $usercancreate = (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->creer) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'creer')));
81 $usercandelete = (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->supprimer) || ($object->type == Product::TYPE_SERVICE && $user->rights->service->supprimer));
82 
83 
84 /*
85  * Actions
86  */
87 
88 if ($cancel) {
89  $action = '';
90 }
91 
92 $reshook = $hookmanager->executeHooks('doActions', [], $object, $action); // Note that $action and $object may have been modified by some hooks
93 if ($reshook < 0) {
94  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
95 }
96 
97 if (empty($reshook)) {
98  // Add subproduct to product
99  if ($action == 'add_prod' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
100  $error = 0;
101  $maxprod = GETPOST("max_prod", 'int');
102 
103  for ($i = 0; $i < $maxprod; $i++) {
104  $qty = price2num(GETPOST("prod_qty_" . $i, 'alpha'), 'MS');
105  if ($qty > 0) {
106  if ($object->add_sousproduit($id, GETPOST("prod_id_" . $i, 'int'), $qty, GETPOST("prod_incdec_" . $i, 'int')) > 0) {
107  //var_dump($i.' '.GETPOST("prod_id_".$i, 'int'), $qty, GETPOST("prod_incdec_".$i, 'int'));
108  $action = 'edit';
109  } else {
110  $error++;
111  $action = 're-edit';
112  if ($object->error == "isFatherOfThis") {
113  setEventMessages($langs->trans("ErrorAssociationIsFatherOfThis"), null, 'errors');
114  } else {
115  setEventMessages($object->error, $object->errors, 'errors');
116  }
117  }
118  } else {
119  if ($object->del_sousproduit($id, GETPOST("prod_id_" . $i, 'int')) > 0) {
120  $action = 'edit';
121  } else {
122  $error++;
123  $action = 're-edit';
124  setEventMessages($object->error, $object->errors, 'errors');
125  }
126  }
127  }
128 
129  if (!$error) {
130  header("Location: " . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
131  exit;
132  }
133  } elseif ($action === 'save_composed_product') {
134  $TProduct = GETPOST('TProduct', 'array');
135  if (!empty($TProduct)) {
136  foreach ($TProduct as $id_product => $row) {
137  if ($row['qty'] > 0) {
138  $object->update_sousproduit($id, $id_product, $row['qty'], isset($row['incdec']) ? 1 : 0);
139  } else {
140  $object->del_sousproduit($id, $id_product);
141  }
142  }
143  setEventMessages('RecordSaved', null);
144  }
145  $action = '';
146  header("Location: " . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
147  exit;
148  }
149 }
150 
151 /*
152  * View
153  */
154 
155 $form = new Form($db);
156 $formproduct = new FormProduct($db);
157 $product_fourn = new ProductFournisseur($db);
158 $productstatic = new Product($db);
159 
160 // action recherche des produits par mot-cle et/ou par categorie
161 if ($action == 'search') {
162  $current_lang = $langs->getDefaultLang();
163 
164  $sql = 'SELECT DISTINCT p.rowid, p.ref, p.label, p.fk_product_type as type, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
165  $sql .= ' p.fk_product_type, p.tms as datem, p.tobatch';
166  $sql .= ', p.tosell as status, p.tobuy as status_buy';
167  if (getDolGlobalInt('MAIN_MULTILANGS')) {
168  $sql .= ', pl.label as labelm, pl.description as descriptionm';
169  }
170 
171  $parameters = array();
172  $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook
173  $sql .= $hookmanager->resPrint;
174 
175  $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
176  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_product as cp ON p.rowid = cp.fk_product';
177  if (getDolGlobalInt('MAIN_MULTILANGS')) {
178  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND lang='".($current_lang)."'";
179  }
180  $sql .= ' WHERE p.entity IN ('.getEntity('product').')';
181 
182  $parameters = array();
183  $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
184  $sql .= $hookmanager->resPrint;
185 
186  if ($key != "") {
187  // For natural search
188  $params = array('p.ref', 'p.label', 'p.description', 'p.note');
189  // multilang
190  if (getDolGlobalInt('MAIN_MULTILANGS')) {
191  $params[] = 'pl.label';
192  $params[] = 'pl.description';
193  $params[] = 'pl.note';
194  }
195  if (isModEnabled('barcode')) {
196  $params[] = 'p.barcode';
197  }
198  $sql .= natural_search($params, $key);
199  }
200  if (isModEnabled('categorie') && !empty($parent) && $parent != -1) {
201  $sql .= " AND cp.fk_categorie ='".$db->escape($parent)."'";
202  }
203  $sql .= " ORDER BY p.ref ASC";
204 
205  $resql = $db->query($sql);
206 }
207 
208 $title = $langs->trans('ProductServiceCard');
209 $help_url = '';
210 $shortlabel = dol_trunc($object->label, 16);
211 if (GETPOST("type") == '0' || ($object->type == Product::TYPE_PRODUCT)) {
212  $title = $langs->trans('Product')." ".$shortlabel." - ".$langs->trans('AssociatedProducts');
213  $help_url = 'EN:Module_Products|FR:Module_Produits|ES:M&oacute;dulo_Productos|DE:Modul_Produkte';
214 }
215 if (GETPOST("type") == '1' || ($object->type == Product::TYPE_SERVICE)) {
216  $title = $langs->trans('Service')." ".$shortlabel." - ".$langs->trans('AssociatedProducts');
217  $help_url = 'EN:Module_Services_En|FR:Module_Services|ES:M&oacute;dulo_Servicios|DE:Modul_Leistungen';
218 }
219 
220 llxHeader('', $title, $help_url);
221 
222 $head = product_prepare_head($object);
223 
224 $titre = $langs->trans("CardProduct".$object->type);
225 $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
226 
227 print dol_get_fiche_head($head, 'subproduct', $titre, -1, $picto);
228 
229 
230 if ($id > 0 || !empty($ref)) {
231  /*
232  * Product card
233  */
234  if ($user->rights->produit->lire || $user->hasRight('service', 'lire')) {
235  $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
236 
237  $shownav = 1;
238  if ($user->socid && !in_array('product', explode(',', $conf->global->MAIN_MODULES_FOR_EXTERNAL))) {
239  $shownav = 0;
240  }
241 
242  dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref', '');
243 
244  if ($object->type != Product::TYPE_SERVICE || !empty($conf->global->STOCK_SUPPORTS_SERVICES) || empty($conf->global->PRODUIT_MULTIPRICES)) {
245  print '<div class="fichecenter">';
246  print '<div class="fichehalfleft">';
247  print '<div class="underbanner clearboth"></div>';
248 
249  print '<table class="border centpercent tableforfield">';
250 
251  // Type
252  if (isModEnabled("product") && isModEnabled("service")) {
253  $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
254  print '<tr><td class="titlefield">';
255  print (empty($conf->global->PRODUCT_DENY_CHANGE_PRODUCT_TYPE)) ? $form->editfieldkey("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat) : $langs->trans('Type');
256  print '</td><td>';
257  print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat);
258  print '</td></tr>';
259  }
260 
261  print '</table>';
262 
263  print '</div><div class="fichehalfright">';
264  print '<div class="underbanner clearboth"></div>';
265 
266  print '<table class="border centpercent tableforfield">';
267 
268  // Nature
269  if ($object->type != Product::TYPE_SERVICE) {
270  if (empty($conf->global->PRODUCT_DISABLE_NATURE)) {
271  print '<tr><td>'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
272  print $object->getLibFinished();
273  //print $formproduct->selectProductNature('finished', $object->finished);
274  print '</td></tr>';
275  }
276  }
277 
278  if (empty($conf->global->PRODUIT_MULTIPRICES)) {
279  // Price
280  print '<tr><td class="titlefield">'.$langs->trans("SellingPrice").'</td><td>';
281  if ($object->price_base_type == 'TTC') {
282  print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
283  } else {
284  print price($object->price).' '.$langs->trans($object->price_base_type ? $object->price_base_type : 'HT');
285  }
286  print '</td></tr>';
287 
288  // Price minimum
289  print '<tr><td>'.$langs->trans("MinPrice").'</td><td>';
290  if ($object->price_base_type == 'TTC') {
291  print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
292  } else {
293  print price($object->price_min).' '.$langs->trans($object->price_base_type ? $object->price_base_type : 'HT');
294  }
295  print '</td></tr>';
296  }
297 
298  print '</table>';
299  print '</div>';
300  print '</div>';
301  }
302 
303  print dol_get_fiche_end();
304 
305 
306  print '<br><br>';
307 
308  $prodsfather = $object->getFather(); // Parent Products
309  $object->get_sousproduits_arbo(); // Load $object->sousprods
310  $parent_label = $object->label;
311  $prods_arbo = $object->get_arbo_each_prod();
312 
313  $tmpid = $id;
314  if (!empty($conf->use_javascript_ajax)) {
315  $nboflines = $prods_arbo;
316  $table_element_line='product_association';
317 
318  include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
319  }
320  $id = $tmpid;
321 
322  $nbofsubsubproducts = count($prods_arbo); // This include sub sub product into nb
323  $prodschild = $object->getChildsArbo($id, 1);
324  $nbofsubproducts = count($prodschild); // This include only first level of childs
325 
326 
327  print '<div class="fichecenter">';
328 
329  print load_fiche_titre($langs->trans("ProductParentList"), '', '');
330 
331  print '<table class="liste">';
332  print '<tr class="liste_titre">';
333  print '<td>'.$langs->trans('ParentProducts').'</td>';
334  print '<td>'.$langs->trans('Label').'</td>';
335  print '<td class="right">'.$langs->trans('Qty').'</td>';
336  print '</td>';
337  if (count($prodsfather) > 0) {
338  foreach ($prodsfather as $value) {
339  $idprod = $value["id"];
340  $productstatic->id = $idprod; // $value["id"];
341  $productstatic->type = $value["fk_product_type"];
342  $productstatic->ref = $value['ref'];
343  $productstatic->label = $value['label'];
344  $productstatic->entity = $value['entity'];
345  $productstatic->status = $value['status'];
346  $productstatic->status_buy = $value['status_buy'];
347 
348  print '<tr class="oddeven">';
349  print '<td>'.$productstatic->getNomUrl(1, 'composition').'</td>';
350  print '<td>'.dol_escape_htmltag($productstatic->label).'</td>';
351  print '<td class="right">'.dol_escape_htmltag($value['qty']).'</td>';
352  print '</tr>';
353  }
354  } else {
355  print '<tr class="oddeven">';
356  print '<td colspan="3"><span class="opacitymedium">'.$langs->trans("None").'</span></td>';
357  print '</tr>';
358  }
359  print '</table>';
360  print '</div>';
361 
362  print '<br>'."\n";
363 
364 
365  print '<div class="fichecenter">';
366 
367  $atleastonenotdefined = 0;
368  print load_fiche_titre($langs->trans("ProductAssociationList"), '', '');
369 
370  print '<form name="formComposedProduct" action="'.$_SERVER['PHP_SELF'].'" method="post">';
371  print '<input type="hidden" name="token" value="'.newToken().'" />';
372  print '<input type="hidden" name="action" value="save_composed_product" />';
373  print '<input type="hidden" name="id" value="'.$id.'" />';
374 
375  print '<table id="tablelines" class="ui-sortable liste nobottom">';
376 
377  print '<tr class="liste_titre nodrag nodrop">';
378  // Rank
379  print '<td>'.$langs->trans('Position').'</td>';
380  // Product ref
381  print '<td>'.$langs->trans('ComposedProduct').'</td>';
382  // Product label
383  print '<td>'.$langs->trans('Label').'</td>';
384  // Min supplier price
385  print '<td class="right" colspan="2">'.$langs->trans('MinSupplierPrice').'</td>';
386  // Min customer price
387  print '<td class="right" colspan="2">'.$langs->trans('MinCustomerPrice').'</td>';
388  // Stock
389  if (isModEnabled('stock')) {
390  print '<td class="right">'.$langs->trans('Stock').'</td>';
391  }
392  // Qty in kit
393  print '<td class="right">'.$langs->trans('Qty').'</td>';
394  // Stoc inc/dev
395  print '<td class="center">'.$langs->trans('ComposedProductIncDecStock').'</td>';
396  // Move
397  print '<td class="linecolmove" style="width: 10px"></td>';
398  print '</tr>'."\n";
399 
400  $totalsell = 0;
401  $total = 0;
402  if (count($prods_arbo)) {
403  foreach ($prods_arbo as $value) {
404  $productstatic->fetch($value['id']);
405 
406  if ($value['level'] <= 1) {
407  print '<tr id="'.$object->sousprods[$parent_label][$value['id']][6].'" class="drag drop oddeven level1">';
408 
409  // Rank
410  print '<td>'.$object->sousprods[$parent_label][$value['id']][7].'</td>';
411 
412  $notdefined = 0;
413  $nb_of_subproduct = $value['nb'];
414 
415  // Product ref
416  print '<td>'.$productstatic->getNomUrl(1, 'composition').'</td>';
417 
418  // Product label
419  print '<td title="'.dol_escape_htmltag($productstatic->label).'" class="tdoverflowmax150">'.dol_escape_htmltag($productstatic->label).'</td>';
420 
421  // Best buying price
422  print '<td class="right">';
423  if ($product_fourn->find_min_price_product_fournisseur($productstatic->id) > 0) {
424  print $langs->trans("BuyingPriceMinShort").': ';
425  if ($product_fourn->product_fourn_price_id > 0) {
426  print $product_fourn->display_price_product_fournisseur(0, 0);
427  } else {
428  print $langs->trans("NotDefined"); $notdefined++; $atleastonenotdefined++;
429  }
430  }
431  print '</td>';
432 
433  // For avoid a non-numeric value
434  $fourn_unitprice = (!empty($product_fourn->fourn_unitprice) ? $product_fourn->fourn_unitprice : 0);
435  $fourn_remise_percent = (!empty($product_fourn->fourn_remise_percent) ? $product_fourn->fourn_remise_percent : 0);
436  $fourn_remise = (!empty($product_fourn->fourn_remise) ? $product_fourn->fourn_remise : 0);
437 
438  $unitline = price2num(($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise), 'MU');
439  $totalline = price2num($value['nb'] * ($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise), 'MT');
440  $total += $totalline;
441 
442  print '<td class="right nowraponall">';
443  print ($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x ' : '').'<span class="amount">'.price($unitline, '', '', 0, 0, -1, $conf->currency)).'</span>';
444  print '</td>';
445 
446  // Best selling price
447  $pricesell = $productstatic->price;
448  if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
449  $pricesell = 'Variable';
450  } else {
451  $totallinesell = price2num($value['nb'] * ($pricesell), 'MT');
452  $totalsell += $totallinesell;
453  }
454  print '<td class="right" colspan="2">';
455  print ($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x ' : ''));
456  if (is_numeric($pricesell)) {
457  print '<span class="amount">'.price($pricesell, '', '', 0, 0, -1, $conf->currency).'</span>';
458  } else {
459  print '<span class="opacitymedium">'.$langs->trans($pricesell).'</span>';
460  }
461  print '</td>';
462 
463  // Stock
464  if (isModEnabled('stock')) {
465  print '<td class="right">'.$value['stock'].'</td>'; // Real stock
466  }
467 
468  // Qty + IncDec
469  if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
470  print '<td class="center"><input type="text" value="'.$nb_of_subproduct.'" name="TProduct['.$productstatic->id.'][qty]" class="right width40" /></td>';
471  print '<td class="center"><input type="checkbox" name="TProduct['.$productstatic->id.'][incdec]" value="1" '.($value['incdec'] == 1 ? 'checked' : '').' /></td>';
472  } else {
473  print '<td>'.$nb_of_subproduct.'</td>';
474  print '<td>'.($value['incdec'] == 1 ? 'x' : '').'</td>';
475  }
476 
477  // Move action
478  print '<td class="linecolmove tdlineupdown center"></td>';
479 
480  print '</tr>'."\n";
481  } else {
482  $hide = '';
483  if (empty($conf->global->PRODUCT_SHOW_SUB_SUB_PRODUCTS)) {
484  $hide = ' hideobject'; // By default, we do not show this. It makes screen very difficult to understand
485  }
486 
487  print '<tr class="oddeven'.$hide.'" id="sub-'.$value['id_parent'].'" data-ignoreidfordnd=1>';
488 
489  //$productstatic->ref=$value['label'];
490  $productstatic->ref = $value['ref'];
491 
492  // Rankd
493  print '<td></td>';
494 
495  // Product ref
496  print '<td>';
497  for ($i = 0; $i < $value['level']; $i++) {
498  print ' &nbsp; &nbsp; '; // Add indentation
499  }
500  print $productstatic->getNomUrl(1, 'composition');
501  print '</td>';
502 
503  // Product label
504  print '<td>'.dol_escape_htmltag($productstatic->label).'</td>';
505 
506  // Best buying price
507  print '<td>&nbsp;</td>';
508  print '<td>&nbsp;</td>';
509  // Best selling price
510  print '<td>&nbsp;</td>';
511  print '<td>&nbsp;</td>';
512 
513  // Stock
514  if (isModEnabled('stock')) {
515  print '<td></td>'; // Real stock
516  }
517 
518  // Qty in kit
519  print '<td class="right">'.dol_escape_htmltag($value['nb']).'</td>';
520 
521  // Inc/dec
522  print '<td>&nbsp;</td>';
523 
524  // Action move
525  print '<td>&nbsp;</td>';
526 
527  print '</tr>'."\n";
528  }
529  }
530 
531 
532  // Total
533 
534  print '<tr class="liste_total">';
535 
536  // Rank
537  print '<td></td>';
538 
539  // Product ref
540  print '<td class="liste_total"></td>';
541 
542  // Product label
543  print '<td class="liste_total"></td>';
544 
545  // Minimum buying price
546  print '<td class="liste_total right">';
547  print $langs->trans("TotalBuyingPriceMinShort");
548  print '</td>';
549 
550  print '<td class="liste_total right">';
551  if ($atleastonenotdefined) {
552  print $langs->trans("Unknown").' ('.$langs->trans("SomeSubProductHaveNoPrices").')';
553  }
554  print ($atleastonenotdefined ? '' : price($total, '', '', 0, 0, -1, $conf->currency));
555  print '</td>';
556 
557  // Minimum selling price
558  print '<td class="liste_total right">';
559  print $langs->trans("TotalSellingPriceMinShort");
560  print '</td>';
561 
562  print '<td class="liste_total right">';
563  if ($atleastonenotdefined) {
564  print $langs->trans("Unknown").' ('.$langs->trans("SomeSubProductHaveNoPrices").')';
565  }
566  print ($atleastonenotdefined ? '' : price($totalsell, '', '', 0, 0, -1, $conf->currency));
567  print '</td>';
568 
569  // Stock
570  if (isModEnabled('stock')) {
571  print '<td class="liste_total right">&nbsp;</td>';
572  }
573 
574  print '<td></td>';
575 
576  print '<td class="center">';
577  if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
578  print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
579  }
580  print '</td>';
581 
582  print '<td></td>';
583 
584  print '</tr>'."\n";
585  } else {
586  $colspan = 10;
587  if (isModEnabled('stock')) {
588  $colspan++;
589  }
590 
591  print '<tr class="oddeven">';
592  print '<td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("None").'</span></td>';
593  print '</tr>';
594  }
595 
596  print '</table>';
597 
598  /*if($user->rights->produit->creer || $user->hasRight('service', 'creer')) {
599  print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
600  }*/
601 
602  print '</form>';
603  print '</div>';
604 
605 
606 
607  // Form with product to add
608  if ((empty($action) || $action == 'view' || $action == 'edit' || $action == 'search' || $action == 're-edit') && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
609  print '<br>';
610 
611  $rowspan = 1;
612  if (isModEnabled('categorie')) {
613  $rowspan++;
614  }
615 
616  print load_fiche_titre($langs->trans("ProductToAddSearch"), '', '');
617  print '<form action="'.DOL_URL_ROOT.'/product/composition/card.php?id='.$id.'" method="POST">';
618  print '<input type="hidden" name="action" value="search">';
619  print '<input type="hidden" name="id" value="'.$id.'">';
620  print '<div class="inline-block">';
621  print '<input type="hidden" name="token" value="'.newToken().'">';
622  print $langs->trans("KeywordFilter").': ';
623  print '<input type="text" name="key" value="'.$key.'"> &nbsp; ';
624  print '</div>';
625  if (isModEnabled('categorie')) {
626  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
627  print '<div class="inline-block">'.$langs->trans("CategoryFilter").': ';
628  print $form->select_all_categories(Categorie::TYPE_PRODUCT, $parent, 'parent').' &nbsp; </div>';
629  print ajax_combobox('parent');
630  }
631  print '<div class="inline-block">';
632  print '<input type="submit" class="button small" value="'.$langs->trans("Search").'">';
633  print '</div>';
634  print '</form>';
635  }
636 
637 
638  // List of products
639  if ($action == 'search') {
640  print '<br>';
641  print '<form action="'.DOL_URL_ROOT.'/product/composition/card.php?id='.$id.'" method="post">';
642  print '<input type="hidden" name="token" value="'.newToken().'">';
643  print '<input type="hidden" name="action" value="add_prod">';
644  print '<input type="hidden" name="id" value="'.$id.'">';
645 
646  print '<table class="noborder centpercent">';
647  print '<tr class="liste_titre">';
648  print '<th class="liste_titre">'.$langs->trans("ComposedProduct").'</td>';
649  print '<th class="liste_titre">'.$langs->trans("Label").'</td>';
650  //print '<th class="liste_titre center">'.$langs->trans("IsInPackage").'</td>';
651  print '<th class="liste_titre right">'.$langs->trans("Qty").'</td>';
652  print '<th class="center">'.$langs->trans('ComposedProductIncDecStock').'</th>';
653  print '</tr>';
654  if ($resql) {
655  $num = $db->num_rows($resql);
656  $i = 0;
657 
658  if ($num == 0) {
659  print '<tr><td colspan="4">'.$langs->trans("NoMatchFound").'</td></tr>';
660  }
661 
662  $MAX = 100;
663 
664  while ($i < min($num, $MAX)) {
665  $objp = $db->fetch_object($resql);
666  if ($objp->rowid != $id) {
667  // check if a product is not already a parent product of this one
668  $prod_arbo = new Product($db);
669  $prod_arbo->id = $objp->rowid;
670  // This type is not supported (not required to have virtual products working).
671  if ($prod_arbo->type == Product::TYPE_ASSEMBLYKIT || $prod_arbo->type == Product::TYPE_STOCKKIT) {
672  $is_pere = 0;
673  $prod_arbo->get_sousproduits_arbo();
674  // associations sousproduits
675  $prods_arbo = $prod_arbo->get_arbo_each_prod();
676  if (count($prods_arbo) > 0) {
677  foreach ($prods_arbo as $key => $value) {
678  if ($value[1] == $id) {
679  $is_pere = 1;
680  }
681  }
682  }
683  if ($is_pere == 1) {
684  $i++;
685  continue;
686  }
687  }
688 
689  print "\n";
690  print '<tr class="oddeven">';
691 
692  $productstatic->id = $objp->rowid;
693  $productstatic->ref = $objp->ref;
694  $productstatic->label = $objp->label;
695  $productstatic->type = $objp->type;
696  $productstatic->entity = $objp->entity;
697  $productstatic->status = $objp->status;
698  $productstatic->status_buy = $objp->status_buy;
699  $productstatic->status_batch = $objp->tobatch;
700 
701  print '<td>'.$productstatic->getNomUrl(1, '', 24).'</td>';
702  $labeltoshow = $objp->label;
703  if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->labelm)) {
704  $labeltoshow = $objp->labelm;
705  }
706 
707  print '<td>'.$labeltoshow.'</td>';
708 
709 
710  if ($object->is_sousproduit($id, $objp->rowid)) {
711  //$addchecked = ' checked';
712  $qty = $object->is_sousproduit_qty;
713  $incdec = $object->is_sousproduit_incdec;
714  } else {
715  //$addchecked = '';
716  $qty = 0;
717  $incdec = 0;
718  }
719  // Contained into package
720  /*print '<td class="center"><input type="hidden" name="prod_id_'.$i.'" value="'.$objp->rowid.'">';
721  print '<input type="checkbox" '.$addchecked.'name="prod_id_chk'.$i.'" value="'.$objp->rowid.'"></td>';*/
722  // Qty
723  print '<td class="right"><input type="hidden" name="prod_id_'.$i.'" value="'.$objp->rowid.'"><input type="text" size="2" name="prod_qty_'.$i.'" value="'.($qty ? $qty : '').'"></td>';
724 
725  // Inc Dec
726  print '<td class="center">';
727  if ($qty) {
728  print '<input type="checkbox" name="prod_incdec_'.$i.'" value="1" '.($incdec ? 'checked' : '').'>';
729  } else {
730  // TODO Hide field and show it when setting a qty
731  print '<input type="checkbox" name="prod_incdec_'.$i.'" value="1" checked>';
732  //print '<input type="checkbox" disabled name="prod_incdec_'.$i.'" value="1" checked>';
733  }
734  print '</td>';
735 
736  print '</tr>';
737  }
738  $i++;
739  }
740  if ($num > $MAX) {
741  print '<tr class="oddeven">';
742  print '<td><span class="opacitymedium">'.$langs->trans("More").'...</span></td>';
743  print '<td></td>';
744  print '<td></td>';
745  print '<td></td>';
746  print '</tr>';
747  }
748  } else {
749  dol_print_error($db);
750  }
751  print '</table>';
752  print '<input type="hidden" name="max_prod" value="'.$i.'">';
753 
754  if ($num > 0) {
755  print '<div class="center">';
756  print '<input type="submit" class="button button-save" name="save" value="'.$langs->trans("Add").'/'.$langs->trans("Update").'">';
757  print '<input type="submit" class="button button-cancel" name="cancel" value="'.$langs->trans("Cancel").'">';
758  print '</div>';
759  }
760 
761  print '</form>';
762  }
763  }
764 }
765 
766 // End of page
767 llxFooter();
768 $db->close();
if(GETPOST('button_removefilter_x', 'alpha')||GETPOST('button_removefilter.x', 'alpha')||GETPOST('button_removefilter', 'alpha')) if(GETPOST('button_search_x', 'alpha')||GETPOST('button_search.x', 'alpha')||GETPOST('button_search', 'alpha')) if($action=="save" &&empty($cancel)) $help_url
View.
Definition: agenda.php:118
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition: ajax.lib.php:449
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:56
llxFooter()
Empty footer.
Definition: wrapper.php:70
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_STOCKKIT
Advanced feature: stock kit.
const TYPE_SERVICE
Service.
const TYPE_ASSEMBLYKIT
Advanced feature: assembly kit.
$parameters
Actions.
Definition: card.php:83
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
Show tab footer of a card.
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_get_fiche_end($notab=0)
Return tab footer of a card.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
isModEnabled($module)
Is Dolibarr module enabled.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
product_prepare_head($object)
Prepare array with list of tabs.
Definition: product.lib.php:36
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.