From 6734a3575e80f351cf52c49c4e2b671e512fd0ff Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Mon, 14 Feb 2022 04:52:39 +0100 Subject: [PATCH] Core --- htdocs/core/class/commonobject.class.php | 4 +- htdocs/core/lib/asset.lib.php | 193 ++- htdocs/core/menus/standard/auguria.lib.php | 2 +- htdocs/core/menus/standard/eldy.lib.php | 10 +- .../doc/doc_generic_asset_odt.modules.php | 499 ++++++ .../asset/doc/pdf_standard_asset.modules.php | 1375 +++++++++++++++++ .../core/modules/asset/mod_asset_advanced.php | 147 ++ .../core/modules/asset/mod_asset_standard.php | 161 ++ htdocs/core/modules/asset/modules_asset.php | 158 ++ htdocs/core/modules/modAsset.class.php | 26 +- htdocs/core/tpl/objectline_create.tpl.php | 6 +- htdocs/core/tpl/objectline_edit.tpl.php | 6 + htdocs/core/tpl/objectline_title.tpl.php | 5 + htdocs/core/tpl/objectline_view.tpl.php | 30 + 14 files changed, 2545 insertions(+), 77 deletions(-) create mode 100644 htdocs/core/modules/asset/doc/doc_generic_asset_odt.modules.php create mode 100644 htdocs/core/modules/asset/doc/pdf_standard_asset.modules.php create mode 100644 htdocs/core/modules/asset/mod_asset_advanced.php create mode 100644 htdocs/core/modules/asset/mod_asset_standard.php create mode 100644 htdocs/core/modules/asset/modules_asset.php diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 87f6ba693fc..00a263f358d 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -7,7 +7,7 @@ * Copyright (C) 2012-2015 Marcos García * Copyright (C) 2012-2015 Raphaël Doursenaud * Copyright (C) 2012 Cedric Salvador - * Copyright (C) 2015-2021 Alexandre Spangaro + * Copyright (C) 2015-2022 Alexandre Spangaro * Copyright (C) 2016 Bahfir abbes * Copyright (C) 2017 ATM Consulting * Copyright (C) 2017-2019 Nicolas ZABOURI @@ -3670,7 +3670,7 @@ abstract class CommonObject // Elements of the core modules which have `$module` property but may to which we don't want to prefix module part to the element name for finding the linked object in llx_element_element. // It's because an entry for this element may be exist in llx_element_element before this modification (version <=14.2) and ave named only with their element name in fk_source or fk_target. - $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization'); + $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset'); // Add module part to target type if object has $module property and isn't in core modules. $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element; diff --git a/htdocs/core/lib/asset.lib.php b/htdocs/core/lib/asset.lib.php index 840aca1339a..9d71e2b38f1 100644 --- a/htdocs/core/lib/asset.lib.php +++ b/htdocs/core/lib/asset.lib.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2018-2022 OpenDSI * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ * * @return array head array with tabs */ -function asset_admin_prepare_head() +function assetAdminPrepareHead() { global $langs, $conf; @@ -43,90 +43,118 @@ function asset_admin_prepare_head() // Show more tabs from modules // Entries must be declared in modules descriptor with line //$this->tabs = array( - // 'entity:+tabname:Title:@assets:/asset/mypage.php?id=__ID__' + // 'entity:+tabname:Title:@asset:/asset/mypage.php?id=__ID__' //); // to add new tab //$this->tabs = array( - // 'entity:-tabname:Title:@assets:/asset/mypage.php?id=__ID__' + // 'entity:-tabname:Title:@asset:/asset/mypage.php?id=__ID__' //); // to remove a tab - complete_head_from_modules($conf, $langs, null, $head, $h, 'assets_admin'); + complete_head_from_modules($conf, $langs, null, $head, $h, 'asset_admin'); - $head[$h][0] = DOL_URL_ROOT.'/asset/admin/assets_extrafields.php'; + $head[$h][0] = DOL_URL_ROOT.'/asset/admin/asset_extrafields.php'; $head[$h][1] = $langs->trans("ExtraFields"); - $head[$h][2] = 'attributes'; + $head[$h][2] = 'asset_extrafields'; $h++; - $head[$h][0] = DOL_URL_ROOT.'/asset/admin/assets_type_extrafields.php'; - $head[$h][1] = $langs->trans("ExtraFieldsAssetsType"); - $head[$h][2] = 'attributes_type'; + $head[$h][0] = DOL_URL_ROOT.'/asset/admin/assetmodel_extrafields.php'; + $head[$h][1] = $langs->trans("ExtraFieldsAssetModel"); + $head[$h][2] = 'assetmodel_extrafields'; $h++; - complete_head_from_modules($conf, $langs, null, $head, $h, 'assets_admin', 'remove'); + complete_head_from_modules($conf, $langs, null, $head, $h, 'asset_admin', 'remove'); return $head; } /** - * Prepare admin pages header + * Prepare array of tabs for Asset * - * @param Contrat $object Object related to tabs - * @return array head array with tabs + * @param Asset $object Asset + * @return array Array of tabs */ -function asset_prepare_head(Asset $object) +function assetPrepareHead(Asset $object) { global $db, $langs, $conf; - $langs->load("assets"); + $langs->load("assets", "admin"); $h = 0; $head = array(); - $head[$h][0] = DOL_URL_ROOT.'/asset/card.php?id='.$object->id; + $head[$h][0] = DOL_URL_ROOT . '/asset/card.php?id=' . $object->id; $head[$h][1] = $langs->trans("Card"); $head[$h][2] = 'card'; $h++; + if (empty($object->not_depreciated)) { + $head[$h][0] = DOL_URL_ROOT . '/asset/depreciation_options.php?id=' . $object->id; + $head[$h][1] = $langs->trans("AssetDepreciationOptions"); + $head[$h][2] = 'depreciation_options'; + $h++; + } + + $head[$h][0] = DOL_URL_ROOT . '/asset/accountancy_codes.php?id=' . $object->id; + $head[$h][1] = $langs->trans("AssetAccountancyCodes"); + $head[$h][2] = 'accountancy_codes'; + $h++; + + if (empty($object->not_depreciated)) { + $head[$h][0] = DOL_URL_ROOT . '/asset/depreciation.php?id=' . $object->id; + $head[$h][1] = $langs->trans("AssetDepreciation"); + $head[$h][2] = 'depreciation'; + $h++; + } + + if (isset($object->disposal_date) && $object->disposal_date !== "") { + $head[$h][0] = DOL_URL_ROOT . '/asset/disposal.php?id=' . $object->id; + $head[$h][1] = $langs->trans("AssetDisposal"); + $head[$h][2] = 'disposal'; + $h++; + } + + if (isset($object->fields['note_public']) || isset($object->fields['note_private'])) { + $nbNote = 0; + if (!empty($object->note_private)) { + $nbNote++; + } + if (!empty($object->note_public)) { + $nbNote++; + } + $head[$h][0] = DOL_URL_ROOT . '/asset/note.php?id=' . $object->id; + $head[$h][1] = $langs->trans('Notes'); + if ($nbNote > 0) { + $head[$h][1] .= (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) ? '' . $nbNote . '' : ''); + } + $head[$h][2] = 'note'; + $h++; + } + + require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + require_once DOL_DOCUMENT_ROOT . '/core/class/link.class.php'; + $upload_dir = $conf->asset->dir_output . "/asset/" . dol_sanitizeFileName($object->ref); + $nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$')); + $nbLinks = Link::count($db, $object->element, $object->id); + $head[$h][0] = DOL_URL_ROOT . '/asset/document.php?id=' . $object->id; + $head[$h][1] = $langs->trans('Documents'); + if (($nbFiles + $nbLinks) > 0) { + $head[$h][1] .= '' . ($nbFiles + $nbLinks) . ''; + } + $head[$h][2] = 'document'; + $h++; + + $head[$h][0] = DOL_URL_ROOT . '/asset/agenda.php?id=' . $object->id; + $head[$h][1] = $langs->trans("Events"); + $head[$h][2] = 'agenda'; + $h++; + // Show more tabs from modules // Entries must be declared in modules descriptor with line //$this->tabs = array( - // 'entity:+tabname:Title:@assets:/asset/mypage.php?id=__ID__' + // 'entity:+tabname:Title:@asset:/asset/mypage.php?id=__ID__' //); // to add new tab //$this->tabs = array( - // 'entity:-tabname:Title:@assets:/asset/mypage.php?id=__ID__' + // 'entity:-tabname:Title:@asset:/asset/mypage.php?id=__ID__' //); // to remove a tab - complete_head_from_modules($conf, $langs, $object, $head, $h, 'assets'); - - require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; - $upload_dir = $conf->assets->dir_output.'/'.dol_sanitizeFileName($object->ref); - $nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$')); - $nbLinks = Link::count($db, $object->element, $object->id); - $head[$h][0] = DOL_URL_ROOT.'/asset/document.php?id='.$object->id; - $head[$h][1] = $langs->trans('Documents'); - if (($nbFiles + $nbLinks) > 0) { - $head[$h][1] .= ''.($nbFiles + $nbLinks).''; - } - $head[$h][2] = 'documents'; - $h++; - - $nbNote = 0; - if (!empty($object->note_private)) { - $nbNote++; - } - if (!empty($object->note_public)) { - $nbNote++; - } - $head[$h][0] = DOL_URL_ROOT.'/asset/note.php?id='.$object->id; - $head[$h][1] = $langs->trans("Notes"); - if ($nbNote > 0) { - $head[$h][1] .= ''.$nbNote.''; - } - $head[$h][2] = 'note'; - $h++; - - $head[$h][0] = DOL_URL_ROOT.'/asset/info.php?id='.$object->id; - $head[$h][1] = $langs->trans("Info"); - $head[$h][2] = 'info'; - $h++; + complete_head_from_modules($conf, $langs, $object, $head, $h, 'asset'); complete_head_from_modules($conf, $langs, $object, $head, $h, 'asset', 'remove'); @@ -134,30 +162,69 @@ function asset_prepare_head(Asset $object) } /** - * Return array head with list of tabs to view object informations + * Prepare array of tabs for AssetModel * - * @param AssetType $object Asset - * @return array head + * @param AssetModel $object AssetModel + * @return array Array of tabs */ -function asset_type_prepare_head(AssetType $object) +function assetModelPrepareHead($object) { - global $langs, $conf, $user; + global $langs, $conf; + + $langs->load("assets", "admin"); $h = 0; $head = array(); - $head[$h][0] = DOL_URL_ROOT.'/asset/type.php?rowid='.$object->id; + $head[$h][0] = DOL_URL_ROOT . '/asset/model/card.php?id=' . $object->id; $head[$h][1] = $langs->trans("Card"); $head[$h][2] = 'card'; $h++; + $head[$h][0] = DOL_URL_ROOT . '/asset/model/depreciation_options.php?id=' . $object->id; + $head[$h][1] = $langs->trans("AssetDepreciationOptions"); + $head[$h][2] = 'depreciation_options'; + $h++; + + $head[$h][0] = DOL_URL_ROOT . '/asset/model/accountancy_codes.php?id=' . $object->id; + $head[$h][1] = $langs->trans("AssetAccountancyCodes"); + $head[$h][2] = 'accountancy_codes'; + $h++; + + if (isset($object->fields['note_public']) || isset($object->fields['note_private'])) { + $nbNote = 0; + if (!empty($object->note_private)) { + $nbNote++; + } + if (!empty($object->note_public)) { + $nbNote++; + } + $head[$h][0] = DOL_URL_ROOT . '/asset/model/note.php?id=' . $object->id; + $head[$h][1] = $langs->trans('Notes'); + if ($nbNote > 0) { + $head[$h][1] .= (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) ? '' . $nbNote . '' : ''); + } + $head[$h][2] = 'note'; + $h++; + } + + $head[$h][0] = DOL_URL_ROOT . '/asset/model/agenda.php?id=' . $object->id; + $head[$h][1] = $langs->trans("Events"); + $head[$h][2] = 'agenda'; + $h++; + + // Show more tabs from modules // Entries must be declared in modules descriptor with line - // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab - // $this->tabs = array('entity:-tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to remove a tab - complete_head_from_modules($conf, $langs, $object, $head, $h, 'assettype'); + //$this->tabs = array( + // 'entity:+tabname:Title:@asset:/asset/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@asset:/asset/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, $object, $head, $h, 'assetmodel'); - complete_head_from_modules($conf, $langs, $object, $head, $h, 'assettype', 'remove'); + complete_head_from_modules($conf, $langs, $object, $head, $h, 'assetmodel', 'remove'); return $head; } diff --git a/htdocs/core/menus/standard/auguria.lib.php b/htdocs/core/menus/standard/auguria.lib.php index 6504cfa27a6..6b415aec7a2 100644 --- a/htdocs/core/menus/standard/auguria.lib.php +++ b/htdocs/core/menus/standard/auguria.lib.php @@ -410,7 +410,7 @@ function print_left_auguria_menu($db, $menu_array_before, $menu_array_after, &$t // To enable when page exists if (empty($conf->global->ACCOUNTANCY_SHOW_DEVELOP_JOURNAL)) { - if ($nature == 'various' || $nature == 'hasnew' || $nature == 'inventory') { + if ($nature == 'hasnew' || $nature == 'inventory') { $nature = ''; } } diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index e69644bd123..9361b01b086 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -1332,7 +1332,7 @@ function print_left_eldy_menu($db, $menu_array_before, $menu_array_after, &$tabM // To enable when page exists if (empty($conf->global->ACCOUNTANCY_SHOW_DEVELOP_JOURNAL)) { - if ($nature == 'various' || $nature == 'hasnew' || $nature == 'inventory') { + if ($nature == 'hasnew' || $nature == 'inventory') { $nature = ''; } } @@ -1507,10 +1507,10 @@ function print_left_eldy_menu($db, $menu_array_before, $menu_array_after, &$tabM $newmenu->add("/asset/list.php?leftmenu=asset&mainmenu=accountancy", $langs->trans("MenuAssets"), 0, $user->rights->asset->read, '', $mainmenu, 'asset', 100, '', '', '', img_picto('', 'payment', 'class="paddingright pictofixedwidth"')); $newmenu->add("/asset/card.php?leftmenu=asset&action=create", $langs->trans("MenuNewAsset"), 1, $user->rights->asset->write); $newmenu->add("/asset/list.php?leftmenu=asset&mainmenu=accountancy", $langs->trans("MenuListAssets"), 1, $user->rights->asset->read); - $newmenu->add("/asset/type.php?leftmenu=asset_type", $langs->trans("MenuTypeAssets"), 1, $user->rights->asset->read, '', $mainmenu, 'asset_type'); - if ($usemenuhider || empty($leftmenu) || preg_match('/asset_type/', $leftmenu)) { - $newmenu->add("/asset/type.php?leftmenu=asset_type&action=create", $langs->trans("MenuNewTypeAssets"), 2, $user->rights->asset->setup_advance); - $newmenu->add("/asset/type.php?leftmenu=asset_type", $langs->trans("MenuListTypeAssets"), 2, $user->rights->asset->read); + $newmenu->add("/asset/model/list.php?leftmenu=asset_model", $langs->trans("MenuAssetModels"), 1, (empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->rights->asset->read) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->asset->setup_advance->read)), '', $mainmenu, 'asset_model'); + if ($usemenuhider || empty($leftmenu) || preg_match('/asset_model/', $leftmenu)) { + $newmenu->add("/asset/model/card.php?leftmenu=asset_model&action=create", $langs->trans("MenuNewAssetModel"), 2, (empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->rights->asset->write) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->asset->setup_advance->write))); + $newmenu->add("/asset/model/list.php?leftmenu=asset_model", $langs->trans("MenuListAssetModels"), 2, (empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->rights->asset->read) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->asset->setup_advance->read))); } } } diff --git a/htdocs/core/modules/asset/doc/doc_generic_asset_odt.modules.php b/htdocs/core/modules/asset/doc/doc_generic_asset_odt.modules.php new file mode 100644 index 00000000000..7fe684c9da1 --- /dev/null +++ b/htdocs/core/modules/asset/doc/doc_generic_asset_odt.modules.php @@ -0,0 +1,499 @@ + + * Copyright (C) 2012 Juanjo Menent + * Copyright (C) 2014 Marcos García + * Copyright (C) 2016 Charlie Benke + * Copyright (C) 2018-2021 Philippe Grand + * Copyright (C) 2018 Frédéric France + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/asset/doc/doc_generic_asset_odt.modules.php + * \ingroup asset + * \brief File of class to build ODT documents for assets + */ + +dol_include_once('/asset/core/modules/asset/modules_asset.php'); +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/doc.lib.php'; + + +/** + * Class to build documents using ODF templates generator + */ +class doc_generic_asset_odt extends ModelePDFAsset +{ + /** + * Issuer + * @var Societe + */ + public $emetteur; + + /** + * @var array Minimum version of PHP required by module. + * e.g.: PHP ≥ 5.6 = array(5, 6) + */ + public $phpmin = array(5, 6); + + /** + * @var string Dolibarr version of the loaded document + */ + public $version = 'dolibarr'; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + global $conf, $langs, $mysoc; + + // Load translation files required by the page + $langs->loadLangs(array("main", "companies")); + + $this->db = $db; + $this->name = "ODT templates"; + $this->description = $langs->trans("DocumentModelOdt"); + $this->scandir = 'ASSET_ASSET_ADDON_PDF_ODT_PATH'; // Name of constant that is used to save list of directories to scan + + // Page size for A4 format + $this->type = 'odt'; + $this->page_largeur = 0; + $this->page_hauteur = 0; + $this->format = array($this->page_largeur, $this->page_hauteur); + $this->marge_gauche = 0; + $this->marge_droite = 0; + $this->marge_haute = 0; + $this->marge_basse = 0; + + $this->option_logo = 1; // Display logo + $this->option_tva = 0; // Manage the vat option FACTURE_TVAOPTION + $this->option_modereg = 0; // Display payment mode + $this->option_condreg = 0; // Display payment terms + $this->option_codeproduitservice = 0; // Display product-service code + $this->option_multilang = 1; // Available in several languages + $this->option_escompte = 0; // Displays if there has been a discount + $this->option_credit_note = 0; // Support credit notes + $this->option_freetext = 1; // Support add of a personalised text + $this->option_draft_watermark = 0; // Support add of a watermark on drafts + + // Get source company + $this->emetteur = $mysoc; + if (!$this->emetteur->country_code) { + $this->emetteur->country_code = substr($langs->defaultlang, -2); // By default if not defined + } + } + + + /** + * Return description of a module + * + * @param Translate $langs Lang object to use for output + * @return string Description + */ + public function info($langs) + { + global $conf, $langs; + + // Load translation files required by the page + $langs->loadLangs(array("errors", "companies")); + + $form = new Form($this->db); + + $texte = $this->description.".
\n"; + $texte .= '
'; + $texte .= ''; + $texte .= ''; + $texte .= ''; + $texte .= ''; + + // List of directories area + $texte .= ''; + + $texte .= ''; + $texte .= ''; + + $texte .= '
'; + $texttitle = $langs->trans("ListOfDirectories"); + $listofdir = explode(',', preg_replace('/[\r\n]+/', ',', trim($conf->global->ASSET_ASSET_ADDON_PDF_ODT_PATH))); + $listoffiles = array(); + foreach ($listofdir as $key => $tmpdir) { + $tmpdir = trim($tmpdir); + $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir); + if (!$tmpdir) { + unset($listofdir[$key]); + continue; + } + if (!is_dir($tmpdir)) { + $texttitle .= img_warning($langs->trans("ErrorDirNotFound", $tmpdir), 0); + } else { + $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.(ods|odt)'); + if (count($tmpfiles)) { + $listoffiles = array_merge($listoffiles, $tmpfiles); + } + } + } + $texthelp = $langs->trans("ListOfDirectoriesForModelGenODT"); + // Add list of substitution keys + $texthelp .= '
'.$langs->trans("FollowingSubstitutionKeysCanBeUsed").'
'; + $texthelp .= $langs->transnoentitiesnoconv("FullListOnOnlineDocumentation"); // This contains an url, we don't modify it + + $texte .= $form->textwithpicto($texttitle, $texthelp, 1, 'help', '', 1); + $texte .= '
'; + $texte .= ''; + $texte .= '
'; + $texte .= ''; + $texte .= '
'; + + // Scan directories + $nbofiles = count($listoffiles); + if (!empty($conf->global->ASSET_ASSET_ADDON_PDF_ODT_PATH)) { + $texte .= $langs->trans("NumberOfModelFilesFound").': '; + //$texte.=$nbofiles?'':''; + $texte .= count($listoffiles); + //$texte.=$nbofiles?'':''; + $texte .= ''; + } + + if ($nbofiles) { + $texte .= ''; + } + + $texte .= '
'; + $texte .= $langs->trans("ExampleOfDirectoriesForModelGen"); + $texte .= '
'; + $texte .= '
'; + + return $texte; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Function to build a document on disk using the generic odt module. + * + * @param Asset $object Object source to build document + * @param Translate $outputlangs Lang output object + * @param string $srctemplatepath Full path of source filename for generator using a template file + * @param int $hidedetails Do not show line details + * @param int $hidedesc Do not show desc + * @param int $hideref Do not show ref + * @return int 1 if OK, <=0 if KO + */ + public function write_file($object, $outputlangs, $srctemplatepath, $hidedetails = 0, $hidedesc = 0, $hideref = 0) + { + // phpcs:enable + global $user, $langs, $conf, $mysoc, $hookmanager; + + if (empty($srctemplatepath)) { + dol_syslog("doc_generic_odt::write_file parameter srctemplatepath empty", LOG_WARNING); + return -1; + } + + // Add odtgeneration hook + if (!is_object($hookmanager)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager = new HookManager($this->db); + } + $hookmanager->initHooks(array('odtgeneration')); + global $action; + + if (!is_object($outputlangs)) { + $outputlangs = $langs; + } + $sav_charset_output = $outputlangs->charset_output; + $outputlangs->charset_output = 'UTF-8'; + + $outputlangs->loadLangs(array("main", "dict", "companies", "bills")); + + if ($conf->asset->dir_output) { + // If $object is id instead of object + if (!is_object($object)) { + $id = $object; + $object = new Asset($this->db); + $result = $object->fetch($id); + if ($result < 0) { + dol_print_error($this->db, $object->error); + return -1; + } + } + + $object->fetch_thirdparty(); + + $dir = $conf->asset->multidir_output[isset($object->entity) ? $object->entity : 1]; + $objectref = dol_sanitizeFileName($object->ref); + if (!preg_match('/specimen/i', $objectref)) { + $dir .= "/".$objectref; + } + $file = $dir."/".$objectref.".odt"; + + if (!file_exists($dir)) { + if (dol_mkdir($dir) < 0) { + $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir); + return -1; + } + } + + if (file_exists($dir)) { + //print "srctemplatepath=".$srctemplatepath; // Src filename + $newfile = basename($srctemplatepath); + $newfiletmp = preg_replace('/\.od(t|s)/i', '', $newfile); + $newfiletmp = preg_replace('/template_/i', '', $newfiletmp); + $newfiletmp = preg_replace('/modele_/i', '', $newfiletmp); + $newfiletmp = $objectref.'_'.$newfiletmp; + //$file=$dir.'/'.$newfiletmp.'.'.dol_print_date(dol_now(),'%Y%m%d%H%M%S').'.odt'; + // Get extension (ods or odt) + $newfileformat = substr($newfile, strrpos($newfile, '.') + 1); + if (!empty($conf->global->MAIN_DOC_USE_TIMING)) { + $format = $conf->global->MAIN_DOC_USE_TIMING; + if ($format == '1') { + $format = '%Y%m%d%H%M%S'; + } + $filename = $newfiletmp.'-'.dol_print_date(dol_now(), $format).'.'.$newfileformat; + } else { + $filename = $newfiletmp.'.'.$newfileformat; + } + $file = $dir.'/'.$filename; + //print "newdir=".$dir; + //print "newfile=".$newfile; + //print "file=".$file; + //print "conf->societe->dir_temp=".$conf->societe->dir_temp; + + dol_mkdir($conf->asset->dir_temp); + if (!is_writable($conf->asset->dir_temp)) { + $this->error = "Failed to write in temp directory ".$conf->asset->dir_temp; + dol_syslog('Error in write_file: '.$this->error, LOG_ERR); + return -1; + } + + // If CUSTOMER contact defined on order, we use it + $usecontact = false; + $arrayidcontact = $object->getIdContact('external', 'CUSTOMER'); + if (count($arrayidcontact) > 0) { + $usecontact = true; + $result = $object->fetch_contact($arrayidcontact[0]); + } + + // Recipient name + $contactobject = null; + if (!empty($usecontact)) { + // We can use the company of contact instead of thirdparty company + if ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT))) { + $object->contact->fetch_thirdparty(); + $socobject = $object->contact->thirdparty; + $contactobject = $object->contact; + } else { + $socobject = $object->thirdparty; + // if we have a CUSTOMER contact and we dont use it as thirdparty recipient we store the contact object for later use + $contactobject = $object->contact; + } + } else { + $socobject = $object->thirdparty; + } + + // Make substitution + $substitutionarray = array( + '__FROM_NAME__' => $this->emetteur->name, + '__FROM_EMAIL__' => $this->emetteur->email, + '__TOTAL_TTC__' => $object->total_ttc, + '__TOTAL_HT__' => $object->total_ht, + '__TOTAL_VAT__' => $object->total_tva + ); + complete_substitutions_array($substitutionarray, $langs, $object); + // Call the ODTSubstitution hook + $parameters = array('file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs, 'substitutionarray'=>&$substitutionarray); + $reshook = $hookmanager->executeHooks('ODTSubstitution', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + // Line of free text + $newfreetext = ''; + $paramfreetext = 'ORDER_FREE_TEXT'; + if (!empty($conf->global->$paramfreetext)) { + $newfreetext = make_substitutions($conf->global->$paramfreetext, $substitutionarray); + } + + // Open and load template + require_once ODTPHP_PATH.'odf.php'; + try { + $odfHandler = new odf( + $srctemplatepath, + array( + 'PATH_TO_TMP' => $conf->asset->dir_temp, + 'ZIP_PROXY' => 'PclZipProxy', // PhpZipProxy or PclZipProxy. Got "bad compression method" error when using PhpZipProxy. + 'DELIMITER_LEFT' => '{', + 'DELIMITER_RIGHT' => '}' + ) + ); + } catch (Exception $e) { + $this->error = $e->getMessage(); + dol_syslog($e->getMessage(), LOG_INFO); + return -1; + } + // After construction $odfHandler->contentXml contains content and + // [!-- BEGIN row.lines --]*[!-- END row.lines --] has been replaced by + // [!-- BEGIN lines --]*[!-- END lines --] + //print html_entity_decode($odfHandler->__toString()); + //print exit; + + + // Make substitutions into odt of freetext + try { + $odfHandler->setVars('free_text', $newfreetext, true, 'UTF-8'); + } catch (OdfException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } + + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties = $this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet = $this->get_substitutionarray_object($object, $outputlangs); + $array_user = $this->get_substitutionarray_user($user, $outputlangs); + $array_soc = $this->get_substitutionarray_mysoc($mysoc, $outputlangs); + $array_thirdparty = $this->get_substitutionarray_thirdparty($socobject, $outputlangs); + $array_other = $this->get_substitutionarray_other($outputlangs); + // retrieve contact information for use in object as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact && is_object($contactobject)) { + $array_thirdparty_contact = $this->get_substitutionarray_contact($contactobject, $outputlangs, 'contact'); + } + + $tmparray = array_merge($substitutionarray, $array_object_from_properties, $array_user, $array_soc, $array_thirdparty, $array_objet, $array_other, $array_thirdparty_contact); + complete_substitutions_array($tmparray, $outputlangs, $object); + + // Call the ODTSubstitution hook + $parameters = array('odfHandler'=>&$odfHandler, 'file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs, 'substitutionarray'=>&$tmparray); + $reshook = $hookmanager->executeHooks('ODTSubstitution', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + foreach ($tmparray as $key => $value) { + try { + if (preg_match('/logo$/', $key)) { + // Image + if (file_exists($value)) { + $odfHandler->setImage($key, $value); + } else { + $odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8'); + } + } else { + // Text + $odfHandler->setVars($key, $value, true, 'UTF-8'); + } + } catch (OdfException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + // Replace tags of lines + try { + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } catch (OdfException $e) { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) { + $linenumber = 0; + foreach ($object->lines as $line) { + $linenumber++; + $tmparray = $this->get_substitutionarray_lines($line, $outputlangs, $linenumber); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters = array('odfHandler'=>&$odfHandler, 'file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs, 'substitutionarray'=>&$tmparray, 'line'=>$line); + $reshook = $hookmanager->executeHooks('ODTSubstitutionLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + foreach ($tmparray as $key => $val) { + try { + $listlines->setVars($key, $val, true, 'UTF-8'); + } catch (OdfException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } catch (SegmentException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); + } + } catch (OdfException $e) { + $this->error = $e->getMessage(); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + + // Replace labels translated + $tmparray = $outputlangs->get_translations_for_substitutions(); + foreach ($tmparray as $key => $value) { + try { + $odfHandler->setVars($key, $value, true, 'UTF-8'); + } catch (OdfException $e) { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + + // Call the beforeODTSave hook + + $parameters = array('odfHandler'=>&$odfHandler, 'file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs, 'substitutionarray'=>&$tmparray); + $reshook = $hookmanager->executeHooks('beforeODTSave', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + // Write new file + if (!empty($conf->global->MAIN_ODT_AS_PDF)) { + try { + $odfHandler->exportAsAttachedPDF($file); + } catch (Exception $e) { + $this->error = $e->getMessage(); + dol_syslog($e->getMessage(), LOG_INFO); + return -1; + } + } else { + try { + $odfHandler->saveToDisk($file); + } catch (Exception $e) { + $this->error = $e->getMessage(); + dol_syslog($e->getMessage(), LOG_INFO); + return -1; + } + } + + $parameters = array('odfHandler'=>&$odfHandler, 'file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs, 'substitutionarray'=>&$tmparray); + $reshook = $hookmanager->executeHooks('afterODTCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + if (!empty($conf->global->MAIN_UMASK)) { + @chmod($file, octdec($conf->global->MAIN_UMASK)); + } + + $odfHandler = null; // Destroy object + + $this->result = array('fullpath'=>$file); + + return 1; // Success + } else { + $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir); + return -1; + } + } + + return -1; + } +} diff --git a/htdocs/core/modules/asset/doc/pdf_standard_asset.modules.php b/htdocs/core/modules/asset/doc/pdf_standard_asset.modules.php new file mode 100644 index 00000000000..de378ee79a8 --- /dev/null +++ b/htdocs/core/modules/asset/doc/pdf_standard_asset.modules.php @@ -0,0 +1,1375 @@ + + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2008 Raphael Bertrand + * Copyright (C) 2010-2014 Juanjo Menent + * Copyright (C) 2012 Christophe Battarel + * Copyright (C) 2012 Cédric Salvador + * Copyright (C) 2012-2014 Raphaël Doursenaud + * Copyright (C) 2015 Marcos García + * Copyright (C) 2017 Ferran Marcet + * Copyright (C) 2018 Frédéric France + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ + +/** + * \file core/modules/asset/doc/pdf_standard.modules.php + * \ingroup asset + * \brief File of class to generate document from standard template + */ + +dol_include_once('/asset/core/modules/asset/modules_asset.php'); +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; + + +/** + * Class to manage PDF template standard_asset + */ +class pdf_standard_asset extends ModelePDFAsset +{ + /** + * @var DoliDb Database handler + */ + public $db; + + /** + * @var string model name + */ + public $name; + + /** + * @var string model description (short text) + */ + public $description; + + /** + * @var int Save the name of generated file as the main doc when generating a doc with this template + */ + public $update_main_doc_field; + + /** + * @var string document type + */ + public $type; + + /** + * @var array Minimum version of PHP required by module. + * e.g.: PHP ≥ 5.6 = array(5, 6) + */ + public $phpmin = array(5, 6); + + /** + * Dolibarr version of the loaded document + * @var string + */ + public $version = 'dolibarr'; + + /** + * @var int page_largeur + */ + public $page_largeur; + + /** + * @var int page_hauteur + */ + public $page_hauteur; + + /** + * @var array format + */ + public $format; + + /** + * @var int marge_gauche + */ + public $marge_gauche; + + /** + * @var int marge_droite + */ + public $marge_droite; + + /** + * @var int marge_haute + */ + public $marge_haute; + + /** + * @var int marge_basse + */ + public $marge_basse; + + /** + * Issuer + * @var Societe Object that emits + */ + public $emetteur; + + /** + * @var bool Situation invoice type + */ + public $situationinvoice; + + + /** + * @var array of document table columns + */ + public $cols; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + global $conf, $langs, $mysoc; + + // Translations + $langs->loadLangs(array("main", "bills")); + + $this->db = $db; + $this->name = "standard"; + $this->description = $langs->trans('DocumentModelStandardPDF'); + $this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template + + // Dimension page + $this->type = 'pdf'; + $formatarray = pdf_getFormat(); + $this->page_largeur = $formatarray['width']; + $this->page_hauteur = $formatarray['height']; + $this->format = array($this->page_largeur, $this->page_hauteur); + $this->marge_gauche = isset($conf->global->MAIN_PDF_MARGIN_LEFT) ? $conf->global->MAIN_PDF_MARGIN_LEFT : 10; + $this->marge_droite = isset($conf->global->MAIN_PDF_MARGIN_RIGHT) ? $conf->global->MAIN_PDF_MARGIN_RIGHT : 10; + $this->marge_haute = isset($conf->global->MAIN_PDF_MARGIN_TOP) ? $conf->global->MAIN_PDF_MARGIN_TOP : 10; + $this->marge_basse = isset($conf->global->MAIN_PDF_MARGIN_BOTTOM) ? $conf->global->MAIN_PDF_MARGIN_BOTTOM : 10; + + // Get source company + $this->emetteur = $mysoc; + if (empty($this->emetteur->country_code)) { + $this->emetteur->country_code = substr($langs->defaultlang, -2); // By default, if was not defined + } + + // Define position of columns + $this->posxdesc = $this->marge_gauche + 1; // used for notes ans other stuff + + + $this->tabTitleHeight = 5; // default height + + // Use new system for position of columns, view $this->defineColumnField() + + $this->tva = array(); + $this->localtax1 = array(); + $this->localtax2 = array(); + $this->atleastoneratenotnull = 0; + $this->atleastonediscount = 0; + $this->situationinvoice = false; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Function to build pdf onto disk + * + * @param Object $object Object to generate + * @param Translate $outputlangs Lang output object + * @param string $srctemplatepath Full path of source filename for generator using a template file + * @param int $hidedetails Do not show line details + * @param int $hidedesc Do not show desc + * @param int $hideref Do not show ref + * @return int 1=OK, 0=KO + */ + public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0) + { + // phpcs:enable + global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblines; + + dol_syslog("write_file outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null')); + + if (!is_object($outputlangs)) { + $outputlangs = $langs; + } + // For backward compatibility with FPDF, force output charset to ISO, because FPDF expect text to be encoded in ISO + if (!empty($conf->global->MAIN_USE_FPDF)) { + $outputlangs->charset_output = 'ISO-8859-1'; + } + + // Load translation files required by the page + $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies")); + + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && $outputlangs->defaultlang != $conf->global->PDF_USE_ALSO_LANGUAGE_CODE) { + global $outputlangsbis; + $outputlangsbis = new Translate('', $conf); + $outputlangsbis->setDefaultLang($conf->global->PDF_USE_ALSO_LANGUAGE_CODE); + $outputlangsbis->loadLangs(array("main", "bills", "products", "dict", "companies")); + } + + $nblines = (is_array($object->lines) ? count($object->lines) : 0); + + $hidetop = 0; + if (!empty($conf->global->MAIN_PDF_DISABLE_COL_HEAD_TITLE)) { + $hidetop = $conf->global->MAIN_PDF_DISABLE_COL_HEAD_TITLE; + } + + // Loop on each lines to detect if there is at least one image to show + $realpatharray = array(); + $this->atleastonephoto = false; + /* + if (!empty($conf->global->MAIN_GENERATE_ASSET_WITH_PICTURE)) + { + $objphoto = new Product($this->db); + + for ($i = 0; $i < $nblines; $i++) + { + if (empty($object->lines[$i]->fk_product)) continue; + + //var_dump($objphoto->ref);exit; + if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { + $pdir[0] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product').$objphoto->id."/photos/"; + $pdir[1] = get_exdir(0, 0, 0, 0, $objphoto, 'product').dol_sanitizeFileName($objphoto->ref).'/'; + } else { + $pdir[0] = get_exdir(0, 0, 0, 0, $objphoto, 'product'); // default + $pdir[1] = get_exdir($objphoto->id, 2, 0, 0, $objphoto, 'product').$objphoto->id."/photos/"; // alternative + } + + $arephoto = false; + foreach ($pdir as $midir) + { + if (!$arephoto) + { + $dir = $conf->product->dir_output.'/'.$midir; + + foreach ($objphoto->liste_photos($dir, 1) as $key => $obj) + { + if (empty($conf->global->CAT_HIGH_QUALITY_IMAGES)) // If CAT_HIGH_QUALITY_IMAGES not defined, we use thumb if defined and then original photo + { + if ($obj['photo_vignette']) + { + $filename = $obj['photo_vignette']; + } else { + $filename = $obj['photo']; + } + } else { + $filename = $obj['photo']; + } + + $realpath = $dir.$filename; + $arephoto = true; + $this->atleastonephoto = true; + } + } + } + + if ($realpath && $arephoto) $realpatharray[$i] = $realpath; + } + } + */ + + //if (count($realpatharray) == 0) $this->posxpicture=$this->posxtva; + + if ($conf->asset->dir_output.'/asset') { + $object->fetch_thirdparty(); + + // Definition of $dir and $file + if ($object->specimen) { + $dir = $conf->asset->dir_output.'/asset'; + $file = $dir."/SPECIMEN.pdf"; + } else { + $objectref = dol_sanitizeFileName($object->ref); + $dir = $conf->asset->dir_output.'/asset/'.$objectref; + $file = $dir."/".$objectref.".pdf"; + } + if (!file_exists($dir)) { + if (dol_mkdir($dir) < 0) { + $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir); + return 0; + } + } + + if (file_exists($dir)) { + // Add pdfgeneration hook + if (!is_object($hookmanager)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager = new HookManager($this->db); + } + $hookmanager->initHooks(array('pdfgeneration')); + $parameters = array('file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs); + global $action; + $reshook = $hookmanager->executeHooks('beforePDFCreation', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + + // Set nblines with the new facture lines content after hook + $nblines = (is_array($object->lines) ? count($object->lines) : 0); + + // Create pdf instance + $pdf = pdf_getInstance($this->format); + $default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance + $pdf->SetAutoPageBreak(1, 0); + + $heightforinfotot = 50; // Height reserved to output the info and total part and payment part + $heightforfreetext = (isset($conf->global->MAIN_PDF_FREETEXT_HEIGHT) ? $conf->global->MAIN_PDF_FREETEXT_HEIGHT : 5); // Height reserved to output the free text on last page + $heightforfooter = $this->marge_basse + (empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS) ? 12 : 22); // Height reserved to output the footer (value include bottom margin) + + if (class_exists('TCPDF')) { + $pdf->setPrintHeader(false); + $pdf->setPrintFooter(false); + } + $pdf->SetFont(pdf_getPDFFont($outputlangs)); + + // Set path to the background PDF File + if (!empty($conf->global->MAIN_ADD_PDF_BACKGROUND)) { + $pagecount = $pdf->setSourceFile($conf->mycompany->multidir_output[$object->entity].'/'.$conf->global->MAIN_ADD_PDF_BACKGROUND); + $tplidx = $pdf->importPage(1); + } + + $pdf->Open(); + $pagenb = 0; + $pdf->SetDrawColor(128, 128, 128); + + $pdf->SetTitle($outputlangs->convToOutputCharset($object->ref)); + $pdf->SetSubject($outputlangs->transnoentities("PdfTitle")); + $pdf->SetCreator("Dolibarr ".DOL_VERSION); + $pdf->SetAuthor($outputlangs->convToOutputCharset($user->getFullName($outputlangs))); + $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name)); + if (!empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) { + $pdf->SetCompression(false); + } + + // Set certificate + $cert = empty($user->conf->CERTIFICATE_CRT) ? '' : $user->conf->CERTIFICATE_CRT; + // If user has no certificate, we try to take the company one + if (!$cert) { + $cert = empty($conf->global->CERTIFICATE_CRT) ? '' : $conf->global->CERTIFICATE_CRT; + } + // If a certificate is found + if ($cert) { + $info = array( + 'Name' => $this->emetteur->name, + 'Location' => getCountry($this->emetteur->country_code, 0), + 'Reason' => 'ASSET', + 'ContactInfo' => $this->emetteur->email + ); + $pdf->setSignature($cert, $cert, $this->emetteur->name, '', 2, $info); + } + + $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right + + // New page + $pdf->AddPage(); + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + $pagenb++; + + $top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs, $outputlangsbis); + $pdf->SetFont('', '', $default_font_size - 1); + $pdf->MultiCell(0, 3, ''); // Set interline to 3 + $pdf->SetTextColor(0, 0, 0); + + $tab_top = 90 + $top_shift; + $tab_top_newpage = (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD) ? 42 + $top_shift : 10); + $tab_height = 130 - $top_shift; + $tab_height_newpage = 150; + if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { + $tab_height_newpage -= $top_shift; + } + + $nexY = $tab_top - 1; + + // Display notes + $notetoshow = empty($object->note_public) ? '' : $object->note_public; + // Extrafields in note + $extranote = $this->getExtrafieldsInHtml($object, $outputlangs); + if (!empty($extranote)) { + $notetoshow = dol_concatdesc($notetoshow, $extranote); + } + + $pagenb = $pdf->getPage(); + if ($notetoshow) { + $tab_top -= 2; + + $tab_width = $this->page_largeur - $this->marge_gauche - $this->marge_droite; + $pageposbeforenote = $pagenb; + + $substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object); + complete_substitutions_array($substitutionarray, $outputlangs, $object); + $notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs); + $notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow); + + $pdf->startTransaction(); + + $pdf->SetFont('', '', $default_font_size - 1); + $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top, dol_htmlentitiesbr($notetoshow), 0, 1); + // Description + $pageposafternote = $pdf->getPage(); + $posyafter = $pdf->GetY(); + + if ($pageposafternote > $pageposbeforenote) { + $pdf->rollbackTransaction(true); + + // prepare pages to receive notes + while ($pagenb < $pageposafternote) { + $pdf->AddPage(); + $pagenb++; + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { + $this->_pagehead($pdf, $object, 0, $outputlangs); + } + // $this->_pagefoot($pdf,$object,$outputlangs,1); + $pdf->setTopMargin($tab_top_newpage); + // The only function to edit the bottom margin of current page to set it. + $pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext); + } + + // back to start + $pdf->setPage($pageposbeforenote); + $pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext); + $pdf->SetFont('', '', $default_font_size - 1); + $pdf->writeHTMLCell(190, 3, $this->posxdesc - 1, $tab_top, dol_htmlentitiesbr($notetoshow), 0, 1); + $pageposafternote = $pdf->getPage(); + + $posyafter = $pdf->GetY(); + + if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + 20))) { // There is no space left for total+free text + $pdf->AddPage('', '', true); + $pagenb++; + $pageposafternote++; + $pdf->setPage($pageposafternote); + $pdf->setTopMargin($tab_top_newpage); + // The only function to edit the bottom margin of current page to set it. + $pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext); + //$posyafter = $tab_top_newpage; + } + + + // apply note frame to previous pages + $i = $pageposbeforenote; + while ($i < $pageposafternote) { + $pdf->setPage($i); + + + $pdf->SetDrawColor(128, 128, 128); + // Draw note frame + if ($i > $pageposbeforenote) { + $height_note = $this->page_hauteur - ($tab_top_newpage + $heightforfooter); + $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 1); + } else { + $height_note = $this->page_hauteur - ($tab_top + $heightforfooter); + $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 1); + } + + // Add footer + $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it. + $this->_pagefoot($pdf, $object, $outputlangs, 1); + + $i++; + } + + // apply note frame to last page + $pdf->setPage($pageposafternote); + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { + $this->_pagehead($pdf, $object, 0, $outputlangs); + } + $height_note = $posyafter - $tab_top_newpage; + $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 1); + } else // No pagebreak + { + $pdf->commitTransaction(); + $posyafter = $pdf->GetY(); + $height_note = $posyafter - $tab_top; + $pdf->Rect($this->marge_gauche, $tab_top - 1, $tab_width, $height_note + 1); + + + if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + 20))) { + // not enough space, need to add page + $pdf->AddPage('', '', true); + $pagenb++; + $pageposafternote++; + $pdf->setPage($pageposafternote); + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { + $this->_pagehead($pdf, $object, 0, $outputlangs); + } + + $posyafter = $tab_top_newpage; + } + } + + $tab_height = $tab_height - $height_note; + $tab_top = $posyafter + 6; + } else { + $height_note = 0; + } + + // Use new auto column system + $this->prepareArrayColumnField($object, $outputlangs, $hidedetails, $hidedesc, $hideref); + + // Table simulation to know the height of the title line + $pdf->startTransaction(); + $this->pdfTabTitles($pdf, $tab_top, $tab_height, $outputlangs, $hidetop); + $pdf->rollbackTransaction(true); + + $nexY = $tab_top + $this->tabTitleHeight; + + // Loop on each lines + $pageposbeforeprintlines = $pdf->getPage(); + $pagenb = $pageposbeforeprintlines; + for ($i = 0; $i < $nblines; $i++) { + $curY = $nexY; + $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage + $pdf->SetTextColor(0, 0, 0); + + // Define size of image if we need it + $imglinesize = array(); + if (!empty($realpatharray[$i])) { + $imglinesize = pdf_getSizeForImage($realpatharray[$i]); + } + + $pdf->setTopMargin($tab_top_newpage); + $pdf->setPageOrientation('', 1, $heightforfooter + $heightforfreetext + $heightforinfotot); // The only function to edit the bottom margin of current page to set it. + $pageposbefore = $pdf->getPage(); + + $showpricebeforepagebreak = 1; + $posYAfterImage = 0; + + if ($this->getColumnStatus('photo')) { + // We start with Photo of product line + if (isset($imglinesize['width']) && isset($imglinesize['height']) && ($curY + $imglinesize['height']) > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) { // If photo too high, we moved completely on new page + $pdf->AddPage('', '', true); + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + $pdf->setPage($pageposbefore + 1); + + $curY = $tab_top_newpage; + + // Allows data in the first page if description is long enough to break in multiples pages + if (!empty($conf->global->MAIN_PDF_DATA_ON_FIRST_PAGE)) { + $showpricebeforepagebreak = 1; + } else { + $showpricebeforepagebreak = 0; + } + } + + if (!empty($this->cols['photo']) && isset($imglinesize['width']) && isset($imglinesize['height'])) { + $pdf->Image($realpatharray[$i], $this->getColumnContentXStart('photo'), $curY, $imglinesize['width'], $imglinesize['height'], '', '', '', 2, 300); // Use 300 dpi + // $pdf->Image does not increase value return by getY, so we save it manually + $posYAfterImage = $curY + $imglinesize['height']; + } + } + + // Description of product line + if ($this->getColumnStatus('desc')) { + $pdf->startTransaction(); + + $this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc); + $pageposafter = $pdf->getPage(); + + if ($pageposafter > $pageposbefore) { // There is a pagebreak + $pdf->rollbackTransaction(true); + $pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it. + + $this->printColDescContent($pdf, $curY, 'desc', $object, $i, $outputlangs, $hideref, $hidedesc); + + $pageposafter = $pdf->getPage(); + $posyafter = $pdf->GetY(); + //var_dump($posyafter); var_dump(($this->page_hauteur - ($heightforfooter+$heightforfreetext+$heightforinfotot))); exit; + if ($posyafter > ($this->page_hauteur - ($heightforfooter + $heightforfreetext + $heightforinfotot))) { // There is no space left for total+free text + if ($i == ($nblines - 1)) { // No more lines, and no space left to show total, so we create a new page + $pdf->AddPage('', '', true); + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + $pdf->setPage($pageposafter + 1); + } + } else { + // We found a page break + // Allows data in the first page if description is long enough to break in multiples pages + if (!empty($conf->global->MAIN_PDF_DATA_ON_FIRST_PAGE)) { + $showpricebeforepagebreak = 1; + } else { + $showpricebeforepagebreak = 0; + } + } + } else // No pagebreak + { + $pdf->commitTransaction(); + } + } + + $nexY = $pdf->GetY(); + $pageposafter = $pdf->getPage(); + $pdf->setPage($pageposbefore); + $pdf->setTopMargin($this->marge_haute); + $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it. + + // We suppose that a too long description or photo were moved completely on next page + if ($pageposafter > $pageposbefore && empty($showpricebeforepagebreak)) { + $pdf->setPage($pageposafter); $curY = $tab_top_newpage; + } + + $pdf->SetFont('', '', $default_font_size - 1); // On repositionne la police par defaut + + // Quantity + // Enough for 6 chars + if ($this->getColumnStatus('qty')) { + $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'qty', $qty); + $nexY = max($pdf->GetY(), $nexY); + } + + // Extrafields + if (!empty($object->lines[$i]->array_options)) { + foreach ($object->lines[$i]->array_options as $extrafieldColKey => $extrafieldValue) { + if ($this->getColumnStatus($extrafieldColKey)) { + $extrafieldValue = $this->getExtrafieldContent($object->lines[$i], $extrafieldColKey, $outputlangs); + $this->printStdColumnContent($pdf, $curY, $extrafieldColKey, $extrafieldValue); + $nexY = max($pdf->GetY(), $nexY); + } + } + } + + + $parameters = array( + 'object' => $object, + 'i' => $i, + 'pdf' =>& $pdf, + 'curY' =>& $curY, + 'nexY' =>& $nexY, + 'outputlangs' => $outputlangs, + 'hidedetails' => $hidedetails + ); + $reshook = $hookmanager->executeHooks('printPDFline', $parameters, $this); // Note that $object may have been modified by hook + + + $sign = 1; + // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva + $prev_progress = $object->lines[$i]->get_prev_progress($object->id); + if ($prev_progress > 0 && !empty($object->lines[$i]->situation_percent)) { // Compute progress from previous situation + if (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) { + $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; + } else { + $tvaligne = $sign * $object->lines[$i]->total_tva * ($object->lines[$i]->situation_percent - $prev_progress) / $object->lines[$i]->situation_percent; + } + } else { + if (!empty($conf->multicurrency->enabled) && $object->multicurrency_tx != 1) { + $tvaligne = $sign * $object->lines[$i]->multicurrency_total_tva; + } else { + $tvaligne = $sign * $object->lines[$i]->total_tva; + } + } + + $localtax1ligne = $object->lines[$i]->total_localtax1; + $localtax2ligne = $object->lines[$i]->total_localtax2; + $localtax1_rate = $object->lines[$i]->localtax1_tx; + $localtax2_rate = $object->lines[$i]->localtax2_tx; + $localtax1_type = $object->lines[$i]->localtax1_type; + $localtax2_type = $object->lines[$i]->localtax2_type; + + if ($object->remise_percent) { + $tvaligne -= ($tvaligne * $object->remise_percent) / 100; + } + if ($object->remise_percent) { + $localtax1ligne -= ($localtax1ligne * $object->remise_percent) / 100; + } + if ($object->remise_percent) { + $localtax2ligne -= ($localtax2ligne * $object->remise_percent) / 100; + } + + $vatrate = (string) $object->lines[$i]->tva_tx; + + // Retrieve type from database for backward compatibility with old records + if ((!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined + && (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax + $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc); + $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : ''; + $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : ''; + } + + // retrieve global local tax + if ($localtax1_type && $localtax1ligne != 0) { + $this->localtax1[$localtax1_type][$localtax1_rate] += $localtax1ligne; + } + if ($localtax2_type && $localtax2ligne != 0) { + $this->localtax2[$localtax2_type][$localtax2_rate] += $localtax2ligne; + } + + if (($object->lines[$i]->info_bits & 0x01) == 0x01) { + $vatrate .= '*'; + } + if (!isset($this->tva[$vatrate])) { + $this->tva[$vatrate] = 0; + } + $this->tva[$vatrate] += $tvaligne; + + $nexY = max($nexY, $posYAfterImage); + + // Add line + if (!empty($conf->global->MAIN_PDF_DASH_BETWEEN_LINES) && $i < ($nblines - 1)) { + $pdf->setPage($pageposafter); + $pdf->SetLineStyle(array('dash'=>'1,1', 'color'=>array(80, 80, 80))); + //$pdf->SetDrawColor(190,190,200); + $pdf->line($this->marge_gauche, $nexY, $this->page_largeur - $this->marge_droite, $nexY); + $pdf->SetLineStyle(array('dash'=>0)); + } + + // Detect if some page were added automatically and output _tableau for past pages + while ($pagenb < $pageposafter) { + $pdf->setPage($pagenb); + if ($pagenb == $pageposbeforeprintlines) { + $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis); + } else { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis); + } + $this->_pagefoot($pdf, $object, $outputlangs, 1); + $pagenb++; + $pdf->setPage($pagenb); + $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it. + if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { + $this->_pagehead($pdf, $object, 0, $outputlangs); + } + } + + if (isset($object->lines[$i + 1]->pagebreak) && $object->lines[$i + 1]->pagebreak) { + if ($pagenb == $pageposafter) { + $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforfooter, 0, $outputlangs, $hidetop, 1, $object->multicurrency_code, $outputlangsbis); + } else { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code, $outputlangsbis); + } + $this->_pagefoot($pdf, $object, $outputlangs, 1); + // New page + $pdf->AddPage(); + if (!empty($tplidx)) { + $pdf->useTemplate($tplidx); + } + $pagenb++; + if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { + $this->_pagehead($pdf, $object, 0, $outputlangs); + } + } + } + + // Show square + if ($pagenb == $pageposbeforeprintlines) { + $this->_tableau($pdf, $tab_top, $this->page_hauteur - $tab_top - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, $hidetop, 0, $object->multicurrency_code, $outputlangsbis); + $bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1; + } else { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code, $outputlangsbis); + $bottomlasttab = $this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1; + } + + // Display infos area + //$posy = $this->drawInfoTable($pdf, $object, $bottomlasttab, $outputlangs); + + // Display total zone + //$posy = $this->drawTotalTable($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs); + + // Display payment area + /* + if (($deja_regle || $amount_credit_notes_included || $amount_deposits_included) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) + { + $posy = $this->drawPaymentsTable($pdf, $object, $posy, $outputlangs); + } + */ + + // Pagefoot + $this->_pagefoot($pdf, $object, $outputlangs); + if (method_exists($pdf, 'AliasNbPages')) { + $pdf->AliasNbPages(); + } + + $pdf->Close(); + + $pdf->Output($file, 'F'); + + // Add pdfgeneration hook + $hookmanager->initHooks(array('pdfgeneration')); + $parameters = array('file'=>$file, 'object'=>$object, 'outputlangs'=>$outputlangs); + global $action; + $reshook = $hookmanager->executeHooks('afterPDFCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + $this->error = $hookmanager->error; + $this->errors = $hookmanager->errors; + } + + if (!empty($conf->global->MAIN_UMASK)) { + @chmod($file, octdec($conf->global->MAIN_UMASK)); + } + + $this->result = array('fullpath'=>$file); + + return 1; // No error + } else { + $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir); + return 0; + } + } else { + $this->error = $langs->transnoentities("ErrorConstantNotDefined", "FAC_OUTPUTDIR"); + return 0; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of active generation modules + * + * @param DoliDB $db Database handler + * @param integer $maxfilenamelength Max length of value to show + * @return array List of templates + */ + public static function liste_modeles($db, $maxfilenamelength = 0) + { + // phpcs:enable + return parent::liste_modeles($db, $maxfilenamelength); // TODO: Change the autogenerated stub + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Show table for lines + * + * @param tcpdf $pdf Object PDF + * @param string $tab_top Top position of table + * @param string $tab_height Height of table (rectangle) + * @param int $nexY Y (not used) + * @param Translate $outputlangs Langs object + * @param int $hidetop 1=Hide top bar of array and title, 0=Hide nothing, -1=Hide only title + * @param int $hidebottom Hide bottom bar of array + * @param string $currency Currency code + * @param Translate $outputlangsbis Langs object bis + * @return void + */ + protected function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop = 0, $hidebottom = 0, $currency = '', $outputlangsbis = null) + { + global $conf; + + // Force to disable hidetop and hidebottom + $hidebottom = 0; + if ($hidetop) { + $hidetop = -1; + } + + $currency = !empty($currency) ? $currency : $conf->currency; + $default_font_size = pdf_getPDFFontSize($outputlangs); + + // Amount in (at tab_top - 1) + $pdf->SetTextColor(0, 0, 0); + $pdf->SetFont('', '', $default_font_size - 2); + + if (empty($hidetop)) { + $titre = $outputlangs->transnoentities("AmountInCurrency", $outputlangs->transnoentitiesnoconv("Currency".$currency)); + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) { + $titre .= ' - '.$outputlangsbis->transnoentities("AmountInCurrency", $outputlangsbis->transnoentitiesnoconv("Currency".$currency)); + } + + $pdf->SetXY($this->page_largeur - $this->marge_droite - ($pdf->GetStringWidth($titre) + 3), $tab_top - 4); + $pdf->MultiCell(($pdf->GetStringWidth($titre) + 3), 2, $titre); + + //$conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR='230,230,230'; + if (!empty($conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR)) { + $pdf->Rect($this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_droite - $this->marge_gauche, $this->tabTitleHeight, 'F', null, explode(',', $conf->global->MAIN_PDF_TITLE_BACKGROUND_COLOR)); + } + } + + $pdf->SetDrawColor(128, 128, 128); + $pdf->SetFont('', '', $default_font_size - 1); + + // Output Rect + $this->printRect($pdf, $this->marge_gauche, $tab_top, $this->page_largeur - $this->marge_gauche - $this->marge_droite, $tab_height, $hidetop, $hidebottom); // Rect takes a length in 3rd parameter and 4th parameter + + + $this->pdfTabTitles($pdf, $tab_top, $tab_height, $outputlangs, $hidetop); + + if (empty($hidetop)) { + $pdf->line($this->marge_gauche, $tab_top + $this->tabTitleHeight, $this->page_largeur - $this->marge_droite, $tab_top + $this->tabTitleHeight); // line takes a position y in 2nd parameter and 4th parameter + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Show top header of page. + * + * @param Tcpdf $pdf Object PDF + * @param Object $object Object to show + * @param int $showaddress 0=no, 1=yes + * @param Translate $outputlangs Object lang for output + * @param Translate $outputlangsbis Object lang for output bis + * @return void + */ + protected function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $outputlangsbis = null) + { + global $conf, $langs; + + // Load traductions files required by page + $outputlangs->loadLangs(array("main", "bills", "propal", "companies")); + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + pdf_pagehead($pdf, $outputlangs, $this->page_hauteur); + + // Show Draft Watermark + if ($object->statut == $object::STATUS_DRAFT && (!empty($conf->global->FACTURE_DRAFT_WATERMARK))) { + pdf_watermark($pdf, $outputlangs, $this->page_hauteur, $this->page_largeur, 'mm', $conf->global->FACTURE_DRAFT_WATERMARK); + } + + $pdf->SetTextColor(0, 0, 60); + $pdf->SetFont('', 'B', $default_font_size + 3); + + $w = 110; + + $posy = $this->marge_haute; + $posx = $this->page_largeur - $this->marge_droite - $w; + + $pdf->SetXY($this->marge_gauche, $posy); + + // Logo + if (empty($conf->global->PDF_DISABLE_MYCOMPANY_LOGO)) { + if ($this->emetteur->logo) { + $logodir = $conf->mycompany->dir_output; + if (!empty($conf->mycompany->multidir_output[$object->entity])) { + $logodir = $conf->mycompany->multidir_output[$object->entity]; + } + if (empty($conf->global->MAIN_PDF_USE_LARGE_LOGO)) { + $logo = $logodir.'/logos/thumbs/'.$this->emetteur->logo_small; + } else { + $logo = $logodir.'/logos/'.$this->emetteur->logo; + } + if (is_readable($logo)) { + $height = pdf_getHeightForLogo($logo); + $pdf->Image($logo, $this->marge_gauche, $posy, 0, $height); // width=0 (auto) + } else { + $pdf->SetTextColor(200, 0, 0); + $pdf->SetFont('', 'B', $default_font_size - 2); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound", $logo), 0, 'L'); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L'); + } + } else { + $text = $this->emetteur->name; + $pdf->MultiCell($w, 4, $outputlangs->convToOutputCharset($text), 0, 'L'); + } + } + + $pdf->SetFont('', 'B', $default_font_size + 3); + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $title = $outputlangs->transnoentities("PdfTitle"); + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) { + $title .= ' - '; + $title .= $outputlangsbis->transnoentities("PdfTitle"); + } + $pdf->MultiCell($w, 3, $title, '', 'R'); + + $pdf->SetFont('', 'B', $default_font_size); + + $posy += 5; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref); + if ($object->statut == $object::STATUS_DRAFT) { + $pdf->SetTextColor(128, 0, 0); + $textref .= ' - '.$outputlangs->transnoentities("NotValidated"); + } + $pdf->MultiCell($w, 4, $textref, '', 'R'); + + $posy += 1; + $pdf->SetFont('', '', $default_font_size - 2); + + if ($object->ref_client) { + $posy += 4; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefCustomer")." : ".$outputlangs->convToOutputCharset($object->ref_client), '', 'R'); + } + + if (!empty($conf->global->PDF_SHOW_PROJECT_TITLE)) { + $object->fetch_projet(); + if (!empty($object->project->ref)) { + $posy += 3; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("Project")." : ".(empty($object->project->title) ? '' : $object->projet->title), '', 'R'); + } + } + + if (!empty($conf->global->PDF_SHOW_PROJECT)) { + $object->fetch_projet(); + if (!empty($object->project->ref)) { + $outputlangs->load("projects"); + $posy += 3; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("RefProject")." : ".(empty($object->project->ref) ? '' : $object->project->ref), '', 'R'); + } + } + + $posy += 4; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + + $title = $outputlangs->transnoentities("Date"); + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE) && is_object($outputlangsbis)) { + $title .= ' - '.$outputlangsbis->transnoentities("Date"); + } + $pdf->MultiCell($w, 3, $title." : ".dol_print_date($object->date, "day", false, $outputlangs), '', 'R'); + + if ($object->thirdparty->code_client) { + $posy += 3; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CustomerCode")." : ".$outputlangs->transnoentities($object->thirdparty->code_client), '', 'R'); + } + + // Get contact + if (!empty($conf->global->DOC_SHOW_FIRST_SALES_REP)) { + $arrayidcontact = $object->getIdContact('internal', 'SALESREPFOLL'); + if (count($arrayidcontact) > 0) { + $usertmp = new User($this->db); + $usertmp->fetch($arrayidcontact[0]); + $posy += 4; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $pdf->MultiCell($w, 3, $langs->transnoentities("SalesRepresentative")." : ".$usertmp->getFullName($langs), '', 'R'); + } + } + + $posy += 1; + + $top_shift = 0; + // Show list of linked objects + $current_y = $pdf->getY(); + $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, $w, 3, 'R', $default_font_size); + if ($current_y < $pdf->getY()) { + $top_shift = $pdf->getY() - $current_y; + } + + if ($showaddress) { + // Sender properties + $carac_emetteur = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); + + // Show sender + $posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42; + $posy += $top_shift; + $posx = $this->marge_gauche; + if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) { + $posx = $this->page_largeur - $this->marge_droite - 80; + } + + $hautcadre = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 38 : 40; + $widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 82; + + + // Show sender frame + $pdf->SetTextColor(0, 0, 0); + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($posx, $posy - 5); + $pdf->MultiCell(66, 5, $outputlangs->transnoentities("BillFrom").":", 0, 'L'); + $pdf->SetXY($posx, $posy); + $pdf->SetFillColor(230, 230, 230); + $pdf->MultiCell($widthrecbox, $hautcadre, "", 0, 'R', 1); + $pdf->SetTextColor(0, 0, 60); + + // Show sender name + $pdf->SetXY($posx + 2, $posy + 3); + $pdf->SetFont('', 'B', $default_font_size); + $pdf->MultiCell($widthrecbox - 2, 4, $outputlangs->convToOutputCharset($this->emetteur->name), 0, 'L'); + $posy = $pdf->getY(); + + // Show sender information + $pdf->SetXY($posx + 2, $posy); + $pdf->SetFont('', '', $default_font_size - 1); + $pdf->MultiCell($widthrecbox - 2, 4, $carac_emetteur, 0, 'L'); + + // If BILLING contact defined on invoice, we use it + $usecontact = false; + $arrayidcontact = $object->getIdContact('external', 'BILLING'); + if (count($arrayidcontact) > 0) { + $usecontact = true; + $result = $object->fetch_contact($arrayidcontact[0]); + } + + // Recipient name + if ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT))) { + $thirdparty = $object->contact; + } else { + $thirdparty = $object->thirdparty; + } + + if (is_object($thirdparty)) { + $carac_client_name = pdfBuildThirdpartyName($thirdparty, $outputlangs); + } + + $carac_client = pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, ($usecontact ? $object->contact : ''), $usecontact, 'target', $object); + + // Show recipient + $widthrecbox = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 92 : 100; + if ($this->page_largeur < 210) { + $widthrecbox = 84; // To work with US executive format + } + $posy = !empty($conf->global->MAIN_PDF_USE_ISO_LOCATION) ? 40 : 42; + $posy += $top_shift; + $posx = $this->page_largeur - $this->marge_droite - $widthrecbox; + if (!empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) { + $posx = $this->marge_gauche; + } + + // Show recipient frame + $pdf->SetTextColor(0, 0, 0); + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($posx + 2, $posy - 5); + $pdf->MultiCell($widthrecbox, 5, $outputlangs->transnoentities("BillTo").":", 0, 'L'); + $pdf->Rect($posx, $posy, $widthrecbox, $hautcadre); + + // Show recipient name + $pdf->SetXY($posx + 2, $posy + 3); + $pdf->SetFont('', 'B', $default_font_size); + $pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, 'L'); + + $posy = $pdf->getY(); + + // Show recipient information + $pdf->SetFont('', '', $default_font_size - 1); + $pdf->SetXY($posx + 2, $posy); + $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); + } + + $pdf->SetTextColor(0, 0, 0); + return $top_shift; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Show footer of page. Need this->emetteur object + * + * @param TCPDF $pdf PDF + * @param Object $object Object to show + * @param Translate $outputlangs Object lang for output + * @param int $hidefreetext 1=Hide free text + * @return int Return height of bottom margin including footer text + */ + protected function _pagefoot(&$pdf, $object, $outputlangs, $hidefreetext = 0) + { + global $conf; + $showdetails = empty($conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS) ? 0 : $conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS; + return pdf_pagefoot($pdf, $outputlangs, 'INVOICE_FREE_TEXT', $this->emetteur, $this->marge_basse, $this->marge_gauche, $this->page_hauteur, $object, $showdetails, $hidefreetext); + } + + /** + * Define Array Column Field + * + * @param object $object common object + * @param Translate $outputlangs langs + * @param int $hidedetails Do not show line details + * @param int $hidedesc Do not show desc + * @param int $hideref Do not show ref + * @return null + */ + public function defineColumnField($object, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0) + { + global $conf, $hookmanager; + + // Default field style for content + $this->defaultContentsFieldsStyle = array( + 'align' => 'R', // R,C,L + 'padding' => array(1, 0.5, 1, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ); + + // Default field style for content + $this->defaultTitlesFieldsStyle = array( + 'align' => 'C', // R,C,L + 'padding' => array(0.5, 0, 0.5, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ); + + /* + * For exemple + $this->cols['theColKey'] = array( + 'rank' => $rank, // int : use for ordering columns + 'width' => 20, // the column width in mm + 'title' => array( + 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label + 'label' => ' ', // the final label : used fore final generated text + 'align' => 'L', // text alignement : R,C,L + 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + 'content' => array( + 'align' => 'L', // text alignement : R,C,L + 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + ); + */ + + $rank = 0; // do not use negative rank + $this->cols['desc'] = array( + 'rank' => $rank, + 'width' => false, // only for desc + 'status' => true, + 'title' => array( + 'textkey' => 'Designation', // use lang key is usefull in somme case with module + 'align' => 'L', + // 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label + // 'label' => ' ', // the final label + 'padding' => array(0.5, 0.5, 0.5, 0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + 'content' => array( + 'align' => 'L', + 'padding' => array(1, 0.5, 1, 1.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + ); + + // PHOTO + $rank = $rank + 10; + $this->cols['photo'] = array( + 'rank' => $rank, + 'width' => (empty($conf->global->MAIN_DOCUMENTS_WITH_PICTURE_WIDTH) ? 20 : $conf->global->MAIN_DOCUMENTS_WITH_PICTURE_WIDTH), // in mm + 'status' => false, + 'title' => array( + 'textkey' => 'Photo', + 'label' => ' ' + ), + 'content' => array( + 'padding' => array(0, 0, 0, 0), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + 'border-left' => false, // remove left line separator + ); + + if (!empty($conf->global->MAIN_GENERATE_INVOICES_WITH_PICTURE) && !empty($this->atleastonephoto)) { + $this->cols['photo']['status'] = true; + } + + + $rank = $rank + 10; + $this->cols['vat'] = array( + 'rank' => $rank, + 'status' => false, + 'width' => 16, // in mm + 'title' => array( + 'textkey' => 'VAT' + ), + 'border-left' => true, // add left line separator + ); + + if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT) && empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_COLUMN)) { + $this->cols['vat']['status'] = true; + } + + $rank = $rank + 10; + $this->cols['subprice'] = array( + 'rank' => $rank, + 'width' => 19, // in mm + 'status' => true, + 'title' => array( + 'textkey' => 'PriceUHT' + ), + 'border-left' => true, // add left line separator + ); + + $rank = $rank + 10; + $this->cols['qty'] = array( + 'rank' => $rank, + 'width' => 16, // in mm + 'status' => true, + 'title' => array( + 'textkey' => 'Qty' + ), + 'border-left' => true, // add left line separator + ); + + $rank = $rank + 10; + $this->cols['progress'] = array( + 'rank' => $rank, + 'width' => 19, // in mm + 'status' => false, + 'title' => array( + 'textkey' => 'Progress' + ), + 'border-left' => true, // add left line separator + ); + + if ($this->situationinvoice) { + $this->cols['progress']['status'] = true; + } + + $rank = $rank + 10; + $this->cols['unit'] = array( + 'rank' => $rank, + 'width' => 11, // in mm + 'status' => false, + 'title' => array( + 'textkey' => 'Unit' + ), + 'border-left' => true, // add left line separator + ); + if (!empty($conf->global->PRODUCT_USE_UNITS)) { + $this->cols['unit']['status'] = true; + } + + $rank = $rank + 10; + $this->cols['discount'] = array( + 'rank' => $rank, + 'width' => 13, // in mm + 'status' => false, + 'title' => array( + 'textkey' => 'ReductionShort' + ), + 'border-left' => true, // add left line separator + ); + if ($this->atleastonediscount) { + $this->cols['discount']['status'] = true; + } + + $rank = $rank + 1000; // add a big offset to be sure is the last col because default extrafield rank is 100 + $this->cols['totalexcltax'] = array( + 'rank' => $rank, + 'width' => 26, // in mm + 'status' => true, + 'title' => array( + 'textkey' => 'TotalHT' + ), + 'border-left' => true, // add left line separator + ); + + // Add extrafields cols + if (!empty($object->lines)) { + $line = reset($object->lines); + $this->defineColumnExtrafield($line, $outputlangs, $hidedetails); + } + + $parameters = array( + 'object' => $object, + 'outputlangs' => $outputlangs, + 'hidedetails' => $hidedetails, + 'hidedesc' => $hidedesc, + 'hideref' => $hideref + ); + + $reshook = $hookmanager->executeHooks('defineColumnField', $parameters, $this); // Note that $object may have been modified by hook + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } elseif (empty($reshook)) { + $this->cols = array_replace($this->cols, $hookmanager->resArray); // array_replace is used to preserve keys + } else { + $this->cols = $hookmanager->resArray; + } + } +} diff --git a/htdocs/core/modules/asset/mod_asset_advanced.php b/htdocs/core/modules/asset/mod_asset_advanced.php new file mode 100644 index 00000000000..0942604466c --- /dev/null +++ b/htdocs/core/modules/asset/mod_asset_advanced.php @@ -0,0 +1,147 @@ + + * Copyright (C) 2004-2007 Laurent Destailleur + * Copyright (C) 2005-2009 Regis Houssin + * Copyright (C) 2008 Raphael Bertrand (Resultic) + * Copyright (C) 2019 Frédéric France + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/asset/mod_asset_advanced.php + * \ingroup asset + * \brief File containing class for advanced numbering model of Asset + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/asset/modules_asset.php'; + +/** + * Class to manage customer Bom numbering rules advanced + */ +class mod_asset_advanced extends ModeleNumRefAsset +{ + /** + * Dolibarr version of the loaded document + * @var string + */ + public $version = 'dolibarr'; // 'development', 'experimental', 'dolibarr' + + /** + * @var string Error message + */ + public $error = ''; + + /** + * @var string name + */ + public $name = 'advanced'; + + + /** + * Returns the description of the numbering model + * + * @return string Texte descripif + */ + public function info() + { + global $conf, $langs, $db; + + $langs->load("bills"); + + $form = new Form($db); + + $texte = $langs->trans('GenericNumRefModelDesc')."
\n"; + $texte .= '
'; + $texte .= ''; + $texte .= ''; + $texte .= ''; + $texte .= ''; + + $tooltip = $langs->trans("GenericMaskCodes", $langs->transnoentities("Asset"), $langs->transnoentities("Asset")); + $tooltip .= $langs->trans("GenericMaskCodes2"); + $tooltip .= $langs->trans("GenericMaskCodes3"); + $tooltip .= $langs->trans("GenericMaskCodes4a", $langs->transnoentities("Asset"), $langs->transnoentities("Asset")); + $tooltip .= $langs->trans("GenericMaskCodes5"); + + // Parametrage du prefix + $texte .= ''; + $texte .= ''; + + $texte .= ''; + + $texte .= ''; + + $texte .= '
'.$langs->trans("Mask").':'.$form->textwithpicto('', $tooltip, 1, 1).' 
'; + $texte .= '
'; + + return $texte; + } + + /** + * Return an example of numbering + * + * @return string Example + */ + public function getExample() + { + global $conf, $db, $langs, $mysoc; + + $object = new Asset($db); + $object->initAsSpecimen(); + + /*$old_code_client = $mysoc->code_client; + $old_code_type = $mysoc->typent_code; + $mysoc->code_client = 'CCCCCCCCCC'; + $mysoc->typent_code = 'TTTTTTTTTT';*/ + + $numExample = $this->getNextValue($object); + + /*$mysoc->code_client = $old_code_client; + $mysoc->typent_code = $old_code_type;*/ + + if (!$numExample) { + $numExample = $langs->trans('NotConfigured'); + } + return $numExample; + } + + /** + * Return next free value + * + * @param Object $object Object we need next value for + * @return string Value if KO, <0 if KO + */ + public function getNextValue($object) + { + global $db, $conf; + + require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + + // We get cursor rule + $mask = $conf->global->ASSET_ASSET_ADVANCED_MASK; + + if (!$mask) { + $this->error = 'NotConfigured'; + return 0; + } + + $date = $object->date; + + $numFinal = get_next_value($db, $mask, 'asset_asset', 'ref', '', null, $date); + + return $numFinal; + } +} diff --git a/htdocs/core/modules/asset/mod_asset_standard.php b/htdocs/core/modules/asset/mod_asset_standard.php new file mode 100644 index 00000000000..af28c0f9c5b --- /dev/null +++ b/htdocs/core/modules/asset/mod_asset_standard.php @@ -0,0 +1,161 @@ + + * Copyright (C) 2005-2009 Regis Houssin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/asset/mod_asset_standard.php + * \ingroup asset + * \brief File of class to manage Asset numbering rules standard + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/asset/modules_asset.php'; + +/** + * Class to manage customer order numbering rules standard + */ +class mod_asset_standard extends ModeleNumRefAsset +{ + /** + * Dolibarr version of the loaded document + * @var string + */ + public $version = 'dolibarr'; // 'development', 'experimental', 'dolibarr' + + public $prefix = 'ASSET'; + + /** + * @var string Error code (or message) + */ + public $error = ''; + + /** + * @var string name + */ + public $name = 'standard'; + + + /** + * Return description of numbering module + * + * @return string Text with description + */ + public function info() + { + global $langs; + return $langs->trans("SimpleNumRefModelDesc", $this->prefix); + } + + + /** + * Return an example of numbering + * + * @return string Example + */ + public function getExample() + { + return $this->prefix."0501-0001"; + } + + + /** + * Checks if the numbers already in the database do not + * cause conflicts that would prevent this numbering working. + * + * @param Object $object Object we need next value for + * @return boolean false if conflict, true if ok + */ + public function canBeActivated($object) + { + global $conf, $langs, $db; + + $coyymm = ''; $max = ''; + + $posindice = strlen($this->prefix) + 6; + $sql = "SELECT MAX(CAST(SUBSTRING(ref FROM ".$posindice.") AS SIGNED)) as max"; + $sql .= " FROM ".MAIN_DB_PREFIX."asset_asset"; + $sql .= " WHERE ref LIKE '".$db->escape($this->prefix)."____-%'"; + if ($object->ismultientitymanaged == 1) { + $sql .= " AND entity = ".$conf->entity; + } elseif ($object->ismultientitymanaged == 2) { + // TODO + } + + $resql = $db->query($sql); + if ($resql) { + $row = $db->fetch_row($resql); + if ($row) { + $coyymm = substr($row[0], 0, 6); $max = $row[0]; + } + } + if ($coyymm && !preg_match('/'.$this->prefix.'[0-9][0-9][0-9][0-9]/i', $coyymm)) { + $langs->load("errors"); + $this->error = $langs->trans('ErrorNumRefModel', $max); + return false; + } + + return true; + } + + /** + * Return next free value + * + * @param Object $object Object we need next value for + * @return string Value if KO, <0 if KO + */ + public function getNextValue($object) + { + global $db, $conf; + + // first we get the max value + $posindice = strlen($this->prefix) + 6; + $sql = "SELECT MAX(CAST(SUBSTRING(ref FROM ".$posindice.") AS SIGNED)) as max"; + $sql .= " FROM ".MAIN_DB_PREFIX."asset_asset"; + $sql .= " WHERE ref LIKE '".$db->escape($this->prefix)."____-%'"; + if ($object->ismultientitymanaged == 1) { + $sql .= " AND entity = ".$conf->entity; + } elseif ($object->ismultientitymanaged == 2) { + // TODO + } + + $resql = $db->query($sql); + if ($resql) { + $obj = $db->fetch_object($resql); + if ($obj) { + $max = intval($obj->max); + } else { + $max = 0; + } + } else { + dol_syslog("mod_asset_standard::getNextValue", LOG_DEBUG); + return -1; + } + + //$date=time(); + $date = $object->date_creation; + $yymm = strftime("%y%m", $date); + + if ($max >= (pow(10, 4) - 1)) { + $num = $max + 1; // If counter > 9999, we do not format on 4 chars, we take number as it is + } else { + $num = sprintf("%04s", $max + 1); + } + + dol_syslog("mod_asset_standard::getNextValue return ".$this->prefix.$yymm."-".$num); + return $this->prefix.$yymm."-".$num; + } +} diff --git a/htdocs/core/modules/asset/modules_asset.php b/htdocs/core/modules/asset/modules_asset.php new file mode 100644 index 00000000000..e158b5bdfe9 --- /dev/null +++ b/htdocs/core/modules/asset/modules_asset.php @@ -0,0 +1,158 @@ + + * Copyright (C) 2004-2011 Laurent Destailleur + * Copyright (C) 2004 Eric Seigne + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2006 Andre Cianfarani + * Copyright (C) 2012 Juanjo Menent + * Copyright (C) 2014 Marcos García + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/asset/modules_asset.php + * \ingroup asset + * \brief File that contains parent class for assets document models and parent class for assets numbering models + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/commondocgenerator.class.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; // required for use by classes that inherit + + +/** + * Parent class for documents models + */ +abstract class ModelePDFAsset extends CommonDocGenerator +{ + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of active generation modules + * + * @param DoliDB $db Database handler + * @param integer $maxfilenamelength Max length of value to show + * @return array List of templates + */ + public static function liste_modeles($db, $maxfilenamelength = 0) + { + // phpcs:enable + global $conf; + + $type = 'asset'; + $list = array(); + + include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + $list = getListOfModels($db, $type, $maxfilenamelength); + + return $list; + } +} + + + +/** + * Parent class to manage numbering of Asset + */ +abstract class ModeleNumRefAsset +{ + /** + * @var string Error code (or message) + */ + public $error = ''; + + /** + * Return if a module can be used or not + * + * @return boolean true if module can be used + */ + public function isEnabled() + { + return true; + } + + /** + * Returns the default description of the numbering template + * + * @return string Texte descripif + */ + public function info() + { + global $langs; + $langs->load("asset@asset"); + return $langs->trans("NoDescription"); + } + + /** + * Returns an example of numbering + * + * @return string Example + */ + public function getExample() + { + global $langs; + $langs->load("asset@asset"); + return $langs->trans("NoExample"); + } + + /** + * Checks if the numbers already in the database do not + * cause conflicts that would prevent this numbering working. + * + * @param Object $object Object we need next value for + * @return boolean false if conflict, true if ok + */ + public function canBeActivated($object) + { + return true; + } + + /** + * Returns next assigned value + * + * @param Object $object Object we need next value for + * @return string Valeur + */ + public function getNextValue($object) + { + global $langs; + return $langs->trans("NotAvailable"); + } + + /** + * Returns version of numbering module + * + * @return string Valeur + */ + public function getVersion() + { + global $langs; + $langs->load("admin"); + + if ($this->version == 'development') { + return $langs->trans("VersionDevelopment"); + } + if ($this->version == 'experimental') { + return $langs->trans("VersionExperimental"); + } + if ($this->version == 'dolibarr') { + return DOL_VERSION; + } + if ($this->version) { + return $this->version; + } + return $langs->trans("NotAvailable"); + } +} diff --git a/htdocs/core/modules/modAsset.class.php b/htdocs/core/modules/modAsset.class.php index b275f38debe..0fe334bc846 100644 --- a/htdocs/core/modules/modAsset.class.php +++ b/htdocs/core/modules/modAsset.class.php @@ -58,9 +58,9 @@ class modAsset extends DolibarrModules // Module label (no space allowed), used if translation string 'ModuleAssetsName' not found (MyModue is name of module). $this->name = preg_replace('/^mod/i', '', get_class($this)); // Module description, used if translation string 'ModuleAssetsDesc' not found (MyModue is name of module). - $this->description = "Assets module"; + $this->description = "Asset module"; // Used only if file README.md and README-LL.md not found. - $this->descriptionlong = "Assets module to manage assets module and depreciation charge on Dolibarr"; + $this->descriptionlong = "Asset module to manage assets module and depreciation charge on Dolibarr"; // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z' $this->version = 'development'; @@ -192,13 +192,29 @@ class modAsset extends DolibarrModules $this->rights[$r][4] = 'delete'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) $this->rights[$r][5] = ''; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + $r++; + $this->rights[$r][0] = 51004; // Permission id (must not be already used) + $this->rights[$r][1] = 'Read asset models'; // Permission label + $this->rights[$r][2] = 'r'; + $this->rights[$r][3] = 0; // Permission by default for new user (0/1) + $this->rights[$r][4] = 'model_advance'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + $this->rights[$r][5] = 'read'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + $r++; $this->rights[$r][0] = 51005; // Permission id (must not be already used) - $this->rights[$r][1] = 'Setup types of asset'; // Permission label + $this->rights[$r][1] = 'Create/Update asset models'; // Permission label $this->rights[$r][2] = 'w'; $this->rights[$r][3] = 0; // Permission by default for new user (0/1) - $this->rights[$r][4] = 'setup_advance'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) - $this->rights[$r][5] = ''; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + $this->rights[$r][4] = 'model_advance'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + $this->rights[$r][5] = 'write'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + + $r++; + $this->rights[$r][0] = 51006; // Permission id (must not be already used) + $this->rights[$r][1] = 'Delete asset models'; // Permission label + $this->rights[$r][2] = 'd'; + $this->rights[$r][3] = 0; // Permission by default for new user (0/1) + $this->rights[$r][4] = 'model_advance'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) + $this->rights[$r][5] = 'delete'; // In php code, permission will be checked by test if ($user->rights->asset->level1->level2) // Menus //------- diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index 335754a8168..5014b47e9d3 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -6,9 +6,10 @@ * Copyright (C) 2014 Florian Henry * Copyright (C) 2014 Raphaël Doursenaud * Copyright (C) 2015-2016 Marcos García - * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018 Frédéric France * Copyright (C) 2018 Ferran Marcet * Copyright (C) 2019 Nicolas ZABOURI + * Copyright (C) 2022 OpenDSI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,6 +69,9 @@ if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf- if (in_array($object->element, array('propal', 'commande', 'order', 'facture', 'facturerec', 'invoice', 'supplier_proposal', 'order_supplier', 'invoice_supplier', 'invoice_supplier_rec'))) { $colspan++; // With this, there is a column move button } +if (!empty($conf->asset->enabled) && $object->element == 'invoice_supplier') { + $colspan++; +} //print $object->element; // Lines for extrafield diff --git a/htdocs/core/tpl/objectline_edit.tpl.php b/htdocs/core/tpl/objectline_edit.tpl.php index 0f0043eadd6..b07ea5042f0 100644 --- a/htdocs/core/tpl/objectline_edit.tpl.php +++ b/htdocs/core/tpl/objectline_edit.tpl.php @@ -6,6 +6,7 @@ * Copyright (C) 2012-2014 Raphaël Doursenaud * Copyright (C) 2013 Florian Henry * Copyright (C) 2018 Frédéric France + * Copyright (C) 2022 OpenDSI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,6 +73,11 @@ if (in_array($object->element, array('propal', 'supplier_proposal', 'facture', ' if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) { $colspan += 2; } +if (!empty($conf->asset->enabled) && $object->element == 'invoice_supplier') { + $colspan++; +} + + print "\n"; diff --git a/htdocs/core/tpl/objectline_title.tpl.php b/htdocs/core/tpl/objectline_title.tpl.php index 0f86289dfe1..c40f07716f2 100644 --- a/htdocs/core/tpl/objectline_title.tpl.php +++ b/htdocs/core/tpl/objectline_title.tpl.php @@ -6,6 +6,7 @@ * Copyright (C) 2012-2014 Raphaël Doursenaud * Copyright (C) 2013 Florian Henry * Copyright (C) 2017 Juanjo Menent + * Copyright (C) 2022 OpenDSI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -140,6 +141,10 @@ if ($outputalsopricetotalwithtax) { print ''.$langs->trans('TotalTTCShort').''; } +if (!empty($conf->asset->enabled) && $object->element == 'invoice_supplier') { + print ''; +} + print ''; // No width to allow autodim print ''; diff --git a/htdocs/core/tpl/objectline_view.tpl.php b/htdocs/core/tpl/objectline_view.tpl.php index 36eceb87341..b44996148f1 100644 --- a/htdocs/core/tpl/objectline_view.tpl.php +++ b/htdocs/core/tpl/objectline_view.tpl.php @@ -6,6 +6,7 @@ * Copyright (C) 2012-2014 Raphaël Doursenaud * Copyright (C) 2013 Florian Henry * Copyright (C) 2017 Juanjo Menent + * Copyright (C) 2022 OpenDSI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -387,6 +388,35 @@ if ($this->statut == 0 && !empty($object_rights->creer) && $action != 'selectlin } } + if (!empty($conf->asset->enabled) && $object->element == 'invoice_supplier') { + print ''; + $coldisplay++; + if (!empty($product_static->accountancy_code_buy) || + !empty($product_static->accountancy_code_buy_intra) || + !empty($product_static->accountancy_code_buy_export) + ) { + $accountancy_category_asset = $conf->global->ASSET_ACCOUNTANCY_CATEGORY; + $filters = array(); + if (!empty($product_static->accountancy_code_buy)) $filters[] = "account_number = '" . $this->db->escape($product_static->accountancy_code_buy) . "'"; + if (!empty($product_static->accountancy_code_buy_intra)) $filters[] = "account_number = '" . $this->db->escape($product_static->accountancy_code_buy_intra) . "'"; + if (!empty($product_static->accountancy_code_buy_export)) $filters[] = "account_number = '" . $this->db->escape($product_static->accountancy_code_buy_export) . "'"; + $sql = "SELECT COUNT(*) AS found"; + $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_account"; + $sql .= " WHERE pcg_type = '" . $this->db->escape($conf->global->ASSET_ACCOUNTANCY_CATEGORY) . "'"; + $sql .= " AND (" . implode(' OR ', $filters). ")"; + $resql_asset = $this->db->query($sql); + if (!$resql_asset) { + print 'Error SQL: ' . $this->db->lasterror(); + } elseif ($obj = $this->db->fetch_object($resql_asset)) { + if (!empty($obj->found)) { + print ''; + print img_edit_add() . ''; + } + } + } + print ''; + } + print ''; $coldisplay++; if (($line->info_bits & 2) == 2 || !empty($disableedit)) {