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