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