From 15d152639374437e86a8342605a77b1a241af86f Mon Sep 17 00:00:00 2001 From: simnandez Date: Fri, 27 Jan 2012 17:41:47 +0100 Subject: [PATCH] [ task #176 ] Allow to use ODT templates. Works with Orders --- htdocs/admin/commande.php | 195 ++++--- .../doc/doc_generic_order_odt.modules.php | 495 ++++++++++++++++++ .../commande/{ => doc}/pdf_edison.modules.php | 0 .../{ => doc}/pdf_einstein.modules.php | 0 .../modules/commande/modules_commande.php | 86 +-- htdocs/core/modules/modCommande.class.php | 8 + .../doctemplates/orders/template_order.odt | Bin 0 -> 24494 bytes 7 files changed, 657 insertions(+), 127 deletions(-) create mode 100644 htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php rename htdocs/core/modules/commande/{ => doc}/pdf_edison.modules.php (100%) rename htdocs/core/modules/commande/{ => doc}/pdf_einstein.modules.php (100%) create mode 100644 htdocs/install/doctemplates/orders/template_order.odt diff --git a/htdocs/admin/commande.php b/htdocs/admin/commande.php index 31756a2c67f..705fcf35365 100644 --- a/htdocs/admin/commande.php +++ b/htdocs/admin/commande.php @@ -6,7 +6,7 @@ * Copyright (C) 2004 Andre Cianfarani * Copyright (C) 2005-2011 Regis Houssin * Copyright (C) 2008 Raphael Bertrand (Resultic) - * Copyright (C) 2011 Juanjo Menent + * Copyright (C) 2011-2012 Juanjo Menent * Copyright (C) 2011 Philippe Grand * * This program is free software; you can redistribute it and/or modify @@ -375,98 +375,119 @@ print "\n"; clearstatcache(); +$var=true; foreach ($conf->file->dol_document_root as $dirroot) { - $dir = $dirroot . "/core/modules/commande/"; + foreach (array('','/doc') as $valdir) + { + $dir = $dirroot . "/core/modules/commande".$valdir; - if (is_dir($dir)) - { - $handle = opendir($dir); - if (is_resource($handle)) - { - $var=true; - while (($file = readdir($handle))!==false) - { - if (preg_match('/\.modules\.php$/i',$file) && substr($file,0,4) == 'pdf_') - { - $name = substr($file, 4, dol_strlen($file) -16); - $classname = substr($file, 0, dol_strlen($file) -12); + if (is_dir($dir)) + { + $handle=opendir($dir); + if (is_resource($handle)) + { + while (($file = readdir($handle))!==false) + { + $filelist[]=$file; + } + closedir($handle); - $var=!$var; - print "\n "; - print "$name"; - print "\n \n"; - require_once($dir.$file); - $module = new $classname($db); - print $module->description; - print "\n"; - - // Activated - print "\n"; - if (in_array($name, $def)) - { - //if ($conf->global->COMMANDE_ADDON_PDF != "$name") - //{ - print 'scandir.'&label='.urlencode($module->name).'">'; - print img_picto($langs->trans("Activated"),'switch_on'); - print ''; - //} - //else - //{ - // print img_picto($langs->trans("Activated"),'switch_on'); - //} - } - else - { - print 'scandir.'&label='.urlencode($module->name).'">'; - print img_picto($langs->trans("Disabled"),'switch_off'); - print ''; - } - print ""; - - // Defaut - print ""; - if ($conf->global->COMMANDE_ADDON_PDF == "$name") - { - print img_picto($langs->trans("Yes"),'on'); - } - else - { - print 'scandir.'&label='.urlencode($module->name).'">'; - print img_picto($langs->trans("No"),'off'); - print ''; - } - print ''; - - // Info - $htmltooltip = ''.$langs->trans("Name").': '.$module->name; - $htmltooltip.='
'.$langs->trans("Type").': '.($module->type?$module->type:$langs->trans("Unknown")); - if ($module->type == 'pdf') + foreach($filelist as $file) + { + if (preg_match('/\.modules\.php$/i',$file) && preg_match('/^(pdf_|doc_)/',$file)) { - $htmltooltip.='
'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur; + if (file_exists($dir.'/'.$file)) + { + $name = substr($file, 4, dol_strlen($file) -16); + $classname = substr($file, 0, dol_strlen($file) -12); + + require_once($dir.'/'.$file); + $module = new $classname($db); + + $modulequalified=1; + if ($module->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2) $modulequalified=0; + if ($module->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) $modulequalified=0; + + if ($modulequalified) + { + $var = !$var; + print ''; + print (empty($module->name)?$name:$module->name); + print "\n"; + if (method_exists($module,'info')) print $module->info($langs); + else print $module->description; + print ''; + + // Active + if (in_array($name, $def)) + { + print ''."\n"; + print ''; + print img_picto($langs->trans("Enabled"),'switch_on'); + print ''; + print ''; + } + else + { + print "\n"; + print 'scandir.'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"),'switch_off').''; + print ""; + } + + // Defaut + print ""; + if ($conf->global->PROPALE_ADDON_PDF == "$name") + { + print img_picto($langs->trans("Default"),'on'); + } + else + { + print 'scandir.'&label='.urlencode($module->name).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"),'off').''; + } + print ''; + + // Info + $htmltooltip = ''.$langs->trans("Name").': '.$module->name; + $htmltooltip.='
'.$langs->trans("Type").': '.($module->type?$module->type:$langs->trans("Unknown")); + if ($module->type == 'pdf') + { + $htmltooltip.='
'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur; + } + $htmltooltip.='

'.$langs->trans("FeaturesSupported").':'; + $htmltooltip.='
'.$langs->trans("Logo").': '.yn($module->option_logo,1,1); + $htmltooltip.='
'.$langs->trans("PaymentMode").': '.yn($module->option_modereg,1,1); + $htmltooltip.='
'.$langs->trans("PaymentConditions").': '.yn($module->option_condreg,1,1); + $htmltooltip.='
'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang,1,1); + //$htmltooltip.='
'.$langs->trans("Escompte").': '.yn($module->option_escompte,1,1); + //$htmltooltip.='
'.$langs->trans("CreditNote").': '.yn($module->option_credit_note,1,1); + $htmltooltip.='
'.$langs->trans("WatermarkOnDraftOrders").': '.yn($module->option_draft_watermark,1,1); + + + print ''; + print $form->textwithpicto('',$htmltooltip,1,0); + print ''; + + // Preview + print ''; + if ($module->type == 'pdf') + { + print ''.img_object($langs->trans("Preview"),'bill').''; + } + else + { + print img_object($langs->trans("PreviewNotAvailable"),'generic'); + } + print ''; + + print "\n"; + } + } } - $htmltooltip.='

'.$langs->trans("FeaturesSupported").':'; - $htmltooltip.='
'.$langs->trans("Logo").': '.yn($module->option_logo,1,1); - $htmltooltip.='
'.$langs->trans("PaymentMode").': '.yn($module->option_modereg,1,1); - $htmltooltip.='
'.$langs->trans("PaymentConditions").': '.yn($module->option_condreg,1,1); - $htmltooltip.='
'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang,1,1); - //$htmltooltip.='
'.$langs->trans("Escompte").': '.yn($module->option_escompte,1,1); - //$htmltooltip.='
'.$langs->trans("CreditNote").': '.yn($module->option_credit_note,1,1); - $htmltooltip.='
'.$langs->trans("WatermarkOnDraftOrders").': '.yn($module->option_draft_watermark,1,1); - - print ''; - print $form->textwithpicto('',$htmltooltip,1,0); - print ''; - print ''; - print ''.img_object($langs->trans("Preview"),'order').''; - print ''; - - print "\n"; - } - } - closedir($handle); - } - } + } + } + } + } } print ''; diff --git a/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php b/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php new file mode 100644 index 00000000000..8c2493730e0 --- /dev/null +++ b/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php @@ -0,0 +1,495 @@ + + * Copyright (C) 2012 Juanjo Menent + * + * 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 2 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/commande/doc_generic_order_odt.modules.php + * \ingroup societe + * \brief File of class to build ODT documents for third parties + */ + +require_once(DOL_DOCUMENT_ROOT."/core/modules/commande/modules_commande.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"); + + +/** + * \class doc_generic_order_odt + * \brief Class to build documents using ODF templates generator + */ +class doc_generic_order_odt extends ModelePDFCommandes +{ + var $emetteur; // Objet societe qui emet + + var $phpmin = array(5,2,0); // Minimum version of PHP required by module + var $version = 'development'; + + + /** + * Constructor + * + * @param DoliDB $DB Database handler + */ + function doc_generic_order_odt($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 = 'COMMANDE_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 + */ + function get_substitutionarray_object($object,$outputlangs) + { + global $conf; + + return array( + 'object_id'=>$object->id, + 'object_ref'=>$object->ref, + 'object_ref_ext'=>$object->ref_ext, + 'object_ref_customer'=>$object->ref_client, + 'object_date'=>dol_print_date($object->date,'day'), + 'object_fin_validite'=>dol_print_date($object->fin_validite,'dayhour'), + 'object_date_creation'=>dol_print_date($object->date_creation,'day'), + 'object_date_modification'=>dol_print_date($object->date_modification,'day'), + 'object_date_validation'=>dol_print_date($object->date_validation,'dayhour'), + 'object_payment_mode'=>$object->mode_reglement, + 'object_payment_term'=>$object->cond_reglement, + 'object_total_ht'=>price($object->total_ht,0,$outputlangs), + 'object_total_vat'=>price($object->total_tva,0,$outputlangs), + 'object_total_ttc'=>price($object->total_ttc,0,$outputlangs), + 'object_vatrate'=>vatrate($object->tva), + 'object_note_private'=>$object->note, + 'object_note'=>$object->note_public, + ); + } + + /** + * Define array with couple substitution key => substitution value + * + * @param array $line + * @param Translate $outputlangs Lang object to use for output + */ + function get_substitutionarray_lines($line,$outputlangs) + { + global $conf; + + return array( + 'line_fulldesc'=>$line->product_ref.(($line->product_ref && $line->desc)?' - ':'').$line->desc, + 'line_product_ref'=>$line->product_ref, + 'line_desc'=>$line->desc, + 'line_vatrate'=>vatrate($line->tva_tx,true,$line->info_bits), + 'line_up'=>price($line->subprice, 0, $outputlangs), + 'line_qty'=>$line->qty, + 'line_discount_percent'=>($line->remise_percent?$line->remise_percent.'%':''), + 'line_price_ht'=>price($line->total_ht, 0, $outputlangs), + 'line_price_ttc'=>price($line->total_ttc, 0, $outputlangs), + 'line_price_vat'=>price($line->total_tva, 0, $outputlangs), + 'line_date_start'=>$line->date_start, + 'line_date_end'=>$line->date_end + ); + } + + /** + * Return description of a module + * @param 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->COMMANDE_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("bills"); + + if ($conf->commande->dir_output) + { + // If $object is id instead of object + if (! is_object($object)) + { + $id = $object; + $object = new Commande($this->db); + $result=$object->fetch($id); + if ($result < 0) + { + dol_print_error($this->db,$object->error); + return -1; + } + } + + $dir = $conf->commande->dir_output; + $objectref = dol_sanitizeFileName($object->ref); + if (! preg_match('/specimen/i',$objectref)) $dir.= "/" . $objectref; + $file = $dir . "/" . $objectref . ".odt"; + + if (! file_exists($dir)) + { + if (create_exdir($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; + + create_exdir($conf->commande->dir_temp); + + + // 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 (! empty($usecontact)) + { + // On peut utiliser le nom de la societe du contact + if ($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) $socobject = $object->contact; + else $socobject = $object->client; + } + else + { + $socobject=$object->client; + } + + // Make substitution + $substitutionarray=array( + '__FROM_NAME__' => $this->emetteur->nom, + '__FROM_EMAIL__' => $this->emetteur->email, + '__TOTAL_TTC__' => $object->total_ttc, + '__TOTAL_HT__' => $object->total_ht, + '__TOTAL_VAT__' => $object->total_vat + ); + complete_substitutions_array($substitutionarray, $langs, $object); + + // Line of free text + $newfreetext=''; + $paramfreetext='COMMANDE_FREE_TEXT'; + if (! empty($conf->global->$paramfreetext)) + { + $newfreetext=make_substitutions($conf->global->$paramfreetext,$substitutionarray); + } + + // Open and load template + require_once(ODTPHP_PATH.'odf.php'); + $odfHandler = new odf($srctemplatepath, array( + 'PATH_TO_TMP' => $conf->commande->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 freetext + try { + $odfHandler->setVars('free_text', $newfreetext, true, 'UTF-8'); + } + catch(OdfException $e) + { + } + + // 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'); + } + } + catch(OdfException $e) + { + } + } + // Replace tags of object + external modules + $tmparray=$this->get_substitutionarray_object($object,$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 + try + { + $listlines = $odfHandler->setSegment('lines'); + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line,$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 + //$result=$odfHandler->exportAsAttachedFile('toto'); + $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; + } + +} + +?> diff --git a/htdocs/core/modules/commande/pdf_edison.modules.php b/htdocs/core/modules/commande/doc/pdf_edison.modules.php similarity index 100% rename from htdocs/core/modules/commande/pdf_edison.modules.php rename to htdocs/core/modules/commande/doc/pdf_edison.modules.php diff --git a/htdocs/core/modules/commande/pdf_einstein.modules.php b/htdocs/core/modules/commande/doc/pdf_einstein.modules.php similarity index 100% rename from htdocs/core/modules/commande/pdf_einstein.modules.php rename to htdocs/core/modules/commande/doc/pdf_einstein.modules.php diff --git a/htdocs/core/modules/commande/modules_commande.php b/htdocs/core/modules/commande/modules_commande.php index 00d3d858f9a..f0a282725ad 100644 --- a/htdocs/core/modules/commande/modules_commande.php +++ b/htdocs/core/modules/commande/modules_commande.php @@ -4,6 +4,7 @@ * Copyright (C) 2004 Eric Seigne * Copyright (C) 2005-2011 Regis Houssin * Copyright (C) 2006 Andre Cianfarani + * Copyright (C) 2012 Juanjo Menent * * 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 @@ -156,46 +157,55 @@ function commande_pdf_create($db, $object, $modele, $outputlangs, $hidedetails=0 $langs->load("orders"); $error=0; - + $dir = "/core/modules/commande/"; $srctemplatepath=''; - $modelisok=0; - $liste=array(); - // Positionne modele sur le nom du modele de commande a utiliser - $file = "pdf_".$modele.".modules.php"; - // On verifie l'emplacement du modele - $file = dol_buildpath($dir.$file); - if ($modele && file_exists($file)) $modelisok=1; - - // Si model pas encore bon - if (! $modelisok) + // Positionne le modele sur le nom du modele a utiliser + if (! dol_strlen($modele)) { - if ($conf->global->COMMANDE_ADDON_PDF) $modele = $conf->global->COMMANDE_ADDON_PDF; - $file = "pdf_".$modele.".modules.php"; - // On verifie l'emplacement du modele - $file = dol_buildpath($dir.$file); - if (file_exists($file)) $modelisok=1; + if (! empty($conf->global->COMMANDE_ADDON_PDF)) + { + $modele = $conf->global->COMMANDE_ADDON_PDF; + } + else + { + $modele = 'einstein'; + } } - // Si model pas encore bon - if (! $modelisok) + // 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 file + $file=''; $classname=''; $filefound=0; + foreach(array('doc','pdf') as $prefix) { - $liste=ModelePDFCommandes::liste_modeles($db); - $modele=key($liste); // Renvoie premiere valeur de cle trouvee dans le tableau - $file = "pdf_".$modele.".modules.php"; - // On verifie l'emplacement du modele - $file = dol_buildpath($dir.$file); - if (file_exists($file)) $modelisok=1; + $file = $prefix."_".$modele.".modules.php"; + + // On verifie l'emplacement du modele + $file = dol_buildpath($dir.'doc/'.$file); + + if (file_exists($file)) + { + $filefound=1; + $classname=$prefix.'_'.$modele; + break; + } } // Charge le modele - if ($modelisok) + if ($filefound) { - $classname = "pdf_".$modele; require_once($file); $obj = new $classname($db); + $obj->message = $message; // We save charset_output to restore it because write_file can change it if needed for // output format that does not support UTF8. @@ -204,10 +214,13 @@ function commande_pdf_create($db, $object, $modele, $outputlangs, $hidedetails=0 { $outputlangs->charset_output=$sav_charset_output; - // we delete preview files - require_once(DOL_DOCUMENT_ROOT."/core/lib/files.lib.php"); + // We delete old preview + 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); @@ -220,22 +233,15 @@ function commande_pdf_create($db, $object, $modele, $outputlangs, $hidedetails=0 else { $outputlangs->charset_output=$sav_charset_output; - dol_syslog("Error"); - dol_print_error($db,$obj->error); - return 0; + dol_print_error($db,"order_pdf_create Error: ".$obj->error); + return -1; } + } else { - if (! $conf->global->COMMANDE_ADDON_PDF) - { - print $langs->trans("Error")." ".$langs->trans("Error_COMMANDE_ADDON_PDF_NotDefined"); - } - else - { - print $langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file); - } - return 0; + dol_print_error('',$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file)); + return -1; } } ?> diff --git a/htdocs/core/modules/modCommande.class.php b/htdocs/core/modules/modCommande.class.php index 64ac2ec3c2b..4ce90bc9178 100644 --- a/htdocs/core/modules/modCommande.class.php +++ b/htdocs/core/modules/modCommande.class.php @@ -5,6 +5,7 @@ * Copyright (C) 2004 Benoit Mortier * Copyright (C) 2004 Eric Seigne * Copyright (C) 2005-2011 Regis Houssin + * Copyright (C) 2012 Juanjo Menent * * 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 @@ -89,6 +90,13 @@ class modCommande extends DolibarrModules $this->const[$r][2] = "mod_commande_marbre"; $this->const[$r][3] = 'Nom du gestionnaire de numerotation des commandes'; $this->const[$r][4] = 0; + + $r++; + $this->const[$r][0] = "COMMANDE_ADDON_PDF_ODT_PATH"; + $this->const[$r][1] = "chaine"; + $this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/orders"; + $this->const[$r][3] = ""; + $this->const[$r][4] = 0; // Boites $this->boxes = array(); diff --git a/htdocs/install/doctemplates/orders/template_order.odt b/htdocs/install/doctemplates/orders/template_order.odt new file mode 100644 index 0000000000000000000000000000000000000000..abfcae8e85afadaee61b430c5139f96053d6660b GIT binary patch literal 24494 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 ANdN!< literal 0 HcmV?d00001