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