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