From ee82330292ee74b4e2522140623476770692bc53 Mon Sep 17 00:00:00 2001 From: Florian Henry Date: Wed, 17 Jul 2013 19:17:54 +0200 Subject: [PATCH] On going sync/merge from other repo --- .../modules/project/task/mod_task_simple.php | 152 ++++ .../project/task/mod_task_universal.php | 142 +++ .../modules/project/task/modules_task.php | 252 ++++++ .../task/pdf/doc_generic_task_odt.modules.php | 844 ++++++++++++++++++ .../tasks/template_task_summary.odt | Bin 0 -> 21249 bytes htdocs/projet/admin/project.php | 19 + 6 files changed, 1409 insertions(+) create mode 100644 htdocs/core/modules/project/task/mod_task_simple.php create mode 100644 htdocs/core/modules/project/task/mod_task_universal.php create mode 100644 htdocs/core/modules/project/task/modules_task.php create mode 100644 htdocs/core/modules/project/task/pdf/doc_generic_task_odt.modules.php create mode 100644 htdocs/install/doctemplates/tasks/template_task_summary.odt diff --git a/htdocs/core/modules/project/task/mod_task_simple.php b/htdocs/core/modules/project/task/mod_task_simple.php new file mode 100644 index 00000000000..25cc347bcf9 --- /dev/null +++ b/htdocs/core/modules/project/task/mod_task_simple.php @@ -0,0 +1,152 @@ + + * Copyright (C) 2010 Laurent Destailleur + * + * 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 http://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/project/mod_project_simple.php + * \ingroup project + * \brief File with class to manage the numbering module Simple for project references + */ + +require_once DOL_DOCUMENT_ROOT .'/core/modules/project/task/modules_task.php'; + + +/** + * Class to manage the numbering module Simple for project references + */ +class mod_task_simple extends ModeleNumRefTask +{ + var $version='dolibarr'; // 'development', 'experimental', 'dolibarr' + var $prefix='TK'; + var $error=''; + var $nom = "Simple"; + + + /** + * Return description of numbering module + * + * @return string Text with description + */ + function info() + { + global $langs; + return $langs->trans("SimpleNumRefModelDesc",$this->prefix); + } + + + /** + * Return an example of numbering module values + * + * @return string Example + */ + function getExample() + { + return $this->prefix."0501-0001"; + } + + + /** Test si les numeros deja en vigueur dans la base ne provoquent pas de + * de conflits qui empechera cette numerotation de fonctionner. + * + * @return boolean false si conflit, true si ok + */ + function canBeActivated() + { + global $conf,$langs; + + $coyymm=''; $max=''; + + $posindice=8; + $sql = "SELECT MAX(SUBSTRING(ref FROM ".$posindice.")) as max"; + $sql.= " FROM ".MAIN_DB_PREFIX."projet_task"; + $sql.= " WHERE ref LIKE '".$this->prefix."____-%'"; + $sql.= " AND entity = ".$conf->entity; + $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)) + { + return true; + } + else + { + $langs->load("errors"); + $this->error=$langs->trans('ErrorNumRefModel',$max); + return false; + } + } + + + /** + * Return next value + * + * @param Societe $objsoc Object third party + * @param Task $Task Object Task + * @return string Value if OK, 0 if KO + */ + function getNextValue($objsoc,$task) + { + global $db,$conf; + + // D'abord on recupere la valeur max + $posindice=8; + $sql = "SELECT MAX(SUBSTRING(ref FROM ".$posindice.")) as max"; + $sql.= " FROM ".MAIN_DB_PREFIX."projet_task"; + $sql.= " WHERE ref like '".$this->prefix."____-%'"; + + $resql=$db->query($sql); + if ($resql) + { + $obj = $db->fetch_object($resql); + if ($obj) $max = intval($obj->max); + else $max=0; + } + else + { + dol_syslog("mod_task_simple::getNextValue sql=".$sql); + return -1; + } + + $date=empty($task->date_c)?dol_now():$task->date_c; + + //$yymm = strftime("%y%m",time()); + $yymm = strftime("%y%m",$date); + $num = sprintf("%04s",$max+1); + + dol_syslog("mod_task_simple::getNextValue return ".$this->prefix.$yymm."-".$num); + return $this->prefix.$yymm."-".$num; + } + + + /** + * Return next reference not yet used as a reference + * + * @param Societe $objsoc Object third party + * @param Task $task Object task + * @return string Next not used reference + */ + function task_get_num($objsoc=0,$task='') + { + return $this->getNextValue($objsoc,$task); + } +} + +?> \ No newline at end of file diff --git a/htdocs/core/modules/project/task/mod_task_universal.php b/htdocs/core/modules/project/task/mod_task_universal.php new file mode 100644 index 00000000000..2e660bd9cdb --- /dev/null +++ b/htdocs/core/modules/project/task/mod_task_universal.php @@ -0,0 +1,142 @@ + + * + * 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 http://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/project/mod_project_universal.php + * \ingroup project + * \brief Fichier contenant la classe du modele de numerotation de reference de projet Universal + */ + +require_once DOL_DOCUMENT_ROOT .'/core/modules/project/task/modules_task.php'; + + +/** + * Classe du modele de numerotation de reference de projet Universal + */ +class mod_task_universal extends ModeleNumRefTask +{ + var $version='dolibarr'; // 'development', 'experimental', 'dolibarr' + var $error = ''; + var $nom = 'Universal'; + + + /** + * Renvoi la description du modele de numerotation + * + * @return string Texte descripif + */ + function info() + { + global $conf,$langs; + + $langs->load("projects"); + $langs->load("admin"); + + $form = new Form($this->db); + + $texte = $langs->trans('GenericNumRefModelDesc')."
\n"; + $texte.= '
'; + $texte.= ''; + $texte.= ''; + $texte.= ''; + $texte.= ''; + + $tooltip=$langs->trans("GenericMaskCodes",$langs->transnoentities("Task"),$langs->transnoentities("Task")); + $tooltip.=$langs->trans("GenericMaskCodes2"); + $tooltip.=$langs->trans("GenericMaskCodes3"); + $tooltip.=$langs->trans("GenericMaskCodes4a",$langs->transnoentities("Task"),$langs->transnoentities("Task")); + $tooltip.=$langs->trans("GenericMaskCodes5"); + + // Parametrage du prefix + $texte.= ''; + $texte.= ''; + + $texte.= ''; + + $texte.= ''; + + $texte.= '
'.$langs->trans("Mask").':'.$form->textwithpicto('',$tooltip,1,1).' 
'; + $texte.= '
'; + + return $texte; + } + + /** + * Renvoi un exemple de numerotation + * + * @return string Example + */ + function getExample() + { + global $conf,$langs,$mysoc; + + $old_code_client=$mysoc->code_client; + $mysoc->code_client='CCCCCCCCCC'; + $numExample = $this->getNextValue($mysoc,''); + $mysoc->code_client=$old_code_client; + + if (! $numExample) + { + $numExample = $langs->trans('NotConfigured'); + } + return $numExample; + } + + /** + * Return next value + * + * @param Societe $objsoc Object third party + * @param Project $project Object project + * @return string Value if OK, 0 if KO + */ + function getNextValue($objsoc,$project) + { + global $db,$conf; + + require_once DOL_DOCUMENT_ROOT .'/core/lib/functions2.lib.php'; + + // On defini critere recherche compteur + $mask=$conf->global->PROJECT_TASK_UNIVERSAL_MASK; + + if (! $mask) + { + $this->error='NotConfigured'; + return 0; + } + + $date=empty($project->date_c)?dol_now():$project->date_c; + $numFinal=get_next_value($db,$mask,'projet_task','ref','',$objsoc->code_client,$date); + + return $numFinal; + } + + + /** + * Return next reference not yet used as a reference + * + * @param Societe $objsoc Object third party + * @param Project $project Object project + * @return string Next not used reference + */ + function project_get_num($objsoc=0,$project='') + { + return $this->getNextValue($objsoc,$project); + } +} + +?> \ No newline at end of file diff --git a/htdocs/core/modules/project/task/modules_task.php b/htdocs/core/modules/project/task/modules_task.php new file mode 100644 index 00000000000..824c5d12152 --- /dev/null +++ b/htdocs/core/modules/project/task/modules_task.php @@ -0,0 +1,252 @@ + + * Copyright (C) 2010 Florian Henry + * + * 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 http://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/project/task/modules_task.php + * \ingroup project + * \brief File that contain parent class for task models + * and parent class for task numbering models + */ +require_once DOL_DOCUMENT_ROOT.'/core/class/commondocgenerator.class.php'; + + +/** + * Parent class for projects models + */ +abstract class ModelePDFTask extends CommonDocGenerator +{ + var $error=''; + + + /** + * Return list of active generation modules + * + * @param DoliDB $db Database handler + * @param string $maxfilenamelength Max length of value to show + * @return array List of templates + */ + static function liste_modeles($db,$maxfilenamelength=0) + { + global $conf; + + $type='project_task'; + $liste=array(); + + include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + $liste=getListOfModels($db,$type,$maxfilenamelength); + + return $liste; + } +} + + + +/** + * Classe mere des modeles de numerotation des references de projets + */ +abstract class ModeleNumRefTask +{ + var $error=''; + + /** + * Return if a module can be used or not + * + * @return boolean true if module can be used + */ + function isEnabled() + { + return true; + } + + /** + * Renvoi la description par defaut du modele de numerotation + * + * @return string Texte descripif + */ + function info() + { + global $langs; + $langs->load("projects"); + return $langs->trans("NoDescription"); + } + + /** + * Renvoi un exemple de numerotation + * + * @return string Example + */ + function getExample() + { + global $langs; + $langs->load("projects"); + return $langs->trans("NoExample"); + } + + /** + * Test si les numeros deja en vigueur dans la base ne provoquent pas de + * de conflits qui empechera cette numerotation de fonctionner. + * + * @return boolean false si conflit, true si ok + */ + function canBeActivated() + { + return true; + } + + /** + * Renvoi prochaine valeur attribuee + * + * @param Societe $objsoc Object third party + * @param Project $project Object project + * @return string Valeur + */ + function getNextValue($objsoc, $project) + { + global $langs; + return $langs->trans("NotAvailable"); + } + + /** + * Renvoi version du module numerotation + * + * @return string Valeur + */ + 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; + return $langs->trans("NotAvailable"); + } +} + + +/** + * Create an intervention document on disk using template defined into PROJECT_TASK_ADDON_PDF + * + * @param DoliDB $db objet base de donnee + * @param Object $object Object fichinter + * @param string $modele force le modele a utiliser ('' par defaut) + * @param Translate $outputlangs objet lang a utiliser pour traduction + * @param int $hidedetails Hide details of lines + * @param int $hidedesc Hide description + * @param int $hideref Hide ref + * @param HookManager $hookmanager Hook manager instance + * @return int 0 if KO, 1 if OK + */ +function task_pdf_create($db, $object, $modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $hookmanager=false) +{ + global $conf,$langs; + $langs->load("projects"); + + $error=0; + + $srctemplatepath=''; + + // Positionne modele sur le nom du modele de projet a utiliser + if (! dol_strlen($modele)) + { + if (! empty($conf->global->PROJECT_TASK_ADDON_PDF)) + { + $modele = $conf->global->PROJECT_TASK_ADDON_PDF; + } + else + { + $modele='baleine'; + } + } + + // If selected modele is a filename template (then $modele="modelname:filename") + $tmp=explode(':',$modele,2); + if (! empty($tmp[1])) + { + $modele=$tmp[0]; + $srctemplatepath=$tmp[1]; + } + + // Search template files + $file=''; $classname=''; $filefound=0; + $dirmodels=array('/'); + if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']); + foreach($dirmodels as $reldir) + { + foreach(array('doc','pdf') as $prefix) + { + $file = $prefix."_".$modele.".modules.php"; + + // On verifie l'emplacement du modele + $file=dol_buildpath($reldir."core/modules/project/task/pdf/".$file,0); + if (file_exists($file)) + { + $filefound=1; + $classname=$prefix.'_'.$modele; + break; + } + } + if ($filefound) break; + } + + // Charge le modele + if ($filefound) + { + require_once $file; + + $obj = new $classname($db); + + // We save charset_output to restore it because write_file can change it if needed for + // output format that does not support UTF8. + $sav_charset_output=$outputlangs->charset_output; + if ($obj->write_file($object, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $hookmanager) > 0) + { + $outputlangs->charset_output=$sav_charset_output; + + // we delete preview files + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + dol_delete_preview($object); + + // Success in building document. We build meta file. + dol_meta_create($object); + + // Appel des triggers + /*include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($db); + $result=$interface->run_triggers('PROJECT_BUILDDOC',$object,$user,$langs,$conf); + if ($result < 0) { $error++; $this->errors=$interface->errors; }*/ + // Fin appel triggers + + return 1; + } + else + { + $outputlangs->charset_output=$sav_charset_output; + dol_print_error($db,"task_pdf_create Error: ".$obj->error); + return 0; + } + } + else + { + print $langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file); + return 0; + } +} + +?> \ No newline at end of file diff --git a/htdocs/core/modules/project/task/pdf/doc_generic_task_odt.modules.php b/htdocs/core/modules/project/task/pdf/doc_generic_task_odt.modules.php new file mode 100644 index 00000000000..3a9c86bdb09 --- /dev/null +++ b/htdocs/core/modules/project/task/pdf/doc_generic_task_odt.modules.php @@ -0,0 +1,844 @@ + + * Copyright (C) 2012 Juanjo Menent +* Copyright (C) 2013 Florian Henry +* +* 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 http://www.gnu.org/ +*/ + +/** + * \file htdocs/core/modules/project/task/pdf/doc_generic_task_odt.modules.php + * \ingroup project + * \brief File of class to build ODT documents for third parties +*/ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/project/modules_project.php'; +require_once DOL_DOCUMENT_ROOT.'/core/modules/project/task/modules_task.php'; +require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; +require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php'; +require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; +require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.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'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; +if (! empty($conf->propal->enabled)) require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; +if (! empty($conf->facture->enabled)) require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; +if (! empty($conf->facture->enabled)) require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php'; +if (! empty($conf->commande->enabled)) require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; +if (! empty($conf->fournisseur->enabled)) require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; +if (! empty($conf->fournisseur->enabled)) require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; +if (! empty($conf->contrat->enabled)) require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php'; +if (! empty($conf->ficheinter->enabled)) require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php'; +if (! empty($conf->deplacement->enabled)) require_once DOL_DOCUMENT_ROOT.'/compta/deplacement/class/deplacement.class.php'; +if (! empty($conf->agenda->enabled)) require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php'; + + +/** + * Class to build documents using ODF templates generator + */ +class doc_generic_task_odt extends ModelePDFTask +{ + var $emetteur; // Objet societe qui emet + + var $phpmin = array(5,2,0); // Minimum version of PHP required by module + var $version = 'dolibarr'; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct($db) + { + global $conf,$langs,$mysoc; + + $langs->load("main"); + $langs->load("companies"); + + $this->db = $db; + $this->name = "ODT templates"; + $this->description = $langs->trans("DocumentModelOdt"); + $this->scandir = 'PROJECT_TASK_ADDON_PDF_ODT_PATH'; // Name of constant that is used to save list of directories to scan + + // Dimension page pour format A4 + $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; // Affiche logo + $this->option_tva = 0; // Gere option tva COMMANDE_TVAOPTION + $this->option_modereg = 0; // Affiche mode reglement + $this->option_condreg = 0; // Affiche conditions reglement + $this->option_codeproduitservice = 0; // Affiche code produit-service + $this->option_multilang = 0; // Dispo en plusieurs langues + $this->option_escompte = 0; // Affiche si il y a eu escompte + $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 + + // Recupere emetteur + $this->emetteur=$mysoc; + if (! $this->emetteur->pays_code) $this->emetteur->pays_code=substr($langs->defaultlang,-2); // Par defaut, si n'etait pas defini + } + + + /** + * Define array with couple substitution key => substitution value + * + * @param Object $object Main object to use as data source + * @param Translate $outputlangs Lang object to use for output + * @return array Array of substitution + */ + function get_substitutionarray_object($object,$outputlangs) + { + global $conf; + + return array( + 'object_id'=>$object->id, + 'object_ref'=>$object->ref, + 'object_title'=>$object->title, + 'object_description'=>$object->description, + 'object_date_creation'=>dol_print_date($object->date_c,'day'), + 'object_date_modification'=>dol_print_date($object->date_m,'day'), + 'object_date_start'=>dol_print_date($object->date_start,'day'), + 'object_date_end'=>dol_print_date($object->date_end,'day'), + 'object_note_private'=>$object->note_private, + 'object_note_public'=>$object->note_public, + 'object_public'=>$object->public, + 'object_statut'=>$object->getLibStatut() + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $task Task Object + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_tasks($task,$outputlangs) + { + global $conf; + + return array( + 'task_ref'=>$task->ref, + 'task_fk_project'=>$task->fk_project, + 'task_projectref'=>$task->projectref, + 'task_projectlabel'=>$task->projectlabel, + 'task_label'=>$task->label, + 'task_description'=>$task->description, + 'task_fk_parent'=>$task->fk_parent, + 'task_duration'=>$task->duration, + 'task_progress'=>$task->progress, + 'task_public'=>$task->public, + 'task_date_start'=>dol_print_date($task->date_start,'day'), + 'task_date_end'=>dol_print_date($task->date_end,'day'), + 'task_note_private'=>$task->note_private, + 'task_note_public'=>$task->note_public + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $contact Contact array + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_project_contacts($contact,$outputlangs) + { + global $conf; + + return array( + 'projcontacts_id'=>$contact['id'], + 'projcontacts_rowid'=>$contact['rowid'], + 'projcontacts_role'=>$contact['libelle'], + 'projcontacts_lastname'=>$contact['lastname'], + 'projcontacts_firstname'=>$contact['firstname'], + 'projcontacts_fullcivname'=>$contact['fullname'], + 'projcontacts_socname'=>$contact['socname'], + 'projcontacts_email'=>$contact['email'] + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $file file array + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_project_file($file,$outputlangs) + { + global $conf; + + return array( + 'projfile_name'=>$file['name'], + 'projfile_date'=>dol_print_date($file['date'],'day'), + 'projfile_size'=>$file['size'] + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $refdetail Reference array + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_project_reference($refdetail,$outputlangs) + { + global $conf; + + return array( + 'projref_type'=>$refdetail['type'], + 'projref_ref'=>$refdetail['ref'], + 'projref_date'=>dol_print_date($refdetail['date'],'day'), + 'projref_socname'=>$refdetail['socname'], + 'projref_amountht'=>price($refdetail['amountht'],0,$outputlangs), + 'projref_amountttc'=>price($refdetail['amountttc'],0,$outputlangs), + 'projref_status'=>$refdetail['status'] + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $taskressource Reference array + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_tasksressource($taskressource,$outputlangs) + { + global $conf; + //dol_syslog(get_class($this).'::get_substitutionarray_tasksressource taskressource='.var_export($taskressource,true),LOG_DEBUG); + return array( + 'taskressource_rowid'=>$taskressource['rowid'], + 'taskressource_role'=>$taskressource['libelle'], + 'taskressource_lastname'=>$taskressource['lastname'], + 'taskressource_firstname'=>$taskressource['firstname'], + 'taskressource_fullcivname'=>$taskressource['fullname'], + 'taskressource_socname'=>$taskressource['socname'], + 'taskressource_email'=>$taskressource['email'] + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param object $tasktime times object + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_taskstime($tasktime,$outputlangs) + { + global $conf; + + return array( + 'tasktime_rowid'=>$tasktime['rowid'], + 'tasktime_task_date'=>dol_print_date($tasktime['task_date'],'day'), + 'tasktime_task_duration'=>convertSecondToTime($tasktime['task_duration'],'all'), + 'tasktime_note'=>$tasktime['note'], + 'tasktime_fk_user'=>$tasktime['fk_user'], + 'tasktime_user_name'=>$tasktime['name'], + 'tasktime_user_first'=>$tasktime['firstname'], + 'tasktime_fullcivname'=>$tasktime['fullcivname'] + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $file file array + * @param Translate $outputlangs Lang object to use for output + * @return array Return a substitution array + */ + function get_substitutionarray_task_file($file,$outputlangs) + { + global $conf; + + return array( + 'tasksfile_name'=>$file['name'], + 'tasksfile_date'=>dol_print_date($file['date'],'day'), + 'tasksfile_size'=>$file['size'] + ); + } + + + /** + * Return description of a module + * + * @param Translate $langs Lang object to use for output + * @return string Description + */ + function info($langs) + { + global $conf,$langs; + + $langs->load("companies"); + $langs->load("errors"); + + $form = new Form($this->db); + + $texte = $this->description.".
\n"; + $texte.= '
'; + $texte.= ''; + $texte.= ''; + $texte.= ''; + $texte.= ''; + + // List of directories area + $texte.= ''; + + + $texte.= ''; + $texte.= ''; + + /*$texte.= ''; + $texte.= ''; + $texte.= '';*/ + + $texte.= '
'; + $texttitle=$langs->trans("ListOfDirectories"); + $listofdir=explode(',',preg_replace('/[\r\n]+/',',',trim($conf->global->PROJECT_TASK_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,'\.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.= ''; + $texte.= '  '; + $texte.= ''; + $texte.= '
'; + + // Scan directories + if (count($listofdir)) $texte.=$langs->trans("NumberOfModelFilesFound").': '.count($listoffiles).''; + + $texte.= '
'; + $texte.= $langs->trans("ExampleOfDirectoriesForModelGen"); + $texte.= '
'; + $texte.= ''; + $texte.= '
'; + $texte.= '
'; + + return $texte; + } + + /** + * Function to build a document on disk using the generic odt module. + * + * @param Commande $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 + * @return int 1 if OK, <=0 if KO + */ + function write_file($object,$outputlangs,$srctemplatepath) + { + global $user,$langs,$conf,$mysoc; + + if (empty($srctemplatepath)) + { + dol_syslog("doc_generic_odt::write_file parameter srctemplatepath empty", LOG_WARNING); + return -1; + } + + if (! is_object($outputlangs)) $outputlangs=$langs; + $sav_charset_output=$outputlangs->charset_output; + $outputlangs->charset_output='UTF-8'; + + $outputlangs->load("main"); + $outputlangs->load("dict"); + $outputlangs->load("companies"); + $outputlangs->load("projects"); + + if ($conf->projet->dir_output) + { + // If $object is id instead of object + if (! is_object($object)) + { + $id = $object; + $object = new Task($this->db); + $result=$object->fetch($id); + if ($result < 0) + { + dol_print_error($this->db,$object->error); + return -1; + } + } + $project= new Project($this->db); + $project->fetch($object->fk_project); + + $dir = $conf->projet->dir_output. "/" . $project->ref. "/";; + $objectref = dol_sanitizeFileName($object->ref); + if (! preg_match('/specimen/i',$objectref)) $dir.= "/" . $objectref; + $file = $dir . "/" . $objectref . ".odt"; + + if (! file_exists($dir)) + { + print '$dir'.$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('/\.odt/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'; + $file=$dir.'/'.$newfiletmp.'.odt'; + //print "newdir=".$dir; + //print "newfile=".$newfile; + //print "file=".$file; + //print "conf->societe->dir_temp=".$conf->societe->dir_temp; + + dol_mkdir($conf->projet->dir_temp); + + $socobject=$object->thirdparty; + + // Make substitution + $substitutionarray=array( + '__FROM_NAME__' => $this->emetteur->nom, + '__FROM_EMAIL__' => $this->emetteur->email, + ); + complete_substitutions_array($substitutionarray, $langs, $object); + + // Open and load template + require_once ODTPHP_PATH.'odf.php'; + $odfHandler = new odf( + $srctemplatepath, + array( + 'PATH_TO_TMP' => $conf->projet->dir_temp, + 'ZIP_PROXY' => 'PclZipProxy', // PhpZipProxy or PclZipProxy. Got "bad compression method" error when using PhpZipProxy. + 'DELIMITER_LEFT' => '{', + 'DELIMITER_RIGHT' => '}' + ) + ); + // 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 user info + $tmparray=$this->get_substitutionarray_user($user,$outputlangs); + //var_dump($tmparray); exit; + foreach($tmparray as $key=>$value) + { + try { + if (preg_match('/logo$/',$key)) // Image + { + //var_dump($value);exit; + 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) + { + } + } + // Make substitutions into odt of mysoc + $tmparray=$this->get_substitutionarray_mysoc($mysoc,$outputlangs); + //var_dump($tmparray); exit; + foreach($tmparray as $key=>$value) + { + try { + if (preg_match('/logo$/',$key)) // Image + { + //var_dump($value);exit; + 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) + { + } + } + + // Make substitutions into odt of thirdparty + $tmparray=$this->get_substitutionarray_thirdparty($socobject,$outputlangs); + 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'); + $odfHandler->setVarsHeadFooter($key, $value, true, 'UTF-8'); + } + } + catch(OdfException $e) + { + } + } + + // Replace tags of object + external modules + $tmparray=$this->get_substitutionarray_object($project,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object); + 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) + { + } + } + + // Replace tags of lines for tasks + try + { + // Security check + $socid=0; + if (!empty($project->fk_soc)) $socid = $project->fk_soc; + + $tmparray=$this->get_substitutionarray_tasks($object,$outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $task); + foreach($tmparray as $key => $val) + { + try + { + $odfHandler->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + + // Replace tags of lines for contacts task + $sourcearray=array('internal','external'); + $contact_arrray=array(); + foreach ($sourcearray as $source) { + $contact_temp=$object->liste_contact(-1,$source); + if ((is_array($contact_temp) && count($contact_temp) > 0)) + { + $contact_arrray=array_merge($contact_arrray,$contact_temp); + } + } + if ((is_array($contact_arrray) && count($contact_arrray) > 0)) + { + $listlinestaskres = $odfHandler->setSegment('tasksressources'); + + foreach ($contact_arrray as $contact) + { + if ($contact['source']=='internal') { + $objectdetail=new User($this->db); + $objectdetail->fetch($contact['id']); + $contact['socname']=$mysoc->name; + } elseif ($contact['source']=='external') { + $objectdetail=new Contact($this->db); + $objectdetail->fetch($contact['id']); + + $soc=new Societe($this->db); + $soc->fetch($contact['socid']); + $contact['socname']=$soc->name; + } + $contact['fullname']=$objectdetail->getFullName($outputlangs,1); + + $tmparray=$this->get_substitutionarray_tasksressource($contact,$outputlangs); + + foreach($tmparray as $key => $val) + { + try + { + $listlinestaskres->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlinestaskres->merge(); + } + $odfHandler->mergeSegment($listlinestaskres); + } + + //Time ressources + $sql = "SELECT t.rowid, t.task_date, t.task_duration, t.fk_user, t.note"; + $sql.= ", u.name, u.firstname"; + $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as t"; + $sql .= " , ".MAIN_DB_PREFIX."user as u"; + $sql .= " WHERE t.fk_task =".$object->id; + $sql .= " AND t.fk_user = u.rowid"; + $sql .= " ORDER BY t.task_date DESC"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + $tasks = array(); + $listlinestasktime = $odfHandler->setSegment('taskstimes'); + while ($i < $num) + { + $row = $this->db->fetch_array($resql); + if (!empty($row['fk_user'])) { + $objectdetail=new User($this->db); + $objectdetail->fetch($row['fk_user']); + $row['fullcivname']=$objectdetail->getFullName($outputlangs,1); + } else { + $row['fullcivname']=''; + } + + $tmparray=$this->get_substitutionarray_taskstime($row,$outputlangs); + + foreach($tmparray as $key => $val) + { + try + { + $listlinestasktime->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlinestasktime->merge(); + $i++; + } + $this->db->free($resql); + + $odfHandler->mergeSegment($listlinestasktime); + } + + + // Replace tags of project files + $listtasksfiles = $odfHandler->setSegment('tasksfiles'); + + $upload_dir = $conf->projet->dir_output.'/'.dol_sanitizeFileName($project->ref).'/'.dol_sanitizeFileName($object->ref); + $filearray=dol_dir_list($upload_dir,"files",0,'','\.meta$','name',SORT_ASC,1); + + + foreach ($filearray as $filedetail) + { + $tmparray=$this->get_substitutionarray_task_file($filedetail,$outputlangs); + //dol_syslog(get_class($this).'::main $tmparray'.var_export($tmparray,true)); + foreach($tmparray as $key => $val) + { + try + { + $listtasksfiles->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listtasksfiles->merge(); + } + //$listlines->merge(); + + $odfHandler->mergeSegment($listtasksfiles); + + } + catch(OdfException $e) + { + $this->error=$e->getMessage(); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + + + + // Replace tags of project files + try + { + $listlines = $odfHandler->setSegment('projectfiles'); + + $upload_dir = $conf->projet->dir_output.'/'.dol_sanitizeFileName($object->ref); + $filearray=dol_dir_list($upload_dir,"files",0,'','\.meta$','name',SORT_ASC,1); + + + foreach ($filearray as $filedetail) + { + //dol_syslog(get_class($this).'::main $filedetail'.var_export($filedetail,true)); + $tmparray=$this->get_substitutionarray_project_file($filedetail,$outputlangs); + + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); + } + catch(OdfException $e) + { + $this->error=$e->getMessage(); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + + // Replace tags of lines for contacts + $sourcearray=array('internal','external'); + $contact_arrray=array(); + foreach ($sourcearray as $source) { + $contact_temp=$project->liste_contact(-1,$source); + if ((is_array($contact_temp) && count($contact_temp) > 0)) + { + $contact_arrray=array_merge($contact_arrray,$contact_temp); + } + } + if ((is_array($contact_arrray) && count($contact_arrray) > 0)) + { + try + { + $listlines = $odfHandler->setSegment('projectcontacts'); + + foreach ($contact_arrray as $contact) + { + if ($contact['source']=='internal') { + $objectdetail=new User($this->db); + $objectdetail->fetch($contact['id']); + $contact['socname']=$mysoc->name; + } elseif ($contact['source']=='external') { + $objectdetail=new Contact($this->db); + $objectdetail->fetch($contact['id']); + + $soc=new Societe($this->db); + $soc->fetch($contact['socid']); + $contact['socname']=$soc->name; + } + $contact['fullname']=$objectdetail->getFullName($outputlangs,1); + + $tmparray=$this->get_substitutionarray_project_contacts($contact,$outputlangs); + + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + catch(SegmentException $e) + { + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); + } + catch(OdfException $e) + { + $this->error=$e->getMessage(); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + } + + + // Write new file + $odfHandler->saveToDisk($file); + + if (! empty($conf->global->MAIN_UMASK)) + @chmod($file, octdec($conf->global->MAIN_UMASK)); + + $odfHandler=null; // Destroy object + + return 1; // Success + } + else + { + $this->error=$langs->transnoentities("ErrorCanNotCreateDir",$dir); + return -1; + } + } + + return -1; + } + +} +?> \ No newline at end of file diff --git a/htdocs/install/doctemplates/tasks/template_task_summary.odt b/htdocs/install/doctemplates/tasks/template_task_summary.odt new file mode 100644 index 0000000000000000000000000000000000000000..76984ebcf8cb915a336d598a1899aa10abed260f GIT binary patch literal 21249 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 literal 0 HcmV?d00001 diff --git a/htdocs/projet/admin/project.php b/htdocs/projet/admin/project.php index d70a801ab2e..c0cc144861c 100644 --- a/htdocs/projet/admin/project.php +++ b/htdocs/projet/admin/project.php @@ -68,6 +68,25 @@ if ($action == 'updateMask') } } +if ($action == 'updateMaskTask') +{ + $maskconstmasktask=GETPOST('maskconsttask','alpha'); + $masktaskt=GETPOST('masktask','alpha'); + + if ($maskconstmasktask) $res = dolibarr_set_const($db,$maskconstmasktask,$masktaskt,'chaine',0,'',$conf->entity); + + if (! $res > 0) $error++; + + if (! $error) + { + $mesg = "".$langs->trans("SetupSaved").""; + } + else + { + $mesg = "".$langs->trans("Error").""; + } +} + else if ($action == 'specimen') { $modele=GETPOST('module','alpha');