dolibarr 25.0.0-alpha
myobject.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2023-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) ---Replace with your own copyright and developer email---
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
26// Put here all includes required by your class file
27require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
28//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
29//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
30
35{
39 public $module = 'mymodule';
40
44 public $element = 'myobject';
45
50 public $TRIGGER_PREFIX = 'MYMODULE_MYOBJECT'; // Will be used to build trigger keys 'MYMODULE_MYOBJECT_MODIFY', ...
51
55 public $table_element = 'mymodule_myobject';
56
60 //public $element_for_permission = 'mymodule';
61
65 public $picto = 'fa-file';
66
70 public $isextrafieldmanaged = 0;
71
76 public $ismultientitymanaged = 0;
77
78
79 const STATUS_DRAFT = 0;
80 const STATUS_VALIDATED = 1;
81 const STATUS_CANCELED = 9;
82
127 // BEGIN MODULEBUILDER PROPERTIES
132 public $fields = array(
133 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => 'Id', 'lang' => 'mymodule@mymodule'),
134 'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 15, 'notnull' => 1, 'visible' => 1, 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'validate' => 1, 'comment' => 'Reference of object', 'lang' => 'mymodule@mymodule'),
135 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 20, 'index' => 1),
136 'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'position' => 30, 'notnull' => 0, 'visible' => 1, 'alwayseditable' => 1, 'searchall' => 1, 'css' => 'minwidth300', 'cssview' => 'wordbreak', 'help' => 'Help text', 'showoncombobox' => 2, 'validate' => 1, 'lang' => 'mymodule@mymodule'),
137 'amount' => array('type' => 'price', 'label' => 'Amount', 'enabled' => 1, 'position' => 40, 'notnull' => 0, 'visible' => 1, 'default' => 'null', 'isameasure' => 1, 'help' => 'Help text for amount', 'validate' => 1, 'lang' => 'mymodule@mymodule'),
138 'qty' => array('type' => 'real', 'label' => 'Qty', 'enabled' => 1, 'position' => 45, 'notnull' => 0, 'visible' => 1, 'default' => '0', 'isameasure' => 1, 'css' => 'maxwidth75imp', 'help' => 'Help text for quantity', 'validate' => 1, 'lang' => 'mymodule@mymodule'),
139 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))', 'label' => 'ThirdParty', 'picto' => 'company', 'enabled' => 'isModEnabled("societe")', 'position' => 50, 'notnull' => -1, 'visible' => 1, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150', 'help' => 'OrganizationEventLinkToThirdParty', 'validate' => 1, 'lang' => 'mymodule@mymodule'),
140 'fk_project' => array('type' => 'integer:Project:projet/class/project.class.php:1', 'label' => 'Project', 'picto' => 'project', 'enabled' => 'isModEnabled("project")', 'position' => 52, 'notnull' => -1, 'visible' => -1, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'csslist' => 'tdoverflowmax150', 'validate' => 1, 'lang' => 'mymodule@mymodule'),
141 'description' => array('type' => 'text', 'label' => 'Description', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => 3, 'validate' => 1, 'lang' => 'mymodule@mymodule'),
142 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'position' => 61, 'notnull' => 0, 'visible' => 0, 'cssview' => 'wordbreak', 'validate' => 1, 'lang' => 'mymodule@mymodule'),
143 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 62, 'notnull' => 0, 'visible' => 0, 'cssview' => 'wordbreak', 'validate' => 1, 'lang' => 'mymodule@mymodule'),
144 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'visible' => -2, 'lang' => 'mymodule@mymodule'),
145 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 501, 'notnull' => 0, 'visible' => -2, 'lang' => 'mymodule@mymodule'),
146 'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'picto' => 'user', 'enabled' => 1, 'position' => 510, 'notnull' => 1, 'visible' => -2, 'foreignkey' => '0', 'csslist' => 'tdoverflowmax150', 'lang' => 'mymodule@mymodule'),
147 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'picto' => 'user', 'enabled' => 1, 'position' => 511, 'notnull' => -1, 'visible' => -2, 'csslist' => 'tdoverflowmax150', 'lang' => 'mymodule@mymodule'),
148 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'position' => 600, 'notnull' => 0, 'visible' => 0, 'lang' => 'mymodule@mymodule'),
149 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 1000, 'notnull' => -1, 'visible' => -2, 'lang' => 'mymodule@mymodule'),
150 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'position' => 1010, 'notnull' => -1, 'visible' => 0, 'lang' => 'mymodule@mymodule'),
151 'status' => array('type' => 'integer', 'label' => 'Status', 'enabled' => 1, 'position' => 2000, 'notnull' => 1, 'visible' => 1, 'index' => 1, 'arrayofkeyval' => array(0 => 'Draft', '1' => 'Validated', 9 => 'Canceled'), 'validate' => 1, 'lang' => 'mymodule@mymodule'),
152 );
153
157 public $rowid;
158
162 public $ref;
163
167 public $label;
168
172 public $amount;
173
177 public $socid; // both socid and fk_soc are used
181 public $fk_soc; // both socid and fk_soc are used
182
186 public $status;
187
191 public $fk_user_creat;
192
196 public $fk_user_modif;
197
201 public $last_main_doc;
202
206 public $import_key;
207 // END MODULEBUILDER PROPERTIES
208
209 //BEGIN MODULEBUILDER LINES
210 // If this object has a subtable with lines
211
215 public $table_element_line = 'mymodule_myobjectline';
216
220 public $fk_element = 'fk_myobject';
221
225 public $class_element_line = 'MyObjectline';
226
230 protected $childtables = array('mychildtable' => array('name' => 'MyObject', 'fk_element' => 'fk_myobject'));
231
237 protected $childtablesoncascade = array('mymodule_myobjectdet');
238
242 public $lines = array();
243
244 //END MODULEBUILDER LINES
245
251 public function __construct(DoliDB $db)
252 {
253 global $langs;
254
255 $this->db = $db;
256
257 if (!getDolGlobalInt('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid']) && !empty($this->fields['ref'])) {
258 $this->fields['rowid']['visible'] = 0;
259 }
260 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
261 $this->fields['entity']['enabled'] = 0;
262 }
263
264 // Example to show how to set values of fields definition dynamically
265 /*if ($user->hasRight('mymodule', 'myobject', 'read')) {
266 $this->fields['myfield']['visible'] = 1;
267 $this->fields['myfield']['noteditable'] = 0;
268 }*/
269
270 // Unset fields that are disabled
271 foreach ($this->fields as $key => $val) {
272 if (isset($val['enabled']) && empty($val['enabled'])) {
273 unset($this->fields[$key]);
274 }
275 }
276
277 // Translate some data of arrayofkeyval
278 if (is_object($langs)) {
279 foreach ($this->fields as $key => $val) {
280 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
281 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
282 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
283 }
284 }
285 }
286 }
287 }
288
296 public function create(User $user, $notrigger = 0)
297 {
298 $result = $this->createCommon($user, $notrigger);
299
300 // uncomment lines below if you want to validate object after creation
301 // if ($result > 0) {
302 // $this->fetch($this->id); // needed to retrieve some fields (ie date_creation for masked ref)
303 // $resultupdate = $this->validate($user, $notrigger);
304 // if ($resultupdate < 0) { return $resultupdate; }
305 // }
306
307 return $result;
308 }
309
317 public function createFromClone(User $user, $fromid)
318 {
319 global $langs, $extrafields;
320 $error = 0;
321
322 dol_syslog(__METHOD__, LOG_DEBUG);
323
324 $object = new self($this->db);
325
326 $this->db->begin();
327
328 // Load source object
329 $result = $object->fetchCommon($fromid);
330 //BEGIN MODULEBUILDER LINES
331 if ($result > 0 && !empty($object->table_element_line)) {
332 $object->fetchLines();
333 }
334
335 // get lines so they will be clone
336 foreach ($this->lines as $line) {
337 $line->fetch_optionals();
338 }
339 //END MODULEBUILDER LINES
340 // Reset some properties
341 unset($object->id);
342 unset($object->fk_user_creat);
343 unset($object->import_key);
344
345 // Clear fields
346 if (property_exists($object, 'ref')) {
347 $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default'];
348 }
349 if (property_exists($object, 'label')) {
350 $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
351 }
352 if (property_exists($object, 'status')) {
353 $object->status = self::STATUS_DRAFT;
354 }
355 if (property_exists($object, 'date_creation')) {
356 $object->date_creation = dol_now();
357 }
358 if (property_exists($object, 'date_modification')) {
359 $object->date_modification = null;
360 }
361 // ...
362 // Clear extrafields that are unique
363 if (is_array($object->array_options) && count($object->array_options) > 0) {
364 $extrafields->fetch_name_optionals_label($this->table_element);
365 foreach ($object->array_options as $key => $option) {
366 $shortkey = preg_replace('/options_/', '', $key);
367 if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
368 //var_dump($key);
369 //var_dump($clonedObj->array_options[$key]); exit;
370 unset($object->array_options[$key]);
371 }
372 }
373 }
374
375 // Create clone
376 $object->context['createfromclone'] = 'createfromclone';
377 $result = $object->createCommon($user);
378 if ($result < 0) {
379 $error++;
381 }
382
383 if (!$error) {
384 // copy internal contacts
385 if ($this->copy_linked_contact($object, 'internal') < 0) {
386 $error++;
387 }
388 }
389
390 if (!$error) {
391 // copy external contacts if same company
392 if (!empty($object->socid) && ((property_exists($this, 'fk_soc') && ($this->fk_soc == $object->socid)) || (property_exists($this, 'socid') && ($this->socid == $object->socid)))) { // @phpstan-ignore-line
393 if ($this->copy_linked_contact($object, 'external') < 0) {
394 $error++;
395 }
396 }
397 }
398
399 unset($object->context['createfromclone']);
400
401 // End
402 if (!$error) {
403 $this->db->commit();
404 return $object;
405 } else {
406 $this->db->rollback();
407 return -1;
408 }
409 }
410
420 public function fetch($id, $ref = null, $noextrafields = 0, $nolines = 0)
421 {
422 $result = $this->fetchCommon($id, $ref, '', $noextrafields);
423 //BEGIN MODULEBUILDER LINES
424 if ($result > 0 && !empty($this->table_element_line) && empty($nolines)) {
425 $this->fetchLines($noextrafields);
426 }
427 //END MODULEBUILDER LINES
428 return $result;
429 }
430
437 public function fetchLines($noextrafields = 0)
438 {
439 $this->lines = array();
440
441 $result = $this->fetchLinesCommon('', $noextrafields);
442 return $result;
443 }
444 //END MODULEBUILDER LINES
459 public function fetchAll($sortorder = '', $sortfield = '', $limit = 1000, $offset = 0, string $filter = '', $filtermode = 'AND')
460 {
461 dol_syslog(__METHOD__, LOG_DEBUG);
462
463 $records = array();
464
465 $sql = "SELECT ";
466 $sql .= $this->getFieldList('t');
467 $sql .= " FROM ".$this->db->prefix().$this->table_element." as t";
468 if (!empty($this->isextrafieldmanaged) && $this->isextrafieldmanaged == 1) {
469 $sql .= " LEFT JOIN ".$this->db->prefix().$this->table_element."_extrafields as te ON te.fk_object = t.rowid";
470 }
471 if (!empty($this->ismultientitymanaged) && (int) $this->ismultientitymanaged == 1) {
472 $sql .= " WHERE t.entity IN (".getEntity($this->element).")";
473 } elseif (preg_match('/^\w+@\w+$/', (string) $this->ismultientitymanaged)) {
474 $tmparray = explode('@', (string) $this->ismultientitymanaged);
475 $sql .= " LEFT JOIN ".$this->db->prefix().$this->db->sanitize($tmparray[1])." as pt ON t.".$this->db->sanitize($tmparray[0])." = pt.rowid";
476 $sql .= " WHERE pt.entity IN (".getEntity($this->element).")";
477 } else {
478 $sql .= " WHERE 1 = 1";
479 }
480
481 // Manage filter
482 $errormessage = '';
483 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
484 if ($errormessage) {
485 $this->errors[] = $errormessage;
486 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
487 return -1;
488 }
489
490 if (!empty($sortfield)) {
491 $sql .= $this->db->order($sortfield, $sortorder);
492 }
493 if (!empty($limit)) {
494 $sql .= $this->db->plimit($limit, $offset);
495 }
496
497 $resql = $this->db->query($sql);
498 if ($resql) {
499 $num = $this->db->num_rows($resql);
500 $i = 0;
501 while ($i < ($limit ? min($limit, $num) : $num)) {
502 $obj = $this->db->fetch_object($resql);
503
504 $record = new self($this->db);
505 $record->setVarsFromFetchObj($obj);
506
507 if (!empty($record->isextrafieldmanaged)) {
508 $record->fetch_optionals();
509 }
510
511 $records[$record->id] = $record;
512
513 $i++;
514 }
515 $this->db->free($resql);
516
517 return $records;
518 } else {
519 $this->errors[] = 'Error '.$this->db->lasterror();
520 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
521
522 return -1;
523 }
524 }
525
533 public function update(User $user, $notrigger = 0)
534 {
535 return $this->updateCommon($user, $notrigger);
536 }
537
545 public function delete(User $user, $notrigger = 0)
546 {
547 return $this->deleteCommon($user, $notrigger);
548 //return $this->deleteCommon($user, $notrigger, 1);
549 }
550
551 //BEGIN MODULEBUILDER LINES
560 public function deleteLine(User $user, $idline, $notrigger = 0)
561 {
562 if ($this->status < 0) {
563 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
564 return -2;
565 }
566
567 return $this->deleteLineCommon($user, $idline, $notrigger);
568 }
569 //END MODULEBUILDER LINES
570
571
579 public function validate($user, $notrigger = 0)
580 {
581 global $conf;
582
583 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
584
585 $error = 0;
586
587 // Protection
588 if ($this->status == self::STATUS_VALIDATED) {
589 dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
590 return 0;
591 }
592
593 /* if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule', 'myobject', 'write'))
594 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule', 'myobject_advance', 'validate')))
595 {
596 $this->error='NotEnoughPermissions';
597 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
598 return -1;
599 }*/
600
601 $now = dol_now();
602
603 $this->db->begin();
604
605 // Define new ref
606 if (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
607 $num = $this->getNextNumRef();
608 } else {
609 $num = (string) $this->ref;
610 }
611 $this->newref = $num;
612
613 if (!empty($num)) {
614 // Validate
615 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
616 $sql .= " SET ";
617 if (!empty($this->fields['ref'])) {
618 $sql .= " ref = '".$this->db->escape($num)."',";
619 }
620 $sql .= " status = ".self::STATUS_VALIDATED;
621 if (!empty($this->fields['date_validation'])) {
622 $sql .= ", date_validation = '".$this->db->idate($now)."'";
623 }
624 if (!empty($this->fields['fk_user_valid'])) {
625 $sql .= ", fk_user_valid = ".((int) $user->id);
626 }
627 $sql .= " WHERE rowid = ".((int) $this->id);
628
629 dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
630 $resql = $this->db->query($sql);
631 if (!$resql) {
632 dol_print_error($this->db);
633 $this->error = $this->db->lasterror();
634 $error++;
635 }
636
637 if (!$error && !$notrigger) {
638 // Call trigger
639 $result = $this->call_trigger('MYMODULE_MYOBJECT_VALIDATE', $user);
640 if ($result < 0) {
641 $error++;
642 }
643 // End call triggers
644 }
645 }
646
647 if (!$error) {
648 $this->oldref = $this->ref;
649
650 // Rename directory if dir was a temporary ref
651 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
652 // Now we rename also files into index
653 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'myobject/".$this->db->escape($this->newref)."'";
654 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'myobject/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
655 $resql = $this->db->query($sql);
656 if (!$resql) {
657 $error++;
658 $this->error = $this->db->lasterror();
659 }
660 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'myobject/".$this->db->escape($this->newref)."'";
661 $sql .= " WHERE filepath = 'myobject/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
662 $resql = $this->db->query($sql);
663 if (!$resql) {
664 $error++;
665 $this->error = $this->db->lasterror();
666 }
667
668 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
669 $oldref = dol_sanitizeFileName($this->ref);
670 $newref = dol_sanitizeFileName($num);
671 $dirsource = $conf->mymodule->dir_output.'/myobject/'.$oldref;
672 $dirdest = $conf->mymodule->dir_output.'/myobject/'.$newref;
673 if (!$error && file_exists($dirsource)) {
674 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
675
676 if (@rename($dirsource, $dirdest)) {
677 dol_syslog("Rename ok");
678 // Rename docs starting with $oldref with $newref
679 $listoffiles = dol_dir_list($conf->mymodule->dir_output.'/myobject/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
680 foreach ($listoffiles as $fileentry) {
681 $dirsource = $fileentry['name'];
682 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
683 $dirsource = $fileentry['path'].'/'.$dirsource;
684 $dirdest = $fileentry['path'].'/'.$dirdest;
685 @rename($dirsource, $dirdest);
686 }
687 }
688 }
689 }
690 }
691
692 // Set new ref and current status
693 if (!$error) {
694 $this->ref = $num;
695 $this->status = self::STATUS_VALIDATED;
696 }
697
698 if (!$error) {
699 $this->db->commit();
700 return 1;
701 } else {
702 $this->db->rollback();
703 return -1;
704 }
705 }
706
707
715 public function setDraft($user, $notrigger = 0)
716 {
717 // Protection
718 if ($this->status <= self::STATUS_DRAFT) {
719 return 0;
720 }
721
722 /* if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule','write'))
723 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule','mymodule_advance','validate'))))
724 {
725 $this->error='Permission denied';
726 return -1;
727 }*/
728
729 return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'MYMODULE_MYOBJECT_UNVALIDATE');
730 }
731
739 public function cancel($user, $notrigger = 0)
740 {
741 // Protection
742 if ($this->status != self::STATUS_VALIDATED) {
743 return 0;
744 }
745
746 /* if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule','write'))
747 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule','mymodule_advance','validate'))))
748 {
749 $this->error='Permission denied';
750 return -1;
751 }*/
752
753 return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'MYMODULE_MYOBJECT_CANCEL');
754 }
755
763 public function reopen($user, $notrigger = 0)
764 {
765 // Protection
766 if ($this->status == self::STATUS_VALIDATED) {
767 return 0;
768 }
769
770 /*if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule','write'))
771 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('mymodule','mymodule_advance','validate'))))
772 {
773 $this->error='Permission denied';
774 return -1;
775 }*/
776
777 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'MYMODULE_MYOBJECT_REOPEN');
778 }
779
787 public function getTooltipContentArray($params)
788 {
789 global $langs;
790
791 $datas = [];
792
793 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
794 return ['optimize' => $langs->trans("ShowMyObject")];
795 }
796 $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("MyObject").'</u>';
797 if (isset($this->status)) {
798 $datas['picto'] .= ' '.$this->getLibStatut(5);
799 }
800 if (property_exists($this, 'ref')) {
801 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
802 }
803 if (property_exists($this, 'label')) {
804 $datas['label'] = '<br>'.$langs->trans('Label').':</b> '.$this->label;
805 }
806
807 return $datas;
808 }
809
820 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
821 {
822 global $conf, $langs, $hookmanager;
823
824 if (!empty($conf->dol_no_mouse_hover)) {
825 $notooltip = 1; // Force disable tooltips
826 }
827
828 $result = '';
829 $params = [
830 'id' => (string) $this->id,
831 'objecttype' => $this->element.($this->module ? '@'.$this->module : ''),
832 'option' => $option,
833 ];
834 $classfortooltip = 'classfortooltip';
835 $dataparams = '';
836 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
837 $classfortooltip = 'classforajaxtooltip';
838 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
839 $label = '';
840 } else {
841 $label = implode($this->getTooltipContentArray($params));
842 }
843
844 $baseurl = dol_buildpath('/mymodule/myobject_card.php', 1);
845 $query = ['id' => $this->id];
846 if ($option !== 'nolink') {
847 // Add param to save lastsearch_values or not
848 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
849 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
850 $add_save_lastsearch_values = 1;
851 }
852 if ($add_save_lastsearch_values) {
853 $query = array_merge($query, ['save_lastsearch_values' => 1]);
854 }
855 }
856 $url = dolBuildUrl($baseurl, $query);
857
858 $linkclose = '';
859 if (empty($notooltip)) {
860 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
861 $label = $langs->trans("ShowMyObject");
862 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
863 }
864 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
865 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
866 } else {
867 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
868 }
869
870 if ($option == 'nolink') {
871 $linkstart = '<span';
872 } else {
873 $linkstart = '<a href="'.$url.'"';
874 }
875 $linkstart .= $linkclose.'>';
876 if ($option == 'nolink') {
877 $linkend = '</span>';
878 } else {
879 $linkend = '</a>';
880 }
881
882 $result .= $linkstart;
883
884 if (empty($this->showphoto_on_popup)) {
885 if ($withpicto) {
886 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
887 }
888 } else {
889 if ($withpicto) {
890 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
891
892 list($class, $module) = explode('@', $this->picto);
893 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref);
894 $filearray = dol_dir_list($upload_dir, "files");
895 $filename = $filearray[0]['name'];
896 if (!empty($filename)) {
897 $pospoint = strpos($filearray[0]['name'], '.');
898
899 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint);
900 if (!getDolGlobalString(strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS')) {
901 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$module.'" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div></div>';
902 } else {
903 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div>';
904 }
905
906 $result .= '</div>';
907 } else {
908 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
909 }
910 }
911 }
912
913 if ($withpicto != 2) {
914 $result .= $this->ref;
915 }
916
917 $result .= $linkend;
918 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
919
920 global $action, $hookmanager;
921 $hookmanager->initHooks(array($this->element.'dao'));
922 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
923 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
924 if ($reshook > 0) {
925 $result = $hookmanager->resPrint;
926 } else {
927 $result .= $hookmanager->resPrint;
928 }
929
930 return $result;
931 }
932
940 public function getKanbanView($option = '', $arraydata = null)
941 {
942 global $conf, $langs;
943
944 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
945
946 $return = '<div class="box-flex-item box-flex-grow-zero">';
947 $return .= '<div class="info-box info-box-sm">';
948 $return .= '<span class="info-box-icon bg-infobox-action">';
949 $return .= img_picto('', $this->picto);
950 $return .= '</span>';
951 $return .= '<div class="info-box-content">';
952 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
953 if ($selected >= 0) {
954 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
955 }
956 if (property_exists($this, 'label')) {
957 $return .= ' <div class="inline-block opacitymedium valignmiddle tdoverflowmax100">'.$this->label.'</div>';
958 }
959 if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) {
960 $return .= '<br><div class="info-box-ref tdoverflowmax150">'.$this->thirdparty->getNomUrl(1).'</div>';
961 }
962 if (property_exists($this, 'amount')) {
963 $return .= '<br>';
964 $return .= '<span class="info-box-label amount">'.price($this->amount, 0, $langs, 1, -1, -1, $conf->currency).'</span>';
965 }
966 if (method_exists($this, 'getLibStatut')) {
967 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
968 }
969 $return .= '</div>';
970 $return .= '</div>';
971 $return .= '</div>';
972
973 return $return;
974 }
975
982 public function getLabelStatus($mode = 0)
983 {
984 return $this->LibStatut($this->status, $mode);
985 }
986
993 public function getLibStatut($mode = 0)
994 {
995 return $this->LibStatut($this->status, $mode);
996 }
997
998 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1006 public function LibStatut($status, $mode = 0)
1007 {
1008 // phpcs:enable
1009 if (is_null($status)) {
1010 return '';
1011 }
1012
1013 $paramsBadge = array('badgeParams' => array('attr' => array(
1014 'data-status-element' => $this->element,
1015 'data-status' => (int) $status
1016 )));
1017
1018
1019 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
1020 global $langs;
1021 //$langs->load("mymodule@mymodule");
1022 // Build status labels from the 'status' field arrayofkeyval so that the badge (LibStatut),
1023 // the list filter and the select all show the same, configurable labels.
1024 if (!empty($this->fields['status']['arrayofkeyval']) && is_array($this->fields['status']['arrayofkeyval'])) {
1025 foreach ($this->fields['status']['arrayofkeyval'] as $statuskey => $statuslabel) {
1026 $this->labelStatus[$statuskey] = $langs->transnoentitiesnoconv($statuslabel);
1027 $this->labelStatusShort[$statuskey] = $langs->transnoentitiesnoconv($statuslabel);
1028 }
1029 }
1030 }
1031
1032 $statusType = 'status'.$status;
1033 //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
1034 if ($status == self::STATUS_CANCELED) {
1035 $statusType = 'status6';
1036 }
1037
1038 return dolGetStatus($this->labelStatus[$status] ?? '', $this->labelStatusShort[$status] ?? '', '', $statusType, $mode, '', $paramsBadge);
1039 }
1040
1047 public function info($id)
1048 {
1049 $sql = "SELECT t.rowid, t.date_creation as datec";
1050 if (!empty($this->isextrafieldmanaged) && $this->isextrafieldmanaged == 1) {
1051 $sql .= ", GREATEST(t.tms, COALESCE(te.tms, t.tms)) as datem";
1052 } else {
1053 $sql .= ", t.tms as datem";
1054 }
1055 if (!empty($this->fields['date_validation'])) {
1056 $sql .= ", t.date_validation as datev";
1057 }
1058 if (!empty($this->fields['fk_user_creat'])) {
1059 $sql .= ", t.fk_user_creat";
1060 }
1061 if (!empty($this->fields['fk_user_modif'])) {
1062 $sql .= ", t.fk_user_modif";
1063 }
1064 if (!empty($this->fields['fk_user_valid'])) {
1065 $sql .= ", t.fk_user_valid";
1066 }
1067 $sql .= " FROM ".$this->db->prefix().$this->table_element." as t";
1068 if (!empty($this->isextrafieldmanaged) && $this->isextrafieldmanaged == 1) {
1069 $sql .= " LEFT JOIN ".$this->db->prefix().$this->table_element."_extrafields as te ON te.fk_object = t.rowid";
1070 }
1071 $sql .= " WHERE t.rowid = ".((int) $id);
1072
1073 $result = $this->db->query($sql);
1074 if ($result) {
1075 if ($this->db->num_rows($result)) {
1076 $obj = $this->db->fetch_object($result);
1077
1078 $this->id = $obj->rowid;
1079
1080 if (!empty($this->fields['fk_user_creat'])) {
1081 $this->user_creation_id = $obj->fk_user_creat;
1082 }
1083 if (!empty($this->fields['fk_user_modif'])) {
1084 $this->user_modification_id = $obj->fk_user_modif;
1085 }
1086 if (!empty($this->fields['fk_user_valid'])) {
1087 $this->user_validation_id = $obj->fk_user_valid;
1088 }
1089 $this->date_creation = $this->db->jdate($obj->datec);
1090 $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1091 if (!empty($obj->datev)) {
1092 $this->date_validation = empty($obj->datev) ? '' : $this->db->jdate($obj->datev);
1093 }
1094 }
1095
1096 $this->db->free($result);
1097 } else {
1098 dol_print_error($this->db);
1099 }
1100 }
1101
1108 public function initAsSpecimen()
1109 {
1110 // Set here init that are not commonf fields
1111 // $this->property1 = ...
1112 // $this->property2 = ...
1113
1114 return $this->initAsSpecimenCommon();
1115 }
1116
1117 //BEGIN MODULEBUILDER LINES
1123 public function getLinesArray()
1124 {
1125 $this->lines = array();
1126
1127 $objectline = new MyObjectLine($this->db);
1128 $result = $objectline->fetchAll('ASC', 'position', 0, 0, '(fk_myobject:=:'.((int) $this->id).')');
1129
1130 if (is_numeric($result)) {
1131 $this->setErrorsFromObject($objectline);
1132 return $result;
1133 } else {
1135 $this->lines = $result;
1136 return $this->lines;
1137 }
1138 }
1139 //END MODULEBUILDER LINES
1140
1146 public function getNextNumRef()
1147 {
1148 global $langs, $conf;
1149 $langs->load("mymodule@mymodule");
1150
1151 if (!getDolGlobalString('MYMODULE_MYOBJECT_ADDON')) {
1152 $conf->global->MYMODULE_MYOBJECT_ADDON = 'mod_myobject_standard';
1153 }
1154
1155 if (getDolGlobalString('MYMODULE_MYOBJECT_ADDON')) {
1156 $mybool = false;
1157
1158 $file = getDolGlobalString('MYMODULE_MYOBJECT_ADDON').".php";
1159 $classname = getDolGlobalString('MYMODULE_MYOBJECT_ADDON');
1160
1161 // Include file with class
1162 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1163 foreach ($dirmodels as $reldir) {
1164 $dir = dol_buildpath($reldir."core/modules/mymodule/");
1165
1166 // Load file with numbering class (if found)
1167 $mybool = $mybool || @include_once $dir.$file;
1168 }
1169
1170 if (!$mybool) {
1171 dol_print_error(null, "Failed to include file ".$file);
1172 return '';
1173 }
1174
1175 if (class_exists($classname)) {
1176 $obj = new $classname();
1177 '@phan-var-force ModeleNumRefMyObject $obj';
1178 $numref = $obj->getNextValue($this);
1179
1180 if ($numref != '' && $numref != '-1') {
1181 return $numref;
1182 } else {
1183 $this->error = $obj->error;
1184 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1185 return "";
1186 }
1187 } else {
1188 print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname;
1189 return "";
1190 }
1191 } else {
1192 print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
1193 return "";
1194 }
1195 }
1196
1208 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1209 {
1210 global $langs;
1211
1212 $result = 0;
1213 $includedocgeneration = 0;
1214
1215 $langs->load("mymodule@mymodule");
1216
1217 if (!dol_strlen($modele)) {
1218 if (!empty($this->model_pdf)) {
1219 $modele = $this->model_pdf;
1220 } else {
1221 $modele = getDolGlobalString('MYOBJECT_ADDON_PDF', 'standard_myobject');
1222 }
1223 }
1224
1225 $modelpath = "core/modules/mymodule/doc/";
1226
1227 if ($includedocgeneration && !empty($modele)) {
1228 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1229 }
1230
1231 return $result;
1232 }
1233
1243 public function validateField($fields, $fieldKey, $fieldValue)
1244 {
1245 // Add your own validation rules here.
1246 // ...
1247
1248 return parent::validateField($fields, $fieldKey, $fieldValue);
1249 }
1250
1258 public function doScheduledJob()
1259 {
1260 //global $conf, $langs;
1261
1262 //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlogfile.log';
1263
1264 $error = 0;
1265 $this->output = '';
1266 $this->error = '';
1267
1268 dol_syslog(__METHOD__." start", LOG_INFO);
1269
1270 //$now = dol_now();
1271
1272 $this->db->begin();
1273
1274 // ...
1275
1276 $this->db->commit();
1277
1278 dol_syslog(__METHOD__." end", LOG_INFO);
1279
1280 return $error;
1281 }
1282}
1283
1284
1285require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
1286//BEGIN MODULEBUILDER LINES
1291{
1292 // To complete with content of an object MyObjectLine
1293 // We should have a field rowid, fk_myobject and position
1294
1299 public $parent_element = ''; // Example: '' or 'myobject'
1300
1305 public $fk_parent_attribute = ''; // Example: '' or 'fk_myobject'
1306
1310 public $isextrafieldmanaged = 0;
1311
1316 public $ismultientitymanaged = 0;
1317
1318
1324 public function __construct(DoliDB $db)
1325 {
1326 $this->db = $db;
1327 }
1328}
1329//END MODULEBUILDER LINES
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$object ref
Definition info.php:90
deleteLineCommon(User $user, $idline, $notrigger=0)
Delete a line of object in database.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
setErrorsFromObject($object)
setErrorsFromObject
createCommon(User $user, $notrigger=0)
Create object in the database.
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
updateCommon(User $user, $notrigger=0)
Update object into database.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
fetchLinesCommon($morewhere='', $noextrafields=0)
Load object in memory from the database.
fetchCommon($id, $ref=null, $morewhere='', $noextrafields=0)
Load object in memory from the database.
deleteCommon(User $user, $notrigger=0, $forcechilddeletion=0)
Delete object in database.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage Dolibarr database access.
Class for MyObject.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
createFromClone(User $user, $fromid)
Clone an object into another one.
getKanbanView($option='', $arraydata=null)
Return a thumb for kanban views.
getLibStatut($mode=0)
Return the label of the status.
create(User $user, $notrigger=0)
Create object into database.
update(User $user, $notrigger=0)
Update object into database.
getTooltipContentArray($params)
getTooltipContentArray
reopen($user, $notrigger=0)
Set back to validated status.
setDraft($user, $notrigger=0)
Set draft status.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
LibStatut($status, $mode=0)
Return the label of a given status.
validateField($fields, $fieldKey, $fieldValue)
Return validation test result for a field.
getNextNumRef()
Returns the reference to the following non used object depending on the active numbering module.
fetchAll($sortorder='', $sortfield='', $limit=1000, $offset=0, string $filter='', $filtermode='AND')
Load list of objects in memory from the database.
initAsSpecimen()
Initialize object with example values Id must be 0 if object instance is a specimen.
__construct(DoliDB $db)
Constructor.
deleteLine(User $user, $idline, $notrigger=0)
Delete a line of object in database.
cancel($user, $notrigger=0)
Set cancel status.
fetchLines($noextrafields=0)
Load object lines in memory from the database.
info($id)
Load the info information in the object.
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
fetch($id, $ref=null, $noextrafields=0, $nolines=0)
Load object in memory from the database.
getLabelStatus($mode=0)
Return the label of the status.
validate($user, $notrigger=0)
Validate object.
$fields
'type' field format: 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortf...
getLinesArray()
Create an array of lines.
Class MyObjectLine.
$parent_element
To overload.
$fk_parent_attribute
To overload.
__construct(DoliDB $db)
Constructor.
Class to manage Dolibarr users.
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
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_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:65
dol_now($mode='gmt')
Return date for now.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
print $langs trans('Date')." left Ref Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right Paid right PaymentTypeShortLIQ right SELECT p pos_change as p datep as p p num_paiement as f pf amount as amount
Definition receipt.php:489