dolibarr 24.0.1
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 // Defined the new fk_parent_line
732 if ($result > 0 && $lines[$i]->product_type == 9) {
733 $fk_parent_line = $result;
734 }
735 }
736
737 // Hooks
738 $parameters = array('objFrom' => $srcobject);
739 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $object, $action); // Note that $action and $object may have been
740 // modified by hook
741 if ($reshook < 0) {
742 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
743 $error++;
744 }
745 } else {
746 setEventMessages($srcobject->error, $srcobject->errors, 'errors');
747 $error++;
748 }
749 } else {
750 setEventMessages($object->error, $object->errors, 'errors');
751 $error++;
752 }
753 } else {
754 // Standard creation
755 $id = $object->create($user);
756 }
757
758 if ($id > 0) {
759 // Insert default contacts if defined
760 if (GETPOST('contactid') > 0) {
761 $result = $object->add_contact(GETPOSTINT('contactid'), 'CUSTOMER', 'external');
762 if ($result < 0) {
763 $error++;
764 setEventMessages($langs->trans("ErrorFailedToAddContact"), null, 'errors');
765 }
766 }
767
768 if (getDolGlobalString('PROPOSAL_AUTO_ADD_AUTHOR_AS_CONTACT')) {
769 $result = $object->add_contact($user->id, 'SALESREPFOLL', 'internal');
770 if ($result < 0) {
771 $error++;
772 setEventMessages($langs->trans("ErrorFailedToAddUserAsContact"), null, 'errors');
773 }
774 }
775 if (isModEnabled('category')) {
776 $categories = GETPOST('categories', 'array');
777 if (method_exists($object, 'setCategories')) {
778 $object->setCategories($categories);
779 }
780 }
781
782 if (!$error) {
783 $db->commit();
784
785 // Define output language
786 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
787 $outputlangs = $langs;
788 $newlang = '';
789 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
790 $newlang = GETPOST('lang_id', 'aZ09');
791 }
792 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
793 $newlang = $object->thirdparty->default_lang;
794 }
795 if (!empty($newlang)) {
796 $outputlangs = new Translate("", $conf);
797 $outputlangs->setDefaultLang($newlang);
798 }
799 $model = $object->model_pdf;
800
801 $ret = $object->fetch($id); // Reload to get new records
802 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
803 if ($result < 0) {
804 dol_print_error($db, $object->error, $object->errors);
805 }
806 }
807
808 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
809 exit();
810 } else {
811 $db->rollback();
812 $action = 'create';
813 }
814 } else {
815 setEventMessages($object->error, $object->errors, 'errors');
816 $db->rollback();
817 $action = 'create';
818 }
819 }
820 }
821 } elseif ($action == 'classifybilled' && $usercanclose) {
822 // Classify billed
823 $db->begin();
824
825 $result = $object->classifyBilled($user, 0, '');
826 if ($result < 0) {
827 setEventMessages($object->error, $object->errors, 'errors');
828 $error++;
829 }
830
831 if (!$error) {
832 $db->commit();
833 } else {
834 $db->rollback();
835 }
836 } elseif ($action == 'confirm_closeas' && $usercanclose && !GETPOST('cancel', 'alpha')) {
837 // Close proposal
838 if (!(GETPOSTINT('statut') > 0)) {
839 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CloseAs")), null, 'errors');
840 $action = 'closeas';
841 } elseif (GETPOSTINT('statut') == $object::STATUS_SIGNED || GETPOSTINT('statut') == $object::STATUS_NOTSIGNED) {
842 $locationTarget = '';
843 // prevent browser refresh from closing proposal several times
844 if ($object->status == $object::STATUS_VALIDATED || (getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE') && $object->status == $object::STATUS_DRAFT)) {
845 $db->begin();
846
847 $oldstatus = $object->status;
848
849 $result = $object->closeProposal($user, GETPOSTINT('statut'), GETPOST('note_private'));
850
851 if ($result < 0) {
852 setEventMessages($object->error, $object->errors, 'errors');
853 $error++;
854 } else {
855 // Needed if object linked modified by trigger (because linked objects can't be fetched two times : linkedObjectsFullLoaded)
856 $locationTarget = DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id;
857 }
858
859 $deposit = null;
860
861 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
862
863 if (
864 !$error && GETPOSTINT('statut') == $object::STATUS_SIGNED && GETPOST('generate_deposit') == 'on'
865 && !empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')
866 ) {
867 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
868
869 $date = dol_mktime(0, 0, 0, GETPOSTINT('datefmonth'), GETPOSTINT('datefday'), GETPOSTINT('datefyear'));
870 $forceFields = array();
871
872 if (GETPOSTISSET('date_pointoftax')) {
873 $forceFields['date_pointoftax'] = dol_mktime(0, 0, 0, GETPOSTINT('date_pointoftaxmonth'), GETPOSTINT('date_pointoftaxday'), GETPOSTINT('date_pointoftaxyear'));
874 }
875
876 $deposit = Facture::createDepositFromOrigin($object, $date, GETPOSTINT('cond_reglement_id'), $user, 0, GETPOSTINT('validate_generated_deposit') == 'on', $forceFields);
877
878 if ($deposit) {
879 setEventMessage('DepositGenerated');
880 $locationTarget = DOL_URL_ROOT . '/compta/facture/card.php?id=' . $deposit->id;
881 } else {
882 $error++;
883 setEventMessages("Failed to create down payment - " . $object->error, $object->errors, 'errors');
884 }
885 }
886
887 if (!$error) {
888 $db->commit();
889
890 if ($deposit && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
891 $ret = $deposit->fetch($deposit->id); // Reload to get new records
892 $outputlangs = $langs;
893
894 if (getDolGlobalInt('MAIN_MULTILANGS')) {
895 $outputlangs = new Translate('', $conf);
896 $outputlangs->setDefaultLang($deposit->thirdparty->default_lang);
897 $outputlangs->load('products');
898 }
899
900 $result = $deposit->generateDocument($deposit->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
901
902 if ($result < 0) {
903 setEventMessages($deposit->error, $deposit->errors, 'errors');
904 }
905 }
906
907 if ($locationTarget) {
908 header('Location: ' . $locationTarget);
909 exit;
910 }
911 } else {
912 $object->status = $oldstatus;
913 $object->statut = $oldstatus; // deprecated
914
915 $db->rollback();
916 $action = '';
917 }
918 }
919 }
920 } elseif ($action == 'confirm_reopen' && $usercanreopen && !GETPOST('cancel', 'alpha')) {
921 // Reopen proposal
922 // prevent browser refresh from reopening proposal several times
924 $db->begin();
925
926 $newstatus = (getDolGlobalInt('PROPAL_SKIP_ACCEPT_REFUSE') ? Propal::STATUS_DRAFT : Propal::STATUS_VALIDATED);
927 $result = $object->reopen($user, $newstatus);
928 if ($result < 0) {
929 setEventMessages($object->error, $object->errors, 'errors');
930 $error++;
931 } else {
932 $object->statut = $newstatus; // deprecated
933 $object->status = $newstatus;
934 }
935
936 if (!$error) {
937 $db->commit();
938 } else {
939 $db->rollback();
940 }
941 }
942 } elseif ($action == 'import_lines_from_object' && $usercancreate && $object->status == Propal::STATUS_DRAFT) {
943 // add lines from objectlinked
944 $fromElement = GETPOST('fromelement');
945 $fromElementid = GETPOST('fromelementid');
946 $importLines = GETPOST('line_checkbox');
947
948 if (!empty($importLines) && is_array($importLines) && !empty($fromElement) && preg_match('/^[a-zA-Z]+$/', $fromElement) && !empty($fromElementid)) {
949 if ($fromElement == 'commande') {
950 dol_include_once('/' . $fromElement . '/class/' . $fromElement . '.class.php');
951 $lineClassName = 'OrderLine';
952 } elseif ($fromElement == 'propal') {
953 dol_include_once('/comm/' . $fromElement . '/class/' . $fromElement . '.class.php');
954 $lineClassName = 'PropaleLigne';
955 } elseif ($fromElement == 'facture') {
956 dol_include_once('/compta/' . $fromElement . '/class/' . $fromElement . '.class.php');
957 $lineClassName = 'FactureLigne';
958 } else {
959 $lineClassName = null;
960 }
961 $nextRang = count($object->lines) + 1;
962 $importCount = 0;
963 $error = 0;
964 foreach ($importLines as $lineId) {
965 $lineId = intval($lineId);
966 $originLine = new $lineClassName($db);
967 if (intval($fromElementid) > 0 && $originLine->fetch($lineId) > 0) {
968 $originLine->fetch_optionals();
969 $desc = $originLine->desc;
970 $pu_ht = $originLine->subprice;
971 $qty = $originLine->qty;
972 $txtva = $originLine->tva_tx;
973 $txlocaltax1 = $originLine->localtax1_tx;
974 $txlocaltax2 = $originLine->localtax2_tx;
975 $fk_product = $originLine->fk_product;
976 $remise_percent = $originLine->remise_percent;
977 $date_start = $originLine->date_start;
978 $date_end = $originLine->date_end;
979 $fk_code_ventilation = 0;
980 $info_bits = $originLine->info_bits;
981 $fk_remise_except = $originLine->fk_remise_except;
982 $price_base_type = 'HT';
983 $pu_ttc = 0;
984 $type = $originLine->product_type;
985 $rang = $nextRang++;
986 $special_code = $originLine->special_code;
987 $origin = $originLine->element;
988 $origin_id = $originLine->id;
989 $fk_parent_line = 0;
990 $fk_fournprice = $originLine->fk_fournprice;
991 $pa_ht = $originLine->pa_ht;
992 $label = $originLine->label;
993 $array_options = $originLine->array_options;
994 $situation_percent = 100;
995 $fk_prev_id = '';
996 $fk_unit = $originLine->fk_unit;
997 $pu_ht_devise = $originLine->multicurrency_subprice;
998
999 $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);
1000
1001 if ($res > 0) {
1002 $importCount++;
1003 } else {
1004 $error++;
1005 }
1006 } else {
1007 $error++;
1008 }
1009 }
1010
1011 if ($error) {
1012 setEventMessages($langs->trans('ErrorsOnXLines', $error), null, 'errors');
1013 }
1014 }
1015 } elseif ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) {
1016 $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0;
1017 $result = $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva);
1018 if ($result < 0) {
1019 setEventMessages($object->error, $object->errors, 'errors');
1020 }
1021 } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) {
1022 $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0;
1023 $result = $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount);
1024 if ($result < 0) {
1025 setEventMessages($object->error, $object->errors, 'errors');
1026 }
1027 }
1028
1029 include DOL_DOCUMENT_ROOT . '/core/actions_printing.inc.php';
1030
1031 // Actions to send emails
1032 $actiontypecode = 'AC_EMAIL';
1033 $triggersendname = 'PROPAL_SENTBYMAIL';
1034 $autocopy = 'MAIN_MAIL_AUTOCOPY_PROPOSAL_TO';
1035 $trackid = 'pro' . $object->id;
1036 include DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php';
1037
1038
1039 // Go back to draft
1040 if ($action == 'modif' && $usercancreate) {
1041 $result = $object->setDraft($user);
1042 if ($result < 0) {
1043 setEventMessages($object->error, $object->errors, 'errors');
1044 }
1045
1046 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1047 // Define output language
1048 $outputlangs = $langs;
1049 if (getDolGlobalInt('MAIN_MULTILANGS')) {
1050 $outputlangs = new Translate("", $conf);
1051 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
1052 $outputlangs->setDefaultLang($newlang);
1053 }
1054 $ret = $object->fetch($id); // Reload to get new records
1055 if ($ret > 0) {
1056 $object->fetch_thirdparty();
1057 }
1058 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1059 }
1060 } elseif ($action == "setabsolutediscount" && $usercancreate) {
1061 if (GETPOSTINT("remise_id")) {
1062 if ($object->id > 0) {
1063 $result = $object->insert_discount(GETPOSTINT("remise_id"));
1064 if ($result < 0) {
1065 setEventMessages($object->error, $object->errors, 'errors');
1066 }
1067 }
1068 }
1069 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'aZ09') && (GETPOST('alldate_start', 'alpha') || GETPOST('alldate_end', 'alpha')) && $usercancreate) {
1070 // Define date start and date end for all line
1071 $alldate_start = dol_mktime(GETPOSTINT('alldate_starthour'), GETPOSTINT('alldate_startmin'), 0, GETPOSTINT('alldate_startmonth'), GETPOSTINT('alldate_startday'), GETPOSTINT('alldate_startyear'));
1072 $alldate_end = dol_mktime(GETPOSTINT('alldate_endhour'), GETPOSTINT('alldate_endmin'), 0, GETPOSTINT('alldate_endmonth'), GETPOSTINT('alldate_endday'), GETPOSTINT('alldate_endyear'));
1073 foreach ($object->lines as $key => $line) {
1074 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1075 continue;
1076 }
1077 if ($line->product_type == 1) { // only service line
1078 // Preserve the original entry mode of the line so the total is not drifted by rounding.
1079 $line_price_base_type = $line->wasEnteredIncludingTax() ? 'TTC' : 'HT';
1080 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
1081 $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);
1082 $object->lines[$key] = $object->line;
1083 }
1084 }
1085 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('vatforalllines', 'alpha') !== '' && $usercancreate) {
1086 // Define a vat_rate for all lines
1087 $vat_rate = (GETPOST('vatforalllines') ? GETPOST('vatforalllines') : 0);
1088 $vat_rate = str_replace('*', '', $vat_rate);
1089 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
1090 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
1091 foreach ($object->lines as $key => $line) {
1092 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1093 continue;
1094 }
1095 // Preserve the original entry mode of the line so the total is not drifted by rounding.
1096 $line_price_base_type = $line->wasEnteredIncludingTax() ? 'TTC' : 'HT';
1097 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
1098 $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);
1099 $object->lines[$key] = $object->line;
1100 }
1101 } elseif ($action == 'addline' && GETPOST('submitforalllines', 'alpha') && GETPOST('remiseforalllines', 'alpha') !== '' && $usercancreate) {
1102 // Define a discount for all lines
1103 $remise_percent = (GETPOST('remiseforalllines') ? GETPOST('remiseforalllines') : 0);
1104 $remise_percent = str_replace('*', '', $remise_percent);
1105 foreach ($object->lines as $key => $line) {
1106 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1107 continue;
1108 }
1109 $tvatx = $line->tva_tx;
1110 if (!empty($line->vat_src_code)) {
1111 $tvatx .= ' (' . $line->vat_src_code . ')';
1112 }
1113 // Preserve the original entry mode of the line so the total is not drifted by rounding.
1114 $line_price_base_type = $line->wasEnteredIncludingTax() ? 'TTC' : 'HT';
1115 $line_pu = ($line_price_base_type === 'TTC') ? (float) $line->subprice_ttc : (float) $line->subprice;
1116 $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);
1117 $object->lines[$key] = $object->line;
1118 }
1119 } elseif ($action == 'addline' && GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '' && $usercancreate) {
1120 // Define margin
1121 $margin_rate = (GETPOST('marginforalllines', 'alpha') ? GETPOST('marginforalllines', 'alpha') : 0);
1122 foreach ($object->lines as $key => $line) {
1123 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1124 continue;
1125 }
1126 $subprice = price2num($line->pa_ht * (1 + $margin_rate / 100), 'MU');
1127 $prod = new Product($db);
1128 $prod->fetch($line->fk_product);
1129 if ($prod->price_min > $subprice) {
1130 $price_subprice = price($subprice, 0, $outlangs, 1, -1, -1, 'auto');
1131 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
1132 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
1133 }
1134 // Manage $line->subprice and $line->multicurrency_subprice
1135 if ($line->subprice <> 0) {
1136 $multicurrency_subprice = (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1137 } else {
1138 $multicurrency_subprice = 0;
1139 }
1140 // Update DB
1141 $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);
1142 $object->lines[$key] = $object->line;
1143 // Update $object with new margin info
1144 // $line->price = $subprice;
1145 // $line->marge_tx = $margin_rate;
1146 // $line->marque_tx = $margin_rate * $line->pa_ht / (float) $subprice;
1147 // $line->total_ht = $line->qty * (float) $subprice;
1148 // $line->total_tva = $line->tva_tx * $line->qty * (float) $subprice;
1149 // $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice;
1150 // // Manage $line->subprice and $line->multicurrency_subprice
1151 // $line->multicurrency_total_ht = $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1152 // $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1153 // $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice * $line->multicurrency_subprice / $line->subprice;
1154 // // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
1155 // $line->subprice = (float) $subprice;
1156 // $line->multicurrency_subprice = $multicurrency_subprice;
1157 }
1158 } elseif ($action == 'confirm_addtitleline' && $usercancreate) {
1159 // Handling adding a new title line for subtotals module
1160
1161 $langs->load('subtotals');
1162
1163 $desc = GETPOST('subtotallinedesc', 'alphanohtml');
1164 $depth = GETPOSTINT('subtotallinelevel') ?? 1;
1165
1166 $subtotal_options = array();
1167
1168 foreach (Propal::$TITLE_OPTIONS as $option) {
1169 $value = GETPOST($option, 'alphanohtml');
1170 if ($value) {
1171 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1172 }
1173 }
1174
1175 // Insert line
1176 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
1177
1178 if ($result >= 0) {
1179 if ($result == 0) {
1180 setEventMessages($object->error, $object->errors, 'warnings');
1181 }
1182 $ret = $object->fetch($object->id); // Reload to get new records
1183 $object->fetch_thirdparty();
1184
1185 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1186 // Define output language
1187 $outputlangs = $langs;
1188 $newlang = GETPOST('lang_id', 'alpha');
1189 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1190 $newlang = $object->thirdparty->default_lang;
1191 }
1192 if (!empty($newlang)) {
1193 $outputlangs = new Translate("", $conf);
1194 $outputlangs->setDefaultLang($newlang);
1195 }
1196
1197 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1198 }
1199 } else {
1200 setEventMessages($object->error, $object->errors, 'errors');
1201 }
1202 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
1203 exit();
1204 } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) {
1205 // Handling adding a new subtotal line for subtotals module
1206
1207 $langs->load('subtotals');
1208
1209 $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml');
1210 foreach ($object->lines as $line) {
1211 if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) {
1212 $desc = $line->desc;
1213 $depth = -$line->qty;
1214 }
1215 }
1216
1217 $subtotal_options = array();
1218
1219 foreach (Propal::$SUBTOTAL_OPTIONS as $option) {
1220 $value = GETPOST($option, 'alphanohtml');
1221 if ($value) {
1222 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1223 }
1224 }
1225
1226 // Insert line
1227 if (isset($desc) && isset($depth)) {
1228 $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options);
1229 } else {
1230 $result = -1;
1231 $object->errors[] = $langs->trans("CorrespondingTitleNotFound");
1232 }
1233
1234 if ($result >= 0) {
1235 $ret = $object->fetch($object->id); // Reload to get new records
1236 $object->fetch_thirdparty();
1237
1238 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1239 // Define output language
1240 $outputlangs = $langs;
1241 $newlang = GETPOST('lang_id', 'alpha');
1242 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1243 $newlang = $object->thirdparty->default_lang;
1244 }
1245 if (!empty($newlang)) {
1246 $outputlangs = new Translate("", $conf);
1247 $outputlangs->setDefaultLang($newlang);
1248 }
1249
1250 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1251 }
1252 } else {
1253 setEventMessages($object->error, $object->errors, 'errors');
1254 }
1255 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
1256 exit();
1257 } elseif ($action == 'addline' && !GETPOST('submitforalllines', 'alpha') && !GETPOST('submitforallmargins', 'alpha') && !GETPOST('markforalllines', 'alpha') && $usercancreate) {
1258 // Add line
1259 // Set if we used free entry or predefined product
1260 $predef = '';
1261 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
1262
1263 $price_ht = '';
1264 $price_ht_devise = '';
1265 $price_ttc = '';
1266 $price_ttc_devise = '';
1267
1268 // TODO Implement if (getDolGlobalInt('MAIN_UNIT_PRICE_WITH_TAX_IS_FOR_ALL_TAXES'))
1269
1270 if (GETPOST('price_ht') !== '') {
1271 $price_ht = price2num(GETPOST('price_ht'), 'MU', 2);
1272 }
1273 if (GETPOST('multicurrency_price_ht') !== '') {
1274 $price_ht_devise = price2num(GETPOST('multicurrency_price_ht'), 'CU', 2);
1275 }
1276 if (GETPOST('price_ttc') !== '') {
1277 $price_ttc = price2num(GETPOST('price_ttc'), 'MU', 2);
1278 }
1279 if (GETPOST('multicurrency_price_ttc') !== '') {
1280 $price_ttc_devise = price2num(GETPOST('multicurrency_price_ttc'), 'CU', 2);
1281 }
1282
1283 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
1284 if ($prod_entry_mode == 'free') {
1285 $idprod = 0;
1286 } else {
1287 $idprod = GETPOSTINT('idprod');
1288
1289 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
1290 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
1291 $error++;
1292 }
1293 }
1294
1295 $tva_tx = GETPOST('tva_tx', 'alpha');
1296
1297 $qty = price2num(GETPOST('qty' . $predef, 'alpha'), 'MS', 2);
1298 $remise_percent = (GETPOSTISSET('remise_percent' . $predef) ? price2num(GETPOST('remise_percent' . $predef, 'alpha'), '', 2) : 0);
1299 if (empty($remise_percent)) {
1300 $remise_percent = 0;
1301 }
1302
1303 // Extrafields
1304 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1305 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
1306 // Unset extrafield
1307 if (is_array($extralabelsline)) {
1308 // Get extra fields
1309 foreach ($extralabelsline as $key => $value) {
1310 unset($_POST["options_" . $key]);
1311 }
1312 }
1313
1314 $price_to_test_sign = ($price_ht ? $price_ht : $price_ttc);
1315
1316 if ((empty($idprod) || $idprod < 0) && ($price_to_test_sign < 0) && ($qty < 0)) {
1317 $langs->load("errors");
1318 setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1319 $error++;
1320 }
1321 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
1322 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
1323 $error++;
1324 }
1325
1326 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.
1327 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("UnitPriceHT")), null, 'errors');
1328 $error++;
1329 }
1330 if ($qty < 0 && !getDolGlobalString('PROPAL_ENABLE_NEGATIVE_QTY')) {
1331 setEventMessages($langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1332 $error++;
1333 }
1334 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
1335 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")), null, 'errors');
1336 $error++;
1337 }
1338
1339 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
1340 if ($combinations = GETPOST('combinations', 'array:alphanohtml')) {
1341 //Check if there is a product with the given combination
1342 $prodcomb = new ProductCombination($db);
1343
1344 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
1345 $idprod = $res->fk_product_child;
1346 } else {
1347 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
1348 $error++;
1349 }
1350 }
1351 }
1352
1353 if (!$error && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
1354 $pu_ht = 0;
1355 $pu_ttc = 0;
1356 $pu_ht_devise = 0;
1357 $pu_ttc_devise = 0;
1358 $price_min = 0;
1359 $price_min_ttc = 0;
1360 $tva_npr = 0;
1361 $price_base_type = (GETPOST('price_base_type', 'alpha') ? GETPOST('price_base_type', 'alpha') : 'HT');
1362
1363 $db->begin();
1364
1365 // $tva_tx can be 'x.x (XXX)'
1366
1367 // Ecrase $pu par celui du produit
1368 // Ecrase $desc par celui du produit
1369 // Replaces $fk_unit with the product unit
1370 if (!empty($idprod) && $idprod > 0) {
1371 $prod = new Product($db);
1372 $prod->fetch($idprod);
1373
1374 $label = ((GETPOST('product_label') && GETPOST('product_label') != $prod->label) ? GETPOST('product_label') : '');
1375
1376 // Update if prices fields are defined
1377 /*$tva_tx = get_default_tva($mysoc, $object->thirdparty, $prod->id);
1378 $tva_npr = get_default_npr($mysoc, $object->thirdparty, $prod->id);
1379 if (empty($tva_tx)) {
1380 $tva_npr = 0;
1381 }*/
1382
1383 // Price unique per product
1384 $pu_ht = $prod->price;
1385 $pu_ttc = $prod->price_ttc;
1386 $price_min = $prod->price_min;
1387 $price_min_ttc = $prod->price_min_ttc;
1388 $price_base_type = $prod->price_base_type;
1389
1390 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1391 // If price per customer
1392 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1393 $prodcustprice = new ProductCustomerPrice($db);
1394 $filter = array('t.fk_product' => (string) $prod->id, 't.fk_soc' => (string) $object->thirdparty->id);
1395
1396 // If a price per customer exist
1397 $pricebycustomerexist = false;
1398 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1399 if ($result >= 0) {
1400 // If there is some prices specific to the customer
1401 if (count($prodcustprice->lines) > 0) {
1402 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
1403 foreach ($prodcustprice->lines as $k => $custprice_line) {
1404 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
1405 $pricebycustomerexist = true;
1406 $pu_ht = price($custprice_line->price);
1407 $pu_ttc = price($custprice_line->price_ttc);
1408 $price_min = price($custprice_line->price_min);
1409 $price_min_ttc = price($custprice_line->price_min_ttc);
1410 $price_base_type = $custprice_line->price_base_type;
1411 /*$tva_tx = ($custprice_line->default_vat_code ? $custprice_line->tva_tx.' ('.$custprice_line->default_vat_code.' )' : $custprice_line->tva_tx);
1412 if ($custprice_line->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1413 $tva_tx .= ' ('.$custprice_line->default_vat_code.')';
1414 }
1415 $tva_npr = $custprice_line->recuperableonly;
1416 if (empty($tva_tx)) {
1417 $tva_npr = 0;
1418 }*/
1419 break;
1420 }
1421 }
1422 }
1423 } else {
1424 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
1425 }
1426
1427 if (!$pricebycustomerexist && !empty($object->thirdparty->price_level)) { // If price per segment
1428 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1429 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1430 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1431 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1432 $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level];
1433 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1434 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1435 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1436 }
1437 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1438 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1439 }
1440 }
1441 }
1442 } elseif (getDolGlobalString('PRODUIT_MULTIPRICES') && !empty($object->thirdparty->price_level)) { // If price per segment
1443 $pu_ht = $prod->multiprices[$object->thirdparty->price_level];
1444 $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level];
1445 $price_min = $prod->multiprices_min[$object->thirdparty->price_level];
1446 $price_min_ttc = $prod->multiprices_min_ttc[$object->thirdparty->price_level];
1447 $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level];
1448 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1449 if (isset($prod->multiprices_tva_tx[$object->thirdparty->price_level])) {
1450 $tva_tx = $prod->multiprices_tva_tx[$object->thirdparty->price_level];
1451 }
1452 if (isset($prod->multiprices_recuperableonly[$object->thirdparty->price_level])) {
1453 $tva_npr = $prod->multiprices_recuperableonly[$object->thirdparty->price_level];
1454 }
1455 }
1456 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1457 // If price per customer
1458 require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
1459
1460 $prodcustprice = new ProductCustomerPrice($db);
1461
1462 $filter = array('t.fk_product' => (string) $prod->id, 't.fk_soc' => (string) $object->thirdparty->id);
1463
1464 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1465 if ($result >= 0) {
1466 // If there is some prices specific to the customer
1467 if (count($prodcustprice->lines) > 0) {
1468 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
1469 foreach ($prodcustprice->lines as $k => $custprice_line) {
1470 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
1471 $pu_ht = price($custprice_line->price);
1472 $pu_ttc = price($custprice_line->price_ttc);
1473 $price_min = price($custprice_line->price_min);
1474 $price_min_ttc = price($custprice_line->price_min_ttc);
1475 $price_base_type = $custprice_line->price_base_type;
1476 /*$tva_tx = ($custprice_line->default_vat_code ? $custprice_line->tva_tx.' ('.$custprice_line->default_vat_code.' )' : $custprice_line->tva_tx);
1477 if ($custprice_line->default_vat_code && !preg_match('/\‍(.*\‍)/', $tva_tx)) {
1478 $tva_tx .= ' ('.$custprice_line->default_vat_code.')';
1479 }
1480 $tva_npr = $custprice_line->recuperableonly;
1481 if (empty($tva_tx)) {
1482 $tva_npr = 0;
1483 }*/
1484 break;
1485 }
1486 }
1487 }
1488 } else {
1489 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
1490 }
1491 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY')) {
1492 // If price per quantity
1493 if ($prod->prices_by_qty[0]) { // yes, this product has some prices per quantity
1494 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
1495 $pqp = GETPOSTINT('pbq');
1496
1497 // Search price into product_price_by_qty from $prod->id
1498 foreach ($prod->prices_by_qty_list[0] as $priceforthequantityarray) {
1499 if ($priceforthequantityarray['rowid'] != $pqp) {
1500 continue;
1501 }
1502 // We found the price
1503 if ($priceforthequantityarray['price_base_type'] == 'HT') {
1504 $pu_ht = $priceforthequantityarray['unitprice'];
1505 } else {
1506 $pu_ttc = $priceforthequantityarray['unitprice'];
1507 }
1508 // Note: the remise_percent or price by qty is used to set data on form, so we will use value from POST.
1509 break;
1510 }
1511 }
1512 } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
1513 // If price per quantity and customer
1514 if ($prod->prices_by_qty[$object->thirdparty->price_level]) { // yes, this product has some prices per quantity
1515 // Search the correct price into loaded array product_price_by_qty using id of array retrieved into POST['pqp'].
1516 $pqp = GETPOSTINT('pbq');
1517
1518 // Search price into product_price_by_qty from $prod->id
1519 foreach ($prod->prices_by_qty_list[$object->thirdparty->price_level] as $priceforthequantityarray) {
1520 if ($priceforthequantityarray['rowid'] != $pqp) {
1521 continue;
1522 }
1523 // We found the price
1524 if ($priceforthequantityarray['price_base_type'] == 'HT') {
1525 $pu_ht = $priceforthequantityarray['unitprice'];
1526 } else {
1527 $pu_ttc = $priceforthequantityarray['unitprice'];
1528 }
1529 // Note: the remise_percent or price by qty is used to set data on form, so we will use value from POST.
1530 break;
1531 }
1532 }
1533 }
1534
1535 $tmpvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
1536 $tmpprodvat = (float) price2num(preg_replace('/\s*\‍(.*\‍)/', '', (string) $prod->tva_tx));
1537
1538 // Set unit price to use.
1539 // Force price_base_type to match what the user actually entered, so the total is computed
1540 // from the typed value and not from the converted/rounded value (avoids 0.01 rounding drift).
1541 if (!empty($price_ht) || (string) $price_ht === '0') {
1542 $pu_ht = (float) price2num($price_ht, 'MU');
1543 $pu_ttc = (float) price2num((float) $pu_ht * (1 + ((float) $tmpvat / 100)), 'MU');
1544 $price_base_type = 'HT';
1545 } elseif (!empty($price_ht_devise) || (string) $price_ht_devise === '0') {
1546 $pu_ht_devise = price2num($price_ht_devise, 'MU');
1547 $pu_ttc_devise = (float) price2num((float) $pu_ht_devise * (1 + ((float) $tmpvat / 100)), 'MU');
1548 $pu_ht = '';
1549 $pu_ttc = '';
1550 $price_base_type = 'HT';
1551 } elseif (!empty($price_ttc) || (string) $price_ttc === '0') {
1552 $pu_ttc = (float) price2num($price_ttc, 'MU');
1553 $pu_ht = (float) price2num((float) $pu_ttc / (1 + ((float) $tmpvat / 100)), 'MU');
1554 $price_base_type = 'TTC';
1555 } elseif (!empty($price_ttc_devise) || (string) $price_ttc_devise === '0') {
1556 $pu_ttc_devise = (float) price2num($price_ttc_devise, 'MU');
1557 $pu_ht_devise = (float) price2num((float) $pu_ttc_devise / (1 + ((float) $tmpvat / 100)), 'MU');
1558 $pu_ht = '';
1559 $pu_ttc = '';
1560 $price_base_type = 'TTC';
1561 } elseif ($tmpvat != $tmpprodvat) {
1562 // Is this still used ?
1563 if ($price_base_type != 'HT') {
1564 $pu_ht = (float) price2num((float) $pu_ttc / (1 + ((float) $tmpvat / 100)), 'MU');
1565 } else {
1566 $pu_ttc = (float) price2num((float) $pu_ht * (1 + ((float) $tmpvat / 100)), 'MU');
1567 }
1568 }
1569
1570 $desc = '';
1571
1572 // Define output language
1573 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1574 $outputlangs = $langs;
1575 $newlang = '';
1576 if (/* empty($newlang) && */GETPOST('lang_id', 'aZ09')) {
1577 $newlang = GETPOST('lang_id', 'aZ09');
1578 }
1579 if (empty($newlang)) {
1580 $newlang = $object->thirdparty->default_lang;
1581 }
1582 if (!empty($newlang)) {
1583 $outputlangs = new Translate("", $conf);
1584 $outputlangs->setDefaultLang($newlang);
1585 }
1586
1587 $desc = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description;
1588 } else {
1589 $desc = $prod->description;
1590 }
1591
1592 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
1593 // 'DoNotAutofillButAutoConcat'
1594 $desc = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
1595 } else {
1596 //'AutoFillFormFieldBeforeSubmit' or 'DoNotUseDescriptionOfProdut' => User has already done the modification they want
1597 $desc = $line_desc;
1598 }
1599
1600 // Add custom code and origin country into description
1601 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
1602 $tmptxt = '(';
1603 // Define output language
1604 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1605 $outputlangs = $langs;
1606 $newlang = '';
1607 if (/* empty($newlang) && */GETPOST('lang_id', 'alpha')) {
1608 $newlang = GETPOST('lang_id', 'alpha');
1609 }
1610 if (empty($newlang)) {
1611 $newlang = $object->thirdparty->default_lang;
1612 }
1613 if (!empty($newlang)) {
1614 $outputlangs = new Translate("", $conf);
1615 $outputlangs->setDefaultLang($newlang);
1616 $outputlangs->load('products');
1617 }
1618 if (!empty($prod->customcode)) {
1619 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode") . ': ' . $prod->customcode;
1620 }
1621 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1622 $tmptxt .= ' - ';
1623 }
1624 if (!empty($prod->country_code)) {
1625 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin") . ': ' . getCountry($prod->country_code, '', $db, $outputlangs, 0);
1626 }
1627 } else {
1628 if (!empty($prod->customcode)) {
1629 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode") . ': ' . $prod->customcode;
1630 }
1631 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1632 $tmptxt .= ' - ';
1633 }
1634 if (!empty($prod->country_code)) {
1635 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin") . ': ' . getCountry($prod->country_code, '', $db, $langs, 0);
1636 }
1637 }
1638 $tmptxt .= ')';
1639 $desc = dol_concatdesc($desc, $tmptxt);
1640 }
1641
1642 $type = $prod->type;
1643 $fk_unit = $prod->fk_unit;
1644 } else {
1645 $pu_ht = price2num($price_ht, 'MU');
1646 $pu_ttc = price2num($price_ttc, 'MU');
1647 $tva_npr = (preg_match('/\*/', $tva_tx) ? 1 : 0);
1648 $tva_tx = str_replace('*', '', $tva_tx);
1649 if (empty($tva_tx)) {
1650 $tva_npr = 0;
1651 }
1652 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
1653 $desc = $line_desc;
1654 $type = GETPOST('type');
1655 $fk_unit = GETPOST('units', 'alpha');
1656 $pu_ht_devise = price2num($price_ht_devise, 'MU');
1657 $pu_ttc_devise = price2num($price_ttc_devise, 'MU');
1658
1659 if ($pu_ttc && !$pu_ht) {
1660 $price_base_type = 'TTC';
1661 }
1662 }
1663
1664 $info_bits = 0;
1665 if ($tva_npr) {
1666 $info_bits |= 0x01;
1667 }
1668
1669 // Local Taxes
1670 $localtax1_tx = get_localtax($tva_tx, 1, $object->thirdparty, $mysoc, $tva_npr);
1671 $localtax2_tx = get_localtax($tva_tx, 2, $object->thirdparty, $mysoc, $tva_npr);
1672
1673 // Margin
1674 $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
1675 $buyingprice = price2num((GETPOST('buying_price' . $predef) != '' ? GETPOST('buying_price' . $predef) : ''), '', 2); // If buying_price is '0', we must keep this value
1676
1677 $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'));
1678 $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'));
1679
1680 // Prepare a price equivalent for minimum price check
1681 $pu_equivalent = $pu_ht;
1682 $pu_equivalent_ttc = $pu_ttc;
1683 $currency_tx = $object->multicurrency_tx;
1684
1685 // Check if we have a foreign currency
1686 // If so, we update the pu_equiv as the equivalent price in base currency
1687 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1688 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
1689 }
1690 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1691 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
1692 }
1693
1694 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
1695 /*
1696 if ($pu_equivalent) {
1697 $tmp = calcul_price_total(1, $pu_equivalent, 0, $tva_tx, -1, -1, 0, 'HT', $info_bits, $type);
1698 $pu_equivalent_ttc = ...
1699 } else {
1700 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $tva_tx, -1, -1, 0, 'TTC', $info_bits, $type);
1701 $pu_equivalent_ht = ...
1702 }
1703 */
1704
1705 //var_dump(price2num($price_min)); var_dump(price2num($pu_ht)); var_dump($remise_percent);
1706 //var_dump(price2num($price_min_ttc)); var_dump(price2num($pu_ttc)); var_dump($remise_percent);exit;
1707
1708 //$desc = dol_htmlcleanlastbr($desc);
1709
1710 // Check price is not lower than minimum
1711 if ($usermustrespectpricemin) {
1712 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
1713 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
1714 setEventMessages($mesg, null, 'errors');
1715 $error++;
1716 } 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') {
1717 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
1718 setEventMessages($mesg, null, 'errors');
1719 $error++;
1720 }
1721 }
1722
1723 if (!$error) {
1724 // Insert line
1725 $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);
1726
1727 if ($result > 0) {
1728 $db->commit();
1729
1730 $ret = $object->fetch($object->id); // Reload to get new records
1731 if ($ret > 0) {
1732 $object->fetch_thirdparty();
1733 }
1734
1735 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1736 // Define output language
1737 $outputlangs = $langs;
1738 $newlang = GETPOST('lang_id', 'alpha');
1739 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1740 $newlang = $object->thirdparty->default_lang;
1741 }
1742 if (!empty($newlang)) {
1743 $outputlangs = new Translate("", $conf);
1744 $outputlangs->setDefaultLang($newlang);
1745 }
1746 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1747 }
1748
1749 unset($_POST['prod_entry_mode']);
1750
1751 unset($_POST['qty']);
1752 unset($_POST['type']);
1753 unset($_POST['remise_percent']);
1754 unset($_POST['price_ht']);
1755 unset($_POST['multicurrency_price_ht']);
1756 unset($_POST['price_ttc']);
1757 unset($_POST['tva_tx']);
1758 unset($_POST['product_ref']);
1759 unset($_POST['product_label']);
1760 unset($_POST['product_desc']);
1761 unset($_POST['fournprice']);
1762 unset($_POST['buying_price']);
1763 unset($_POST['np_marginRate']);
1764 unset($_POST['np_markRate']);
1765 unset($_POST['dp_desc']);
1766 unset($_POST['idprod']);
1767 unset($_POST['units']);
1768
1769 unset($_POST['date_starthour']);
1770 unset($_POST['date_startmin']);
1771 unset($_POST['date_startsec']);
1772 unset($_POST['date_startday']);
1773 unset($_POST['date_startmonth']);
1774 unset($_POST['date_startyear']);
1775 unset($_POST['date_endhour']);
1776 unset($_POST['date_endmin']);
1777 unset($_POST['date_endsec']);
1778 unset($_POST['date_endday']);
1779 unset($_POST['date_endmonth']);
1780 unset($_POST['date_endyear']);
1781 } else {
1782 $db->rollback();
1783
1784 setEventMessages($object->error, $object->errors, 'errors');
1785 }
1786 }
1787 }
1788 } elseif ($action == 'addline' && $usercancreate && (
1789 (GETPOST('submitforallmargins', 'alpha') && GETPOST('marginforalllines', 'alpha') !== '') ||
1790 (GETPOST('submitforallmark', 'alpha') && GETPOST('markforalllines', 'alpha') !== '')
1791 )) {
1792 $outlangs = $langs;
1793 // Define margin
1794 $margin_rate = GETPOSTISSET('marginforalllines') ? GETPOST('marginforalllines', 'int') : '';
1795 $mark_rate = GETPOSTISSET('markforalllines') ? GETPOST('markforalllines', 'int') : '';
1796 foreach ($object->lines as &$line) {
1797 if ($line->subprice > 0) {
1798 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1799 continue;
1800 }
1801 $subprice_multicurrency = $line->subprice;
1802 if (is_numeric($margin_rate) && $margin_rate > 0) {
1803 $line->subprice = (float) price2num((float) $line->pa_ht * (1 + (float) $margin_rate / 100), 'MU');
1804 } elseif (is_numeric($mark_rate) && $mark_rate > 0) {
1805 $line->subprice = (float) ($line->pa_ht / (1 - ((float) $mark_rate / 100)));
1806 } else {
1807 $line->subprice = (float) $line->pa_ht;
1808 }
1809
1810 if ($line->fk_product > 0) {
1811 $prod = new Product($db);
1812 $res = $prod->fetch($line->fk_product);
1813 if ($res > 0) {
1814 if ($prod->price_min > $line->subprice) {
1815 $price_subprice = price($line->subprice, 0, $outlangs, 1, -1, -1, 'auto');
1816 $price_price_min = price($prod->price_min, 0, $outlangs, 1, -1, -1, 'auto');
1817 setEventMessages($prod->ref . ' - ' . $prod->label . ' (' . $price_subprice . ' < ' . $price_price_min . ' ' . strtolower($langs->trans("MinPrice")) . ')' . "\n", null, 'warnings');
1818 } else {
1819 setEventMessages($prod->error, $prod->errors, 'errors');
1820 }
1821 } else {
1822 setEventMessages($prod->error, $prod->errors, 'errors');
1823 }
1824 }
1825
1826 // Manage $line->subprice and $line->multicurrency_subprice
1827 $multicurrency_subprice = (float) $line->subprice * $line->multicurrency_subprice / $subprice_multicurrency;
1828 // Update DB
1829 $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);
1830 // Update $object with new margin info
1831 if ($result > 0) {
1832 if (is_numeric($margin_rate) && empty($mark_rate)) {
1833 $line->marge_tx = $margin_rate;
1834 } elseif (is_numeric($mark_rate) && empty($margin_rate)) {
1835 $line->marque_tx = $mark_rate;
1836 }
1837 $line->total_ht = $line->qty * (float) $line->subprice;
1838 $line->total_tva = $line->tva_tx * $line->qty * (float) $line->subprice;
1839 $line->total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $line->subprice;
1840 // Manage $line->subprice and $line->multicurrency_subprice
1841 $line->multicurrency_total_ht = $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1842 $line->multicurrency_total_tva = $line->tva_tx * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1843 $line->multicurrency_total_ttc = (1 + $line->tva_tx) * $line->qty * (float) $subprice_multicurrency * $line->multicurrency_subprice / $line->subprice;
1844 // Used previous $line->subprice and $line->multicurrency_subprice above, now they can be set to their new values
1845 $line->multicurrency_subprice = $multicurrency_subprice;
1846 } else {
1847 setEventMessages($object->error, $object->errors, 'errors');
1848 }
1849 }
1850 }
1851 } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
1852 // Handling updating a title line for subtotals module
1853
1854 $langs->load('subtotals');
1855
1856 $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title");
1857 $depth = GETPOSTINT('line_depth') ?? 1;
1858
1859 $subtotal_options = array();
1860
1861 foreach (Propal::$TITLE_OPTIONS as $option) {
1862 $value = GETPOST($option, 'alphanohtml');
1863 if ($value) {
1864 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1865 }
1866 }
1867
1868 // Update line
1869 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
1870
1871 if ($result >= 0) {
1872 if ($result == 0) {
1873 setEventMessages($object->error, $object->errors, 'warnings');
1874 }
1875 $ret = $object->fetch($object->id); // Reload to get new records
1876 $object->fetch_thirdparty();
1877
1878 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1879 // Define output language
1880 $outputlangs = $langs;
1881 $newlang = GETPOST('lang_id', 'alpha');
1882 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1883 $newlang = $object->thirdparty->default_lang;
1884 }
1885 if (!empty($newlang)) {
1886 $outputlangs = new Translate("", $conf);
1887 $outputlangs->setDefaultLang($newlang);
1888 }
1889
1890 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1891 }
1892 } else {
1893 setEventMessages($object->error, $object->errors, 'errors');
1894 }
1895 } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) {
1896 // Handling updating a subtotal line for subtotals module
1897
1898 $langs->load('subtotals');
1899
1900 $desc = GETPOST('line_desc', 'alphanohtml');
1901 $depth = GETPOSTINT('line_depth');
1902
1903 $subtotal_options = array();
1904
1905 foreach (Propal::$SUBTOTAL_OPTIONS as $option) {
1906 $value = GETPOST($option, 'alphanohtml');
1907 if ($value) {
1908 $subtotal_options[$option] = $value == 'on' ? 1 : $value;
1909 }
1910 }
1911
1912 // Update line
1913 $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options);
1914
1915 if ($result > 0) {
1916 $ret = $object->fetch($object->id); // Reload to get new records
1917 $object->fetch_thirdparty();
1918
1919 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1920 // Define output language
1921 $outputlangs = $langs;
1922 $newlang = GETPOST('lang_id', 'alpha');
1923 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1924 $newlang = $object->thirdparty->default_lang;
1925 }
1926 if (!empty($newlang)) {
1927 $outputlangs = new Translate("", $conf);
1928 $outputlangs->setDefaultLang($newlang);
1929 }
1930
1931 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1932 }
1933 } else {
1934 setEventMessages($object->error, $object->errors, 'errors');
1935 }
1936 } elseif ($action == 'updateline' && $usercancreate && GETPOST('save')) {
1937 // Update a line
1938
1939 // Clean parameters
1940 $description = dol_htmlcleanlastbr(GETPOST('product_desc', 'restricthtml'));
1941
1942 // Define info_bits
1943 $info_bits = 0;
1944 if (preg_match('/\*/', GETPOST('tva_tx'))) {
1945 $info_bits |= 0x01;
1946 }
1947
1948 // Define vat_rate
1949 $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx', 'alpha') : 0);
1950 $vat_rate = str_replace('*', '', $vat_rate);
1951 $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc);
1952 $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc);
1953 $pu_ht = price2num(GETPOST('price_ht'), '', 2);
1954 $pu_ttc = price2num(GETPOST('price_ttc'), '', 2);
1955
1956 // Add buying price
1957 $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
1958 $buyingprice = price2num((GETPOST('buying_price') != '' ? GETPOST('buying_price') : ''), '', 2); // If buying_price is '0', we must keep this value
1959
1960 $pu_ht_devise = price2num(GETPOST('multicurrency_subprice'), '', 2);
1961 $pu_ttc_devise = price2num(GETPOST('multicurrency_subprice_ttc'), '', 2);
1962
1963 $date_start = dol_mktime(GETPOSTINT('date_starthour'), GETPOSTINT('date_startmin'), GETPOSTINT('date_startsec'), GETPOSTINT('date_startmonth'), GETPOSTINT('date_startday'), GETPOSTINT('date_startyear'));
1964 $date_end = dol_mktime(GETPOSTINT('date_endhour'), GETPOSTINT('date_endmin'), GETPOSTINT('date_endsec'), GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'));
1965
1966 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
1967 if (empty($remise_percent)) {
1968 $remise_percent = 0;
1969 }
1970
1971 // Prepare a price equivalent for minimum price check
1972 $pu_equivalent = $pu_ht;
1973 $pu_equivalent_ttc = $pu_ttc;
1974
1975 $currency_tx = $object->multicurrency_tx;
1976
1977 // Check if we have a foreign currency
1978 // If so, we update the pu_equiv as the equivalent price in base currency
1979 if ($pu_ht == '' && $pu_ht_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1980 $pu_equivalent = (float) $pu_ht_devise / (float) $currency_tx;
1981 }
1982 if ($pu_ttc == '' && $pu_ttc_devise != '' && $currency_tx != '' && !empty((float) $currency_tx)) {
1983 $pu_equivalent_ttc = (float) $pu_ttc_devise / (float) $currency_tx;
1984 }
1985
1986 // TODO $pu_equivalent or $pu_equivalent_ttc must be calculated from the one not null taking into account all taxes
1987 /*
1988 if ($pu_equivalent) {
1989 $tmp = calcul_price_total(1, $pu_equivalent, 0, $vat_rate, -1, -1, 0, 'HT', $info_bits, $type);
1990 $pu_equivalent_ttc = ...
1991 } else {
1992 $tmp = calcul_price_total(1, $pu_equivalent_ttc, 0, $vat_rate, -1, -1, 0, 'TTC', $info_bits, $type);
1993 $pu_equivalent_ht = ...
1994 }
1995 */
1996
1997 // Extrafields
1998 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1999 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
2000 // Unset extrafield
2001 if (is_array($extralabelsline)) {
2002 // Get extra fields
2003 foreach ($extralabelsline as $key => $value) {
2004 unset($_POST["options_" . $key]);
2005 }
2006 }
2007
2008 // Define special_code for special lines
2009 $special_code = GETPOSTINT('special_code');
2010 if (!GETPOST('qty')) {
2011 $special_code = 3;
2012 }
2013
2014 // The form JS clears the other field when the user edits one of them: only the modified field is filled.
2015 // When both fields are submitted, the user did not change the price - we must preserve the original
2016 // storage mode of the line, otherwise a no-op save would shift the total by rounding.
2017 $pu = $pu_ht;
2018 $price_base_type = 'HT';
2019 if (empty($pu_ht) && !empty($pu_ttc)) {
2020 $pu = $pu_ttc;
2021 $price_base_type = 'TTC';
2022 } elseif (!empty($pu_ht) && !empty($pu_ttc)) {
2023 foreach ($object->lines as $line_obj) {
2024 if ($line_obj->id == GETPOSTINT('lineid')) {
2025 if ($line_obj->wasEnteredIncludingTax()) {
2026 $pu = $pu_ttc;
2027 $price_base_type = 'TTC';
2028 }
2029 break;
2030 }
2031 }
2032 }
2033
2034 // Check minimum price
2035 $productid = GETPOSTINT('productid');
2036 if (!empty($productid)) {
2037 $product = new Product($db);
2038 $res = $product->fetch($productid);
2039
2040 $type = $product->type;
2041
2042 $label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
2043
2044 $price_min = $product->price_min;
2045 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
2046 $price_min = $product->multiprices_min[$object->thirdparty->price_level];
2047 }
2048 $price_min_ttc = $product->price_min_ttc;
2049 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($object->thirdparty->price_level)) {
2050 $price_min_ttc = $product->multiprices_min_ttc[$object->thirdparty->price_level];
2051 }
2052
2053 //var_dump(price2num($price_min)); var_dump(price2num($pu_ht)); var_dump($remise_percent);
2054 //var_dump(price2num($price_min_ttc)); var_dump(price2num($pu_ttc)); var_dump($remise_percent);exit;
2055
2056 // Check price is not lower than minimum
2057 if ($usermustrespectpricemin) {
2058 if ($pu_equivalent && $price_min && (((float) price2num($pu_equivalent) * (1 - (float) $remise_percent / 100)) < (float) price2num($price_min)) && $price_base_type == 'HT') {
2059 $mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2060 setEventMessages($mesg, null, 'errors');
2061 $error++;
2062 $action = 'editline';
2063 } 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') {
2064 $mesg = $langs->trans("CantBeLessThanMinPriceInclTax", price(price2num($price_min_ttc, 'MU'), 0, $langs, 0, 0, -1, $conf->currency));
2065 setEventMessages($mesg, null, 'errors');
2066 $error++;
2067 $action = 'editline';
2068 }
2069 }
2070 } else {
2071 $type = GETPOST('type');
2072 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
2073
2074 // Check parameters
2075 if (GETPOST('type') < 0) {
2076 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Type")), null, 'errors');
2077 $error++;
2078 }
2079 }
2080
2081 if (!$error) {
2082 $db->begin();
2083
2084 if (!$user->hasRight('margins', 'creer')) {
2085 foreach ($object->lines as &$line) {
2086 if ($line->id == GETPOSTINT('lineid')) {
2087 $fournprice = $line->fk_fournprice;
2088 $buyingprice = (string) $line->pa_ht; // do not convert to float here, it breaks the functioning of $pa_ht_isemptystring
2089 break;
2090 }
2091 }
2092 }
2093
2094 $qty = price2num(GETPOST('qty', 'alpha'), 'MS');
2095
2096 $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);
2097
2098 if ($result >= 0) {
2099 $db->commit();
2100
2101 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
2102 // Define output language
2103 $outputlangs = $langs;
2104 if (getDolGlobalInt('MAIN_MULTILANGS')) {
2105 $outputlangs = new Translate("", $conf);
2106 $newlang = (GETPOST('lang_id', 'aZ09') ? GETPOST('lang_id', 'aZ09') : $object->thirdparty->default_lang);
2107 $outputlangs->setDefaultLang($newlang);
2108 }
2109 $ret = $object->fetch($id); // Reload to get new records
2110 if ($ret > 0) {
2111 $object->fetch_thirdparty();
2112 }
2113 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
2114 }
2115
2116 unset($_POST['qty']);
2117 unset($_POST['type']);
2118 unset($_POST['productid']);
2119 unset($_POST['remise_percent']);
2120 unset($_POST['price_ht']);
2121 unset($_POST['multicurrency_price_ht']);
2122 unset($_POST['price_ttc']);
2123 unset($_POST['tva_tx']);
2124 unset($_POST['product_ref']);
2125 unset($_POST['product_label']);
2126 unset($_POST['product_desc']);
2127 unset($_POST['fournprice']);
2128 unset($_POST['buying_price']);
2129
2130 unset($_POST['date_starthour']);
2131 unset($_POST['date_startmin']);
2132 unset($_POST['date_startsec']);
2133 unset($_POST['date_startday']);
2134 unset($_POST['date_startmonth']);
2135 unset($_POST['date_startyear']);
2136 unset($_POST['date_endhour']);
2137 unset($_POST['date_endmin']);
2138 unset($_POST['date_endsec']);
2139 unset($_POST['date_endday']);
2140 unset($_POST['date_endmonth']);
2141 unset($_POST['date_endyear']);
2142 } else {
2143 $db->rollback();
2144
2145 setEventMessages($object->error, $object->errors, 'errors');
2146 }
2147 }
2148 } elseif ($action == 'updateline' && $usercancreate && GETPOST('cancel', 'alpha')) {
2149 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To re-display card in edit mode
2150 exit();
2151 } elseif ($action == 'classin' && $usercancreate) {
2152 // Set project
2153 $object->setProject(GETPOSTINT('projectid'));
2154 } elseif ($action == 'setavailability' && $usercancreate) {
2155 // Delivery time
2156 $result = $object->set_availability($user, GETPOSTINT('availability_id'));
2157 } elseif ($action == 'setdemandreason' && $usercancreate) {
2158 // Origin of the commercial proposal
2159 $result = $object->set_demand_reason($user, GETPOSTINT('demand_reason_id'));
2160 } elseif ($action == 'setconditions' && $usercancreate) {
2161 // Terms of payment
2162 $result = $object->setPaymentTerms(GETPOSTINT('cond_reglement_id'), GETPOSTFLOAT('cond_reglement_id_deposit_percent'));
2163 } elseif ($action == 'setmode' && $usercancreate) {
2164 // Payment choice
2165 $result = $object->setPaymentMethods(GETPOSTINT('mode_reglement_id'));
2166 } elseif ($action == 'setmulticurrencycode' && $usercancreate) {
2167 // Multicurrency Code
2168 $result = $object->setMulticurrencyCode(GETPOST('multicurrency_code', 'alpha'));
2169 } elseif ($action == 'setmulticurrencyrate' && $usercancreate) {
2170 // Multicurrency rate
2171 $result = $object->setMulticurrencyRate(GETPOSTFLOAT('multicurrency_tx'), GETPOSTINT('calculation_mode'));
2172 } elseif ($action == 'setbankaccount' && $usercancreate) {
2173 // bank account
2174 $result = $object->setBankAccount(GETPOSTINT('fk_account'));
2175 } elseif ($action == 'setshippingmethod' && $usercancreate) {
2176 // shipping method
2177 $result = $object->setShippingMethod(GETPOSTINT('shipping_method_id'));
2178 } elseif ($action == 'setwarehouse' && $usercancreate) {
2179 // warehouse
2180 $result = $object->setWarehouse(GETPOSTINT('warehouse_id'));
2181 } elseif ($action == 'update_extras' && $permissiontoeditextra) {
2182 $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty
2183
2184 $attribute_name = GETPOST('attribute', 'aZ09');
2185
2186 // Fill array 'array_options' with data from update form
2187 $result = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
2188 if ($result < 0) {
2189 $error++;
2190 }
2191
2192 if (!$error) {
2193 $result = $object->updateExtraField($attribute_name, 'PROPAL_MODIFY');
2194 if ($result < 0) {
2195 setEventMessages($object->error, $object->errors, 'errors');
2196 $error++;
2197 }
2198 }
2199 if ($error) {
2200 $action = 'edit_extras';
2201 }
2202 }
2203
2204 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
2205 if ($action == 'addcontact' && $usercancreate) {
2206 $result = 0;
2207 if ($object->id > 0) {
2208 $contactid = (GETPOST('userid') ? GETPOSTINT('userid') : GETPOSTINT('contactid'));
2209 $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type'));
2210 $result = $object->add_contact($contactid, $typeid, GETPOST("source", 'aZ09'));
2211 }
2212
2213 if ($result >= 0) {
2214 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
2215 exit();
2216 } else {
2217 if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2218 $langs->load("errors");
2219 setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
2220 } else {
2221 setEventMessages($object->error, $object->errors, 'errors');
2222 }
2223 }
2224 } elseif ($action == 'swapstatut' && $usercancreate) {
2225 // Toggle the status of a contact
2226 if ($object->fetch($id) > 0) {
2227 $result = $object->swapContactStatus(GETPOSTINT('ligne'));
2228 } else {
2230 }
2231 } elseif ($action == 'deletecontact' && $usercancreate) {
2232 // Delete a contact
2233 $object->fetch($id);
2234 $result = $object->delete_contact($lineid);
2235
2236 if ($result >= 0) {
2237 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
2238 exit();
2239 } else {
2241 }
2242 }
2243 }
2244
2245 // Actions to build doc
2246 $upload_dir = !empty($conf->propal->multidir_output[$object->entity ?? $conf->entity]) ? $conf->propal->multidir_output[$object->entity ?? $conf->entity] : $conf->propal->dir_output;
2247 $permissiontoadd = $usercancreate;
2248 include DOL_DOCUMENT_ROOT . '/core/actions_builddoc.inc.php';
2249}
2250
2251
2252/*
2253 * View
2254 */
2255
2256$form = new Form($db);
2257$formfile = new FormFile($db);
2258$formpropal = new FormPropal($db);
2259$formmargin = new FormMargin($db);
2260$formproject = null;
2261if (isModEnabled('project')) {
2262 $formproject = new FormProjets($db);
2263}
2264
2265$title = $object->ref . " - " . $langs->trans('Card');
2266if ($action == 'create') {
2267 $title = $langs->trans("NewPropal");
2268}
2269$help_url = 'EN:Commercial_Proposals|FR:Proposition_commerciale|ES:Presupuestos|DE:Modul_Angebote';
2270
2271llxHeader('', $title, $help_url);
2272
2273$now = dol_now();
2274
2275// Add new proposal
2276if ($action == 'create') {
2277 $currency_code = getDolCurrency();
2278
2279 print load_fiche_titre($langs->trans("NewProp"), '', 'propal');
2280
2281 $soc = new Societe($db);
2282 if ($socid > 0) {
2283 $res = $soc->fetch($socid);
2284 }
2285
2286 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
2287 $deposit_percent = GETPOSTFLOAT('cond_reglement_id_deposit_percent');
2288 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
2289 $fk_account = GETPOSTINT('fk_account');
2290 $datepropal = (getDolGlobalString('MAIN_DO_NOT_AUTOFILL_DATE_PROPOSAL') ? -1 : ''); // By default '' so we will autofill date. -1 means keep empty.
2291
2292 // Load objectsrc
2293 $objectsrc = null;
2294 if (!empty($origin) && !empty($originid)) {
2295 // Parse element/subelement (ex: project_task)
2296 $element = $subelement = $origin;
2297 $regs = array();
2298 if (preg_match('/^([^_]+)_([^_]+)/i', $origin, $regs)) {
2299 $element = $regs[1];
2300 $subelement = $regs[2];
2301 }
2302
2303 if ($element == 'project') {
2304 $projectid = $originid;
2305
2306 // Fetch project and thirdparty
2307 $project = new Project($db);
2308 $project->fetch($projectid);
2309 if ($project->socid > 0) {
2310 $soc = new Societe($db);
2311 $soc->fetch($project->socid);
2312 }
2313 } else {
2314 // For compatibility
2315 if ($element == 'order' || $element == 'commande') {
2316 $element = $subelement = 'commande';
2317 }
2318 if ($element == 'propal') {
2319 $element = 'comm/propal';
2320 $subelement = 'propal';
2321 }
2322 if ($element == 'contract') {
2323 $element = $subelement = 'contrat';
2324 }
2325 if ($element == 'shipping') {
2326 $element = $subelement = 'expedition';
2327 }
2328
2329 dol_include_once('/' . $element . '/class/' . $subelement . '.class.php');
2330
2331 $classname = ucfirst($subelement);
2332 $objectsrc = new $classname($db);
2333 '@phan-var-force Commande|Propal|Contrat|Expedition $objectsrc'; // Can be other class, but CommonObject is too generic
2335 $objectsrc->fetch($originid);
2336 if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
2337 $objectsrc->fetch_lines();
2338 }
2339 $objectsrc->fetch_thirdparty();
2340
2341 $projectid = (int) $objectsrc->fk_project;
2342 $ref_client = (!empty($objectsrc->ref_client) ? $objectsrc->ref_client : '');
2343
2344 $soc = $objectsrc->thirdparty;
2345
2346 $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 0));
2347 $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0));
2348 $warehouse_id = (!empty($objectsrc->warehouse_id) ? $objectsrc->warehouse_id : (!empty($soc->warehouse_id) ? $soc->warehouse_id : -1));
2349
2350 // Replicate extrafields
2351 $objectsrc->fetch_optionals();
2352 $object->array_options = $objectsrc->array_options;
2353
2354 if (isModEnabled("multicurrency")) {
2355 if (!empty($objectsrc->multicurrency_code)) {
2356 $currency_code = $objectsrc->multicurrency_code;
2357 }
2358 if (getDolGlobalString('MULTICURRENCY_USE_ORIGIN_TX') && !empty($objectsrc->multicurrency_tx)) {
2359 $currency_tx = $objectsrc->multicurrency_tx;
2360 }
2361 }
2362 }
2363 }
2364
2365 // Load default values from thirdparty
2366 if (!empty($soc)) {
2367 $cond_reglement_id = empty($soc->cond_reglement_id) ? $cond_reglement_id : $soc->cond_reglement_id;
2368 $deposit_percent = empty($soc->deposit_percent) ? $deposit_percent : $soc->deposit_percent;
2369 $mode_reglement_id = empty($soc->mode_reglement_id) ? $mode_reglement_id : $soc->mode_reglement_id;
2370 $fk_account = empty($soc->fk_account) ? $fk_account : $soc->fk_account;
2371 $shipping_method_id = $soc->shipping_method_id;
2372 $warehouse_id = !empty($soc->fk_warehouse) ? $soc->fk_warehouse : $warehouse_id;
2373 $remise_percent = $soc->remise_percent;
2374
2375 if (isModEnabled("multicurrency") && !empty($soc->multicurrency_code)) {
2376 $currency_code = $soc->multicurrency_code;
2377 }
2378 }
2379
2380 // If form was posted (but error returned), we must reuse the value posted in priority (standard Dolibarr behaviour)
2381 if (!GETPOST('changecompany')) {
2382 if (GETPOSTISSET('cond_reglement_id')) {
2383 $cond_reglement_id = GETPOSTINT('cond_reglement_id');
2384 }
2385 if (GETPOSTISSET('deposit_percent')) {
2386 $deposit_percent = price2num(GETPOST('deposit_percent', 'alpha'));
2387 }
2388 if (GETPOSTISSET('mode_reglement_id')) {
2389 $mode_reglement_id = GETPOSTINT('mode_reglement_id');
2390 }
2391 if (GETPOSTISSET('fk_account')) {
2392 $fk_account = GETPOSTINT('fk_account');
2393 }
2394 if (GETPOSTISSET('shipping_method_id')) {
2395 $shipping_method_id = GETPOSTINT('shipping_method_id');
2396 }
2397 }
2398 // Warehouse default if null
2399 if ($soc->fk_warehouse > 0) {
2400 $warehouse_id = $soc->fk_warehouse;
2401 }
2402 if (isModEnabled('stock') && $warehouse_id < 0 && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2403 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE')) {
2404 $warehouse_id = getDolGlobalString('MAIN_DEFAULT_WAREHOUSE');
2405 }
2406 if (empty($object->warehouse_id) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && !empty($user->fk_warehouse)) {
2407 $warehouse_id = $user->fk_warehouse;
2408 }
2409 }
2410
2411 print '<form name="addprop" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
2412 print '<input type="hidden" name="token" value="' . newToken() . '">';
2413 print '<input type="hidden" name="action" value="add">';
2414 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
2415 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
2416 if ($origin != 'project' && $originid) {
2417 print '<input type="hidden" name="origin" value="' . $origin . '">';
2418 print '<input type="hidden" name="originid" value="' . $originid . '">';
2419 if ($origin == 'contrat' && !empty($renewal)) {
2420 print '<input type="hidden" name="renewal" value="' . $renewal . '">';
2421 }
2422 } elseif ($origin == 'project' && !empty($projectid)) {
2423 print '<input type="hidden" name="projectid" value="' . $projectid . '">';
2424 }
2425
2426 print dol_get_fiche_head();
2427
2428 // Call Hook tabContentCreateProposal
2429 $parameters = array();
2430 // Note that $action and $object may be modified by hook
2431 $reshook = $hookmanager->executeHooks('tabContentCreateProposal', $parameters, $object, $action);
2432 if (empty($reshook)) {
2433 print '<table class="border centpercent">';
2434
2435 // Reference
2436 //print '<tr class="field_ref"><td class="titlefieldcreate fieldrequired">' . $langs->trans('Ref') . '</td><td class="valuefieldcreate">' . $langs->trans("Draft") . '</td></tr>';
2437
2438 // Third party
2439 print '<tr class="field_socid">';
2440
2441 print '<td class="titlefieldcreate fieldrequired">' . $langs->trans('Customer') . '</td>';
2442 $shipping_method_id = 0;
2443
2444 if ($socid > 0) {
2445 print '<td class="valuefieldcreate">';
2446 print $soc->getNomUrl(1, 'customer');
2447 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
2448 print '</td>';
2449 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2450 $shipping_method_id = $soc->shipping_method_id;
2451 }
2452 } else {
2453 print '<td class="valuefieldcreate">';
2454 $filter = '((s.client:IN:1,2,3) AND (s.status:=:1))';
2455 print img_picto('', 'company', 'class="pictofixedwidth"');
2456 print $form->select_company('', 'socid', $filter, 'SelectThirdParty', 1, 0, array(), 0, 'minwidth300 maxwidth500 widthcentpercentminusxx');
2457 // reload page to retrieve customer information
2458 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
2459 print '<script>
2460 $(document).ready(function() {
2461 $("#socid").change(function() {
2462 console.log("We have changed the company - Reload page");
2463 var socid = $(this).val();
2464 // reload page
2465 $("input[name=action]").val("create");
2466 $("input[name=changecompany]").val("1");
2467 $("form[name=addprop]").submit();
2468 });
2469 });
2470 </script>';
2471 }
2472 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>';
2473 print '</td>';
2474 }
2475 print '</tr>' . "\n";
2476
2477 // Reference of proposal on customer side
2478 if (getDolGlobalString('MAIN_ASK_CUSTOMER_REF_OF_PROPOSAL_AT_CREATION')) {
2479 print '<tr class="field_ref_client"><td class="titlefieldcreate">';
2480 print $form->textwithpicto($langs->trans('RefCustomer'), $langs->trans('RefOfOnCustomerSide', $langs->transnoentitiesnoconv("Proposal")));
2481 print '</td><td class="valuefieldcreate">';
2482 print '<input type="text" name="ref_client" value="' . (!empty($ref_client) ? $ref_client : GETPOST('ref_client')) . '"></td>';
2483 print '</tr>';
2484 }
2485
2486 if ($socid > 0) {
2487 // Contacts (ask contact only if thirdparty already defined).
2488 print '<tr class="field_contactid"><td class="titlefieldcreate">' . $langs->trans("DefaultContact") . '</td><td class="valuefieldcreate">';
2489 print img_picto('', 'contact', 'class="pictofixedwidth"');
2490 //print $form->selectcontacts($soc->id, $contactid, 'contactid', 1, '', '', 0, 'minwidth300 widthcentpercentminusx');
2491 print $form->select_contact($soc->id, $contactid, 'contactid', 1, '', '', 1, 'maxwidth300 widthcentpercentminusx', true);
2492 print '</td></tr>';
2493
2494 // Third party discounts info line
2495 print '<tr class="field_discount_info"><td class="titlefieldcreate">' . $langs->trans('Discounts') . '</td><td class="valuefieldcreate">';
2496
2497 $absolute_discount = $soc->getAvailableDiscounts();
2498
2499 $thirdparty = $soc;
2500 $discount_type = 0;
2501 $backtopage = $_SERVER["PHP_SELF"] . '?socid=' . $thirdparty->id . '&action=' . $action . '&origin=' . urlencode((string) (GETPOST('origin'))) . '&originid=' . urlencode((string) (GETPOSTINT('originid')));
2502 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
2503 print '</td></tr>';
2504 }
2505
2506 $newdatepropal = dol_mktime(0, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'), 'tzserver');
2507 // Date
2508 print '<tr class="field_addprop"><td class="titlefieldcreate fieldrequired">' . $langs->trans('Date') . '</td><td class="valuefieldcreate">';
2509 print img_picto('', 'action', 'class="pictofixedwidth"');
2510 print $form->selectDate($newdatepropal ? $newdatepropal : $datepropal, '', 0, 0, 0, "addprop", 1, 1);
2511 print '</td></tr>';
2512
2513 // Validaty duration
2514 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>';
2515
2516 // Terms of payment
2517 print '<tr class="field_cond_reglement_id"><td class="nowrap">' . $langs->trans('PaymentConditionsShort') . '</td><td>';
2518 print img_picto('', 'payment', 'class="pictofixedwidth"');
2519 // at last resort we take the payment term id which may be filled by default values set (if not getpostisset)
2520 print $form->getSelectConditionsPaiements((int) $cond_reglement_id, 'cond_reglement_id', 1, 1, 0, '', $deposit_percent);
2521 print '</td></tr>';
2522
2523 // Mode of payment
2524 print '<tr class="field_mode_reglement_id"><td class="titlefieldcreate">' . $langs->trans('PaymentMode') . '</td><td class="valuefieldcreate">';
2525 print img_picto('', 'bank', 'class="pictofixedwidth"');
2526 print $form->select_types_paiements((string) $mode_reglement_id, 'mode_reglement_id', 'CRDT', 0, 1, 0, 0, 1, 'maxwidth200 widthcentpercentminusx', 1);
2527 print '</td></tr>';
2528
2529 // Bank Account
2530 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
2531 print '<tr class="field_fk_account"><td class="titlefieldcreate">' . $langs->trans('BankAccount') . '</td><td class="valuefieldcreate">';
2532 print img_picto('', 'bank_account', 'class="pictofixedwidth"') . $form->select_comptes((int) $fk_account, 'fk_account', 0, '', 1, '', 0, 'maxwidth200 widthcentpercentminusx', 1);
2533 print '</td></tr>';
2534 }
2535
2536 // Source / Channel - What trigger creation
2537 print '<tr class="field_demand_reason_id"><td class="titlefieldcreate">' . $langs->trans('Source') . '</td><td class="valuefieldcreate">';
2538 print img_picto('', 'question', 'class="pictofixedwidth"');
2539 $form->selectInputReason((GETPOSTISSET('demand_reason_id') ? GETPOSTINT('demand_reason_id') : ''), 'demand_reason_id', "SRC_PROP", 1, 'maxwidth200 widthcentpercentminusx');
2540 print '</td></tr>';
2541
2542 // Shipping Method
2543 if (isModEnabled("shipping")) {
2544 if (getDolGlobalString('SOCIETE_ASK_FOR_SHIPPING_METHOD') && !empty($soc->shipping_method_id)) {
2545 $shipping_method_id = $soc->shipping_method_id;
2546 }
2547 print '<tr class="field_shipping_method_id"><td class="titlefieldcreate">' . $langs->trans('SendingMethod') . '</td><td class="valuefieldcreate">';
2548 print img_picto('', 'dolly', 'class="pictofixedwidth"');
2549 $form->selectShippingMethod((string) $shipping_method_id, 'shipping_method_id', '', 1, '', 0, 'maxwidth200 widthcentpercentminusx');
2550 print '</td></tr>';
2551 }
2552
2553 $formproduct = null;
2554 // Warehouse
2555 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
2556 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
2557 $formproduct = new FormProduct($db);
2558 print '<tr class="field_warehouse_id"><td class="titlefieldcreate">' . $langs->trans('Warehouse') . '</td><td class="valuefieldcreate">';
2559 print img_picto('', 'stock', 'class="pictofixedwidth"') . $formproduct->selectWarehouses((int) $warehouse_id, 'warehouse_id', '', 1, 0, 0, '', 0, 0, array(), 'maxwidth500 widthcentpercentminusxx');
2560 print '</td></tr>';
2561 }
2562
2563 // Delivery delay
2564 print '<tr class="field_availability_id"><td class="titlefieldcreate">' . $langs->trans('AvailabilityPeriod');
2565 if (isModEnabled('order')) {
2566 print ' (' . $langs->trans('AfterOrder') . ')';
2567 }
2568 print '</td><td class="valuefieldcreate">';
2569 print img_picto('', 'clock', 'class="pictofixedwidth"');
2570 print $form->selectAvailabilityDelay((GETPOSTISSET('availability_id') ? GETPOSTINT('availability_id') : ''), 'availability_id', '', 1, 'maxwidth200 widthcentpercentminusx', 1);
2571 print '</td></tr>';
2572
2573 // Delivery date (or manufacturing)
2574 print '<tr class="field_date_livraison"><td class="titlefieldcreate">' . $langs->trans("DeliveryDate") . '</td>';
2575 print '<td class="valuefieldcreate">';
2576 print img_picto('', 'action', 'class="pictofixedwidth"');
2577 if (is_numeric(getDolGlobalString('DATE_LIVRAISON_WEEK_DELAY'))) { // If value set to 0 or a num, not empty
2578 $tmpdte = time() + (7 * getDolGlobalInt('DATE_LIVRAISON_WEEK_DELAY') * 24 * 60 * 60);
2579 $syear = date("Y", $tmpdte);
2580 $smonth = date("m", $tmpdte);
2581 $sday = date("d", $tmpdte);
2582 print $form->selectDate($syear . "-" . $smonth . "-" . $sday, 'date_livraison', 0, 0, 0, "addprop");
2583 } else {
2584 $tmp_date_delivery = GETPOST('date_delivery') ?: -1;
2585 print $form->selectDate($tmp_date_delivery, 'date_livraison', 0, 0, 0, "addprop", 1, 1);
2586 }
2587 print '</td></tr>';
2588
2589 // Project
2590 if (isModEnabled('project') && is_object($formproject)) {
2591 $langs->load("projects");
2592 print '<tr class="field_projectid">';
2593 print '<td class="titlefieldcreate">'.$langs->trans("Project").'</td><td class="valuefieldcreate">';
2594 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');
2595 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>';
2596 print '</td>';
2597 print '</tr>';
2598 }
2599
2600 // Incoterms
2601 if (isModEnabled('incoterm')) {
2602 print '<tr class="field_incoterm_id">';
2603 print '<td class="titlefieldcreate"><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $soc->label_incoterms, 1) . '</label></td>';
2604 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2605 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
2606 print $form->select_incoterms((!empty($soc->fk_incoterms) ? $soc->fk_incoterms : ''), (!empty($soc->location_incoterms) ? $soc->location_incoterms : ''));
2607 print '</td></tr>';
2608 }
2609 if (isModEnabled('category')) {
2610 // Categories
2611 print '<tr><td>'.$langs->trans("Categories").'</td><td colspan="3">';
2612 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
2613 print "</td></tr>";
2614 }
2615 // Template to use by default
2616 include_once DOL_DOCUMENT_ROOT . '/core/modules/propale/modules_propale.php';
2618 if (is_array($list) && count($list) > 0) {
2619 print '<tr class="field_model">';
2620 print '<td class="titlefieldcreate">' . $langs->trans("DefaultModel") . '</td>';
2621 print '<td class="valuefieldcreate">';
2622 print img_picto('', 'pdf', 'class="pictofixedwidth"');
2623 $preselected = getDolGlobalString('PROPALE_ADDON_PDF_ODT_DEFAULT', getDolGlobalString("PROPALE_ADDON_PDF"));
2624 print $form->selectarray('model', $list, $preselected, 0, 0, 0, '', 0, 0, 0, '', 'maxwidth200 widthcentpercentminusx', 1);
2625 print "</td></tr>";
2626 }
2627
2628 // Multicurrency
2629 if (isModEnabled("multicurrency")) {
2630 print '<tr class="field_currency">';
2631 print '<td class="titlefieldcreate">' . $form->editfieldkey('Currency', 'multicurrency_code', '', $object, 0) . '</td>';
2632 print '<td class="valuefieldcreate maxwidthonsmartphone">';
2633 print img_picto('', 'currency', 'class="pictofixedwidth"') . $form->selectMultiCurrency(((GETPOSTISSET('multicurrency_code') && !GETPOST('changecompany')) ? GETPOST('multicurrency_code') : $currency_code), 'multicurrency_code', 0, '', false, 'maxwidth200 widthcentpercentminusx');
2634 print '</td></tr>';
2635 }
2636
2637 // Public note
2638 print '<tr class="field_note_public">';
2639 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePublic') . '</td>';
2640 print '<td class="valuefieldcreate">';
2641 $note_public = GETPOST('note_public', 'restricthtml');
2642 if (!GETPOSTISSET('note_public') && empty($note_public) && !empty($objectsrc)) {
2643 $note_public = $objectsrc->note_public;
2644 }
2645 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
2646 print $doleditor->Create(1);
2647
2648 // Private note
2649 if (empty($user->socid)) {
2650 print '<tr class="field_note_private">';
2651 print '<td class="titlefieldcreate tdtop">' . $langs->trans('NotePrivate') . '</td>';
2652 print '<td class="valuefieldcreate">';
2653 $note_private = GETPOST('note_private', 'restricthtml');
2654 if (!GETPOSTISSET('note_private') && empty($note_private) && !empty($objectsrc)) {
2655 $note_private = $objectsrc->note_private;
2656 }
2657 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
2658 print $doleditor->Create(1);
2659 // print '<textarea name="note_private" wrap="soft" cols="70" rows="'.ROWS_3.'">'.$note_private.'.</textarea>
2660 print '</td></tr>';
2661 }
2662
2663 // Other attributes
2664 if (getDolGlobalInt('THIRDPARTY_PROPAGATE_EXTRAFIELDS_TO_PROPAL') && $socid > 0) {
2665 $thirdpartytopropagateextrafieldsfrom = $soc;
2666 }
2667 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php';
2668
2669 // Lines from source
2670 if (!empty($origin) && !empty($originid) && is_object($objectsrc) && !empty($classname)) {
2671 // TODO for compatibility
2672 if ($origin == 'contrat') {
2673 // Calcul contrat->price (HT), contrat->total (TTC), contrat->tva
2674 $objectsrc->update_price(1, 'auto', 1);
2675 }
2676
2677 print "\n<!-- " . $classname . " info -->";
2678 print "\n";
2679 print '<input type="hidden" name="amount" value="' . $objectsrc->total_ht . '">' . "\n";
2680 print '<input type="hidden" name="total" value="' . $objectsrc->total_ttc . '">' . "\n";
2681 print '<input type="hidden" name="tva" value="' . $objectsrc->total_tva . '">' . "\n";
2682 print '<input type="hidden" name="origin" value="' . $objectsrc->element . '">';
2683 print '<input type="hidden" name="originid" value="' . $objectsrc->id . '">';
2684
2685 $newclassname = $classname;
2686 if ($newclassname == 'Propal') {
2687 $newclassname = 'CommercialProposal';
2688 } elseif ($newclassname == 'Commande') {
2689 $newclassname = 'Order';
2690 } elseif ($newclassname == 'Expedition') {
2691 $newclassname = 'Sending';
2692 } elseif ($newclassname == 'Fichinter') {
2693 $newclassname = 'Intervention';
2694 }
2695
2696 print '<tr><td>' . $langs->trans($newclassname) . '</td><td>' . $objectsrc->getNomUrl(1) . '</td></tr>';
2697 print '<tr><td>' . $langs->trans('AmountHT') . '</td><td>' . price($objectsrc->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td></tr>';
2698 print '<tr><td>' . $langs->trans('AmountVAT') . '</td><td>' . price($objectsrc->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2699 if ($mysoc->localtax1_assuj == "1" || $objectsrc->total_localtax1 != 0) { // Localtax1
2700 print '<tr><td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2701 }
2702
2703 if ($mysoc->localtax2_assuj == "1" || $objectsrc->total_localtax2 != 0) { // Localtax2
2704 print '<tr><td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td><td>' . price($objectsrc->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2705 }
2706 print '<tr><td>' . $langs->trans('AmountTTC') . '</td><td>' . price($objectsrc->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . "</td></tr>";
2707
2708 if (isModEnabled("multicurrency")) {
2709 print '<tr><td>'.$langs->trans('MulticurrencyAmountHT').'</td><td>'.price($objectsrc->multicurrency_total_ht, 0, $langs, 1, -1, -1, $objectsrc->multicurrency_code).'</td></tr>';
2710 print '<tr><td>'.$langs->trans('MulticurrencyAmountVAT').'</td><td>'.price($objectsrc->multicurrency_total_tva, 0, $langs, 1, -1, -1, $objectsrc->multicurrency_code)."</td></tr>";
2711 print '<tr><td>'.$langs->trans('MulticurrencyAmountTTC').'</td><td>'.price($objectsrc->multicurrency_total_ttc, 0, $langs, 1, -1, -1, $objectsrc->multicurrency_code)."</td></tr>";
2712 }
2713 }
2714
2715 print "</table>\n";
2716
2717
2718 /*
2719 * Combobox for copy function
2720 */
2721
2722 if (!getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2723 print '<input type="hidden" name="createmode" value="empty">';
2724 }
2725
2726 if (getDolGlobalString('PROPAL_CLONE_ON_CREATE_PAGE')) {
2727 print '<br><table>';
2728
2729 // For backward compatibility
2730 print '<tr>';
2731 print '<td><input type="radio" name="createmode" value="copy"></td>';
2732 print '<td>' . $langs->trans("CopyPropalFrom") . ' </td>';
2733 print '<td>';
2734 $liste_propal = array();
2735 $liste_propal[0] = '';
2736
2737 $sql = "SELECT p.rowid as id, p.ref, s.nom";
2738 $sql .= " FROM " . MAIN_DB_PREFIX . "propal p";
2739 $sql .= ", " . MAIN_DB_PREFIX . "societe s";
2740 $sql .= " WHERE s.rowid = p.fk_soc";
2741 $sql .= " AND p.entity IN (" . getEntity('propal') . ")";
2742 $sql .= " AND p.fk_statut <> 0";
2743 $sql .= " ORDER BY Id";
2744
2745 $resql = $db->query($sql);
2746 if ($resql) {
2747 $num = $db->num_rows($resql);
2748 $i = 0;
2749 while ($i < $num) {
2750 $row = $db->fetch_row($resql);
2751 $propalRefAndSocName = $row[1] . " - " . $row[2];
2752 $liste_propal[$row[0]] = $propalRefAndSocName;
2753 $i++;
2754 }
2755 print $form->selectarray("copie_propal", $liste_propal, 0);
2756 } else {
2758 }
2759 print '</td></tr>';
2760
2761 print '<tr><td class="tdtop"><input type="radio" name="createmode" value="empty" checked></td>';
2762 print '<td valign="top" colspan="2">' . $langs->trans("CreateEmptyPropal") . '</td></tr>';
2763 print '</table>';
2764 }
2765 }
2766
2767 print dol_get_fiche_end();
2768
2769 $langs->load("bills");
2770
2771 print $form->buttonsSaveCancel("CreateDraft");
2772
2773 print "</form>";
2774
2775
2776 // Show origin lines
2777 if (!empty($origin) && !empty($originid) && is_object($objectsrc)) {
2778 print '<br>';
2779
2780 $title = $langs->trans('ProductsAndServices');
2781 print load_fiche_titre($title);
2782
2783 print '<div class="div-table-responsive-no-min">';
2784 print '<table class="noborder centpercent">';
2785
2786 $objectsrc->printOriginLinesList();
2787
2788 print '</table>';
2789 print '</div>';
2790 }
2791} elseif ($object->id > 0) {
2792 /*
2793 * Show object in view mode
2794 */
2795 $object->fetch_thirdparty();
2796 if ($object->thirdparty) {
2797 $soc = $object->thirdparty;
2798 } else {
2799 $soc = new Societe($db);
2800 }
2801
2802 $head = propal_prepare_head($object);
2803 print dol_get_fiche_head($head, 'comm', $langs->trans('Proposal'), -1, $object->picto, 0, '', '', 0, '', 1);
2804
2805 $formconfirm = '';
2806
2807 // Clone confirmation
2808 if ($action == 'clone') {
2809 // Create an array for form
2810 $filter = '(s.client:IN:1,2,3)';
2811 $formquestion = array(
2812 // 'text' => $langs->trans("ConfirmClone"),
2813 // array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneMainAttributes"), 'value' => 1),
2814 array('type' => 'other', 'name' => 'socid', 'label' => $langs->trans("SelectThirdParty"), 'value' => $form->select_company(GETPOSTINT('socid'), 'socid', $filter, '', 0, 0, array(), 0, 'maxwidth300')),
2815 array('type' => 'checkbox', 'name' => 'update_prices', 'label' => $langs->trans('PuttingPricesUpToDate'), 'value' => 0),
2816 array('type' => 'checkbox', 'name' => 'update_desc', 'label' => $langs->trans('PuttingDescUpToDate'), 'value' => 0),
2817 );
2818 if (getDolGlobalString('PROPAL_CLONE_DATE_DELIVERY') && !empty($object->delivery_date)) {
2819 $formquestion[] = array('type' => 'date', 'name' => 'date_delivery', 'label' => $langs->trans("DeliveryDate"), 'value' => $object->delivery_date);
2820 }
2821 // Incomplete payment. We ask if reason = discount or other
2822 $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);
2823 }
2824
2825 // Subtotal line form
2826 if ($action == 'add_title_line') {
2827 $langs->load('subtotals');
2828 $type = 'title';
2829 $depth_array = $object->getPossibleLevels($langs);
2830 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2831 } elseif ($action == 'add_subtotal_line') {
2832 $langs->load('subtotals');
2833 $type = 'subtotal';
2834 $titles = $object->getPossibleTitles();
2835 require dol_buildpath('/core/tpl/subtotal_create.tpl.php');
2836 }
2837
2838 if ($action == 'closeas') {
2839 //Form to close proposal (signed or not)
2840 $formquestion = array();
2841 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2842 $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)));
2843 }
2844 $formquestion[] = array('type' => 'text', 'name' => 'note_private', 'label' => $langs->trans("Note"), 'value' => ''); // Field to complete private note (not replace)
2845
2846 if (getDolGlobalInt('PROPOSAL_SUGGEST_DOWN_PAYMENT_INVOICE_CREATION')) {
2847 // This is a hidden option:
2848 // Suggestion to create invoice during proposal signature is not enabled by default.
2849 // Such choice should be managed by the workflow module and trigger. This option generates conflicts with some setup.
2850 // It may also break step of creating an order when invoicing must be done from orders and not from proposal
2851 $deposit_percent_from_payment_terms = getDictionaryValue('c_payment_term', 'deposit_percent', $object->cond_reglement_id);
2852
2853 if (!empty($deposit_percent_from_payment_terms) && isModEnabled('invoice') && $user->hasRight('facture', 'creer')) {
2854 require_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php';
2855
2856 $object->fetchObjectLinked();
2857
2858 $eligibleForDepositGeneration = true;
2859
2860 if (array_key_exists('facture', $object->linkedObjects)) {
2861 foreach ($object->linkedObjects['facture'] as $invoice) {
2862 '@phan-var-force Facture $invoice';
2864 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2865 $eligibleForDepositGeneration = false;
2866 break;
2867 }
2868 }
2869 }
2870
2871 if ($eligibleForDepositGeneration && array_key_exists('commande', $object->linkedObjects)) {
2872 foreach ($object->linkedObjects['commande'] as $order) {
2873 $order->fetchObjectLinked();
2874
2875 if (array_key_exists('facture', $order->linkedObjects)) {
2876 foreach ($order->linkedObjects['facture'] as $invoice) {
2877 '@phan-var-force Facture $invoice';
2879 if ($invoice->type == Facture::TYPE_DEPOSIT) {
2880 $eligibleForDepositGeneration = false;
2881 break 2;
2882 }
2883 }
2884 }
2885 }
2886 }
2887
2888
2889 if ($eligibleForDepositGeneration) {
2890 $formquestion[] = array(
2891 'type' => 'checkbox',
2892 'tdclass' => 'showonlyifsigned',
2893 'name' => 'generate_deposit',
2894 'morecss' => 'margintoponly marginbottomonly',
2895 'label' => $form->textwithpicto($langs->trans('GenerateDeposit', $object->deposit_percent), $langs->trans('DepositGenerationPermittedByThePaymentTermsSelected'))
2896 );
2897
2898 $formquestion[] = array(
2899 'type' => 'date',
2900 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2901 'name' => 'datef',
2902 'label' => $langs->trans('DateInvoice'),
2903 'value' => dol_now(),
2904 'datenow' => true
2905 );
2906
2907 if (getDolGlobalString('INVOICE_POINTOFTAX_DATE')) {
2908 $formquestion[] = array(
2909 'type' => 'date',
2910 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2911 'name' => 'date_pointoftax',
2912 'label' => $langs->trans('DatePointOfTax'),
2913 'value' => dol_now(),
2914 'datenow' => true
2915 );
2916 }
2917
2918 $paymentTermsSelect = $form->getSelectConditionsPaiements(0, 'cond_reglement_id', -1, 0, 1, 'minwidth200');
2919
2920 $formquestion[] = array(
2921 'type' => 'other',
2922 'tdclass' => 'fieldrequired showonlyifgeneratedeposit',
2923 'name' => 'cond_reglement_id',
2924 'label' => $langs->trans('PaymentTerm'),
2925 'value' => $paymentTermsSelect
2926 );
2927
2928 $formquestion[] = array(
2929 'type' => 'checkbox',
2930 'tdclass' => 'showonlyifgeneratedeposit',
2931 'name' => 'validate_generated_deposit',
2932 'morecss' => 'margintoponly marginbottomonly',
2933 'label' => $langs->trans('ValidateGeneratedDeposit')
2934 );
2935
2936 $formquestion[] = array(
2937 'type' => 'onecolumn',
2938 'value' => '
2939 <script>
2940 let signedValue = ' . $object::STATUS_SIGNED . ';
2941
2942 $(document).ready(function() {
2943 $("[name=generate_deposit]").change(function () {
2944 let $self = $(this);
2945 let $target = $(".showonlyifgeneratedeposit").parent(".tagtr");
2946
2947 if (! $self.parents(".tagtr").is(":hidden") && $self.is(":checked")) {
2948 $target.show();
2949 } else {
2950 $target.hide();
2951 }
2952
2953 return true;
2954 });
2955
2956 $("#statut").change(function() {
2957 let $target = $(".showonlyifsigned").parent(".tagtr");
2958
2959 if ($(this).val() == signedValue) {
2960 $target.show();
2961 } else {
2962 $target.hide();
2963 }
2964
2965 $("[name=generate_deposit]").trigger("change");
2966
2967 return true;
2968 });
2969
2970 $("#statut").trigger("change");
2971 });
2972 </script>
2973 '
2974 );
2975 }
2976 }
2977 }
2978
2979 if (isModEnabled('notification')) {
2980 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2981 $notify = new Notify($db);
2982 $formquestion = array_merge($formquestion, array(
2983 array('type' => 'onecolumn', 'value' => $notify->confirmMessage('PROPAL_CLOSE_SIGNED', $object->socid, $object)),
2984 ));
2985 }
2986
2987 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
2988 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SetAcceptedRefused'), '', 'confirm_closeas', $formquestion, '', 1, 250);
2989 } else {
2990 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?statut=3&id=' . $object->id, $langs->trans('Close'), '', 'confirm_closeas', $formquestion, '', 1, 250);
2991 }
2992 } elseif ($action == 'cancel') {
2993 // Confirm cancel
2994 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans("CancelPropal"), $langs->trans('ConfirmCancelPropal', $object->ref), 'confirm_cancel', '', 0, 1);
2995 } elseif ($action == 'delete') {
2996 // Confirm delete
2997 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('DeleteProp'), $langs->trans('ConfirmDeleteProp', $object->ref), 'confirm_delete', '', 0, 1);
2998 } elseif ($action == 'reopen') {
2999 // Confirm reopen
3000 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ReOpen'), $langs->trans('ConfirmReOpenProp', $object->ref), 'confirm_reopen', '', 0, 1);
3001 } elseif ($action == 'ask_deleteline') {
3002 // Confirmation delete product/service line
3003 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1);
3004 } elseif ($action == 'ask_subtotal_deleteline') {
3005 // Confirmation of removal of a subtotal line
3006 $langs->load("subtotals");
3007 $title = "DeleteSubtotalLine";
3008 $question = "ConfirmDeleteSubtotalLine";
3009 if (GETPOST('type') == 'title') {
3010 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
3011 $title = "DeleteTitleLine";
3012 $question = "ConfirmDeleteTitleLine";
3013 }
3014 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
3015 } elseif ($action == 'validate') {
3016 // Confirm validate proposal
3017 $error = 0;
3018
3019 // We verify whether the object is provisionally numbering
3020 $ref = substr($object->ref, 1, 4);
3021 if ($ref == 'PROV' || $ref == '') {
3022 $numref = $object->getNextNumRef($soc);
3023 if (empty($numref)) {
3024 $error++;
3025 setEventMessages($object->error, $object->errors, 'errors');
3026 }
3027 } else {
3028 $numref = (string) $object->ref;
3029 }
3030
3031 $text = $langs->trans('ConfirmValidateProp', $numref);
3032 if (isModEnabled('notification')) {
3033 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
3034 $notify = new Notify($db);
3035 $text .= '<br>';
3036 $text .= $notify->confirmMessage('PROPAL_VALIDATE', $object->socid, $object);
3037 }
3038
3039 // mandatoryPeriod
3040 $nbMandated = 0;
3041 foreach ($object->lines as $line) {
3042 $res = $line->fetch_product();
3043 if ($res > 0) {
3044 if ($line->product->isService() && $line->product->isMandatoryPeriod() && (empty($line->date_start) || empty($line->date_end))) {
3045 $nbMandated++;
3046 break;
3047 }
3048 }
3049 }
3050 if ($nbMandated > 0) {
3051 if (getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
3052 setEventMessages($langs->trans("mandatoryPeriodNeedTobeSetMsgValidate"), null, 'errors');
3053 $error++;
3054 } else {
3055 $text .= '<div><span class="clearboth nowraponall warning">' . img_warning() . $langs->trans("mandatoryPeriodNeedTobeSetMsgValidate") . '</span></div>';
3056 }
3057 }
3058
3059 if (!$error) {
3060 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('ValidateProp'), $text, 'confirm_validate', '', 0, 1, 240);
3061 }
3062 }
3063
3064 // Call Hook formConfirm
3065 $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
3066 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3067 if (empty($reshook)) {
3068 $formconfirm .= $hookmanager->resPrint;
3069 } elseif ($reshook > 0) {
3070 $formconfirm = $hookmanager->resPrint;
3071 }
3072
3073 // Print form confirm
3074 print $formconfirm;
3075
3076
3077 // Proposal card
3078
3079 $linkback = '<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
3080
3081 $morehtmlref = '<div class="refidno">';
3082 // Ref customer
3083 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, $usercancreate, 'string', '', 0, 1);
3084 $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);
3085 // Thirdparty
3086 $morehtmlref .= '<br>' . $soc->getNomUrl(1, 'customer');
3087 if (!getDolGlobalString('MAIN_DISABLE_OTHER_LINK') && $soc->id > 0) {
3088 $morehtmlref .= ' (<a href="' . DOL_URL_ROOT . '/comm/propal/list.php?socid=' . ((int) $soc->id) . '">' . $langs->trans("OtherProposals") . '</a>)';
3089 }
3090 // Project
3091 if (isModEnabled('project')) {
3092 $langs->load("projects");
3093 $morehtmlref .= '<br>';
3094 if ($usercancreate) {
3095 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
3096 if ($action != 'classify') {
3097 $morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
3098 }
3099 $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');
3100 } else {
3101 if (!empty($object->fk_project)) {
3102 $proj = new Project($db);
3103 $proj->fetch($object->fk_project);
3104 $morehtmlref .= $proj->getNomUrl(1);
3105 if ($proj->title) {
3106 $morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
3107 }
3108 }
3109 }
3110 }
3111 $morehtmlref .= '</div>';
3112
3113
3114 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
3115
3116 // Call Hook tabContentViewProposal
3117 $parameters = array();
3118 // Note that $action and $object may be modified by hook
3119 $reshook = $hookmanager->executeHooks('tabContentViewProposal', $parameters, $object, $action);
3120 if (empty($reshook)) {
3121 print '<div class="fichecenter">';
3122 print '<div class="fichehalfleft">';
3123 print '<div class="underbanner clearboth"></div>';
3124
3125 print '<table class="border tableforfield centpercent">';
3126
3127 // Link for thirdparty discounts
3128 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
3129 $filterabsolutediscount = "fk_facture_source IS NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3130 $filtercreditnote = "fk_facture_source IS NOT NULL"; // If we want deposit to be subtracted to payments only and not to total of final invoice
3131 } else {
3132 $filterabsolutediscount = "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')";
3133 $filtercreditnote = "fk_facture_source IS NOT NULL AND (description NOT LIKE '(DEPOSIT)%' OR description LIKE '(EXCESS RECEIVED)%')";
3134 }
3135
3136 print '<tr><td class="titlefieldmax45">' . $langs->trans('Discounts') . '</td><td>';
3137
3138 $absolute_discount = $soc->getAvailableDiscounts(null, $filterabsolutediscount);
3139 $absolute_creditnote = $soc->getAvailableDiscounts(null, $filtercreditnote);
3140 $absolute_discount = price2num($absolute_discount, 'MT');
3141 $absolute_creditnote = price2num($absolute_creditnote, 'MT');
3142
3143 $caneditfield = ($object->status != Propal::STATUS_SIGNED && $object->status != Propal::STATUS_BILLED);
3144
3145 $thirdparty = $soc;
3146 $discount_type = 0;
3147 $backtopage = $_SERVER["PHP_SELF"] . '?id=' . $object->id;
3148 include DOL_DOCUMENT_ROOT . '/core/tpl/object_discounts.tpl.php';
3149
3150 print '</td></tr>';
3151
3152 // Date of proposal
3153 print '<tr>';
3154 print '<td>';
3155 // print '<table class="nobordernopadding" width="100%"><tr><td>';
3156 // print $langs->trans('DatePropal');
3157 // print '</td>';
3158 // if ($action != 'editdate' && $usercancreate && $caneditfield) {
3159 // 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>';
3160 // }
3161
3162 // print '</tr></table>';
3163 $editenable = $usercancreate && $caneditfield && $object->status == Propal::STATUS_DRAFT;
3164 print $form->editfieldkey("DatePropal", 'date', '', $object, (int) $editenable);
3165 print '</td><td class="valuefield">';
3166 if ($action == 'editdate' && $usercancreate && $caneditfield) {
3167 print '<form name="editdate" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3168 print '<input type="hidden" name="token" value="' . newToken() . '">';
3169 print '<input type="hidden" name="action" value="setdate">';
3170 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3171 print $form->selectDate($object->date, 're', 0, 0, 0, "editdate");
3172 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3173 print '</form>';
3174 } else {
3175 if ($object->date) {
3176 print dol_print_date($object->date, 'day');
3177 } else {
3178 print '&nbsp;';
3179 }
3180 }
3181 print '</td>';
3182
3183 // Date end proposal
3184 print '<tr>';
3185 print '<td>';
3186 print '<table class="nobordernopadding centpercent"><tr><td>';
3187 print $langs->trans('DateEndPropal');
3188 print '</td>';
3189 if ($action != 'editecheance' && $usercancreate && $caneditfield) {
3190 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>';
3191 }
3192 print '</tr></table>';
3193 print '</td><td class="valuefield">';
3194 if ($action == 'editecheance' && $usercancreate && $caneditfield) {
3195 print '<form name="editecheance" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3196 print '<input type="hidden" name="token" value="' . newToken() . '">';
3197 print '<input type="hidden" name="action" value="setecheance">';
3198 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
3199 print $form->selectDate($object->fin_validite, 'ech', 0, 0, 0, "editecheance");
3200 print '<input type="submit" class="button button-edit" value="' . $langs->trans('Modify') . '">';
3201 print '</form>';
3202 } else {
3203 if (!empty($object->fin_validite)) {
3204 print dol_print_date($object->fin_validite, 'day');
3205 if ($object->status == Propal::STATUS_VALIDATED && $object->fin_validite < ($now - getWarningDelay('propal', 'cloture'))) {
3206 print img_warning($langs->trans("Late"));
3207 }
3208 } else {
3209 print '&nbsp;';
3210 }
3211 }
3212 print '</td>';
3213 print '</tr>';
3214
3215 // Payment term
3216 print '<tr><td>';
3217 print '<table class="nobordernopadding centpercent"><tr><td>';
3218 print $langs->trans('PaymentConditionsShort');
3219 print '</td>';
3220 if ($action != 'editconditions' && $usercancreate && $caneditfield) {
3221 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>';
3222 }
3223 print '</tr></table>';
3224 print '</td><td class="valuefield">';
3225 if ($action == 'editconditions' && $usercancreate && $caneditfield) {
3226 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'cond_reglement_id', 0, '', 1, $object->deposit_percent);
3227 } else {
3228 $form->form_conditions_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->cond_reglement_id, 'none', 0, '', 1, $object->deposit_percent);
3229 }
3230 print '</td>';
3231 print '</tr>';
3232
3233 // Payment mode
3234 print '<tr class="field_mode_reglement_id">';
3235 print '<td>';
3236 print '<table class="nobordernopadding centpercent"><tr><td>';
3237 print $langs->trans('PaymentMode');
3238 print '</td>';
3239 if ($action != 'editmode' && $usercancreate && $caneditfield) {
3240 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>';
3241 }
3242 print '</tr></table>';
3243 print '</td><td class="valuefieldcreate">';
3244 if ($action == 'editmode' && $usercancreate && $caneditfield) {
3245 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
3246 } else {
3247 $form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->mode_reglement_id, 'none');
3248 }
3249 print '</td></tr>';
3250
3251 // Delivery delay
3252 print '<tr class="fielddeliverydelay"><td>';
3253 print '<table class="nobordernopadding centpercent"><tr><td>';
3254 if (isModEnabled('order')) {
3255 print $form->textwithpicto($langs->trans('AvailabilityPeriod'), $langs->trans('AvailabilityPeriod') . ' (' . $langs->trans('AfterOrder') . ')');
3256 } else {
3257 print $langs->trans('AvailabilityPeriod');
3258 }
3259 print '</td>';
3260 if ($action != 'editavailability' && $usercancreate && $caneditfield) {
3261 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>';
3262 }
3263 print '</tr></table>';
3264 print '</td><td class="valuefield">';
3265 if ($action == 'editavailability' && $usercancreate && $caneditfield) {
3266 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'availability_id', $langs->trans("Delay"));
3267 } else {
3268 $form->form_availability($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->availability_id, 'none', 1);
3269 }
3270
3271 print '</td>';
3272 print '</tr>';
3273
3274 // Delivery date
3275 print '<tr><td>';
3276 print $form->editfieldkey($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, (int) ($usercancreate && $caneditfield), 'datepicker');
3277 print '</td><td class="valuefieldedit">';
3278 print $form->editfieldval($langs->trans('DeliveryDate'), 'date_livraison', $object->delivery_date, $object, $usercancreate && $caneditfield, 'datepicker');
3279 print '</td>';
3280 print '</tr>';
3281
3282 // Shipping Method
3283 if (isModEnabled("shipping")) {
3284 print '<tr><td>';
3285 print '<table class="nobordernopadding centpercent"><tr><td>';
3286 print $langs->trans('SendingMethod');
3287 print '</td>';
3288 if ($action != 'editshippingmethod' && $usercancreate && $caneditfield) {
3289 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>';
3290 }
3291 print '</tr></table>';
3292 print '</td><td class="valuefield">';
3293 if ($action == 'editshippingmethod' && $usercancreate && $caneditfield) {
3294 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'shipping_method_id', 1);
3295 } else {
3296 $form->formSelectShippingMethod($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->shipping_method_id, 'none');
3297 }
3298 print '</td>';
3299 print '</tr>';
3300 }
3301
3302 // Warehouse
3303 if (isModEnabled('stock') && getDolGlobalString('WAREHOUSE_ASK_WAREHOUSE_DURING_PROPAL')) {
3304 $langs->load('stocks');
3305 require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
3306 $formproduct = new FormProduct($db);
3307 print '<tr class="field_warehouse_id"><td>';
3308 $editenable = $usercancreate;
3309 print $form->editfieldkey("Warehouse", 'warehouse', '', $object, $editenable);
3310 print '</td><td class="valuefieldcreate">';
3311 if ($action == 'editwarehouse') {
3312 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'warehouse_id', 1);
3313 } else {
3314 $formproduct->formSelectWarehouses($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->warehouse_id, 'none');
3315 }
3316 print '</td>';
3317 print '</tr>';
3318 }
3319
3320 // Origin of demand
3321 print '<tr><td>';
3322 print '<table class="nobordernopadding centpercent"><tr><td>';
3323 print $langs->trans('Source');
3324 print '</td>';
3325 if ($action != 'editdemandreason' && $usercancreate) {
3326 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>';
3327 }
3328 print '</tr></table>';
3329 print '</td><td class="valuefield">';
3330 if ($action == 'editdemandreason' && $usercancreate) {
3331 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'demand_reason_id', 1);
3332 } else {
3333 $form->formInputReason($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->demand_reason_id, 'none');
3334 }
3335 print '</td>';
3336 print '</tr>';
3337
3338 if ($soc->outstanding_limit) {
3339 // Outstanding Bill
3340 print '<tr><td>';
3341 print $langs->trans('OutstandingBill');
3342 print '</td><td class="valuefield">';
3343 $arrayoutstandingbills = $soc->getOutstandingBills();
3344 print($arrayoutstandingbills['opened'] > $soc->outstanding_limit ? img_warning() : '');
3345 print price($arrayoutstandingbills['opened']) . ' / ';
3346 print price($soc->outstanding_limit, 0, $langs, 1, -1, -1, $conf->currency);
3347 print '</td>';
3348 print '</tr>';
3349 }
3350
3351 if (getDolGlobalString('BANK_ASK_PAYMENT_BANK_DURING_PROPOSAL') && isModEnabled("bank")) {
3352 // Bank Account
3353 print '<tr><td>';
3354 print '<table width="100%" class="nobordernopadding"><tr><td>';
3355 print $langs->trans('BankAccount');
3356 print '</td>';
3357 if ($action != 'editbankaccount' && $usercancreate) {
3358 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>';
3359 }
3360 print '</tr></table>';
3361 print '</td><td class="valuefield">';
3362 if ($action == 'editbankaccount') {
3363 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'fk_account', 1);
3364 } else {
3365 $form->formSelectAccount($_SERVER['PHP_SELF'] . '?id=' . $object->id, (string) $object->fk_account, 'none');
3366 }
3367 print '</td>';
3368 print '</tr>';
3369 }
3370
3371 if (!getDolGlobalString('PROPOSAL_HIDE_CALCULATED_WEIGHT_VOLUME')) {
3372 $tmparray = $object->getTotalWeightVolume();
3373 $totalWeight = isset($tmparray['weight']) ? $tmparray['weight'] : 0;
3374 $totalVolume = isset($tmparray['volume']) ? $tmparray['volume'] : 0;
3375 if ($totalWeight) {
3376 print '<tr><td>' . $langs->trans("CalculatedWeight") . '</td>';
3377 print '<td class="valuefield">';
3378 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'), 1);
3379 print '</td></tr>';
3380 }
3381 if ($totalVolume) {
3382 print '<tr><td>' . $langs->trans("CalculatedVolume") . '</td>';
3383 print '<td class="valuefield">';
3384 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'), 1);
3385 print '</td></tr>';
3386 }
3387 }
3388
3389 // Incoterms
3390 if (isModEnabled('incoterm')) {
3391 print '<tr><td>';
3392 print '<table width="100%" class="nobordernopadding"><tr><td>';
3393 print $langs->trans('IncotermLabel');
3394 print '<td><td class="right">';
3395 if ($action != 'editincoterm' && $usercancreate && $caneditfield) {
3396 print '<a class="editfielda" href="' . DOL_URL_ROOT . '/comm/propal/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
3397 } else {
3398 print '&nbsp;';
3399 }
3400 print '</td></tr></table>';
3401 print '</td>';
3402 print '<td class="valuefield">';
3403 if ($action == 'editincoterm' && $usercancreate && $caneditfield) {
3404 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
3405 } else {
3406 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
3407 }
3408 print '</td></tr>';
3409 }
3410
3411 // Categories
3412 if (isModEnabled('category')) {
3413 print '<tr><td>';
3414 print '<table class="nobordernopadding centpercent"><tr><td>';
3415 print $langs->trans("Categories");
3416 print '<td><td class="right">';
3417 if ($usercancreate) {
3418 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/comm/propal/card.php?id='.$object->id.'&action=edittags&token='.newToken().'">'.img_edit().'</a>';
3419 } else {
3420 print '&nbsp;';
3421 }
3422 print '</td></tr></table>';
3423 print '</td>';
3424 print '<td>';
3425 if ($action == 'edittags') {
3426 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'">';
3427 print '<input type="hidden" name="action" value="settags">';
3428 print '<input type="hidden" name="token" value="'.newToken().'">';
3429 print $form->selectCategories(Categorie::TYPE_PROPOSAL, 'categories', $object);
3430 print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
3431 print '</form>';
3432 } else {
3433 print $form->showCategories($object->id, Categorie::TYPE_PROPOSAL, 1);
3434 }
3435 print "</td></tr>";
3436 }
3437
3438 // Other attributes
3439 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
3440
3441 print '</table>';
3442
3443 print '</div>';
3444 print '<div class="fichehalfright">';
3445 print '<div class="underbanner clearboth"></div>';
3446
3447 print '<table class="border tableforfield centpercent">';
3448
3449 include DOL_DOCUMENT_ROOT . '/core/tpl/object_currency_amount.tpl.php';
3450
3451 print '<tr>';
3452 print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';
3453 print '<td class="nowrap amountcard right">' . price($object->total_ht, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3454 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3455 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ht, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3456 }
3457 print '</tr>';
3458
3459 print '<tr>';
3460 print '<td>' . $langs->trans('AmountVAT') . '</td>';
3461 print '<td class="nowrap amountcard right">' . price($object->total_tva, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3462 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3463 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_tva, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3464 }
3465 print '</tr>';
3466
3467 if ($mysoc->localtax1_assuj == "1" || $object->total_localtax1 != 0) {
3468 print '<tr>';
3469 print '<td>' . $langs->transcountry("AmountLT1", $mysoc->country_code) . '</td>';
3470 print '<td class="nowrap amountcard right">' . price($object->total_localtax1, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3471 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3472 $object->multicurrency_total_localtax1 = price2num($object->total_localtax1 * $object->multicurrency_tx, 'MT');
3473
3474 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax1, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3475 }
3476 print '</tr>';
3477 }
3478
3479 if ($mysoc->localtax2_assuj == "1" || $object->total_localtax2 != 0) {
3480 print '<tr>';
3481 print '<td>' . $langs->transcountry("AmountLT2", $mysoc->country_code) . '</td>';
3482 print '<td class="nowrap amountcard right">' . price($object->total_localtax2, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3483 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3484 $object->multicurrency_total_localtax2 = price2num($object->total_localtax2 * $object->multicurrency_tx, 'MT');
3485
3486 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_localtax2, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3487 }
3488 print '</tr>';
3489 }
3490
3491 print '<tr>';
3492 print '<td>' . $langs->trans('AmountTTC') . '</td>';
3493 print '<td class="nowrap amountcard right">' . price($object->total_ttc, 0, $langs, 1, -1, -1, $conf->currency) . '</td>';
3494 if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
3495 print '<td class="nowrap amountcard right">' . price($object->multicurrency_total_ttc, 0, $langs, 1, -1, -1, $object->multicurrency_code) . '</td>';
3496 }
3497 print '</tr>';
3498
3499 print '</table>';
3500
3501 // Margin Infos
3502 if (isModEnabled('margin')) {
3503 $formmargin->displayMarginInfos($object);
3504 }
3505
3506 print '</div>';
3507 print '</div>';
3508
3509 print '<div class="clearboth"></div><br>';
3510
3511 if (getDolGlobalString('MAIN_DISABLE_CONTACTS_TAB')) {
3512 $blocname = 'contacts';
3513 $title = $langs->trans('ContactsAddresses');
3514 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3515 }
3516
3517 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
3518 $blocname = 'notes';
3519 $title = $langs->trans('Notes');
3520 include DOL_DOCUMENT_ROOT . '/core/tpl/bloc_showhide.tpl.php';
3521 }
3522
3523 /*
3524 * Lines
3525 */
3526
3527 // Get object lines
3528 $result = $object->getLinesArray();
3529
3530 // Add products/services form
3531 //$forceall = 1;
3532 global $inputalsopricewithtax;
3533 $inputalsopricewithtax = 1;
3534
3535 print ' <form name="addproduct" id="addproduct" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="POST">
3536 <input type="hidden" name="token" value="' . newToken() . '">
3537 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline') . '">
3538 <input type="hidden" name="mode" value="">
3539 <input type="hidden" name="page_y" value="">
3540 <input type="hidden" name="backtopage" value="' . $backtopage . '">
3541 <input type="hidden" name="id" value="' . $object->id . '">
3542 ';
3543
3544 if (!empty($conf->use_javascript_ajax) && $object->status == Propal::STATUS_DRAFT) {
3545 if (isModEnabled('subtotals')) {
3546 include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php';
3547 } else {
3548 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
3549 }
3550 }
3551
3552 print '<div class="div-table-responsive-no-min">';
3553 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3554 print '<table id="tablelines" class="noborder noshadow centpercent">';
3555 }
3556
3557 if (!empty($object->lines)) {
3558 $object->printObjectLines($action, $mysoc, $object->thirdparty, $lineid, 1);
3559 }
3560
3561 // Form to add new line
3562 if ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines') {
3563 if ($action != 'editline') {
3564 $parameters = array();
3565 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3566 if ($reshook < 0) {
3567 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3568 }
3569 if (empty($reshook)) {
3570 $object->formAddObjectLine(1, $mysoc, $soc);
3571 }
3572 } else {
3573 $parameters = array();
3574 $reshook = $hookmanager->executeHooks('formEditObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3575 }
3576 }
3577
3578 if (!empty($object->lines) || ($object->status == Propal::STATUS_DRAFT && $usercancreate && $action != 'selectlines' && $action != 'editline')) {
3579 print '</table>';
3580 }
3581 print '</div>';
3582
3583 print "</form>\n";
3584 }
3585
3586 print dol_get_fiche_end();
3587
3588
3589 /*
3590 * Button Actions
3591 */
3592
3593 if ($action != 'presend') {
3594 $numlines = count($object->lines);
3595 print '<div class="tabsAction">';
3596
3597 $parameters = array();
3598 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3599 // modified by hook
3600 if (empty($reshook)) {
3601 if ($action != 'editline') {
3602 // Subtotal
3603 if ($object->status == Propal::STATUS_DRAFT && isModEnabled('subtotals')
3604 && (getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element)) || getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element)))) {
3605 $langs->load('subtotals');
3606
3607 $url_button = array();
3608
3609 $url_button[] = array(
3610 'lang' => 'subtotals',
3611 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_TITLE_'.strtoupper($object->element))),
3612 'perm' => (bool) $usercancreate,
3613 'label' => $langs->trans('AddTitleLine'),
3614 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_title_line&token=' . newToken()
3615 );
3616
3617 $url_button[] = array(
3618 'lang' => 'subtotals',
3619 'enabled' => (isModEnabled('propal') && $object->status == Propal::STATUS_DRAFT && getDolGlobalInt('SUBTOTAL_'.strtoupper($object->element))),
3620 'perm' => (bool) $usercancreate,
3621 'label' => $langs->trans('AddSubtotalLine'),
3622 'url' => '/comm/propal/card.php?id=' . $object->id . '&action=add_subtotal_line&token=' . newToken()
3623 );
3624
3625 print dolGetButtonAction('', $langs->trans('SubTotal'), 'default', $url_button, '', true);
3626 }
3627
3628 // Validate
3629 if (($object->status == Propal::STATUS_DRAFT && $object->total_ttc >= 0 && count($object->lines) > 0)
3630 || ($object->status == Propal::STATUS_DRAFT && getDolGlobalString('PROPAL_ENABLE_NEGATIVE') && count($object->lines) > 0)
3631 ) {
3632 if ($usercanvalidate) {
3633 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>';
3634 } else {
3635 print '<a class="butActionRefused classfortooltip" href="#">' . $langs->trans('Validate') . '</a>';
3636 }
3637 }
3638 // Create event
3639 /*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.
3640 {
3641 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>';
3642 }*/
3643 // Edit
3644 if ($object->status == Propal::STATUS_VALIDATED && $usercancreate) {
3645 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=modif&token=' . newToken() . '">' . $langs->trans('Modify') . '</a>';
3646 }
3647
3648 // ReOpen
3649 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)))) {
3650 if ($usercanreopen) {
3651 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=reopen&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#reopen') . '"';
3652 print '>' . $langs->trans('ReOpen') . '</a>';
3653 } else {
3654 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ReOpen") . '</a>';
3655 }
3656 }
3657
3658 // Send
3659 if (empty($user->socid)) {
3660 if ($object->status == Propal::STATUS_VALIDATED || $object->status == Propal::STATUS_SIGNED || getDolGlobalString('PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS')) {
3661 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '', $usercansend);
3662 }
3663 }
3664
3665 $arrayforbutaction = array();
3666
3667 // Create a sale order
3668 $arrayforbutaction[] = array(
3669 'lang' => 'orders',
3670 'enabled' => (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED),
3671 'perm' => $usercancreateorder,
3672 'label' => 'AddOrder',
3673 'url' => '/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3674 );
3675 /*if (isModEnabled('order') && $object->status == Propal::STATUS_SIGNED) {
3676 if ($usercancreateorder) {
3677 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>';
3678 }
3679 }*/
3680
3681 // Create a purchase order
3682 if (getDolGlobalString('WORKFLOW_CAN_CREATE_PURCHASE_ORDER_FROM_PROPOSAL')) {
3683 $arrayforbutaction[] = array(
3684 'lang' => 'orders',
3685 'enabled' => ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")),
3686 'perm' => $usercancreatepurchaseorder,
3687 'label' => 'AddPurchaseOrder',
3688 'url' => '/fourn/commande/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3689 );
3690 /*if ($object->status == Propal::STATUS_SIGNED && isModEnabled("supplier_order")) {
3691 if ($usercancreatepurchaseorder) {
3692 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>';
3693 }
3694 }*/
3695 }
3696
3697 // Create an intervention
3698 $arrayforbutaction[] = array(
3699 'lang' => 'interventions',
3700 'enabled' => (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED),
3701 'perm' => $usercancreateintervention,
3702 'label' => 'AddIntervention',
3703 'url' => '/fichinter/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3704 );
3705 /*if (isModEnabled("service") && isModEnabled('intervention') && $object->status == Propal::STATUS_SIGNED) {
3706 if ($usercancreateintervention) {
3707 $langs->load("interventions");
3708 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>';
3709 }
3710 }*/
3711
3712 // Create contract
3713 $arrayforbutaction[] = array(
3714 'lang' => 'contracts',
3715 'enabled' => (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED),
3716 'perm' => $usercancreatecontract,
3717 'label' => 'AddContract',
3718 'url' => '/contrat/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid)
3719 );
3720 /*if (isModEnabled('contract') && $object->status == Propal::STATUS_SIGNED) {
3721 $langs->load("contracts");
3722
3723 if ($usercancreatecontract) {
3724 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>';
3725 }
3726 }*/
3727
3728 // Create an invoice and classify billed
3729 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3730 $arrayforbutaction[] = [
3731 'lang' => 'invoice',
3732 'enabled' => isModEnabled('invoice'),
3733 'perm' => $usercancreateinvoice,
3734 'label' => 'CreateBill',
3735 'url' => '/compta/facture/card.php?action=create&origin=' . urlencode($object->element) . '&originid=' . ((int) $object->id) . '&socid=' . ((int) $object->socid),
3736 ];
3737 /*if (isModEnabled('invoice') && $usercancreateinvoice) {
3738 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>';
3739 }*/
3740 }
3741
3742 $actionButtonsParameters = [
3743 "areDropdownButtons" => !getDolGlobalInt("MAIN_REMOVE_DROPDOWN_CREATE_BUTTONS_ON_ORDER"),
3744 "backtopage" => $_SERVER["PHP_SELF"] . "?id=" . ((int) $id)
3745 ];
3746
3747 if ($numlines > 0) {
3748 print dolGetButtonAction('', $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 1, $actionButtonsParameters);
3749 } else {
3750 print dolGetButtonAction($langs->trans("ErrorObjectMustHaveLinesToBeValidated", $object->ref), $langs->trans("Create"), 'default', $arrayforbutaction, (string) $object->id, 0, $actionButtonsParameters);
3751 }
3752
3753 if ($object->status == Propal::STATUS_SIGNED && !getDolGlobalString('PROPOSAL_ARE_NOT_BILLABLE')) {
3754 $arrayofinvoiceforpropal = $object->getInvoiceArrayList();
3755 if ((is_array($arrayofinvoiceforpropal) && count($arrayofinvoiceforpropal) > 0) || !getDolGlobalString('WORKFLOW_PROPAL_NEED_INVOICE_TO_BE_CLASSIFIED_BILLED')) {
3756 if ($usercanclose) {
3757 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=classifybilled&token=' . newToken() . '&socid=' . $object->socid . '">' . $langs->trans("ClassifyBilled") . '</a>';
3758 } else {
3759 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '">' . $langs->trans("ClassifyBilled") . '</a>';
3760 }
3761 }
3762 }
3763
3764 if (!getDolGlobalString('PROPAL_SKIP_ACCEPT_REFUSE')) {
3765 // Close as accepted/refused
3766 if ($object->status == Propal::STATUS_VALIDATED) {
3767 if ($usercanclose) {
3768 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3769 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3770 } else {
3771 print '<a class="butActionRefused classfortooltip" href="#" title="' . $langs->trans("NotEnoughPermissions") . '"';
3772 print '>' . $langs->trans('SetAcceptedRefused') . '</a>';
3773 }
3774 }
3775 } else {
3776 // Set not signed (close)
3777 if ($object->status == Propal::STATUS_DRAFT && $usercanclose) {
3778 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&token=' . newToken() . '&action=closeas&token=' . newToken() . (!getDolGlobalString('MAIN_JUMP_TAG') ? '' : '#close') . '"';
3779 print '>' . $langs->trans('SetRefusedAndClose') . '</a>';
3780 }
3781 }
3782
3783 // Cancel propal
3784 if ($object->status > Propal::STATUS_DRAFT && $usercanclose) {
3785 print '<a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=cancel&token=' . newToken() . '">' . $langs->trans("CancelPropal") . '</a>';
3786 }
3787
3788 // Clone
3789 if ($usercancreate) {
3790 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>';
3791 }
3792
3793 // Delete
3794 print dolGetButtonAction($langs->trans("Delete"), '', 'delete', $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=delete&token=' . newToken(), 'delete', $usercandelete);
3795 }
3796 }
3797
3798 print '</div>';
3799 }
3800
3801 //Select mail models is same action as presend
3802 if (GETPOST('modelselected')) {
3803 $action = 'presend';
3804 }
3805
3806 if ($action != 'presend') {
3807 print '<div class="fichecenter"><div class="fichehalfleft">';
3808 print '<a name="builddoc"></a>'; // ancre
3809 /*
3810 * Generated documents
3811 */
3812 $objref = dol_sanitizeFileName($object->ref);
3813 $filedir = $conf->propal->multidir_output[$object->entity ?? $conf->entity] . "/" . dol_sanitizeFileName($object->ref);
3814 $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
3815 $genallowed = $usercanread;
3816 $delallowed = $usercancreate;
3817 $tooltipAfterComboOfModels = '';
3818 if (getDolGlobalString('MAIN_PDF_ADD_TERMSOFSALE_PROPAL')) {
3819 $tooltipAfterComboOfModels = $langs->trans("AccordingToYourSetupTheFileWillBeConcatenated", getDolGlobalString('MAIN_INFO_PROPAL_TERMSOFSALE'));
3820 }
3821
3822 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);
3823
3824 // Show links to link elements
3825 $tmparray = $form->showLinkToObjectBlock($object, array(), array('propal'), 1);
3826 $linktoelem = $tmparray['linktoelem'];
3827 $htmltoenteralink = $tmparray['htmltoenteralink'];
3828 print $htmltoenteralink;
3829
3830 $compatibleImportElementsList = false;
3831 if ($user->hasRight('propal', 'creer') && $object->status == Propal::STATUS_DRAFT) {
3832 $compatibleImportElementsList = array('commande', 'propal', 'facture', 'subscription'); // import from linked elements
3833 }
3834 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem, $compatibleImportElementsList);
3835
3836 // Show online signature link
3837 $useonlinesignature = getDolGlobalInt('PROPOSAL_ALLOW_ONLINESIGN');
3838
3839 if ($object->status != Propal::STATUS_DRAFT && $useonlinesignature) {
3840 print '<br><!-- Link to sign -->';
3841 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
3842 print showOnlineSignatureUrl('proposal', $object->ref, $object) . '<br>';
3843 }
3844
3845 print '</div><div class="fichehalfright">';
3846
3847 $MAXEVENT = 10;
3848
3849 $morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/comm/propal/messaging.php?id=' . $object->id);
3850 $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT . '/comm/propal/agenda.php?id=' . $object->id);
3851
3852 // List of actions on element
3853 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
3854 $formactions = new FormActions($db);
3855 $somethingshown = $formactions->showactions($object, 'propal', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for thirdparty
3856
3857 print '</div></div>';
3858 }
3859
3860 // Presend form
3861 $modelmail = 'propal_send';
3862 $defaulttopic = 'SendPropalRef';
3863 $diroutput = $conf->propal->multidir_output[$object->entity ?? $conf->entity];
3864 $trackid = 'pro' . $object->id;
3865
3866 include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
3867}
3868
3869// End of page
3870llxFooter();
3871$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 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:487
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.