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