dolibarr 24.0.2
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-2022 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
5 * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
8 * Copyright (C) 2010-2023 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2010-2022 Philippe Grand <philippe.grand@atoo-net.com>
10 * Copyright (C) 2012-2023 Christophe Battarel <christophe.battarel@altairis.fr>
11 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2014 Florian Henry <florian.henry@open-concept.pro>
13 * Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
16 * Copyright (C) 2020 Nicolas ZABOURI <info@inovea-conseil.com>
17 * Copyright (C) 2022 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
18 * Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
19 * Copyright (C) 2023 William Mead <william.mead@manchenumerique.fr>
20 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
21 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
22 * Copyright (C) 2025 Benjamin Falière <benjamin@faliere.com>
23 * Copyright (C) 2025 Anthony Berton <anthony.berton@bb2a.fr>
24 *
25 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation; either version 3 of the License, or
28 * (at your option) any later version.
29 *
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
34 *
35 * You should have received a copy of the GNU General Public License
36 * along with this program. If not, see <https://www.gnu.org/licenses/>.
37 */
38
39
46// Load Dolibarr environment
47require '../../main.inc.php';
57require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
58require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
59require_once DOL_DOCUMENT_ROOT . '/core/class/html.formfile.class.php';
60require_once DOL_DOCUMENT_ROOT . '/core/class/html.formpropal.class.php';
61require_once DOL_DOCUMENT_ROOT . '/core/class/html.formmargin.class.php';
62require_once DOL_DOCUMENT_ROOT . '/comm/propal/class/propal.class.php';
63require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
64require_once DOL_DOCUMENT_ROOT . '/core/modules/propale/modules_propale.php';
65require_once DOL_DOCUMENT_ROOT . '/core/lib/propal.lib.php';
66require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
67require_once DOL_DOCUMENT_ROOT . '/core/class/extrafields.class.php';
68require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
69if (isModEnabled('project')) {
70 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
71 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
72}
73
74if (isModEnabled('variants')) {
75 require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php';
76}
77
78
79// Load translation files required by the page
80$langs->loadLangs(array('companies', 'propal', 'compta', 'bills', 'orders', 'products', 'sendings', 'other'));
81if (isModEnabled('incoterm')) {
82 $langs->load('incoterm');
83}
84if (isModEnabled('margin')) {
85 $langs->load('margins');
86}
87
88$error = 0;
89$outlangs = null;
90$array_options = array();
91
92$id = GETPOSTINT('id');
93$ref = GETPOST('ref', 'alpha');
94$socid = GETPOSTINT('socid');
95$action = GETPOST('action', 'aZ09');
96$cancel = GETPOST('cancel', 'alpha');
97$origin = GETPOST('origin', 'alpha');
98$originid = GETPOSTINT('originid');
99$renewal = GETPOST('renewal'); // for contract renewal
100$confirm = GETPOST('confirm', 'alpha');
101$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
102$lineid = GETPOSTINT('lineid');
103$contactid = GETPOSTINT('contactid');
104$projectid = GETPOSTINT('projectid');
105$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
106
107// PDF
108$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
109$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
110$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
111
112$object = new Propal($db);
113
114// fetch optionals attributes and labels
115$extrafields->fetch_name_optionals_label($object->table_element);
116
117// Load object
118if ($id > 0 || !empty($ref)) {
119 $ret = $object->fetch($id, $ref);
120 if ($ret > 0) {
121 $ret = $object->fetch_thirdparty();
122 if ($ret > 0 && isset($object->fk_project)) {
123 $ret = $object->fetchProject();
124 }
125 }
126 if ($ret <= 0) {
127 setEventMessages($object->error, $object->errors, 'errors');
128 $action = '';
129 }
130}
131
132// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
133$hookmanager->initHooks(array('propalcard', 'globalcard'));
134
135$usercanread = $user->hasRight("propal", "lire");
136$usercancreate = $user->hasRight("propal", "creer");
137$usercandelete = $user->hasRight("propal", "supprimer");
138
139$usercanclose = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'close')));
140$usercanvalidate = ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $usercancreate) || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'validate')));
141$usercansend = (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'send')));
142
143$usermustrespectpricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
144$usercancreateorder = ($user->hasRight('commande', 'creer') == 1);
145$usercancreateinvoice = ($user->hasRight('facture', 'creer') == 1);
146$usercancreatecontract = ($user->hasRight('contrat', 'creer') == 1);
147$usercancreateintervention = ($user->hasRight('ficheinter', 'creer') == 1);
148$usercancreatepurchaseorder = ($user->hasRight('fournisseur', 'commande', 'creer') || $user->hasRight('supplier_order', 'creer'));
149$usercanreopen = ((!getDolGlobalBool('MAIN_USE_ADVANCED_PERMS') && $usercanclose) || (getDolGlobalBool('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('propal', 'propal_advance', 'reopen')));
150if (getDolGlobalBool('PROPAL_DISALLOW_REOPEN')) {
151 $usercanreopen = false;
152}
153
154$permissiontoadd = $usercancreate;
155$permissionnote = $usercancreate; // Used by the include of actions_setnotes.inc.php
156$permissiondellink = $usercancreate; // Used by the include of actions_dellink.inc.php
157$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdown.inc.php
158$permissiontoeditextra = $permissiontoadd;
159if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
160 // For action 'update_extras', is there a specific permission set for the attribute to update
161 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
162}
163
164$price_base_type = null;
165$shipping_method_id = null;
166$warehouse_id = -1;
167
168// Security check
169if (!empty($user->socid)) {
170 $socid = $user->socid;
171}
172restrictedArea($user, 'propal', $object->id);
173
174
175/*
176 * Actions
177 */
178
179$parameters = array('socid' => $socid);
180$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
181if ($reshook < 0) {
182 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
183}
184if (empty($reshook)) {
185 $backurlforlist = dolBuildUrl(DOL_URL_ROOT . '/comm/propal/list.php');
186
187 if (empty($backtopage) || ($cancel && empty($id))) {
188 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
189 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
190 $backtopage = $backurlforlist;
191 } else {
192 $backtopage = dolBuildUrl(DOL_URL_ROOT . '/comm/propal/card.php', ['id' => ((!empty($id) && $id > 0) ? $id : '__ID__')]);
193 }
194 }
195 }
196
197 if ($cancel) {
198 if (!empty($backtopageforcancel)) {
199 header("Location: " . $backtopageforcancel);
200 exit;
201 } elseif (!empty($backtopage)) {
202 header("Location: " . $backtopage);
203 exit;
204 }
205 $action = '';
206 }
207
208 include DOL_DOCUMENT_ROOT . '/core/actions_setnotes.inc.php'; // Must be include, not includ_once
209
210 include DOL_DOCUMENT_ROOT . '/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
211
212 include DOL_DOCUMENT_ROOT . '/core/actions_lineupdown.inc.php'; // Must be 'include', not 'include_once'
213
214 // Action clone object
215 if ($action == 'confirm_clone' && $confirm == 'yes' && $usercancreate) {
216 if (!($socid > 0)) {
217 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('IdThirdParty')), null, 'errors');
218 } else {
219 if ($object->id > 0) {
220 // We clone object to avoid to denaturate loaded object when setting some properties for clone or if createFromClone modifies the object.
221 $objectutil = dol_clone($object, 1);
222 '@phan-var-force Propal $objectutil';
223
224 if (getDolGlobalString('PROPAL_CLONE_DATE_DELIVERY')) {
225 //Get difference between old and new delivery date and change lines according to difference
226 $date_delivery = dol_mktime(
227 12,
228 0,
229 0,
230 GETPOSTINT('date_deliverymonth'),
231 GETPOSTINT('date_deliveryday'),
232 GETPOSTINT('date_deliveryyear')
233 );
234 $date_delivery_old = $objectutil->delivery_date;
235 if (!empty($date_delivery_old) && !empty($date_delivery)) {
236 //Attempt to get the date without possible hour rounding errors
237 $old_date_delivery = dol_mktime(
238 12,
239 0,
240 0,
241 (int) dol_print_date($date_delivery_old, '%m'),
242 (int) dol_print_date($date_delivery_old, '%d'),
243 (int) dol_print_date($date_delivery_old, '%Y')
244 );
245 //Calculate the difference and apply if necessary
246 $difference = $date_delivery - $old_date_delivery;
247 if ($difference != 0) {
248 $objectutil->delivery_date = $date_delivery;
249 foreach ($objectutil->lines as $line) {
250 if (isset($line->date_start)) {
251 $line->date_start += $difference;
252 }
253 if (isset($line->date_end)) {
254 $line->date_end += $difference;
255 }
256 }
257 }
258 }
259 }
260
261 $result = $objectutil->createFromClone($user, $socid, (GETPOSTISSET('entity') ? GETPOSTINT('entity') : null), (GETPOST('update_prices') == 'on'), (GETPOST('update_desc') == 'on'));
262 if ($result > 0) {
263 $warningMsgLineList = array();
264 // check all product lines are to sell otherwise add a warning message for each product line is not to sell
265 foreach ($objectutil->lines as $line) {
266 if (!is_object($line->product)) {
267 $line->fetch_product();
268 }
269 if (is_object($line->product) && $line->product->id > 0) {
270 if (empty($line->product->status)) {
271 $warningMsgLineList[$line->id] = $langs->trans('WarningLineProductNotToSell', $line->product->ref);
272 }
273 }
274 }
275 if (!empty($warningMsgLineList)) {
276 setEventMessages('', $warningMsgLineList, 'warnings');
277 }
278
279 header("Location: " . $_SERVER['PHP_SELF'] . '?id=' . $result);
280 exit();
281 } else {
282 if (count($objectutil->errors) > 0) {
283 setEventMessages($objectutil->error, $objectutil->errors, 'errors');
284 }
285 $action = '';
286 }
287 }
288 }
289 } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && $usercanclose) {
290 // Cancel proposal
291 $result = $object->setCancel($user);
292 if ($result > 0) {
293 header('Location: ' . dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]));
294 exit();
295 } else {
296 $langs->load("errors");
297 setEventMessages($object->error, $object->errors, 'errors');
298 }
299 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $usercandelete) {
300 // Delete proposal
301 $result = $object->delete($user);
302 if ($result > 0) {
303 header('Location: ' . dolBuildUrl(DOL_URL_ROOT . '/comm/propal/list.php', ['restore_lastsearch_values' => 1]));
304 exit();
305 } else {
306 $langs->load("errors");
307 setEventMessages($object->error, $object->errors, 'errors');
308 }
309 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
310 // Remove line
311 $result = $object->deleteLine($lineid);
312 // reorder lines
313 if ($result > 0) {
314 $object->line_order(true);
315 } else {
316 $langs->load("errors");
317 setEventMessages($object->error, $object->errors, 'errors');
318 }
319
320 $ret = $object->fetch($id); // Reload to get new records
321 if ($ret > 0) {
322 $object->fetch_thirdparty();
323 }
324
325 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
326 // Define output language
327 $outputlangs = $langs;
328 if (getDolGlobalInt('MAIN_MULTILANGS')) {
329 $outputlangs = new Translate("", $conf);
330 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
331 $outputlangs->setDefaultLang($newlang);
332 }
333 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
334 }
335 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $usercancreate) {
336 // Delete line
337 $object->fetch($id);
338 $object->fetch_thirdparty();
339
340 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'));
341 if ($result > 0) {
342 // reorder lines
343 $object->line_order(true);
344 // Define output language
345 $outputlangs = $langs;
346 $newlang = '';
347 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id')) {
348 $newlang = GETPOST('lang_id');
349 }
350 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
351 $newlang = $object->thirdparty->default_lang;
352 }
353 if (!empty($newlang)) {
354 $outputlangs = new Translate("", $conf);
355 $outputlangs->setDefaultLang($newlang);
356 $outputlangs->load('products');
357 }
358 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
359 $ret = $object->fetch($id); // Reload to get new records
360 $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
361 }
362 if ($result >= 0) {
363 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
364 exit();
365 }
366 } else {
367 setEventMessages($object->error, $object->errors, 'errors');
368 $action = '';
369 }
370 } elseif ($action == 'confirm_validate' && $confirm == 'yes' && $usercanvalidate) {
371 // Validation
372 $idwarehouse = GETPOSTINT('idwarehouse');
373 $result = $object->valid($user);
374 if ($result > 0 && getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
375 $result = $object->closeProposal($user, $object::STATUS_SIGNED);
376 }
377
378 $ret = $object->fetch($id); // Reload to get new records
379
380 if ($result >= 0) {
381 $ret = $object->fetch($id); // Reload to get new records
382 if ($ret > 0) {
383 $object->fetch_thirdparty();
384 }
385 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
386 $outputlangs = $langs;
387 $newlang = '';
388 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
389 $newlang = GETPOST('lang_id', 'aZ09');
390 }
391 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
392 $newlang = $object->thirdparty->default_lang;
393 }
394 if (!empty($newlang)) {
395 $outputlangs = new Translate("", $conf);
396 $outputlangs->setDefaultLang($newlang);
397 }
398 $model = $object->model_pdf;
399
400 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
401 }
402 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
403 exit();
404 } else {
405 $langs->load("errors");
406 if (count($object->errors) > 0) {
407 setEventMessages($object->error, $object->errors, 'errors');
408 } else {
409 setEventMessages($langs->trans($object->error), null, 'errors');
410 }
411 }
412 } elseif ($action == 'setdate' && $usercancreate) {
413 $datep = dol_mktime(12, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
414
415 if (empty($datep)) {
416 $error++;
417 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
418 }
419
420 if (!$error) {
421 $result = $object->set_date($user, $datep);
422 if ($result > 0 && !empty($object->duree_validite) && !empty($object->fin_validite)) {
423 $datev = $datep + ($object->duree_validite * 24 * 3600);
424 $result = $object->set_echeance($user, $datev, 1);
425 }
426 if ($result < 0) {
427 dol_print_error($db, $object->error);
428 } elseif (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
429 $outputlangs = $langs;
430 $newlang = '';
431 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
432 $newlang = GETPOST('lang_id', 'aZ09');
433 }
434 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
435 $newlang = $object->thirdparty->default_lang;
436 }
437 if (!empty($newlang)) {
438 $outputlangs = new Translate("", $conf);
439 $outputlangs->setDefaultLang($newlang);
440 }
441 $model = $object->model_pdf;
442 $ret = $object->fetch($id); // Reload to get new records
443 if ($ret > 0) {
444 $object->fetch_thirdparty();
445 }
446
447 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
448 }
449 }
450 } elseif ($action == 'setecheance' && $usercancreate) {
451 $result = $object->set_echeance($user, dol_mktime(12, 0, 0, GETPOSTINT('echmonth'), GETPOSTINT('echday'), GETPOSTINT('echyear')));
452 if ($result >= 0) {
453 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
454 $outputlangs = $langs;
455 $newlang = '';
456 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
457 $newlang = GETPOST('lang_id', 'aZ09');
458 }
459 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
460 $newlang = $object->thirdparty->default_lang;
461 }
462 if (!empty($newlang)) {
463 $outputlangs = new Translate("", $conf);
464 $outputlangs->setDefaultLang($newlang);
465 }
466 $model = $object->model_pdf;
467 $ret = $object->fetch($id); // Reload to get new records
468 if ($ret > 0) {
469 $object->fetch_thirdparty();
470 }
471
472 $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
473 }
474 } else {
475 setEventMessages($object->error, $object->errors, 'errors');
476 }
477 } elseif ($action == 'setdate_livraison' && $usercancreate) {
478 $result = $object->setDeliveryDate($user, dol_mktime(12, 0, 0, GETPOSTINT('date_livraisonmonth'), GETPOSTINT('date_livraisonday'), GETPOSTINT('date_livraisonyear')));
479 if ($result < 0) {
480 setEventMessages($object->error, $object->errors, 'errors');
481 }
482 } elseif ($action == 'setref_client' && $usercancreate) {
483 // Positionne ref client
484 $result = $object->set_ref_client($user, GETPOST('ref_client'));
485 if ($result < 0) {
486 setEventMessages($object->error, $object->errors, 'errors');
487 }
488 } elseif ($action == 'set_incoterms' && isModEnabled('incoterm') && $usercancreate) {
489 // Set incoterm
490 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
491 if ($result < 0) {
492 setEventMessages($object->error, $object->errors, 'errors');
493 }
494 } elseif ($action == 'settags' && isModEnabled('category') && $usercancreate) { // Set tags
495 $result = $object->setCategories(GETPOST('categories', 'array'));
496 if ($result < 0) {
497 setEventMessages($object->error, $object->errors, 'errors');
498 }
499 } elseif ($action == 'add' && $usercancreate) {
500 // Create proposal
501 $object->socid = $socid;
502 $object->fetch_thirdparty();
503
504 $datep = dol_mktime(12, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
505 $date_delivery = dol_mktime(12, 0, 0, GETPOSTINT('date_livraisonmonth'), GETPOSTINT('date_livraisonday'), GETPOSTINT('date_livraisonyear'));
506 $duration = GETPOSTINT('duree_validite');
507
508 if (empty($datep)) {
509 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("DatePropal")), null, 'errors');
510 $action = 'create';
511 $error++;
512 }
513 if (empty($duration)) {
514 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ValidityDuration")), null, 'errors');
515 $action = 'create';
516 $error++;
517 }
518
519 if ($socid < 1) {
520 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Customer")), null, 'errors');
521
522 $action = 'create';
523 $error++;
524 }
525
526 if (!$error) {
527 $db->begin();
528
529 // If we select proposal to clone during creation (when option PROPAL_CLONE_ON_CREATE_PAGE is on)
530 if (GETPOST('createmode') == 'copy' && GETPOST('copie_propal')) {
531 if ($object->fetch(GETPOSTINT('copie_propal')) > 0) {
532 $object->ref = GETPOST('ref');
533 $object->datep = $datep;
534 $object->date = $datep;
535 $object->delivery_date = $date_delivery;
536 $object->availability_id = GETPOSTINT('availability_id');
537 $object->demand_reason_id = GETPOSTINT('demand_reason_id');
538 $object->fk_delivery_address = GETPOSTINT('fk_address');
539 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
540 $object->warehouse_id = GETPOSTINT('warehouse_id');
541 $object->duree_validite = $duration;
542 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
543 $object->deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
544 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
545 $object->fk_account = GETPOSTINT('fk_account');
546 $object->socid = GETPOSTINT('socid');
547 $object->contact_id = GETPOSTINT('contactid');
548 $object->fk_project = GETPOSTINT('projectid');
549 $object->model_pdf = GETPOST('model', 'alphanohtml');
550 $object->author = $user; // deprecated
551 $object->user_author_id = $user->id;
552 $object->note_private = GETPOST('note_private', 'restricthtml');
553 $object->note_public = GETPOST('note_public', 'restricthtml');
556 $object->fk_incoterms = GETPOSTINT('incoterm_id');
557 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
558 } else {
559 setEventMessages($langs->trans("ErrorFailedToCopyProposal", GETPOST('copie_propal')), null, 'errors');
560 }
561 } else {
562 $object->ref = GETPOST('ref');
563 $object->ref_customer = GETPOST('ref_client');
564 $object->ref_client = $object->ref_customer;
565 $object->datep = $datep;
566 $object->date = $datep;
567 $object->delivery_date = $date_delivery;
568 $object->availability_id = GETPOSTINT('availability_id');
569 $object->demand_reason_id = GETPOSTINT('demand_reason_id');
570 $object->fk_delivery_address = GETPOSTINT('fk_address');
571 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
572 $object->warehouse_id = GETPOSTINT('warehouse_id');
573 $object->duree_validite = price2num(GETPOST('duree_validite', 'alpha'));
574 $object->cond_reglement_id = GETPOSTINT('cond_reglement_id');
575 $object->deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
576 $object->mode_reglement_id = GETPOSTINT('mode_reglement_id');
577 $object->fk_account = GETPOSTINT('fk_account');
578 $object->contact_id = GETPOSTINT('contactid');
579 $object->fk_project = GETPOSTINT('projectid');
580 $object->model_pdf = GETPOST('model');
581 $object->author = $user; // deprecated
582 $object->user_author_id = $user->id;
583 $object->note_private = GETPOST('note_private', 'restricthtml');
584 $object->note_public = GETPOST('note_public', 'restricthtml');
585 $object->fk_incoterms = GETPOSTINT('incoterm_id');
586 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
587
588 $object->origin = GETPOST('origin');
589 $object->origin_id = GETPOSTINT('originid');
590
591 // Multicurrency
592 if (isModEnabled("multicurrency")) {
593 $object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
594 }
595
596 // Fill array 'array_options' with data from add form
597 $ret = $extrafields->setOptionalsFromPost(null, $object);
598 if ($ret < 0) {
599 $error++;
600 $action = 'create';
601 }
602 }
603
604 if (!$error) {
605 if ($origin && $originid) {
606 // Parse element/subelement (ex: project_task)
607 $element = $subelement = $origin;
608 $regs = array();
609 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
610 $element = $regs[1];
611 $subelement = $regs[2];
612 }
613
614 // For compatibility
615 if ($element == 'order') {
616 $element = $subelement = 'commande';
617 }
618 if ($element == 'propal') {
619 $element = 'comm/propal';
620 $subelement = 'propal';
621 }
622 if ($element == 'contract') {
623 $element = $subelement = 'contrat';
624 }
625 if ($element == 'inter') {
626 $element = $subelement = 'fichinter';
627 }
628 if ($element == 'shipping') {
629 $element = $subelement = 'expedition';
630 }
631
632 // If this is a renewal proposal for a contract, we might choose to systematically create a new contract,
633 if (($origin != 'contrat' || $renewal != 'true') && !getDolGlobalInt('CONTRACT_NEW_CONTRACT_ON_RENEWAL')) {
634 $object->origin = $origin;
635 $object->origin_id = $originid;
636
637 // Possibility to add external linked objects with hooks
638 $object->linked_objects[$object->origin] = $object->origin_id;
639 }
640 if (GETPOSTISARRAY('other_linked_objects')) {
641 $object->linked_objects = array_merge($object->linked_objects, GETPOST('other_linked_objects', 'array:int'));
642 }
643
644 $id = $object->create($user);
645 if ($id > 0) {
646 dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
647
648 $classname = ucfirst($subelement);
649 $srcobject = new $classname($db);
650 '@phan-var-force Commande|Propal|Contrat|Fichinter|Expedition $srcobject'; // Can be other class, but CommonObject is too generic
653 dol_syslog("Try to find source object origin=" . $object->origin . " originid=" . $object->origin_id . " to add lines");
654 $result = $srcobject->fetch($object->origin_id);
655
656 if ($result > 0) {
657 $lines = $srcobject->lines;
658 if (empty($lines) && method_exists($srcobject, 'fetch_lines')) {
659 $srcobject->fetch_lines();
660 $lines = $srcobject->lines;
661 }
662
663 $fk_parent_line = 0;
664 $num = count($lines);
665 for ($i = 0; $i < $num; $i++) {
666 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : '');
667 $desc = (!empty($lines[$i]->desc) ? $lines[$i]->desc : '');
668
669 // Positive line
670 $product_type = ($lines[$i]->product_type ? (int) $lines[$i]->product_type : 0);
671
672 // Date start
673 $date_start = false;
674 if ($lines[$i]->date_debut_prevue) {
675 $date_start = $lines[$i]->date_debut_prevue;
676 }
677 if ($lines[$i]->date_debut_reel) {
678 $date_start = $lines[$i]->date_debut_reel;
679 }
680 if ($lines[$i]->date_start) {
681 $date_start = $lines[$i]->date_start;
682 }
683
684 // Date end
685 $date_end = false;
686 if ($lines[$i]->date_fin_prevue) {
687 $date_end = $lines[$i]->date_fin_prevue;
688 }
689 if ($lines[$i]->date_fin_reel) {
690 $date_end = $lines[$i]->date_fin_reel;
691 }
692 if ($lines[$i]->date_end) {
693 $date_end = $lines[$i]->date_end;
694 }
695
696 // For a contract renewal, we report duration, starting after the end of contract
697 if ($origin == 'contrat' && $renewal == 'true') {
698 if ($lines[$i]->date_start && $lines[$i]->date_end) {
699 $duration = $lines[$i]->date_end - $lines[$i]->date_start;
700 $date_start = $lines[$i]->date_end + 86400;
701 $date_end = $date_start + $duration;
702 }
703 }
704
705 // Reset fk_parent_line for no child products and special product
706 if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) {
707 $fk_parent_line = 0;
708 }
709
710 // Extrafields
711 if (method_exists($lines[$i], 'fetch_optionals')) {
712 $lines[$i]->fetch_optionals();
713 $array_options = $lines[$i]->array_options;
714 }
715
716 $tva_tx = $lines[$i]->tva_tx;
717 if (!empty($lines[$i]->vat_src_code) && !preg_match('/\‍(/', $tva_tx)) {
718 $tva_tx .= ' (' . $lines[$i]->vat_src_code . ')';
719 }
720
721 $result = $object->addline($desc, $lines[$i]->subprice, $lines[$i]->qty, $tva_tx, $lines[$i]->localtax1_tx, $lines[$i]->localtax2_tx, $lines[$i]->fk_product, $lines[$i]->remise_percent, 'HT', 0, $lines[$i]->info_bits, $product_type, $lines[$i]->rang, $lines[$i]->special_code, $fk_parent_line, $lines[$i]->fk_fournprice, $lines[$i]->pa_ht, $label, $date_start, $date_end, $array_options, $lines[$i]->fk_unit);
722
723 if ($result > 0) {
724 $lineid = $result;
725 } else {
726 $lineid = 0;
727 $error++;
728 break;
729 }
730
731 // Keep the extra parameters of the source line (for example the options of subtotal lines)
732 if ($result > 0 && !empty($lines[$i]->extraparams)) {
733 $newline = new PropaleLigne($db);
734 $newline->id = $result;
735 $newline->extraparams = $lines[$i]->extraparams;
736 $newline->setExtraParameters();
737 }
738
739 // Defined the new fk_parent_line
740 if ($result > 0 && $lines[$i]->product_type == 9) {
741 $fk_parent_line = $result;
742 }
743 }
744
745 // Hooks
746 $parameters = array('objFrom' => $srcobject);
747 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
748 // modified by hook
749 if ($reshook < 0) {
750 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
751 $error++;
752 }
753 } else {
754 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
755 $error++;
756 }
757 } else {
758 setEventMessages($object->error, $object->errors, 'errors');
759 $error++;
760 }
761 } else {
762 // Standard creation
763 $id = $object->create($user);
764 }
765
766 if ($id > 0) {
767 // Insert default contacts if defined
768 if (GETPOST('contactid') > 0) {
769 $result = $object->add_contact(GETPOSTINT('contactid'), 'CUSTOMER', 'external');
770 if ($result < 0) {
771 $error++;
772 setEventMessages($langs->trans("ErrorFailedToAddContact"), null, 'errors');
773 }
774 }
775
776 if (getDolGlobalString('PROPOSAL_AUTO_ADD_AUTHOR_AS_CONTACT')) {
777 $result = $object->add_contact($user->id, 'SALESREPFOLL', 'internal');
778 if ($result < 0) {
779 $error++;
780 setEventMessages($langs->trans("ErrorFailedToAddUserAsContact"), null, 'errors');
781 }
782 }
783 if (isModEnabled('category')) {
784 $categories = GETPOST('categories', 'array');
785 if (method_exists($object, 'setCategories')) {
786 $object->setCategories($categories);
787 }
788 }
789
790 if (!$error) {
791 $db->commit();
792
793 // Define output language
794 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
795 $outputlangs = $langs;
796 $newlang = '';
797 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
798 $newlang = GETPOST('lang_id', 'aZ09');
799 }
800 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
801 $newlang = $object->thirdparty->default_lang;
802 }
803 if (!empty($newlang)) {
804 $outputlangs = new Translate("", $conf);
805 $outputlangs->setDefaultLang($newlang);
806 }
807 $model = $object->model_pdf;
808
809 $ret = $object->fetch($id); // Reload to get new records
810 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
811 if ($result < 0) {
812 dol_print_error($db, $object->error, $object->errors);
813 }
814 }
815
816 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
817 exit();
818 } else {
819 $db->rollback();
820 $action = 'create';
821 }
822 } else {
823 setEventMessages($object->error, $object->errors, 'errors');
824 $db->rollback();
825 $action = 'create';
826 }
827 }
828 }
829 } elseif ($action == 'classifybilled' && $usercanclose) {
830 // Classify billed
831 $db->begin();
832
833 $result = $object->classifyBilled($user, 0, '');
834 if ($result < 0) {
835 setEventMessages($object->error, $object->errors, 'errors');
836 $error++;
837 }
838
839 if (!$error) {
840 $db->commit();
841 } else {
842 $db->rollback();
843 }
844 } elseif ($action == 'confirm_closeas' && $usercanclose && !GETPOST('cancel', 'alpha')) {
845 // Close proposal
846 if (!(GETPOSTINT('statut') > 0)) {
847 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CloseAs")), null, 'errors');
848 $action = 'closeas';
849 } elseif (GETPOSTINT('statut') == $object::STATUS_SIGNED || GETPOSTINT('statut') == $object::STATUS_NOTSIGNED) {
850 $locationTarget = '';
851 // prevent browser refresh from closing proposal several times
852 if ($object->status == $object::STATUS_VALIDATED || (getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE') && $object->status == $object::STATUS_DRAFT)) {
853 $db->begin();
854
855 $oldstatus = $object->status;
856
857 $result = $object->closeProposal($user, GETPOSTINT('statut'), GETPOST('note_private'));
858
859 if ($result < 0) {
860 setEventMessages($object->error, $object->errors, 'errors');
861 $error++;
862 } else {
863 // Needed if object linked modified by trigger (because linked objects can't be fetched two times : linkedObjectsFullLoaded)
864 $locationTarget = DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id;
865 }
866
867 $deposit = null;
868
869 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
870
871 if (
872 !$error && GETPOSTINT('statut') == $object::STATUS_SIGNED && GETPOST('generate_deposit') == 'on'
873 && !empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')
874 ) {
875 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
876
877 $date = dol_mktime(0, 0, 0, GETPOSTINT('datefmonth'), GETPOSTINT('datefday'), GETPOSTINT('datefyear'));
878 $forceFields = array();
879
880 if (GETPOSTISSET('date_pointoftax')) {
881 $forceFields['date_pointoftax'] = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'));
882 }
883
884 $deposit = Facture::createDepositFromOrigin($object, $date, GETPOSTINT('cond_reglement_id'), $user, 0, GETPOSTINT('validate_generated_deposit') == 'on', $forceFields);
885
886 if ($deposit) {
887 setEventMessage('DepositGenerated');
888 $locationTarget = DOL_URL_ROOT . '/compta/facture/card.php?id=' . $deposit->id;
889 } else {
890 $error++;
891 setEventMessages("Failed to create down payment - " . $object->error, $object->errors, 'errors');
892 }
893 }
894
895 if (!$error) {
896 $db->commit();
897
898 if ($deposit && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
899 $ret = $deposit->fetch($deposit->id); // Reload to get new records
900 $outputlangs = $langs;
901
902 if (getDolGlobalInt('MAIN_MULTILANGS')) {
903 $outputlangs = new Translate('', $conf);
904 $outputlangs->setDefaultLang($deposit->thirdparty->default_lang);
905 $outputlangs->load('products');
906 }
907
908 $result = $deposit->generateDocument($deposit->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
909
910 if ($result < 0) {
911 setEventMessages($deposit->error, $deposit->errors, 'errors');
912 }
913 }
914
915 if ($locationTarget) {
916 header('Location: ' . $locationTarget);
917 exit;
918 }
919 } else {
920 $object->status = $oldstatus;
921 $object->statut = $oldstatus; // deprecated
922
923 $db->rollback();
924 $action = '';
925 }
926 }
927 }
928 } elseif ($action == 'confirm_reopen' && $usercanreopen && !GETPOST('cancel', 'alpha')) {
929 // Reopen proposal
930 // prevent browser refresh from reopening proposal several times
932 $db->begin();
933
934 $newstatus = (getDolGlobalInt('PROPAL_SKIP_ACCEPT_REFUSE') ? Propal::STATUS_DRAFT : Propal::STATUS_VALIDATED);
935 $result = $object->reopen($user, $newstatus);
936 if ($result < 0) {
937 setEventMessages($object->error, $object->errors, 'errors');
938 $error++;
939 } else {
940 $object->statut = $newstatus; // deprecated
941 $object->status = $newstatus;
942 }
943
944 if (!$error) {
945 $db->commit();
946 } else {
947 $db->rollback();
948 }
949 }
950 } elseif ($action == 'import_lines_from_object' && $usercancreate && $object->status == Propal::STATUS_DRAFT) {
951 // add lines from objectlinked
952 $fromElement = GETPOST('fromelement');
953 $fromElementid = GETPOST('fromelementid');
954 $importLines = GETPOST('line_checkbox');
955
956 if (!empty($importLines) && is_array($importLines) && !empty($fromElement) && preg_match('/^[a-zA-Z]+$/', $fromElement) && !empty($fromElementid)) {
957 if ($fromElement == 'commande') {
958 dol_include_once('/' . $fromElement . '/class/' . $fromElement . '.class.php');
959 $lineClassName = 'OrderLine';
960 } elseif ($fromElement == 'propal') {
961 dol_include_once('/comm/' . $fromElement . '/class/' . $fromElement . '.class.php');
962 $lineClassName = 'PropaleLigne';
963 } elseif ($fromElement == 'facture') {
964 dol_include_once('/compta/' . $fromElement . '/class/' . $fromElement . '.class.php');
965 $lineClassName = 'FactureLigne';
966 } else {
967 $lineClassName = null;
968 }
969 $nextRang = count($object->lines) + 1;
970 $importCount = 0;
971 $error = 0;
972 foreach ($importLines as $lineId) {
973 $lineId = intval($lineId);
974 $originLine = new $lineClassName($db);
975 if (intval($fromElementid) > 0 && $originLine->fetch($lineId) > 0) {
976 $originLine->fetch_optionals();
977 $desc = $originLine->desc;
978 $pu_ht = $originLine->subprice;
979 $qty = $originLine->qty;
980 $txtva = $originLine->tva_tx;
981 $txlocaltax1 = $originLine->localtax1_tx;
982 $txlocaltax2 = $originLine->localtax2_tx;
983 $fk_product = $originLine->fk_product;
984 $remise_percent = $originLine->remise_percent;
985 $date_start = $originLine->date_start;
986 $date_end = $originLine->date_end;
987 $fk_code_ventilation = 0;
988 $info_bits = $originLine->info_bits;
989 $fk_remise_except = $originLine->fk_remise_except;
990 $price_base_type = 'HT';
991 $pu_ttc = 0;
992 $type = $originLine->product_type;
993 $rang = $nextRang++;
994 $special_code = $originLine->special_code;
995 $origin = $originLine->element;
996 $origin_id = $originLine->id;
997 $fk_parent_line = 0;
998 $fk_fournprice = $originLine->fk_fournprice;
999 $pa_ht = $originLine->pa_ht;
1000 $label = $originLine->label;
1001 $array_options = $originLine->array_options;
1002 $situation_percent = 100;
1003 $fk_prev_id = '';
1004 $fk_unit = $originLine->fk_unit;
1005 $pu_ht_devise = $originLine->multicurrency_subprice;
1006
1007 $res = $object->addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $price_base_type, $pu_ttc, $info_bits, $type, $rang, $special_code, $fk_parent_line, $fk_fournprice, $pa_ht, $label, $date_start, $date_end, $array_options, $fk_unit, $origin, $origin_id, $pu_ht_devise, $fk_remise_except);
1008
1009 if ($res > 0) {
1010 $importCount++;
1011 } else {
1012 $error++;
1013 }
1014 } else {
1015 $error++;
1016 }
1017 }
1018
1019 if ($error) {
1020 setEventMessages($langs->trans('ErrorsOnXLines', $error), null, 'errors');
1021 }
1022 }
1023 } elseif ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) {
1024 $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0;
1025 $result = $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva);
1026 if ($result < 0) {
1027 setEventMessages($object->error, $object->errors, 'errors');
1028 }
1029 } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) {
1030 $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0;
1031 $result = $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount);
1032 if ($result < 0) {
1033 setEventMessages($object->error, $object->errors, 'errors');
1034 }
1035 }
1036
1037 include DOL_DOCUMENT_ROOT . '/core/actions_printing.inc.php';
1038
1039 // Actions to send emails
1040 $actiontypecode = 'AC_EMAIL';
1041 $triggersendname = 'PROPAL_SENTBYMAIL';
1042 $autocopy = 'MAIN_MAIL_AUTOCOPY_PROPOSAL_TO';
1043 $trackid = 'pro' . $object->id;
1044 include DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php';
1045
1046
1047 // Go back to draft
1048 if ($action == 'modif' && $usercancreate) {
1049 $result = $object->setDraft($user);
1050 if ($result < 0) {
1051 setEventMessages($object->error, $object->errors, 'errors');
1052 }
1053
1054 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1055 // Define output language
1056 $outputlangs = $langs;
1057 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1058 $outputlangs = new Translate("", $conf);
1059 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
1060 $outputlangs->setDefaultLang($newlang);
1061 }
1062 $ret = $object->fetch($id); // Reload to get new records
1063 if ($ret > 0) {
1064 $object->fetch_thirdparty();
1065 }
1066 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1067 }
1068 } elseif ($action == "setabsolutediscount" && $usercancreate) {
1069 if (GETPOSTINT("remise_id")) {
1070 if ($object->id > 0) {
1071 $result = $object->insert_discount(GETPOSTINT("remise_id"));
1072 if ($result < 0) {
1073 setEventMessages($object->error, $object->errors, 'errors');
1074 }
1075 }
1076 }
1077 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
1078 // Define date start and date end for all line
1079 $alldate_start = dol_mktime(GETPOSTINT('alldate_starthour'), GETPOSTINT('alldate_startmin'), 0, GETPOSTINT('alldate_startmonth'), GETPOSTINT('alldate_startday'), GETPOSTINT('alldate_startyear'));
1080 $alldate_end = dol_mktime(GETPOSTINT('alldate_endhour'), GETPOSTINT('alldate_endmin'), 0, GETPOSTINT('alldate_endmonth'), GETPOSTINT('alldate_endday'), GETPOSTINT('alldate_endyear'));
1081 foreach ($object->lines as $key => $line) {
1082 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1083 continue;
1084 }
1085 if ($line->product_type == 1) { // only service line
1086 // Preserve the original entry mode of the line so the total is not drifted by rounding.
1087 $line_price_base_type = $line->wasEnteredIncludingTax() ? 'TTC' : 'HT';
1088 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
1089 $result = $object->updateline($line->id, $line_pu, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, $line_price_base_type, $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $alldate_start, $alldate_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice);
1090 $object->lines[$key] = $object->line;
1091 }
1092 }
1093 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
1094 // Define a vat_rate for all lines
1095 $vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
1096 $vat_rate = str_replace('*', '', $vat_rate);
1097 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
1098 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
1099 foreach ($object->lines as $key => $line) {
1100 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1101 continue;
1102 }
1103 // Preserve the original entry mode of the line so the total is not drifted by rounding.
1104 $line_price_base_type = $line->wasEnteredIncludingTax() ? 'TTC' : 'HT';
1105 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
1106 $result = $object->updateline($line->id, $line_pu, $line->qty, $line->remise_percent, $vat_rate, $localtax1_rate, $localtax2_rate, $line->desc, $line_price_base_type, $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice);
1107 $object->lines[$key] = $object->line;
1108 }
1109 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('remiseforalllines', 'alpha') !== '' && $usercancreate) {
1110 // Define a discount for all lines
1111 $remise_percent = (GETPOST('remiseforalllines') ? GETPOST('remiseforalllines') : 0);
1112 $remise_percent = str_replace('*', '', $remise_percent);
1113 foreach ($object->lines as $key => $line) {
1114 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1115 continue;
1116 }
1117 $tvatx = $line->tva_tx;
1118 if (!empty($line->vat_src_code)) {
1119 $tvatx .= ' (' . $line->vat_src_code . ')';
1120 }
1121 // Preserve the original entry mode of the line so the total is not drifted by rounding.
1122 $line_price_base_type = $line->wasEnteredIncludingTax() ? 'TTC' : 'HT';
1123 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
1124 $result = $object->updateline($line->id, $line_pu, $line->qty, (float) $remise_percent, $tvatx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, $line_price_base_type, $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice);
1125 $object->lines[$key] = $object->line;
1126 }
1127 } elseif ($action == 'addline' && GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '' && $usercancreate) {
1128 // Define margin
1129 $margin_rate = (GETPOST('marginforalllines', 'alpha') ? GETPOST('marginforalllines', 'alpha') : 0);
1130 foreach ($object->lines as $key => $line) {
1131 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1132 continue;
1133 }
1134 $subprice = price2num($line->pa_ht * (1 + $margin_rate / 100), 'MU');
1135 $prod = new Product($db);
1136 $prod->fetch($line->fk_product);
1137 if ($prod->price_min > $subprice) {
1138 $price_subprice = price($subprice, 0, $outlangs, 1, -1, -1, 'auto');
1139 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
1140 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
1141 }
1142 // Manage $line->subprice and $line->multicurrency_subprice
1143 if ($line->subprice <> 0) {
1144 $multicurrency_subprice = (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1145 } else {
1146 $multicurrency_subprice = 0;
1147 }
1148 // Update DB
1149 $result = $object->updateline($line->id, (float) $subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $multicurrency_subprice);
1150 $object->lines[$key] = $object->line;
1151 // Update $object with new margin info
1152 // $line->price = $subprice;
1153 // $line->marge_tx = $margin_rate;
1154 // $line->marque_tx = $margin_rate * $line->pa_ht / (float) $subprice;
1155 // $line->total_ht = $line->qty * (float) $subprice;
1156 // $line->total_tva = $line->tva_tx * $line->qty * (float) $subprice;
1157 // $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice;
1158 // // Manage $line->subprice and $line->multicurrency_subprice
1159 // $line->multicurrency_total_ht = $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1160 // $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1161 // $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1162 // // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
1163 // $line->subprice = (float) $subprice;
1164 // $line->multicurrency_subprice = $multicurrency_subprice;
1165 }
1166 } elseif ($action == 'confirm_addtitleline' && $usercancreate) {
1167 // Handling adding a new title line for subtotals module
1168
1169 $langs->load('subtotals');
1170
1171 $desc = GETPOST('subtotallinedesc', 'alphanohtml');
1172 $depth = GETPOSTINT('subtotallinelevel') ?? 1;
1173
1174 $subtotal_options = array();
1175
1176 foreach (Propal::$TITLE_OPTIONS as $option) {
1177 $value = GETPOST($option, 'alphanohtml');
1178 if ($value) {
1179 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1180 }
1181 }
1182
1183 // Insert line
1184 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
1185
1186 if ($result >= 0) {
1187 if ($result == 0) {
1188 setEventMessages($object->error, $object->errors, 'warnings');
1189 }
1190 $ret = $object->fetch($object->id); // Reload to get new records
1191 $object->fetch_thirdparty();
1192
1193 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1194 // Define output language
1195 $outputlangs = $langs;
1196 $newlang = GETPOST('lang_id', 'alpha');
1197 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1198 $newlang = $object->thirdparty->default_lang;
1199 }
1200 if (!empty($newlang)) {
1201 $outputlangs = new Translate("", $conf);
1202 $outputlangs->setDefaultLang($newlang);
1203 }
1204
1205 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1206 }
1207 } else {
1208 setEventMessages($object->error, $object->errors, 'errors');
1209 }
1210 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
1211 exit();
1212 } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) {
1213 // Handling adding a new subtotal line for subtotals module
1214
1215 $langs->load('subtotals');
1216
1217 $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml');
1218 foreach ($object->lines as $line) {
1219 if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) {
1220 $desc = $line->desc;
1221 $depth = -$line->qty;
1222 }
1223 }
1224
1225 $subtotal_options = array();
1226
1227 foreach (Propal::$SUBTOTAL_OPTIONS as $option) {
1228 $value = GETPOST($option, 'alphanohtml');
1229 if ($value) {
1230 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1231 }
1232 }
1233
1234 // Insert line
1235 if (isset($desc) && isset($depth)) {
1236 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
1237 } else {
1238 $result = -1;
1239 $object->errors[] = $langs->trans("CorrespondingTitleNotFound");
1240 }
1241
1242 if ($result >= 0) {
1243 $ret = $object->fetch($object->id); // Reload to get new records
1244 $object->fetch_thirdparty();
1245
1246 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1247 // Define output language
1248 $outputlangs = $langs;
1249 $newlang = GETPOST('lang_id', 'alpha');
1250 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1251 $newlang = $object->thirdparty->default_lang;
1252 }
1253 if (!empty($newlang)) {
1254 $outputlangs = new Translate("", $conf);
1255 $outputlangs->setDefaultLang($newlang);
1256 }
1257
1258 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1259 }
1260 } else {
1261 setEventMessages($object->error, $object->errors, 'errors');
1262 }
1263 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
1264 exit();
1265 } elseif ($action == 'addline' && !GETPOST('submitforalllines', 'alpha') && !GETPOST('submitforallmargins', 'alpha') && !GETPOST('markforalllines', 'alpha') && $usercancreate) {
1266 // Add line
1267 // Set if we used free entry or predefined product
1268 $predef = '';
1269 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
1270
1271 $price_ht = '';
1272 $price_ht_devise = '';
1273 $price_ttc = '';
1274 $price_ttc_devise = '';
1275
1276 // TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES'))
1277
1278 if (GETPOST('price_ht') !== '') {
1279 $price_ht = price2num(GETPOST('price_ht'), 'MU', 2);
1280 }
1281 if (GETPOST('multicurrency_price_ht') !== '') {
1282 $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
1283 }
1284 if (GETPOST('price_ttc') !== '') {
1285 $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2);
1286 }
1287 if (GETPOST('multicurrency_price_ttc') !== '') {
1288 $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2);
1289 }
1290
1291 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
1292 if ($prod_entry_mode == 'free') {
1293 $idprod = 0;
1294 } else {
1295 $idprod = GETPOSTINT('idprod');
1296
1297 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
1298 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
1299 $error++;
1300 }
1301 }
1302
1303 $tva_tx = GETPOST('tva_tx', 'alpha');
1304
1305 $qty = price2num(GETPOST('qty' . $predef, 'alpha'), 'MS', 2);
1306 $remise_percent = (GETPOSTISSET('remise_percent' . $predef) ? price2num(GETPOST('remise_percent' . $predef, 'alpha'), '', 2) : 0);
1307 if (empty($remise_percent)) {
1308 $remise_percent = 0;
1309 }
1310
1311 // Extrafields
1312 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1313 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
1314 // Unset extrafield
1315 if (is_array($extralabelsline)) {
1316 // Get extra fields
1317 foreach ($extralabelsline as $key => $value) {
1318 unset($_POST["options_" . $key]);
1319 }
1320 }
1321
1322 $price_to_test_sign = ($price_ht ? $price_ht : $price_ttc);
1323
1324 if ((empty($idprod) || $idprod < 0) && ($price_to_test_sign < 0) && ($qty < 0)) {
1325 $langs->load("errors");
1326 setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1327 $error++;
1328 }
1329 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
1330 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
1331 $error++;
1332 }
1333
1334 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && $price_ht === '' && $price_ht_devise === '' && $price_ttc === '' && $price_ttc_devise === '') { // Unit price can be 0 but not ''. Also price can be negative for proposal.
1335 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
1336 $error++;
1337 }
1338 if ($qty < 0 && !getDolGlobalString('PROPAL_ENABLE_NEGATIVE_QTY')) {
1339 setEventMessages($langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1340 $error++;
1341 }
1342 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
1343 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")), null, 'errors');
1344 $error++;
1345 }
1346
1347 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
1348 if ($combinations = GETPOST('combinations', 'array:alphanohtml')) {
1349 //Check if there is a product with the given combination
1350 $prodcomb = new ProductCombination($db);
1351
1352 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
1353 $idprod = $res->fk_product_child;
1354 } else {
1355 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
1356 $error++;
1357 }
1358 }
1359 }
1360
1361 if (!$error && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
1362 $pu_ht = 0;
1363 $pu_ttc = 0;
1364 $pu_ht_devise = 0;
1365 $pu_ttc_devise = 0;
1366 $price_min = 0;
1367 $price_min_ttc = 0;
1368 $tva_npr = 0;
1369 $price_base_type = (GETPOST('price_base_type', 'alpha') ? GETPOST('price_base_type', 'alpha') : 'HT');
1370
1371 $db->begin();
1372
1373 // $tva_tx can be 'x.x (XXX)'
1374
1375 // Ecrase $pu par celui du produit
1376 // Ecrase $desc par celui du produit
1377 // Replaces $fk_unit with the product unit
1378 if (!empty($idprod) && $idprod > 0) {
1379 $prod = new Product($db);
1380 $prod->fetch($idprod);
1381
1382 $label = ((GETPOST('product_label') && GETPOST('product_label') != $prod->label) ? GETPOST('product_label') : '');
1383
1384 // Update if prices fields are defined
1385 /*$tva_tx = get_default_tva($mysoc, $object->thirdparty, $prod->id);
1386 $tva_npr = get_default_npr($mysoc, $object->thirdparty, $prod->id);
1387 if (empty($tva_tx)) {
1388 $tva_npr = 0;
1389 }*/
1390
1391 // Price unique per product
1392 $pu_ht = $prod->price;
1393 $pu_ttc = $prod->price_ttc;
1394 $price_min = $prod->price_min;
1395 $price_min_ttc = $prod->price_min_ttc;
1396 $price_base_type = $prod->price_base_type;
1397
1398 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1399 // If price per customer
1400 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1401 $prodcustprice = new ProductCustomerPrice($db);
1402 $filter = array('t.fk_product' => (string) $prod->id, 't.fk_soc' => (string) $object->thirdparty->id);
1403
1404 // If a price per customer exist
1405 $pricebycustomerexist = false;
1406 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1407 if ($result >= 0) {
1408 // If there is some prices specific to the customer
1409 if (count($prodcustprice->lines) > 0) {
1410 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
1411 foreach ($prodcustprice->lines as $k => $custprice_line) {
1412 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
1413 $pricebycustomerexist = true;
1414 $pu_ht = price($custprice_line->price);
1415 $pu_ttc = price($custprice_line->price_ttc);
1416 $price_min = price($custprice_line->price_min);
1417 $price_min_ttc = price($custprice_line->price_min_ttc);
1418 $price_base_type = $custprice_line->price_base_type;
1419 /*$tva_tx = ($custprice_line->default_vat_code ? $custprice_line->tva_tx.' ('.$custprice_line->default_vat_code.' )' : $custprice_line->tva_tx);
1420 if ($custprice_line->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1421 $tva_tx .= ' ('.$custprice_line->default_vat_code.')';
1422 }
1423 $tva_npr = $custprice_line->recuperableonly;
1424 if (empty($tva_tx)) {
1425 $tva_npr = 0;
1426 }*/
1427 break;
1428 }
1429 }
1430 }
1431 } else {
1432 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
1433 }
1434
1435 if (!$pricebycustomerexist && !empty($object->thirdparty->price_level)) { // If price per segment
1436 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1437 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1438 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1439 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1440 $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level];
1441 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1442 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1443 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1444 }
1445 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1446 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1447 }
1448 }
1449 }
1450 } elseif (getDolGlobalString('PRODUIT_MULTIPRICES') && !empty($object->thirdparty->price_level)) { // If price per segment
1451 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1452 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1453 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1454 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1455 $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level];
1456 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1457 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1458 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1459 }
1460 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1461 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1462 }
1463 }
1464 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1465 // If price per customer
1466 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1467
1468 $prodcustprice = new ProductCustomerPrice($db);
1469
1470 $filter = array('t.fk_product' => (string) $prod->id, 't.fk_soc' => (string) $object->thirdparty->id);
1471
1472 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1473 if ($result >= 0) {
1474 // If there is some prices specific to the customer
1475 if (count($prodcustprice->lines) > 0) {
1476 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
1477 foreach ($prodcustprice->lines as $k => $custprice_line) {
1478 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
1479 $pu_ht = price($custprice_line->price);
1480 $pu_ttc = price($custprice_line->price_ttc);
1481 $price_min = price($custprice_line->price_min);
1482 $price_min_ttc = price($custprice_line->price_min_ttc);
1483 $price_base_type = $custprice_line->price_base_type;
1484 /*$tva_tx = ($custprice_line->default_vat_code ? $custprice_line->tva_tx.' ('.$custprice_line->default_vat_code.' )' : $custprice_line->tva_tx);
1485 if ($custprice_line->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1486 $tva_tx .= ' ('.$custprice_line->default_vat_code.')';
1487 }
1488 $tva_npr = $custprice_line->recuperableonly;
1489 if (empty($tva_tx)) {
1490 $tva_npr = 0;
1491 }*/
1492 break;
1493 }
1494 }
1495 }
1496 } else {
1497 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
1498 }
1499 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY')) {
1500 // If price per quantity
1501 if ($prod->prices_by_qty[0]) { // yes, this product has some prices per quantity
1502 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
1503 $pqp = GETPOSTINT('pbq');
1504
1505 // Search price into product_price_by_qty from $prod->id
1506 foreach ($prod->prices_by_qty_list[0] as $priceforthequantityarray) {
1507 if ($priceforthequantityarray['rowid'] != $pqp) {
1508 continue;
1509 }
1510 // We found the price
1511 if ($priceforthequantityarray['price_base_type'] == 'HT') {
1512 $pu_ht = $priceforthequantityarray['unitprice'];
1513 } else {
1514 $pu_ttc = $priceforthequantityarray['unitprice'];
1515 }
1516 // Note: the remise_percent or price by qty is used to set data on form, so we will use value from POST.
1517 break;
1518 }
1519 }
1520 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
1521 // If price per quantity and customer
1522 if (!empty($object->thirdparty->price_level)) {
1523 // The price of the level of the customer is the price to use when no price per quantity is selected
1524 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1525 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1526 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1527 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1528 $price_base_type = (empty($prod->multiprices_base_type[$object->thirdparty->price_level]) ? 'HT' : $prod->multiprices_base_type[$object->thirdparty->price_level]);
1529 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1530 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1531 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1532 }
1533 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1534 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1535 }
1536 }
1537 }
1538 if (!empty($object->thirdparty->price_level) && !empty($prod->prices_by_qty[$object->thirdparty->price_level])) { // yes, this product has some prices per quantity
1539 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
1540 $pqp = GETPOSTINT('pbq');
1541
1542 // Search price into product_price_by_qty from $prod->id
1543 foreach ($prod->prices_by_qty_list[$object->thirdparty->price_level] as $priceforthequantityarray) {
1544 if ($priceforthequantityarray['rowid'] != $pqp) {
1545 continue;
1546 }
1547 // We found the price
1548 if ($priceforthequantityarray['price_base_type'] == 'HT') {
1549 $pu_ht = $priceforthequantityarray['unitprice'];
1550 } else {
1551 $pu_ttc = $priceforthequantityarray['unitprice'];
1552 }
1553 // Note: the remise_percent or price by qty is used to set data on form, so we will use value from POST.
1554 break;
1555 }
1556 }
1557 }
1558
1559 $tmpvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
1560 $tmpprodvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', (string) $prod->tva_tx));
1561
1562 // Set unit price to use.
1563 // Force price_base_type to match what the user actually entered, so the total is computed
1564 // from the typed value and not from the converted/rounded value (avoids 0.01 rounding drift).
1565 if (!empty($price_ht) || (string) $price_ht === '0') {
1566 $pu_ht = (float) price2num($price_ht, 'MU');
1567 $pu_ttc = (float) price2num((float) $pu_ht * (1 + ((float) $tmpvat / 100)), 'MU');
1568 $price_base_type = 'HT';
1569 } elseif (!empty($price_ht_devise) || (string) $price_ht_devise === '0') {
1570 $pu_ht_devise = price2num($price_ht_devise, 'MU');
1571 $pu_ttc_devise = (float) price2num((float) $pu_ht_devise * (1 + ((float) $tmpvat / 100)), 'MU');
1572 $pu_ht = '';
1573 $pu_ttc = '';
1574 $price_base_type = 'HT';
1575 } elseif (!empty($price_ttc) || (string) $price_ttc === '0') {
1576 $pu_ttc = (float) price2num($price_ttc, 'MU');
1577 $pu_ht = (float) price2num((float) $pu_ttc / (1 + ((float) $tmpvat / 100)), 'MU');
1578 $price_base_type = 'TTC';
1579 } elseif (!empty($price_ttc_devise) || (string) $price_ttc_devise === '0') {
1580 $pu_ttc_devise = (float) price2num($price_ttc_devise, 'MU');
1581 $pu_ht_devise = (float) price2num((float) $pu_ttc_devise / (1 + ((float) $tmpvat / 100)), 'MU');
1582 $pu_ht = '';
1583 $pu_ttc = '';
1584 $price_base_type = 'TTC';
1585 } elseif ($tmpvat != $tmpprodvat) {
1586 // Is this still used ?
1587 if ($price_base_type != 'HT') {
1588 $pu_ht = (float) price2num((float) $pu_ttc / (1 + ((float) $tmpvat / 100)), 'MU');
1589 } else {
1590 $pu_ttc = (float) price2num((float) $pu_ht * (1 + ((float) $tmpvat / 100)), 'MU');
1591 }
1592 }
1593
1594 $desc = '';
1595
1596 // Define output language
1597 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1598 $outputlangs = $langs;
1599 $newlang = '';
1600 if (/* empty($newlang) && */GETPOST('lang_id', 'aZ09')) {
1601 $newlang = GETPOST('lang_id', 'aZ09');
1602 }
1603 if (empty($newlang)) {
1604 $newlang = $object->thirdparty->default_lang;
1605 }
1606 if (!empty($newlang)) {
1607 $outputlangs = new Translate("", $conf);
1608 $outputlangs->setDefaultLang($newlang);
1609 }
1610
1611 $desc = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description;
1612 } else {
1613 $desc = $prod->description;
1614 }
1615
1616 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
1617 // 'DoNotAutofillButAutoConcat'
1618 $desc = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
1619 } else {
1620 //'AutoFillFormFieldBeforeSubmit' or 'DoNotUseDescriptionOfProdut' => User has already done the modification they want
1621 $desc = $line_desc;
1622 }
1623
1624 // Add custom code and origin country into description
1625 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
1626 $tmptxt = '(';
1627 // Define output language
1628 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1629 $outputlangs = $langs;
1630 $newlang = '';
1631 if (/* empty($newlang) && */GETPOST('lang_id', 'alpha')) {
1632 $newlang = GETPOST('lang_id', 'alpha');
1633 }
1634 if (empty($newlang)) {
1635 $newlang = $object->thirdparty->default_lang;
1636 }
1637 if (!empty($newlang)) {
1638 $outputlangs = new Translate("", $conf);
1639 $outputlangs->setDefaultLang($newlang);
1640 $outputlangs->load('products');
1641 }
1642 if (!empty($prod->customcode)) {
1643 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode") . ': ' . $prod->customcode;
1644 }
1645 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1646 $tmptxt .= ' - ';
1647 }
1648 if (!empty($prod->country_code)) {
1649 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin") . ': ' . getCountry($prod->country_code, '', $db, $outputlangs, 0);
1650 }
1651 } else {
1652 if (!empty($prod->customcode)) {
1653 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode") . ': ' . $prod->customcode;
1654 }
1655 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1656 $tmptxt .= ' - ';
1657 }
1658 if (!empty($prod->country_code)) {
1659 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin") . ': ' . getCountry($prod->country_code, '', $db, $langs, 0);
1660 }
1661 }
1662 $tmptxt .= ')';
1663 $desc = dol_concatdesc($desc, $tmptxt);
1664 }
1665
1666 $type = $prod->type;
1667 $fk_unit = $prod->fk_unit;
1668 } else {
1669 $pu_ht = price2num($price_ht, 'MU');
1670 $pu_ttc = price2num($price_ttc, 'MU');
1671 $tva_npr = (preg_match('/\*/', $tva_tx) ? 1 : 0);
1672 $tva_tx = str_replace('*', '', $tva_tx);
1673 if (empty($tva_tx)) {
1674 $tva_npr = 0;
1675 }
1676 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
1677 $desc = $line_desc;
1678 $type = GETPOST('type');
1679 $fk_unit = GETPOST('units', 'alpha');
1680 $pu_ht_devise = price2num($price_ht_devise, 'MU');
1681 $pu_ttc_devise = price2num($price_ttc_devise, 'MU');
1682
1683 if ($pu_ttc && !$pu_ht) {
1684 $price_base_type = 'TTC';
1685 }
1686 }
1687
1688 $info_bits = 0;
1689 if ($tva_npr) {
1690 $info_bits |= 0x01;
1691 }
1692
1693 // Local Taxes
1694 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty, $mysoc, $tva_npr);
1695 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty, $mysoc, $tva_npr);
1696
1697 // Margin
1698 $fournprice = (int) (GETPOST('fournprice' . $predef) ? GETPOST('fournprice' . $predef) : ''); // This can be id of supplier price, or 'pmpprice' or 'costprice', or 'inputprice', we force to keep ID only
1699 $buyingprice = price2num((GETPOST('buying_price' . $predef) != '' ? GETPOST('buying_price' . $predef) : ''), '', 2); // If buying_price is '0', we must keep this value
1700
1701 $date_start = dol_mktime(GETPOSTINT('date_start' . $predef . 'hour'), GETPOSTINT('date_start' . $predef . 'min'), GETPOSTINT('date_start' . $predef . 'sec'), GETPOSTINT('date_start' . $predef . 'month'), GETPOSTINT('date_start' . $predef . 'day'), GETPOSTINT('date_start' . $predef . 'year'));
1702 $date_end = dol_mktime(GETPOSTINT('date_end' . $predef . 'hour'), GETPOSTINT('date_end' . $predef . 'min'), GETPOSTINT('date_end' . $predef . 'sec'), GETPOSTINT('date_end' . $predef . 'month'), GETPOSTINT('date_end' . $predef . 'day'), GETPOSTINT('date_end' . $predef . 'year'));
1703
1704 // Prepare a price equivalent for minimum price check
1705 $pu_equivalent = $pu_ht;
1706 $pu_equivalent_ttc = $pu_ttc;
1707 $currency_tx = $object->multicurrency_tx;
1708
1709 // Check if we have a foreign currency
1710 // If so, we update the pu_equiv as the equivalent price in base currency
1711 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1712 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
1713 }
1714 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1715 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
1716 }
1717
1718 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
1719 /*
1720 if ($pu_equivalent) {
1721 $tmp = calcul_price_total(1, $pu_equivalent, 0, $tva_tx, -1, -1, 0, 'HT', $info_bits, $type);
1722 $pu_equivalent_ttc = ...
1723 } else {
1724 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $tva_tx, -1, -1, 0, 'TTC', $info_bits, $type);
1725 $pu_equivalent_ht = ...
1726 }
1727 */
1728
1729 //var_dump(price2num($price_min)); var_dump(price2num($pu_ht)); var_dump($remise_percent);
1730 //var_dump(price2num($price_min_ttc)); var_dump(price2num($pu_ttc)); var_dump($remise_percent);exit;
1731
1732 //$desc = dol_htmlcleanlastbr($desc);
1733
1734 // Check price is not lower than minimum
1735 if ($usermustrespectpricemin) {
1736 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
1737 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
1738 setEventMessages($mesg, null, 'errors');
1739 $error++;
1740 } elseif ($pu_equivalent_ttc && $price_min_ttc && (((float) price2num($pu_equivalent_ttc) * (1 - $remise_percent / 100)) < (float) price2num($price_min_ttc)) && $price_base_type == 'TTC') {
1741 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
1742 setEventMessages($mesg, null, 'errors');
1743 $error++;
1744 }
1745 }
1746
1747 if (!$error) {
1748 // Insert line
1749 $result = $object->addline($desc, $pu_ht, (float) $qty, $tva_tx, $localtax1_tx, $localtax2_tx, $idprod, $remise_percent, $price_base_type, $pu_ttc, $info_bits, $type, min($rank, count($object->lines) + 1), 0, GETPOSTINT('fk_parent_line'), (int) $fournprice, $buyingprice, $label, $date_start, $date_end, $array_options, $fk_unit, '', 0, (float) $pu_ht_devise);
1750
1751 if ($result > 0) {
1752 $db->commit();
1753
1754 $ret = $object->fetch($object->id); // Reload to get new records
1755 if ($ret > 0) {
1756 $object->fetch_thirdparty();
1757 }
1758
1759 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1760 // Define output language
1761 $outputlangs = $langs;
1762 $newlang = GETPOST('lang_id', 'alpha');
1763 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1764 $newlang = $object->thirdparty->default_lang;
1765 }
1766 if (!empty($newlang)) {
1767 $outputlangs = new Translate("", $conf);
1768 $outputlangs->setDefaultLang($newlang);
1769 }
1770 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1771 }
1772
1773 unset($_POST['prod_entry_mode']);
1774
1775 unset($_POST['qty']);
1776 unset($_POST['type']);
1777 unset($_POST['remise_percent']);
1778 unset($_POST['price_ht']);
1779 unset($_POST['multicurrency_price_ht']);
1780 unset($_POST['price_ttc']);
1781 unset($_POST['tva_tx']);
1782 unset($_POST['product_ref']);
1783 unset($_POST['product_label']);
1784 unset($_POST['product_desc']);
1785 unset($_POST['fournprice']);
1786 unset($_POST['buying_price']);
1787 unset($_POST['np_marginRate']);
1788 unset($_POST['np_markRate']);
1789 unset($_POST['dp_desc']);
1790 unset($_POST['idprod']);
1791 unset($_POST['units']);
1792
1793 unset($_POST['date_starthour']);
1794 unset($_POST['date_startmin']);
1795 unset($_POST['date_startsec']);
1796 unset($_POST['date_startday']);
1797 unset($_POST['date_startmonth']);
1798 unset($_POST['date_startyear']);
1799 unset($_POST['date_endhour']);
1800 unset($_POST['date_endmin']);
1801 unset($_POST['date_endsec']);
1802 unset($_POST['date_endday']);
1803 unset($_POST['date_endmonth']);
1804 unset($_POST['date_endyear']);
1805 } else {
1806 $db->rollback();
1807
1808 setEventMessages($object->error, $object->errors, 'errors');
1809 }
1810 }
1811 }
1812 } elseif ($action == 'addline' && $usercancreate && (
1813 (GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '') ||
1814 (GETPOST('submitforallmark', 'alpha') && GETPOST('markforalllines', 'alpha') !== '')
1815 )) {
1816 $outlangs = $langs;
1817 // Define margin
1818 $margin_rate = GETPOSTISSET('marginforalllines') ? GETPOST('marginforalllines', 'int') : '';
1819 $mark_rate = GETPOSTISSET('markforalllines') ? GETPOST('markforalllines', 'int') : '';
1820 foreach ($object->lines as &$line) {
1821 if ($line->subprice > 0) {
1822 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1823 continue;
1824 }
1825 $subprice_multicurrency = $line->subprice;
1826 if (is_numeric($margin_rate) && $margin_rate > 0) {
1827 $line->subprice = (float) price2num((float) $line->pa_ht * (1 + (float) $margin_rate / 100), 'MU');
1828 } elseif (is_numeric($mark_rate) && $mark_rate > 0) {
1829 $line->subprice = (float) ($line->pa_ht / (1 - ((float) $mark_rate / 100)));
1830 } else {
1831 $line->subprice = (float) $line->pa_ht;
1832 }
1833
1834 if ($line->fk_product > 0) {
1835 $prod = new Product($db);
1836 $res = $prod->fetch($line->fk_product);
1837 if ($res > 0) {
1838 if ($prod->price_min > $line->subprice) {
1839 $price_subprice = price($line->subprice, 0, $outlangs, 1, -1, -1, 'auto');
1840 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
1841 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
1842 } else {
1843 setEventMessages($prod->error, $prod->errors, 'errors');
1844 }
1845 } else {
1846 setEventMessages($prod->error, $prod->errors, 'errors');
1847 }
1848 }
1849
1850 // Manage $line->subprice and $line->multicurrency_subprice
1851 $multicurrency_subprice = (float) $line->subprice * $line->multicurrency_subprice / $subprice_multicurrency;
1852 // Update DB
1853 $result = $object->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $multicurrency_subprice);
1854 // Update $object with new margin info
1855 if ($result > 0) {
1856 if (is_numeric($margin_rate) && empty($mark_rate)) {
1857 $line->marge_tx = $margin_rate;
1858 } elseif (is_numeric($mark_rate) && empty($margin_rate)) {
1859 $line->marque_tx = $mark_rate;
1860 }
1861 $line->total_ht = $line->qty * (float) $line->subprice;
1862 $line->total_tva = $line->tva_tx * $line->qty * (float) $line->subprice;
1863 $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $line->subprice;
1864 // Manage $line->subprice and $line->multicurrency_subprice
1865 $line->multicurrency_total_ht = $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1866 $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1867 $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1868 // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
1869 $line->multicurrency_subprice = $multicurrency_subprice;
1870 } else {
1871 setEventMessages($object->error, $object->errors, 'errors');
1872 }
1873 }
1874 }
1875 } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
1876 // Handling updating a title line for subtotals module
1877
1878 $langs->load('subtotals');
1879
1880 $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title");
1881 $depth = GETPOSTINT('line_depth') ?? 1;
1882
1883 $subtotal_options = array();
1884
1885 foreach (Propal::$TITLE_OPTIONS as $option) {
1886 $value = GETPOST($option, 'alphanohtml');
1887 if ($value) {
1888 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1889 }
1890 }
1891
1892 // Update line
1893 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
1894
1895 if ($result >= 0) {
1896 if ($result == 0) {
1897 setEventMessages($object->error, $object->errors, 'warnings');
1898 }
1899 $ret = $object->fetch($object->id); // Reload to get new records
1900 $object->fetch_thirdparty();
1901
1902 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1903 // Define output language
1904 $outputlangs = $langs;
1905 $newlang = GETPOST('lang_id', 'alpha');
1906 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1907 $newlang = $object->thirdparty->default_lang;
1908 }
1909 if (!empty($newlang)) {
1910 $outputlangs = new Translate("", $conf);
1911 $outputlangs->setDefaultLang($newlang);
1912 }
1913
1914 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1915 }
1916 } else {
1917 setEventMessages($object->error, $object->errors, 'errors');
1918 }
1919 } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
1920 // Handling updating a subtotal line for subtotals module
1921
1922 $langs->load('subtotals');
1923
1924 $desc = GETPOST('line_desc', 'alphanohtml');
1925 $depth = GETPOSTINT('line_depth');
1926
1927 $subtotal_options = array();
1928
1929 foreach (Propal::$SUBTOTAL_OPTIONS as $option) {
1930 $value = GETPOST($option, 'alphanohtml');
1931 if ($value) {
1932 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1933 }
1934 }
1935
1936 // Update line
1937 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
1938
1939 if ($result > 0) {
1940 $ret = $object->fetch($object->id); // Reload to get new records
1941 $object->fetch_thirdparty();
1942
1943 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1944 // Define output language
1945 $outputlangs = $langs;
1946 $newlang = GETPOST('lang_id', 'alpha');
1947 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1948 $newlang = $object->thirdparty->default_lang;
1949 }
1950 if (!empty($newlang)) {
1951 $outputlangs = new Translate("", $conf);
1952 $outputlangs->setDefaultLang($newlang);
1953 }
1954
1955 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1956 }
1957 } else {
1958 setEventMessages($object->error, $object->errors, 'errors');
1959 }
1960 } elseif ($action == 'updateline' && $usercancreate && GETPOST('save')) {
1961 // Update a line
1962
1963 // Clean parameters
1964 $description = dol_htmlcleanlastbr(GETPOST('product_desc', 'restricthtml'));
1965
1966 // Define info_bits
1967 $info_bits = 0;
1968 if (preg_match('/\*/', GETPOST('tva_tx'))) {
1969 $info_bits |= 0x01;
1970 }
1971
1972 // Define vat_rate
1973 $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx', 'alpha') : 0);
1974 $vat_rate = str_replace('*', '', $vat_rate);
1975 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
1976 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
1977 $pu_ht = price2num(GETPOST('price_ht'), '', 2);
1978 $pu_ttc = price2num(GETPOST('price_ttc'), '', 2);
1979
1980 // Add buying price
1981 $fournprice = (int) (GETPOST('fournprice') ? GETPOST('fournprice') : ''); // This can be id of supplier price, or 'pmpprice' or 'costprice', or 'inputprice', we force to keep ID only
1982 $buyingprice = price2num((GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''), '', 2); // If buying_price is '0', we must keep this value
1983
1984 $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), '', 2);
1985 $pu_ttc_devise = price2num(GETPOST('multicurrency_subprice_ttc'), '', 2);
1986
1987 $date_start = dol_mktime(GETPOSTINT('date_starthour'), GETPOSTINT('date_startmin'), GETPOSTINT('date_startsec'), GETPOSTINT('date_startmonth'), GETPOSTINT('date_startday'), GETPOSTINT('date_startyear'));
1988 $date_end = dol_mktime(GETPOSTINT('date_endhour'), GETPOSTINT('date_endmin'), GETPOSTINT('date_endsec'), GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'));
1989
1990 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1991 if (empty($remise_percent)) {
1992 $remise_percent = 0;
1993 }
1994
1995 // Prepare a price equivalent for minimum price check
1996 $pu_equivalent = $pu_ht;
1997 $pu_equivalent_ttc = $pu_ttc;
1998
1999 $currency_tx = $object->multicurrency_tx;
2000
2001 // Check if we have a foreign currency
2002 // If so, we update the pu_equiv as the equivalent price in base currency
2003 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
2004 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
2005 }
2006 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
2007 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
2008 }
2009
2010 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
2011 /*
2012 if ($pu_equivalent) {
2013 $tmp = calcul_price_total(1, $pu_equivalent, 0, $vat_rate, -1, -1, 0, 'HT', $info_bits, $type);
2014 $pu_equivalent_ttc = ...
2015 } else {
2016 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $vat_rate, -1, -1, 0, 'TTC', $info_bits, $type);
2017 $pu_equivalent_ht = ...
2018 }
2019 */
2020
2021 // Extrafields
2022 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
2023 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
2024 // Unset extrafield
2025 if (is_array($extralabelsline)) {
2026 // Get extra fields
2027 foreach ($extralabelsline as $key => $value) {
2028 unset($_POST["options_" . $key]);
2029 }
2030 }
2031
2032 // Define special_code for special lines
2033 $special_code = GETPOSTINT('special_code');
2034 if (!GETPOST('qty')) {
2035 $special_code = 3;
2036 }
2037
2038 // The form JS clears the other field when the user edits one of them: only the modified field is filled.
2039 // When both fields are submitted, the user did not change the price - we must preserve the original
2040 // storage mode of the line, otherwise a no-op save would shift the total by rounding.
2041 $pu = $pu_ht;
2042 $price_base_type = 'HT';
2043 if (empty($pu_ht) && !empty($pu_ttc)) {
2044 $pu = $pu_ttc;
2045 $price_base_type = 'TTC';
2046 } elseif (!empty($pu_ht) && !empty($pu_ttc)) {
2047 foreach ($object->lines as $line_obj) {
2048 if ($line_obj->id == GETPOSTINT('lineid')) {
2049 if ($line_obj->wasEnteredIncludingTax()) {
2050 $pu = $pu_ttc;
2051 $price_base_type = 'TTC';
2052 }
2053 break;
2054 }
2055 }
2056 }
2057
2058 // Check minimum price
2059 $productid = GETPOSTINT('productid');
2060 if (!empty($productid)) {
2061 $product = new Product($db);
2062 $res = $product->fetch($productid);
2063
2064 $type = $product->type;
2065
2066 $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
2067
2068 $price_min = $product->price_min;
2069 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
2070 $price_min = $product->multiprices_min[$object->thirdparty->price_level];
2071 }
2072 $price_min_ttc = $product->price_min_ttc;
2073 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
2074 $price_min_ttc = $product->multiprices_min_ttc[$object->thirdparty->price_level];
2075 }
2076
2077 //var_dump(price2num($price_min)); var_dump(price2num($pu_ht)); var_dump($remise_percent);
2078 //var_dump(price2num($price_min_ttc)); var_dump(price2num($pu_ttc)); var_dump($remise_percent);exit;
2079
2080 // Check price is not lower than minimum
2081 if ($usermustrespectpricemin) {
2082 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2083 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2084 setEventMessages($mesg, null, 'errors');
2085 $error++;
2086 $action = 'editline';
2087 } elseif ($pu_equivalent_ttc && $price_min_ttc && (((float) price2num($pu_equivalent_ttc) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min_ttc)) && $price_base_type == 'TTC') {
2088 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2089 setEventMessages($mesg, null, 'errors');
2090 $error++;
2091 $action = 'editline';
2092 }
2093 }
2094 } else {
2095 $type = GETPOST('type');
2096 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2097
2098 // Check parameters
2099 if (GETPOST('type') < 0) {
2100 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
2101 $error++;
2102 }
2103 }
2104
2105 if (!$error) {
2106 $db->begin();
2107
2108 if (!$user->hasRight('margins', 'creer')) {
2109 foreach ($object->lines as &$line) {
2110 if ($line->id == GETPOSTINT('lineid')) {
2111 $fournprice = $line->fk_fournprice;
2112 $buyingprice = (string) $line->pa_ht; // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
2113 break;
2114 }
2115 }
2116 }
2117
2118 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
2119
2120 $result = $object->updateline(GETPOSTINT('lineid'), (float) $pu, (float) $qty, (float) $remise_percent, $vat_rate, $localtax1_rate, $localtax2_rate, $description, $price_base_type, $info_bits, $special_code, GETPOSTINT('fk_parent_line'), 0, (int) $fournprice, $buyingprice, $label, $type, $date_start, $date_end, $array_options, GETPOSTINT("units"), (float) $pu_ht_devise);
2121
2122 if ($result >= 0) {
2123 $db->commit();
2124
2125 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2126 // Define output language
2127 $outputlangs = $langs;
2128 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2129 $outputlangs = new Translate("", $conf);
2130 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
2131 $outputlangs->setDefaultLang($newlang);
2132 }
2133 $ret = $object->fetch($id); // Reload to get new records
2134 if ($ret > 0) {
2135 $object->fetch_thirdparty();
2136 }
2137 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2138 }
2139
2140 unset($_POST['qty']);
2141 unset($_POST['type']);
2142 unset($_POST['productid']);
2143 unset($_POST['remise_percent']);
2144 unset($_POST['price_ht']);
2145 unset($_POST['multicurrency_price_ht']);
2146 unset($_POST['price_ttc']);
2147 unset($_POST['tva_tx']);
2148 unset($_POST['product_ref']);
2149 unset($_POST['product_label']);
2150 unset($_POST['product_desc']);
2151 unset($_POST['fournprice']);
2152 unset($_POST['buying_price']);
2153
2154 unset($_POST['date_starthour']);
2155 unset($_POST['date_startmin']);
2156 unset($_POST['date_startsec']);
2157 unset($_POST['date_startday']);
2158 unset($_POST['date_startmonth']);
2159 unset($_POST['date_startyear']);
2160 unset($_POST['date_endhour']);
2161 unset($_POST['date_endmin']);
2162 unset($_POST['date_endsec']);
2163 unset($_POST['date_endday']);
2164 unset($_POST['date_endmonth']);
2165 unset($_POST['date_endyear']);
2166 } else {
2167 $db->rollback();
2168
2169 setEventMessages($object->error, $object->errors, 'errors');
2170 }
2171 }
2172 } elseif ($action == 'updateline' && $usercancreate && GETPOST('cancel', 'alpha')) {
2173 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To re-display card in edit mode
2174 exit();
2175 } elseif ($action == 'classin' && $usercancreate) {
2176 // Set project
2177 $object->setProject(GETPOSTINT('projectid'));
2178 } elseif ($action == 'setavailability' && $usercancreate) {
2179 // Delivery time
2180 $result = $object->set_availability($user, GETPOSTINT('availability_id'));
2181 } elseif ($action == 'setdemandreason' && $usercancreate) {
2182 // Origin of the commercial proposal
2183 $result = $object->set_demand_reason($user, GETPOSTINT('demand_reason_id'));
2184 } elseif ($action == 'setconditions' && $usercancreate) {
2185 // Terms of payment
2186 $result = $object->setPaymentTerms(GETPOSTINT('cond_reglement_id'), GETPOSTFLOAT('cond_reglement_id_deposit_percent'));
2187 } elseif ($action == 'setmode' && $usercancreate) {
2188 // Payment choice
2189 $result = $object->setPaymentMethods(GETPOSTINT('mode_reglement_id'));
2190 } elseif ($action == 'setmulticurrencycode' && $usercancreate) {
2191 // Multicurrency Code
2192 $result = $object->setMulticurrencyCode(GETPOST('multicurrency_code', 'alpha'));
2193 } elseif ($action == 'setmulticurrencyrate' && $usercancreate) {
2194 // Multicurrency rate
2195 $result = $object->setMulticurrencyRate(GETPOSTFLOAT('multicurrency_tx'), GETPOSTINT('calculation_mode'));
2196 } elseif ($action == 'setbankaccount' && $usercancreate) {
2197 // bank account
2198 $result = $object->setBankAccount(GETPOSTINT('fk_account'));
2199 } elseif ($action == 'setshippingmethod' && $usercancreate) {
2200 // shipping method
2201 $result = $object->setShippingMethod(GETPOSTINT('shipping_method_id'));
2202 } elseif ($action == 'setwarehouse' && $usercancreate) {
2203 // warehouse
2204 $result = $object->setWarehouse(GETPOSTINT('warehouse_id'));
2205 } elseif ($action == 'update_extras' && $permissiontoeditextra) {
2206 $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty
2207
2208 $attribute_name = GETPOST('attribute', 'aZ09');
2209
2210 // Fill array 'array_options' with data from update form
2211 $result = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
2212 if ($result < 0) {
2213 $error++;
2214 }
2215
2216 if (!$error) {
2217 $result = $object->updateExtraField($attribute_name, 'PROPAL_MODIFY');
2218 if ($result < 0) {
2219 setEventMessages($object->error, $object->errors, 'errors');
2220 $error++;
2221 }
2222 }
2223 if ($error) {
2224 $action = 'edit_extras';
2225 }
2226 }
2227
2228 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
2229 if ($action == 'addcontact' && $usercancreate) {
2230 $result = 0;
2231 if ($object->id > 0) {
2232 $contactid = (GETPOST('userid') ? GETPOSTINT('userid') : GETPOSTINT('contactid'));
2233 $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type'));
2234 $result = $object->add_contact($contactid, $typeid, GETPOST("source", 'aZ09'));
2235 }
2236
2237 if ($result >= 0) {
2238 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
2239 exit();
2240 } else {
2241 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2242 $langs->load("errors");
2243 setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
2244 } else {
2245 setEventMessages($object->error, $object->errors, 'errors');
2246 }
2247 }
2248 } elseif ($action == 'swapstatut' && $usercancreate) {
2249 // Toggle the status of a contact
2250 if ($object->fetch($id) > 0) {
2251 $result = $object->swapContactStatus(GETPOSTINT('ligne'));
2252 } else {
2254 }
2255 } elseif ($action == 'deletecontact' && $usercancreate) {
2256 // Delete a contact
2257 $object->fetch($id);
2258 $result = $object->delete_contact($lineid);
2259
2260 if ($result >= 0) {
2261 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
2262 exit();
2263 } else {
2265 }
2266 }
2267 }
2268
2269 // Actions to build doc
2270 $upload_dir = !empty($conf->propal->multidir_output[$object->entity ?? $conf->entity]) ? $conf->propal->multidir_output[$object->entity ?? $conf->entity] : $conf->propal->dir_output;
2271 $permissiontoadd = $usercancreate;
2272 include DOL_DOCUMENT_ROOT . '/core/actions_builddoc.inc.php';
2273}
2274
2275
2276/*
2277 * View
2278 */
2279
2280$form = new Form($db);
2281$formfile = new FormFile($db);
2282$formpropal = new FormPropal($db);
2283$formmargin = new FormMargin($db);
2284$formproject = null;
2285if (isModEnabled('project')) {
2286 $formproject = new FormProjets($db);
2287}
2288
2289$title = $object->ref . " - " . $langs->trans('Card');
2290if ($action == 'create') {
2291 $title = $langs->trans("NewPropal");
2292}
2293$help_url = 'EN:Commercial_Proposals|FR:Proposition_commerciale|ES:Presupuestos|DE:Modul_Angebote';
2294
2295llxHeader('', $title, $help_url);
2296
2297$now = dol_now();
2298
2299// Add new proposal
2300if ($action == 'create') {
2301 $currency_code = getDolCurrency();
2302
2303 print load_fiche_titre($langs->trans("NewProp"), '', 'propal');
2304
2305 $soc = new Societe($db);
2306 if ($socid > 0) {
2307 $res = $soc->fetch($socid);
2308 }
2309
2310 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
2311 $deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
2312 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
2313 $fk_account = GETPOSTINT('fk_account');
2314 $datepropal = (getDolGlobalString('MAIN_DO_NOT_AUTOFILL_DATE_PROPOSAL') ? -1 : ''); // By default '' so we will autofill date. -1 means keep empty.
2315
2316 // Load objectsrc
2317 $objectsrc = null;
2318 if (!empty($origin) && !empty($originid)) {
2319 // Parse element/subelement (ex: project_task)
2320 $element = $subelement = $origin;
2321 $regs = array();
2322 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
2323 $element = $regs[1];
2324 $subelement = $regs[2];
2325 }
2326
2327 if ($element == 'project') {
2328 $projectid = $originid;
2329
2330 // Fetch project and thirdparty
2331 $project = new Project($db);
2332 $project->fetch($projectid);
2333 if ($project->socid > 0) {
2334 $soc = new Societe($db);
2335 $soc->fetch($project->socid);
2336 }
2337 } else {
2338 // For compatibility
2339 if ($element == 'order' || $element == 'commande') {
2340 $element = $subelement = 'commande';
2341 }
2342 if ($element == 'propal') {
2343 $element = 'comm/propal';
2344 $subelement = 'propal';
2345 }
2346 if ($element == 'contract') {
2347 $element = $subelement = 'contrat';
2348 }
2349 if ($element == 'shipping') {
2350 $element = $subelement = 'expedition';
2351 }
2352
2353 dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
2354
2355 $classname = ucfirst($subelement);
2356 $objectsrc = new $classname($db);
2357 '@phan-var-force Commande|Propal|Contrat|Expedition $objectsrc'; // Can be other class, but CommonObject is too generic
2359 $objectsrc->fetch($originid);
2360 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
2361 $objectsrc->fetch_lines();
2362 }
2363 $objectsrc->fetch_thirdparty();
2364
2365 $projectid = (int) $objectsrc->fk_project;
2366 $ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : '');
2367
2368 $soc = $objectsrc->thirdparty;
2369
2370 $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 0));
2371 $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
2372 $warehouse_id = (!empty($objectsrc->warehouse_id) ? $objectsrc->warehouse_id : (!empty($soc->warehouse_id) ? $soc->warehouse_id : -1));
2373
2374 // Replicate extrafields
2375 $objectsrc->fetch_optionals();
2376 $object->array_options = $objectsrc->array_options;
2377
2378 if (isModEnabled("multicurrency")) {
2379 if (!empty($objectsrc->multicurrency_code)) {
2380 $currency_code = $objectsrc->multicurrency_code;
2381 }
2382 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($objectsrc->multicurrency_tx)) {
2383 $currency_tx = $objectsrc->multicurrency_tx;
2384 }
2385 }
2386 }
2387 }
2388
2389 // Load default values from thirdparty
2390 if (!empty($soc)) {
2391 $cond_reglement_id = empty($soc->cond_reglement_id) ? $cond_reglement_id : $soc->cond_reglement_id;
2392 $deposit_percent = empty($soc->deposit_percent) ? $deposit_percent : $soc->deposit_percent;
2393 $mode_reglement_id = empty($soc->mode_reglement_id) ? $mode_reglement_id : $soc->mode_reglement_id;
2394 $fk_account = empty($soc->fk_account) ? $fk_account : $soc->fk_account;
2395 $shipping_method_id = $soc->shipping_method_id;
2396 $warehouse_id = !empty($soc->fk_warehouse) ? $soc->fk_warehouse : $warehouse_id;
2397 $remise_percent = $soc->remise_percent;
2398
2399 if (isModEnabled("multicurrency") && !empty($soc->multicurrency_code)) {
2400 $currency_code = $soc->multicurrency_code;
2401 }
2402 }
2403
2404 // If form was posted (but error returned), we must reuse the value posted in priority (standard Dolibarr behaviour)
2405 if (!GETPOST('changecompany')) {
2406 if (GETPOSTISSET('cond_reglement_id')) {
2407 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
2408 }
2409 if (GETPOSTISSET('deposit_percent')) {
2410 $deposit_percent = price2num(GETPOST('deposit_percent', 'alpha'));
2411 }
2412 if (GETPOSTISSET('mode_reglement_id')) {
2413 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
2414 }
2415 if (GETPOSTISSET('fk_account')) {
2416 $fk_account = GETPOSTINT('fk_account');
2417 }
2418 if (GETPOSTISSET('shipping_method_id')) {
2419 $shipping_method_id = GETPOSTINT('shipping_method_id');
2420 }
2421 }
2422 // Warehouse default if null
2423 if ($soc->fk_warehouse > 0) {
2424 $warehouse_id = $soc->fk_warehouse;
2425 }
2426 if (isModEnabled('stock') && $warehouse_id < 0 && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2427 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
2428 $warehouse_id = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
2429 }
2430 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && !empty($user->fk_warehouse)) {
2431 $warehouse_id = $user->fk_warehouse;
2432 }
2433 }
2434
2435 print '<form name="addprop" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
2436 print '<input type="hidden" name="token" value="' . newToken() . '">';
2437 print '<input type="hidden" name="action" value="add">';
2438 print '<input type="hidden" name="changecompany" value="0">'; // will be set to 1 by javascript so we know post is done after a company change
2439 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
2440 if ($origin != 'project' && $originid) {
2441 print '<input type="hidden" name="origin" value="' . $origin . '">';
2442 print '<input type="hidden" name="originid" value="' . $originid . '">';
2443 if ($origin == 'contrat' && !empty($renewal)) {
2444 print '<input type="hidden" name="renewal" value="' . $renewal . '">';
2445 }
2446 } elseif ($origin == 'project' && !empty($projectid)) {
2447 print '<input type="hidden" name="projectid" value="' . $projectid . '">';
2448 }
2449
2450 print dol_get_fiche_head();
2451
2452 // Call Hook tabContentCreateProposal
2453 $parameters = array();
2454 // Note that $action and $object may be modified by hook
2455 $reshook = $hookmanager->executeHooks('tabContentCreateProposal', $parameters, $object, $action);
2456 if (empty($reshook)) {
2457 print '<table class="border centpercent">';
2458
2459 // Reference
2460 //print '<tr class="field_ref"><td class="titlefieldcreate fieldrequired">' . $langs->trans('Ref') . '</td><td class="valuefieldcreate">' . $langs->trans("Draft") . '</td></tr>';
2461
2462 // Third party
2463 print '<tr class="field_socid">';
2464
2465 print '<td class="titlefieldcreate fieldrequired">' . $langs->trans('Customer') . '</td>';
2466 $shipping_method_id = 0;
2467
2468 if ($socid > 0) {
2469 print '<td class="valuefieldcreate">';
2470 print $soc->getNomUrl(1, 'customer');
2471 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
2472 print '</td>';
2473 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2474 $shipping_method_id = $soc->shipping_method_id;
2475 }
2476 } else {
2477 print '<td class="valuefieldcreate">';
2478 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
2479 print img_picto('', 'company', 'class="pictofixedwidth"');
2480 print $form->select_company('', 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 maxwidth500 widthcentpercentminusxx');
2481 // reload page to retrieve customer information
2482 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
2483 print '<script>
2484 $(document).ready(function() {
2485 $("#socid").change(function() {
2486 console.log("We have changed the company - Reload page");
2487 var socid = $(this).val();
2488 // reload page
2489 $("input[name=action]").val("create");
2490 $("input[name=changecompany]").val("1");
2491 $("form[name=addprop]").submit();
2492 });
2493 });
2494 </script>';
2495 }
2496 print ' <a href="' . DOL_URL_ROOT . '/societe/card.php?action=create&prospect=3&fournisseur=0&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?action=create') . '"><span class="fa fa-plus-circle valignmiddle paddingleft" title="' . $langs->trans("AddThirdParty") . '"></span></a>';
2497 print '</td>';
2498 }
2499 print '</tr>' . "\n";
2500
2501 // Reference of proposal on customer side
2502 if (getDolGlobalString('MAIN_ASK_CUSTOMER_REF_OF_PROPOSAL_AT_CREATION')) {
2503 print '<tr class="field_ref_client"><td class="titlefieldcreate">';
2504 print $form->textwithpicto($langs->trans('RefCustomer'), $langs->trans('RefOfOnCustomerSide', $langs->transnoentitiesnoconv("Proposal")));
2505 print '</td><td class="valuefieldcreate">';
2506 print '<input type="text" name="ref_client" value="' . (!empty($ref_client) ? $ref_client : GETPOST('ref_client')) . '"></td>';
2507 print '</tr>';
2508 }
2509
2510 if ($socid > 0) {
2511 // Contacts (ask contact only if thirdparty already defined).
2512 print '<tr class="field_contactid"><td class="titlefieldcreate">' . $langs->trans("DefaultContact") . '</td><td class="valuefieldcreate">';
2513 print img_picto('', 'contact', 'class="pictofixedwidth"');
2514 //print $form->selectcontacts($soc->id, $contactid, 'contactid', 1, '', '', 0, 'minwidth300 widthcentpercentminusx');
2515 print $form->select_contact($soc->id, $contactid, 'contactid', 1, '', '', 1, 'maxwidth300 widthcentpercentminusx', true);
2516 print '</td></tr>';
2517
2518 // Third party discounts info line
2519 print '<tr class="field_discount_info"><td class="titlefieldcreate">' . $langs->trans('Discounts') . '</td><td class="valuefieldcreate">';
2520
2521 $absolute_discount = $soc->getAvailableDiscounts();
2522
2523 $thirdparty = $soc;
2524 $discount_type = 0;
2525 $backtopage = $_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . urlencode((string) (GETPOST('origin'))) . '&originid=' . urlencode((string) (GETPOSTINT('originid')));
2526 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
2527 print '</td></tr>';
2528 }
2529
2530 $newdatepropal = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
2531 // Date
2532 print '<tr class="field_addprop"><td class="titlefieldcreate fieldrequired">' . $langs->trans('Date') . '</td><td class="valuefieldcreate">';
2533 print img_picto('', 'action', 'class="pictofixedwidth"');
2534 print $form->selectDate($newdatepropal ? $newdatepropal : $datepropal, '', 0, 0, 0, "addprop", 1, 1);
2535 print '</td></tr>';
2536
2537 // Validaty duration
2538 print '<tr class="field_duree_validitee"><td class="titlefieldcreate fieldrequired">' . $langs->trans("ValidityDuration") . '</td><td class="valuefieldcreate">' . img_picto('', 'clock', 'class="pictofixedwidth"') . '<input name="duree_validite" class="width50" value="' . (GETPOSTISSET('duree_validite') ? GETPOST('duree_validite', 'alphanohtml') : getDolGlobalString('PROPALE_VALIDITY_DURATION')) . '"> ' . $langs->trans("days") . '</td></tr>';
2539
2540 // Terms of payment
2541 print '<tr class="field_cond_reglement_id"><td class="nowrap">' . $langs->trans('PaymentConditionsShort') . '</td><td>';
2542 print img_picto('', 'payment', 'class="pictofixedwidth"');
2543 // at last resort we take the payment term id which may be filled by default values set (if not getpostisset)
2544 print $form->getSelectConditionsPaiements((int) $cond_reglement_id, 'cond_reglement_id', 1, 1, 0, '', $deposit_percent);
2545 print '</td></tr>';
2546
2547 // Mode of payment
2548 print '<tr class="field_mode_reglement_id"><td class="titlefieldcreate">' . $langs->trans('PaymentMode') . '</td><td class="valuefieldcreate">';
2549 print img_picto('', 'bank', 'class="pictofixedwidth"');
2550 print $form->select_types_paiements((string) $mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth200 widthcentpercentminusx', 1);
2551 print '</td></tr>';
2552
2553 // Bank Account
2554 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
2555 print '<tr class="field_fk_account"><td class="titlefieldcreate">' . $langs->trans('BankAccount') . '</td><td class="valuefieldcreate">';
2556 print img_picto('', 'bank_account', 'class="pictofixedwidth"') . $form->select_comptes((int) $fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth200 widthcentpercentminusx', 1);
2557 print '</td></tr>';
2558 }
2559
2560 // Source / Channel - What trigger creation
2561 print '<tr class="field_demand_reason_id"><td class="titlefieldcreate">' . $langs->trans('Source') . '</td><td class="valuefieldcreate">';
2562 print img_picto('', 'question', 'class="pictofixedwidth"');
2563 $form->selectInputReason((GETPOSTISSET('demand_reason_id') ? GETPOSTINT('demand_reason_id') : ''), 'demand_reason_id', "SRC_PROP", 1, 'maxwidth200 widthcentpercentminusx');
2564 print '</td></tr>';
2565
2566 // Shipping Method
2567 if (isModEnabled("shipping")) {
2568 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2569 $shipping_method_id = $soc->shipping_method_id;
2570 }
2571 print '<tr class="field_shipping_method_id"><td class="titlefieldcreate">' . $langs->trans('SendingMethod') . '</td><td class="valuefieldcreate">';
2572 print img_picto('', 'dolly', 'class="pictofixedwidth"');
2573 $form->selectShippingMethod((string) $shipping_method_id, 'shipping_method_id', '', 1, '', 0, 'maxwidth200 widthcentpercentminusx');
2574 print '</td></tr>';
2575 }
2576
2577 $formproduct = null;
2578 // Warehouse
2579 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2580 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
2581 $formproduct = new FormProduct($db);
2582 print '<tr class="field_warehouse_id"><td class="titlefieldcreate">' . $langs->trans('Warehouse') . '</td><td class="valuefieldcreate">';
2583 print img_picto('', 'stock', 'class="pictofixedwidth"') . $formproduct->selectWarehouses((int) $warehouse_id, 'warehouse_id', '', 1, 0, 0, '', 0, 0, array(), 'maxwidth500 widthcentpercentminusxx');
2584 print '</td></tr>';
2585 }
2586
2587 // Delivery delay
2588 print '<tr class="field_availability_id"><td class="titlefieldcreate">' . $langs->trans('AvailabilityPeriod');
2589 if (isModEnabled('order')) {
2590 print ' (' . $langs->trans('AfterOrder') . ')';
2591 }
2592 print '</td><td class="valuefieldcreate">';
2593 print img_picto('', 'clock', 'class="pictofixedwidth"');
2594 print $form->selectAvailabilityDelay((GETPOSTISSET('availability_id') ? GETPOSTINT('availability_id') : ''), 'availability_id', '', 1, 'maxwidth200 widthcentpercentminusx', 1);
2595 print '</td></tr>';
2596
2597 // Delivery date (or manufacturing)
2598 print '<tr class="field_date_livraison"><td class="titlefieldcreate">' . $langs->trans("DeliveryDate") . '</td>';
2599 print '<td class="valuefieldcreate">';
2600 print img_picto('', 'action', 'class="pictofixedwidth"');
2601 if (is_numeric(getDolGlobalString('DATE_LIVRAISON_WEEK_DELAY'))) { // If value set to 0 or a num, not empty
2602 $tmpdte = time() + (7 * getDolGlobalInt('DATE_LIVRAISON_WEEK_DELAY') * 24 * 60 * 60);
2603 $syear = date("Y", $tmpdte);
2604 $smonth = date("m", $tmpdte);
2605 $sday = date("d", $tmpdte);
2606 print $form->selectDate($syear . "-" . $smonth . "-" . $sday, 'date_livraison', 0, 0, 0, "addprop");
2607 } else {
2608 $tmp_date_delivery = GETPOST('date_delivery') ?: -1;
2609 print $form->selectDate($tmp_date_delivery, 'date_livraison', 0, 0, 0, "addprop", 1, 1);
2610 }
2611 print '</td></tr>';
2612
2613 // Project
2614 if (isModEnabled('project') && is_object($formproject)) {
2615 $langs->load("projects");
2616 print '<tr class="field_projectid">';
2617 print '<td class="titlefieldcreate">'.$langs->trans("Project").'</td><td class="valuefieldcreate">';
2618 print img_picto('', 'project', 'class="pictofixedwidth"').$formproject->select_projects((($soc->id > 0 && !getDolGlobalString('PROJECT_CAN_ALWAYS_LINK_TO_ALL_CUSTOMERS')) ? $soc->id : -1), (string) $projectid, 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500 widthcentpercentminusxx');
2619 print ' <a href="'.DOL_URL_ROOT.'/projet/card.php?socid='.$soc->id.'&action=create&status=1&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create&socid='.$soc->id).'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddProject").'"></span></a>';
2620 print '</td>';
2621 print '</tr>';
2622 }
2623
2624 // Incoterms
2625 if (isModEnabled('incoterm')) {
2626 print '<tr class="field_incoterm_id">';
2627 print '<td class="titlefieldcreate"><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $soc->label_incoterms, 1) . '</label></td>';
2628 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2629 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
2630 print $form->select_incoterms((!empty($soc->fk_incoterms) ? $soc->fk_incoterms : ''), (!empty($soc->location_incoterms) ? $soc->location_incoterms : ''));
2631 print '</td></tr>';
2632 }
2633 if (isModEnabled('category')) {
2634 // Categories
2635 print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
2636 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
2637 print "</td></tr>";
2638 }
2639 // Template to use by default
2640 include_once DOL_DOCUMENT_ROOT . '/core/modules/propale/modules_propale.php';
2642 if (is_array($list) && count($list) > 0) {
2643 print '<tr class="field_model">';
2644 print '<td class="titlefieldcreate">' . $langs->trans("DefaultModel") . '</td>';
2645 print '<td class="valuefieldcreate">';
2646 print img_picto('', 'pdf', 'class="pictofixedwidth"');
2647 $preselected = getDolGlobalString('PROPALE_ADDON_PDF_ODT_DEFAULT', getDolGlobalString("PROPALE_ADDON_PDF"));
2648 print $form->selectarray('model', $list, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
2649 print "</td></tr>";
2650 }
2651
2652 // Multicurrency
2653 if (isModEnabled("multicurrency")) {
2654 print '<tr class="field_currency">';
2655 print '<td class="titlefieldcreate">' . $form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0) . '</td>';
2656 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2657 print img_picto('', 'currency', 'class="pictofixedwidth"') . $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth200 widthcentpercentminusx');
2658 print '</td></tr>';
2659 }
2660
2661 // Public note
2662 print '<tr class="field_note_public">';
2663 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePublic') . '</td>';
2664 print '<td class="valuefieldcreate">';
2665 $note_public = GETPOST('note_public', 'restricthtml');
2666 if (!GETPOSTISSET('note_public') && empty($note_public) && !empty($objectsrc)) {
2667 $note_public = $objectsrc->note_public;
2668 }
2669 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
2670 print $doleditor->Create(1);
2671
2672 // Private note
2673 if (empty($user->socid)) {
2674 print '<tr class="field_note_private">';
2675 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePrivate') . '</td>';
2676 print '<td class="valuefieldcreate">';
2677 $note_private = GETPOST('note_private', 'restricthtml');
2678 if (!GETPOSTISSET('note_private') && empty($note_private) && !empty($objectsrc)) {
2679 $note_private = $objectsrc->note_private;
2680 }
2681 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
2682 print $doleditor->Create(1);
2683 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
2684 print '</td></tr>';
2685 }
2686
2687 // Other attributes
2688 if (getDolGlobalInt('THIRDPARTY_PROPAGATE_EXTRAFIELDS_TO_PROPAL') && $socid > 0) {
2689 $thirdpartytopropagateextrafieldsfrom = $soc;
2690 }
2691 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php';
2692
2693 // Lines from source
2694 if (!empty($origin) && !empty($originid) && is_object($objectsrc) && !empty($classname)) {
2695 // TODO for compatibility
2696 if ($origin == 'contrat') {
2697 // Calcul contrat->price (HT), contrat->total (TTC), contrat->tva
2698 $objectsrc->update_price(1, 'auto', 1);
2699 }
2700
2701 print "\n<!-- " . $classname . " info -->";
2702 print "\n";
2703 print '<input type="hidden" name="amount" value="' . $objectsrc->total_ht . '">' . "\n";
2704 print '<input type="hidden" name="total" value="' . $objectsrc->total_ttc . '">' . "\n";
2705 print '<input type="hidden" name="tva" value="' . $objectsrc->total_tva . '">' . "\n";
2706 print '<input type="hidden" name="origin" value="' . $objectsrc->element . '">';
2707 print '<input type="hidden" name="originid" value="' . $objectsrc->id . '">';
2708
2709 $newclassname = $classname;
2710 if ($newclassname == 'Propal') {
2711 $newclassname = 'CommercialProposal';
2712 } elseif ($newclassname == 'Commande') {
2713 $newclassname = 'Order';
2714 } elseif ($newclassname == 'Expedition') {
2715 $newclassname = 'Sending';
2716 } elseif ($newclassname == 'Fichinter') {
2717 $newclassname = 'Intervention';
2718 }
2719
2720 print '<tr><td>' . $langs->trans($newclassname) . '</td><td>' . $objectsrc->getNomUrl(1) . '</td></tr>';
2721 print '<tr><td>' . $langs->trans('AmountHT') . '</td><td>' . price($objectsrc->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td></tr>';
2722 print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td>' . price($objectsrc->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2723 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
2724 print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2725 }
2726
2727 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
2728 print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2729 }
2730 print '<tr><td>' . $langs->trans('AmountTTC') . '</td><td>' . price($objectsrc->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2731
2732 if (isModEnabled("multicurrency")) {
2733 print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td>'.price($objectsrc->multicurrency_total_ht, 0, $langs, 1, -1, -1, $objectsrc->multicurrency_code).'</td></tr>';
2734 print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td>'.price($objectsrc->multicurrency_total_tva, 0, $langs, 1, -1, -1, $objectsrc->multicurrency_code)."</td></tr>";
2735 print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td>'.price($objectsrc->multicurrency_total_ttc, 0, $langs, 1, -1, -1, $objectsrc->multicurrency_code)."</td></tr>";
2736 }
2737 }
2738
2739 print "</table>\n";
2740
2741
2742 /*
2743 * Combobox for copy function
2744 */
2745
2746 if (!getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2747 print '<input type="hidden" name="createmode" value="empty">';
2748 }
2749
2750 if (getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2751 print '<br><table>';
2752
2753 // For backward compatibility
2754 print '<tr>';
2755 print '<td><input type="radio" name="createmode" value="copy"></td>';
2756 print '<td>' . $langs->trans("CopyPropalFrom") . ' </td>';
2757 print '<td>';
2758 $liste_propal = array();
2759 $liste_propal[0] = '';
2760
2761 $sql = "SELECT p.rowid as id, p.ref, s.nom";
2762 $sql .= " FROM " . MAIN_DB_PREFIX . "propal p";
2763 $sql .= ", " . MAIN_DB_PREFIX . "societe s";
2764 $sql .= " WHERE s.rowid = p.fk_soc";
2765 $sql .= " AND p.entity IN (" . getEntity('propal') . ")";
2766 $sql .= " AND p.fk_statut <> 0";
2767 $sql .= " ORDER BY Id";
2768
2769 $resql = $db->query($sql);
2770 if ($resql) {
2771 $num = $db->num_rows($resql);
2772 $i = 0;
2773 while ($i < $num) {
2774 $row = $db->fetch_row($resql);
2775 $propalRefAndSocName = $row[1] . " - " . $row[2];
2776 $liste_propal[$row[0]] = $propalRefAndSocName;
2777 $i++;
2778 }
2779 print $form->selectarray("copie_propal", $liste_propal, 0);
2780 } else {
2782 }
2783 print '</td></tr>';
2784
2785 print '<tr><td class="tdtop"><input type="radio" name="createmode" value="empty" checked></td>';
2786 print '<td valign="top" colspan="2">' . $langs->trans("CreateEmptyPropal") . '</td></tr>';
2787 print '</table>';
2788 }
2789 }
2790
2791 print dol_get_fiche_end();
2792
2793 $langs->load("bills");
2794
2795 print $form->buttonsSaveCancel("CreateDraft");
2796
2797 print "</form>";
2798
2799
2800 // Show origin lines
2801 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
2802 print '<br>';
2803
2804 $title = $langs->trans('ProductsAndServices');
2805 print load_fiche_titre($title);
2806
2807 print '<div class="div-table-responsive-no-min">';
2808 print '<table class="noborder centpercent">';
2809
2810 $objectsrc->printOriginLinesList();
2811
2812 print '</table>';
2813 print '</div>';
2814 }
2815} elseif ($object->id > 0) {
2816 /*
2817 * Show object in view mode
2818 */
2819 $object->fetch_thirdparty();
2820 if ($object->thirdparty) {
2821 $soc = $object->thirdparty;
2822 } else {
2823 $soc = new Societe($db);
2824 }
2825
2826 $head = propal_prepare_head($object);
2827 print dol_get_fiche_head($head, 'comm', $langs->trans('Proposal'), -1, $object->picto, 0, '', '', 0, '', 1);
2828
2829 $formconfirm = '';
2830
2831 // Clone confirmation
2832 if ($action == 'clone') {
2833 // Create an array for form
2834 $filter = '(s.client:IN:1,2,3)';
2835 $formquestion = array(
2836 // 'text' => $langs->trans("ConfirmClone"),
2837 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
2838 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company(GETPOSTINT('socid'), 'socid', $filter, '', 0, 0, array(), 0, 'maxwidth300')),
2839 array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans('PuttingPricesUpToDate'), 'value' => 0),
2840 array('type' => 'checkbox', 'name' => 'update_desc', 'label' => $langs->trans('PuttingDescUpToDate'), 'value' => 0),
2841 );
2842 if (getDolGlobalString('PROPAL_CLONE_DATE_DELIVERY') && !empty($object->delivery_date)) {
2843 $formquestion[] = array('type' => 'date', 'name' => 'date_delivery', 'label' => $langs->trans("DeliveryDate"), 'value' => $object->delivery_date);
2844 }
2845 // Incomplete payment. We ask if reason = discount or other
2846 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ToClone'), $langs->trans('ConfirmClonePropal', $object->ref), 'confirm_clone', $formquestion, 'yes', 1, 0, 600);
2847 }
2848
2849 // Subtotal line form
2850 if ($action == 'add_title_line') {
2851 $langs->load('subtotals');
2852 $type = 'title';
2853 $depth_array = $object->getPossibleLevels($langs);
2854 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2855 } elseif ($action == 'add_subtotal_line') {
2856 $langs->load('subtotals');
2857 $type = 'subtotal';
2858 $titles = $object->getPossibleTitles();
2859 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2860 }
2861
2862 if ($action == 'closeas') {
2863 //Form to close proposal (signed or not)
2864 $formquestion = array();
2865 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2866 $formquestion[] = array('type' => 'select', 'name' => 'statut', 'label' => '<span class="fieldrequired">' . $langs->trans("CloseAs") . '</span>', 'values' => array($object::STATUS_SIGNED => $object->LibStatut($object::STATUS_SIGNED), $object::STATUS_NOTSIGNED => $object->LibStatut($object::STATUS_NOTSIGNED)));
2867 }
2868 $formquestion[] = array('type' => 'text', 'name' => 'note_private', 'label' => $langs->trans("Note"), 'value' => ''); // Field to complete private note (not replace)
2869
2870 if (getDolGlobalInt('PROPOSAL_SUGGEST_DOWN_PAYMENT_INVOICE_CREATION')) {
2871 // This is a hidden option:
2872 // Suggestion to create invoice during proposal signature is not enabled by default.
2873 // Such choice should be managed by the workflow module and trigger. This option generates conflicts with some setup.
2874 // It may also break step of creating an order when invoicing must be done from orders and not from proposal
2875 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
2876
2877 if (!empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')) {
2878 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
2879
2880 $object->fetchObjectLinked();
2881
2882 $eligibleForDepositGeneration = true;
2883
2884 if (array_key_exists('facture', $object->linkedObjects)) {
2885 foreach ($object->linkedObjects['facture'] as $invoice) {
2886 '@phan-var-force Facture $invoice';
2888 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2889 $eligibleForDepositGeneration = false;
2890 break;
2891 }
2892 }
2893 }
2894
2895 if ($eligibleForDepositGeneration && array_key_exists('commande', $object->linkedObjects)) {
2896 foreach ($object->linkedObjects['commande'] as $order) {
2897 $order->fetchObjectLinked();
2898
2899 if (array_key_exists('facture', $order->linkedObjects)) {
2900 foreach ($order->linkedObjects['facture'] as $invoice) {
2901 '@phan-var-force Facture $invoice';
2903 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2904 $eligibleForDepositGeneration = false;
2905 break 2;
2906 }
2907 }
2908 }
2909 }
2910 }
2911
2912
2913 if ($eligibleForDepositGeneration) {
2914 $formquestion[] = array(
2915 'type' => 'checkbox',
2916 'tdclass' => 'showonlyifsigned',
2917 'name' => 'generate_deposit',
2918 'morecss' => 'margintoponly marginbottomonly',
2919 'label' => $form->textwithpicto($langs->trans('GenerateDeposit', $object->deposit_percent), $langs->trans('DepositGenerationPermittedByThePaymentTermsSelected'))
2920 );
2921
2922 $formquestion[] = array(
2923 'type' => 'date',
2924 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2925 'name' => 'datef',
2926 'label' => $langs->trans('DateInvoice'),
2927 'value' => dol_now(),
2928 'datenow' => true
2929 );
2930
2931 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
2932 $formquestion[] = array(
2933 'type' => 'date',
2934 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2935 'name' => 'date_pointoftax',
2936 'label' => $langs->trans('DatePointOfTax'),
2937 'value' => dol_now(),
2938 'datenow' => true
2939 );
2940 }
2941
2942 $paymentTermsSelect = $form->getSelectConditionsPaiements(0, 'cond_reglement_id', -1, 0, 1, 'minwidth200');
2943
2944 $formquestion[] = array(
2945 'type' => 'other',
2946 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2947 'name' => 'cond_reglement_id',
2948 'label' => $langs->trans('PaymentTerm'),
2949 'value' => $paymentTermsSelect
2950 );
2951
2952 $formquestion[] = array(
2953 'type' => 'checkbox',
2954 'tdclass' => 'showonlyifgeneratedeposit',
2955 'name' => 'validate_generated_deposit',
2956 'morecss' => 'margintoponly marginbottomonly',
2957 'label' => $langs->trans('ValidateGeneratedDeposit')
2958 );
2959
2960 $formquestion[] = array(
2961 'type' => 'onecolumn',
2962 'value' => '
2963 <script>
2964 let signedValue = ' . $object::STATUS_SIGNED . ';
2965
2966 $(document).ready(function() {
2967 $("[name=generate_deposit]").change(function () {
2968 let $self = $(this);
2969 let $target = $(".showonlyifgeneratedeposit").parent(".tagtr");
2970
2971 if (! $self.parents(".tagtr").is(":hidden") && $self.is(":checked")) {
2972 $target.show();
2973 } else {
2974 $target.hide();
2975 }
2976
2977 return true;
2978 });
2979
2980 $("#statut").change(function() {
2981 let $target = $(".showonlyifsigned").parent(".tagtr");
2982
2983 if ($(this).val() == signedValue) {
2984 $target.show();
2985 } else {
2986 $target.hide();
2987 }
2988
2989 $("[name=generate_deposit]").trigger("change");
2990
2991 return true;
2992 });
2993
2994 $("#statut").trigger("change");
2995 });
2996 </script>
2997 '
2998 );
2999 }
3000 }
3001 }
3002
3003 if (isModEnabled('notification')) {
3004 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
3005 $notify = new Notify($db);
3006 $formquestion = array_merge($formquestion, array(
3007 array('type' => 'onecolumn', 'value' => $notify->confirmMessage('PROPAL_CLOSE_SIGNED', $object->socid, $object)),
3008 ));
3009 }
3010
3011 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
3012 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SetAcceptedRefused'), '', 'confirm_closeas', $formquestion, '', 1, 250);
3013 } else {
3014 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?statut=3&id=' . $object->id, $langs->trans('Close'), '', 'confirm_closeas', $formquestion, '', 1, 250);
3015 }
3016 } elseif ($action == 'cancel') {
3017 // Confirm cancel
3018 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans("CancelPropal"), $langs->trans('ConfirmCancelPropal', $object->ref), 'confirm_cancel', '', 0, 1);
3019 } elseif ($action == 'delete') {
3020 // Confirm delete
3021 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('DeleteProp'), $langs->trans('ConfirmDeleteProp', $object->ref), 'confirm_delete', '', 0, 1);
3022 } elseif ($action == 'reopen') {
3023 // Confirm reopen
3024 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ReOpen'), $langs->trans('ConfirmReOpenProp', $object->ref), 'confirm_reopen', '', 0, 1);
3025 } elseif ($action == 'ask_deleteline') {
3026 // Confirmation delete product/service line
3027 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1);
3028 } elseif ($action == 'ask_subtotal_deleteline') {
3029 // Confirmation of removal of a subtotal line
3030 $langs->load("subtotals");
3031 $title = "DeleteSubtotalLine";
3032 $question = "ConfirmDeleteSubtotalLine";
3033 if (GETPOST('type') == 'title') {
3034 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
3035 $title = "DeleteTitleLine";
3036 $question = "ConfirmDeleteTitleLine";
3037 }
3038 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
3039 } elseif ($action == 'validate') {
3040 // Confirm validate proposal
3041 $error = 0;
3042
3043 // We verify whether the object is provisionally numbering
3044 $ref = substr($object->ref, 1, 4);
3045 if ($ref == 'PROV' || $ref == '') {
3046 $numref = $object->getNextNumRef($soc);
3047 if (empty($numref)) {
3048 $error++;
3049 setEventMessages($object->error, $object->errors, 'errors');
3050 }
3051 } else {
3052 $numref = (string) $object->ref;
3053 }
3054
3055 $text = $langs->trans('ConfirmValidateProp', $numref);
3056 if (isModEnabled('notification')) {
3057 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
3058 $notify = new Notify($db);
3059 $text .= '<br>';
3060 $text .= $notify->confirmMessage('PROPAL_VALIDATE', $object->socid, $object);
3061 }
3062
3063 // mandatoryPeriod
3064 $nbMandated = 0;
3065 foreach ($object->lines as $line) {
3066 $res = $line->fetch_product();
3067 if ($res > 0) {
3068 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
3069 $nbMandated++;
3070 break;
3071 }
3072 }
3073 }
3074 if ($nbMandated > 0) {
3075 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
3076 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
3077 $error++;
3078 } else {
3079 $text .= '<div><span class="clearboth nowraponall warning">' . img_warning() . $langs->trans("mandatoryPeriodNeedTobeSetMsgValidate") . '</span></div>';
3080 }
3081 }
3082
3083 if (!$error) {
3084 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ValidateProp'), $text, 'confirm_validate', '', 0, 1, 240);
3085 }
3086 }
3087
3088 // Call Hook formConfirm
3089 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
3090 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3091 if (empty($reshook)) {
3092 $formconfirm .= $hookmanager->resPrint;
3093 } elseif ($reshook > 0) {
3094 $formconfirm = $hookmanager->resPrint;
3095 }
3096
3097 // Print form confirm
3098 print $formconfirm;
3099
3100
3101 // Proposal card
3102
3103 $linkback = '<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
3104
3105 $morehtmlref = '<div class="refidno">';
3106 // Ref customer
3107 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, $usercancreate, 'string', '', 0, 1);
3108 $morehtmlref .= $form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, $usercancreate, 'string' . (isset($conf->global->THIRDPARTY_REF_INPUT_SIZE) ? ':' . getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') : ''), '', null, null, '', 1);
3109 // Thirdparty
3110 $morehtmlref .= '<br>' . $soc->getNomUrl(1, 'customer');
3111 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $soc->id > 0) {
3112 $morehtmlref .= ' (<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?socid=' . ((int) $soc->id) . '">' . $langs->trans("OtherProposals") . '</a>)';
3113 }
3114 // Project
3115 if (isModEnabled('project')) {
3116 $langs->load("projects");
3117 $morehtmlref .= '<br>';
3118 if ($usercancreate) {
3119 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
3120 if ($action != 'classify') {
3121 $morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
3122 }
3123 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, (string) $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
3124 } else {
3125 if (!empty($object->fk_project)) {
3126 $proj = new Project($db);
3127 $proj->fetch($object->fk_project);
3128 $morehtmlref .= $proj->getNomUrl(1);
3129 if ($proj->title) {
3130 $morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
3131 }
3132 }
3133 }
3134 }
3135 $morehtmlref .= '</div>';
3136
3137
3138 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
3139
3140 // Call Hook tabContentViewProposal
3141 $parameters = array();
3142 // Note that $action and $object may be modified by hook
3143 $reshook = $hookmanager->executeHooks('tabContentViewProposal', $parameters, $object, $action);
3144 if (empty($reshook)) {
3145 print '<div class="fichecenter">';
3146 print '<div class="fichehalfleft">';
3147 print '<div class="underbanner clearboth"></div>';
3148
3149 print '<table class="border tableforfield centpercent">';
3150
3151 // Link for thirdparty discounts
3152 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
3153 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3154 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3155 } else {
3156 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
3157 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
3158 }
3159
3160 print '<tr><td class="titlefieldmax45">' . $langs->trans('Discounts') . '</td><td>';
3161
3162 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
3163 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
3164 $absolute_discount = price2num($absolute_discount, 'MT');
3165 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
3166
3167 $caneditfield = ($object->status != Propal::STATUS_SIGNED && $object->status != Propal::STATUS_BILLED);
3168
3169 $thirdparty = $soc;
3170 $discount_type = 0;
3171 $backtopage = $_SERVER["PHP_SELF"] . '?id=' . $object->id;
3172 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
3173
3174 print '</td></tr>';
3175
3176 // Date of proposal
3177 print '<tr>';
3178 print '<td>';
3179 // print '<table class="nobordernopadding" width="100%"><tr><td>';
3180 // print $langs->trans('DatePropal');
3181 // print '</td>';
3182 // if ($action != 'editdate' && $usercancreate && $caneditfield) {
3183 // print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editdate&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->trans('SetDate'), 1).'</a></td>';
3184 // }
3185
3186 // print '</tr></table>';
3187 $editenable = $usercancreate && $caneditfield && $object->status == Propal::STATUS_DRAFT;
3188 print $form->editfieldkey("DatePropal", 'date', '', $object, (int) $editenable);
3189 print '</td><td class="valuefield">';
3190 if ($action == 'editdate' && $usercancreate && $caneditfield) {
3191 print '<form name="editdate" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3192 print '<input type="hidden" name="token" value="' . newToken() . '">';
3193 print '<input type="hidden" name="action" value="setdate">';
3194 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3195 print $form->selectDate($object->date, 're', 0, 0, 0, "editdate");
3196 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3197 print '</form>';
3198 } else {
3199 if ($object->date) {
3200 print dol_print_date($object->date, 'day');
3201 } else {
3202 print '&nbsp;';
3203 }
3204 }
3205 print '</td>';
3206
3207 // Date end proposal
3208 print '<tr>';
3209 print '<td>';
3210 print '<table class="nobordernopadding centpercent"><tr><td>';
3211 print $langs->trans('DateEndPropal');
3212 print '</td>';
3213 if ($action != 'editecheance' && $usercancreate && $caneditfield) {
3214 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editecheance&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetConditions'), 1) . '</a></td>';
3215 }
3216 print '</tr></table>';
3217 print '</td><td class="valuefield">';
3218 if ($action == 'editecheance' && $usercancreate && $caneditfield) {
3219 print '<form name="editecheance" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3220 print '<input type="hidden" name="token" value="' . newToken() . '">';
3221 print '<input type="hidden" name="action" value="setecheance">';
3222 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3223 print $form->selectDate($object->fin_validite, 'ech', 0, 0, 0, "editecheance");
3224 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3225 print '</form>';
3226 } else {
3227 if (!empty($object->fin_validite)) {
3228 print dol_print_date($object->fin_validite, 'day');
3229 if ($object->status == Propal::STATUS_VALIDATED && $object->fin_validite < ($now - getWarningDelay('propal', 'cloture'))) {
3230 print img_warning($langs->trans("Late"));
3231 }
3232 } else {
3233 print '&nbsp;';
3234 }
3235 }
3236 print '</td>';
3237 print '</tr>';
3238
3239 // Payment term
3240 print '<tr><td>';
3241 print '<table class="nobordernopadding centpercent"><tr><td>';
3242 print $langs->trans('PaymentConditionsShort');
3243 print '</td>';
3244 if ($action != 'editconditions' && $usercancreate && $caneditfield) {
3245 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editconditions&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetConditions'), 1) . '</a></td>';
3246 }
3247 print '</tr></table>';
3248 print '</td><td class="valuefield">';
3249 if ($action == 'editconditions' && $usercancreate && $caneditfield) {
3250 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'cond_reglement_id', 0, '', 1, $object->deposit_percent);
3251 } else {
3252 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'none', 0, '', 1, $object->deposit_percent);
3253 }
3254 print '</td>';
3255 print '</tr>';
3256
3257 // Payment mode
3258 print '<tr class="field_mode_reglement_id">';
3259 print '<td>';
3260 print '<table class="nobordernopadding centpercent"><tr><td>';
3261 print $langs->trans('PaymentMode');
3262 print '</td>';
3263 if ($action != 'editmode' && $usercancreate && $caneditfield) {
3264 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editmode&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetMode'), 1) . '</a></td>';
3265 }
3266 print '</tr></table>';
3267 print '</td><td class="valuefieldcreate">';
3268 if ($action == 'editmode' && $usercancreate && $caneditfield) {
3269 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
3270 } else {
3271 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'none');
3272 }
3273 print '</td></tr>';
3274
3275 // Delivery delay
3276 print '<tr class="fielddeliverydelay"><td>';
3277 print '<table class="nobordernopadding centpercent"><tr><td>';
3278 if (isModEnabled('order')) {
3279 print $form->textwithpicto($langs->trans('AvailabilityPeriod'), $langs->trans('AvailabilityPeriod') . ' (' . $langs->trans('AfterOrder') . ')');
3280 } else {
3281 print $langs->trans('AvailabilityPeriod');
3282 }
3283 print '</td>';
3284 if ($action != 'editavailability' && $usercancreate && $caneditfield) {
3285 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editavailability&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetAvailability'), 1) . '</a></td>';
3286 }
3287 print '</tr></table>';
3288 print '</td><td class="valuefield">';
3289 if ($action == 'editavailability' && $usercancreate && $caneditfield) {
3290 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'availability_id', $langs->trans("Delay"));
3291 } else {
3292 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'none', 1);
3293 }
3294
3295 print '</td>';
3296 print '</tr>';
3297
3298 // Delivery date
3299 print '<tr><td>';
3300 print $form->editfieldkey($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, (int) ($usercancreate && $caneditfield), 'datepicker');
3301 print '</td><td class="valuefieldedit">';
3302 print $form->editfieldval($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, $usercancreate && $caneditfield, 'datepicker');
3303 print '</td>';
3304 print '</tr>';
3305
3306 // Shipping Method
3307 if (isModEnabled("shipping")) {
3308 print '<tr><td>';
3309 print '<table class="nobordernopadding centpercent"><tr><td>';
3310 print $langs->trans('SendingMethod');
3311 print '</td>';
3312 if ($action != 'editshippingmethod' && $usercancreate && $caneditfield) {
3313 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editshippingmethod&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetShippingMode'), 1) . '</a></td>';
3314 }
3315 print '</tr></table>';
3316 print '</td><td class="valuefield">';
3317 if ($action == 'editshippingmethod' && $usercancreate && $caneditfield) {
3318 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'shipping_method_id', 1);
3319 } else {
3320 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'none');
3321 }
3322 print '</td>';
3323 print '</tr>';
3324 }
3325
3326 // Warehouse
3327 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
3328 $langs->load('stocks');
3329 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
3330 $formproduct = new FormProduct($db);
3331 print '<tr class="field_warehouse_id"><td>';
3332 $editenable = $usercancreate;
3333 print $form->editfieldkey("Warehouse", 'warehouse', '', $object, $editenable);
3334 print '</td><td class="valuefieldcreate">';
3335 if ($action == 'editwarehouse') {
3336 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'warehouse_id', 1);
3337 } else {
3338 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'none');
3339 }
3340 print '</td>';
3341 print '</tr>';
3342 }
3343
3344 // Origin of demand
3345 print '<tr><td>';
3346 print '<table class="nobordernopadding centpercent"><tr><td>';
3347 print $langs->trans('Source');
3348 print '</td>';
3349 if ($action != 'editdemandreason' && $usercancreate) {
3350 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editdemandreason&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetDemandReason'), 1) . '</a></td>';
3351 }
3352 print '</tr></table>';
3353 print '</td><td class="valuefield">';
3354 if ($action == 'editdemandreason' && $usercancreate) {
3355 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'demand_reason_id', 1);
3356 } else {
3357 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'none');
3358 }
3359 print '</td>';
3360 print '</tr>';
3361
3362 if ($soc->outstanding_limit) {
3363 // Outstanding Bill
3364 print '<tr><td>';
3365 print $langs->trans('OutstandingBill');
3366 print '</td><td class="valuefield">';
3367 $arrayoutstandingbills = $soc->getOutstandingBills();
3368 print($arrayoutstandingbills['opened'] > $soc->outstanding_limit ? img_warning() : '');
3369 print price($arrayoutstandingbills['opened']) . ' / ';
3370 print price($soc->outstanding_limit, 0, $langs, 1, -1, -1, $conf->currency);
3371 print '</td>';
3372 print '</tr>';
3373 }
3374
3375 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
3376 // Bank Account
3377 print '<tr><td>';
3378 print '<table width="100%" class="nobordernopadding"><tr><td>';
3379 print $langs->trans('BankAccount');
3380 print '</td>';
3381 if ($action != 'editbankaccount' && $usercancreate) {
3382 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editbankaccount&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetBankAccount'), 1) . '</a></td>';
3383 }
3384 print '</tr></table>';
3385 print '</td><td class="valuefield">';
3386 if ($action == 'editbankaccount') {
3387 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'fk_account', 1);
3388 } else {
3389 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'none');
3390 }
3391 print '</td>';
3392 print '</tr>';
3393 }
3394
3395 if (!getDolGlobalString('PROPOSAL_HIDE_CALCULATED_WEIGHT_VOLUME')) {
3396 $tmparray = $object->getTotalWeightVolume();
3397 $totalWeight = isset($tmparray['weight']) ? $tmparray['weight'] : 0;
3398 $totalVolume = isset($tmparray['volume']) ? $tmparray['volume'] : 0;
3399 if ($totalWeight) {
3400 print '<tr><td>' . $langs->trans("CalculatedWeight") . '</td>';
3401 print '<td class="valuefield">';
3402 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'), 1);
3403 print '</td></tr>';
3404 }
3405 if ($totalVolume) {
3406 print '<tr><td>' . $langs->trans("CalculatedVolume") . '</td>';
3407 print '<td class="valuefield">';
3408 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'), 1);
3409 print '</td></tr>';
3410 }
3411 }
3412
3413 // Incoterms
3414 if (isModEnabled('incoterm')) {
3415 print '<tr><td>';
3416 print '<table width="100%" class="nobordernopadding"><tr><td>';
3417 print $langs->trans('IncotermLabel');
3418 print '<td><td class="right">';
3419 if ($action != 'editincoterm' && $usercancreate && $caneditfield) {
3420 print '<a class="editfielda" href="' . DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
3421 } else {
3422 print '&nbsp;';
3423 }
3424 print '</td></tr></table>';
3425 print '</td>';
3426 print '<td class="valuefield">';
3427 if ($action == 'editincoterm' && $usercancreate && $caneditfield) {
3428 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
3429 } else {
3430 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
3431 }
3432 print '</td></tr>';
3433 }
3434
3435 // Categories
3436 if (isModEnabled('category')) {
3437 print '<tr><td>';
3438 print '<table class="nobordernopadding centpercent"><tr><td>';
3439 print $langs->trans("Categories");
3440 print '<td><td class="right">';
3441 if ($usercancreate) {
3442 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/comm/propal/card.php?id='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
3443 } else {
3444 print '&nbsp;';
3445 }
3446 print '</td></tr></table>';
3447 print '</td>';
3448 print '<td>';
3449 if ($action == 'edittags') {
3450 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
3451 print '<input type="hidden" name="action" value="settags">';
3452 print '<input type="hidden" name="token" value="'.newToken().'">';
3453 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
3454 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
3455 print '</form>';
3456 } else {
3457 print $form->showCategories($object->id, Categorie::TYPE_PROPOSAL, 1);
3458 }
3459 print "</td></tr>";
3460 }
3461
3462 // Other attributes
3463 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
3464
3465 print '</table>';
3466
3467 print '</div>';
3468 print '<div class="fichehalfright">';
3469 print '<div class="underbanner clearboth"></div>';
3470
3471 print '<table class="border tableforfield centpercent">';
3472
3473 include DOL_DOCUMENT_ROOT . '/core/tpl/object_currency_amount.tpl.php';
3474
3475 print '<tr>';
3476 print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';
3477 print '<td class="nowrap amountcard right">' . price($object->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3478 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3479 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ht, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3480 }
3481 print '</tr>';
3482
3483 print '<tr>';
3484 print '<td>' . $langs->trans('AmountVAT') . '</td>';
3485 print '<td class="nowrap amountcard right">' . price($object->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3486 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3487 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_tva, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3488 }
3489 print '</tr>';
3490
3491 if ($mysoc->localtax1_assuj == "1" || $object->total_localtax1 != 0) {
3492 print '<tr>';
3493 print '<td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
3494 print '<td class="nowrap amountcard right">' . price($object->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3495 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3496 $object->multicurrency_total_localtax1 = price2num($object->total_localtax1 * $object->multicurrency_tx, 'MT');
3497
3498 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax1, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3499 }
3500 print '</tr>';
3501 }
3502
3503 if ($mysoc->localtax2_assuj == "1" || $object->total_localtax2 != 0) {
3504 print '<tr>';
3505 print '<td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
3506 print '<td class="nowrap amountcard right">' . price($object->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3507 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3508 $object->multicurrency_total_localtax2 = price2num($object->total_localtax2 * $object->multicurrency_tx, 'MT');
3509
3510 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax2, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3511 }
3512 print '</tr>';
3513 }
3514
3515 print '<tr>';
3516 print '<td>' . $langs->trans('AmountTTC') . '</td>';
3517 print '<td class="nowrap amountcard right">' . price($object->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3518 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3519 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ttc, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3520 }
3521 print '</tr>';
3522
3523 print '</table>';
3524
3525 // Margin Infos
3526 if (isModEnabled('margin')) {
3527 $formmargin->displayMarginInfos($object);
3528 }
3529
3530 print '</div>';
3531 print '</div>';
3532
3533 print '<div class="clearboth"></div><br>';
3534
3535 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
3536 $blocname = 'contacts';
3537 $title = $langs->trans('ContactsAddresses');
3538 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3539 }
3540
3541 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
3542 $blocname = 'notes';
3543 $title = $langs->trans('Notes');
3544 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3545 }
3546
3547 /*
3548 * Lines
3549 */
3550
3551 // Get object lines
3552 $result = $object->getLinesArray();
3553
3554 // Add products/services form
3555 //$forceall = 1;
3556 global $inputalsopricewithtax;
3557 $inputalsopricewithtax = 1;
3558
3559 print ' <form name="addproduct" id="addproduct" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="POST">
3560 <input type="hidden" name="token" value="' . newToken() . '">
3561 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline') . '">
3562 <input type="hidden" name="mode" value="">
3563 <input type="hidden" name="page_y" value="">
3564 <input type="hidden" name="backtopage" value="' . $backtopage . '">
3565 <input type="hidden" name="id" value="' . $object->id . '">
3566 ';
3567
3568 if (!empty($conf->use_javascript_ajax) && $object->status == Propal::STATUS_DRAFT) {
3569 if (isModEnabled('subtotals')) {
3570 include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php';
3571 } else {
3572 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
3573 }
3574 }
3575
3576 print '<div class="div-table-responsive-no-min">';
3577 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3578 print '<table id="tablelines" class="noborder noshadow centpercent">';
3579 }
3580
3581 if (!empty($object->lines)) {
3582 $object->printObjectLines($action, $mysoc, $object->thirdparty, $lineid, 1);
3583 }
3584
3585 // Form to add new line
3586 if ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines') {
3587 if ($action != 'editline') {
3588 $parameters = array();
3589 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3590 if ($reshook < 0) {
3591 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3592 }
3593 if (empty($reshook)) {
3594 $object->formAddObjectLine(1, $mysoc, $soc);
3595 }
3596 } else {
3597 $parameters = array();
3598 $reshook = $hookmanager->executeHooks('formEditObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3599 }
3600 }
3601
3602 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3603 print '</table>';
3604 }
3605 print '</div>';
3606
3607 print "</form>\n";
3608 }
3609
3610 print dol_get_fiche_end();
3611
3612
3613 /*
3614 * Button Actions
3615 */
3616
3617 if ($action != 'presend') {
3618 $numlines = count($object->lines);
3619 print '<div class="tabsAction">';
3620
3621 $parameters = array();
3622 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3623 // modified by hook
3624 if (empty($reshook)) {
3625 if ($action != 'editline') {
3626 // Subtotal
3627 if ($object->status == Propal::STATUS_DRAFT && isModEnabled('subtotals')
3628 && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
3629 $langs->load('subtotals');
3630
3631 $url_button = array();
3632
3633 $url_button[] = array(
3634 'lang' => 'subtotals',
3635 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
3636 'perm' => (bool) $usercancreate,
3637 'label' => $langs->trans('AddTitleLine'),
3638 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_title_line&token=' . newToken()
3639 );
3640
3641 $url_button[] = array(
3642 'lang' => 'subtotals',
3643 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
3644 'perm' => (bool) $usercancreate,
3645 'label' => $langs->trans('AddSubtotalLine'),
3646 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_subtotal_line&token=' . newToken()
3647 );
3648
3649 print dolGetButtonAction('', $langs->trans('SubTotal'), 'default', $url_button, '', true);
3650 }
3651
3652 // Validate
3653 if (($object->status == Propal::STATUS_DRAFT && $object->total_ttc >= 0 && count($object->lines) > 0)
3654 || ($object->status == Propal::STATUS_DRAFT && getDolGlobalString('PROPAL_ENABLE_NEGATIVE') && count($object->lines) > 0)
3655 ) {
3656 if ($usercanvalidate) {
3657 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=validate&token=' . newToken() . '">' . (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE') ? $langs->trans('Validate') : $langs->trans('ValidateAndSign')) . '</a>';
3658 } else {
3659 print '<a class="butActionRefused classfortooltip" href="#">' . $langs->trans('Validate') . '</a>';
3660 }
3661 }
3662 // Create event
3663 /*if (isModEnabled('agenda') && !empty($conf->global->MAIN_ADD_EVENT_ON_ELEMENT_CARD)) // Add hidden condition because this is not a "workflow" action so should appears somewhere else on page.
3664 {
3665 print '<a class="butAction" href="' . DOL_URL_ROOT . '/comm/action/card.php?action=create&amp;origin=' . $object->element . '&amp;originid=' . $object->id . '&amp;socid=' . $object->socid . '">' . $langs->trans("AddAction") . '</a></div>';
3666 }*/
3667 // Edit
3668 if ($object->status == Propal::STATUS_VALIDATED && $usercancreate) {
3669 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=modif&token=' . newToken() . '">' . $langs->trans('Modify') . '</a>';
3670 }
3671
3672 // ReOpen
3673 if (((getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && $object->status == Propal::STATUS_NOTSIGNED) || (!getDolGlobalString('PROPAL_REOPEN_UNSIGNED_ONLY') && ($object->status == Propal::STATUS_SIGNED || $object->status == Propal::STATUS_NOTSIGNED || $object->status == Propal::STATUS_BILLED || $object->status == Propal::STATUS_CANCELED)))) {
3674 if ($usercanreopen) {
3675 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=reopen&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#reopen') . '"';
3676 print '>' . $langs->trans('ReOpen') . '</a>';
3677 } else {
3678 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ReOpen") . '</a>';
3679 }
3680 }
3681
3682 // Send
3683 if (empty($user->socid)) {
3684 if ($object->status == Propal::STATUS_VALIDATED || $object->status == Propal::STATUS_SIGNED || getDolGlobalString('PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS')) {
3685 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '', $usercansend);
3686 }
3687 }
3688
3689 $arrayforbutaction = array();
3690
3691 // Create a sale order
3692 $arrayforbutaction[] = array(
3693 'lang' => 'orders',
3694 'enabled' => (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED),
3695 'perm' => $usercancreateorder,
3696 'label' => 'AddOrder',
3697 'url' => '/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3698 );
3699 /*if (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED) {
3700 if ($usercancreateorder) {
3701 print '<a class="butAction" href="'.DOL_URL_ROOT.'/commande/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("AddOrder").'</a>';
3702 }
3703 }*/
3704
3705 // Create a purchase order
3706 if (getDolGlobalString('WORKFLOW_CAN_CREATE_PURCHASE_ORDER_FROM_PROPOSAL')) {
3707 $arrayforbutaction[] = array(
3708 'lang' => 'orders',
3709 'enabled' => ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")),
3710 'perm' => $usercancreatepurchaseorder,
3711 'label' => 'AddPurchaseOrder',
3712 'url' => '/fourn/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id)
3713 );
3714 /*if ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")) {
3715 if ($usercancreatepurchaseorder) {
3716 print '<a class="butAction" href="'.DOL_URL_ROOT.'/fourn/commande/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("AddPurchaseOrder").'</a>';
3717 }
3718 }*/
3719 }
3720
3721 // Create an intervention
3722 $arrayforbutaction[] = array(
3723 'lang' => 'interventions',
3724 'enabled' => (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED),
3725 'perm' => $usercancreateintervention,
3726 'label' => 'AddIntervention',
3727 'url' => '/fichinter/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3728 );
3729 /*if (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED) {
3730 if ($usercancreateintervention) {
3731 $langs->load("interventions");
3732 print '<a class="butAction" href="'.DOL_URL_ROOT.'/fichinter/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("AddIntervention").'</a>';
3733 }
3734 }*/
3735
3736 // Create contract
3737 $arrayforbutaction[] = array(
3738 'lang' => 'contracts',
3739 'enabled' => (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED),
3740 'perm' => $usercancreatecontract,
3741 'label' => 'AddContract',
3742 'url' => '/contrat/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3743 );
3744 /*if (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED) {
3745 $langs->load("contracts");
3746
3747 if ($usercancreatecontract) {
3748 print '<a class="butAction" href="'.DOL_URL_ROOT.'/contrat/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans('AddContract').'</a>';
3749 }
3750 }*/
3751
3752 // Create an invoice and classify billed
3753 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3754 $arrayforbutaction[] = [
3755 'lang' => 'invoice',
3756 'enabled' => isModEnabled('invoice'),
3757 'perm' => $usercancreateinvoice,
3758 'label' => 'CreateBill',
3759 'url' => '/compta/facture/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid),
3760 ];
3761 /*if (isModEnabled('invoice') && $usercancreateinvoice) {
3762 print '<a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid.'">'.$langs->trans("CreateBill").'</a>';
3763 }*/
3764 }
3765
3766 $actionButtonsParameters = [
3767 "areDropdownButtons" => !getDolGlobalInt("MAIN_REMOVE_DROPDOWN_CREATE_BUTTONS_ON_ORDER"),
3768 "backtopage" => $_SERVER["PHP_SELF"] . "?id=" . ((int) $id)
3769 ];
3770
3771 if ($numlines > 0) {
3772 print dolGetButtonAction('', $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 1, $actionButtonsParameters);
3773 } else {
3774 print dolGetButtonAction($langs->trans("ErrorObjectMustHaveLinesToBeValidated", $object->ref), $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 0, $actionButtonsParameters);
3775 }
3776
3777 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3778 $arrayofinvoiceforpropal = $object->getInvoiceArrayList();
3779 if ((is_array($arrayofinvoiceforpropal) && count($arrayofinvoiceforpropal) > 0) || !getDolGlobalString('WORKFLOW_PROPAL_NEED_INVOICE_TO_BE_CLASSIFIED_BILLED')) {
3780 if ($usercanclose) {
3781 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=classifybilled&token=' . newToken() . '&socid=' . $object->socid . '">' . $langs->trans("ClassifyBilled") . '</a>';
3782 } else {
3783 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ClassifyBilled") . '</a>';
3784 }
3785 }
3786 }
3787
3788 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
3789 // Close as accepted/refused
3790 if ($object->status == Propal::STATUS_VALIDATED) {
3791 if ($usercanclose) {
3792 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3793 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3794 } else {
3795 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '"';
3796 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3797 }
3798 }
3799 } else {
3800 // Set not signed (close)
3801 if ($object->status == Propal::STATUS_DRAFT && $usercanclose) {
3802 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&token=' . newToken() . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3803 print '>' . $langs->trans('SetRefusedAndClose') . '</a>';
3804 }
3805 }
3806
3807 // Cancel propal
3808 if ($object->status > Propal::STATUS_DRAFT && $usercanclose) {
3809 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=cancel&token=' . newToken() . '">' . $langs->trans("CancelPropal") . '</a>';
3810 }
3811
3812 // Clone
3813 if ($usercancreate) {
3814 print '<a class="butAction butActionClone" href="' . $_SERVER['PHP_SELF'] . '?id=' . $object->id . '&socid=' . $object->socid . '&action=clone&token=' . newToken() . '&object=' . $object->element . '">' . $langs->trans("ToClone") . '</a>';
3815 }
3816
3817 // Delete
3818 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=delete&token=' . newToken(), 'delete', $usercandelete);
3819 }
3820 }
3821
3822 print '</div>';
3823 }
3824
3825 //Select mail models is same action as presend
3826 if (GETPOST('modelselected')) {
3827 $action = 'presend';
3828 }
3829
3830 if ($action != 'presend') {
3831 print '<div class="fichecenter"><div class="fichehalfleft">';
3832 print '<a name="builddoc"></a>'; // ancre
3833 /*
3834 * Generated documents
3835 */
3836 $objref = dol_sanitizeFileName($object->ref);
3837 $filedir = $conf->propal->multidir_output[$object->entity ?? $conf->entity] . "/" . dol_sanitizeFileName($object->ref);
3838 $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
3839 $genallowed = $usercanread;
3840 $delallowed = $usercancreate;
3841 $tooltipAfterComboOfModels = '';
3842 if (getDolGlobalString('MAIN_PDF_ADD_TERMSOFSALE_PROPAL')) {
3843 $tooltipAfterComboOfModels = $langs->trans("AccordingToYourSetupTheFileWillBeConcatenated", getDolGlobalString('MAIN_INFO_PROPAL_TERMSOFSALE'));
3844 }
3845
3846 print $formfile->showdocuments('propal', $objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '0', '', $soc->default_lang, '', $object, 0, 'remove_file', $tooltipAfterComboOfModels);
3847
3848 // Show links to link elements
3849 $tmparray = $form->showLinkToObjectBlock($object, array(), array('propal'), 1);
3850 $linktoelem = $tmparray['linktoelem'];
3851 $htmltoenteralink = $tmparray['htmltoenteralink'];
3852 print $htmltoenteralink;
3853
3854 $compatibleImportElementsList = false;
3855 if ($user->hasRight('propal', 'creer') && $object->status == Propal::STATUS_DRAFT) {
3856 $compatibleImportElementsList = array('commande', 'propal', 'facture', 'subscription'); // import from linked elements
3857 }
3858 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
3859
3860 // Show online signature link
3861 $useonlinesignature = getDolGlobalInt('PROPOSAL_ALLOW_ONLINESIGN');
3862
3863 if ($object->status != Propal::STATUS_DRAFT && $useonlinesignature) {
3864 print '<br><!-- Link to sign -->';
3865 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
3866 print showOnlineSignatureUrl('proposal', $object->ref, $object) . '<br>';
3867 }
3868
3869 print '</div><div class="fichehalfright">';
3870
3871 $MAXEVENT = 10;
3872
3873 $morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/comm/propal/messaging.php?id=' . $object->id);
3874 $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT . '/comm/propal/agenda.php?id=' . $object->id);
3875
3876 // List of actions on element
3877 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
3878 $formactions = new FormActions($db);
3879 $somethingshown = $formactions->showactions($object, 'propal', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for thirdparty
3880
3881 print '</div></div>';
3882 }
3883
3884 // Presend form
3885 $modelmail = 'propal_send';
3886 $defaulttopic = 'SendPropalRef';
3887 $diroutput = $conf->propal->multidir_output[$object->entity ?? $conf->entity];
3888 $trackid = 'pro' . $object->id;
3889
3890 include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
3891}
3892
3893// End of page
3894llxFooter();
3895$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
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:73
Class to manage a WYSIWYG editor.
static createDepositFromOrigin(CommonObject $origin, $date, $payment_terms_id, User $user, $notrigger=0, $autoValidateDeposit=false, $overrideFields=array())
Creates a deposit from a proposal or an order by grouping lines by VAT rates.
const TYPE_DEPOSIT
Deposit invoice.
Class to manage building of HTML components.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class to manage "other" html components Only common components are here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage building of HTML components.
Class to manage generation of HTML components for proposal management.
static liste_modeles($db, $maxfilenamelength=0)
Return list of active generation modules.
Class to manage the table of subscription to notifications.
Class ProductCombination Used to represent the relation between a product and one of its variants.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
Class to manage projects.
Class to manage proposals.
const STATUS_DRAFT
Draft status.
const STATUS_SIGNED
Signed quote.
const STATUS_NOTSIGNED
Not signed quote.
const STATUS_BILLED
Billed or processed quote.
const STATUS_CANCELED
Canceled status.
const STATUS_VALIDATED
Validated status.
Class to manage commercial proposal lines.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
$date_start
Variables from include:
dol_now($mode='gmt')
Return date for now.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
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.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
getWarningDelay($module, $parmlevel1, $parmlevel2='')
Return a warning delay You can use it like this: if (getWarningDelay('module', 'paramlevel1')) It rep...
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
getDictionaryValue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
Return the value of a filed into a dictionary for the record $id.
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
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...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
propal_prepare_head($object)
Prepare array with list of tabs.
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:492
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $nodie=0, $mode='')
Check permissions of a user to show a page and an object.