dolibarr 25.0.0-alpha
invoice.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2018 Andreu Bisquerra <jove@bisquerra.com>
3 * Copyright (C) 2021 Nicolas ZABOURI <info@inovea-conseil.com>
4 * Copyright (C) 2022-2023 Christophe Battarel <christophe.battarel@altairis.fr>
5 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2026 Jose Martinez <jose.martinez@pichinov.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
29// if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER', '1'); // Not disabled cause need to load personalized language
30// if (! defined('NOREQUIREDB')) define('NOREQUIREDB', '1'); // Not disabled cause need to load personalized language
31// if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC', '1');
32// if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN', '1');
33
34if (!defined('NOTOKENRENEWAL')) {
35 define('NOTOKENRENEWAL', '1');
36}
37if (!defined('NOREQUIREMENU')) {
38 define('NOREQUIREMENU', '1');
39}
40if (!defined('NOREQUIREHTML')) {
41 define('NOREQUIREHTML', '1');
42}
43if (!defined('NOREQUIREAJAX')) {
44 define('NOREQUIREAJAX', '1');
45}
46
47// Load Dolibarr environment
48if (!defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
49 require '../main.inc.php';
50}
51require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
52require_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
53require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
54require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
55require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
65$hookmanager->initHooks(array('takeposinvoice'));
66
67$langs->loadLangs(array("companies", "commercial", "bills", "cashdesk", "stocks", "banks"));
68
69$action = GETPOST('action', 'aZ09');
70$idproduct = GETPOSTINT('idproduct');
71$place = (GETPOST('place', 'aZ09') ? GETPOST('place', 'aZ09') : 0); // $place is id of table for Bar or Restaurant
72$placeid = 0; // $placeid is ID of invoice
73$mobilepage = GETPOST('mobilepage', 'alpha');
74$batch = ''; // Default no batch if missing
75
76// Terminal is stored into $_SESSION["takeposterminal"];
77
78if (!$user->hasRight('takepos', 'run') && !defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
79 accessforbidden('No permission to use the TakePOS');
80}
81
82if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
83 // DIRECT LINK TO THIS PAGE FROM MOBILE AND NO TERMINAL SELECTED
84 if ($_SESSION["takeposterminal"] == "") {
85 if (getDolGlobalString('TAKEPOS_NUM_TERMINALS') == "1") {
86 $_SESSION["takeposterminal"] = 1;
87 } else {
88 header("Location: ".DOL_URL_ROOT."/takepos/index.php");
89 exit;
90 }
91 }
92}
93
94
95$takeposterminal = isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '';
96
97// When session has expired (selected terminal has been lost from session), redirect to the terminal selection.
98if (empty($takeposterminal)) {
99 if (getDolGlobalInt('TAKEPOS_NUM_TERMINALS') == 1) {
100 $_SESSION["takeposterminal"] = 1; // Use terminal 1 if there is only 1 terminal
101 $takeposterminal = 1;
102 } elseif (!empty($_COOKIE["takeposterminal"])) {
103 $_SESSION["takeposterminal"] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $_COOKIE["takeposterminal"]); // Restore takeposterminal from previous session
104 $takeposterminal = $_SESSION["takeposterminal"];
105 } else {
106 print <<<SCRIPT
107<script language="javascript">
108 $( document ).ready(function() {
109 ModalBox('ModalTerminal');
110 });
111</script>
112SCRIPT;
113 exit;
114 }
115}
116
117
124function fail($message)
125{
126 header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error', true, 500);
127 die($message);
128}
129
137function takeposDeleteLineWithChildren($invoice, $lineid)
138{
139 $lineid = (int) $lineid;
140 if ($lineid <= 0) {
141 return 0;
142 }
143
144 $childrenbyparent = array();
145 if (is_array($invoice->lines)) {
146 foreach ($invoice->lines as $line) {
147 $parentid = (int) $line->fk_parent_line;
148 if ($parentid > 0) {
149 $childrenbyparent[$parentid][] = (int) $line->id;
150 }
151 }
152 }
153
154 $linestodelete = array();
155 $stack = array($lineid);
156 while (!empty($stack)) {
157 $currentlineid = array_pop($stack);
158 if (in_array($currentlineid, $linestodelete, true)) {
159 continue;
160 }
161
162 $linestodelete[] = $currentlineid;
163 if (!empty($childrenbyparent[$currentlineid])) {
164 foreach ($childrenbyparent[$currentlineid] as $childlineid) {
165 $stack[] = $childlineid;
166 }
167 }
168 }
169
170 // Delete supplements before their parent line so no orphan line remains visible on receipts.
171 foreach (array_reverse($linestodelete) as $deletelineid) {
172 $result = $invoice->deleteLine($deletelineid);
173 if ($result < 0) {
174 return $result;
175 }
176 }
177
178 return 1;
179}
180
181
182
183$number = (float) GETPOST('number', 'alpha');
184$idline = GETPOSTINT('idline');
185$selectedline = GETPOSTINT('selectedline');
186$desc = GETPOST('desc', 'alphanohtml');
187$pay = GETPOST('pay', 'aZ09');
188$amountofpayment = GETPOSTFLOAT('amount');
189
190$invoiceid = GETPOSTINT('invoiceid');
191
192$paycode = $pay;
193if ($pay == 'cash') {
194 $paycode = 'LIQ'; // For backward compatibility
195}
196if ($pay == 'card') {
197 $paycode = 'CB'; // For backward compatibility
198}
199if ($pay == 'cheque') {
200 $paycode = 'CHQ'; // For backward compatibility
201}
202
203// Retrieve paiementid and paiementcode
204$paiementid = 0;
205if ($paycode) {
206 $sql = "SELECT id, code FROM ".MAIN_DB_PREFIX."c_paiement";
207 $sql .= " WHERE entity IN (".getEntity('c_paiement').")";
208 $sql .= " AND code = '".$db->escape($paycode)."'";
209 $resql = $db->query($sql);
210 if ($resql) {
211 $obj = $db->fetch_object($resql);
212 if ($obj) {
213 $paiementid = $obj->id;
214 }
215 }
216}
217
218$invoice = new Facture($db);
219if ($invoiceid > 0) {
220 $ret = $invoice->fetch($invoiceid);
221} else {
222 $ret = $invoice->fetch(0, '(PROV-POS'.$takeposterminal.'-'.$place.')');
223}
224if ($ret > 0) {
225 $placeid = $invoice->id;
226}
227
228$constforcompanyid = 'CASHDESK_ID_THIRDPARTY'.$takeposterminal;
229
230$soc = new Societe($db);
231if ($invoice->socid > 0) {
232 $soc->fetch($invoice->socid);
233} else {
234 $soc->fetch(getDolGlobalInt($constforcompanyid));
235}
236
237// Assign a default project, if relevant
238if (isModEnabled('project') && getDolGlobalInt("CASHDESK_ID_PROJECT".$takeposterminal)) {
239 $invoice->fk_project = getDolGlobalInt("CASHDESK_ID_PROJECT".$takeposterminal);
240}
241
242// Change the currency of invoice if it was modified
243if (isModEnabled('multicurrency') && !empty($_SESSION["takeposcustomercurrency"])) {
244 if ($invoice->multicurrency_code != $_SESSION["takeposcustomercurrency"]) {
245 $invoice->setMulticurrencyCode($_SESSION["takeposcustomercurrency"]);
246 }
247}
248
249$term = empty($_SESSION["takeposterminal"]) ? 1 : $_SESSION["takeposterminal"];
250
251
252/*
253 * Actions
254 */
255$error = 0;
256$parameters = array();
257$reshook = $hookmanager->executeHooks('doActions', $parameters, $invoice, $action); // Note that $action and $object may have been modified by some hooks
258if ($reshook < 0) {
259 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
260}
261
262
263$sectionwithinvoicelink = '';
264$CUSTOMER_DISPLAY_line1 = '';
265$CUSTOMER_DISPLAY_line2 = '';
266$headerorder = '';
267$footerorder = '';
268$printer = null;
269$idoflineadded = 0;
270
271// Enforce the "edit lines" permission on every action that modifies an existing line
272// (delete, quantity, price, discount). Adding a line, a free zone or a note is gated by
273// the "run" permission elsewhere and must stay available to a plain cashier (#38949).
274if (in_array($action, array('deleteline', 'updateqty', 'updateprice', 'updatereduction', 'update_reduction_global')) && !$user->hasRight('takepos', 'editlines')) {
275 dol_htmloutput_errors($langs->trans("NotEnoughPermissions", "TakePos"), array(), 1);
276 $action = '';
277}
278
279
280if (empty($reshook)) {
281 // Test that period is not close
282 $tmpcurrentday = dol_getdate(dol_now());
283
284 $sql = "SELECT MIN(ref) as firstref FROM ".MAIN_DB_PREFIX."pos_cash_fence";
285 $sql .= " WHERE entity = ".((int) $conf->entity);
286 $sql .= " AND posnumber = ".((int) $takeposterminal);
287 $sql .= " AND year_close = ".((int) $tmpcurrentday['year']);
288 $sql .= " AND (";
289 $sql .= " (month_close IS NULL AND day_close IS NULL)";
290 $sql .= " OR (month_close = ".((int) $tmpcurrentday['mon'])." AND day_close IS NULL)";
291 $sql .= " OR (month_close = ".((int) $tmpcurrentday['mon'])." AND day_close = ".((int) $tmpcurrentday['mday']).")";
292 $sql .= ")";
293 $sql .= " AND status = 1";
294
295 $refcashcontrol = 0;
296 $resql = $db->query($sql);
297 if ($resql) {
298 $obj = $db->fetch_object($resql);
299 if ($obj) {
300 $refcashcontrol = $obj->firstref;
301 }
302 }
303
304 if ($refcashcontrol) {
305 $error++;
306 $langs->load('errors');
307 dol_htmloutput_errors($langs->trans("ACashControlHasBeenclosedForCurrentDay", $refcashcontrol), [], 1);
308 $action = '';
309 }
310
311 // Action to record a payment on a TakePOS invoice
312 if ($action == 'valid' && $user->hasRight('facture', 'creer')) {
313 $bankaccount = 0;
314 $error = 0;
315
316 if (getDolGlobalString('TAKEPOS_CAN_FORCE_BANK_ACCOUNT_DURING_PAYMENT')) {
317 $bankaccount = GETPOSTINT('accountid');
318 } else {
319 if ($pay == 'LIQ') {
320 $bankaccount = getDolGlobalInt('CASHDESK_ID_BANKACCOUNT_CASH'.$_SESSION["takeposterminal"]); // For backward compatibility
321 } elseif ($pay == "CHQ") {
322 $bankaccount = getDolGlobalInt('CASHDESK_ID_BANKACCOUNT_CHEQUE'.$_SESSION["takeposterminal"]); // For backward compatibility
323 } else {
324 $accountname = "CASHDESK_ID_BANKACCOUNT_".$pay.$_SESSION["takeposterminal"];
325 $bankaccount = getDolGlobalInt($accountname);
326 }
327 }
328
329 if ($bankaccount <= 0 && $pay != "delayed" && isModEnabled("bank")) {
330 $errormsg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("BankAccount"));
331 $error++;
332 }
333
334 $now = dol_now();
335 $res = 0;
336
337 $invoice = new Facture($db);
338 $invoice->fetch($placeid);
339
340 $invoice->oldcopy = dol_clone($invoice, 2);
341
342 $db->begin();
343
344 if ($invoice->total_ttc < 0) {
345 $invoice->type = $invoice::TYPE_CREDIT_NOTE;
346
347 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facture";
348 $sql .= " WHERE entity IN (".getEntity('invoice').")";
349 $sql .= " AND fk_soc = ".((int) $invoice->socid);
350 $sql .= " AND type <> ".Facture::TYPE_CREDIT_NOTE;
351 $sql .= " AND fk_statut >= ".$invoice::STATUS_VALIDATED;
352 $sql .= " ORDER BY rowid DESC";
353
354 $fk_source = 0;
355 $resql = $db->query($sql);
356 if ($resql) {
357 $obj = $db->fetch_object($resql);
358 $fk_source = $obj->rowid;
359 if ((int) $fk_source == 0) {
360 fail($langs->transnoentitiesnoconv("NoPreviousBillForCustomer"));
361 }
362 } else {
363 fail($langs->transnoentitiesnoconv("NoPreviousBillForCustomer"));
364 }
365 $invoice->fk_facture_source = $fk_source;
366 $invoice->update($user);
367 }
368
369 $constantforkey = 'CASHDESK_NO_DECREASE_STOCK'.(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
370 $allowstockchange = (getDolGlobalString($constantforkey) != "1");
371
372 if ($error) {
373 dol_htmloutput_errors($errormsg, [], 1);
374 } elseif ($invoice->status != Facture::STATUS_DRAFT) {
375 //If invoice is validated but it is not fully paid is not error and make the payment
376 $remaintopay = $invoice->getRemainToPay();
377 if (($remaintopay > 0 && $invoice->type != Facture::TYPE_CREDIT_NOTE) || ($remaintopay < 0 && $invoice->type == Facture::TYPE_CREDIT_NOTE)) {
378 $res = 1;
379 } else {
380 dol_syslog("Sale already validated");
381 dol_htmloutput_errors($langs->trans("InvoiceIsAlreadyValidated", "TakePos"), [], 1);
382 }
383 } elseif (count($invoice->lines) == 0) {
384 $error++;
385 dol_syslog('Sale without lines');
386 dol_htmloutput_errors($langs->trans("NoLinesToBill", "TakePos"), [], 1);
387 } elseif (isModEnabled('stock') && !isModEnabled('productbatch') && $allowstockchange) {
388 // Validation of invoice with change into stock when product/lot module is NOT enabled and stock change NOT disabled.
389 // The case for isModEnabled('productbatch') is processed few lines later.
390 $savconst = getDolGlobalString('STOCK_CALCULATE_ON_BILL');
391
392 $conf->global->STOCK_CALCULATE_ON_BILL = 1; // To force the change of stock during invoice validation
393
394 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
395 dol_syslog("Validate invoice with stock change. Warehouse defined into constant ".$constantforkey." = ".getDolGlobalString($constantforkey));
396
397 // Validate invoice with stock change into warehouse getDolGlobalInt($constantforkey)
398 // Label of stock movement will be the same as when we validate invoice "Invoice XXXX validated"
399 $batch_rule = 0; // Module productbatch is disabled here, so no need for a batch_rule.
400 $res = $invoice->validate($user, '', getDolGlobalInt($constantforkey), 0, $batch_rule);
401
402 // Restore setup
403 $conf->global->STOCK_CALCULATE_ON_BILL = $savconst;
404 } else {
405 // Validation of invoice with no change into stock (because param $idwarehouse is not fill)
406 dol_syslog("Call validate on invoice ".$invoice->ref, LOG_DEBUG);
407 $res = $invoice->validate($user);
408 if ($res < 0) {
409 $error++;
410 $langs->load("admin");
411 dol_htmloutput_errors($invoice->error == 'NotConfigured' ? $langs->trans("NotConfigured").' (TakePos numbering module)' : $invoice->error, $invoice->errors, 1);
412 }
413 }
414
415 // Add the payment
416 if (!$error && $res >= 0) {
417 $remaintopay = $invoice->getRemainToPay();
418 // Credit notes have negative remaintopay; regular invoices have positive
419 if (($remaintopay > 0 && $invoice->type != Facture::TYPE_CREDIT_NOTE) || ($remaintopay < 0 && $invoice->type == Facture::TYPE_CREDIT_NOTE)) {
420 $payment = new Paiement($db);
421
422 $payment->datepaye = $now;
423 $payment->fk_account = $bankaccount;
424 if ($pay == 'LIQ') {
425 $payment->pos_change = GETPOSTFLOAT('excess');
426 }
427
428 $payment->amounts[$invoice->id] = $amountofpayment;
429 // If user has not used change control, add total invoice payment
430 // Or if user has used change control and the amount of payment is higher than remain to pay, add the remain to pay
431 if ($amountofpayment <= 0 || $amountofpayment > $remaintopay) {
432 $payment->amounts[$invoice->id] = $remaintopay;
433 }
434 // We do not set $payments->multicurrency_amounts because we want payment to be in main currency.
435
436 $payment->paiementid = $paiementid;
437 $payment->paiementcode = $paycode;
438 $payment->num_payment = '';
439
440 if ($pay != "delayed") {
441 $res = $payment->create($user); // This record payment and regenerate the PDF
442 if ($res < 0) {
443 $error++;
444 //setEventMessages($payment->error, $payment->errors, 'error');
445 dol_htmloutput_mesg($payment->error, $payment->errors, 'error', 1);
446 } else {
447 //setEventMessages(null, $payment->warnings, 'warnings');
448 if (!empty($payment->warnings)) {
449 dol_htmloutput_mesg('', $payment->warnings, 'warning', 1);
450 }
451
452 $res = $payment->addPaymentToBank($user, 'payment', '(CustomerInvoicePayment)', $bankaccount, '', '');
453 if ($res < 0) {
454 $error++;
455 dol_htmloutput_mesg($langs->trans('ErrorNoPaymentDefined').' '.$payment->error, $payment->errors, 'error', 1);
456 }
457 }
458 $remaintopay = $invoice->getRemainToPay(); // Recalculate remain to pay after the payment is recorded
459 } elseif (getDolGlobalInt("TAKEPOS_DELAYED_TERMS")) {
460 $invoice->setPaymentTerms(getDolGlobalInt("TAKEPOS_DELAYED_TERMS"));
461 }
462 }
463
464 if ($remaintopay == 0) {
465 dol_syslog("Invoice is paid, so we set it to status Paid");
466 $result = $invoice->setPaid($user);
467 if ($result > 0) {
468 $invoice->paye = 1;
469 $invoice->status = $invoice::STATUS_CLOSED;
470 $invoice->close_code = '';
471 }
472 // set payment method
473 $invoice->setPaymentMethods($paiementid);
474 } else {
475 dol_syslog("Invoice is not paid, remain to pay = ".$remaintopay);
476 }
477 } else {
478 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
479 }
480
481
482 $warehouseid = 0;
483 // Update stock for batch products
484 if (!$error && $res >= 0) {
485 if (isModEnabled('stock') && isModEnabled('productbatch') && $allowstockchange) {
486 // Update stocks
487 dol_syslog("Now we record the stock movement for each qualified line");
488
489 // The case !isModEnabled('productbatch') was processed few lines before.
490 require_once DOL_DOCUMENT_ROOT . "/product/stock/class/mouvementstock.class.php";
491 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.$_SESSION["takeposterminal"];
492 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
493 // Label of stock movement will be "TakePOS - Invoice XXXX"
494 $labeltakeposmovement = 'TakePOS - '.$langs->trans("Invoice").' '.$invoice->ref;
495
496 foreach ($invoice->lines as $line) {
497 // Use the warehouse id defined on invoice line else in the setup
498 $warehouseid = ($line->fk_warehouse ? $line->fk_warehouse : getDolGlobalInt($constantforkey));
499
500 // var_dump('fk_product='.$line->fk_product.' batch='.$line->batch.' warehouse='.$line->fk_warehouse.' qty='.$line->qty);
501 if ($line->batch != '' && $warehouseid > 0) {
502 $prod_batch = new Productbatch($db);
503 $prod_batch->find(0, '', '', $line->batch, $warehouseid, (int) $line->fk_product);
504
505 $mouvP = new MouvementStock($db);
506 $mouvP->setOrigin($invoice->element, $invoice->id);
507
508 if ($invoice->type == Facture::TYPE_CREDIT_NOTE) {
509 $res = $mouvP->reception($user, $line->fk_product, $warehouseid, $line->qty, $line->price, $labeltakeposmovement, '', '', $line->batch, '', 0, $inventorycode);
510 } else {
511 $res = $mouvP->livraison($user, $line->fk_product, $warehouseid, $line->qty, $line->price, $labeltakeposmovement, '', '', '', $prod_batch->batch, $prod_batch->id, $inventorycode);
512 }
513
514 if ($res < 0) {
515 dol_htmloutput_errors($mouvP->error, $mouvP->errors, 1);
516 $error++;
517 }
518 } else {
519 $mouvP = new MouvementStock($db);
520 $mouvP->setOrigin($invoice->element, $invoice->id);
521
522 if ($invoice->type == Facture::TYPE_CREDIT_NOTE) {
523 $res = $mouvP->reception($user, $line->fk_product, $warehouseid, $line->qty, $line->price, $labeltakeposmovement, '', '', '', '', 0, $inventorycode);
524 } else {
525 $res = $mouvP->livraison($user, $line->fk_product, $warehouseid, $line->qty, $line->price, $labeltakeposmovement, '', '', '', '', 0, $inventorycode);
526 }
527
528 if ($res < 0) {
529 dol_htmloutput_errors($mouvP->error, $mouvP->errors, 1);
530 $error++;
531 }
532 }
533 }
534 }
535 }
536
537 if (!$error && $res >= 0) {
538 $db->commit();
539 } else {
540 $invoice->ref = $invoice->oldcopy->ref;
541 $invoice->paye = $invoice->oldcopy->paye;
542 $invoice->status = $invoice->oldcopy->status;
543 $invoice->statut = $invoice->oldcopy->statut;
544
545 $db->rollback();
546 }
547 }
548
549 $creditnote = null;
550 if ($action == 'creditnote' && $user->hasRight('facture', 'creer')) {
551 $db->begin();
552
553 $creditnote = new Facture($db);
554 $creditnote->socid = $invoice->socid;
555 $creditnote->date = dol_now();
556 $creditnote->module_source = 'takepos';
557 $creditnote->pos_source = isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '' ;
558 $creditnote->type = Facture::TYPE_CREDIT_NOTE;
559 $creditnote->fk_facture_source = $placeid;
560
561 $creditnote->create($user);
562
563 $fk_parent_line = 0; // Initialise
564
565 foreach ($invoice->lines as $line) {
566 // Reset fk_parent_line for no child products and special product
567 if (($line->product_type != 9 && empty($line->fk_parent_line)) || $line->product_type == 9) {
568 $fk_parent_line = 0;
569 }
570
571 if (getDolGlobalInt('INVOICE_USE_SITUATION')) {
572 if (!empty($invoice->situation_counter)) {
573 $source_fk_prev_id = $line->fk_prev_id; // temporary storing situation invoice fk_prev_id
574 $line->fk_prev_id = $line->id; // The new line of the new credit note we are creating must be linked to the situation invoice line it is created from
575 if (!empty($invoice->tab_previous_situation_invoice)) {
576 // search the last standard invoice in cycle and the possible credit note between this last and invoice
577 // TODO Move this out of loop of $invoice->lines
578 $tab_jumped_credit_notes = array();
579 $lineIndex = count($invoice->tab_previous_situation_invoice) - 1;
580 $searchPreviousInvoice = true;
581 while ($searchPreviousInvoice) {
582 if ($invoice->tab_previous_situation_invoice[$lineIndex]->situation_cycle_ref || $lineIndex < 1) {
583 $searchPreviousInvoice = false; // find, exit;
584 break;
585 } else {
586 if ($invoice->tab_previous_situation_invoice[$lineIndex]->type == Facture::TYPE_CREDIT_NOTE) {
587 $tab_jumped_credit_notes[$lineIndex] = $invoice->tab_previous_situation_invoice[$lineIndex]->id;
588 }
589 $lineIndex--; // go to previous invoice in cycle
590 }
591 }
592
593 $maxPrevSituationPercent = 0;
594 foreach ($invoice->tab_previous_situation_invoice[$lineIndex]->lines as $prevLine) {
595 if ($prevLine->id == $source_fk_prev_id) {
596 $maxPrevSituationPercent = max($maxPrevSituationPercent, $prevLine->situation_percent);
597
598 //$line->subprice = $line->subprice - $prevLine->subprice;
599 $line->total_ht -= $prevLine->total_ht;
600 $line->total_tva -= $prevLine->total_tva;
601 $line->total_ttc -= $prevLine->total_ttc;
602 $line->total_localtax1 -= $prevLine->total_localtax1;
603 $line->total_localtax2 -= $prevLine->total_localtax2;
604
605 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
606 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
607 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
608 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
609 }
610 }
611
612 // prorata
613 $line->situation_percent = $maxPrevSituationPercent - $line->situation_percent;
614
615 //print 'New line based on invoice id '.$invoice->tab_previous_situation_invoice[$lineIndex]->id.' fk_prev_id='.$source_fk_prev_id.' will be fk_prev_id='.$line->fk_prev_id.' '.$line->total_ht.' '.$line->situation_percent.'<br>';
616
617 // If there is some credit note between last situation invoice and invoice used for credit note generation (note: credit notes are stored as delta)
618 $maxPrevSituationPercent = 0;
619 foreach ($tab_jumped_credit_notes as $index => $creditnoteid) {
620 foreach ($invoice->tab_previous_situation_invoice[$index]->lines as $prevLine) {
621 if ($prevLine->fk_prev_id == $source_fk_prev_id) {
622 $maxPrevSituationPercent = $prevLine->situation_percent;
623
624 $line->total_ht -= $prevLine->total_ht;
625 $line->total_tva -= $prevLine->total_tva;
626 $line->total_ttc -= $prevLine->total_ttc;
627 $line->total_localtax1 -= $prevLine->total_localtax1;
628 $line->total_localtax2 -= $prevLine->total_localtax2;
629
630 $line->multicurrency_subprice -= $prevLine->multicurrency_subprice;
631 $line->multicurrency_total_ht -= $prevLine->multicurrency_total_ht;
632 $line->multicurrency_total_tva -= $prevLine->multicurrency_total_tva;
633 $line->multicurrency_total_ttc -= $prevLine->multicurrency_total_ttc;
634 }
635 }
636 }
637
638 // prorata
639 $line->situation_percent += $maxPrevSituationPercent;
640
641 //print 'New line based on invoice id '.$invoice->tab_previous_situation_invoice[$lineIndex]->id.' fk_prev_id='.$source_fk_prev_id.' will be fk_prev_id='.$line->fk_prev_id.' '.$line->total_ht.' '.$line->situation_percent.'<br>';
642 }
643 }
644 }
645
646 // We update field for credit notes
647 $line->fk_facture = $creditnote->id;
648 $line->fk_parent_line = $fk_parent_line;
649
650 $line->subprice = -$line->subprice; // invert price for object
651 // $line->pa_ht = $line->pa_ht; // we chose to have the buy/cost price always positive, so no inversion of the sign here
652 $line->total_ht = -$line->total_ht;
653 $line->total_tva = -$line->total_tva;
654 $line->total_ttc = -$line->total_ttc;
655 $line->total_localtax1 = -$line->total_localtax1;
656 $line->total_localtax2 = -$line->total_localtax2;
657
658 $line->multicurrency_subprice = -$line->multicurrency_subprice;
659 $line->multicurrency_total_ht = -$line->multicurrency_total_ht;
660 $line->multicurrency_total_tva = -$line->multicurrency_total_tva;
661 $line->multicurrency_total_ttc = -$line->multicurrency_total_ttc;
662
663 $result = $line->insert(0, 1); // When creating credit note with same lines than source, we must ignore error if discount already linked
664
665 $creditnote->lines[] = $line; // insert new line in current object
666
667 // Defined the new fk_parent_line
668 if ($result > 0 && $line->product_type == 9) {
669 $fk_parent_line = $result;
670 }
671 }
672 $creditnote->update_price(1);
673
674 // The credit note is created here.
675 // If TAKEPOS_VALIDATE_CREDIT_NOTE_ON_CREATION is set, we validate it immediately (old behavior).
676 // Otherwise, it is left as draft so it can be edited from TakePOS (e.g. to modify quantities or batches).
677 // Stock movements will be done when the credit note is validated later (via Payment / Valid action).
678 $res = 1;
679 if (getDolGlobalString('TAKEPOS_VALIDATE_CREDIT_NOTE_ON_CREATION')) {
680 $constantforkey = 'CASHDESK_NO_DECREASE_STOCK'.(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
681 $allowstockchange = getDolGlobalString($constantforkey) != "1";
682
683 if (isModEnabled('stock') && !isModEnabled('productbatch') && $allowstockchange) {
684 $savconst = getDolGlobalString('STOCK_CALCULATE_ON_BILL');
685 $conf->global->STOCK_CALCULATE_ON_BILL = 1; // Force stock update on invoice validation
686
687 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
688 $warehouseid = getDolGlobalInt($constantforkey);
689
690 dol_syslog("Validate invoice with stock change into warehouse defined into constant ".$constantforkey." = ".getDolGlobalString($constantforkey)." or warehouseid= ".$warehouseid." if defined.");
691
692 $batch_rule = 0; // Module productbatch is disabled here, so no need for a batch_rule.
693 $res = $creditnote->validate($user, '', $warehouseid, 0, $batch_rule);
694 if ($res < 0) {
695 $error++;
696 dol_htmloutput_errors($creditnote->error, $creditnote->errors, 1);
697 }
698
699 // Restore setup
700 $conf->global->STOCK_CALCULATE_ON_BILL = $savconst;
701 } else {
702 $res = $creditnote->validate($user);
703 }
704
705 // Update stock for batch products
706 if (!$error && $res >= 0) {
707 if (isModEnabled('stock') && isModEnabled('productbatch') && $allowstockchange) {
708 dol_syslog("Now we record the stock movement for each qualified line");
709
710 require_once DOL_DOCUMENT_ROOT . "/product/stock/class/mouvementstock.class.php";
711 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
712 $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
713 $labeltakeposmovement = 'TakePOS - '.$langs->trans("CreditNote").' '.$creditnote->ref;
714
715 foreach ($creditnote->lines as $line) {
716 // Use the warehouse id defined on invoice line else in the setup
717 $warehouseid = ($line->fk_warehouse ? $line->fk_warehouse : getDolGlobalInt($constantforkey));
718
719 $mouvP = new MouvementStock($db);
720 $mouvP->setOrigin($creditnote->element, $creditnote->id);
721
722 $batch_to_use = ($line->batch != '' && $warehouseid > 0) ? $line->batch : '';
723
724 $res = $mouvP->reception($user, $line->fk_product, $warehouseid, $line->qty, $line->price, $labeltakeposmovement, '', '', $batch_to_use, '', 0, $inventorycode);
725 if ($res < 0) {
726 dol_htmloutput_errors($mouvP->error, $mouvP->errors, 1);
727 $error++;
728 }
729 }
730 }
731 }
732 }
733
734 if (!$error) {
735 $db->commit();
736 } else {
737 $creditnote->id = $placeid; // Creation has failed, we reset to ID of source invoice so we go back to this one in action=history
738 $db->rollback();
739 }
740 }
741
742 if (($action == 'history' || $action == 'creditnote') && $user->hasRight('takepos', 'run')) {
743 if ($action == 'creditnote' && $creditnote !== null && $creditnote->id > 0) { // Test on permission already done
744 $placeid = $creditnote->id;
745 } else {
746 $placeid = GETPOSTINT('placeid');
747 }
748
749 $invoice = new Facture($db);
750 $invoice->fetch($placeid);
751 }
752
753 // If we add a line and no invoice yet, we create the invoice
754 if (($action == "addline" || $action == "freezone") && $placeid == 0 && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
755 $invoice->socid = getDolGlobalInt($constforcompanyid);
756
757 $dolnowtzuserrel = dol_now('tzuserrel'); // If user is 02 january 22:00, we want to store '02 january'
758 $monthuser = dol_print_date($dolnowtzuserrel, '%m', 'gmt');
759 $dayuser = dol_print_date($dolnowtzuserrel, '%d', 'gmt');
760 $yearuser = dol_print_date($dolnowtzuserrel, '%Y', 'gmt');
761 $dateinvoice = dol_mktime(0, 0, 0, (int) $monthuser, (int) $dayuser, (int) $yearuser, 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server
762
763 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
764 $invoice->date = $dateinvoice; // Invoice::create() needs a date with no hours
765
766 /*
767 print "monthuser=".$monthuser." dayuser=".$dayuser." yearuser=".$yearuser.'<br>';
768 print '---<br>';
769 print 'TZSERVER: '.dol_print_date(dol_now('tzserver'), 'dayhour', 'gmt').'<br>';
770 print 'TZUSER: '.dol_print_date(dol_now('tzuserrel'), 'dayhour', 'gmt').'<br>';
771 print 'GMT: '.dol_print_date(dol_now('gmt'), 'dayhour', 'gmt').'<br>'; // Hour in greenwich
772 print '---<br>';
773 print dol_print_date($invoice->date, 'dayhour', 'gmt').'<br>';
774 print "IN SQL, we will got: ".dol_print_date($db->idate($invoice->date), 'dayhour', 'gmt').'<br>';
775 print dol_print_date($db->idate($invoice->date, 'gmt'), 'dayhour', 'gmt').'<br>';
776 */
777
778 $invoice->module_source = 'takepos';
779 $invoice->pos_source = isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '' ;
780 $invoice->entity = !empty($_SESSION["takeposinvoiceentity"]) ? $_SESSION["takeposinvoiceentity"] : $conf->entity;
781
782 if ($invoice->socid <= 0) {
783 $langs->load('errors');
784 dol_htmloutput_errors($langs->trans("ErrorModuleSetupNotComplete", "TakePos"), [], 1);
785 } else {
786 $db->begin();
787
788 // Create invoice
789 $placeid = $invoice->create($user);
790
791 if ($placeid < 0) {
792 $error++;
793 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
794 } else {
795 $sql = "UPDATE ".MAIN_DB_PREFIX."facture";
796 $sql .= " SET ref='(PROV-POS".$_SESSION["takeposterminal"]."-".$place.")'";
797 $sql .= " WHERE rowid = ".((int) $placeid);
798 $resql = $db->query($sql);
799 if (!$resql) {
800 $error++;
801 }
802 }
803
804 if (!$error) {
805 $db->commit();
806 } else {
807 $db->rollback();
808 }
809 }
810 }
811
812 $tva_npr = 0;
813 // If we add a line by clicking on a product (invoice exists here because it was created juste before if it didn't exists)
814 if ($action == "addline" && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
815 $prod = new Product($db);
816 $prod->fetch($idproduct);
817
818 $customer = new Societe($db);
819 $customer->fetch($invoice->socid);
820
821 $datapriceofproduct = $prod->getSellPrice($mysoc, $customer, 0);
822
823 $qty = GETPOSTISSET('qty') ? GETPOSTFLOAT('qty', '', GETPOSTINT('qty_std') ? 1 : 2) : 1;
824 $price = $datapriceofproduct['pu_ht'];
825 $price_ttc = $datapriceofproduct['pu_ttc'];
826 //$price_min = $datapriceofproduct['price_min'];
827 $price_base_type = empty($datapriceofproduct['price_base_type']) ? 'HT' : $datapriceofproduct['price_base_type'];
828 $tva_tx = $datapriceofproduct['tva_tx'];
829 $tva_npr = (int) $datapriceofproduct['tva_npr'];
830
831 // Local Taxes
832 $localtax1_tx = get_localtax($tva_tx, 1, $customer, $mysoc, $tva_npr);
833 $localtax2_tx = get_localtax($tva_tx, 2, $customer, $mysoc, $tva_npr);
834
835
836 if (isModEnabled('productbatch') && isModEnabled('stock')) {
837 $batch = GETPOST('batch', 'alpha');
838
839 if (!empty($batch)) { // We have just clicked on a batch number, we will execute action=setbatch later...
840 $action = "setbatch";
841 } elseif ($prod->status_batch > 0) {
842 // If product need a lot/serial, we show the list of lot/serial available for the product...
843
844 // Set nb of suggested with nb of batch into the warehouse of the terminal
845 $nbofsuggested = 0;
846 $prod->load_stock('warehouseopen');
847
848 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.$_SESSION["takeposterminal"];
849 $warehouseid = getDolGlobalInt($constantforkey);
850
851 //var_dump($prod->stock_warehouse);
852 foreach ($prod->stock_warehouse as $tmpwarehouseid => $tmpval) {
853 if (getDolGlobalInt($constantforkey) && $tmpwarehouseid != getDolGlobalInt($constantforkey)) {
854 // Product to select is not on the warehouse configured for terminal, so we ignore this warehouse
855 continue;
856 }
857 if (!empty($prod->stock_warehouse[$tmpwarehouseid]) && is_array($prod->stock_warehouse[$tmpwarehouseid]->detail_batch)) {
858 if (is_object($prod->stock_warehouse[$tmpwarehouseid]) && count($prod->stock_warehouse[$tmpwarehouseid]->detail_batch)) {
859 foreach ($prod->stock_warehouse[$tmpwarehouseid]->detail_batch as $dbatch) {
860 $nbofsuggested++;
861 }
862 }
863 }
864 }
865 //var_dump($prod->stock_warehouse);
866
867 echo "<script>\n";
868 echo "function addbatch(batch, warehouseid) {\n";
869 echo "console.log('We add batch '+batch+' from warehouse id '+warehouseid);\n";
870 echo '$("#poslines").load("'.DOL_URL_ROOT.'/takepos/invoice.php?action=addline&batch="+encodeURI(batch)+"&warehouseid="+warehouseid+"&place='.$place.'&idproduct='.$idproduct.'&token='.newToken().'", function() {});'."\n";
871 echo "}\n";
872 echo "</script>\n";
873
874 $suggestednb = 1;
875 echo "<center>".$langs->trans("SearchIntoBatch").": <b> $nbofsuggested </b></center><br><table>";
876 foreach ($prod->stock_warehouse as $tmpwarehouseid => $tmpval) {
877 if (getDolGlobalInt($constantforkey) && $tmpwarehouseid != getDolGlobalInt($constantforkey)) {
878 // Not on the forced warehouse, so we ignore this warehouse
879 continue;
880 }
881 if (!empty($prod->stock_warehouse[$tmpwarehouseid]) && is_array($prod->stock_warehouse[$tmpwarehouseid]->detail_batch)) {
882 foreach ($prod->stock_warehouse[$tmpwarehouseid]->detail_batch as $dbatch) { // $dbatch is instance of Productbatch
883 $batchStock = + $dbatch->qty; // To get a numeric
884 $quantityToBeDelivered = 1;
885 $deliverableQty = min($quantityToBeDelivered, $batchStock);
886 print '<tr>';
887 print '<!-- subj='.$suggestednb.'/'.$nbofsuggested.' -->';
888 print '<!-- Show details of lot/serial in warehouseid='.$tmpwarehouseid.' -->';
889 print '<td class="left">';
890 $detail = '';
891 $detail .= '<span class="opacitymedium">'.$langs->trans("LotSerial").':</span> '.$dbatch->batch;
892 //if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
893 //$detail .= ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
894 //}
895 //if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
896 //$detail .= ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
897 //}
898 $detail .= '</td><td>';
899 $detail .= '<span class="opacitymedium">'.$langs->trans("Qty").':</span> '.$dbatch->qty;
900 $detail .= '</td><td>';
901 $detail .= ' <button class="marginleftonly" onclick="addbatch(\''.dol_escape_js($dbatch->batch).'\', '.$tmpwarehouseid.')">'.$langs->trans("Select")."</button>";
902 $detail .= '<br>';
903 print $detail;
904
905 $quantityToBeDelivered -= $deliverableQty;
906 if ($quantityToBeDelivered < 0) {
907 $quantityToBeDelivered = 0;
908 }
909 $suggestednb++;
910 print '</td></tr>';
911 }
912 }
913 }
914 print "</table>";
915
916 print '</body></html>';
917 exit;
918 }
919 }
920
921
922 if (getDolGlobalString('TAKEPOS_SUPPLEMENTS')) {
923 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
924 $cat = new Categorie($db);
925 $categories = $cat->containing($idproduct, 'product');
926 $found = (array_search(getDolGlobalInt('TAKEPOS_SUPPLEMENTS_CATEGORY'), array_column($categories, 'id')));
927 if ($found !== false) { // If this product is a supplement
928 $sql = "SELECT fk_parent_line FROM ".MAIN_DB_PREFIX."facturedet where rowid = ".((int) $selectedline);
929 $resql = $db->query($sql);
930 $row = $db->fetch_array($resql);
931 if ($row[0] == null) {
932 $parent_line = $selectedline;
933 } else {
934 $parent_line = $row[0]; //If the parent line is already a supplement, add the supplement to the main product
935 }
936 }
937 }
938
939 $err = 0;
940 // Group if enabled. Skip group if line already sent to the printer
941 if (getDolGlobalString('TAKEPOS_GROUP_SAME_PRODUCT')) {
942 foreach ($invoice->lines as $line) {
943 if ($line->product_ref == $prod->ref) {
944 if ($line->special_code == 4) {
945 continue;
946 } // If this line is sended to printer create new line
947 // check if qty in stock
948 if (getDolGlobalString('TAKEPOS_QTY_IN_STOCK') && (($line->qty + $qty) > $prod->stock_reel)) {
949 $invoice->error = $langs->trans("ErrorStockIsNotEnough");
950 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
951 $err++;
952 break;
953 }
954 $result = $invoice->updateline($line->id, $line->desc, $line->subprice, $line->qty + $qty, $line->remise_percent, $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
955 if ($result < 0) {
956 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
957 } else {
958 $idoflineadded = $line->id;
959 }
960 break;
961 }
962 }
963 }
964 if ($idoflineadded <= 0 && empty($err)) {
965 $invoice->fetch_thirdparty();
966 $array_options = array();
967
968 $line = array('description' => $prod->description, 'price' => $price, 'tva_tx' => $tva_tx, 'localtax1_tx' => $localtax1_tx, 'localtax2_tx' => $localtax2_tx, 'remise_percent' => $customer->remise_percent, 'price_ttc' => $price_ttc, 'array_options' => $array_options);
969
970 /* setup of margin calculation */
971 if (getDolGlobalString('MARGIN_TYPE')) {
972 if (getDolGlobalString('MARGIN_TYPE') == 'pmp' && !empty($prod->pmp)) {
973 $line['fk_fournprice'] = null;
974 $line['pa_ht'] = $prod->pmp;
975 } elseif (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($prod->cost_price)) {
976 $line['fk_fournprice'] = null;
977 $line['pa_ht'] = $prod->cost_price;
978 } else {
979 // default is fournprice
980 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
981 $pf = new ProductFournisseur($db);
982 if ($pf->find_min_price_product_fournisseur($idproduct, $qty) > 0) {
983 $line['fk_fournprice'] = $pf->product_fourn_price_id;
984 $line['pa_ht'] = $pf->fourn_unitprice_with_discount;
985 if (getDolGlobalString('PRODUCT_CHARGES') && $pf->fourn_charges > 0) {
986 $line['pa_ht'] += (float) $pf->fourn_charges / $pf->fourn_qty;
987 }
988 }
989 }
990 }
991
992 // complete line by hook
993 $parameters = array('prod' => $prod, 'line' => $line);
994 $reshook = $hookmanager->executeHooks('completeTakePosAddLine', $parameters, $invoice, $action); // Note that $action and $line may have been modified by some hooks
995 if ($reshook < 0) {
996 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
997 }
998
999
1000 if (empty($reshook)) {
1001 if (!empty($hookmanager->resArray)) {
1002 $line = $hookmanager->resArray;
1003 }
1004
1005 // check if qty in stock
1006 if (getDolGlobalString('TAKEPOS_QTY_IN_STOCK') && $qty > $prod->stock_reel) {
1007 $invoice->error = $langs->trans("ErrorStockIsNotEnough");
1008 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
1009 $err++;
1010 }
1011
1012 if (empty($err)) {
1013 $idoflineadded = $invoice->addline($line['description'], $line['price'], $qty, $line['tva_tx'], $line['localtax1_tx'], $line['localtax2_tx'], $idproduct, (float) $line['remise_percent'], '', 0, 0, 0, 0, $price_base_type, $line['price_ttc'], $prod->type, -1, 0, '', 0, (empty($parent_line) ? '' : $parent_line), (empty($line['fk_fournprice']) ? 0 : $line['fk_fournprice']), (empty($line['pa_ht']) ? '' : $line['pa_ht']), '', $line['array_options'], 100, 0, $prod->fk_unit, 0);
1014 }
1015 }
1016
1017 if (getDolGlobalString('TAKEPOS_CUSTOMER_DISPLAY')) {
1018 $CUSTOMER_DISPLAY_line1 = $prod->label;
1019 $CUSTOMER_DISPLAY_line2 = price($price_ttc);
1020 }
1021 }
1022
1023 $invoice->fetch($placeid);
1024 }
1025
1026 // If we add a line by submitting freezone form (invoice exists here because it was created just before if it didn't exist)
1027 if ($action == "freezone" && $user->hasRight('takepos', 'run')) {
1028 $customer = new Societe($db);
1029 $customer->fetch($invoice->socid);
1030
1031 $tva_tx = GETPOST('tva_tx', 'alpha');
1032 if ($tva_tx != '') {
1033 if (!preg_match('/\‍((.*)\‍)/', $tva_tx)) {
1034 $tva_tx = price2num($tva_tx);
1035 }
1036 } else {
1037 $tva_tx = get_default_tva($mysoc, $customer);
1038 }
1039
1040 // Local Taxes
1041 $localtax1_tx = get_localtax($tva_tx, 1, $customer, $mysoc, $tva_npr);
1042 $localtax2_tx = get_localtax($tva_tx, 2, $customer, $mysoc, $tva_npr);
1043
1044 $res = $invoice->addline($desc, $number, 1, $tva_tx, $localtax1_tx, $localtax2_tx, 0, 0, '', 0, 0, 0, 0, getDolGlobalInt('TAKEPOS_DISCOUNT_TTC') ? ($number >= 0 ? 'HT' : 'TTC') : (getDolGlobalInt('TAKEPOS_CHANGE_PRICE_HT') ? 'HT' : 'TTC'), $number, 0, -1, 0, '', 0, 0, 0, 0, '', array(), 100, 0, null, 0);
1045 if ($res < 0) {
1046 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
1047 }
1048 $invoice->fetch($placeid);
1049 }
1050
1051 if ($action == "addnote" && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
1052 $desc = GETPOST('addnote', 'alpha');
1053 if ($idline == 0) {
1054 $invoice->update_note($desc, '_public');
1055 } else {
1056 foreach ($invoice->lines as $line) {
1057 if ($line->id == $idline) {
1058 $result = $invoice->updateline($line->id, $desc, $line->subprice, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
1059 }
1060 }
1061 }
1062 $invoice->fetch($placeid);
1063 }
1064
1065 if ($action == "deleteline" && ($user->hasRight('takepos', 'editlines') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
1066 /*
1067 $permissiontoupdateline = ($user->hasRight('takepos', 'editlines') && ($user->hasRight('takepos', 'editorderedlines') || $line->special_code != "4"));
1068 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1069 if ($invoice->status == $invoice::STATUS_DRAFT && $invoice->pos_source && $invoice->module_source == 'takepos') {
1070 $permissiontoupdateline = true;
1071 // TODO Add also a test on $_SESSION('publicobjectid'] defined at creation of object
1072 // TODO Check also that invoice->ref is (PROV-POS1-2) with 1 = terminal and 2, the table ID
1073 }
1074 }*/
1075 $db->begin();
1076
1077 if ($idline > 0 && $placeid > 0) { // If invoice exists and a line is selected.
1078 $result = takeposDeleteLineWithChildren($invoice, $idline);
1079 if ($result < 0) {
1080 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
1081 }
1082 $invoice->fetch($placeid);
1083 } elseif ($placeid > 0) { // If invoice exists but no line selected (delete from another device or with no line selected), proceed to delete the last line.
1084 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."facturedet where fk_facture = ".((int) $placeid)." ORDER BY rowid DESC";
1085 $resql = $db->query($sql);
1086 $obj = $db->fetch_object($resql);
1087 if ($obj) {
1088 $deletelineid = $obj->rowid;
1089 $result = takeposDeleteLineWithChildren($invoice, $deletelineid);
1090 if ($result < 0) {
1091 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
1092 }
1093 }
1094 $invoice->fetch($placeid);
1095 }
1096
1097 $db->commit();
1098
1099 if (count($invoice->lines) == 0) {
1100 // Keep an empty draft invoice alive when a non-default customer was
1101 // already attached so deleting the last line does not silently lose
1102 // the customer that was just selected (#38219). Only drop the invoice
1103 // when it is still on the default cashdesk thirdparty (or none).
1104 $defaultsocid = (int) getDolGlobalString('CASHDESK_ID_THIRDPARTY'.$_SESSION["takeposterminal"]);
1105 $invoicesocid = (int) $invoice->socid;
1106 if ($invoicesocid === 0 || $invoicesocid === $defaultsocid) {
1107 $invoice->delete($user);
1108
1109 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1110 header("Location: ".DOL_URL_ROOT."/takepos/public/auto_order.php");
1111 } else {
1112 header("Location: ".DOL_URL_ROOT."/takepos/invoice.php");
1113 }
1114 exit;
1115 }
1116 }
1117 }
1118
1119 // Action to delete or discard an invoice
1120 if ($action == "delete" && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
1121 // $placeid is the invoice id (it differs from place) and is defined if the place is set and
1122 // the ref of invoice is '(PROV-POS'.$_SESSION["takeposterminal"].'-'.$place.')', so the fetch at beginning of page works.
1123 if ($placeid > 0) {
1124 $result = $invoice->fetch($placeid);
1125
1126 if ($result > 0 && $invoice->status == Facture::STATUS_DRAFT) {
1127 $db->begin();
1128
1129 // We delete the lines
1130 $resdeletelines = 1;
1131 foreach ($invoice->lines as $line) {
1132 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
1133 $tmpres = $invoice->deleteLine($line->id);
1134 if ($tmpres < 0) {
1135 $resdeletelines = 0;
1136 break;
1137 }
1138 }
1139
1140 $sql = "UPDATE ".MAIN_DB_PREFIX."facture";
1141 $varforconst = 'CASHDESK_ID_THIRDPARTY'.$_SESSION["takeposterminal"];
1142 $sql .= " SET fk_soc = ".((int) getDolGlobalString($varforconst)).", ";
1143 $sql .= " datec = '".$db->idate(dol_now())."'";
1144 $sql .= " WHERE entity IN (".getEntity('invoice').")";
1145 $sql .= " AND ref = '(PROV-POS".$db->escape($_SESSION["takeposterminal"]."-".$place).")'";
1146 $resql1 = $db->query($sql);
1147
1148 if ($resdeletelines && $resql1) {
1149 $db->commit();
1150 } else {
1151 $db->rollback();
1152 }
1153
1154 $invoice->fetch($placeid);
1155 }
1156 }
1157 }
1158
1159 // Action to fully remove (discard) the current sale: delete the draft invoice so its tab disappears
1160 // (the "delete" action above only empties and resets the cart).
1161 if ($action == "discardsale" && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
1162 if ($placeid > 0) {
1163 $result = $invoice->fetch($placeid);
1164 if ($result > 0 && $invoice->status == Facture::STATUS_DRAFT) {
1165 $invoice->delete($user);
1166 $placeid = 0;
1167 }
1168 }
1169 }
1170
1171 if ($action == "updateqty") { // Test on permission is done later
1172 foreach ($invoice->lines as $line) {
1173 if ($line->id == $idline) {
1174 $permissiontoupdateline = ($user->hasRight('takepos', 'editlines') && ($user->hasRight('takepos', 'editorderedlines') || $line->special_code != "4"));
1175 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1176 if ($invoice->status == $invoice::STATUS_DRAFT && $invoice->pos_source && $invoice->module_source == 'takepos') {
1177 $permissiontoupdateline = true;
1178 // TODO Add also a test on $_SESSION('publicobjectid'] defined at creation of object
1179 // TODO Check also that invoice->ref is (PROV-POS1-2) with 1 = terminal and 2, the table ID
1180 }
1181 }
1182 if (!$permissiontoupdateline) {
1183 dol_htmloutput_errors($langs->trans("NotEnoughPermissions", "TakePos").' - No permission to updateqty', [], 1);
1184 } else {
1185 if ($number <= 0) {
1186 $result = takeposDeleteLineWithChildren($invoice, $line->id);
1187 if ($result < 0) {
1188 dol_htmloutput_errors($invoice->error, $invoice->errors, 1);
1189 }
1190 } else {
1191 $vatratecode = $line->tva_tx;
1192 if ($line->vat_src_code) {
1193 $vatratecode .= ' ('.$line->vat_src_code.')';
1194 }
1195
1196 $result = $invoice->updateline($line->id, $line->desc, $line->subprice, $number, $line->remise_percent, $line->date_start, $line->date_end, $vatratecode, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
1197 }
1198 }
1199 }
1200 }
1201
1202 $invoice->fetch($placeid);
1203 }
1204
1205 if ($action == "updateprice") { // Test on permission is done later
1206 $customer = new Societe($db);
1207 $customer->fetch($invoice->socid);
1208
1209 foreach ($invoice->lines as $line) {
1210 if ($line->id == $idline) {
1211 $prod = new Product($db);
1212 $prod->fetch($line->fk_product);
1213 $datapriceofproduct = $prod->getSellPrice($mysoc, $customer, 0);
1214 $price_min = $datapriceofproduct['price_min'];
1215 $usercanproductignorepricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
1216
1217 $vatratecleaned = $line->tva_tx;
1218 $reg = array();
1219 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $line->tva_tx, $reg)) { // If vat is "xx (yy)"
1220 $vatratecleaned = trim($reg[1]);
1221 //$vatratecode = $reg[2];
1222 }
1223
1224 $pu_ht = price2num((float) price2num($number, 'MU') / (1 + ((float) $vatratecleaned / 100)), 'MU');
1225 // Check min price
1226 if ($usercanproductignorepricemin && (!empty($price_min) && ((float) price2num($pu_ht) * (1 - (float) price2num($line->remise_percent) / 100) < price2num($price_min)))) {
1227 $langs->load("products");
1228 dol_htmloutput_errors($langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency)));
1229 // echo $langs->trans("CantBeLessThanMinPrice");
1230 } else {
1231 $permissiontoupdateline = ($user->hasRight('takepos', 'editlines') && ($user->hasRight('takepos', 'editorderedlines') || $line->special_code != "4"));
1232 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1233 if ($invoice->status == $invoice::STATUS_DRAFT && $invoice->pos_source && $invoice->module_source == 'takepos') {
1234 $permissiontoupdateline = true;
1235 // TODO Add also a test on $_SESSION('publicobjectid'] defined at creation of object
1236 // TODO Check also that invoice->ref is (PROV-POS1-2) with 1 = terminal and 2, the table ID
1237 }
1238 }
1239
1240 $vatratecode = $line->tva_tx;
1241 if ($line->vat_src_code) {
1242 $vatratecode .= ' ('.$line->vat_src_code.')';
1243 }
1244
1245 if (!$permissiontoupdateline) {
1246 dol_htmloutput_errors($langs->trans("NotEnoughPermissions", "TakePos").' - No permission to updateprice', [], 1);
1247 } elseif (getDolGlobalInt('TAKEPOS_CHANGE_PRICE_HT') == 1) {
1248 $result = $invoice->updateline($line->id, $line->desc, $number, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $vatratecode, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
1249 } else {
1250 $result = $invoice->updateline($line->id, $line->desc, $number, $line->qty, $line->remise_percent, $line->date_start, $line->date_end, $vatratecode, $line->localtax1_tx, $line->localtax2_tx, 'TTC', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
1251 }
1252 }
1253 }
1254 }
1255
1256 // Reload data
1257 $invoice->fetch($placeid);
1258 }
1259
1260 if ($action == "updatereduction") { // Test on permission is done later
1261 $customer = new Societe($db);
1262 $customer->fetch($invoice->socid);
1263
1264 foreach ($invoice->lines as $line) {
1265 if ($line->id == $idline) {
1266 dol_syslog("updatereduction Process line ".$line->id.' to apply discount of '.$number.'%');
1267
1268 $prod = new Product($db);
1269 $prod->fetch($line->fk_product);
1270
1271 $datapriceofproduct = $prod->getSellPrice($mysoc, $customer, 0);
1272 $price_min = $datapriceofproduct['price_min'];
1273 $usercanproductignorepricemin = ((getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('produit', 'ignore_price_min_advance')) || !getDolGlobalString('MAIN_USE_ADVANCED_PERMS'));
1274
1275 $pu_ht = price2num($line->subprice / (1 + ($line->tva_tx / 100)), 'MU');
1276
1277 // Check min price
1278 if ($usercanproductignorepricemin && (!empty($price_min) && ((float) price2num($line->subprice) * (1 - (float) price2num($number) / 100) < (float) price2num($price_min)))) {
1279 $langs->load("products");
1280 dol_htmloutput_errors($langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, -1, $conf->currency)));
1281 } else {
1282 $permissiontoupdateline = ($user->hasRight('takepos', 'editlines') && ($user->hasRight('takepos', 'editorderedlines') || $line->special_code != "4"));
1283 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1284 if ($invoice->status == $invoice::STATUS_DRAFT && $invoice->pos_source && $invoice->module_source == 'takepos') {
1285 $permissiontoupdateline = true;
1286 // TODO Add also a test on $_SESSION('publicobjectid'] defined at creation of object
1287 // TODO Check also that invoice->ref is (PROV-POS1-2) with 1 = terminal and 2, the table ID
1288 }
1289 }
1290 if (!$permissiontoupdateline) {
1291 dol_htmloutput_errors($langs->trans("NotEnoughPermissions", "TakePos"), [], 1);
1292 } else {
1293 $vatratecode = $line->tva_tx;
1294 if ($line->vat_src_code) {
1295 $vatratecode .= ' ('.$line->vat_src_code.')';
1296 }
1297 $result = $invoice->updateline($line->id, $line->desc, $line->subprice, $line->qty, $number, $line->date_start, $line->date_end, $vatratecode, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
1298 }
1299 }
1300 }
1301 }
1302
1303 // Reload data
1304 $invoice->fetch($placeid);
1305 } elseif ($action == 'update_reduction_global' && $user->hasRight('takepos', 'editlines')) {
1306 foreach ($invoice->lines as $line) {
1307 $vatratecode = $line->tva_tx;
1308 if ($line->vat_src_code) {
1309 $vatratecode .= ' ('.$line->vat_src_code.')';
1310 }
1311 $result = $invoice->updateline($line->id, $line->desc, $line->subprice, $line->qty, $number, $line->date_start, $line->date_end, $vatratecode, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit);
1312 }
1313
1314 $invoice->fetch($placeid);
1315 }
1316
1317 if ($action == "setbatch" && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
1318 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.$_SESSION["takeposterminal"];
1319 $warehouseid = (GETPOSTINT('warehouseid') > 0 ? GETPOSTINT('warehouseid') : getDolGlobalInt($constantforkey)); // Get the warehouse id from GETPOSTINT('warehouseid'), otherwise use default setup.
1320 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET batch = '".$db->escape($batch)."', fk_warehouse = ".((int) $warehouseid);
1321 $sql .= " WHERE rowid = ".((int) $idoflineadded);
1322 $db->query($sql);
1323 }
1324
1325 if ($action == "order" && $placeid != 0 && ($user->hasRight('takepos', 'run') || defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE'))) {
1326 include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1327 if ((isModEnabled('receiptprinter') && getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term) > 0) || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "receiptprinter" || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "takeposconnector") {
1328 require_once DOL_DOCUMENT_ROOT.'/takepos/class/dolreceiptprinter.class.php';
1329 $printer = new dolReceiptPrinter($db);
1330 }
1331
1332 $sql = "SELECT label FROM ".MAIN_DB_PREFIX."takepos_floor_tables where rowid = ".((int) $place);
1333 $resql = $db->query($sql);
1334 $row = $db->fetch_object($resql);
1335 $headerorder = '<html><br><b>'.$langs->trans('Place').' '.$row->label.'<br><table width="65%"><thead><tr><th class="left">'.$langs->trans("Label").'</th><th class="right">'.$langs->trans("Qty").'</th></tr></thead><tbody>';
1336 $footerorder = '</tbody></table>'.dol_print_date(dol_now(), 'dayhour').'<br></html>';
1337 $order_receipt_printer1 = "";
1338 $order_receipt_printer2 = "";
1339 $order_receipt_printer3 = "";
1340 $catsprinter1 = explode(';', getDolGlobalString('TAKEPOS_PRINTED_CATEGORIES_1'));
1341 $catsprinter2 = explode(';', getDolGlobalString('TAKEPOS_PRINTED_CATEGORIES_2'));
1342 $catsprinter3 = explode(';', getDolGlobalString('TAKEPOS_PRINTED_CATEGORIES_3'));
1343 $linestoprint = 0;
1344 foreach ($invoice->lines as $line) {
1345 if ($line->special_code == "4") {
1346 continue;
1347 }
1348 $c = new Categorie($db);
1349 $existing = $c->containing($line->fk_product, Categorie::TYPE_PRODUCT, 'id');
1350 $result = array_intersect($catsprinter1, $existing);
1351 $count = count($result);
1352 if (!$line->fk_product) {
1353 $count++; // Print Free-text item (Unassigned printer) to Printer 1
1354 }
1355 if ($count > 0) {
1356 $linestoprint++;
1357 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet set special_code='1' where rowid = ".((int) $line->id); //Set to print on printer 1
1358 $db->query($sql);
1359 $order_receipt_printer1 .= '<tr><td class="left">';
1360 if ($line->fk_product) {
1361 $order_receipt_printer1 .= $line->product_label;
1362 } else {
1363 $order_receipt_printer1 .= $line->description;
1364 }
1365 $order_receipt_printer1 .= '</td><td class="right">'.$line->qty;
1366 if (!empty($line->array_options['options_order_notes'])) {
1367 $order_receipt_printer1 .= "<br>(".$line->array_options['options_order_notes'].")";
1368 }
1369 $order_receipt_printer1 .= '</td></tr>';
1370 }
1371 }
1372 if (((isModEnabled('receiptprinter') && getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term) > 0) || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "receiptprinter" || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "takeposconnector") && $linestoprint > 0 && $printer !== null) {
1373 $invoice->fetch($placeid); //Reload object before send to printer
1374 $printer->orderprinter = 1;
1375 echo "<script>";
1376 echo "var orderprinter1esc='";
1377 $ret = $printer->sendToPrinter($invoice, getDolGlobalInt('TAKEPOS_TEMPLATE_TO_USE_FOR_ORDERS'.$_SESSION["takeposterminal"]), getDolGlobalInt('TAKEPOS_ORDER_PRINTER1_TO_USE'.$_SESSION["takeposterminal"])); // PRINT TO PRINTER 1
1378 echo "';</script>";
1379 }
1380 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet set special_code='4' where special_code='1' and fk_facture = ".((int) $invoice->id); // Set as printed
1381 $db->query($sql);
1382 $invoice->fetch($placeid); //Reload object after set lines as printed
1383 $linestoprint = 0;
1384
1385 foreach ($invoice->lines as $line) {
1386 if ($line->special_code == "4") {
1387 continue;
1388 }
1389 $c = new Categorie($db);
1390 $existing = $c->containing($line->fk_product, Categorie::TYPE_PRODUCT, 'id');
1391 $result = array_intersect($catsprinter2, $existing);
1392 $count = count($result);
1393 if ($count > 0) {
1394 $linestoprint++;
1395 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet set special_code='2' where rowid = ".((int) $line->id); //Set to print on printer 2
1396 $db->query($sql);
1397 $order_receipt_printer2 .= '<tr>'.$line->product_label.'<td class="right">'.$line->qty;
1398 if (!empty($line->array_options['options_order_notes'])) {
1399 $order_receipt_printer2 .= "<br>(".$line->array_options['options_order_notes'].")";
1400 }
1401 $order_receipt_printer2 .= '</td></tr>';
1402 }
1403 }
1404 if (((isModEnabled('receiptprinter') && getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term) > 0) || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "receiptprinter" || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "takeposconnector") && $linestoprint > 0) {
1405 $invoice->fetch($placeid); //Reload object before send to printer
1406 $printer->orderprinter = 2;
1407 echo "<script>";
1408 echo "var orderprinter2esc='";
1409 $ret = $printer->sendToPrinter($invoice, getDolGlobalInt('TAKEPOS_TEMPLATE_TO_USE_FOR_ORDERS'.$_SESSION["takeposterminal"]), getDolGlobalInt('TAKEPOS_ORDER_PRINTER2_TO_USE'.$_SESSION["takeposterminal"])); // PRINT TO PRINTER 2
1410 echo "';</script>";
1411 }
1412 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet set special_code='4' where special_code='2' and fk_facture = ".((int) $invoice->id); // Set as printed
1413 $db->query($sql);
1414 $invoice->fetch($placeid); //Reload object after set lines as printed
1415 $linestoprint = 0;
1416
1417 foreach ($invoice->lines as $line) {
1418 if ($line->special_code == "4") {
1419 continue;
1420 }
1421 $c = new Categorie($db);
1422 $existing = $c->containing($line->fk_product, Categorie::TYPE_PRODUCT, 'id');
1423 $result = array_intersect($catsprinter3, $existing);
1424 $count = count($result);
1425 if ($count > 0) {
1426 $linestoprint++;
1427 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet set special_code='3' where rowid = ".((int) $line->id); //Set to print on printer 3
1428 $db->query($sql);
1429 $order_receipt_printer3 .= '<tr>'.$line->product_label.'<td class="right">'.$line->qty;
1430 if (!empty($line->array_options['options_order_notes'])) {
1431 $order_receipt_printer3 .= "<br>(".$line->array_options['options_order_notes'].")";
1432 }
1433 $order_receipt_printer3 .= '</td></tr>';
1434 }
1435 }
1436 if (((isModEnabled('receiptprinter') && getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term) > 0) || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "receiptprinter" || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "takeposconnector") && $linestoprint > 0 && $printer !== null) {
1437 $invoice->fetch($placeid); //Reload object before send to printer
1438 $printer->orderprinter = 3;
1439 echo "<script>";
1440 echo "var orderprinter3esc='";
1441 $ret = $printer->sendToPrinter($invoice, getDolGlobalInt('TAKEPOS_TEMPLATE_TO_USE_FOR_ORDERS'.$_SESSION["takeposterminal"]), getDolGlobalInt('TAKEPOS_ORDER_PRINTER3_TO_USE'.$_SESSION["takeposterminal"])); // PRINT TO PRINTER 3
1442 echo "';</script>";
1443 }
1444 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet set special_code='4' where special_code='3' and fk_facture = ".((int) $invoice->id); // Set as printed
1445 $db->query($sql);
1446 $invoice->fetch($placeid); //Reload object after set lines as printed
1447 }
1448
1449 $sectionwithinvoicelink = '';
1450 if (($action == "valid" || $action == "history" || $action == 'creditnote' || ($action == 'addline' && $invoice->status == $invoice::STATUS_CLOSED)) && $user->hasRight('takepos', 'run')) {
1451 $sectionwithinvoicelink .= '<!-- Section with invoice link -->'."\n";
1452 $sectionwithinvoicelink .= '<span style="font-size:120%;" class="center inline-block marginbottomonly">';
1453 if ($invoice->status == $invoice::STATUS_DRAFT) {
1454 $sectionwithinvoicelink .= $invoice->ref;
1455 } else {
1456 $sectionwithinvoicelink .= $invoice->getNomUrl(1, '', 0, 0, '', 0, 0, -1, '_backoffice');
1457 }
1458 $sectionwithinvoicelink .= " - ";
1459 $remaintopay = $invoice->getRemainToPay();
1460 if ($remaintopay > 0) {
1461 $sectionwithinvoicelink .= $langs->trans('RemainToPay').': <span class="amountremaintopay" style="font-size: unset">'.price($remaintopay, 1, $langs, 1, -1, -1, $conf->currency).'</span>';
1462 } else {
1463 $sectionwithinvoicelink .= $invoice->getLibStatut(2);
1464 }
1465
1466 $sectionwithinvoicelink .= '</span><br>';
1467
1468 $customprinterallowed = false;
1469 $customprinttemplateallowed = true;
1470
1471 // BAR RESTAURANT specific menu
1472 if (getDolGlobalString('TAKEPOS_BAR_RESTAURANT')) {
1473 // Button to print receipt before payment
1474 $customprinterallowed = true;
1475 $customprinttemplateallowed = true;
1476 }
1477
1478 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
1479 if (isALNERunningVersion()) {
1480 // Custom printer may be allowed if mandatory information in template are guaranteed. For the moment, we prefer not allow this.
1481 $customprinttemplateallowed = false;
1482 }
1483
1484 if ($invoice->status == $invoice::STATUS_CLOSED) {
1485 if (getDolGlobalInt('TAKEPOS_PRINT_INVOICE_DOC_INSTEAD_OF_RECEIPT')) {
1486 $sectionwithinvoicelink .= ' <a target="_blank" class="button" href="' . DOL_URL_ROOT . '/document.php?token=' . newToken() . '&modulepart=facture&file=' . $invoice->ref . '/' . $invoice->ref . '.pdf">'.$langs->trans("Invoice").'</a>';
1487 } else {
1488 // This section should be same than into index.php
1489 if (getDolGlobalString('TAKEPOS_PRINT_METHOD') == "takeposconnector") {
1490 // Used when the external addon takeposconnector is installed. Deprecated.
1491 if (getDolGlobalString('TAKEPOS_PRINT_SERVER') && filter_var(getDolGlobalString('TAKEPOS_PRINT_SERVER'), FILTER_VALIDATE_URL) == true) {
1492 // If TAKEPOS_PRINT_SERVER is an URL
1493 $sectionwithinvoicelink .= ' <button id="buttonprint" type="button" onclick="PrintByESCPOSOld('.$placeid.')">'.$langs->trans('PrintTicket').'</button>';
1494 } else {
1495 // If TAKEPOS_PRINT_SERVER is an IP
1496 // Print by calling the receipt.php to get HTML content and send the HTML content to TAKEPOS_PRINT_SERVER:8111/print
1497 $sectionwithinvoicelink .= ' <button id="buttonprint" type="button" onclick="PrintHTMLToSlashPrint('.$placeid.')">'.$langs->trans('PrintTicket').'</button>';
1498 }
1499 } elseif ($customprinterallowed && $customprinttemplateallowed && (isModEnabled('receiptprinter') && getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term) > 0) || getDolGlobalString('TAKEPOS_PRINT_METHOD') == "receiptprinter") { // @phpstan-ignore-line
1500 // Button Print Receipt on special custom printer using custom template
1501 $nameOfPrinter = dol_getIdFromCode($db, getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term), 'printer_receipt', 'rowid', 'name', 1);
1502 $sectionwithinvoicelink .= ' <button id="buttonprint" type="button" onclick="PrintByESCPOS('.$placeid.')" title="'.dolPrintHTMLForAttribute($langs->trans("SentToPrinter").' '.$nameOfPrinter).'">'.$langs->trans('PrintTicket').'</button>';
1503 } else {
1504 // Button Print Receipt on browser
1505 $sectionwithinvoicelink .= ' <button id="buttonprint" type="button" onclick="PrintByBrowser('.$placeid.')">'.$langs->trans('PrintTicket').'</button>';
1506
1507 // Additional buttons
1508 if ($customprinttemplateallowed && getDolGlobalString('TAKEPOS_PRINT_WITHOUT_DETAILS')) {
1509 $sectionwithinvoicelink .= ' <button id="buttonprint" type="button" onclick="PrintBox('.$placeid.', \'without_details\')">'.$langs->trans('PrintWithoutDetails').'</button>';
1510 }
1511 if ($customprinttemplateallowed && getDolGlobalString('TAKEPOS_GIFT_RECEIPT')) {
1512 $sectionwithinvoicelink .= ' <button id="buttonprint" type="button" onclick="PrintByBrowser('.$placeid.', 1)">'.$langs->trans('GiftReceipt').'</button>';
1513 }
1514 }
1515 }
1516 if (getDolGlobalString('TAKEPOS_EMAIL_TEMPLATE_INVOICE') && getDolGlobalInt('TAKEPOS_EMAIL_TEMPLATE_INVOICE') > 0) {
1517 $sectionwithinvoicelink .= ' <button id="buttonsend" type="button" onclick="SendTicket('.$placeid.')">'.$langs->trans('SendTicket').'</button>';
1518 }
1519
1520 if ($remaintopay <= 0 && getDolGlobalString('TAKEPOS_AUTO_PRINT_TICKETS') && $action != "history") {
1521 $sectionwithinvoicelink .= '<script type="text/javascript">console.log("Emulate click on #buttonprint"); $("#buttonprint").click();</script>';
1522 }
1523 }
1524 }
1525}
1526
1527
1528/*
1529 * View
1530 */
1531
1532$form = new Form($db);
1533
1534// llxHeader
1535if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1536 $title = 'TakePOS - Dolibarr '.DOL_VERSION;
1537 if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
1538 $title = 'TakePOS - ' . getDolGlobalString('MAIN_APPLICATION_TITLE');
1539 }
1540 $head = '<meta name="apple-mobile-web-app-title" content="TakePOS"/>
1541 <meta name="apple-mobile-web-app-capable" content="yes">
1542 <meta name="mobile-web-app-capable" content="yes">
1543 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>';
1544 $arrayofcss = array(
1545 '/takepos/css/pos.css.php',
1546 );
1547 $arrayofjs = array('/takepos/js/jquery.colorbox-min.js');
1548 $disablejs = 0;
1549 $disablehead = 0;
1550 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss);
1551
1552 print '<body>'."\n";
1553} else {
1554 top_httphead('text/html', 1);
1555}
1556
1557?>
1558<!-- invoice.php -->
1559<script type="text/javascript">
1560var selectedline=0;
1561var selectedtext="";
1562<?php if ($action == "valid") {
1563 echo "var place=0;";
1564}?> // Set to default place after close sale
1565var placeid=<?php echo($placeid > 0 ? $placeid : 0); ?>;
1566$(document).ready(function() {
1567 var idoflineadded = <?php echo(empty($idoflineadded) ? 0 : $idoflineadded); ?>;
1568
1569 $('.posinvoiceline').click(function(){
1570 console.log("Click done on "+this.id);
1571 $('.posinvoiceline').removeClass("selected");
1572 $(this).addClass("selected");
1573 if (!this.id) {
1574 return;
1575 }
1576 if (selectedline == this.id) {
1577 return; // If is already selected
1578 } else {
1579 selectedline = this.id;
1580 }
1581 selectedtext=$('#'+selectedline).find("td:first").html();
1582 <?php
1583 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
1584 print '$("#phonediv1").load("'.DOL_URL_ROOT.'/takepos/public/auto_order.php?action=editline&token='.newToken().'&placeid="+placeid+"&selectedline="+selectedline, function() {
1585 });';
1586 }
1587 ?>
1588 });
1589
1590 /* Autoselect the line */
1591 if (idoflineadded > 0)
1592 {
1593 console.log("Auto select "+idoflineadded);
1594 $('.posinvoiceline#'+idoflineadded).click();
1595 }
1596<?php
1597
1598if ($action == "order" && !empty($order_receipt_printer1)) {
1599 if (filter_var(getDolGlobalString('TAKEPOS_PRINT_SERVER'), FILTER_VALIDATE_URL) == true) {
1600 ?>
1601 $.ajax({
1602 type: "POST",
1603 url: '<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>/printer/index.php',
1604 data: 'invoice='+orderprinter1esc
1605 });
1606 <?php
1607 } else {
1608 ?>
1609 $.ajax({
1610 type: "POST",
1611 url: 'http://<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>:8111/print',
1612 data: '<?php
1613 print $headerorder.$order_receipt_printer1.$footerorder; ?>'
1614 });
1615 <?php
1616 }
1617}
1618
1619if ($action == "order" && !empty($order_receipt_printer2)) {
1620 if (filter_var(getDolGlobalString('TAKEPOS_PRINT_SERVER'), FILTER_VALIDATE_URL) == true) {
1621 ?>
1622 $.ajax({
1623 type: "POST",
1624 url: '<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>/printer/index.php?printer=2',
1625 data: 'invoice='+orderprinter2esc
1626 });
1627 <?php
1628 } else {
1629 ?>
1630 $.ajax({
1631 type: "POST",
1632 url: 'http://<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>:8111/print2',
1633 data: '<?php
1634 print $headerorder.$order_receipt_printer2.$footerorder; ?>'
1635 });
1636 <?php
1637 }
1638}
1639
1640if ($action == "order" && !empty($order_receipt_printer3)) {
1641 if (filter_var(getDolGlobalString('TAKEPOS_PRINT_SERVER'), FILTER_VALIDATE_URL) == true) {
1642 ?>
1643 $.ajax({
1644 type: "POST",
1645 url: '<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>/printer/index.php?printer=3',
1646 data: 'invoice='+orderprinter3esc
1647 });
1648 <?php
1649 }
1650}
1651
1652// Set focus to search field
1653if ($action == "search" || $action == "valid") {
1654 ?>
1655 parent.ClearSearch(true);
1656 <?php
1657}
1658
1659
1660if ($action == "temp" && !empty($ticket_printer1)) {
1661 ?>
1662 $.ajax({
1663 type: "POST",
1664 url: 'http://<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>:8111/print',
1665 data: '<?php
1666 print $header_soc.$header_ticket.$body_ticket.$ticket_printer1.$ticket_total.$footer_ticket; ?>'
1667 });
1668 <?php
1669}
1670
1671if ($action == "search") {
1672 ?>
1673 $('#search').focus();
1674 <?php
1675}
1676
1677?>
1678
1679});
1680
1681function SendTicket(id)
1682{
1683 console.log("Open box to select the Print/Send form");
1684 $.colorbox({href:"send.php?facid="+id, width:"70%", height:"30%", transition:"none", iframe:"true", title:'<?php echo dol_escape_js($langs->trans("SendTicket")); ?>'});
1685 return true;
1686}
1687
1688/* Open the popup of the receipt to allow printing */
1689function PrintBox(id, action) {
1690 console.log("Open box before printing");
1691 $.colorbox({href:"printbox.php?facid="+id+"&action="+action+"&token=<?php echo newToken(); ?>", width:"80%", height:"200px", transition:"none", iframe:"true", title:"<?php echo $langs->trans("PrintWithoutDetails"); ?>"});
1692 return true;
1693}
1694
1695/* Open the popup of the receipt to allow printing */
1696function PrintByBrowser(id, gift) {
1697 console.log("Call PrintByBrowser() to generate the receipt.");
1698 $.colorbox({href:"receipt.php?facid="+id+"&gift="+gift, width:"40%", height:"90%", transition:"none", iframe:"true", title:'<?php echo dol_escape_js($langs->trans("PrintTicket")); ?>'});
1699 return true;
1700}
1701
1702/* Print of configured printer when TAKEPOS_PRINT_SERVER is IP */
1703function PrintHTMLToSlashPrint(id){
1704 var receipt;
1705 console.log("PrintHTMLToSlashPrint" + id);
1706 $.get("receipt.php?facid="+id, function(data, status) {
1707 receipt=data.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '');
1708 $.ajax({
1709 type: "POST",
1710 url: 'http://<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>:8111/print',
1711 data: receipt
1712 });
1713 });
1714 return true;
1715}
1716
1717/* Print of configured printer when TAKEPOS_PRINT_SERVER is URL, using the ESCPOS driver */
1718function PrintByESCPOSOld(id){
1719 console.log("PrintByESCPOSOld id=" + id);
1720 $.get("<?php echo DOL_URL_ROOT; ?>/takepos/ajax/ajax.php?action=printinvoiceticket&token=<?php echo currentToken(); ?>&term=<?php echo urlencode(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : ''); ?>&id="+id, function(data, status) {
1721 $.ajax({
1722 type: "POST",
1723 url: '<?php print getDolGlobalString('TAKEPOS_PRINT_SERVER'); ?>/printer/index.php',
1724 data: 'invoice='+data
1725 });
1726 });
1727 return true;
1728}
1729
1730<?php
1731$nameOfPrinter = dol_getIdFromCode($db, getDolGlobalInt('TAKEPOS_PRINTER_TO_USE'.$term), 'printer_receipt', 'rowid', 'name', 1);
1732?>
1733// Call the ajax to execute the printinvoiceticket action, using the ESCPOS driver
1734// With some external module another method may be called.
1735function PrintByESCPOS(id) {
1736 console.log("PrintByESCPOS Printing invoice ticket by calling takepos/aja/ajax.php id=" + id);
1737
1738 $.ajax({
1739 type: "GET",
1740 data: { token: '<?php echo currentToken(); ?>' },
1741 url: "<?php print DOL_URL_ROOT.'/takepos/ajax/ajax.php?action=printinvoiceticket&token='.currentToken().'&term='.urlencode(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '').'&id='; ?>" + id,
1742 success: function(){
1743 showPrintResultPopup('<?php echo dol_escape_js($langs->trans("SentToPrinter").' '.$nameOfPrinter); ?>', 2000);
1744 },
1745 error: function(){
1746 showPrintResultPopup("<?php echo dol_escape_js($langs->trans("FailedToSendToPrinter")); ?>", 2000);
1747 }
1748 });
1749 return true;
1750}
1751
1752// Show the message in div popup
1753function showPrintResultPopup(message, duration) {
1754 $("#dialogforpopuptakepos").show().text(message).fadeIn();
1755
1756 setTimeout(function(){
1757 $("#dialogforpopuptakepos").fadeOut().hide();
1758 }, duration);
1759}
1760
1761
1762
1763// Call url to generate a credit note (with same lines) from existing invoice
1764function CreditNote() {
1765 $("#poslines").load("<?php print DOL_URL_ROOT; ?>/takepos/invoice.php?action=creditnote&token=<?php echo newToken() ?>&invoiceid="+placeid, function() { });
1766 return true;
1767}
1768
1769// Call url to add notes
1770function SetNote() {
1771 $("#poslines").load("<?php print DOL_URL_ROOT; ?>/takepos/invoice.php?action=addnote&token=<?php echo newToken() ?>&invoiceid="+placeid+"&idline="+selectedline, { "addnote": $("#textinput").val() });
1772 return true;
1773}
1774
1775
1776$( document ).ready(function() {
1777 console.log("Set customer info and sales in header placeid=<?php echo $placeid; ?> status=<?php echo $invoice->statut; ?>");
1778
1779 <?php
1780 $s = $langs->trans("Customer");
1781 if ($invoice->id > 0 && ($invoice->socid != getDolGlobalString($constforcompanyid))) {
1782 $s = $soc->name;
1783 if (getDolGlobalInt('TAKEPOS_CHOOSE_CONTACT')) {
1784 $contactids = $invoice->getIdContact('external', 'BILLING');
1785 $contactid = $contactids[0];
1786 if ($contactid > 0) {
1787 $contact = new Contact($db);
1788 $contact->fetch($contactid);
1789 $s .= " - " . $contact->getFullName($langs);
1790 }
1791 }
1792 } elseif (getDolGlobalInt("TAKEPOS_NO_GENERIC_THIRDPARTY")) {
1793 print '$("#idcustomer").val("");';
1794 }
1795 ?>
1796
1797 $("#customerandsales").html('');
1798 $("#shoppingcart").html('');
1799
1800 <?php if (getDolGlobalInt('TAKEPOS_CHOOSE_CONTACT') == 0) { ?>
1801 $("#customerandsales").append('<a class="valignmiddle tdoverflowmax100 minwidth100" id="customer" onclick="Customer();" title="<?php print dol_escape_js(dol_escape_htmltag((string) $s)); ?>"><span class="fas fa-building paddingrightonly"></span><?php print dol_escape_js((string) $s); ?></a>');
1802 <?php } else { ?>
1803 $("#customerandsales").append('<a class="valignmiddle tdoverflowmax300 minwidth100" id="contact" onclick="Contact();" title="<?php print dol_escape_js(dol_escape_htmltag((string) $s)); ?>"><span class="fas fa-building paddingrightonly"></span><?php print dol_escape_js((string) $s); ?></a>');
1804 <?php } ?>
1805
1806 <?php
1807 $sql = "SELECT rowid, datec, ref FROM ".MAIN_DB_PREFIX."facture";
1808 $sql .= " WHERE entity IN (".getEntity('invoice').")";
1809 if (!getDolGlobalString('TAKEPOS_CAN_EDIT_IF_ALREADY_VALIDATED')) {
1810 // By default, only invoices with a ref not already defined can in list of open invoice we can edit.
1811 $sql .= " AND ref LIKE '(PROV-POS".$db->escape(isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '')."-0%'";
1812 } else {
1813 // If TAKEPOS_CAN_EDIT_IF_ALREADY_VALIDATED set, we show also draft invoice that already has a reference defined
1814 $sql .= " AND pos_source = '".$db->escape((string) $_SESSION["takeposterminal"])."'";
1815 $sql .= " AND module_source = 'takepos'";
1816 }
1817
1818 $sql .= $db->order('datec', 'ASC');
1819 $resql = $db->query($sql);
1820 if ($resql) {
1821 $max_sale = 0;
1822 while ($obj = $db->fetch_object($resql)) {
1823 echo '$("#shoppingcart").append(\'';
1824 echo '<a class="valignmiddle" title="'.dol_escape_js($langs->trans("SaleStartedAt", dol_print_date($db->jdate($obj->datec), '%H:%M', 'tzuser')).' - '.$obj->ref).'" onclick="place=\\\'';
1825 $num_sale = str_replace(")", "", str_replace("(PROV-POS".$_SESSION["takeposterminal"]."-", "", $obj->ref));
1826 echo $num_sale;
1827 if (str_replace("-", "", $num_sale) > $max_sale) {
1828 $max_sale = str_replace("-", "", $num_sale);
1829 }
1830 echo '\\\'; invoiceid=\\\'';
1831 echo $obj->rowid;
1832 echo '\\\'; Refresh();">';
1833 if ($placeid == $obj->rowid) {
1834 echo '<span class="basketselected">';
1835 } else {
1836 echo '<span class="basketnotselected">';
1837 }
1838 echo '<span class="fa fa-shopping-cart paddingright"></span>'.dol_print_date($db->jdate($obj->datec), '%H:%M', 'tzuser');
1839 echo '</span>';
1840 echo '</a>\');';
1841 }
1842 echo '$("#shoppingcart").append(\'<a onclick="place=\\\'0-';
1843 echo $max_sale + 1;
1844 echo '\\\'; invoiceid=0; Refresh();"><div><span class="fa fa-plus" title="'.dol_escape_htmltag($langs->trans("StartAParallelSale")).'"><span class="fa fa-shopping-cart"></span></div></a>\');';
1845 } else {
1847 }
1848
1849 $s = '';
1850
1851 $idwarehouse = 0;
1852 $constantforkey = 'CASHDESK_NO_DECREASE_STOCK'. (isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
1853 if (isModEnabled('stock')) {
1854 if (getDolGlobalString($constantforkey) != "1") {
1855 $constantforkey = 'CASHDESK_ID_WAREHOUSE'. (isset($_SESSION["takeposterminal"]) ? $_SESSION["takeposterminal"] : '');
1856 $idwarehouse = getDolGlobalInt($constantforkey);
1857 if ($idwarehouse > 0) {
1858 $s = '<span class="small">';
1859 $warehouse = new Entrepot($db);
1860 $warehouse->fetch($idwarehouse);
1861 $s .= '<span class="hideonsmartphone">'.$langs->trans("Warehouse").'<br></span>'.$warehouse->ref;
1862 if ($warehouse->statut == Entrepot::STATUS_CLOSED) {
1863 $s .= ' ('.$langs->trans("Closed").')';
1864 }
1865 $s .= '</span>';
1866 print "$('#infowarehouse').html('".dol_escape_js($s)."');";
1867 print '$("#infowarehouse").css("display", "inline-block");';
1868 } else {
1869 $s = '<span class="small hideonsmartphone">';
1870 $s .= $langs->trans("StockChangeDisabled").'<br>'.$langs->trans("NoWarehouseDefinedForTerminal");
1871 $s .= '</span>';
1872 print "$('#infowarehouse').html('".dol_escape_js($s)."');";
1873 if (!empty($conf->dol_optimize_smallscreen)) {
1874 print '$("#infowarehouse").css("display", "none");';
1875 }
1876 }
1877 } else {
1878 $s = '<span class="small hideonsmartphone">'.$langs->trans("StockChangeDisabled").'</span>';
1879 print "$('#infowarehouse').html('".dol_escape_js($s)."');";
1880 if (!empty($conf->dol_optimize_smallscreen)) {
1881 print '$("#infowarehouse").css("display", "none");';
1882 }
1883 }
1884 }
1885
1886
1887 // Module Adherent
1888 $s = '';
1889 if (isModEnabled('member') && $invoice->socid > 0 && $invoice->socid != getDolGlobalInt($constforcompanyid)) {
1890 $s = '<span class="small">';
1891 require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
1892 $langs->load("members");
1893 $s .= $langs->trans("Member").': ';
1894 $adh = new Adherent($db);
1895 $result = $adh->fetch(0, '', $invoice->socid);
1896 if ($result > 0) {
1897 $adh->ref = $adh->getFullName($langs);
1898 if (empty($adh->status) || $adh->status == Adherent::STATUS_EXCLUDED) {
1899 $s .= "<s>";
1900 }
1901 $s .= $adh->getFullName($langs);
1902 $s .= ' - '.$adh->type;
1903 if ($adh->datefin) {
1904 $s .= '<br>'.$langs->trans("SubscriptionEndDate").': '.dol_print_date($adh->datefin, 'day');
1905 if ($adh->hasDelay()) {
1906 $s .= " ".img_warning($langs->trans("Late"));
1907 }
1908 } else {
1909 $s .= '<br>'.$langs->trans("SubscriptionNotReceived");
1910 if ($adh->status > 0) {
1911 $s .= " ".img_warning($langs->trans("Late")); // displays delay Pictogram only if not a draft and not terminated
1912 }
1913 }
1914 if (empty($adh->status) || $adh->status == Adherent::STATUS_EXCLUDED) {
1915 $s .= "</s>";
1916 }
1917 } else {
1918 $s .= '<br>'.$langs->trans("ThirdpartyNotLinkedToMember");
1919 }
1920 $s .= '</span>';
1921 }
1922 ?>
1923 $("#moreinfo").html('<?php print dol_escape_js($s); ?>');
1924
1925});
1926
1927
1928<?php
1929if (getDolGlobalString('TAKEPOS_CUSTOMER_DISPLAY')) {
1930 echo "function CustomerDisplay(){";
1931 echo "var line1='".$CUSTOMER_DISPLAY_line1."'.substring(0,20);";
1932 echo "line1=line1.padEnd(20);";
1933 echo "var line2='".$CUSTOMER_DISPLAY_line2."'.substring(0,20);";
1934 echo "line2=line2.padEnd(20);";
1935 if (getDolGlobalString('TAKEPOS_CONNECTOR_TO_WHB_CUSTOMER_DISPLAY')) {
1936 echo 'webSocketCustomerDisplay.send(line1);';
1937 echo 'webSocketCustomerDisplay.send(line2);';
1938 } else {
1939 echo "$.ajax({
1940 type: 'GET',
1941 data: { text: line1+line2 },
1942 url: '".getDolGlobalString('TAKEPOS_PRINT_SERVER')."/display/index.php',
1943 });";
1944 }
1945 echo "}";
1946}
1947?>
1948
1949</script>
1950
1951<?php
1952// Add again js for footer because this content is injected into index.php page so all init
1953// for tooltip and other js beautifiers must be reexecuted too.
1954if (!empty($conf->use_javascript_ajax)) {
1955 print "\n".'<!-- Includes JS Footer of Dolibarr -->'."\n";
1956 print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang.'"></script>'."\n";
1957}
1958
1959$usediv = (GETPOST('format') == 'div');
1960
1961print '<!-- invoice.php place='.(int) $place.' invoice='.$invoice->ref.' usediv='.json_encode($usediv).', mobilepage='.(empty($mobilepage) ? '' : $mobilepage).' $_SESSION["basiclayout"]='.(empty($_SESSION["basiclayout"]) ? '' : $_SESSION["basiclayout"]).' conf TAKEPOS_BAR_RESTAURANT='.getDolGlobalString('TAKEPOS_BAR_RESTAURANT').' -->'."\n";
1962print '<div class="div-table-responsive-no-min invoice">';
1963if ($usediv) {
1964 print '<div id="tablelines">';
1965} else {
1966 print '<table id="tablelines" class="noborder noshadow postablelines centpercent">';
1967}
1968
1969$buttontocreatecreditnote = '';
1970if (($action == "valid" || $action == "history" || $action == "addline")
1971 && $invoice->type != Facture::TYPE_CREDIT_NOTE && !getDolGlobalString('TAKEPOS_NO_CREDITNOTE') && $invoice->status == $invoice::STATUS_CLOSED) {
1972 $buttontocreatecreditnote .= ' &nbsp; <!-- Show button to create a credit note -->'."\n";
1973 $buttontocreatecreditnote .= '<button id="buttonprint" type="button" onclick="ModalBox(\'ModalCreditNote\')">'.$langs->trans('CreateCreditNote').'</button>';
1974 if (getDolGlobalInt('TAKEPOS_PRINT_INVOICE_DOC_INSTEAD_OF_RECEIPT')) {
1975 $buttontocreatecreditnote .= ' <a target="_blank" class="button" href="' . DOL_URL_ROOT . '/document.php?token=' . newToken() . '&modulepart=facture&file=' . urlencode($invoice->ref . '/' . $invoice->ref . '.pdf').'">'.$langs->trans("Invoice").'</a>';
1976 }
1977}
1978
1979// Show the ref of invoice
1980if ($sectionwithinvoicelink && ($mobilepage == "invoice" || $mobilepage == "")) {
1981 print '<!-- Print table line with link to invoice ref -->';
1982 if (getDolGlobalString('TAKEPOS_SHOW_HT')) {
1983 print '<tr><td colspan="5" class="paddingtopimp paddingbottomimp" style="padding-top: 10px !important; padding-bottom: 10px !important;">';
1984 print $sectionwithinvoicelink;
1985 print $buttontocreatecreditnote;
1986 print '</td></tr>';
1987 } else {
1988 print '<tr><td colspan="4" class="paddingtopimp paddingbottomimp" style="padding-top: 10px !important; padding-bottom: 10px !important;">';
1989 print $sectionwithinvoicelink;
1990 print $buttontocreatecreditnote;
1991 print '</td></tr>';
1992 }
1993}
1994
1995// Show the list of selected product
1996if (!$usediv) {
1997 print '<tr class="liste_titre nodrag nodrop">';
1998 print '<td class="linecoldescription">';
1999}
2000// In phone version only show when it is invoice page
2001if (empty($mobilepage) || $mobilepage == "invoice") {
2002 print '<!-- hidden var used by some js functions -->';
2003 print '<input type="hidden" name="invoiceid" id="invoiceid" value="'.$invoice->id.'">';
2004 print '<input type="hidden" name="thirdpartyid" id="thirdpartyid" value="'.$invoice->socid.'">';
2005}
2006if (!$usediv) {
2007 if (getDolGlobalString('TAKEPOS_BAR_RESTAURANT')) {
2008 $sql = "SELECT floor, label FROM ".MAIN_DB_PREFIX."takepos_floor_tables where rowid = ".((int) $place);
2009 $resql = $db->query($sql);
2010 $obj = $db->fetch_object($resql);
2011 if ($obj) {
2012 $label = $obj->label;
2013 $floor = $obj->floor;
2014 }
2015 if ($mobilepage == "invoice" || $mobilepage == "") {
2016 // If not on smartphone version or if it is the invoice page
2017 //print 'mobilepage='.$mobilepage;
2018 print '<span class="opacitymedium">'.$langs->trans('Place')."</span> <b>".(empty($label) ? '?' : $label)."</b><br>";
2019 print '<span class="opacitymedium">'.$langs->trans('Floor')."</span> <b>".(empty($floor) ? '?' : $floor)."</b>";
2020 }
2021 }
2022 print '</td>';
2023}
2024
2025// Complete header by hook
2026$parameters = array();
2027$reshook = $hookmanager->executeHooks('completeTakePosInvoiceHeader', $parameters, $invoice, $action); // Note that $action and $object may have been modified by some hooks
2028if ($reshook < 0) {
2029 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2030}
2031print $hookmanager->resPrint;
2032
2033if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) {
2034 if (getDolGlobalInt("TAKEPOS_SHOW_SUBPRICE")) {
2035 print '<td class="linecolqty right">'.$langs->trans('PriceUHT').'</td>';
2036 }
2037 print '<td class="linecolqty right">'.$langs->trans('ReductionShort').'</td>';
2038 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
2039 if (getDolGlobalString('TAKEPOS_SHOW_HT')) {
2040 print '<td class="linecolht right nowraponall">';
2041 print '<span class="opacitymedium small">' . $langs->trans('TotalHTShort') . '</span><br>';
2042 // In phone version only show when it is invoice page
2043 if (empty($mobilepage) || $mobilepage == "invoice") {
2044 print '<span id="linecolht-span-total" style="font-size:1.3em; font-weight: bold;">' . price($invoice->total_ht, 1, '', 1, -1, -1, $conf->currency) . '</span>';
2045 if (isModEnabled('multicurrency') && !empty($_SESSION["takeposcustomercurrency"]) && $conf->currency != $_SESSION["takeposcustomercurrency"]) {
2046 //Only show customer currency if multicurrency module is enabled, if currency selected and if this currency selected is not the same as main currency
2047 include_once DOL_DOCUMENT_ROOT . '/multicurrency/class/multicurrency.class.php';
2048 $multicurrency = new MultiCurrency($db);
2049 $multicurrency->fetch(0, $_SESSION["takeposcustomercurrency"]);
2050 print '<br><span id="linecolht-span-total" style="font-size:0.9em; font-style:italic;">(' . price($invoice->total_ht * $multicurrency->rate->rate) . ' ' . $_SESSION["takeposcustomercurrency"] . ')</span>';
2051 }
2052 }
2053 print '</td>';
2054 }
2055 print '<td class="linecolht right nowraponall">';
2056 print '<span class="opacitymedium small">'.$langs->trans('TotalTTCShort').'</span><br>';
2057 // In phone version only show when it is invoice page
2058 if (empty($mobilepage) || $mobilepage == "invoice") {
2059 print '<span id="linecolht-span-total" style="font-size:1.3em; font-weight: bold;">'.price($invoice->total_ttc, 1, '', 1, -1, -1, $conf->currency).'</span>';
2060 if (isModEnabled('multicurrency') && !empty($_SESSION["takeposcustomercurrency"]) && $conf->currency != $_SESSION["takeposcustomercurrency"]) {
2061 //Only show customer currency if multicurrency module is enabled, if currency selected and if this currency selected is not the same as main currency
2062 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
2063 $multicurrency = new MultiCurrency($db);
2064 $multicurrency->fetch(0, $_SESSION["takeposcustomercurrency"]);
2065 print '<br><span id="linecolht-span-total" style="font-size:0.9em; font-style:italic;">('.price($invoice->total_ttc * $multicurrency->rate->rate).' '.$_SESSION["takeposcustomercurrency"].')</span>';
2066 }
2067 }
2068 print '</td>';
2069} elseif ($mobilepage == "invoice") {
2070 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
2071}
2072if (!$usediv) {
2073 print "</tr>\n";
2074}
2075
2076if (!empty($_SESSION["basiclayout"]) && $_SESSION["basiclayout"] == 1) {
2077 if ($mobilepage == "cats") {
2078 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2079 $categorie = new Categorie($db);
2080 $categories = $categorie->get_full_arbo('product');
2081 $htmlforlines = '';
2082 foreach ($categories as $row) {
2083 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
2084 $htmlforlines .= '<div class="leftcat"';
2085 } else {
2086 $htmlforlines .= '<tr class="drag drop oddeven posinvoiceline"';
2087 }
2088 $htmlforlines .= ' onclick="LoadProducts('.$row['id'].');">';
2089 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
2090 $htmlforlines .= '<img class="imgwrapper" width="33%" src="'.DOL_URL_ROOT.'/takepos/public/auto_order.php?genimg=cat&query=cat&id='.$row['id'].'"><br>';
2091 } else {
2092 $htmlforlines .= '<td class="left">';
2093 }
2094 $htmlforlines .= $row['label'];
2095 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
2096 $htmlforlines .= '</div>'."\n";
2097 } else {
2098 $htmlforlines .= '</td></tr>'."\n";
2099 }
2100 }
2101 print $htmlforlines;
2102 }
2103
2104 if ($mobilepage == "products") {
2105 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2106 $object = new Categorie($db);
2107 $catid = GETPOSTINT('catid');
2108 $result = $object->fetch($catid);
2109 $prods = $object->getObjectsInCateg("product");
2111 '@phan-var-force Product[] $prods';
2112 $htmlforlines = '';
2113 foreach ($prods as $row) {
2114 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
2115 $htmlforlines .= '<div class="leftcat"';
2116 } else {
2117 $htmlforlines .= '<tr class="drag drop oddeven posinvoiceline"';
2118 }
2119 $htmlforlines .= ' onclick="AddProduct(\''.$place.'\', '.$row->id.')"';
2120 $htmlforlines .= '>';
2121 if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
2122 $htmlforlines .= '<img class="imgwrapper" width="33%" src="'.DOL_URL_ROOT.'/takepos/public/auto_order.php?genimg=pro&query=pro&id='.$row->id.'"><br>';
2123 $htmlforlines .= $row->label.' '.price($row->price_ttc, 1, $langs, 1, -1, -1, $conf->currency);
2124 $htmlforlines .= '</div>'."\n";
2125 } else {
2126 $htmlforlines .= '<td class="left">';
2127 $htmlforlines .= $row->label;
2128 $htmlforlines .= '<div class="right">'.price($row->price_ttc, 1, $langs, 1, -1, -1, $conf->currency).'</div>';
2129 $htmlforlines .= '</td>';
2130 $htmlforlines .= '</tr>'."\n";
2131 }
2132 }
2133 print $htmlforlines;
2134 }
2135
2136 if ($mobilepage == "places") {
2137 $sql = "SELECT rowid, entity, label, leftpos, toppos, floor FROM ".MAIN_DB_PREFIX."takepos_floor_tables";
2138 $resql = $db->query($sql);
2139
2140 $rows = array();
2141 $htmlforlines = '';
2142 while ($row = $db->fetch_array($resql)) {
2143 $rows[] = $row;
2144 $htmlforlines .= '<tr class="drag drop oddeven posinvoiceline';
2145 $htmlforlines .= '" onclick="LoadPlace(\''.$row['label'].'\')">';
2146 $htmlforlines .= '<td class="left">';
2147 $htmlforlines .= $row['label'];
2148 $htmlforlines .= '</td>';
2149 $htmlforlines .= '</tr>'."\n";
2150 }
2151 print $htmlforlines;
2152 }
2153}
2154
2155if ($placeid > 0) {
2156 //In Phone basic layout hide some content depends situation
2157 if (!empty($_SESSION["basiclayout"]) && $_SESSION["basiclayout"] == 1 && $mobilepage != "invoice" && $action != "order") {
2158 return;
2159 }
2160
2161 // Loop on each lines on invoice
2162 if (is_array($invoice->lines) && count($invoice->lines)) {
2163 print '<!-- invoice.php show lines of invoices -->'."\n";
2164 $tmplines = array_reverse($invoice->lines);
2165 $htmlsupplements = array();
2166 foreach ($tmplines as $line) {
2167 if ($line->fk_parent_line != false) {
2168 if (!isset($htmlsupplements[$line->fk_parent_line])) {
2169 $htmlsupplements[$line->fk_parent_line] = '';
2170 }
2171 $htmlsupplements[$line->fk_parent_line] .= '<tr class="drag drop oddeven posinvoiceline';
2172 if ($line->special_code == "4") {
2173 $htmlsupplements[$line->fk_parent_line] .= ' order';
2174 }
2175 $htmlsupplements[$line->fk_parent_line] .= '" id="'.$line->id.'"';
2176 if ($line->special_code == "4") {
2177 $htmlsupplements[$line->fk_parent_line] .= ' title="'.dol_escape_htmltag($langs->trans("AlreadyPrinted")).'"';
2178 }
2179 $htmlsupplements[$line->fk_parent_line] .= '>';
2180 $htmlsupplements[$line->fk_parent_line] .= '<td class="left">';
2181 $htmlsupplements[$line->fk_parent_line] .= img_picto('', 'rightarrow.png');
2182 if ($line->product_label) {
2183 $htmlsupplements[$line->fk_parent_line] .= $line->product_label;
2184 }
2185 if ($line->product_label && $line->desc) {
2186 $htmlsupplements[$line->fk_parent_line] .= '<br>';
2187 }
2188 if ($line->product_label != $line->desc) {
2189 $firstline = dolGetFirstLineOfText($line->desc);
2190 if ($firstline != $line->desc) {
2191 $htmlsupplements[$line->fk_parent_line] .= $form->textwithpicto(dolGetFirstLineOfText($line->desc), $line->desc);
2192 } else {
2193 $htmlsupplements[$line->fk_parent_line] .= $line->desc;
2194 }
2195 }
2196 $htmlsupplements[$line->fk_parent_line] .= '</td>';
2197
2198 // complete line by hook
2199 $parameters = array('line' => $line);
2200 $reshook = $hookmanager->executeHooks('completeTakePosInvoiceParentLine', $parameters, $invoice, $action); // Note that $action and $object may have been modified by some hooks
2201 if ($reshook < 0) {
2202 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2203 }
2204 $htmlsupplements[$line->fk_parent_line] .= $hookmanager->resPrint;
2205
2206 if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) {
2207 $htmlsupplements[$line->fk_parent_line] .= '<td class="right">'.vatrate(price2num($line->remise_percent), true).'</td>';
2208 $htmlsupplements[$line->fk_parent_line] .= '<td class="right">'.$line->qty.'</td>';
2209 $htmlsupplements[$line->fk_parent_line] .= '<td class="right">'.price($line->total_ttc).'</td>';
2210 }
2211 $htmlsupplements[$line->fk_parent_line] .= '</tr>'."\n";
2212 continue;
2213 }
2214 $htmlforlines = '';
2215
2216 $htmlforlines .= '<tr class="drag drop oddeven posinvoiceline';
2217 if ($line->special_code == "4") {
2218 $htmlforlines .= ' order';
2219 }
2220 $htmlforlines .= '" id="'.$line->id.'"';
2221 if ($line->special_code == "4") {
2222 $htmlforlines .= ' title="'.dol_escape_htmltag($langs->trans("AlreadyPrinted")).'"';
2223 }
2224 $htmlforlines .= '>';
2225 $htmlforlines .= '<td class="left">';
2226 if (!empty($_SESSION["basiclayout"]) && $_SESSION["basiclayout"] == 1) {
2227 $htmlforlines .= '<span class="phoneqty">'.$line->qty."</span> x ";
2228 }
2229 if (isset($line->product_type)) {
2230 if (empty($line->product_type)) {
2231 $htmlforlines .= img_object('', 'product').' ';
2232 } else {
2233 $htmlforlines .= img_object('', 'service').' ';
2234 }
2235 }
2236 $tooltiptext = '';
2237 if (!getDolGlobalString('TAKEPOS_SHOW_N_FIRST_LINES')) {
2238 if ($line->product_ref) {
2239 $tooltiptext .= '<b>'.$langs->trans("Ref").'</b> : '.$line->product_ref.'<br>';
2240 $tooltiptext .= '<b>'.$langs->trans("Label").'</b> : '.$line->product_label.'<br>';
2241 if (!empty($line->batch)) {
2242 $tooltiptext .= '<br><b>'.$langs->trans("LotSerial").'</b> : '.$line->batch.'<br>';
2243 }
2244 if (!empty($line->fk_warehouse)) {
2245 $tooltiptext .= '<b>'.$langs->trans("Warehouse").'</b> : '.$line->fk_warehouse.'<br>';
2246 }
2247 if ($line->product_label != $line->desc) {
2248 if ($line->desc) {
2249 $tooltiptext .= '<br>';
2250 }
2251 $tooltiptext .= $line->desc;
2252 }
2253 }
2254 if (getDolGlobalInt('TAKEPOS_SHOW_PRODUCT_REFERENCE') == 1) {
2255 $htmlforlines .= $form->textwithpicto($line->product_label ? '<b>' . $line->product_ref . '</b> - ' . $line->product_label : dolGetFirstLineOfText($line->desc, 1), $tooltiptext);
2256 } elseif (getDolGlobalInt('TAKEPOS_SHOW_PRODUCT_REFERENCE') == 2) {
2257 $htmlforlines .= $form->textwithpicto($line->product_ref ? '<b>'.$line->product_ref.'<b>' : dolGetFirstLineOfText($line->desc, 1), $tooltiptext);
2258 } else {
2259 $htmlforlines .= $form->textwithpicto($line->product_label ? $line->product_label : ($line->product_ref ? $line->product_ref : dolGetFirstLineOfText($line->desc, 1)), $tooltiptext);
2260 }
2261 } else {
2262 if ($line->product_ref) {
2263 $tooltiptext .= '<b>'.$langs->trans("Ref").'</b> : '.$line->product_ref.'<br>';
2264 $tooltiptext .= '<b>'.$langs->trans("Label").'</b> : '.$line->product_label.'<br>';
2265 }
2266 if (!empty($line->batch)) {
2267 $tooltiptext .= '<br><b>'.$langs->trans("LotSerial").'</b> : '.$line->batch.'<br>';
2268 }
2269 if (!empty($line->fk_warehouse)) {
2270 $tooltiptext .= '<b>'.$langs->trans("Warehouse").'</b> : '.$line->fk_warehouse.'<br>';
2271 }
2272
2273 if ($line->product_label) {
2274 $htmlforlines .= $line->product_label;
2275 }
2276 if ($line->product_label != $line->desc) {
2277 if ($line->product_label && $line->desc) {
2278 $htmlforlines .= '<br>';
2279 }
2280 $firstline = dolGetFirstLineOfText($line->desc, getDolGlobalInt('TAKEPOS_SHOW_N_FIRST_LINES'));
2281 if ($firstline != $line->desc) {
2282 $htmlforlines .= $form->textwithpicto(dolGetFirstLineOfText($line->desc), $line->desc);
2283 } else {
2284 $htmlforlines .= $line->desc;
2285 }
2286 }
2287 }
2288 if (!empty($line->array_options['options_order_notes'])) {
2289 $htmlforlines .= "<br>(".$line->array_options['options_order_notes'].")";
2290 }
2291 if (!empty($_SESSION["basiclayout"]) && $_SESSION["basiclayout"] == 1) {
2292 $htmlforlines .= '</td><td class="right phonetable"><button type="button" onclick="SetQty(place, '.$line->rowid.', '.($line->qty - 1).');" class="publicphonebutton2 phonered">-</button>&nbsp;&nbsp;<button type="button" onclick="SetQty(place, '.$line->rowid.', '.($line->qty + 1).');" class="publicphonebutton2 phonegreen">+</button>';
2293 }
2294 if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) {
2295 // Set the content of tooltip
2296 $moreinfo = '';
2297 $moreinfo .= $langs->trans("VATRate").': '.price($line->tva_tx).' %<br>';
2298 $moreinfo .= $langs->trans("UnitPrice").': '.price($line->subprice).'<br>';
2299 $moreinfo .= '<br>';
2300 $moreinfo .= $langs->transcountry("TotalHT", $mysoc->country_code).': '.price($line->total_ht);
2301 if ($line->vat_src_code) {
2302 $moreinfo .= '<br>'.$langs->trans("VATCode").': '.$line->vat_src_code;
2303 }
2304 $moreinfo .= '<br>'.$langs->trans("TotalVAT").': '.price($line->total_tva);
2305 if ($mysoc->useLocalTax(1, 1, $mysoc)) {
2306 $moreinfo .= '<br>'.$langs->transcountry("TotalLT1", $mysoc->country_code).': '.price($line->total_localtax1);
2307 }
2308 if ($mysoc->useLocalTax(2, 1, $mysoc)) {
2309 $moreinfo .= '<br>'.$langs->transcountry("TotalLT2", $mysoc->country_code).': '.price($line->total_localtax2);
2310 }
2311 $moreinfo .= '<hr>';
2312 $moreinfo .= $langs->transcountry("TotalTTC", $mysoc->country_code).': '.price($line->total_ttc);
2313 //$moreinfo .= $langs->trans("TotalHT").': '.$line->total_ht;
2314 if ($line->date_start || $line->date_end) {
2315 $htmlforlines .= '<br><div class="clearboth nowraponall">'.get_date_range($line->date_start, $line->date_end).'</div>';
2316 }
2317 $htmlforlines .= '</td>';
2318
2319 // complete line by hook
2320 $parameters = array('line' => $line);
2321 $reshook = $hookmanager->executeHooks('completeTakePosInvoiceLine', $parameters, $invoice, $action); // Note that $action and $object may have been modified by some hooks
2322 if ($reshook < 0) {
2323 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2324 }
2325 $htmlforlines .= $hookmanager->resPrint;
2326
2327 if (getDolGlobalInt("TAKEPOS_SHOW_SUBPRICE")) {
2328 $htmlforlines .= '<td class="right">'.price($line->subprice).'</td>';
2329 }
2330 $htmlforlines .= '<td class="right">'.vatrate(price2num($line->remise_percent), true).'</td>';
2331 // nowraponall keeps the qty and its '(stock)' block on one line: on narrow layouts
2332 // the native display otherwise wraps over three lines inside the qty column.
2333 $htmlforlines .= '<td class="right nowraponall">';
2334 $htmlforlines .= $line->qty;
2335 if (isModEnabled('stock') && $user->hasRight('stock', 'mouvement', 'lire')) {
2336 $constantforkey = 'CASHDESK_ID_WAREHOUSE'.$_SESSION["takeposterminal"];
2337 if (getDolGlobalString($constantforkey) && $line->fk_product > 0 && !getDolGlobalString('TAKEPOS_HIDE_STOCK_ON_LINE')) {
2338 $productChildrenNb = 0;
2339 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
2340 if (empty($line->product) || !($line->product->id > 0)) {
2341 $line->fetch_product();
2342 }
2343 if (!empty($line->product)) {
2344 $productChildrenNb = $line->product->hasFatherOrChild(1);
2345 }
2346 }
2347 if ($productChildrenNb == 0) {
2348 $sql = "SELECT e.rowid, e.ref, e.lieu, e.fk_parent, e.statut, ps.reel, ps.rowid as product_stock_id, p.pmp";
2349 $sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,";
2350 $sql .= " ".MAIN_DB_PREFIX."product_stock as ps";
2351 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = ps.fk_product";
2352 $sql .= " WHERE ps.reel != 0";
2353 $sql .= " AND ps.fk_entrepot = ".((int) getDolGlobalString($constantforkey));
2354 $sql .= " AND e.entity IN (".getEntity('stock').")";
2355 $sql .= " AND ps.fk_product = ".((int) $line->fk_product);
2356 $resql = $db->query($sql);
2357 if ($resql) {
2358 $stock_real = 0;
2359 $obj = $db->fetch_object($resql);
2360 if ($obj) {
2361 $stock_real = price2num($obj->reel, 'MS');
2362 }
2363 $htmlforlines .= '&nbsp; ';
2364 $htmlforlines .= '<span class="opacitylow" title="'.$langs->trans("Stock").' '.price($stock_real, 1, '', 1, 0).'">';
2365 $htmlforlines .= '(';
2366 if ($line->qty && $line->qty > $stock_real) {
2367 $htmlforlines .= '<span style="color: var(--amountremaintopaycolor)">';
2368 }
2369 $htmlforlines .= img_picto('', 'stock', 'class="pictofixedwidth"').price($stock_real, 1, '', 1, 0);
2370 if ($line->qty && $line->qty > $stock_real) {
2371 $htmlforlines .= "</span>";
2372 }
2373 $htmlforlines .= ')';
2374 $htmlforlines .= '</span>';
2375 } else {
2376 dol_print_error($db);
2377 }
2378 }
2379 }
2380 }
2381
2382 $htmlforlines .= '</td>';
2383 if (getDolGlobalInt('TAKEPOS_SHOW_HT')) {
2384 $htmlforlines .= '<td class="right classfortooltip" title="'.$moreinfo.'">';
2385 $htmlforlines .= price($line->total_ht, 1, '', 1, -1, -1, $conf->currency);
2386 if (isModEnabled('multicurrency') && !empty($_SESSION["takeposcustomercurrency"]) && $conf->currency != $_SESSION["takeposcustomercurrency"]) {
2387 //Only show customer currency if multicurrency module is enabled, if currency selected and if this currency selected is not the same as main currency
2388 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
2389 $multicurrency = new MultiCurrency($db);
2390 $multicurrency->fetch(0, $_SESSION["takeposcustomercurrency"]);
2391 $htmlforlines .= '<br><span id="linecolht-span-total" style="font-size:0.9em; font-style:italic;">('.price($line->total_ht * $multicurrency->rate->rate).' '.$_SESSION["takeposcustomercurrency"].')</span>';
2392 }
2393 $htmlforlines .= '</td>';
2394 }
2395 $htmlforlines .= '<td class="right classfortooltip" title="'.$moreinfo.'">';
2396 $htmlforlines .= price($line->total_ttc, 1, '', 1, -1, -1, $conf->currency);
2397 if (isModEnabled('multicurrency') && !empty($_SESSION["takeposcustomercurrency"]) && $conf->currency != $_SESSION["takeposcustomercurrency"]) {
2398 //Only show customer currency if multicurrency module is enabled, if currency selected and if this currency selected is not the same as main currency
2399 include_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
2400 $multicurrency = new MultiCurrency($db);
2401 $multicurrency->fetch(0, $_SESSION["takeposcustomercurrency"]);
2402 $htmlforlines .= '<br><span id="linecolht-span-total" style="font-size:0.9em; font-style:italic;">('.price($line->total_ttc * $multicurrency->rate->rate).' '.$_SESSION["takeposcustomercurrency"].')</span>';
2403 }
2404 $htmlforlines .= '</td>';
2405 }
2406 $htmlforlines .= '</tr>'."\n";
2407 $htmlforlines .= empty($htmlsupplements[$line->id]) ? '' : $htmlsupplements[$line->id];
2408
2409 print $htmlforlines;
2410 }
2411 } else {
2412 print '<tr class="drag drop oddeven"><td class="left"><span class="opacitymedium">'.$langs->trans("Empty").'</span></td><td></td>';
2413 if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) {
2414 print '<td></td><td></td>';
2415 if (getDolGlobalString('TAKEPOS_SHOW_HT')) {
2416 print '<td></td>';
2417 }
2418 }
2419 print '</tr>';
2420 }
2421} else { // No invoice generated yet
2422 print '<tr class="drag drop oddeven"><td class="left"><span class="opacitymedium">'.$langs->trans("Empty").'</span></td><td></td>';
2423 if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) {
2424 print '<td></td><td></td>';
2425 if (getDolGlobalString('TAKEPOS_SHOW_HT')) {
2426 print '<td></td>';
2427 }
2428 }
2429 print '</tr>';
2430}
2431
2432if ($usediv) {
2433 print '</div>';
2434} else {
2435 print '</table>';
2436}
2437
2438if ($action == "search") {
2439 print '<center>
2440 <input type="text" id="search" class="input-nobottom" name="search" onkeyup="Search2(\'\', null);" style="width: 80%; font-size: 150%;" placeholder="'.dol_escape_htmltag($langs->trans('Search')).'">
2441 </center>';
2442}
2443
2444print '</div>';
2445
2446// llxFooter
2447if (defined('INCLUDE_PHONEPAGE_FROM_PUBLIC_PAGE')) {
2448 print '</body></html>';
2449}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$object ref
Definition info.php:90
Class to manage members of a foundation.
const STATUS_EXCLUDED
Excluded.
Class to manage categories.
Class to manage contact/addresses.
Class to manage warehouses.
const STATUS_CLOSED
Warehouse closed, inactive.
Class to manage invoices.
const STATUS_DRAFT
Draft status.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage generation of HTML components Only common components must be here.
Class to manage stock movements.
Class Currency.
Class to manage payments of customer invoices.
Class to manage products or services.
Manage record for batch number management.
Class to manage third parties objects (customers, suppliers, prospects...)
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
global $mysoc
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.
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...
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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).
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
treeview li table
No Email.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formatted messages to output (Used to show messages on html output).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formatted error messages to output (Used to show messages on html output).
top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $disableforlogin=0, $disablenofollow=0, $disablenoindex=0)
Output html header of a page.
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.
if(empty( $takeposterminal)) fail($message)
Abort invoice creation with a given error message.
Definition invoice.php:124