dolibarr 25.0.0-alpha
asset.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2018-2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
4 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024 Jose MARTINEZ <jose.martinez@pichinov.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
28require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
29require_once DOL_DOCUMENT_ROOT.'/core/lib/asset.lib.php';
30
34class Asset extends CommonObject
35{
39 public $module = 'asset';
40
44 public $element = 'asset';
45
49 public $table_element = 'asset';
50
54 public $picto = 'asset';
55
60 public $fk_element = 'fk_asset';
61
68 protected $childtablesoncascade = array(
69 '@AssetAccountancyCodes:/asset/class/assetaccountancycodes.class.php:fk_asset',
70 '@AssetAccountancyCodesFiscal:/asset/class/assetaccountancycodesfiscal.class.php:fk_asset'
71 );
72
73
74 const STATUS_DRAFT = 0; // Draft
75 const STATUS_VALIDATED = 1; // In progress
76 const STATUS_DISPOSED = 9; // Disposed
77
109 public $fields = array(
110 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
111 'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'default' => '1', 'index' => 1, 'position' => 5),
112 'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 20, 'notnull' => 1, 'visible' => 4, 'noteditable' => 0, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'validate' => 1, 'comment' => "Reference of object", "css" => "maxwidth150"),
113 'label' => array('type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'visible' => 1, 'searchall' => 1, 'csslist' => 'tdoverflowmax125', 'css' => 'minwidth300', 'cssview' => 'wordbreak', 'showoncombobox' => 2, 'validate' => 1,),
114 'fk_asset_model' => array('type' => 'integer:AssetModel:asset/class/assetmodel.class.php:1:((status:=:1) and (entity:IN:__SHARED_ENTITIES__))', 'label' => 'AssetModel', 'enabled' => 1, 'position' => 40, 'notnull' => 0, 'visible' => 1, 'index' => 1, 'validate' => 1, 'csslist' => 'tdoverflowmax75', 'css' => 'maxwidth300'),
115 'qty' => array('type' => 'real', 'label' => 'Qty', 'enabled' => 1, 'position' => 50, 'notnull' => 1, 'visible' => 0, 'default' => '1', 'isameasure' => 1, 'css' => 'maxwidth75imp', 'validate' => 1,),
116 'acquisition_type' => array('type' => 'smallint', 'label' => 'AssetAcquisitionType', 'enabled' => 1, 'position' => 60, 'notnull' => 1, 'visible' => 1, 'arrayofkeyval' => array(0 => 'AssetAcquisitionTypeNew', 1 => 'AssetAcquisitionTypeOccasion'), 'validate' => 1, 'csslist' => 'tdoverflowmax75'),
117 'asset_type' => array('type' => 'smallint', 'label' => 'AssetType', 'enabled' => 1, 'position' => 70, 'notnull' => 1, 'visible' => 1, 'arrayofkeyval' => array(0 => 'AssetTypeIntangible', 1 => 'AssetTypeTangible', 2 => 'AssetTypeInProgress', 3 => 'AssetTypeFinancial'), 'validate' => 1, 'csslist' => 'tdoverflowmax75'),
118 'not_depreciated' => array('type' => 'boolean', 'label' => 'AssetNotDepreciated', 'enabled' => 1, 'position' => 80, 'notnull' => 0, 'default' => '0', 'visible' => 1, 'validate' => 1, 'csslist' => 'maxwidth50 tdoverflowmax50', 'css' => 'maxwidth50imp'),
119 'date_acquisition' => array('type' => 'date', 'label' => 'AssetDateAcquisition', 'enabled' => 1, 'position' => 90, 'notnull' => 1, 'visible' => 1,),
120 'date_start' => array('type' => 'date', 'label' => 'AssetDateStart', 'enabled' => 1, 'position' => 100, 'notnull' => 0, 'visible' => -1,),
121 'acquisition_value_ht' => array('type' => 'price', 'label' => 'AssetAcquisitionValueHT', 'enabled' => 1, 'position' => 110, 'notnull' => 1, 'visible' => 1, 'isameasure' => 1, 'validate' => 1,),
122 'recovered_vat' => array('type' => 'price', 'label' => 'AssetRecoveredVAT', 'enabled' => 1, 'position' => 120, 'notnull' => 0, 'visible' => 1, 'isameasure' => 1, 'validate' => 1,),
123 'reversal_date' => array('type' => 'date', 'label' => 'AssetReversalDate', 'enabled' => 1, 'position' => 130, 'notnull' => 0, 'visible' => 1,),
124 'reversal_amount_ht' => array('type' => 'price', 'label' => 'AssetReversalAmountHT', 'enabled' => 1, 'position' => 140, 'notnull' => 0, 'visible' => 1, 'isameasure' => 1, 'validate' => 1, 'csslist' => 'maxwidth50'),
125 'disposal_date' => array('type' => 'date', 'label' => 'AssetDisposalDate', 'enabled' => 1, 'position' => 200, 'notnull' => 0, 'visible' => -2,),
126 'disposal_amount_ht' => array('type' => 'price', 'label' => 'AssetDisposalAmount', 'enabled' => 1, 'position' => 210, 'notnull' => 0, 'visible' => -2, 'default' => '0', 'isameasure' => 1, 'validate' => 1,),
127 'fk_disposal_type' => array('type' => 'sellist:c_asset_disposal_type:label:rowid::active=1', 'label' => 'AssetDisposalType', 'enabled' => 1, 'position' => 220, 'notnull' => 0, 'visible' => -2, 'index' => 1, 'validate' => 1,),
128 'disposal_depreciated' => array('type' => 'boolean', 'label' => 'AssetDisposalDepreciated', 'enabled' => 1, 'position' => 230, 'notnull' => 0, 'default' => '0', 'visible' => -2, 'validate' => 1,),
129 'disposal_subject_to_vat' => array('type' => 'boolean', 'label' => 'AssetDisposalSubjectToVat', 'enabled' => 1, 'position' => 240, 'notnull' => 0, 'default' => '0', 'visible' => -2, 'validate' => 1,),
130 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'position' => 300, 'notnull' => 0, 'visible' => 0, 'validate' => 1,),
131 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 301, 'notnull' => 0, 'visible' => 0, 'validate' => 1,),
132 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'visible' => -2,),
133 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 501, 'notnull' => 0, 'visible' => -2,),
134 'date_valid' => array('type' => 'datetime', 'label' => 'DateValidation', 'enabled' => 1, 'visible' => -2, 'position' => 502, 'notnull' => 0,),
135 'fk_user_creat' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'enabled' => 1, 'position' => 510, 'notnull' => 1, 'visible' => -2, 'foreignkey' => 'user.rowid',),
136 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'position' => 511, 'notnull' => -1, 'visible' => -2,),
137 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'picto' => 'user', 'enabled' => 1, 'visible' => -2, 'position' => 512, 'notnull' => 0, 'csslist' => 'tdoverflowmax100'),
138 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'position' => 600, 'notnull' => 0, 'visible' => 0,),
139 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 1000, 'notnull' => -1, 'visible' => -2,),
140 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'position' => 1010, 'notnull' => -1, 'visible' => 0,),
141 'status' => array('type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'default' => '0', 'visible' => 2, 'index' => 1, 'arrayofkeyval' => array(0 => 'Draft', 1 => 'Validated', 9 => 'Canceled'), 'validate' => 1,),
142 );
143
147 public $rowid;
151 public $ref;
155 public $label;
159 public $fk_asset_model;
163 public $reversal_amount_ht;
167 public $acquisition_value_ht;
171 public $recovered_vat;
175 public $reversal_date;
179 public $date_acquisition;
183 public $date_start;
187 public $qty;
191 public $acquisition_type;
195 public $asset_type;
199 public $not_depreciated;
203 public $disposal_date;
207 public $disposal_amount_ht;
211 public $fk_disposal_type;
215 public $disposal_depreciated;
219 public $disposal_subject_to_vat;
223 public $supplier_invoice_id;
227 public $note_public;
231 public $note_private;
235 public $date_valid;
239 public $fk_user_creat;
243 public $fk_user_modif;
247 public $fk_user_valid;
251 public $last_main_doc;
252
256 public $import_key;
260 public $model_pdf;
261
265 public $status;
266
270 public $asset_depreciation_options;
271
275 public $asset_accountancy_codes;
276
280 public $depreciation_lines = array();
281
282
288 public function __construct(DoliDB $db)
289 {
290 global $langs;
291
292 $this->db = $db;
293
294 $this->ismultientitymanaged = 1;
295 $this->isextrafieldmanaged = 1;
296
297 if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
298 $this->fields['rowid']['visible'] = 0;
299 }
300 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
301 $this->fields['entity']['enabled'] = 0;
302 }
303
304 // Unset fields that are disabled
305 foreach ($this->fields as $key => $val) {
306 if (isset($val['enabled']) && empty($val['enabled'])) {
307 unset($this->fields[$key]);
308 }
309 }
310
311 // Translate some data of arrayofkeyval
312 if (is_object($langs)) {
313 foreach ($this->fields as $key => $val) {
314 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
315 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
316 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
317 }
318 }
319 }
320 }
321 }
322
330 public function create(User $user, $notrigger = 0)
331 {
332 if (!isset($this->date_start) || $this->date_start === "") {
333 $this->date_start = $this->date_acquisition;
334 }
335
336 $this->db->begin();
337
338 $result = $result_create = $this->createCommon($user, $notrigger);
339 if ($result > 0 && $this->fk_asset_model > 0) {
340 $result = $this->setDataFromAssetModel($user, $notrigger);
341 }
342 if ($result > 0) {
343 if ($this->supplier_invoice_id > 0) {
344 $this->add_object_linked('invoice_supplier', $this->supplier_invoice_id);
345 }
346 }
347
348 if ($result < 0) {
349 $this->db->rollback();
350 } else {
351 $this->db->commit();
352 }
353
354 return $result > 0 ? $result_create : $result;
355 }
356
364 public function createFromClone(User $user, $fromid)
365 {
366 //global $langs, $extrafields;
367 //$error = 0;
368
369 dol_syslog(__METHOD__, LOG_DEBUG);
370
371 // $object = new self($this->db);
372 //
373 // $this->db->begin();
374 //
375 // // Load source object
376 // $result = $object->fetchCommon($fromid);
377 //
378 //
379 // // Reset some properties
380 // unset($object->id);
381 // unset($object->fk_user_creat);
382 // unset($object->import_key);
383 //
384 // // Clear fields
385 // if (property_exists($object, 'ref')) {
386 // $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default'];
387 // }
388 // if (property_exists($object, 'label')) {
389 // $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default'];
390 // }
391 // if (property_exists($object, 'status')) {
392 // $object->status = self::STATUS_DRAFT;
393 // }
394 // if (property_exists($object, 'date_creation')) {
395 // $object->date_creation = dol_now();
396 // }
397 // if (property_exists($object, 'date_modification')) {
398 // $object->date_modification = null;
399 // }
400 // // ...
401 // // Clear extrafields that are unique
402 // if (is_array($object->array_options) && count($object->array_options) > 0) {
403 // $extrafields->fetch_name_optionals_label($this->table_element);
404 // foreach ($object->array_options as $key => $option) {
405 // $shortkey = preg_replace('/options_/', '', $key);
406 // if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
407 // //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
408 // unset($object->array_options[$key]);
409 // }
410 // }
411 // }
412 //
413 // // Create clone
414 // $object->context['createfromclone'] = 'createfromclone';
415 // $result = $object->createCommon($user);
416 // if ($result < 0) {
417 // $error++;
418 // $this->error = $object->error;
419 // $this->errors = $object->errors;
420 // }
421 //
422 // if (!$error) {
423 // // copy internal contacts
424 // if ($this->copy_linked_contact($object, 'internal') < 0) {
425 // $error++;
426 // }
427 // }
428 //
429 // if (!$error) {
430 // // copy external contacts if same company
431 // if (property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) {
432 // if ($this->copy_linked_contact($object, 'external') < 0) {
433 // $error++;
434 // }
435 // }
436 // }
437 //
438 // unset($object->context['createfromclone']);
439 //
440 // // End
441 // if (!$error) {
442 // $this->db->commit();
443 // return $object;
444 // } else {
445 // $this->db->rollback();
446 // return -1;
447 // }
448
449 return -1;
450 }
451
459 public function fetch($id, $ref = null)
460 {
461 $result = $this->fetchCommon($id, $ref);
462 if ($result > 0) {
463 $res = $this->hasDepreciationLinesInBookkeeping();
464 if ($res < 0) {
465 return -1;
466 } elseif ($res > 0) {
467 $this->fields['date_acquisition']['noteditable'] = 1;
468 $this->fields['date_start']['noteditable'] = 1;
469 $this->fields['acquisition_value_ht']['noteditable'] = 1;
470 $this->fields['recovered_vat']['noteditable'] = 1;
471 $this->fields['reversal_date']['noteditable'] = 1;
472 $this->fields['reversal_amount_ht']['noteditable'] = 1;
473 }
474 }
475
476 return $result;
477 }
478
479
492 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
493 {
494 dol_syslog(__METHOD__, LOG_DEBUG);
495
496 $records = array();
497
498 $sql = "SELECT ";
499 $sql .= $this->getFieldList('t');
500 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
501 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
502 $sql .= " WHERE t.entity IN (".getEntity($this->element).")";
503 } else {
504 $sql .= " WHERE 1 = 1";
505 }
506
507 // Manage filter
508 $errormessage = '';
509 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
510 if ($errormessage) {
511 $this->errors[] = $errormessage;
512 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
513
514 return -1;
515 }
516
517 if (!empty($sortfield)) {
518 $sql .= $this->db->order($sortfield, $sortorder);
519 }
520 if (!empty($limit)) {
521 $sql .= $this->db->plimit($limit, $offset);
522 }
523
524 $resql = $this->db->query($sql);
525 if ($resql) {
526 $num = $this->db->num_rows($resql);
527 $i = 0;
528 while ($i < ($limit ? min($limit, $num) : $num)) {
529 $obj = $this->db->fetch_object($resql);
530
531 $record = new self($this->db);
532 $record->setVarsFromFetchObj($obj);
533
534 $records[$record->id] = $record;
535
536 $i++;
537 }
538 $this->db->free($resql);
539
540 return $records;
541 } else {
542 $this->errors[] = 'Error '.$this->db->lasterror();
543 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
544
545 return -1;
546 }
547 }
548
556 public function update(User $user, $notrigger = 0)
557 {
558 if (!isset($this->date_start) || $this->date_start === "") {
559 $this->date_start = $this->date_acquisition;
560 }
561
562 $this->db->begin();
563
564 $result = $this->updateCommon($user, $notrigger);
565 if ($result > 0 && $this->fk_asset_model > 0 && $this->fk_asset_model != $this->oldcopy->fk_asset_model) {
566 $result = $this->setDataFromAssetModel($user, $notrigger);
567 }
568 if ($result > 0 && is_object($this->oldcopy) && (
569 $this->date_start != $this->oldcopy->date_start ||
570 $this->acquisition_value_ht != $this->oldcopy->acquisition_value_ht ||
571 $this->reversal_date != $this->oldcopy->reversal_date ||
572 $this->reversal_amount_ht != $this->oldcopy->reversal_amount_ht ||
573 ($this->fk_asset_model > 0 && $this->fk_asset_model != $this->oldcopy->fk_asset_model)
574 )) {
575 $result = $this->calculationDepreciation();
576 }
577
578 if ($result < 0) {
579 $this->db->rollback();
580 } else {
581 $this->db->commit();
582 }
583
584 return $result;
585 }
586
594 public function delete(User $user, $notrigger = 0)
595 {
596 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'asset_depreciation_codes_economic WHERE fk_asset = '.((int) $this->id);
597 $this->db->query($sql);
598
599 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'asset_depreciation_codes_fiscal WHERE fk_asset = '.((int) $this->id);
600 $this->db->query($sql);
601
602 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'asset_depreciation_options_fiscal WHERE fk_asset = '.((int) $this->id);
603 $this->db->query($sql);
604
605 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'asset_depreciation_options_economic WHERE fk_asset = '.((int) $this->id);
606 $this->db->query($sql);
607
608 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.'asset_depreciation WHERE fk_asset = '.((int) $this->id);
609 $this->db->query($sql);
610
611 return $this->deleteCommon($user, $notrigger);
612 //return $this->deleteCommon($user, $notrigger, 1);
613 }
614
622 public function setDataFromAssetModel(User $user, $notrigger = 0)
623 {
624 global $langs;
625 $langs->load('assets');
626
627 // Clean parameters
628 $this->id = $this->id > 0 ? $this->id : 0;
629 $this->fk_asset_model = $this->fk_asset_model > 0 ? $this->fk_asset_model : 0;
630
631 // Check parameters
632 $error = 0;
633 if (empty($this->id)) {
634 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Asset") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
635 $error++;
636 }
637 if (empty($this->fk_asset_model)) {
638 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("AssetModel") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
639 $error++;
640 }
641 if ($error) {
642 return -1;
643 }
644
645 $this->db->begin();
646
647 // Get depreciation options
648 //---------------------------
649 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetdepreciationoptions.class.php';
650 $options_model = new AssetDepreciationOptions($this->db);
651 $result = $options_model->fetchDeprecationOptions(0, $this->fk_asset_model);
652 if ($result < 0) {
653 $this->error = $options_model->error;
654 $this->errors = $options_model->errors;
655 $error++;
656 } elseif ($result > 0) {
657 $options = new AssetDepreciationOptions($this->db);
658 $result = $options->fetchDeprecationOptions($this->id);
659 if ($result < 0) {
660 $this->error = $options->error;
661 $this->errors = $options->errors;
662 $error++;
663 }
664
665 if (!$error) {
666 foreach ($options_model->deprecation_options as $mode_key => $fields) {
667 foreach ($fields as $field_key => $value) {
668 $options->deprecation_options[$mode_key][$field_key] = $value;
669 }
670 // 'amount_base_depreciation_ht' only exists on the asset (not on the model, see 'only_on_asset'),
671 // so it is never part of $fields above and must be computed from the asset itself, otherwise
672 // it stays at 0 and all depreciation lines are calculated on a zero base.
673 if (!array_key_exists('amount_base_depreciation_ht', $options->deprecation_options[$mode_key])) {
674 $options->deprecation_options[$mode_key]['amount_base_depreciation_ht'] = $this->reversal_amount_ht > 0 ? $this->reversal_amount_ht : $this->acquisition_value_ht;
675 }
676 }
677
678 $result = $options->updateDeprecationOptions($user, $this->id, 0, $notrigger);
679 if ($result < 0) {
680 $this->error = $options->error;
681 $this->errors = $options->errors;
682 $error++;
683 }
684 }
685 }
686
687 // Get accountancy codes
688 //---------------------------
689 if (!$error) {
690 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
691 $accountancy_codes_model = new AssetAccountancyCodes($this->db);
692 $result = $accountancy_codes_model->fetchAccountancyCodes(0, $this->fk_asset_model);
693 if ($result < 0) {
694 $this->error = $accountancy_codes_model->error;
695 $this->errors = $accountancy_codes_model->errors;
696 $error++;
697 } elseif ($result > 0) {
698 $accountancy_codes = new AssetAccountancyCodes($this->db);
699 $result = $accountancy_codes->fetchAccountancyCodes($this->id);
700 if ($result < 0) {
701 $this->error = $accountancy_codes->error;
702 $this->errors = $accountancy_codes->errors;
703 $error++;
704 }
705
706 if (!$error) {
707 foreach ($accountancy_codes_model->accountancy_codes as $mode_key => $fields) {
708 foreach ($fields as $field_key => $value) {
709 $accountancy_codes->accountancy_codes[$mode_key][$field_key] = $value;
710 }
711 }
712
713 $result = $accountancy_codes->updateAccountancyCodes($user, $this->id, 0, $notrigger);
714 if ($result < 0) {
715 $this->error = $accountancy_codes->error;
716 $this->errors = $accountancy_codes->errors;
717 $error++;
718 }
719 }
720 }
721 }
722
723 if ($error) {
724 $this->db->rollback();
725 return -1;
726 } else {
727 $this->db->commit();
728 return 1;
729 }
730 }
731
737 public function fetchDepreciationLines()
738 {
739 global $langs;
740 $langs->load('assets');
741 $this->depreciation_lines = array();
742
743 // Clean parameters
744 $this->id = $this->id > 0 ? $this->id : 0;
745
746 // Check parameters
747 $error = 0;
748 if (empty($this->id)) {
749 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Asset") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
750 $error++;
751 }
752 if ($error) {
753 return -1;
754 }
755
756 $sql = "SELECT ad.rowid, ad.depreciation_mode, ad.ref, ad.depreciation_date, ad.depreciation_ht, ad.cumulative_depreciation_ht,";
757 if (isModEnabled('accounting')) {
758 $sql .= " " . $this->db->ifsql('EXISTS (SELECT fk_docdet FROM ' . MAIN_DB_PREFIX . 'accounting_bookkeeping as ab WHERE ab.doc_type = \'asset\' AND ab.fk_docdet = ad.rowid)', '1', '0') . " AS bookkeeping";
759 } else {
760 $sql .= " 0 AS bookkeeping";
761 }
762 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation AS ad";
763 $sql .= " WHERE ad.fk_asset = " . (int) $this->id;
764 $sql .= " ORDER BY ad.depreciation_date ASC";
765
766 $resql = $this->db->query($sql);
767 if (!$resql) {
768 $this->errors[] = $langs->trans('AssetErrorFetchDepreciationLines') . ': ' . $this->db->lasterror();
769 return -1;
770 }
771
772 while ($obj = $this->db->fetch_object($resql)) {
773 if (!isset($this->depreciation_lines[$obj->depreciation_mode])) {
774 $this->depreciation_lines[$obj->depreciation_mode] = array();
775 }
776 $this->depreciation_lines[$obj->depreciation_mode][] = array(
777 'id' => $obj->rowid,
778 'ref' => $obj->ref,
779 'depreciation_date' => $this->db->jdate($obj->depreciation_date),
780 'depreciation_ht' => $obj->depreciation_ht,
781 'cumulative_depreciation_ht' => $obj->cumulative_depreciation_ht,
782 'bookkeeping' => $obj->bookkeeping,
783 );
784 }
785
786 return 1;
787 }
788
795 {
796 global $langs;
797 $langs->load('assets');
798
799 // Clean parameters
800 $this->id = $this->id > 0 ? $this->id : 0;
801
802 // Check parameters
803 $error = 0;
804 if (empty($this->id)) {
805 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Asset") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
806 $error++;
807 }
808 if ($error) {
809 return -1;
810 }
811 if (!isModEnabled('accounting')) {
812 return 0;
813 }
814
815 // Old request with 'WITH'
816 /*
817 $sql = "WITH in_accounting_bookkeeping(fk_docdet) AS (";
818 $sql .= " SELECT DISTINCT fk_docdet";
819 $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping";
820 $sql .= " WHERE doc_type = 'asset'";
821 $sql .= ")";
822 $sql .= "SELECT COUNT(*) AS has_bookkeeping";
823 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation AS ad";
824 $sql .= " LEFT JOIN in_accounting_bookkeeping as iab ON iab.fk_docdet = ad.rowid";
825 $sql .= " WHERE ad.fk_asset = " . (int) $this->id;
826 $sql .= " AND iab.fk_docdet IS NOT NULL";
827 */
828
829 $sql = "SELECT COUNT(*) AS has_bookkeeping";
830 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation AS ad";
831 $sql .= " LEFT JOIN (SELECT DISTINCT fk_docdet FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE doc_type = 'asset') AS iab ON iab.fk_docdet = ad.rowid";
832 $sql .= " WHERE ad.fk_asset = " . (int) $this->id;
833 $sql .= " AND iab.fk_docdet IS NOT NULL";
834
835 $resql = $this->db->query($sql);
836 if (!$resql) {
837 $this->errors[] = $langs->trans('AssetErrorFetchDepreciationLines') . ': ' . $this->db->lasterror();
838 return -1;
839 }
840
841 if ($obj = $this->db->fetch_object($resql)) {
842 return $obj->has_bookkeeping > 0 ? 1 : 0;
843 }
844
845 return 0;
846 }
847
860 public function addDepreciationLine($mode, $ref, $depreciation_date, $depreciation_ht, $cumulative_depreciation_ht, $accountancy_code_debit, $accountancy_code_credit)
861 {
862 global $langs;
863 $langs->load('assets');
864
865 // Clean parameters
866 $this->id = $this->id > 0 ? $this->id : 0;
867 $mode = strtolower(trim($mode));
868 $ref = trim($ref);
869 $accountancy_code_debit = trim($accountancy_code_debit);
870 $accountancy_code_credit = trim($accountancy_code_credit);
871
872 // Check parameters
873 $error = 0;
874 if (empty($this->id)) {
875 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Asset") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
876 $error++;
877 }
878 if ($error) {
879 return -1;
880 }
881
882 $sql = "INSERT INTO " . MAIN_DB_PREFIX . "asset_depreciation(fk_asset, depreciation_mode, ref, depreciation_date, depreciation_ht, cumulative_depreciation_ht, accountancy_code_debit, accountancy_code_credit)";
883 $sql .= " VALUES ( ";
884 $sql .= " " . (int) $this->id;
885 $sql .= ", '" . $this->db->escape($mode) . "'";
886 $sql .= ", '" . $this->db->escape($ref) . "'";
887 $sql .= ", '" . $this->db->idate($depreciation_date) . "'";
888 $sql .= ", " . (float) $depreciation_ht;
889 $sql .= ", " . (float) $cumulative_depreciation_ht;
890 $sql .= ", '" . $this->db->escape($accountancy_code_debit) . "'";
891 $sql .= ", '" . $this->db->escape($accountancy_code_credit) . "'";
892 $sql .= ")";
893
894 $resql = $this->db->query($sql);
895 if (!$resql) {
896 $this->errors[] = $langs->trans('AssetErrorAddDepreciationLine') . ': ' . $this->db->lasterror();
897 return -1;
898 }
899
900 return 1;
901 }
902
908 public function calculationDepreciation()
909 {
910 global $conf, $langs;
911 $langs->load('assets');
912
913 // Clean parameters
914 $this->id = $this->id > 0 ? $this->id : 0;
915
916 // Check parameters
917 $error = 0;
918 if (empty($this->id)) {
919 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Asset") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
920 $error++;
921 }
922 if ($error) {
923 return -1;
924 }
925
926 if (! empty($this->not_depreciated)) {
927 return 1;
928 }
929
930 // Get depreciation options
931 //---------------------------
932 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetdepreciationoptions.class.php';
933 $options = new AssetDepreciationOptions($this->db);
934 $result = $options->fetchDeprecationOptions($this->id);
935 if ($result < 0) {
936 $this->error = $options->error;
937 $this->errors = $options->errors;
938 return -1;
939 }
940
941 // Get accountancy codes
942 //---------------------------
943 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetaccountancycodes.class.php';
944 $accountancy_codes = new AssetAccountancyCodes($this->db);
945 $result = $accountancy_codes->fetchAccountancyCodes($this->id);
946 if ($result < 0) {
947 $this->error = $accountancy_codes->error;
948 $this->errors = $accountancy_codes->errors;
949 return -1;
950 }
951
952 $this->db->begin();
953
954 // Delete old lines
955 $modes = array();
956 foreach ($options->deprecation_options as $mode_key => $fields) {
957 $modes[$mode_key] = $this->db->escape($mode_key);
958 }
959 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "asset_depreciation";
960 $sql .= " WHERE fk_asset = " . (int) $this->id;
961 $sql .= " AND depreciation_mode NOT IN ('" . $this->db->sanitize(implode("', '", $modes)) . "')";
962
963 $resql = $this->db->query($sql);
964 if (!$resql) {
965 $this->errors[] = $langs->trans('AssetErrorClearDepreciationLines') . ': ' . $this->db->lasterror();
966 $error++;
967 }
968
969 if (!$error) {
970 // Get fiscal period
971 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
972 require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php';
973
974 // @FIXME getCurrentPeriodOfFiscalYear return the first period found. What if there is several ? And what if not closed ? And what if end date not yet defined.
975 $dates = getCurrentPeriodOfFiscalYear($this->db, $conf, $this->date_start > $this->date_acquisition ? $this->date_start : $this->date_acquisition);
976 $init_fiscal_period_start = $dates['date_start'];
977 $init_fiscal_period_end = $dates['date_end'];
978 /*
979 if (empty($init_fiscal_period_start) || empty($init_fiscal_period_end)) {
980 $pastmonthyear = $dates['pastmonthyear'];
981 $pastmonth = $dates['pastmonth'];
982 $init_fiscal_period_start = dol_get_first_day((int) $pastmonthyear, (int) $pastmonth, false);
983 $init_fiscal_period_end = dol_get_last_day((int) $pastmonthyear, (int) $pastmonth, false);
984 }
985 */
986
987 foreach ($options->deprecation_options as $mode_key => $fields) {
988 // Get last depreciation lines save in bookkeeping
989 //-----------------------------------------------------
990
991 // Old request with 'WITH'
992 /*
993 $sql = "WITH in_accounting_bookkeeping(fk_docdet) AS (";
994 $sql .= " SELECT fk_docdet";
995 $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping";
996 $sql .= " WHERE doc_type = 'asset'";
997 $sql .= ")";
998 $sql .= "SELECT ad.depreciation_date, ad.cumulative_depreciation_ht";
999 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation AS ad";
1000 $sql .= " LEFT JOIN in_accounting_bookkeeping as iab ON iab.fk_docdet = ad.rowid";
1001 $sql .= " WHERE ad.fk_asset = " . (int) $this->id;
1002 $sql .= " AND ad.depreciation_mode = '" . $this->db->escape($mode_key) . "'";
1003 $sql .= " AND iab.fk_docdet IS NOT NULL";
1004 $sql .= " ORDER BY ad.depreciation_date DESC";
1005 $sql .= " LIMIT 1";
1006 */
1007
1008 $last_depreciation_date = '';
1009 $last_cumulative_depreciation_ht = $this->reversal_amount_ht;
1010 if (isModEnabled('accounting')) {
1011 $sql = "SELECT ad.depreciation_date, ad.cumulative_depreciation_ht";
1012 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation AS ad";
1013 $sql .= " LEFT JOIN (SELECT DISTINCT fk_docdet FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE doc_type = 'asset') AS iab ON iab.fk_docdet = ad.rowid";
1014 $sql .= " WHERE ad.fk_asset = " . (int) $this->id;
1015 $sql .= " AND ad.depreciation_mode = '" . $this->db->escape($mode_key) . "'";
1016 $sql .= " AND iab.fk_docdet IS NOT NULL";
1017 $sql .= " ORDER BY ad.depreciation_date DESC";
1018 $sql .= " LIMIT 1";
1019
1020 $resql = $this->db->query($sql);
1021 if (!$resql) {
1022 $this->errors[] = $langs->trans('AssetErrorFetchMaxDepreciationDateForMode', $mode_key) . ': ' . $this->db->lasterror();
1023 $error++;
1024 break;
1025 }
1026 if ($obj = $this->db->fetch_object($resql)) {
1027 $last_depreciation_date = $this->db->jdate($obj->depreciation_date);
1028 $last_cumulative_depreciation_ht = $obj->cumulative_depreciation_ht;
1029 }
1030 }
1031
1032 // Set last cumulative depreciation
1033 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->db->sanitize($options->deprecation_options_fields[$mode_key]['table']);
1034 $sql .= " SET total_amount_last_depreciation_ht = " . ((float) (empty($last_cumulative_depreciation_ht) ? 0 : $last_cumulative_depreciation_ht));
1035 $sql .= " WHERE fk_asset = " . (int) $this->id;
1036 $resql = $this->db->query($sql);
1037 if (!$resql) {
1038 $this->errors[] = $langs->trans('AssetErrorSetLastCumulativeDepreciation') . ': ' . $this->db->lasterror();
1039 $error++;
1040 break;
1041 }
1042
1043 // Delete old lines
1044 $sql = "DELETE FROM " . MAIN_DB_PREFIX . "asset_depreciation";
1045 $sql .= " WHERE fk_asset = " . (int) $this->id;
1046 $sql .= " AND depreciation_mode = '" . $this->db->escape($mode_key) . "'";
1047 if (isModEnabled('accounting')) {
1048 $sql .= " AND NOT EXISTS (SELECT fk_docdet FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping WHERE doc_type = 'asset' AND fk_docdet = " . MAIN_DB_PREFIX . "asset_depreciation.rowid)";
1049 }
1050 if ($last_depreciation_date !== "") {
1051 $sql .= " AND ref <> ''";
1052 }
1053 $resql = $this->db->query($sql);
1054 if (!$resql) {
1055 $this->errors[] = $langs->trans('AssetErrorClearDepreciationLines') . ': ' . $this->db->lasterror();
1056 $error++;
1057 break;
1058 }
1059
1060 // Get depreciation period
1061 $depreciation_date_start = $this->date_start > $this->date_acquisition ? $this->date_start : $this->date_acquisition;
1062 $depreciation_date_end = dol_time_plus_duree(dol_time_plus_duree((int) $depreciation_date_start, (float) $fields['duration'], $fields['duration_type'] == 1 ? 'm' : ($fields['duration_type'] == 2 ? 'd' : 'y')), -1, 'd');
1063 $depreciation_amount = $fields['amount_base_depreciation_ht'];
1064 if ($fields['duration_type'] == 2) { // Daily
1065 $fiscal_period_start = $depreciation_date_start;
1066 $fiscal_period_end = $depreciation_date_start;
1067 } elseif ($fields['duration_type'] == 1) { // Monthly
1068 $date_temp = dol_getdate((int) $depreciation_date_start);
1069 $fiscal_period_start = dol_get_first_day($date_temp['year'], $date_temp['mon'], false);
1070 $fiscal_period_end = dol_get_last_day($date_temp['year'], $date_temp['mon'], false);
1071 } else { // Annually
1072 $fiscal_period_start = $init_fiscal_period_start;
1073 $fiscal_period_end = $init_fiscal_period_end;
1074 }
1075 $cumulative_depreciation_ht = (float) $last_cumulative_depreciation_ht;
1076 $depreciation_period_amount = $depreciation_amount - (float) $this->reversal_amount_ht;
1077 $start_date = $depreciation_date_start;
1078 $disposal_date = isset($this->disposal_date) && $this->disposal_date !== "" ? $this->disposal_date : "";
1079 $finish_date = $disposal_date !== "" ? $disposal_date : $depreciation_date_end;
1080 $accountancy_code_depreciation_debit_key = $accountancy_codes->accountancy_codes_fields[$mode_key]['depreciation_debit'];
1081 $accountancy_code_depreciation_debit = $accountancy_codes->accountancy_codes[$mode_key][$accountancy_code_depreciation_debit_key] ?? '';
1082 $accountancy_code_depreciation_credit_key = $accountancy_codes->accountancy_codes_fields[$mode_key]['depreciation_credit'];
1083 $accountancy_code_credit = $accountancy_codes->accountancy_codes[$mode_key][$accountancy_code_depreciation_credit_key] ?? '';
1084
1085 // Reversal depreciation line
1086 //-----------------------------------------------------
1087 if ($last_depreciation_date === "" && ($depreciation_date_start < $fiscal_period_start || is_numeric($this->reversal_date))) {
1088 if (is_numeric($this->reversal_date)) {
1089 if ($this->reversal_date < $fiscal_period_start) {
1090 $this->errors[] = $langs->trans('AssetErrorReversalDateNotGreaterThanCurrentBeginFiscalDateForMode', $mode_key);
1091 $error++;
1092 break;
1093 }
1094
1095 if (empty($this->reversal_amount_ht)) {
1096 $this->errors[] = $langs->trans('AssetErrorReversalAmountNotProvidedForMode', $mode_key);
1097 $error++;
1098 break;
1099 }
1100
1101 $start_date = $this->reversal_date;
1102 $result = $this->addDepreciationLine($mode_key, '', $start_date, (float) $this->reversal_amount_ht, (float) $this->reversal_amount_ht, $accountancy_code_depreciation_debit, $accountancy_code_credit);
1103 if ($result < 0) {
1104 $error++;
1105 break;
1106 }
1107 } else {
1108 $this->errors[] = $langs->trans('AssetErrorReversalDateNotProvidedForMode', $mode_key);
1109 $error++;
1110 break;
1111 }
1112 }
1113
1114 // futures depreciation lines
1115 //-----------------------------------------------------
1116 $day_count_convention = getAssetDepreciationDayCountConvention();
1117 $period_amount = (float) ($fields['duration'] > 0 ? price2num($depreciation_period_amount / $fields['duration'], 'MT') : 0);
1118 $first_period_found = false;
1119
1120 //$first_period_date = isset($begin_period) && $begin_period > $fiscal_period_start ? $begin_period : $fiscal_period_start;
1121 $first_period_date = $fiscal_period_start;
1122
1123 $ref_date_format = "%Y" . ($fields['duration_type'] == 1 || $fields['duration_type'] == 2 ? '-%m' : '') . ($fields['duration_type'] == 2 ? '-%d' : '');
1124
1125 // Loop security
1126 $idx_loop = 0;
1127 $max_loop = $fields['duration'] + 2;
1128 do {
1129 // Loop security
1130 $idx_loop++;
1131 if ($idx_loop > $max_loop) {
1132 break;
1133 }
1134
1135 if ($last_depreciation_date < $fiscal_period_end && ($first_period_date <= $start_date || $first_period_found)) {
1136 // Disposal not depreciated
1137 if ($fiscal_period_start <= $disposal_date && $disposal_date <= $fiscal_period_end && empty($this->disposal_depreciated)) {
1138 break;
1139 }
1140
1141 $first_period_found = true;
1142
1143 $period_begin = dol_print_date($fiscal_period_start, $ref_date_format);
1144 $period_end = dol_print_date($fiscal_period_end, $ref_date_format);
1145 $ref = $period_begin . ($period_begin != $period_end ? ' - ' . $period_end : '');
1146 if ($fiscal_period_start <= $disposal_date && $disposal_date <= $fiscal_period_end) {
1147 $ref .= ' - ' . $langs->transnoentitiesnoconv('AssetDisposal');
1148 }
1149
1150 $begin_date = $fiscal_period_start < $start_date && $start_date <= $fiscal_period_end ? $start_date : $fiscal_period_start;
1151 $end_date = $fiscal_period_start < $finish_date && $finish_date <= $fiscal_period_end ? $finish_date : $fiscal_period_end;
1152 if ($fields['duration_type'] == 2) { // Daily
1153 $depreciation_ht = $period_amount;
1154 } elseif ($fields['duration_type'] == 1) { // Monthly
1155 // Same rule as the annual branch: the count of the days and the length of a full
1156 // month must come from the same day count convention. Counting the real calendar
1157 // days then dividing them by a conventional month of 30 made the depreciation of a
1158 // partial month depend on the length of that month, and required a special case to
1159 // give a full February a whole monthly amount.
1160 $period_fraction = getAssetDepreciationMonthFraction($begin_date, $end_date, $day_count_convention, 'gmt');
1161 $depreciation_ht = (float) price2num($period_amount * $period_fraction, 'MT');
1162 } else { // Annually, taking care for adjustments to shortened or extended periods (e.g., fiscal years of 9 or 15 months)
1163 // A standard fiscal year lasts 365 days, or 366 when it covers a leap day. Anything else
1164 // is a shortened or an extended fiscal year (e.g. 9 or 15 months), whose depreciation is
1165 // prorated with no upper limit.
1166 $nb_days_fiscal_period = num_between_day($fiscal_period_start, $fiscal_period_end, 1);
1167 $is_standard_fiscal_period = ($nb_days_fiscal_period >= 365 && $nb_days_fiscal_period <= 366);
1168
1169 if ($is_standard_fiscal_period && $begin_date <= $fiscal_period_start && $end_date >= $fiscal_period_end) {
1170 // The asset is depreciated over the whole fiscal year: full annuity, whatever the
1171 // convention. Computing a prorata here would give slightly less than a full annuity
1172 // with THIRTY_360 when the fiscal year ends in February (a 30/360 count ending on
1173 // Feb 28th or 29th is 358 or 359 days instead of 360).
1174 $period_fraction = 1;
1175 } else {
1176 // The count of the days and the number of days of a full year must always come from
1177 // the same day count convention, otherwise every partial period (so the first and the
1178 // last year of every asset) is over or under evaluated. Counting the real calendar
1179 // days then dividing them by 360 overestimated every prorata by about 1.39% (365/360).
1180 // The bounds are anchored on GMT midnight, so the calendar decomposition of
1181 // THIRTY_360 and ACT_ACT must be read in GMT too, otherwise a partial period is
1182 // prorated on a day shifted by the UTC offset of the server.
1183 $period_fraction = getAssetDepreciationPeriodFraction($begin_date, $end_date, $day_count_convention, 'gmt');
1184 if ($is_standard_fiscal_period) {
1185 // Never more than a full annuity on a standard fiscal year
1186 $period_fraction = min(1, $period_fraction);
1187 }
1188 }
1189 $depreciation_ht = (float) price2num($period_amount * $period_fraction, 'MT');
1190 }
1191 if (getDolGlobalInt('ASSET_ROUND_INTEGER_NUMBER_UPWARDS') == 1) {
1192 if ($idx_loop < $max_loop) { // avoid last depreciation value
1193 $depreciation_ht = ceil($depreciation_ht);
1194 }
1195 }
1196
1197 if ($fiscal_period_start <= $depreciation_date_end && $depreciation_date_end <= $fiscal_period_end) { // last period
1198 $depreciation_ht = (float) price2num($depreciation_amount - (float) $cumulative_depreciation_ht, 'MT');
1199 $cumulative_depreciation_ht = $depreciation_amount;
1200 } else {
1201 $cumulative_depreciation_ht += $depreciation_ht;
1202 }
1203
1204 $result = $this->addDepreciationLine($mode_key, $ref, $fiscal_period_end, $depreciation_ht, (float) $cumulative_depreciation_ht, $accountancy_code_depreciation_debit, $accountancy_code_credit);
1205 if ($result < 0) {
1206 $error++;
1207 break;
1208 }
1209 }
1210
1211 // Next fiscal period (+1 day/month/year)
1212 $fiscal_period_start = dol_time_plus_duree($fiscal_period_end, 1, 'd');
1213
1214 if ($fields['duration_type'] == 0) { // Annually - use fiscal year-end
1215 // Search for the NEXT fiscal year
1216 $next_fy = getNextFiscalYear($this->db, $fiscal_period_end, 'gmt');
1217
1218 if ($next_fy !== null) {
1219 // Exercise found in database
1220 $fiscal_period_start = $next_fy['date_start'];
1221 $fiscal_period_end = $next_fy['date_end'];
1222 } else {
1223 // If there is no subsequent fiscal year in the database, a one-year fiscal year is automatically generated.
1224 // based on the structure of the current fiscal year
1225 $current_fy_start_parts = dol_getdate($fiscal_period_start);
1226
1227 // Calculate the end: 1 year minus 1 day from the start
1228 $fiscal_period_end = dol_time_plus_duree(dol_time_plus_duree($fiscal_period_start, 1, 'y'), -1, 'd');
1229
1230 // If the amortization end date is exceeded, there is no need to continue after that.
1231 // The loop will end naturally with the while condition.
1232 }
1233 } elseif ($fields['duration_type'] == 1) { // Monthly
1234 $fiscal_period_end = dol_time_plus_duree(dol_time_plus_duree($fiscal_period_start, 1, 'm'), -1, 'd');
1235 } else { // Daily
1236 $fiscal_period_end = $fiscal_period_start;
1237 }
1238 $last_period_date = $disposal_date !== "" && $disposal_date < $depreciation_date_end ? $disposal_date : $depreciation_date_end;
1239 } while ($fiscal_period_start < $last_period_date);
1240
1241 if ($error) {
1242 break;
1243 }
1244 }
1245 }
1246
1247 if ($error) {
1248 $this->db->rollback();
1249 return -1;
1250 } else {
1251 $this->db->commit();
1252 return 1;
1253 }
1254 }
1255
1262 public function setLastCumulativeDepreciation($asset_depreciation_id)
1263 {
1264 global $langs;
1265 $langs->load('assets');
1266
1267 // Clean parameters
1268 $asset_depreciation_id = $asset_depreciation_id > 0 ? $asset_depreciation_id : 0;
1269
1270 // Check parameters
1271 $error = 0;
1272 if (empty($asset_depreciation_id)) {
1273 $this->errors[] = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("AssetDepreciation") . ' (' . $langs->transnoentitiesnoconv("TechnicalID") . ')');
1274 $error++;
1275 }
1276 if ($error) {
1277 return -1;
1278 }
1279
1280 $this->db->begin();
1281
1282 require_once DOL_DOCUMENT_ROOT . '/asset/class/assetdepreciationoptions.class.php';
1283 $options = new AssetDepreciationOptions($this->db);
1284
1285 // Get last depreciation lines save in bookkeeping
1286 //-----------------------------------------------------
1287 $sql = "SELECT fk_asset, depreciation_mode, cumulative_depreciation_ht";
1288 $sql .= " FROM " . MAIN_DB_PREFIX . "asset_depreciation";
1289 $sql .= " WHERE rowid = " . (int) $asset_depreciation_id;
1290 $resql = $this->db->query($sql);
1291 if (!$resql) {
1292 $this->errors[] = $langs->trans('AssetErrorFetchCumulativeDepreciation') . ': ' . $this->db->lasterror();
1293 $error++;
1294 } else {
1295 if ($obj = $this->db->fetch_object($resql)) {
1296 $mode_key = $obj->depreciation_mode;
1297 if (!empty($options->deprecation_options_fields[$mode_key])) {
1298 $sql = "UPDATE " . MAIN_DB_PREFIX . $this->db->sanitize($options->deprecation_options_fields[$mode_key]['table']);
1299 $sql .= " SET total_amount_last_depreciation_ht = " . ((float) $obj->cumulative_depreciation_ht);
1300 $sql .= " WHERE fk_asset = " . (int) $obj->fk_asset;
1301 $resql = $this->db->query($sql);
1302 if (!$resql) {
1303 $this->errors[] = $langs->trans('AssetErrorSetLastCumulativeDepreciation') . ': ' . $this->db->lasterror();
1304 $error++;
1305 }
1306 }
1307 }
1308 }
1309
1310 if ($error) {
1311 $this->db->rollback();
1312 return -1;
1313 } else {
1314 $this->db->commit();
1315 return 1;
1316 }
1317 }
1318
1327 public function dispose($user, $disposal_invoice_id, $notrigger = 0)
1328 {
1329 global $conf, $langs;
1330
1331 // Protection
1332 if ($this->status == self::STATUS_DISPOSED) {
1333 return 0;
1334 }
1335
1336 $this->db->begin();
1337
1338 $required_fields = array('disposal_date', 'disposal_date', 'fk_disposal_type');
1339 foreach ($required_fields as $field) {
1340 $this->fields[$field]['notnull'] = 1;
1341 }
1342 $result = $this->update($user, 1);
1343 foreach ($required_fields as $field) {
1344 $this->fields[$field]['notnull'] = 0;
1345 }
1346 if ($result > 0) {
1347 if ($disposal_invoice_id > 0) {
1348 $this->add_object_linked('facture', $disposal_invoice_id);
1349 }
1350 $result = $this->setStatusCommon($user, self::STATUS_DISPOSED, $notrigger, 'ASSET_DISPOSED');
1351 }
1352 if ($result > 0) {
1353 $result = $this->calculationDepreciation();
1354 }
1355
1356 if ($result < 0) {
1357 $this->db->rollback();
1358 } else {
1359 $this->db->commit();
1360 }
1361
1362 // Define output language
1363 if ($result > 0 && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1364 if (method_exists($this, 'generateDocument')) {
1365 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
1366 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
1367 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
1368 $outputlangs = $langs;
1369 $newlang = '';
1370 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1371 $newlang = GETPOST('lang_id', 'aZ09');
1372 }
1373 if (!empty($newlang)) {
1374 $outputlangs = new Translate("", $conf);
1375 $outputlangs->setDefaultLang($newlang);
1376 }
1377 $model = $this->model_pdf;
1378 $ret = $this->fetch($this->id); // Reload to get new records
1379
1380 $this->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
1381 }
1382 }
1383
1384 return $result;
1385 }
1386
1394 public function reopen($user, $notrigger = 0)
1395 {
1396 global $conf, $langs;
1397
1398 // Protection
1399 if ($this->status != self::STATUS_DISPOSED) {
1400 return 0;
1401 }
1402
1403
1404 $this->db->begin();
1405
1406 $this->disposal_date = null;
1407 $this->disposal_amount_ht = null;
1408 $this->fk_disposal_type = null;
1409 $this->disposal_depreciated = 0;
1410 $this->disposal_subject_to_vat = 0;
1411 $result = $this->update($user, 1);
1412 if ($result > 0) {
1413 $this->deleteObjectLinked(null, 'facture');
1414 $result = $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'ASSET_REOPEN');
1415 }
1416 if ($result > 0) {
1417 $result = $this->calculationDepreciation();
1418 }
1419
1420 if ($result < 0) {
1421 $this->db->rollback();
1422 } else {
1423 $this->db->commit();
1424 }
1425
1426 // Define output language
1427 if ($result > 0 && !getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1428 if (method_exists($this, 'generateDocument')) {
1429 $hidedetails = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0);
1430 $hidedesc = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0);
1431 $hideref = (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0);
1432 $outputlangs = $langs;
1433 $newlang = '';
1434 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1435 $newlang = GETPOST('lang_id', 'aZ09');
1436 }
1437 if (!empty($newlang)) {
1438 $outputlangs = new Translate("", $conf);
1439 $outputlangs->setDefaultLang($newlang);
1440 }
1441 $model = $this->model_pdf;
1442 $ret = $this->fetch($this->id); // Reload to get new records
1443
1444 $this->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
1445 }
1446 }
1447
1448 return $result;
1449 }
1450
1462 public function getNomUrl($withpicto = 0, $option = '', $maxlen = 0, $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1463 {
1464 global $db, $conf, $langs, $hookmanager;
1465 global $dolibarr_main_authentication, $dolibarr_main_demo;
1466 global $menumanager;
1467
1468 if (!empty($conf->dol_no_mouse_hover)) {
1469 $notooltip = 1; // Force disable tooltips
1470 }
1471
1472 $result = '';
1473
1474 $label = img_picto('', $this->picto).' <u>'.$langs->trans("Asset").'</u>';
1475 if (isset($this->status)) {
1476 $label .= ' '.$this->getLibStatut(5);
1477 }
1478 $label .= '<br>';
1479 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref;
1480
1481 $url = dol_buildpath('/asset/card.php', 1).'?id='.$this->id;
1482
1483 if ($option != 'nolink') {
1484 // Add param to save lastsearch_values or not
1485 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1486 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1487 $add_save_lastsearch_values = 1;
1488 }
1489 if ($add_save_lastsearch_values) {
1490 $url .= '&save_lastsearch_values=1';
1491 }
1492 }
1493
1494 $linkclose = '';
1495 if (empty($notooltip)) {
1496 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1497 $label = $langs->trans("ShowAsset");
1498 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1499 }
1500 $linkclose .= ' title="'.dolPrintHTMLForAttribute($label).'"';
1501 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
1502 } else {
1503 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1504 }
1505
1506 if ($option == 'nolink') {
1507 $linkstart = '<span';
1508 } else {
1509 $linkstart = '<a href="'.$url.'"';
1510 }
1511 $linkstart .= $linkclose.'>';
1512 if ($option == 'nolink') {
1513 $linkend = '</span>';
1514 } else {
1515 $linkend = '</a>';
1516 }
1517
1518 $result .= $linkstart;
1519
1520 if (empty($this->showphoto_on_popup)) {
1521 if ($withpicto) {
1522 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1523 }
1524 } else {
1525 if ($withpicto) {
1526 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1527
1528 list($class, $module) = explode('@', $this->picto);
1529 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref);
1530 $filearray = dol_dir_list($upload_dir, "files");
1531 $filename = $filearray[0]['name'];
1532 if (!empty($filename)) {
1533 $pospoint = strpos($filearray[0]['name'], '.');
1534
1535 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.dol_substr($filename, 0, $pospoint).'_mini'.dol_substr($filename, $pospoint);
1536 if (!getDolGlobalString(strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS')) {
1537 $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>';
1538 } else {
1539 $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>';
1540 }
1541
1542 $result .= '</div>';
1543 } else {
1544 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1545 }
1546 }
1547 }
1548
1549 if ($withpicto != 2) {
1550 $name = $this->ref;
1551 if ($option == 'label') {
1552 $name = $this->label;
1553 } elseif ($option == 'with_label') {
1554 $name .= ' - ' . $this->label;
1555 }
1556 $result .= dol_escape_htmltag($maxlen ? dol_trunc($name, $maxlen) : $name);
1557 }
1558
1559 $result .= $linkend;
1560 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1561
1562 global $action;
1563 $hookmanager->initHooks(array($this->element . 'dao'));
1564 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1565 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1566 if ($reshook > 0) {
1567 $result = $hookmanager->resPrint;
1568 } else {
1569 $result .= $hookmanager->resPrint;
1570 }
1571 return $result;
1572 }
1573
1580 public function getLabelStatus($mode = 0)
1581 {
1582 return $this->LibStatut((int) $this->status, $mode);
1583 }
1584
1591 public function getLibStatut($mode = 0)
1592 {
1593 return $this->LibStatut((int) $this->status, $mode);
1594 }
1595
1596 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1604 public function LibStatut($status, $mode = 0)
1605 {
1606 // phpcs:enable
1607 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
1608 global $langs;
1609 //$langs->load("assets");
1610 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('AssetInDraft');
1611 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('AssetInProgress');
1612 $this->labelStatus[self::STATUS_DISPOSED] = $langs->transnoentitiesnoconv('AssetDisposed');
1613 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('AssetInDraft');
1614 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('AssetInProgress');
1615 $this->labelStatusShort[self::STATUS_DISPOSED] = $langs->transnoentitiesnoconv('AssetDisposed');
1616 }
1617
1618 $statusType = 'status'.$status;
1619 if ($status == self::STATUS_VALIDATED) {
1620 $statusType = 'status4';
1621 }
1622 if ($status == self::STATUS_DISPOSED) {
1623 $statusType = 'status6';
1624 }
1625
1626 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
1627 }
1628
1635 public function info($id)
1636 {
1637 $sql = "SELECT rowid, date_creation as datec, tms as datem, date_valid as datev,";
1638 $sql .= " fk_user_creat, fk_user_modif, fk_user_valid";
1639 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
1640 $sql .= " WHERE t.rowid = ".((int) $id);
1641
1642 $result = $this->db->query($sql);
1643 if ($result) {
1644 if ($this->db->num_rows($result)) {
1645 $obj = $this->db->fetch_object($result);
1646 $this->id = $obj->rowid;
1647
1648 $this->user_creation_id = $obj->fk_user_creat;
1649 $this->user_modification_id = $obj->fk_user_modif;
1650 $this->user_validation_id = $obj->fk_user_valid;
1651 $this->date_creation = $this->db->jdate($obj->datec);
1652 $this->date_modification = $this->db->jdate($obj->datem);
1653 $this->date_validation = $this->db->jdate($obj->datev);
1654 }
1655
1656 $this->db->free($result);
1657 } else {
1658 dol_print_error($this->db);
1659 }
1660 }
1661
1668 public function initAsSpecimen()
1669 {
1670 // Set here init that are not commonf fields
1671 // $this->property1 = ...
1672 // $this->property2 = ...
1673
1674 return $this->initAsSpecimenCommon();
1675 }
1676
1688 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1689 {
1690 global $langs;
1691
1692 $langs->load("assets");
1693
1694 if (!dol_strlen($modele)) {
1695 $modele = 'standard_asset';
1696
1697 if ($this->model_pdf) {
1698 $modele = $this->model_pdf;
1699 } elseif (getDolGlobalString('ASSET_ASSET_ADDON_PDF')) {
1700 $modele = getDolGlobalString('ASSET_ASSET_ADDON_PDF');
1701 }
1702 }
1703
1704 $modelpath = "core/modules/asset/doc/";
1705
1706 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1707 }
1708
1714 public function getNextNumRef()
1715 {
1716 global $langs, $conf;
1717 $langs->load("assets");
1718
1719 if (!getDolGlobalString('ASSET_ASSET_ADDON')) {
1720 $conf->global->ASSET_ASSET_ADDON = 'mod_asset_standard';
1721 }
1722
1723 if (getDolGlobalString('ASSET_ASSET_ADDON')) {
1724 $mybool = false;
1725
1726 $file = getDolGlobalString('ASSET_ASSET_ADDON') . ".php";
1727 $classname = getDolGlobalString('ASSET_ASSET_ADDON');
1728
1729 // Include file with class
1730 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1731 foreach ($dirmodels as $reldir) {
1732 $dir = dol_buildpath($reldir."core/modules/asset/");
1733
1734 // Load file with numbering class (if found)
1735 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1736 }
1737
1738 if (!$mybool) {
1739 dol_print_error(null, "Failed to include file ".$file);
1740 return '';
1741 }
1742
1743 if (class_exists($classname)) {
1744 $obj = new $classname();
1745
1746 '@phan-var-force ModeleNumRefAsset $obj';
1749 $numref = $obj->getNextValue($this);
1750
1751 if ($numref != '' && $numref != '-1') {
1752 return $numref;
1753 } else {
1754 $this->error = $obj->error;
1755 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1756 return "";
1757 }
1758 } else {
1759 print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname;
1760 return "";
1761 }
1762 } else {
1763 print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
1764 return "";
1765 }
1766 }
1767
1775 public function validate($user, $notrigger = 0)
1776 {
1777 global $conf;
1778
1779 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1780
1781 $error = 0;
1782
1783 // Protection
1784 if ($this->status == self::STATUS_VALIDATED) {
1785 dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
1786 return 0;
1787 }
1788
1789 $now = dol_now();
1790
1791 $this->db->begin();
1792
1793 // Define new ref
1794 if (/* !$error && */ (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1795 $num = $this->getNextNumRef();
1796 } else {
1797 $num = (string) $this->ref;
1798 }
1799 $this->newref = dol_sanitizeFileName($num);
1800
1801 // Validate
1802 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1803 $sql .= " SET ref = '".$this->db->escape($num)."',";
1804 $sql .= " status = ".self::STATUS_VALIDATED.",";
1805 $sql .= " date_valid='".$this->db->idate($now)."',";
1806 $sql .= " fk_user_valid = ".((int) $user->id);
1807 $sql .= " WHERE rowid = ".((int) $this->id);
1808
1809 dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
1810 $resql = $this->db->query($sql);
1811 if (!$resql) {
1812 dol_print_error($this->db);
1813 $this->error = $this->db->lasterror();
1814 $error++;
1815 }
1816
1817 if (!$error && !$notrigger) {
1818 // Call trigger
1819 $result = $this->call_trigger('ASSET_VALIDATE', $user);
1820 if ($result < 0) {
1821 $error++;
1822 }
1823 // End call triggers
1824 }
1825
1826 if (!$error) {
1827 $this->oldref = $this->ref;
1828
1829 // Rename directory if dir was a temporary ref
1830 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1831 // Now we rename also files into index
1832 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'asset/".$this->db->escape($this->newref)."'";
1833 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'asset/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1834 $resql = $this->db->query($sql);
1835 if (!$resql) {
1836 $error++;
1837 $this->error = $this->db->lasterror();
1838 }
1839 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'asset/".$this->db->escape($this->newref)."'";
1840 $sql .= " WHERE filepath = 'asset/".$this->db->escape($this->ref)."' and entity = ".((int) $conf->entity);
1841 $resql = $this->db->query($sql);
1842 if (!$resql) {
1843 $error++;
1844 $this->error = $this->db->lasterror();
1845 }
1846
1847 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1848 $oldref = dol_sanitizeFileName($this->ref);
1849 $newref = dol_sanitizeFileName($num);
1850 $dirsource = $conf->asset->dir_output.'/'.$oldref;
1851 $dirdest = $conf->asset->dir_output.'/'.$newref;
1852 if (!$error && file_exists($dirsource)) {
1853 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
1854
1855 if (@rename($dirsource, $dirdest)) {
1856 dol_syslog("Rename ok");
1857 // Rename docs starting with $oldref with $newref
1858 $listoffiles = dol_dir_list($conf->asset->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1859 foreach ($listoffiles as $fileentry) {
1860 $dirsource = $fileentry['name'];
1861 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1862 $dirsource = $fileentry['path'].'/'.$dirsource;
1863 $dirdest = $fileentry['path'].'/'.$dirdest;
1864 @rename($dirsource, $dirdest);
1865 }
1866 }
1867 }
1868 }
1869 }
1870
1871 // Set new ref and current status
1872 if (!$error) {
1873 $this->ref = $num;
1874 $this->status = self::STATUS_VALIDATED;
1875 }
1876
1877 if (!$error) {
1878 $this->db->commit();
1879 return 1;
1880 } else {
1881 $this->db->rollback();
1882 return -1;
1883 }
1884 }
1885
1893 public function setDraft($user, $notrigger = 0)
1894 {
1895 // Protection
1896 if ($this->status <= self::STATUS_DRAFT) {
1897 return 0;
1898 }
1899
1900 return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'ASSET_UNVALIDATE');
1901 }
1902}
getCurrentPeriodOfFiscalYear($db, $conf, $from_time=null, $gm='tzserver', $withenddateonly=1)
Get current period of fiscal year?
getNextFiscalYear($db, $after_date, $gm='tzserver')
Get next fiscal year period after a given date.
getAssetDepreciationPeriodFraction($timestampStart, $timestampEnd, $convention='', $forcetimezone='')
Return the fraction of a year (the prorata temporis) between two dates, for a given day count convent...
getAssetDepreciationDayCountConvention()
Return the day count convention to use to compute the prorata temporis of a depreciation.
getAssetDepreciationMonthFraction($timestampStart, $timestampEnd, $convention='', $forcetimezone='')
Return the fraction of a month covered by a period, for the given day count convention.
$object ref
Definition info.php:90
Class for AssetAccountancyCodes.
Class for AssetDepreciationOptions.
Class for Asset.
getNomUrl($withpicto=0, $option='', $maxlen=0, $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
hasDepreciationLinesInBookkeeping()
If has depreciation lines in bookkeeping.
setDraft($user, $notrigger=0)
Set draft status.
dispose($user, $disposal_invoice_id, $notrigger=0)
Set dispose status.
validate($user, $notrigger=0)
Validate asset.
createFromClone(User $user, $fromid)
Clone an object into another one.
calculationDepreciation()
Calculation depreciation lines (reversal and future) for each mode.
__construct(DoliDB $db)
Constructor.
create(User $user, $notrigger=0)
Create object into database.
info($id)
Load the info information in the object.
getLabelStatus($mode=0)
Return the label of the status.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
fetch($id, $ref=null)
Load object in memory from the database.
LibStatut($status, $mode=0)
Return the status.
reopen($user, $notrigger=0)
Set back to validated status if disposed status.
getLibStatut($mode=0)
Return the label of the status.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
addDepreciationLine($mode, $ref, $depreciation_date, $depreciation_ht, $cumulative_depreciation_ht, $accountancy_code_debit, $accountancy_code_credit)
Add depreciation line for a mode.
fetchDepreciationLines()
Fetch depreciation lines for each mode in $this->depreciation_lines (sort by depreciation date)
setLastCumulativeDepreciation($asset_depreciation_id)
Set last cumulative depreciation for each mode.
update(User $user, $notrigger=0)
Update object into database.
setDataFromAssetModel(User $user, $notrigger=0)
Set asset model.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
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.
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.
Class to manage Dolibarr database access.
Class to manage translations.
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
dol_get_first_day($year, $month=1, $gm=false)
Return GMT time for first day of a month or year.
Definition date.lib.php:616
num_between_day($timestampStart, $timestampEnd, $lastday=0)
Function to return number of days between two dates (date must be UTC date !) Example: 2012-01-01 201...
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:126
dol_get_last_day($year, $month=12, $gm=false)
Return GMT time for last day of a month or year.
Definition date.lib.php:635
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
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).
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
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.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181