dolibarr 19.0.3
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
32require '../../main.inc.php';
33require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
34require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
35require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
36require_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
50if (!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;
61if ($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
69if ($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
88if ($cancel) {
89 $action = '';
90}
91
92$reshook = $hookmanager->executeHooks('doActions', [], $object, $action); // Note that $action and $object may have been modified by some hooks
93if ($reshook < 0) {
94 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
95}
96
97if (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
161if ($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);
211if (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}
215if (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
220llxHeader('', $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
227print dol_get_fiche_head($head, 'subproduct', $titre, -1, $picto);
228
229
230if ($id > 0 || !empty($ref)) {
231 /*
232 * Product card
233 */
234 if ($user->hasRight('produit', 'lire') || $user->hasRight('service', 'lire')) {
235 $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
236
237 $shownav = 1;
238 if ($user->socid && !in_array('product', explode(',', getDolGlobalString('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 || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || !getDolGlobalString('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 (!getDolGlobalString('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 (!getDolGlobalString('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 (!getDolGlobalString('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 // Hook fields
393 $parameters = array();
394 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
395 print $hookmanager->resPrint;
396 // Qty in kit
397 print '<td class="right">'.$langs->trans('Qty').'</td>';
398 // Stoc inc/dev
399 print '<td class="center">'.$langs->trans('ComposedProductIncDecStock').'</td>';
400 // Move
401 print '<td class="linecolmove" style="width: 10px"></td>';
402 print '</tr>'."\n";
403
404 $totalsell = 0;
405 $total = 0;
406 if (count($prods_arbo)) {
407 foreach ($prods_arbo as $value) {
408 $productstatic->fetch($value['id']);
409
410 if ($value['level'] <= 1) {
411 print '<tr id="'.$object->sousprods[$parent_label][$value['id']][6].'" class="drag drop oddeven level1">';
412
413 // Rank
414 print '<td>'.$object->sousprods[$parent_label][$value['id']][7].'</td>';
415
416 $notdefined = 0;
417 $nb_of_subproduct = $value['nb'];
418
419 // Product ref
420 print '<td>'.$productstatic->getNomUrl(1, 'composition').'</td>';
421
422 // Product label
423 print '<td title="'.dol_escape_htmltag($productstatic->label).'" class="tdoverflowmax150">'.dol_escape_htmltag($productstatic->label).'</td>';
424
425 // Best buying price
426 print '<td class="right">';
427 if ($product_fourn->find_min_price_product_fournisseur($productstatic->id) > 0) {
428 print $langs->trans("BuyingPriceMinShort").': ';
429 if ($product_fourn->product_fourn_price_id > 0) {
430 print $product_fourn->display_price_product_fournisseur(0, 0);
431 } else {
432 print $langs->trans("NotDefined");
433 $notdefined++;
434 $atleastonenotdefined++;
435 }
436 }
437 print '</td>';
438
439 // For avoid a non-numeric value
440 $fourn_unitprice = (!empty($product_fourn->fourn_unitprice) ? $product_fourn->fourn_unitprice : 0);
441 $fourn_remise_percent = (!empty($product_fourn->fourn_remise_percent) ? $product_fourn->fourn_remise_percent : 0);
442 $fourn_remise = (!empty($product_fourn->fourn_remise) ? $product_fourn->fourn_remise : 0);
443
444 $unitline = price2num(($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise), 'MU');
445 $totalline = price2num($value['nb'] * ($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise), 'MT');
446 $total += $totalline;
447
448 print '<td class="right nowraponall">';
449 print($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x ' : '').'<span class="amount">'.price($unitline, '', '', 0, 0, -1, $conf->currency)).'</span>';
450 print '</td>';
451
452 // Best selling price
453 $pricesell = $productstatic->price;
454 if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
455 $pricesell = 'Variable';
456 } else {
457 $totallinesell = price2num($value['nb'] * ($pricesell), 'MT');
458 $totalsell += $totallinesell;
459 }
460 print '<td class="right" colspan="2">';
461 print($notdefined ? '' : ($value['nb'] > 1 ? $value['nb'].'x ' : ''));
462 if (is_numeric($pricesell)) {
463 print '<span class="amount">'.price($pricesell, '', '', 0, 0, -1, $conf->currency).'</span>';
464 } else {
465 print '<span class="opacitymedium">'.$langs->trans($pricesell).'</span>';
466 }
467 print '</td>';
468
469 // Stock
470 if (isModEnabled('stock')) {
471 print '<td class="right">'.$value['stock'].'</td>'; // Real stock
472 }
473
474 // Hook fields
475 $parameters = array();
476 $reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $productstatic); // Note that $action and $object may have been modified by hook
477 print $hookmanager->resPrint;
478
479 // Qty + IncDec
480 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
481 print '<td class="center"><input type="text" value="'.$nb_of_subproduct.'" name="TProduct['.$productstatic->id.'][qty]" class="right width40" /></td>';
482 print '<td class="center"><input type="checkbox" name="TProduct['.$productstatic->id.'][incdec]" value="1" '.($value['incdec'] == 1 ? 'checked' : '').' /></td>';
483 } else {
484 print '<td>'.$nb_of_subproduct.'</td>';
485 print '<td>'.($value['incdec'] == 1 ? 'x' : '').'</td>';
486 }
487
488 // Move action
489 print '<td class="linecolmove tdlineupdown center"></td>';
490
491 print '</tr>'."\n";
492 } else {
493 $hide = '';
494 if (!getDolGlobalString('PRODUCT_SHOW_SUB_SUB_PRODUCTS')) {
495 $hide = ' hideobject'; // By default, we do not show this. It makes screen very difficult to understand
496 }
497
498 print '<tr class="oddeven'.$hide.'" id="sub-'.$value['id_parent'].'" data-ignoreidfordnd=1>';
499
500 //$productstatic->ref=$value['label'];
501 $productstatic->ref = $value['ref'];
502
503 // Rankd
504 print '<td></td>';
505
506 // Product ref
507 print '<td>';
508 for ($i = 0; $i < $value['level']; $i++) {
509 print ' &nbsp; &nbsp; '; // Add indentation
510 }
511 print $productstatic->getNomUrl(1, 'composition');
512 print '</td>';
513
514 // Product label
515 print '<td>'.dol_escape_htmltag($productstatic->label).'</td>';
516
517 // Best buying price
518 print '<td>&nbsp;</td>';
519 print '<td>&nbsp;</td>';
520 // Best selling price
521 print '<td>&nbsp;</td>';
522 print '<td>&nbsp;</td>';
523
524 // Stock
525 if (isModEnabled('stock')) {
526 print '<td></td>'; // Real stock
527 }
528
529 // Hook fields
530 $parameters = array();
531 $reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $productstatic); // Note that $action and $object may have been modified by hook
532 print $hookmanager->resPrint;
533
534 // Qty in kit
535 print '<td class="right">'.dol_escape_htmltag($value['nb']).'</td>';
536
537 // Inc/dec
538 print '<td>&nbsp;</td>';
539
540 // Action move
541 print '<td>&nbsp;</td>';
542
543 print '</tr>'."\n";
544 }
545 }
546
547
548 // Total
549
550 print '<tr class="liste_total">';
551
552 // Rank
553 print '<td></td>';
554
555 // Product ref
556 print '<td class="liste_total"></td>';
557
558 // Product label
559 print '<td class="liste_total"></td>';
560
561 // Minimum buying price
562 print '<td class="liste_total right">';
563 print $langs->trans("TotalBuyingPriceMinShort");
564 print '</td>';
565
566 print '<td class="liste_total right">';
567 if ($atleastonenotdefined) {
568 print $langs->trans("Unknown").' ('.$langs->trans("SomeSubProductHaveNoPrices").')';
569 }
570 print($atleastonenotdefined ? '' : price($total, '', '', 0, 0, -1, $conf->currency));
571 print '</td>';
572
573 // Minimum selling price
574 print '<td class="liste_total right">';
575 print $langs->trans("TotalSellingPriceMinShort");
576 print '</td>';
577
578 print '<td class="liste_total right">';
579 if ($atleastonenotdefined) {
580 print $langs->trans("Unknown").' ('.$langs->trans("SomeSubProductHaveNoPrices").')';
581 }
582 print($atleastonenotdefined ? '' : price($totalsell, '', '', 0, 0, -1, $conf->currency));
583 print '</td>';
584
585 // Stock
586 if (isModEnabled('stock')) {
587 print '<td class="liste_total right">&nbsp;</td>';
588 }
589
590 print '<td></td>';
591
592 print '<td class="center">';
593 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
594 print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
595 }
596 print '</td>';
597
598 print '<td></td>';
599
600 print '</tr>'."\n";
601 } else {
602 $colspan = 10;
603 if (isModEnabled('stock')) {
604 $colspan++;
605 }
606
607 print '<tr class="oddeven">';
608 print '<td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("None").'</span></td>';
609 print '</tr>';
610 }
611
612 print '</table>';
613
614 /*if($user->rights->produit->creer || $user->hasRight('service', 'creer')) {
615 print '<input type="submit" class="button button-save" value="'.$langs->trans("Save").'">';
616 }*/
617
618 print '</form>';
619 print '</div>';
620
621
622
623 // Form with product to add
624 if ((empty($action) || $action == 'view' || $action == 'edit' || $action == 'search' || $action == 're-edit') && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
625 print '<br>';
626
627 $rowspan = 1;
628 if (isModEnabled('categorie')) {
629 $rowspan++;
630 }
631
632 print load_fiche_titre($langs->trans("ProductToAddSearch"), '', '');
633 print '<form action="'.DOL_URL_ROOT.'/product/composition/card.php?id='.$id.'" method="POST">';
634 print '<input type="hidden" name="action" value="search">';
635 print '<input type="hidden" name="id" value="'.$id.'">';
636 print '<div class="inline-block">';
637 print '<input type="hidden" name="token" value="'.newToken().'">';
638 print $langs->trans("KeywordFilter").': ';
639 print '<input type="text" name="key" value="'.$key.'"> &nbsp; ';
640 print '</div>';
641 if (isModEnabled('categorie')) {
642 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
643 print '<div class="inline-block">'.$langs->trans("CategoryFilter").': ';
644 print $form->select_all_categories(Categorie::TYPE_PRODUCT, $parent, 'parent').' &nbsp; </div>';
645 print ajax_combobox('parent');
646 }
647 print '<div class="inline-block">';
648 print '<input type="submit" class="button small" value="'.$langs->trans("Search").'">';
649 print '</div>';
650 print '</form>';
651 }
652
653
654 // List of products
655 if ($action == 'search') {
656 print '<br>';
657 print '<form action="'.DOL_URL_ROOT.'/product/composition/card.php?id='.$id.'" method="post">';
658 print '<input type="hidden" name="token" value="'.newToken().'">';
659 print '<input type="hidden" name="action" value="add_prod">';
660 print '<input type="hidden" name="id" value="'.$id.'">';
661
662 print '<table class="noborder centpercent">';
663 print '<tr class="liste_titre">';
664 print '<th class="liste_titre">'.$langs->trans("ComposedProduct").'</td>';
665 print '<th class="liste_titre">'.$langs->trans("Label").'</td>';
666 //print '<th class="liste_titre center">'.$langs->trans("IsInPackage").'</td>';
667 print '<th class="liste_titre right">'.$langs->trans("Qty").'</td>';
668 print '<th class="center">'.$langs->trans('ComposedProductIncDecStock').'</th>';
669 print '</tr>';
670 if ($resql) {
671 $num = $db->num_rows($resql);
672 $i = 0;
673
674 if ($num == 0) {
675 print '<tr><td colspan="4">'.$langs->trans("NoMatchFound").'</td></tr>';
676 }
677
678 $MAX = 100;
679
680 while ($i < min($num, $MAX)) {
681 $objp = $db->fetch_object($resql);
682 if ($objp->rowid != $id) {
683 // check if a product is not already a parent product of this one
684 $prod_arbo = new Product($db);
685 $prod_arbo->id = $objp->rowid;
686 // This type is not supported (not required to have virtual products working).
687 if (getDolGlobalString('PRODUCT_USE_DEPRECATED_ASSEMBLY_AND_STOCK_KIT_TYPE')) {
688 if ($prod_arbo->type == 2 || $prod_arbo->type == 3) {
689 $is_pere = 0;
690 $prod_arbo->get_sousproduits_arbo();
691 // associations sousproduits
692 $prods_arbo = $prod_arbo->get_arbo_each_prod();
693 if (count($prods_arbo) > 0) {
694 foreach ($prods_arbo as $key => $value) {
695 if ($value[1] == $id) {
696 $is_pere = 1;
697 }
698 }
699 }
700 if ($is_pere == 1) {
701 $i++;
702 continue;
703 }
704 }
705 }
706
707 print "\n";
708 print '<tr class="oddeven">';
709
710 $productstatic->id = $objp->rowid;
711 $productstatic->ref = $objp->ref;
712 $productstatic->label = $objp->label;
713 $productstatic->type = $objp->type;
714 $productstatic->entity = $objp->entity;
715 $productstatic->status = $objp->status;
716 $productstatic->status_buy = $objp->status_buy;
717 $productstatic->status_batch = $objp->tobatch;
718
719 print '<td>'.$productstatic->getNomUrl(1, '', 24).'</td>';
720 $labeltoshow = $objp->label;
721 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($objp->labelm)) {
722 $labeltoshow = $objp->labelm;
723 }
724
725 print '<td>'.$labeltoshow.'</td>';
726
727
728 if ($object->is_sousproduit($id, $objp->rowid)) {
729 //$addchecked = ' checked';
730 $qty = $object->is_sousproduit_qty;
731 $incdec = $object->is_sousproduit_incdec;
732 } else {
733 //$addchecked = '';
734 $qty = 0;
735 $incdec = 0;
736 }
737 // Contained into package
738 /*print '<td class="center"><input type="hidden" name="prod_id_'.$i.'" value="'.$objp->rowid.'">';
739 print '<input type="checkbox" '.$addchecked.'name="prod_id_chk'.$i.'" value="'.$objp->rowid.'"></td>';*/
740 // Qty
741 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>';
742
743 // Inc Dec
744 print '<td class="center">';
745 if ($qty) {
746 print '<input type="checkbox" name="prod_incdec_'.$i.'" value="1" '.($incdec ? 'checked' : '').'>';
747 } else {
748 // TODO Hide field and show it when setting a qty
749 print '<input type="checkbox" name="prod_incdec_'.$i.'" value="1" checked>';
750 //print '<input type="checkbox" disabled name="prod_incdec_'.$i.'" value="1" checked>';
751 }
752 print '</td>';
753
754 print '</tr>';
755 }
756 $i++;
757 }
758 if ($num > $MAX) {
759 print '<tr class="oddeven">';
760 print '<td><span class="opacitymedium">'.$langs->trans("More").'...</span></td>';
761 print '<td></td>';
762 print '<td></td>';
763 print '<td></td>';
764 print '</tr>';
765 }
766 } else {
767 dol_print_error($db);
768 }
769 print '</table>';
770 print '<input type="hidden" name="max_prod" value="'.$i.'">';
771
772 if ($num > 0) {
773 print '<div class="center">';
774 print '<input type="submit" class="button button-save" name="save" value="'.$langs->trans("Add").'/'.$langs->trans("Update").'">';
775 print '<input type="submit" class="button button-cancel" name="cancel" value="'.$langs->trans("Cancel").'">';
776 print '</div>';
777 }
778
779 print '</form>';
780 }
781 }
782}
783
784// End of page
785llxFooter();
786$db->close();
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:455
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader()
Empty header.
Definition wrapper.php:55
llxFooter()
Empty footer.
Definition wrapper.php:69
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_SERVICE
Service.
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 a 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.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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.
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.