From 008163628ab1ed78afd5dab12d4f5da593e5eef5 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 26 Jun 2018 08:03:25 +0200 Subject: [PATCH 1/4] New pdf documents using new column system --- .../core/class/commondocgenerator.class.php | 225 ++ .../commande/doc/pdf_eratosthene.modules.php | 1675 ++++++++++++++ .../facture/doc/pdf_sponge.modules.php | 2030 +++++++++++++++++ .../modules/propale/doc/pdf_cyan.modules.php | 1891 +++++++++++++++ htdocs/langs/en_US/bills.lang | 3 +- htdocs/langs/en_US/orders.lang | 3 +- htdocs/langs/en_US/propal.lang | 3 +- 7 files changed, 5827 insertions(+), 3 deletions(-) create mode 100644 htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php create mode 100644 htdocs/core/modules/facture/doc/pdf_sponge.modules.php create mode 100644 htdocs/core/modules/propale/doc/pdf_cyan.modules.php diff --git a/htdocs/core/class/commondocgenerator.class.php b/htdocs/core/class/commondocgenerator.class.php index d3d77640540..ef0283a85d5 100644 --- a/htdocs/core/class/commondocgenerator.class.php +++ b/htdocs/core/class/commondocgenerator.class.php @@ -749,5 +749,230 @@ abstract class CommonDocGenerator if (empty($hidebottom)) $pdf->line($x+$l, $y+$h, $x, $y+$h); $pdf->line($x, $y+$h, $x, $y); } + + + /** + * uasort callback function to Sort colums fields + * + * @param array $a PDF lines array fields configs + * @param array $b PDF lines array fields configs + * @return int Return compare result + */ + function columnSort($a, $b) { + + if(empty($a['rank'])){ $a['rank'] = 0; } + if(empty($b['rank'])){ $b['rank'] = 0; } + if ($a['rank'] == $b['rank']) { + return 0; + } + return ($a['rank'] > $b['rank']) ? -1 : 1; + + } + + /** + * Prepare Array Column Field + * + * @param object $object common object + * @param outputlangs $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 + */ + function prepareArrayColumnField($object,$outputlangs,$hidedetails=0,$hidedesc=0,$hideref=0){ + + global $conf; + + $this->defineColumnField($object,$outputlangs,$hidedetails,$hidedesc,$hideref); + + + // Sorting + uasort ( $this->cols, array( $this, 'columnSort' ) ); + + // Positionning + $curX = $this->page_largeur-$this->marge_droite; // start from right + + // Array witdh + $arrayWidth = $this->page_largeur-$this->marge_droite-$this->marge_gauche; + + // Count flexible column + $totalDefinedColWidth = 0; + $countFlexCol = 0; + foreach ($this->cols as $colKey =>& $colDef) + { + if(!$this->getColumnStatus($colKey)) continue; // continue if desable + + if(!empty($colDef['scale'])){ + // In case of column widht is defined by percentage + $colDef['width'] = abs($arrayWidth * $colDef['scale'] / 100 ); + } + + if(empty($colDef['width'])){ + $countFlexCol++; + } + else{ + $totalDefinedColWidth += $colDef['width']; + } + } + + foreach ($this->cols as $colKey =>& $colDef) + { + // setting empty conf with default + if(!empty($colDef['title'])){ + $colDef['title'] = array_replace($this->defaultTitlesFieldsStyle, $colDef['title']); + } + else{ + $colDef['title'] = $this->defaultTitlesFieldsStyle; + } + + // setting empty conf with default + if(!empty($colDef['content'])){ + $colDef['content'] = array_replace($this->defaultContentsFieldsStyle, $colDef['content']); + } + else{ + $colDef['content'] = $this->defaultContentsFieldsStyle; + } + + if($this->getColumnStatus($colKey)) + { + // In case of flexible column + if(empty($colDef['width'])){ + $colDef['width'] = abs(($arrayWidth - $totalDefinedColWidth)) / $countFlexCol; + } + + // Set positions + $lastX = $curX; + $curX = $lastX - $colDef['width']; + $colDef['xStartPos'] = $curX; + $colDef['xEndPos'] = $lastX; + } + } + } + + /** + * get column content width from column key + * + * @param string $colKey the column key + * @return float width in mm + */ + function getColumnContentWidth($colKey) + { + $colDef = $this->cols[$colKey]; + return $colDef['width'] - $colDef['content']['padding'][3] - $colDef['content']['padding'][1]; + } + + + /** + * get column content X (abscissa) left position from column key + * + * @param string $colKey the column key + * @return float X position in mm + */ + function getColumnContentXStart($colKey) + { + $colDef = $this->cols[$colKey]; + return $colDef['xStartPos'] + $colDef['content']['padding'][3]; + } + + /** + * get column position rank from column key + * + * @param string $colKey the column key + * @return int rank on success and -1 on error + */ + function getColumnRank($colKey) + { + if(!isset($this->cols[$colKey]['rank'])) return -1; + return $this->cols[$colKey]['rank']; + } + + /** + * get column position rank from column key + * + * @param string $newColKey the new column key + * @param array $defArray a single column definition array + * @param string $targetCol target column used to place the new column beside + * @param bool $insertAfterTarget insert before or after target column ? + * @return int new rank on success and -1 on error + */ + function insertNewColumnDef($newColKey, $defArray, $targetCol = false, $insertAfterTarget = false) + { + // prepare wanted rank + $rank = -1; + + // try to get rank from target column + if(!empty($targetCol)){ + $rank = $this->getColumnRank($targetCol); + if($rank>=0 && $insertAfterTarget){ $rank++; } + } + + // get rank from new column definition + if($rank<0 && !empty($defArray['rank'])){ + $rank = $defArray['rank']; + } + + // error: no rank + if($rank<0){ return -1; } + + foreach ($this->cols as $colKey =>& $colDef) + { + if( $rank <= $colDef['rank']) + { + $colDef['rank'] = $colDef['rank'] + 1; + } + } + + $defArray['rank'] = $rank; + $this->cols[$newColKey] = $defArray; // array_replace is used to preserve keys + + return $rank; + } + + + /** + * print standard column content + * + * @param PDF $pdf pdf object + * @param float $curY curent Y position + * @param string $colKey the column key + * @param string $columnText column text + * @return int new rank on success and -1 on error + */ + function printStdColumnContent($pdf, &$curY, $colKey, $columnText = '') + { + global $hookmanager; + + $parameters=array( + 'object' => $object, + 'curY' =>& $curY, + 'columnText' => $columnText, + 'colKey' => $colKey + ); + $reshook=$hookmanager->executeHooks('printStdColumnContent',$parameters,$this); // Note that $action and $object may have been modified by hook + if ($reshook < 0) setEventMessages($hookmanager->error,$hookmanager->errors,'errors'); + if (!$reshook) + { + if(empty($columnText)) return; + $pdf->SetXY($this->getColumnContentXStart($colKey),$curY); // Set curent position + $colDef = $this->cols[$colKey]; + $pdf->MultiCell( $this->getColumnContentWidth($colKey),2, $columnText,'',$colDef['content']['align']); + } + + } + + + /** + * get column status from column key + * + * @param string $colKey the column key + * @return float width in mm + */ + function getColumnStatus($colKey) + { + if( !empty($this->cols[$colKey]['status'])){ + return true; + } + else return false; + } } diff --git a/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php b/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php new file mode 100644 index 00000000000..58129bea6e2 --- /dev/null +++ b/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php @@ -0,0 +1,1675 @@ + + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2008 Raphael Bertrand + * Copyright (C) 2010-2013 Juanjo Menent + * Copyright (C) 2012 Christophe Battarel + * Copyright (C) 2012 Cedric Salvador + * Copyright (C) 2015 Marcos García + * Copyright (C) 2017 Ferran Marcet + * + * 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/commande/doc/pdf_eratosthene.modules.php + * \ingroup commande + * \brief Fichier de la classe permettant de generer les commandes au modele Eratosthène + */ + +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/pdf.lib.php'; + + +/** + * Classe to generate PDF orders with template Eratosthene + */ +class pdf_eratosthene extends ModelePDFCommandes +{ + /** + * @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.3 = array(5, 3) + */ + public $phpmin = array(5, 2); + + /** + * Dolibarr version of the loaded document + * @public string + */ + public $version = 'dolibarr'; + + public $page_largeur; + public $page_hauteur; + public $format; + public $marge_gauche; + public $marge_droite; + public $marge_haute; + public $marge_basse; + + public $emetteur; // Objet societe qui emet + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + global $conf,$langs,$mysoc; + + // Translations + $langs->loadLangs(array("main", "bills", "products")); + + $this->db = $db; + $this->name = "eratosthene"; + $this->description = $langs->trans('PDFEratostheneDescription'); + $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; + + $this->option_logo = 1; // Affiche logo + $this->option_tva = 1; // Gere option tva FACTURE_TVAOPTION + $this->option_modereg = 1; // Affiche mode reglement + $this->option_condreg = 1; // Affiche conditions reglement + $this->option_codeproduitservice = 1; // Affiche code produit-service + $this->option_multilang = 1; // 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 = 1; // Support add of a watermark on drafts + + $this->franchise=!$mysoc->tva_assuj; + + // 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; + + + $this->tva=array(); + $this->localtax1=array(); + $this->localtax2=array(); + $this->atleastoneratenotnull=0; + $this->atleastonediscount=0; + } + + /** + * 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 + */ + function write_file($object, $outputlangs, $srctemplatepath='', $hidedetails=0, $hidedesc=0, $hideref=0) + { + global $user, $langs, $conf, $mysoc, $db, $hookmanager, $nblignes; + + 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'; + + // Translations + $outputlangs->loadLangs(array("main", "dict", "companies", "bills", "products", "orders", "deliveries")); + + $nblignes = count($object->lines); + + if ($conf->commande->dir_output) + { + $object->fetch_thirdparty(); + + $deja_regle = 0; + + // Definition of $dir and $file + if ($object->specimen) + { + $dir = $conf->commande->dir_output; + $file = $dir . "/SPECIMEN.pdf"; + } + else + { + $objectref = dol_sanitizeFileName($object->ref); + $dir = $conf->commande->dir_output . "/" . $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 + + // Create pdf instance + $pdf=pdf_getInstance($this->format); + $default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance + $pdf->SetAutoPageBreak(1,0); + + $heightforinfotot = 40; // Height reserved to output the info and total 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->dir_output.'/'.$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("PdfOrderTitle")); + $pdf->SetCreator("Dolibarr ".DOL_VERSION); + $pdf->SetAuthor($outputlangs->convToOutputCharset($user->getFullName($outputlangs))); + $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfOrderTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name)); + if (! empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) $pdf->SetCompression(false); + + $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right + + /// Does we have at least one line with discount $this->atleastonediscount + foreach ($object->lines as $line) { + if ($line->remise_percent){ + $this->atleastonediscount = true; + break; + } + } + + if (empty($this->atleastonediscount) && empty($conf->global->PRODUCT_USE_UNITS)) + { + $this->posxpicture+=($this->postotalht - $this->posxdiscount); + $this->posxtva+=($this->postotalht - $this->posxdiscount); + $this->posxup+=($this->postotalht - $this->posxdiscount); + $this->posxqty+=($this->postotalht - $this->posxdiscount); + $this->posxdiscount+=($this->postotalht - $this->posxdiscount); + //$this->postotalht; + } + + // New page + $pdf->AddPage(); + if (! empty($tplidx)) $pdf->useTemplate($tplidx); + $pagenb++; + $top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs); + $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); + + // Incoterm + if ($conf->incoterm->enabled) + { + $desc_incoterms = $object->getIncotermsForPDF(); + if ($desc_incoterms) + { + $tab_top -= 2; + + $pdf->SetFont('','', $default_font_size - 1); + $pdf->writeHTMLCell(190, 3, $this->posxdesc-1, $tab_top-1, dol_htmlentitiesbr($desc_incoterms), 0, 1); + $nexY = $pdf->GetY(); + $height_incoterms=$nexY-$tab_top; + + // Rect prend une longueur en 3eme param + $pdf->SetDrawColor(192,192,192); + $pdf->Rect($this->marge_gauche, $tab_top-1, $this->page_largeur-$this->marge_gauche-$this->marge_droite, $height_incoterms+1); + + $tab_top = $nexY+6; + } + } + + // Affiche notes + $notetoshow=empty($object->note_public)?'':$object->note_public; + if (! empty($conf->global->MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE)) + { + // Get first sale rep + if (is_object($object->thirdparty)) + { + $salereparray=$object->thirdparty->getSalesRepresentatives($user); + $salerepobj=new User($this->db); + $salerepobj->fetch($salereparray[0]['id']); + if (! empty($salerepobj->signature)) $notetoshow=dol_concatdesc($notetoshow, $salerepobj->signature); + } + } + + $pagenb = $pdf->getPage(); + if ($notetoshow) + { + $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); + + $tab_top -= 2; + + $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); + + // prepar 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 previus 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; + } + + $iniY = $tab_top + 7; + $curY = $tab_top + 7; + $nexY = $tab_top + 7; + + // Use new auto collum system + $this->prepareArrayColumnField($object,$outputlangs,$hidedetails,$hidedesc,$hideref); + + // Loop on each lines + $pageposbeforeprintlines=$pdf->getPage(); + $pagenb = $pageposbeforeprintlines; + for ($i = 0 ; $i < $nblignes ; $i++) + { + $curY = $nexY; + $pdf->SetFont('','', $default_font_size - 1); // Into loop to work with multipage + $pdf->SetTextColor(0,0,0); + + $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(); + + // Description of product line + $curX = $this->posxdesc-1; + + $showpricebeforepagebreak=1; + + if($this->getColumnStatus('desc')) + { + $pdf->startTransaction(); + pdf_writelinedesc($pdf,$object,$i,$outputlangs,$this->getColumnContentWidth('desc'),3,$this->getColumnContentXStart('desc'),$curY,$hideref,$hidedesc); + $pageposafter=$pdf->getPage(); + if ($pageposafter > $pageposbefore) // There is a pagebreak + { + $pdf->rollbackTransaction(true); + $pageposafter=$pageposbefore; + //print $pageposafter.'-'.$pageposbefore;exit; + $pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it. + pdf_writelinedesc($pdf,$object,$i,$outputlangs,$this->getColumnContentWidth('desc'),3,$this->getColumnContentXStart('desc'),$curY,$hideref,$hidedesc); + $pageposafter=$pdf->getPage(); + $posyafter=$pdf->GetY(); + if ($posyafter > ($this->page_hauteur - ($heightforfooter+$heightforfreetext+$heightforinfotot))) // There is no space left for total+free text + { + if ($i == ($nblignes-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); + //if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) $this->_pagehead($pdf, $object, 0, $outputlangs); + $pdf->setPage($pageposafter+1); + } + } + else + { + // We found a page break + $showpricebeforepagebreak=0; + } + } + else // No pagebreak + { + $pdf->commitTransaction(); + } + $posYAfterDescription=$pdf->GetY(); + } + + $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 is 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 + + // VAT Rate + if ($this->getColumnStatus('vat')) + { + $vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate); + $nexY = max($pdf->GetY(),$nexY); + } + + // Unit price before discount + if ($this->getColumnStatus('subprice')) + { + $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax); + $nexY = max($pdf->GetY(),$nexY); + } + + // 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); + } + + + // Unit + if ($this->getColumnStatus('unit')) + { + $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails, $hookmanager); + $this->printStdColumnContent($pdf, $curY, 'unit', $unit); + $nexY = max($pdf->GetY(),$nexY); + } + + // Discount on line + if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) + { + $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent); + $nexY = max($pdf->GetY(),$nexY); + } + + // Total HT line + if ($this->getColumnStatus('totalexcltax')) + { + $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax); + $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 + + + // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva + if ($conf->multicurrency->enabled && $object->multicurrency_tx != 1) $tvaligne=$object->lines[$i]->multicurrency_total_tva; + else $tvaligne=$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 = $localtaxtmp_array[0]; + $localtax2_type = $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; + + // Add line + if (! empty($conf->global->MAIN_PDF_DASH_BETWEEN_LINES) && $i < ($nblignes - 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+1, $this->page_largeur - $this->marge_droite, $nexY+1); + $pdf->SetLineStyle(array('dash'=>0)); + } + + $nexY+=2; // Passe espace entre les lignes + + // 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, 0, 1, $object->multicurrency_code); + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code); + } + $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, 0, 1, $object->multicurrency_code); + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code); + } + $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, 0, 0, $object->multicurrency_code); + else + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code); + $bottomlasttab=$this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1; + + // Affiche zone infos + $posy=$this->_tableau_info($pdf, $object, $bottomlasttab, $outputlangs); + + // Affiche zone totaux + $posy=$this->_tableau_tot($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs); + + // Affiche zone versements + /* + if ($deja_regle) + { + $posy=$this->_tableau_versements($pdf, $object, $posy, $outputlangs); + } + */ + + // Pied de page + $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 (! empty($conf->global->MAIN_UMASK)) + @chmod($file, octdec($conf->global->MAIN_UMASK)); + + $this->result = array('fullpath'=>$file); + + return 1; // Pas d'erreur + } + else + { + $this->error=$langs->transnoentities("ErrorCanNotCreateDir",$dir); + return 0; + } + } + else + { + $this->error=$langs->transnoentities("ErrorConstantNotDefined","COMMANDE_OUTPUTDIR"); + return 0; + } + } + + /** + * Show payments table + * + * @param TCPDF $pdf Object PDF + * @param Object $object Object order + * @param int $posy Position y in PDF + * @param Translate $outputlangs Object langs for output + * @return int <0 if KO, >0 if OK + */ + function _tableau_versements(&$pdf, $object, $posy, $outputlangs) + { + + } + + + /** + * Show miscellaneous information (payment mode, payment term, ...) + * + * @param TCPDF $pdf Object PDF + * @param Object $object Object to show + * @param int $posy Y + * @param Translate $outputlangs Langs object + * @return void + */ + function _tableau_info(&$pdf, $object, $posy, $outputlangs) + { + global $conf; + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $pdf->SetFont('','', $default_font_size - 1); + + // If France, show VAT mention if not applicable + if ($this->emetteur->country_code == 'FR' && $this->franchise == 1) + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0); + + $posy=$pdf->GetY()+4; + } + + $posxval=52; + + // Show payments conditions + if ($object->cond_reglement_code || $object->cond_reglement) + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentConditions").':'; + $pdf->MultiCell(43, 4, $titre, 0, 'L'); + + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_condition_paiement=$outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code)!=('PaymentCondition'.$object->cond_reglement_code)?$outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code):$outputlangs->convToOutputCharset($object->cond_reglement_doc); + $lib_condition_paiement=str_replace('\n',"\n",$lib_condition_paiement); + $pdf->MultiCell(67, 4, $lib_condition_paiement,0,'L'); + + $posy=$pdf->GetY()+3; + } + + // Check a payment mode is defined + /* Not used with orders + if (empty($object->mode_reglement_code) + && ! $conf->global->FACTURE_CHQ_NUMBER + && ! $conf->global->FACTURE_RIB_NUMBER) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetTextColor(200,0,0); + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->MultiCell(80, 3, $outputlangs->transnoentities("ErrorNoPaiementModeConfigured"),0,'L',0); + $pdf->SetTextColor(0,0,0); + + $posy=$pdf->GetY()+1; + } + */ + /* TODO + else if (! empty($object->availability_code)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetTextColor(200,0,0); + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->MultiCell(80, 3, $outputlangs->transnoentities("AvailabilityPeriod").': '.,0,'L',0); + $pdf->SetTextColor(0,0,0); + + $posy=$pdf->GetY()+1; + }*/ + + // Show planed date of delivery + if (! empty($object->date_livraison)) + { + $outputlangs->load("sendings"); + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("DateDeliveryPlanned").':'; + $pdf->MultiCell(80, 4, $titre, 0, 'L'); + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $dlp=dol_print_date($object->date_livraison,"daytext",false,$outputlangs,true); + $pdf->MultiCell(80, 4, $dlp, 0, 'L'); + + $posy=$pdf->GetY()+1; + } + elseif ($object->availability_code || $object->availability) // Show availability conditions + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("AvailabilityPeriod").':'; + $pdf->MultiCell(80, 4, $titre, 0, 'L'); + $pdf->SetTextColor(0,0,0); + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_availability=$outputlangs->transnoentities("AvailabilityType".$object->availability_code)!=('AvailabilityType'.$object->availability_code)?$outputlangs->transnoentities("AvailabilityType".$object->availability_code):$outputlangs->convToOutputCharset(isset($object->availability)?$object->availability:''); + $lib_availability=str_replace('\n',"\n",$lib_availability); + $pdf->MultiCell(80, 4, $lib_availability, 0, 'L'); + + $posy=$pdf->GetY()+1; + } + + // Show payment mode + if ($object->mode_reglement_code + && $object->mode_reglement_code != 'CHQ' + && $object->mode_reglement_code != 'VIR') + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentMode").':'; + $pdf->MultiCell(80, 5, $titre, 0, 'L'); + + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_mode_reg=$outputlangs->transnoentities("PaymentType".$object->mode_reglement_code)!=('PaymentType'.$object->mode_reglement_code)?$outputlangs->transnoentities("PaymentType".$object->mode_reglement_code):$outputlangs->convToOutputCharset($object->mode_reglement); + $pdf->MultiCell(80, 5, $lib_mode_reg,0,'L'); + + $posy=$pdf->GetY()+2; + } + + // Show payment mode CHQ + if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') + { + // Si mode reglement non force ou si force a CHQ + if (! empty($conf->global->FACTURE_CHQ_NUMBER)) + { + if ($conf->global->FACTURE_CHQ_NUMBER > 0) + { + $account = new Account($this->db); + $account->fetch($conf->global->FACTURE_CHQ_NUMBER); + + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','B', $default_font_size - 3); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo',$account->proprio),0,'L',0); + $posy=$pdf->GetY()+1; + + if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','', $default_font_size - 3); + $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0); + $posy=$pdf->GetY()+2; + } + } + if ($conf->global->FACTURE_CHQ_NUMBER == -1) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','B', $default_font_size - 3); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo',$this->emetteur->name),0,'L',0); + $posy=$pdf->GetY()+1; + + if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','', $default_font_size - 3); + $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0); + $posy=$pdf->GetY()+2; + } + } + } + } + + // If payment mode not forced or forced to VIR, show payment with BAN + if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') + { + if (! empty($object->fk_account) || ! empty($object->fk_bank) || ! empty($conf->global->FACTURE_RIB_NUMBER)) + { + $bankid=(empty($object->fk_account)?$conf->global->FACTURE_RIB_NUMBER:$object->fk_account); + if (! empty($object->fk_bank)) $bankid=$object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank + $account = new Account($this->db); + $account->fetch($bankid); + + $curx=$this->marge_gauche; + $cury=$posy; + + $posy=pdf_bank($pdf,$outputlangs,$curx,$cury,$account,0,$default_font_size); + + $posy+=2; + } + } + + return $posy; + } + + + /** + * Show total to pay + * + * @param TCPDF $pdf Object PDF + * @param Facture $object Object invoice + * @param int $deja_regle Montant deja regle + * @param int $posy Position depart + * @param Translate $outputlangs Objet langs + * @return int Position pour suite + */ + function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs) + { + global $conf,$mysoc; + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $tab2_top = $posy; + $tab2_hl = 4; + $pdf->SetFont('','', $default_font_size - 1); + + // Tableau total + $col1x = 120; $col2x = 170; + if ($this->page_largeur < 210) // To work with US executive format + { + $col2x-=20; + } + $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x); + + $useborder=0; + $index = 0; + + // Total HT + $pdf->SetFillColor(255,255,255); + $pdf->SetXY($col1x, $tab2_top + 0); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("TotalHT"), 0, 'L', 1); + + $total_ht = ($conf->multicurrency->enabled && $object->mylticurrency_tx != 1 ? $object->multicurrency_total_ht : $object->total_ht); + $pdf->SetXY($col2x, $tab2_top + 0); + $pdf->MultiCell($largcol2, $tab2_hl, price($total_ht + (! empty($object->remise)?$object->remise:0), 0, $outputlangs), 0, 'R', 1); + + // Show VAT by rates and total + $pdf->SetFillColor(248,248,248); + + $total_ttc = ($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc; + + $this->atleastoneratenotnull=0; + if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT)) + { + $tvaisnull=((! empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false); + if (! empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL) && $tvaisnull) + { + // Nothing to do + } + else + { + //Local tax 1 before VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on') + //{ + foreach( $this->localtax1 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('1','3','5'))) continue; + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey!=0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT1",$mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + //Local tax 2 before VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on') + //{ + foreach( $this->localtax2 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('1','3','5'))) continue; + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey!=0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT2",$mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + + } + } + } + //} + // VAT + foreach($this->tva as $tvakey => $tvaval) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + $this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat =$outputlangs->transcountrynoentities("TotalVAT",$mysoc->country_code).' '; + $totalvat.=vatrate($tvakey,1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + + //Local tax 1 after VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on') + //{ + foreach( $this->localtax1 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('2','4','6'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT1",$mysoc->country_code).' '; + + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + //Local tax 2 after VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on') + //{ + foreach( $this->localtax2 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('2','4','6'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT2",$mysoc->country_code).' '; + + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + + // Total TTC + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->SetTextColor(0,0,60); + $pdf->SetFillColor(224,224,224); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($total_ttc, 0, $outputlangs), $useborder, 'R', 1); + } + } + + $pdf->SetTextColor(0,0,0); + + $creditnoteamount=0; + $depositsamount=0; + //$creditnoteamount=$object->getSumCreditNotesUsed(); + //$depositsamount=$object->getSumDepositsUsed(); + //print "x".$creditnoteamount."-".$depositsamount;exit; + $resteapayer = price2num($total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 'MT'); + if (! empty($object->paye)) $resteapayer=0; + + if ($deja_regle > 0) + { + // Already paid + Deposits + $index++; + + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("AlreadyPaid"), 0, 'L', 0); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle, 0, $outputlangs), 0, 'R', 0); + + $index++; + $pdf->SetTextColor(0,0,60); + $pdf->SetFillColor(224,224,224); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1); + + $pdf->SetFont('','', $default_font_size - 1); + $pdf->SetTextColor(0,0,0); + } + + $index++; + return ($tab2_top + ($tab2_hl * $index)); + } + + /** + * Show table for lines + * + * @param PDF $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 + * @return void + */ + function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop=0, $hidebottom=0, $currency='') + { + 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)); + $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, 5, '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 prend une longueur en 3eme param et 4eme param + + + foreach ($this->cols as $colKey => $colDef) + { + if(!$this->getColumnStatus($colKey)) continue; + + // get title label + $colDef['title']['label'] = !empty($colDef['title']['label'])?$colDef['title']['label']:$outputlangs->transnoentities($colDef['title']['textkey']); + + // Add column separator + if(!empty($colDef['border-left'])){ + $pdf->line($colDef['xStartPos'], $tab_top, $colDef['xStartPos'], $tab_top + $tab_height); + } + + if (empty($hidetop)) + { + $pdf->SetXY($colDef['xStartPos'] + $colDef['title']['padding'][3], $tab_top + $colDef['title']['padding'][0] ); + + $textWidth = $colDef['width'] - $colDef['title']['padding'][3] -$colDef['title']['padding'][1]; + $pdf->MultiCell($textWidth,2,$colDef['title']['label'],'',$colDef['title']['align']); + } + } + + if (empty($hidetop)){ + $pdf->line($this->marge_gauche, $tab_top+5, $this->page_largeur-$this->marge_droite, $tab_top+5); // line prend une position y en 2eme param et 4eme param + } + + + } + + /** + * 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 string $titlekey Translation key to show as title of document + * @return void + */ + function _pagehead(&$pdf, $object, $showaddress, $outputlangs, $titlekey="PdfOrderTitle") + { + global $conf,$langs,$hookmanager; + + // Translations + $outputlangs->loadLangs(array("main", "bills", "propal", "orders", "companies")); + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + pdf_pagehead($pdf,$outputlangs,$this->page_hauteur); + + // Show Draft Watermark + if($object->statut==0 && (! empty($conf->global->COMMANDE_DRAFT_WATERMARK)) ) + { + pdf_watermark($pdf,$outputlangs,$this->page_hauteur,$this->page_largeur,'mm',$conf->global->COMMANDE_DRAFT_WATERMARK); + } + + $pdf->SetTextColor(0,0,60); + $pdf->SetFont('','B', $default_font_size + 3); + + $posy=$this->marge_haute; + $posx=$this->page_largeur-$this->marge_droite-100; + + $pdf->SetXY($this->marge_gauche,$posy); + + // Logo + $logo=$conf->mycompany->dir_output.'/logos/'.$this->emetteur->logo; + if ($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(100, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound",$logo), 0, 'L'); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L'); + } + } + else + { + $text=$this->emetteur->name; + $pdf->MultiCell(100, 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($titlekey); + $pdf->MultiCell(100, 3, $title, '', 'R'); + + $pdf->SetFont('','B',$default_font_size); + + $posy+=5; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 4, $outputlangs->transnoentities("Ref")." : " . $outputlangs->convToOutputCharset($object->ref), '', 'R'); + + $posy+=1; + $pdf->SetFont('','', $default_font_size - 1); + + if ($object->ref_client) + { + $posy+=5; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("RefCustomer")." : " . $outputlangs->convToOutputCharset($object->ref_client), '', 'R'); + } + + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("OrderDate")." : " . dol_print_date($object->date,"%d %b %Y",false,$outputlangs,true), '', '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(100, 3, $langs->trans("SalesRepresentative")." : ".$usertmp->getFullName($langs), '', 'R'); + } + } + + $posy+=2; + + $top_shift = 0; + // Show list of linked objects + $current_y = $pdf->getY(); + $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, 100, 3, 'R', $default_font_size); + if ($current_y < $pdf->getY()) + { + $top_shift = $pdf->getY() - $current_y; + } + + if ($showaddress) + { + // Sender properties + $carac_emetteur=''; + // Add internal contact of proposal if defined + $arrayidcontact=$object->getIdContact('internal','SALESREPFOLL'); + if (count($arrayidcontact) > 0) + { + $object->fetch_user($arrayidcontact[0]); + $labelbeforecontactname=($outputlangs->transnoentities("FromContactName")!='FromContactName'?$outputlangs->transnoentities("FromContactName"):$outputlangs->transnoentities("Name")); + $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$labelbeforecontactname." ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; + } + + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); + + // Show sender + $posy=42+$top_shift; + $posx=$this->marge_gauche; + if (! empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) $posx=$this->page_largeur-$this->marge_droite-80; + $hautcadre=40; + + // 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(82, $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(80, 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(80, 4, $carac_emetteur, 0, 'L'); + + + + // 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 + // On peut utiliser le nom de la societe du contact + if ($usecontact && !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)) { + $thirdparty = $object->contact; + } else { + $thirdparty = $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=100; + if ($this->page_largeur < 210) $widthrecbox=84; // To work with US executive format + $posy=42+$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, 4, $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; + } + + /** + * 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 + */ + function _pagefoot(&$pdf,$object,$outputlangs,$hidefreetext=0) + { + global $conf; + $showdetails=$conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS; + return pdf_pagefoot($pdf,$outputlangs,'ORDER_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 outputlangs $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 + */ + 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(0.5,0.5,0.5,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', + ), + ); + + $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_ORDERS_WITH_PICTURE)) + { + $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' => false, // 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($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 + 10; + $this->cols['totalexcltax'] = array( + 'rank' => $rank, + 'width' => 26, // in mm + 'status' => true, + 'title' => array( + 'textkey' => 'TotalHT' + ), + 'border-left' => true, // add left line separator + ); + + + $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/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php new file mode 100644 index 00000000000..72c2a5aaca6 --- /dev/null +++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php @@ -0,0 +1,2030 @@ + + * 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 + * + * 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/facture/doc/pdf_sponge.modules.php + * \ingroup facture + * \brief File of class to generate customers invoices from sponge model + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/facture/modules_facture.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 invoice template sponge + */ +class pdf_sponge extends ModelePDFFactures +{ + /** + * @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.3 = array(5, 3) + */ + public $phpmin = array(5, 2); + + /** + * Dolibarr version of the loaded document + * @public string + */ + public $version = 'dolibarr'; + + public $page_largeur; + public $page_hauteur; + public $format; + public $marge_gauche; + public $marge_droite; + public $marge_haute; + public $marge_basse; + + public $emetteur; // Objet societe qui emet + + /** + * @var bool Situation invoice type + */ + public $situationinvoice; + + /** + * @var float X position for the situation progress column + */ + public $posxprogress; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct($db) + { + global $conf,$langs,$mysoc; + + // Translations + $langs->loadLangs(array("main", "bills")); + + $this->db = $db; + $this->name = "sponge"; + $this->description = $langs->trans('PDFSpongeDescription'); + $this->update_main_doc_field = 1; // Save the name of generated file as the main doc when generating a doc with this template + + // Dimensiont 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; + + $this->option_logo = 1; // Affiche logo + $this->option_tva = 1; // Gere option tva FACTURE_TVAOPTION + $this->option_modereg = 1; // Affiche mode reglement + $this->option_condreg = 1; // Affiche conditions reglement + $this->option_codeproduitservice = 1; // Affiche code produit-service + $this->option_multilang = 1; // Dispo en plusieurs langues + $this->option_escompte = 1; // Affiche si il y a eu escompte + $this->option_credit_note = 1; // Support credit notes + $this->option_freetext = 1; // Support add of a personalised text + $this->option_draft_watermark = 1; // Support add of a watermark on drafts + + $this->franchise=!$mysoc->tva_assuj; + + // 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 + + // 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; + } + + + /** + * 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 + */ + function write_file($object,$outputlangs,$srctemplatepath='',$hidedetails=0,$hidedesc=0,$hideref=0) + { + global $user,$langs,$conf,$mysoc,$db,$hookmanager,$nblignes; + + 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'; + + // Translations + $outputlangs->loadLangs(array("main", "bills", "products", "dict", "companies")); + + $nblignes = count($object->lines); + + // 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_INVOICES_WITH_PICTURE)) + { + $objphoto = new Product($this->db); + + for ($i = 0 ; $i < $nblignes ; $i++) + { + if (empty($object->lines[$i]->fk_product)) continue; + + $objphoto->fetch($object->lines[$i]->fk_product); + //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') . dol_sanitizeFileName($objphoto->ref).'/'; // 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->facture->dir_output) + { + $object->fetch_thirdparty(); + + $deja_regle = $object->getSommePaiement(($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? 1 : 0); + $amount_credit_notes_included = $object->getSumCreditNotesUsed(($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? 1 : 0); + $amount_deposits_included = $object->getSumDepositsUsed(($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? 1 : 0); + + // Definition of $dir and $file + if ($object->specimen) + { + $dir = $conf->facture->dir_output; + $file = $dir . "/SPECIMEN.pdf"; + } + else + { + $objectref = dol_sanitizeFileName($object->ref); + $dir = $conf->facture->dir_output . "/" . $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 nblignes with the new facture lines content after hook + $nblignes = count($object->lines); + $nbpayments = count($object->getListOfPayments()); + + // 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+(4*$nbpayments); // 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->dir_output.'/'.$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("PdfInvoiceTitle")); + $pdf->SetCreator("Dolibarr ".DOL_VERSION); + $pdf->SetAuthor($outputlangs->convToOutputCharset($user->getFullName($outputlangs))); + $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfInvoiceTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name)); + if (! empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) $pdf->SetCompression(false); + + $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right + + // Does we have at least one line with discount $this->atleastonediscount + foreach ($object->lines as $line) { + if ($line->remise_percent){ + $this->atleastonediscount = true; + break; + } + } + + + // Situation invoice handling + if ($object->situation_cycle_ref) + { + $this->situationinvoice = true; + } + + // New page + $pdf->AddPage(); + if (! empty($tplidx)) $pdf->useTemplate($tplidx); + $pagenb++; + + $top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs); + $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; + + // Incoterm + $height_incoterms = 0; + if ($conf->incoterm->enabled) + { + $desc_incoterms = $object->getIncotermsForPDF(); + if ($desc_incoterms) + { + $tab_top -= 2; + + $pdf->SetFont('','', $default_font_size - 1); + $pdf->writeHTMLCell(190, 3, $this->posxdesc-1, $tab_top-1, dol_htmlentitiesbr($desc_incoterms), 0, 1); + $nexY = max($pdf->GetY(),$nexY); + $height_incoterms=$nexY-$tab_top; + + // Rect prend une longueur en 3eme param + $pdf->SetDrawColor(192,192,192); + $pdf->Rect($this->marge_gauche, $tab_top-1, $this->page_largeur-$this->marge_gauche-$this->marge_droite, $height_incoterms+1); + + $tab_top = $nexY+6; + $height_incoterms += 4; + } + } + + // Affiche notes + $notetoshow=empty($object->note_public)?'':$object->note_public; + if (! empty($conf->global->MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE)) + { + // Get first sale rep + if (is_object($object->thirdparty)) + { + $salereparray=$object->thirdparty->getSalesRepresentatives($user); + $salerepobj=new User($this->db); + $salerepobj->fetch($salereparray[0]['id']); + if (! empty($salerepobj->signature)) $notetoshow=dol_concatdesc($notetoshow, $salerepobj->signature); + } + } + + $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); + + + $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); + + // prepar 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 previus 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; + } + + $iniY = $tab_top + 7; + $curY = $tab_top + 7; + $nexY = $tab_top + 7; + + // Use new auto collum system + $this->prepareArrayColumnField($object,$outputlangs,$hidedetails,$hidedesc,$hideref); + + // Loop on each lines + $pageposbeforeprintlines=$pdf->getPage(); + $pagenb = $pageposbeforeprintlines; + for ($i = 0; $i < $nblignes; $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; + $posYAfterDescription=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; + $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(); + pdf_writelinedesc($pdf,$object,$i,$outputlangs,$this->getColumnContentWidth('desc'),3,$this->getColumnContentXStart('desc'),$curY,$hideref,$hidedesc); + $pageposafter=$pdf->getPage(); + if ($pageposafter > $pageposbefore) // There is a pagebreak + { + $pdf->rollbackTransaction(true); + $pageposafter=$pageposbefore; + //print $pageposafter.'-'.$pageposbefore;exit; + $pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it. + pdf_writelinedesc($pdf,$object,$i,$outputlangs,$this->getColumnContentWidth('desc'),3,$this->getColumnContentXStart('desc'),$curY,$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 == ($nblignes-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 + $showpricebeforepagebreak=0; + } + } + else // No pagebreak + { + $pdf->commitTransaction(); + } + $posYAfterDescription=$pdf->GetY(); + } + + $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 + + // VAT Rate + if ($this->getColumnStatus('vat')) + { + $vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate); + $nexY = max($pdf->GetY(),$nexY); + } + + // Unit price before discount + if ($this->getColumnStatus('subprice')) + { + $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax); + $nexY = max($pdf->GetY(),$nexY); + } + + // 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); + } + + // Situation progress + if ($this->getColumnStatus('progress')) + { + $progress = pdf_getlineprogress($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'progress', $progress); + $nexY = max($pdf->GetY(),$nexY); + } + + // Unit + if ($this->getColumnStatus('unit')) + { + $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails, $hookmanager); + $this->printStdColumnContent($pdf, $curY, 'unit', $unit); + $nexY = max($pdf->GetY(),$nexY); + } + + // Discount on line + if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) + { + $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent); + $nexY = max($pdf->GetY(),$nexY); + } + + // Total HT line + if ($this->getColumnStatus('totalexcltax')) + { + $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax); + $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; + if (isset($object->type) && $object->type == 2 && ! empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) $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 ($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 ($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 = $localtaxtmp_array[0]; + $localtax2_type = $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 < ($nblignes - 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+1, $this->page_largeur - $this->marge_droite, $nexY+1); + $pdf->SetLineStyle(array('dash'=>0)); + } + + $nexY+=2; // Passe espace entre les lignes + + // 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, 0, 1, $object->multicurrency_code); + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code); + } + $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, 0, 1, $object->multicurrency_code); + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code); + } + $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, 0, 0, $object->multicurrency_code); + $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); + $bottomlasttab=$this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforfooter + 1; + } + + // Affiche zone infos + $posy=$this->_tableau_info($pdf, $object, $bottomlasttab, $outputlangs); + + // Affiche zone totaux + $posy=$this->_tableau_tot($pdf, $object, $deja_regle, $bottomlasttab, $outputlangs); + + // Affiche zone versements + if (($deja_regle || $amount_credit_notes_included || $amount_deposits_included) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) + { + $posy=$this->_tableau_versements($pdf, $object, $posy, $outputlangs); + } + + // Pied de page + $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 (! 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; + } + } + + + /** + * Show payments table + * + * @param PDF $pdf Object PDF + * @param Object $object Object invoice + * @param int $posy Position y in PDF + * @param Translate $outputlangs Object langs for output + * @return int <0 if KO, >0 if OK + */ + function _tableau_versements(&$pdf, $object, $posy, $outputlangs) + { + global $conf; + + $sign=1; + if ($object->type == 2 && ! empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) $sign=-1; + + $tab3_posx = 120; + $tab3_top = $posy + 8; + $tab3_width = 80; + $tab3_height = 4; + if ($this->page_largeur < 210) // To work with US executive format + { + $tab3_posx -= 20; + } + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $title=$outputlangs->transnoentities("PaymentsAlreadyDone"); + if ($object->type == 2) $title=$outputlangs->transnoentities("PaymentsBackAlreadyDone"); + + $pdf->SetFont('','', $default_font_size - 3); + $pdf->SetXY($tab3_posx, $tab3_top - 4); + $pdf->MultiCell(60, 3, $title, 0, 'L', 0); + + $pdf->line($tab3_posx, $tab3_top, $tab3_posx+$tab3_width, $tab3_top); + + $pdf->SetFont('','', $default_font_size - 4); + $pdf->SetXY($tab3_posx, $tab3_top); + $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Payment"), 0, 'L', 0); + $pdf->SetXY($tab3_posx+21, $tab3_top); + $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Amount"), 0, 'L', 0); + $pdf->SetXY($tab3_posx+40, $tab3_top); + $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Type"), 0, 'L', 0); + $pdf->SetXY($tab3_posx+58, $tab3_top); + $pdf->MultiCell(20, 3, $outputlangs->transnoentities("Num"), 0, 'L', 0); + + $pdf->line($tab3_posx, $tab3_top-1+$tab3_height, $tab3_posx+$tab3_width, $tab3_top-1+$tab3_height); + + $y=0; + + $pdf->SetFont('','', $default_font_size - 4); + + + // Loop on each deposits and credit notes included + $sql = "SELECT re.rowid, re.amount_ht, re.multicurrency_amount_ht, re.amount_tva, re.multicurrency_amount_tva, re.amount_ttc, re.multicurrency_amount_ttc,"; + $sql.= " re.description, re.fk_facture_source,"; + $sql.= " f.type, f.datef"; + $sql.= " FROM ".MAIN_DB_PREFIX ."societe_remise_except as re, ".MAIN_DB_PREFIX ."facture as f"; + $sql.= " WHERE re.fk_facture_source = f.rowid AND re.fk_facture = ".$object->id; + $resql=$this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i=0; + $invoice=new Facture($this->db); + while ($i < $num) + { + $y+=3; + $obj = $this->db->fetch_object($resql); + + if ($obj->type == 2) $text=$outputlangs->trans("CreditNote"); + elseif ($obj->type == 3) $text=$outputlangs->trans("Deposit"); + else $text=$outputlangs->trans("UnknownType"); + + $invoice->fetch($obj->fk_facture_source); + + $pdf->SetXY($tab3_posx, $tab3_top+$y); + $pdf->MultiCell(20, 3, dol_print_date($obj->datef,'day',false,$outputlangs,true), 0, 'L', 0); + $pdf->SetXY($tab3_posx+21, $tab3_top+$y); + $pdf->MultiCell(20, 3, price(($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? $obj->multicurrency_amount_ttc : $obj->amount_ttc, 0, $outputlangs), 0, 'L', 0); + $pdf->SetXY($tab3_posx+40, $tab3_top+$y); + $pdf->MultiCell(20, 3, $text, 0, 'L', 0); + $pdf->SetXY($tab3_posx+58, $tab3_top+$y); + $pdf->MultiCell(20, 3, $invoice->ref, 0, 'L', 0); + + $pdf->line($tab3_posx, $tab3_top+$y+3, $tab3_posx+$tab3_width, $tab3_top+$y+3); + + $i++; + } + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + + // Loop on each payment + // TODO Call getListOfPaymentsgetListOfPayments instead of hard coded sql + $sql = "SELECT p.datep as date, p.fk_paiement, p.num_paiement as num, pf.amount as amount, pf.multicurrency_amount,"; + $sql.= " cp.code"; + $sql.= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."paiement as p"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as cp ON p.fk_paiement = cp.id"; + $sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = ".$object->id; + //$sql.= " WHERE pf.fk_paiement = p.rowid AND pf.fk_facture = 1"; + $sql.= " ORDER BY p.datep"; + + $resql=$this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i=0; + while ($i < $num) { + $y+=3; + $row = $this->db->fetch_object($resql); + + $pdf->SetXY($tab3_posx, $tab3_top+$y); + $pdf->MultiCell(20, 3, dol_print_date($this->db->jdate($row->date),'day',false,$outputlangs,true), 0, 'L', 0); + $pdf->SetXY($tab3_posx+21, $tab3_top+$y); + $pdf->MultiCell(20, 3, price($sign * (($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? $row->multicurrency_amount : $row->amount), 0, $outputlangs), 0, 'L', 0); + $pdf->SetXY($tab3_posx+40, $tab3_top+$y); + $oper = $outputlangs->transnoentitiesnoconv("PaymentTypeShort" . $row->code); + + $pdf->MultiCell(20, 3, $oper, 0, 'L', 0); + $pdf->SetXY($tab3_posx+58, $tab3_top+$y); + $pdf->MultiCell(30, 3, $row->num, 0, 'L', 0); + + $pdf->line($tab3_posx, $tab3_top+$y+3, $tab3_posx+$tab3_width, $tab3_top+$y+3); + + $i++; + } + } + else + { + $this->error=$this->db->lasterror(); + return -1; + } + + } + + + /** + * Show miscellaneous information (payment mode, payment term, ...) + * + * @param PDF $pdf Object PDF + * @param Object $object Object to show + * @param int $posy Y + * @param Translate $outputlangs Langs object + * @return void + */ + function _tableau_info(&$pdf, $object, $posy, $outputlangs) + { + global $conf; + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $pdf->SetFont('','', $default_font_size - 1); + + // If France, show VAT mention if not applicable + if ($this->emetteur->country_code == 'FR' && $this->franchise == 1) + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0); + + $posy=$pdf->GetY()+4; + } + + $posxval=52; + + // Show payments conditions + if ($object->type != 2 && ($object->cond_reglement_code || $object->cond_reglement)) + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentConditions").':'; + $pdf->MultiCell(43, 4, $titre, 0, 'L'); + + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_condition_paiement=$outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code)!=('PaymentCondition'.$object->cond_reglement_code)?$outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code):$outputlangs->convToOutputCharset($object->cond_reglement_doc); + $lib_condition_paiement=str_replace('\n',"\n",$lib_condition_paiement); + $pdf->MultiCell(67, 4, $lib_condition_paiement,0,'L'); + + $posy=$pdf->GetY()+3; + } + + if ($object->type != 2) + { + // Check a payment mode is defined + if (empty($object->mode_reglement_code) + && empty($conf->global->FACTURE_CHQ_NUMBER) + && empty($conf->global->FACTURE_RIB_NUMBER)) + { + $this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured"); + } + // Avoid having any valid PDF with setup that is not complete + elseif (($object->mode_reglement_code == 'CHQ' && empty($conf->global->FACTURE_CHQ_NUMBER) && empty($object->fk_account) && empty($object->fk_bank)) + || ($object->mode_reglement_code == 'VIR' && empty($conf->global->FACTURE_RIB_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))) + { + $outputlangs->load("errors"); + + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetTextColor(200,0,0); + $pdf->SetFont('','B', $default_font_size - 2); + $this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup",$object->mode_reglement_code); + $pdf->MultiCell(80, 3, $this->error,0,'L',0); + $pdf->SetTextColor(0,0,0); + + $posy=$pdf->GetY()+1; + } + + // Show payment mode + if ($object->mode_reglement_code + && $object->mode_reglement_code != 'CHQ' + && $object->mode_reglement_code != 'VIR') + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentMode").':'; + $pdf->MultiCell(80, 5, $titre, 0, 'L'); + + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_mode_reg=$outputlangs->transnoentities("PaymentType".$object->mode_reglement_code)!=('PaymentType'.$object->mode_reglement_code)?$outputlangs->transnoentities("PaymentType".$object->mode_reglement_code):$outputlangs->convToOutputCharset($object->mode_reglement); + $pdf->MultiCell(80, 5, $lib_mode_reg,0,'L'); + + $posy=$pdf->GetY()+2; + } + + // Show payment mode CHQ + if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') + { + // Si mode reglement non force ou si force a CHQ + if (! empty($conf->global->FACTURE_CHQ_NUMBER)) + { + $diffsizetitle=(empty($conf->global->PDF_DIFFSIZE_TITLE)?3:$conf->global->PDF_DIFFSIZE_TITLE); + + if ($conf->global->FACTURE_CHQ_NUMBER > 0) + { + $account = new Account($this->db); + $account->fetch($conf->global->FACTURE_CHQ_NUMBER); + + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','B', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo',$account->proprio),0,'L',0); + $posy=$pdf->GetY()+1; + + if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0); + $posy=$pdf->GetY()+2; + } + } + if ($conf->global->FACTURE_CHQ_NUMBER == -1) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','B', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo',$this->emetteur->name),0,'L',0); + $posy=$pdf->GetY()+1; + + if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0); + $posy=$pdf->GetY()+2; + } + } + } + } + + // If payment mode not forced or forced to VIR, show payment with BAN + if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') + { + if (! empty($object->fk_account) || ! empty($object->fk_bank) || ! empty($conf->global->FACTURE_RIB_NUMBER)) + { + $bankid=(empty($object->fk_account)?$conf->global->FACTURE_RIB_NUMBER:$object->fk_account); + if (! empty($object->fk_bank)) $bankid=$object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank + $account = new Account($this->db); + $account->fetch($bankid); + + $curx=$this->marge_gauche; + $cury=$posy; + + $posy=pdf_bank($pdf,$outputlangs,$curx,$cury,$account,0,$default_font_size); + + $posy+=2; + } + } + } + + return $posy; + } + + + /** + * Show total to pay + * + * @param PDF $pdf Object PDF + * @param Facture $object Object invoice + * @param int $deja_regle Montant deja regle + * @param int $posy Position depart + * @param Translate $outputlangs Objet langs + * @return int Position pour suite + */ + function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs) + { + global $conf,$mysoc; + + $sign=1; + if ($object->type == 2 && ! empty($conf->global->INVOICE_POSITIVE_CREDIT_NOTE)) $sign=-1; + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $tab2_top = $posy; + $tab2_hl = 4; + $pdf->SetFont('','', $default_font_size - 1); + + // Tableau total + $col1x = 120; $col2x = 170; + if ($this->page_largeur < 210) // To work with US executive format + { + $col2x-=20; + } + $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x); + + $useborder=0; + $index = 0; + + // Total HT + $pdf->SetFillColor(255,255,255); + $pdf->SetXY($col1x, $tab2_top + 0); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("TotalHT"), 0, 'L', 1); + + $total_ht = ($conf->multicurrency->enabled && $object->mylticurrency_tx != 1 ? $object->multicurrency_total_ht : $object->total_ht); + $pdf->SetXY($col2x, $tab2_top + 0); + $pdf->MultiCell($largcol2, $tab2_hl, price($sign * ($total_ht + (! empty($object->remise)?$object->remise:0)), 0, $outputlangs), 0, 'R', 1); + + // Show VAT by rates and total + $pdf->SetFillColor(248,248,248); + + $total_ttc = ($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc; + + $this->atleastoneratenotnull=0; + if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT)) + { + $tvaisnull=((! empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false); + if (! empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL) && $tvaisnull) + { + // Nothing to do + } + else + { + // FIXME amount of vat not supported with multicurrency + + //Local tax 1 before VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on') + //{ + foreach( $this->localtax1 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('1','3','5'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey!=0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + + $totalvat = $outputlangs->transcountrynoentities("TotalLT1",$mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + //Local tax 2 before VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on') + //{ + foreach( $this->localtax2 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('1','3','5'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey!=0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT2",$mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + + } + } + } + + //} + + // VAT + // Situations totals migth be wrong on huge amounts + if ($object->situation_cycle_ref && $object->situation_counter > 1) { + + $sum_pdf_tva = 0; + foreach($this->tva as $tvakey => $tvaval){ + $sum_pdf_tva+=$tvaval; // sum VAT amounts to compare to object + } + + if($sum_pdf_tva!=$object->total_tva) { // apply coef to recover the VAT object amount (the good one) + $coef_fix_tva = $object->total_tva / $sum_pdf_tva; + + foreach($this->tva as $tvakey => $tvaval) { + $this->tva[$tvakey]=$tvaval * $coef_fix_tva; + } + } + + } + + foreach($this->tva as $tvakey => $tvaval) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + $this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat =$outputlangs->transcountrynoentities("TotalVAT",$mysoc->country_code).' '; + $totalvat.=vatrate($tvakey,1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + + //Local tax 1 after VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on') + //{ + foreach( $this->localtax1 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('2','4','6'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT1",$mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + //Local tax 2 after VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on') + //{ + foreach( $this->localtax2 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('2','4','6'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + // retrieve global local tax + if ($tvakey != 0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT2",$mysoc->country_code).' '; + + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + //} + } + + // Revenue stamp + if (price2num($object->revenuestamp) != 0) + { + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("RevenueStamp"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->revenuestamp), $useborder, 'R', 1); + } + + // Total TTC + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->SetTextColor(0,0,60); + $pdf->SetFillColor(224,224,224); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $total_ttc, 0, $outputlangs), $useborder, 'R', 1); + } + } + + $pdf->SetTextColor(0,0,0); + + $creditnoteamount=$object->getSumCreditNotesUsed(($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? 1 : 0); + $depositsamount=$object->getSumDepositsUsed(($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? 1 : 0); + //print "x".$creditnoteamount."-".$depositsamount;exit; + $resteapayer = price2num($total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 'MT'); + if ($object->paye) $resteapayer=0; + + if (($deja_regle > 0 || $creditnoteamount > 0 || $depositsamount > 0) && empty($conf->global->INVOICE_NO_PAYMENT_DETAILS)) + { + // Already paid + Deposits + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("Paid"), 0, 'L', 0); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle + $depositsamount, 0, $outputlangs), 0, 'R', 0); + + // Credit note + if ($creditnoteamount) + { + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("CreditNotes"), 0, 'L', 0); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($creditnoteamount, 0, $outputlangs), 0, 'R', 0); + } + + // Escompte + if ($object->close_code == Facture::CLOSECODE_DISCOUNTVAT) + { + $index++; + $pdf->SetFillColor(255,255,255); + + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 0, $outputlangs), $useborder, 'R', 1); + + $resteapayer=0; + } + + $index++; + $pdf->SetTextColor(0,0,60); + $pdf->SetFillColor(224,224,224); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1); + + $pdf->SetFont('','', $default_font_size - 1); + $pdf->SetTextColor(0,0,0); + } + + $index++; + return ($tab2_top + ($tab2_hl * $index)); + } + + /** + * Show table for lines + * + * @param PDF $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 + * @return void + */ + function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop=0, $hidebottom=0, $currency='') + { + 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)); + $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, 5, '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 prend une longueur en 3eme param et 4eme param + + + foreach ($this->cols as $colKey => $colDef) + { + if(!$this->getColumnStatus($colKey)) continue; + + // get title label + $colDef['title']['label'] = !empty($colDef['title']['label'])?$colDef['title']['label']:$outputlangs->transnoentities($colDef['title']['textkey']); + + // Add column separator + if(!empty($colDef['border-left'])){ + $pdf->line($colDef['xStartPos'], $tab_top, $colDef['xStartPos'], $tab_top + $tab_height); + } + + if (empty($hidetop)) + { + $pdf->SetXY($colDef['xStartPos'] + $colDef['title']['padding'][3], $tab_top + $colDef['title']['padding'][0] ); + + $textWidth = $colDef['width'] - $colDef['title']['padding'][3] -$colDef['title']['padding'][1]; + $pdf->MultiCell($textWidth,2,$colDef['title']['label'],'',$colDef['title']['align']); + } + } + + if (empty($hidetop)){ + $pdf->line($this->marge_gauche, $tab_top+5, $this->page_largeur-$this->marge_droite, $tab_top+5); // line prend une position y en 2eme param et 4eme param + } + + + } + + /** + * Show top header of page. + * + * @param PDF $pdf Object PDF + * @param Object $object Object to show + * @param int $showaddress 0=no, 1=yes + * @param Translate $outputlangs Object lang for output + * @return void + */ + function _pagehead(&$pdf, $object, $showaddress, $outputlangs) + { + global $conf, $langs; + + // Translations + $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==Facture::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 + $logo=$conf->mycompany->dir_output.'/logos/'.$this->emetteur->logo; + if ($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("PdfInvoiceTitle"); + if ($object->type == 1) $title=$outputlangs->transnoentities("InvoiceReplacement"); + if ($object->type == 2) $title=$outputlangs->transnoentities("InvoiceAvoir"); + if ($object->type == 3) $title=$outputlangs->transnoentities("InvoiceDeposit"); + if ($object->type == 4) $title=$outputlangs->transnoentities("InvoiceProForma"); + if ($this->situationinvoice) $title=$outputlangs->transnoentities("InvoiceSituation"); + $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 == Facture::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'); + } + + $objectidnext=$object->getIdReplacingInvoice('validated'); + if ($object->type == 0 && $objectidnext) + { + $objectreplacing=new Facture($this->db); + $objectreplacing->fetch($objectidnext); + + $posy+=3; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementByInvoice").' : '.$outputlangs->convToOutputCharset($objectreplacing->ref), '', 'R'); + } + if ($object->type == 1) + { + $objectreplaced=new Facture($this->db); + $objectreplaced->fetch($object->fk_facture_source); + + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("ReplacementInvoice").' : '.$outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R'); + } + if ($object->type == 2 && !empty($object->fk_facture_source)) + { + $objectreplaced=new Facture($this->db); + $objectreplaced->fetch($object->fk_facture_source); + + $posy+=3; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("CorrectionInvoice").' : '.$outputlangs->convToOutputCharset($objectreplaced->ref), '', 'R'); + } + + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("DateInvoice")." : " . dol_print_date($object->date,"day",false,$outputlangs), '', 'R'); + + if (! empty($conf->global->INVOICE_POINTOFTAX_DATE)) + { + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("DatePointOfTax")." : " . dol_print_date($object->date_pointoftax,"day",false,$outputlangs), '', 'R'); + } + + if ($object->type != 2) + { + $posy+=3; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell($w, 3, $outputlangs->transnoentities("DateDue")." : " . dol_print_date($object->date_lim_reglement,"day",false,$outputlangs,true), '', '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 + // On peut utiliser le nom de la societe du contact + if ($usecontact && !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)) { + $thirdparty = $object->contact; + } else { + $thirdparty = $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; + } + + /** + * Show footer of page. Need this->emetteur object + * + * @param PDF $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 + */ + function _pagefoot(&$pdf,$object,$outputlangs,$hidefreetext=0) + { + global $conf; + $showdetails=$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 outputlangs $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 + */ + 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(0.5,0.5,0.5,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', + ), + ); + + // 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($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 + 10; + $this->cols['totalexcltax'] = array( + 'rank' => $rank, + 'width' => 26, // in mm + 'status' => true, + 'title' => array( + 'textkey' => 'TotalHT' + ), + 'border-left' => true, // add left line separator + ); + + + $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/propale/doc/pdf_cyan.modules.php b/htdocs/core/modules/propale/doc/pdf_cyan.modules.php new file mode 100644 index 00000000000..383f34acebd --- /dev/null +++ b/htdocs/core/modules/propale/doc/pdf_cyan.modules.php @@ -0,0 +1,1891 @@ + + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2008 Raphael Bertrand + * Copyright (C) 2010-2015 Juanjo Menent + * Copyright (C) 2012 Christophe Battarel + * Copyright (C) 2012 Cedric Salvador + * Copyright (C) 2015 Marcos García + * Copyright (C) 2017 Ferran Marcet + * + * 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/propale/doc/pdf_cyan.modules.php + * \ingroup propale + * \brief Fichier de la classe permettant de generer les propales au modele Cyan + */ +require_once DOL_DOCUMENT_ROOT.'/core/modules/propale/modules_propale.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 generate PDF proposal Cyan + */ +class pdf_cyan extends ModelePDFPropales +{ + var $db; + var $name; + var $description; + var $update_main_doc_field; // Save the name of generated file as the main doc when generating a doc with this template + var $type; + + var $phpmin = array(4,3,0); // Minimum version of PHP required by module + var $version = 'dolibarr'; + + var $page_largeur; + var $page_hauteur; + var $format; + var $marge_gauche; + var $marge_droite; + var $marge_haute; + var $marge_basse; + + var $emetteur; // Objet societe qui emet + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct($db) + { + global $conf,$langs,$mysoc; + + // Translations + $langs->loadLangs(array("main", "bills")); + + $this->db = $db; + $this->name = "cyan"; + $this->description = $langs->trans('DocModelCyanDescription'); + $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; + + $this->option_logo = 1; // Affiche logo + $this->option_tva = 1; // Gere option tva FACTURE_TVAOPTION + $this->option_modereg = 1; // Affiche mode reglement + $this->option_condreg = 1; // Affiche conditions reglement + $this->option_codeproduitservice = 1; // Affiche code produit-service + $this->option_multilang = 1; // 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 = 1; //Support add of a watermark on drafts + + $this->franchise=!$mysoc->tva_assuj; + + // 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; + + + + $this->tva=array(); + $this->localtax1=array(); + $this->localtax2=array(); + $this->atleastoneratenotnull=0; + $this->atleastonediscount=0; + } + + /** + * 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 + */ + function write_file($object,$outputlangs,$srctemplatepath='',$hidedetails=0,$hidedesc=0,$hideref=0) + { + global $user,$langs,$conf,$mysoc,$db,$hookmanager,$nblignes; + + 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'; + + $outputlangs->load("main"); + $outputlangs->load("dict"); + $outputlangs->load("companies"); + $outputlangs->load("bills"); + $outputlangs->load("propal"); + $outputlangs->load("products"); + + $nblignes = count($object->lines); + + // 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_PROPOSALS_WITH_PICTURE)) + { + $objphoto = new Product($this->db); + + for ($i = 0 ; $i < $nblignes ; $i++) + { + if (empty($object->lines[$i]->fk_product)) continue; + + $objphoto->fetch($object->lines[$i]->fk_product); + //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') . dol_sanitizeFileName($objphoto->ref).'/'; // 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->propal->multidir_output[$conf->entity]) + { + $object->fetch_thirdparty(); + + $deja_regle = 0; + + // Definition of $dir and $file + if ($object->specimen) + { + $dir = $conf->propal->multidir_output[$conf->entity]; + $file = $dir . "/SPECIMEN.pdf"; + } + else + { + $objectref = dol_sanitizeFileName($object->ref); + $dir = $conf->propal->multidir_output[$object->entity] . "/" . $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 + + // Create pdf instance + $pdf=pdf_getInstance($this->format); + $default_font_size = pdf_getPDFFontSize($outputlangs); // Must be after pdf_getInstance + $pdf->SetAutoPageBreak(1,0); + + 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->dir_output.'/'.$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("PdfCommercialProposalTitle")); + $pdf->SetCreator("Dolibarr ".DOL_VERSION); + $pdf->SetAuthor($outputlangs->convToOutputCharset($user->getFullName($outputlangs))); + $pdf->SetKeyWords($outputlangs->convToOutputCharset($object->ref)." ".$outputlangs->transnoentities("PdfCommercialProposalTitle")." ".$outputlangs->convToOutputCharset($object->thirdparty->name)); + if (! empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) $pdf->SetCompression(false); + + $pdf->SetMargins($this->marge_gauche, $this->marge_haute, $this->marge_droite); // Left, Top, Right + + // Does we have at least one line with discount $this->atleastonediscount + foreach ($object->lines as $line) { + if ($line->remise_percent){ + $this->atleastonediscount = true; + break; + } + } + + + + // New page + $pdf->AddPage(); + if (! empty($tplidx)) $pdf->useTemplate($tplidx); + $pagenb++; + + $heightforinfotot = 40; // Height reserved to output the info and total part + $heightforsignature = empty($conf->global->PROPAL_DISABLE_SIGNATURE)?(pdfGetHeightForHtmlContent($pdf, $outputlangs->transnoentities("ProposalCustomerSignature"))+10):0; + $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) + //print $heightforinfotot + $heightforsignature + $heightforfreetext + $heightforfooter;exit; + + $top_shift = $this->_pagehead($pdf, $object, 1, $outputlangs); + $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); + + + // Incoterm + $height_incoterms = 0; + if ($conf->incoterm->enabled) + { + $desc_incoterms = $object->getIncotermsForPDF(); + if ($desc_incoterms) + { + $tab_top -= 2; + + $pdf->SetFont('','', $default_font_size - 1); + $pdf->writeHTMLCell(190, 3, $this->posxdesc-1, $tab_top-1, dol_htmlentitiesbr($desc_incoterms), 0, 1); + $nexY = $pdf->GetY(); + $height_incoterms=$nexY-$tab_top; + + // Rect prend une longueur en 3eme param + $pdf->SetDrawColor(192,192,192); + $pdf->Rect($this->marge_gauche, $tab_top-1, $this->page_largeur-$this->marge_gauche-$this->marge_droite, $height_incoterms+1); + + $tab_top = $nexY+6; + } + } + + // Affiche notes + $notetoshow=empty($object->note_public)?'':$object->note_public; + if (! empty($conf->global->MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE)) + { + // Get first sale rep + if (is_object($object->thirdparty)) + { + $salereparray=$object->thirdparty->getSalesRepresentatives($user); + $salerepobj=new User($this->db); + $salerepobj->fetch($salereparray[0]['id']); + if (! empty($salerepobj->signature)) $notetoshow=dol_concatdesc($notetoshow, $salerepobj->signature); + } + } + if (! empty($conf->global->MAIN_ADD_CREATOR_IN_NOTE) && $object->user_author_id > 0) + { + $tmpuser=new User($this->db); + $tmpuser->fetch($object->user_author_id); + $notetoshow.='Affaire suivi par '.$tmpuser->getFullName($langs); + if ($tmpuser->email) $notetoshow.=', Mail: '.$tmpuser->email; + if ($tmpuser->office_phone) $notetoshow.=', Tel: '.$tmpuser->office_phone; + } + + $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); + + + $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); + + // prepar 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 previus 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; + } + + $iniY = $tab_top + 7; + $curY = $tab_top + 7; + $nexY = $tab_top + 7; + + // Use new auto collum system + $this->prepareArrayColumnField($object,$outputlangs,$hidedetails,$hidedesc,$hideref); + + // Loop on each lines + $pageposbeforeprintlines=$pdf->getPage(); + $pagenb = $pageposbeforeprintlines; + for ($i = 0; $i < $nblignes; $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+$heightforsignature+$heightforinfotot); // The only function to edit the bottom margin of current page to set it. + $pageposbefore=$pdf->getPage(); + + $showpricebeforepagebreak=1; + $posYAfterImage=0; + $posYAfterDescription=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+$heightforsignature+$heightforinfotot))) // If photo too high, we moved completely on new page + { + $pdf->AddPage('','',true); + if (! empty($tplidx)) $pdf->useTemplate($tplidx); + //if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) $this->_pagehead($pdf, $object, 0, $outputlangs); + $pdf->setPage($pageposbefore+1); + + $curY = $tab_top_newpage; + $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(); + pdf_writelinedesc($pdf,$object,$i,$outputlangs,$this->getColumnContentWidth('desc'),3,$this->getColumnContentXStart('desc'),$curY,$hideref,$hidedesc); + $pageposafter=$pdf->getPage(); + if ($pageposafter > $pageposbefore) // There is a pagebreak + { + $pdf->rollbackTransaction(true); + $pageposafter=$pageposbefore; + //print $pageposafter.'-'.$pageposbefore;exit; + $pdf->setPageOrientation('', 1, $heightforfooter); // The only function to edit the bottom margin of current page to set it. + pdf_writelinedesc($pdf,$object,$i,$outputlangs,$this->getColumnContentWidth('desc'),3,$this->getColumnContentXStart('desc'),$curY,$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+$heightforsignature+$heightforinfotot))) // There is no space left for total+free text + { + if ($i == ($nblignes-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); + //if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) $this->_pagehead($pdf, $object, 0, $outputlangs); + $pdf->setPage($pageposafter+1); + } + } + else + { + // We found a page break + $showpricebeforepagebreak=0; + } + } + else // No pagebreak + { + $pdf->commitTransaction(); + } + $posYAfterDescription=$pdf->GetY(); + } + + $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 + + // VAT Rate + if ($this->getColumnStatus('vat')) + { + $vat_rate = pdf_getlinevatrate($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'vat', $vat_rate); + $nexY = max($pdf->GetY(),$nexY); + } + + // Unit price before discount + if ($this->getColumnStatus('subprice')) + { + $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax); + $nexY = max($pdf->GetY(),$nexY); + } + + // 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); + } + + + // Unit + if ($this->getColumnStatus('unit')) + { + $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails, $hookmanager); + $this->printStdColumnContent($pdf, $curY, 'unit', $unit); + $nexY = max($pdf->GetY(),$nexY); + } + + // Discount on line + if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) + { + $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent); + $nexY = max($pdf->GetY(),$nexY); + } + + // Total HT line + if ($this->getColumnStatus('totalexcltax')) + { + $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax); + $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 + + + + // Collecte des totaux par valeur de tva dans $this->tva["taux"]=total_tva + if ($conf->multicurrency->enabled && $object->multicurrency_tx != 1) $tvaligne=$object->lines[$i]->multicurrency_total_tva; + else $tvaligne=$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 = $localtaxtmp_array[0]; + $localtax2_type = $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; + + if ($posYAfterImage > $posYAfterDescription) $nexY=$posYAfterImage; + + // Add line + if (! empty($conf->global->MAIN_PDF_DASH_BETWEEN_LINES) && $i < ($nblignes - 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+1, $this->page_largeur - $this->marge_droite, $nexY+1); + $pdf->SetLineStyle(array('dash'=>0)); + } + + $nexY+=2; // Passe espace entre les lignes + + // 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, 0, 1, $object->multicurrency_code); + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code); + } + $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, 0, 1, $object->multicurrency_code); + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforfooter, 0, $outputlangs, 1, 1, $object->multicurrency_code); + } + $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 - $heightforsignature - $heightforfooter, 0, $outputlangs, 0, 0, $object->multicurrency_code); + $bottomlasttab=$this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1; + } + else + { + $this->_tableau($pdf, $tab_top_newpage, $this->page_hauteur - $tab_top_newpage - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter, 0, $outputlangs, 1, 0, $object->multicurrency_code); + $bottomlasttab=$this->page_hauteur - $heightforinfotot - $heightforfreetext - $heightforsignature - $heightforfooter + 1; + } + + // Affiche zone infos + $posy=$this->_tableau_info($pdf, $object, $bottomlasttab, $outputlangs); + + // Affiche zone totaux + $posy=$this->_tableau_tot($pdf, $object, 0, $bottomlasttab, $outputlangs); + + // Affiche zone versements + /* + if ($deja_regle || $amount_credit_notes_included || $amount_deposits_included) + { + $posy=$this->_tableau_versements($pdf, $object, $posy, $outputlangs); + } + */ + + // Customer signature area + if (empty($conf->global->PROPAL_DISABLE_SIGNATURE)) + { + $posy=$this->_signature_area($pdf, $object, $posy, $outputlangs); + } + + // Pied de page + $this->_pagefoot($pdf,$object,$outputlangs); + if (method_exists($pdf,'AliasNbPages')) $pdf->AliasNbPages(); + + //If propal merge product PDF is active + if (!empty($conf->global->PRODUIT_PDF_MERGE_PROPAL)) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/propalmergepdfproduct.class.php'; + + $already_merged = array (); + foreach ( $object->lines as $line ) { + if (! empty($line->fk_product) && ! (in_array($line->fk_product, $already_merged))) { + // Find the desire PDF + $filetomerge = new Propalmergepdfproduct($this->db); + + if ($conf->global->MAIN_MULTILANGS) { + $filetomerge->fetch_by_product($line->fk_product, $outputlangs->defaultlang); + } else { + $filetomerge->fetch_by_product($line->fk_product); + } + + $already_merged[] = $line->fk_product; + + $product = new Product($this->db); + $product->fetch($line->fk_product); + + if ($product->entity!=$conf->entity) { + $entity_product_file=$product->entity; + } else { + $entity_product_file=$conf->entity; + } + + // If PDF is selected and file is not empty + if (count($filetomerge->lines) > 0) { + foreach ( $filetomerge->lines as $linefile ) { + if (! empty($linefile->id) && ! empty($linefile->file_name)) { + + + if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) + { + if (! empty($conf->product->enabled)) { + $filetomerge_dir = $conf->product->multidir_output[$entity_product_file] . '/' . get_exdir($product->id,2,0,0,$product,'product') . $product->id ."/photos"; + } elseif (! empty($conf->service->enabled)) { + $filetomerge_dir = $conf->service->multidir_output[$entity_product_file] . '/' . get_exdir($product->id,2,0,0,$product,'product') . $product->id ."/photos"; + } + } + else + { + if (! empty($conf->product->enabled)) { + $filetomerge_dir = $conf->product->multidir_output[$entity_product_file] . '/' . get_exdir(0,0,0,0,$product,'product') . dol_sanitizeFileName($product->ref); + } elseif (! empty($conf->service->enabled)) { + $filetomerge_dir = $conf->service->multidir_output[$entity_product_file] . '/' . get_exdir(0,0,0,0,$product,'product') . dol_sanitizeFileName($product->ref); + } + } + + dol_syslog(get_class($this) . ':: upload_dir=' . $filetomerge_dir, LOG_DEBUG); + + $infile = $filetomerge_dir . '/' . $linefile->file_name; + if (file_exists($infile) && is_readable($infile)) { + $pagecount = $pdf->setSourceFile($infile); + for($i = 1; $i <= $pagecount; $i ++) { + $tplIdx = $pdf->importPage($i); + if ($tplIdx!==false) { + $s = $pdf->getTemplatesize($tplIdx); + $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L'); + $pdf->useTemplate($tplIdx); + } else { + setEventMessages(null, array($infile.' cannot be added, probably protected PDF'),'warnings'); + } + } + } + } + } + } + } + } + } + + $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 (! empty($conf->global->MAIN_UMASK)) + @chmod($file, octdec($conf->global->MAIN_UMASK)); + + $this->result = array('fullpath'=>$file); + + return 1; // Pas d'erreur + } + else + { + $this->error=$langs->trans("ErrorCanNotCreateDir",$dir); + return 0; + } + } + else + { + $this->error=$langs->trans("ErrorConstantNotDefined","PROP_OUTPUTDIR"); + return 0; + } + } + + /** + * Show payments table + * + * @param TCPDF $pdf Object PDF + * @param Object $object Object proposal + * @param int $posy Position y in PDF + * @param Translate $outputlangs Object langs for output + * @return int <0 if KO, >0 if OK + */ + function _tableau_versements(&$pdf, $object, $posy, $outputlangs) + { + + } + + + /** + * Show miscellaneous information (payment mode, payment term, ...) + * + * @param TCPDF $pdf Object PDF + * @param Object $object Object to show + * @param int $posy Y + * @param Translate $outputlangs Langs object + * @return void + */ + function _tableau_info(&$pdf, $object, $posy, $outputlangs) + { + global $conf; + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $pdf->SetFont('','', $default_font_size - 1); + + // If France, show VAT mention if not applicable + if ($this->emetteur->country_code == 'FR' && $this->franchise == 1) + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("VATIsNotUsedForInvoice"), 0, 'L', 0); + + $posy=$pdf->GetY()+4; + } + + $posxval=52; + + // Show shipping date + if (! empty($object->date_livraison)) + { + $outputlangs->load("sendings"); + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("DateDeliveryPlanned").':'; + $pdf->MultiCell(80, 4, $titre, 0, 'L'); + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $dlp=dol_print_date($object->date_livraison,"daytext",false,$outputlangs,true); + $pdf->MultiCell(80, 4, $dlp, 0, 'L'); + + $posy=$pdf->GetY()+1; + } + elseif ($object->availability_code || $object->availability) // Show availability conditions + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("AvailabilityPeriod").':'; + $pdf->MultiCell(80, 4, $titre, 0, 'L'); + $pdf->SetTextColor(0,0,0); + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_availability=$outputlangs->transnoentities("AvailabilityType".$object->availability_code)!=('AvailabilityType'.$object->availability_code)?$outputlangs->transnoentities("AvailabilityType".$object->availability_code):$outputlangs->convToOutputCharset($object->availability); + $lib_availability=str_replace('\n',"\n",$lib_availability); + $pdf->MultiCell(80, 4, $lib_availability, 0, 'L'); + + $posy=$pdf->GetY()+1; + } + + // Show payments conditions + if (empty($conf->global->PROPALE_PDF_HIDE_PAYMENTTERMCOND) && ($object->cond_reglement_code || $object->cond_reglement)) + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentConditions").':'; + $pdf->MultiCell(43, 4, $titre, 0, 'L'); + + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_condition_paiement=$outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code)!=('PaymentCondition'.$object->cond_reglement_code)?$outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code):$outputlangs->convToOutputCharset($object->cond_reglement_doc); + $lib_condition_paiement=str_replace('\n',"\n",$lib_condition_paiement); + $pdf->MultiCell(67, 4, $lib_condition_paiement,0,'L'); + + $posy=$pdf->GetY()+3; + } + + if (empty($conf->global->PROPALE_PDF_HIDE_PAYMENTTERMMODE)) + { + // Check a payment mode is defined + /* Not required on a proposal + if (empty($object->mode_reglement_code) + && ! $conf->global->FACTURE_CHQ_NUMBER + && ! $conf->global->FACTURE_RIB_NUMBER) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetTextColor(200,0,0); + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->MultiCell(90, 3, $outputlangs->transnoentities("ErrorNoPaiementModeConfigured"),0,'L',0); + $pdf->SetTextColor(0,0,0); + + $posy=$pdf->GetY()+1; + } + */ + + // Show payment mode + if ($object->mode_reglement_code + && $object->mode_reglement_code != 'CHQ' + && $object->mode_reglement_code != 'VIR') + { + $pdf->SetFont('','B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentMode").':'; + $pdf->MultiCell(80, 5, $titre, 0, 'L'); + $pdf->SetFont('','', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_mode_reg=$outputlangs->transnoentities("PaymentType".$object->mode_reglement_code)!=('PaymentType'.$object->mode_reglement_code)?$outputlangs->transnoentities("PaymentType".$object->mode_reglement_code):$outputlangs->convToOutputCharset($object->mode_reglement); + $pdf->MultiCell(80, 5, $lib_mode_reg,0,'L'); + + $posy=$pdf->GetY()+2; + } + + // Show payment mode CHQ + if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') + { + // Si mode reglement non force ou si force a CHQ + if (! empty($conf->global->FACTURE_CHQ_NUMBER)) + { + $diffsizetitle=(empty($conf->global->PDF_DIFFSIZE_TITLE)?3:$conf->global->PDF_DIFFSIZE_TITLE); + + if ($conf->global->FACTURE_CHQ_NUMBER > 0) + { + $account = new Account($this->db); + $account->fetch($conf->global->FACTURE_CHQ_NUMBER); + + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','B', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo',$account->proprio),0,'L',0); + $posy=$pdf->GetY()+1; + + if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0); + $posy=$pdf->GetY()+2; + } + } + if ($conf->global->FACTURE_CHQ_NUMBER == -1) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','B', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo',$this->emetteur->name),0,'L',0); + $posy=$pdf->GetY()+1; + + if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) + { + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetFont('','', $default_font_size - $diffsizetitle); + $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0); + $posy=$pdf->GetY()+2; + } + } + } + } + + // If payment mode not forced or forced to VIR, show payment with BAN + if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') + { + if (! empty($object->fk_account) || ! empty($object->fk_bank) || ! empty($conf->global->FACTURE_RIB_NUMBER)) + { + $bankid=(empty($object->fk_account)?$conf->global->FACTURE_RIB_NUMBER:$object->fk_account); + if (! empty($object->fk_bank)) $bankid=$object->fk_bank; // For backward compatibility when object->fk_account is forced with object->fk_bank + $account = new Account($this->db); + $account->fetch($bankid); + + $curx=$this->marge_gauche; + $cury=$posy; + + $posy=pdf_bank($pdf,$outputlangs,$curx,$cury,$account,0,$default_font_size); + + $posy+=2; + } + } + } + + return $posy; + } + + + /** + * Show total to pay + * + * @param PDF $pdf Object PDF + * @param Facture $object Object invoice + * @param int $deja_regle Montant deja regle + * @param int $posy Position depart + * @param Translate $outputlangs Objet langs + * @return int Position pour suite + */ + function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs) + { + global $conf,$mysoc; + $default_font_size = pdf_getPDFFontSize($outputlangs); + + $tab2_top = $posy; + $tab2_hl = 4; + $pdf->SetFont('','', $default_font_size - 1); + + // Tableau total + $col1x = 120; $col2x = 170; + if ($this->page_largeur < 210) // To work with US executive format + { + $col2x-=20; + } + $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x); + + $useborder=0; + $index = 0; + + // Total HT + $pdf->SetFillColor(255,255,255); + $pdf->SetXY($col1x, $tab2_top + 0); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("TotalHT"), 0, 'L', 1); + + $total_ht = ($conf->multicurrency->enabled && $object->mylticurrency_tx != 1 ? $object->multicurrency_total_ht : $object->total_ht); + $pdf->SetXY($col2x, $tab2_top + 0); + $pdf->MultiCell($largcol2, $tab2_hl, price($total_ht + (! empty($object->remise)?$object->remise:0), 0, $outputlangs), 0, 'R', 1); + + // Show VAT by rates and total + $pdf->SetFillColor(248,248,248); + + $total_ttc = ($conf->multicurrency->enabled && $object->multicurrency_tx != 1) ? $object->multicurrency_total_ttc : $object->total_ttc; + + $this->atleastoneratenotnull=0; + if (empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT)) + { + $tvaisnull=((! empty($this->tva) && count($this->tva) == 1 && isset($this->tva['0.000']) && is_float($this->tva['0.000'])) ? true : false); + if (! empty($conf->global->MAIN_GENERATE_DOCUMENTS_WITHOUT_VAT_IFNULL) && $tvaisnull) + { + // Nothing to do + } + else + { + //Local tax 1 before VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on') + //{ + foreach( $this->localtax1 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('1','3','5'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey!=0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT1",$mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + //Local tax 2 before VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on') + //{ + foreach( $this->localtax2 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('1','3','5'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey!=0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT2", $mysoc->country_code).' '; + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + + } + } + } + //} + // VAT + foreach($this->tva as $tvakey => $tvaval) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + $this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat =$outputlangs->transcountrynoentities("TotalVAT",$mysoc->country_code).' '; + $totalvat.=vatrate($tvakey,1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + + //Local tax 1 after VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX1_OPTION) && $conf->global->FACTURE_LOCAL_TAX1_OPTION=='localtax1on') + //{ + foreach( $this->localtax1 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('2','4','6'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + if ($tvakey != 0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT1",$mysoc->country_code).' '; + + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + //Local tax 2 after VAT + //if (! empty($conf->global->FACTURE_LOCAL_TAX2_OPTION) && $conf->global->FACTURE_LOCAL_TAX2_OPTION=='localtax2on') + //{ + foreach( $this->localtax2 as $localtax_type => $localtax_rate ) + { + if (in_array((string) $localtax_type, array('2','4','6'))) continue; + + foreach( $localtax_rate as $tvakey => $tvaval ) + { + // retrieve global local tax + if ($tvakey != 0) // On affiche pas taux 0 + { + //$this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + + $tvacompl=''; + if (preg_match('/\*/',$tvakey)) + { + $tvakey=str_replace('*','',$tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalLT2",$mysoc->country_code).' '; + + $totalvat.=vatrate(abs($tvakey),1).$tvacompl; + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $totalvat, 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($tvaval, 0, $outputlangs), 0, 'R', 1); + } + } + } + //} + + // Total TTC + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->SetTextColor(0,0,60); + $pdf->SetFillColor(224,224,224); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("TotalTTC"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($total_ttc, 0, $outputlangs), $useborder, 'R', 1); + } + } + + $pdf->SetTextColor(0,0,0); + + /* + $resteapayer = $object->total_ttc - $deja_regle; + if (! empty($object->paye)) $resteapayer=0; + */ + + if ($deja_regle > 0) + { + $index++; + + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("AlreadyPaid"), 0, 'L', 0); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($deja_regle, 0, $outputlangs), 0, 'R', 0); + + /* + if ($object->close_code == 'discount_vat') + { + $index++; + $pdf->SetFillColor(255,255,255); + + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle, 0, $outputlangs), $useborder, 'R', 1); + + $resteapayer=0; + } + */ + + $index++; + $pdf->SetTextColor(0,0,60); + $pdf->SetFillColor(224,224,224); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x-$col1x, $tab2_hl, $outputlangs->transnoentities("RemainderToPay"), $useborder, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($resteapayer, 0, $outputlangs), $useborder, 'R', 1); + + $pdf->SetFont('','', $default_font_size - 1); + $pdf->SetTextColor(0,0,0); + } + + $index++; + return ($tab2_top + ($tab2_hl * $index)); + } + + /** + * Show table for lines + * + * @param PDF $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 + * @return void + */ + function _tableau(&$pdf, $tab_top, $tab_height, $nexY, $outputlangs, $hidetop=0, $hidebottom=0, $currency='') + { + 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)); + $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, 5, '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 prend une longueur en 3eme param et 4eme param + + + foreach ($this->cols as $colKey => $colDef) + { + if(!$this->getColumnStatus($colKey)) continue; + + // get title label + $colDef['title']['label'] = !empty($colDef['title']['label'])?$colDef['title']['label']:$outputlangs->transnoentities($colDef['title']['textkey']); + + // Add column separator + if(!empty($colDef['border-left'])){ + $pdf->line($colDef['xStartPos'], $tab_top, $colDef['xStartPos'], $tab_top + $tab_height); + } + + if (empty($hidetop)) + { + $pdf->SetXY($colDef['xStartPos'] + $colDef['title']['padding'][3], $tab_top + $colDef['title']['padding'][0] ); + + $textWidth = $colDef['width'] - $colDef['title']['padding'][3] -$colDef['title']['padding'][1]; + $pdf->MultiCell($textWidth,2,$colDef['title']['label'],'',$colDef['title']['align']); + } + } + + if (empty($hidetop)){ + $pdf->line($this->marge_gauche, $tab_top+5, $this->page_largeur-$this->marge_droite, $tab_top+5); // line prend une position y en 2eme param et 4eme param + } + + + } + + /** + * Show top header of page. + * + * @param PDF $pdf Object PDF + * @param Object $object Object to show + * @param int $showaddress 0=no, 1=yes + * @param Translate $outputlangs Object lang for output + * @return void + */ + function _pagehead(&$pdf, $object, $showaddress, $outputlangs) + { + global $conf,$langs; + + $outputlangs->load("main"); + $outputlangs->load("bills"); + $outputlangs->load("propal"); + $outputlangs->load("companies"); + + $default_font_size = pdf_getPDFFontSize($outputlangs); + + pdf_pagehead($pdf,$outputlangs,$this->page_hauteur); + + // Show Draft Watermark + if($object->statut==0 && (! empty($conf->global->PROPALE_DRAFT_WATERMARK)) ) + { + pdf_watermark($pdf,$outputlangs,$this->page_hauteur,$this->page_largeur,'mm',$conf->global->PROPALE_DRAFT_WATERMARK); + } + + $pdf->SetTextColor(0,0,60); + $pdf->SetFont('','B', $default_font_size + 3); + + $posy=$this->marge_haute; + $posx=$this->page_largeur-$this->marge_droite-100; + + $pdf->SetXY($this->marge_gauche,$posy); + + // Logo + $logo=$conf->mycompany->dir_output.'/logos/'.$this->emetteur->logo; + if ($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(100, 3, $outputlangs->transnoentities("ErrorLogoFileNotFound",$logo), 0, 'L'); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("ErrorGoToGlobalSetup"), 0, 'L'); + } + } + else + { + $text=$this->emetteur->name; + $pdf->MultiCell(100, 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("PdfCommercialProposalTitle"); + $pdf->MultiCell(100, 4, $title, '', 'R'); + + $pdf->SetFont('','B',$default_font_size); + + $posy+=5; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 4, $outputlangs->transnoentities("Ref")." : " . $outputlangs->convToOutputCharset($object->ref), '', '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(100, 3, $outputlangs->transnoentities("RefCustomer")." : " . $outputlangs->convToOutputCharset($object->ref_client), '', 'R'); + } + + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("Date")." : " . dol_print_date($object->date,"day",false,$outputlangs,true), '', 'R'); + + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 3, $outputlangs->transnoentities("DateEndPropal")." : " . dol_print_date($object->fin_validite,"day",false,$outputlangs,true), '', 'R'); + + if ($object->thirdparty->code_client) + { + $posy+=4; + $pdf->SetXY($posx,$posy); + $pdf->SetTextColor(0,0,60); + $pdf->MultiCell(100, 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(100, 3, $langs->trans("SalesRepresentative")." : ".$usertmp->getFullName($langs), '', 'R'); + } + } + + $posy+=2; + + $top_shift = 0; + // Show list of linked objects + $current_y = $pdf->getY(); + $posy = pdf_writeLinkedObjects($pdf, $object, $outputlangs, $posx, $posy, 100, 3, 'R', $default_font_size); + if ($current_y < $pdf->getY()) + { + $top_shift = $pdf->getY() - $current_y; + } + + if ($showaddress) + { + // Sender properties + $carac_emetteur=''; + // Add internal contact of proposal if defined + $arrayidcontact=$object->getIdContact('internal','SALESREPFOLL'); + if (count($arrayidcontact) > 0) + { + $object->fetch_user($arrayidcontact[0]); + $labelbeforecontactname=($outputlangs->transnoentities("FromContactName")!='FromContactName'?$outputlangs->transnoentities("FromContactName"):$outputlangs->transnoentities("Name")); + $carac_emetteur .= ($carac_emetteur ? "\n" : '' ).$labelbeforecontactname." ".$outputlangs->convToOutputCharset($object->user->getFullName($outputlangs))."\n"; + } + + $carac_emetteur .= pdf_build_address($outputlangs, $this->emetteur, $object->thirdparty, '', 0, 'source', $object); + + // Show sender + $posy=42+$top_shift; + $posx=$this->marge_gauche; + if (! empty($conf->global->MAIN_INVERT_SENDER_RECIPIENT)) $posx=$this->page_largeur-$this->marge_droite-80; + $hautcadre=40; + + // 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(82, $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(80, 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(80, 4, $carac_emetteur, 0, 'L'); + + + // If CUSTOMER contact defined, we use it + $usecontact=false; + $arrayidcontact=$object->getIdContact('external','CUSTOMER'); + if (count($arrayidcontact) > 0) + { + $usecontact=true; + $result=$object->fetch_contact($arrayidcontact[0]); + } + + //Recipient name + // On peut utiliser le nom de la societe du contact + if ($usecontact && !empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)) { + $thirdparty = $object->contact; + } else { + $thirdparty = $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=100; + if ($this->page_largeur < 210) $widthrecbox=84; // To work with US executive format + $posy=42+$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, 4, $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; + } + + /** + * Show footer of page. Need this->emetteur object + * + * @param PDF $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 + */ + function _pagefoot(&$pdf,$object,$outputlangs,$hidefreetext=0) + { + global $conf; + $showdetails=$conf->global->MAIN_GENERATE_DOCUMENTS_SHOW_FOOT_DETAILS; + return pdf_pagefoot($pdf,$outputlangs,'PROPOSAL_FREE_TEXT',$this->emetteur,$this->marge_basse,$this->marge_gauche,$this->page_hauteur,$object,$showdetails,$hidefreetext); + } + + /** + * Show area for the customer to sign + * + * @param PDF $pdf Object PDF + * @param Facture $object Object invoice + * @param int $posy Position depart + * @param Translate $outputlangs Objet langs + * @return int Position pour suite + */ + function _signature_area(&$pdf, $object, $posy, $outputlangs) + { + global $conf; + $default_font_size = pdf_getPDFFontSize($outputlangs); + $tab_top = $posy + 4; + $tab_hl = 4; + + $posx = 120; + $largcol = ($this->page_largeur - $this->marge_droite - $posx); + $useborder=0; + $index = 0; + // Total HT + $pdf->SetFillColor(255,255,255); + $pdf->SetXY($posx, $tab_top + 0); + $pdf->SetFont('','', $default_font_size - 2); + $pdf->MultiCell($largcol, $tab_hl, $outputlangs->transnoentities("ProposalCustomerSignature"), 0, 'L', 1); + + $pdf->SetXY($posx, $tab_top + $tab_hl); + $pdf->MultiCell($largcol, $tab_hl*3, '', 1, 'R'); + if (! empty($conf->global->MAIN_PDF_PROPAL_USE_ELECTRONIC_SIGNING)) { + $pdf->addEmptySignatureAppearance($posx, $tab_top + $tab_hl, $largcol, $tab_hl*3); + } + + return ($tab_hl*7); + } + + + /** + * Define Array Column Field + * + * @param object $object common object + * @param outputlangs $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 + */ + 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(0.5,0.5,0.5,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', + ), + ); + + $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_PROPOSALS_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' => false, // 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($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 + 10; + $this->cols['totalexcltax'] = array( + 'rank' => $rank, + 'width' => 26, // in mm + 'status' => true, + 'title' => array( + 'textkey' => 'TotalHT' + ), + 'border-left' => true, // add left line separator + ); + + + $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/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index d06b1512cc6..3b60256c3ef 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -486,7 +486,8 @@ RevenueStamp=Revenue stamp YouMustCreateInvoiceFromThird=This option is only available when creating invoice from tab "customer" of third party YouMustCreateInvoiceFromSupplierThird=This option is only available when creating invoice from tab "supplier" of third party YouMustCreateStandardInvoiceFirstDesc=You have to create a standard invoice first and convert it to "template" to create a new template invoice -PDFCrabeDescription=Invoice PDF template Crabe. A complete invoice template (recommended Template) +PDFCrabeDescription=Invoice PDF template Crabe. A complete invoice template (deprecated Template) +PDFSpongeDescription=Invoice PDF template Sponge. A complete invoice template (recommended Template) PDFCrevetteDescription=Invoice PDF template Crevette. A complete invoice template for situation invoices TerreNumRefModelDesc1=Return number with format %syymm-nnnn for standard invoices and %syymm-nnnn for credit notes where yy is year, mm is month and nnnn is a sequence with no break and no return to 0 MarsNumRefModelDesc1=Return number with format %syymm-nnnn for standard invoices, %syymm-nnnn for replacement invoices, %syymm-nnnn for down payment invoices and %syymm-nnnn for credit notes where yy is year, mm is month and nnnn is a sequence with no break and no return to 0 diff --git a/htdocs/langs/en_US/orders.lang b/htdocs/langs/en_US/orders.lang index 06191d9f7b7..30fb0aa1320 100644 --- a/htdocs/langs/en_US/orders.lang +++ b/htdocs/langs/en_US/orders.lang @@ -140,7 +140,8 @@ OrderByEMail=EMail OrderByWWW=Online OrderByPhone=Phone # Documents models -PDFEinsteinDescription=A complete order model (logo...) +PDFEinsteinDescription=A complete order model (logo...)(deprecated Template) +PDFEratostheneDescription=A complete order model (logo...)(recommended Template) PDFEdisonDescription=A simple order model PDFProformaDescription=A complete proforma invoice (logo…) CreateInvoiceForThisCustomer=Bill orders diff --git a/htdocs/langs/en_US/propal.lang b/htdocs/langs/en_US/propal.lang index 5a7169ac925..55fb6372d74 100644 --- a/htdocs/langs/en_US/propal.lang +++ b/htdocs/langs/en_US/propal.lang @@ -77,7 +77,8 @@ TypeContact_propal_external_BILLING=Customer invoice contact TypeContact_propal_external_CUSTOMER=Customer contact following-up proposal TypeContact_propal_external_SHIPPING=Customer contact for delivery # Document models -DocModelAzurDescription=A complete proposal model (logo...) +DocModelAzurDescription=A complete proposal model (logo...)(deprecated Template) +DocModelCyanDescription=A complete proposal model (logo...)(recommended Template) DefaultModelPropalCreate=Default model creation DefaultModelPropalToBill=Default template when closing a business proposal (to be invoiced) DefaultModelPropalClosed=Default template when closing a business proposal (unbilled) From 477acb2e4c6267971ca11c7a36889b6c5adf0204 Mon Sep 17 00:00:00 2001 From: John BOTELLA Date: Fri, 14 Sep 2018 09:48:15 +0200 Subject: [PATCH 2/4] change new PDF version --- htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php | 2 +- htdocs/core/modules/propale/doc/pdf_azur.modules.php | 2 +- htdocs/core/modules/propale/doc/pdf_cyan.modules.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php b/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php index 58129bea6e2..039f29fa379 100644 --- a/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php +++ b/htdocs/core/modules/commande/doc/pdf_eratosthene.modules.php @@ -76,7 +76,7 @@ class pdf_eratosthene extends ModelePDFCommandes * Dolibarr version of the loaded document * @public string */ - public $version = 'dolibarr'; + public $version = 'development'; public $page_largeur; public $page_hauteur; diff --git a/htdocs/core/modules/propale/doc/pdf_azur.modules.php b/htdocs/core/modules/propale/doc/pdf_azur.modules.php index 6e72e757fb6..e25abd5bc9e 100644 --- a/htdocs/core/modules/propale/doc/pdf_azur.modules.php +++ b/htdocs/core/modules/propale/doc/pdf_azur.modules.php @@ -47,7 +47,7 @@ class pdf_azur extends ModelePDFPropales var $type; var $phpmin = array(4,3,0); // Minimum version of PHP required by module - var $version = 'dolibarr'; + var $version = 'development'; var $page_largeur; var $page_hauteur; diff --git a/htdocs/core/modules/propale/doc/pdf_cyan.modules.php b/htdocs/core/modules/propale/doc/pdf_cyan.modules.php index 383f34acebd..c9bce8b07a4 100644 --- a/htdocs/core/modules/propale/doc/pdf_cyan.modules.php +++ b/htdocs/core/modules/propale/doc/pdf_cyan.modules.php @@ -47,7 +47,7 @@ class pdf_cyan extends ModelePDFPropales var $type; var $phpmin = array(4,3,0); // Minimum version of PHP required by module - var $version = 'dolibarr'; + var $version = 'development'; var $page_largeur; var $page_hauteur; From d1321e5a7b21ad5fa83ed03b494384d72a7487e5 Mon Sep 17 00:00:00 2001 From: John BOTELLA Date: Fri, 14 Sep 2018 09:54:39 +0200 Subject: [PATCH 3/4] change new PDF version --- htdocs/core/modules/facture/doc/pdf_sponge.modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php index 72c2a5aaca6..74f4bf34acf 100644 --- a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php @@ -77,7 +77,7 @@ class pdf_sponge extends ModelePDFFactures * Dolibarr version of the loaded document * @public string */ - public $version = 'dolibarr'; + public $version = 'development'; public $page_largeur; public $page_hauteur; From 3fb2d4c159bb3dd62b143ca3ca9584bb25cb2849 Mon Sep 17 00:00:00 2001 From: John BOTELLA Date: Tue, 18 Sep 2018 15:52:57 +0200 Subject: [PATCH 4/4] remove change on text --- htdocs/langs/en_US/bills.lang | 4 ++-- htdocs/langs/en_US/orders.lang | 4 ++-- htdocs/langs/en_US/propal.lang | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang index ab2adf33441..95114c96446 100644 --- a/htdocs/langs/en_US/bills.lang +++ b/htdocs/langs/en_US/bills.lang @@ -488,8 +488,8 @@ RevenueStamp=Revenue stamp YouMustCreateInvoiceFromThird=This option is only available when creating invoice from tab "customer" of third party YouMustCreateInvoiceFromSupplierThird=This option is only available when creating invoice from tab "supplier" of third party YouMustCreateStandardInvoiceFirstDesc=You have to create a standard invoice first and convert it to "template" to create a new template invoice -PDFCrabeDescription=Invoice PDF template Crabe. A complete invoice template (deprecated Template) -PDFSpongeDescription=Invoice PDF template Sponge. A complete invoice template (recommended Template) +PDFCrabeDescription=Invoice PDF template Crabe. A complete invoice template (recommended Template) +PDFSpongeDescription=Invoice PDF template Sponge. A complete invoice template PDFCrevetteDescription=Invoice PDF template Crevette. A complete invoice template for situation invoices TerreNumRefModelDesc1=Return number with format %syymm-nnnn for standard invoices and %syymm-nnnn for credit notes where yy is year, mm is month and nnnn is a sequence with no break and no return to 0 MarsNumRefModelDesc1=Return number with format %syymm-nnnn for standard invoices, %syymm-nnnn for replacement invoices, %syymm-nnnn for down payment invoices and %syymm-nnnn for credit notes where yy is year, mm is month and nnnn is a sequence with no break and no return to 0 diff --git a/htdocs/langs/en_US/orders.lang b/htdocs/langs/en_US/orders.lang index 1d286cc5ebd..77d2eda19a3 100644 --- a/htdocs/langs/en_US/orders.lang +++ b/htdocs/langs/en_US/orders.lang @@ -140,8 +140,8 @@ OrderByEMail=EMail OrderByWWW=Online OrderByPhone=Phone # Documents models -PDFEinsteinDescription=A complete order model (logo...)(deprecated Template) -PDFEratostheneDescription=A complete order model (logo...)(recommended Template) +PDFEinsteinDescription=A complete order model (logo...) +PDFEratostheneDescription=A complete order model (logo...) PDFEdisonDescription=A simple order model PDFProformaDescription=A complete proforma invoice (logo…) CreateInvoiceForThisCustomer=Bill orders diff --git a/htdocs/langs/en_US/propal.lang b/htdocs/langs/en_US/propal.lang index 44157780ce7..f336daa242d 100644 --- a/htdocs/langs/en_US/propal.lang +++ b/htdocs/langs/en_US/propal.lang @@ -77,8 +77,8 @@ TypeContact_propal_external_BILLING=Customer invoice contact TypeContact_propal_external_CUSTOMER=Customer contact following-up proposal TypeContact_propal_external_SHIPPING=Customer contact for delivery # Document models -DocModelAzurDescription=A complete proposal model (logo...)(deprecated Template) -DocModelCyanDescription=A complete proposal model (logo...)(recommended Template) +DocModelAzurDescription=A complete proposal model (logo...) +DocModelCyanDescription=A complete proposal model (logo...) DefaultModelPropalCreate=Default model creation DefaultModelPropalToBill=Default template when closing a business proposal (to be invoiced) DefaultModelPropalClosed=Default template when closing a business proposal (unbilled)