From 1f2bb2969a1de0b7690f0d1be247486db3a9fe7f Mon Sep 17 00:00:00 2001 From: sonikf <93765174+sonikf@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:41:00 +0200 Subject: [PATCH] NEW: PDF Invoice show vat analysis per rate (#31381) * Show vat analysis * Show vat analysis * Show vat analysis * fix white space * Update main.lang * Show vat analysis * Show vat analysis * add copyright * fix pre-commit * fix pre-commit * fix pre-commit * fix php-stan (8.2) * fix php-stan (8.2) * fix php-stan (8.2) * fix pre-commit tab * Update pdf_other.php --------- Co-authored-by: Laurent Destailleur --- htdocs/admin/pdf_other.php | 39 +++-- .../modules/facture/doc/pdf_crabe.modules.php | 133 +++++++++++--- .../facture/doc/pdf_octopus.modules.php | 158 ++++++++++++----- .../facture/doc/pdf_sponge.modules.php | 163 +++++++++++++----- htdocs/langs/en_US/admin.lang | 1 + htdocs/langs/en_US/main.lang | 2 + 6 files changed, 375 insertions(+), 121 deletions(-) diff --git a/htdocs/admin/pdf_other.php b/htdocs/admin/pdf_other.php index dd552151738af..8b9badab823c6 100644 --- a/htdocs/admin/pdf_other.php +++ b/htdocs/admin/pdf_other.php @@ -6,7 +6,7 @@ * Copyright (C) 2019 Ferran Marcet * Copyright (C) 2021-2024 Anthony Berton * Copyright (C) 2022 Alexandre Spangaro - * Copyright (C) 2024 Frédéric France + * Copyright (C) 2024 Frédéric France * Copyright (C) 2024 Nick Fragoulis * * This program is free software; you can redistribute it and/or modify @@ -128,6 +128,20 @@ dolibarr_set_const($db, "INVOICE_SHOW_SHIPPING_ADDRESS", GETPOSTINT("INVOICE_SHOW_SHIPPING_ADDRESS"), 'chaine', 0, '', $conf->entity); dolibarr_del_const($db, "INVOICE_SHOW_SHIPPING_ADDRESS", $conf->entity); } + if (GETPOSTISSET('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + dolibarr_set_const($db, "PDF_INVOICE_SHOW_VAT_ANALYSIS", GETPOSTINT("PDF_INVOICE_SHOW_VAT_ANALYSIS"), 'chaine', 0, '', $conf->entity); + dolibarr_del_const($db, "PDF_INVOICE_SHOW_VAT_ANALYSIS", $conf->entity); + } + if (GETPOSTISSET('BARCODE_ON_SHIPPING_PDF')) { + dolibarr_set_const($db, "BARCODE_ON_SHIPPING_PDF", GETPOSTINT("BARCODE_ON_SHIPPING_PDF"), 'chaine', 0, '', $conf->entity); + } + if (GETPOSTISSET('BARCODE_ON_RECEPTION_PDF')) { + dolibarr_set_const($db, "BARCODE_ON_RECEPTION_PDF", GETPOSTINT("BARCODE_ON_RECEPTION_PDF"), 'chaine', 0, '', $conf->entity); + } + if (GETPOSTISSET('BARCODE_ON_STOCKTRANSFER_PDF')) { + dolibarr_set_const($db, "BARCODE_ON_STOCKTRANSFER_PDF", GETPOSTINT("BARCODE_ON_STOCKTRANSFER_PDF"), 'chaine', 0, '', $conf->entity); + } + // Terms of sale if ($_FILES['termsofsale']["name"]) { if (!preg_match('/(\.pdf)$/i', $_FILES['termsofsale']["name"])) { // Document can be used on a lot of different places. Only pdf can be supported. @@ -143,16 +157,6 @@ } } - if (GETPOSTISSET('BARCODE_ON_SHIPPING_PDF')) { - dolibarr_set_const($db, "BARCODE_ON_SHIPPING_PDF", GETPOSTINT("BARCODE_ON_SHIPPING_PDF"), 'chaine', 0, '', $conf->entity); - } - if (GETPOSTISSET('BARCODE_ON_RECEPTION_PDF')) { - dolibarr_set_const($db, "BARCODE_ON_RECEPTION_PDF", GETPOSTINT("BARCODE_ON_RECEPTION_PDF"), 'chaine', 0, '', $conf->entity); - } - if (GETPOSTISSET('BARCODE_ON_STOCKTRANSFER_PDF')) { - dolibarr_set_const($db, "BARCODE_ON_STOCKTRANSFER_PDF", GETPOSTINT("BARCODE_ON_STOCKTRANSFER_PDF"), 'chaine', 0, '', $conf->entity); - } - setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); header("Location: ".$_SERVER["PHP_SELF"]."?mainmenu=home&leftmenu=setup"); @@ -437,6 +441,19 @@ } print ''; + /* too late to have it enabled by default in v21 + print ''; + print $form->textwithpicto($langs->trans("PDF_INVOICE_SHOW_VAT_ANALYSIS"), ''); + print ''; + if ($conf->use_javascript_ajax) { + print ajax_constantonoff('PDF_INVOICE_SHOW_VAT_ANALYSIS'); + } else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("PDF_INVOICE_SHOW_VAT_ANALYSIS", $arrval, $conf->global->PDF_INVOICE_SHOW_VAT_ANALYSIS); + } + print ''; + */ + /* Keep this option hidden for the moment to avoid options inflation. We'll see later if it is used enough... print ''; print $form->textwithpicto($langs->trans("SUPPLIER_PROPOSAL_ADD_BILLING_CONTACT"), $langs->trans("SUPPLIER_PROPOSAL_ADD_BILLING_CONTACTMore")); diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php index 255aa356a7327..6594616134bc1 100644 --- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php @@ -796,10 +796,17 @@ public function write_file($object, $outputlangs, $srctemplatepath = '', $hidede } $this->tva[$vatrate] += $tvaligne; // ->tva is abandoned, we use now ->tva_array that is more complete $vatcode = $object->lines[$i]->vat_src_code; - if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { - $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; + if (getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['tot_ht'])) { + $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['tot_ht'] = 0; + } + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate'=>$vatrate, 'vatcode'=>$vatcode, 'amount'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne, 'tot_ht'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['tot_ht'] + $object->lines[$i]->total_ht); + } else { + if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; + } + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne); } - $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne); if ($posYAfterImage > $posYAfterDescription) { $nexY = $posYAfterImage; @@ -1161,6 +1168,65 @@ protected function _tableau_info(&$pdf, $object, $posy, $outputlangs, $outputlan $pdf->SetFont('', '', $default_font_size - 1); + krsort($this->tva_array); + + // Show VAT details + if ($object->total_tva != 0 && getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + $pdf->SetFillColor(224, 224, 224); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("VAT"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 25, $posy); + $titre = $outputlangs->transnoentities("NetTotal"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 50, $posy); + $titre = $outputlangs->transnoentities("VATAmount"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 75, $posy); + $titre = $outputlangs->transnoentities("AmountTotal"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $posy = $pdf->GetY(); + $tot_ht = 0; + $tot_tva = 0; + $tot_ttc = 0; + + foreach ($this->tva_array as $tvakey => $tvaval) { + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = round((float) $tvakey, 2) . "%"; + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 25, $posy); + $titre = price($tvaval['tot_ht']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_ht += $tvaval['tot_ht']; + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 50, $posy); + $titre = price($tvaval['amount']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_tva += $tvaval['amount']; + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 75, $posy); + $titre = price($tvaval['tot_ht'] + $tvaval['amount']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_ttc += ($tvaval['tot_ht'] + $tvaval['amount']); + + $posy = $pdf->GetY(); + } + } + // If France, show VAT mention if not applicable if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) { $pdf->SetFont('', 'B', $default_font_size - 2); @@ -1456,6 +1522,16 @@ protected function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); $pdf->MultiCell($largcol2, $tab2_hl, price($sign * ($total_ht + (!empty($object->remise) ? $object->remise : 0)), 0, $outputlangs), 0, 'R', 1); + if (getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + $index++; + $pdf->SetFillColor(255, 255, 255); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalVAT"), 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->total_tva), 0, 'R', 1); + } + // Show VAT by rates and total $pdf->SetFillColor(248, 248, 248); @@ -1542,34 +1618,35 @@ protected function _tableau_tot(&$pdf, $object, $deja_regle, $posy, $outputlangs } } - // VAT - foreach ($this->tva_array as $tvakey => $tvaval) { - if ($tvakey != 0) { // On affiche pas taux 0 - $this->atleastoneratenotnull++; + if (!getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + // VAT + foreach ($this->tva_array as $tvakey => $tvaval) { + if ($tvakey != 0) { // On affiche pas taux 0 + $this->atleastoneratenotnull++; - $index++; - $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $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).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : ''); - $totalvat .= ' '; - if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') { - $totalvat .= vatrate($tvaval['vatrate'], true).$tvacompl; - } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') { - $totalvat .= $tvaval['vatcode'].$tvacompl; - } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'nocodenorate') { - $totalvat .= $tvacompl; - } else { - $totalvat .= vatrate($tvaval['vatrate'], true).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$tvacompl; + $tvacompl = ''; + if (preg_match('/\*/', $tvakey)) { + $tvakey = str_replace('*', '', $tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : ''); + $totalvat .= ' '; + if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') { + $totalvat .= vatrate($tvaval['vatrate'], true).$tvacompl; + } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') { + $totalvat .= $tvaval['vatcode'].$tvacompl; + } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'nocodenorate') { + $totalvat .= $tvacompl; + } else { + $totalvat .= vatrate($tvaval['vatrate'], true).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$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(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1); } - $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1); - - $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); - $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1); } } diff --git a/htdocs/core/modules/facture/doc/pdf_octopus.modules.php b/htdocs/core/modules/facture/doc/pdf_octopus.modules.php index 9ec9d52a63e1c..8559501ab0141 100644 --- a/htdocs/core/modules/facture/doc/pdf_octopus.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_octopus.modules.php @@ -1042,10 +1042,17 @@ public function write_file($object, $outputlangs, $srctemplatepath = '', $hidede } $this->tva[$vatrate] += $tvaligne; // ->tva is abandoned, we use now ->tva_array that is more complete $vatcode = $object->lines[$i]->vat_src_code; - if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { - $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; + if (getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['tot_ht'])) { + $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['tot_ht'] = 0; + } + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate'=>$vatrate, 'vatcode'=>$vatcode, 'amount'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne, 'tot_ht'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['tot_ht'] + $object->lines[$i]->total_ht); + } else { + if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; + } + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne); } - $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne); $nexY = max($nexY, $posYAfterImage); @@ -1328,6 +1335,65 @@ protected function drawInfoTable(&$pdf, $object, $posy, $outputlangs, $outputlan $pdf->SetFont('', '', $default_font_size - 1); + krsort($this->tva_array); + + // Show VAT details + if ($object->total_tva != 0 && getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + $pdf->SetFillColor(224, 224, 224); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("VAT"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 25, $posy); + $titre = $outputlangs->transnoentities("NetTotal"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 50, $posy); + $titre = $outputlangs->transnoentities("VATAmount"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 75, $posy); + $titre = $outputlangs->transnoentities("AmountTotal"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $posy = $pdf->GetY(); + $tot_ht = 0; + $tot_tva = 0; + $tot_ttc = 0; + + foreach ($this->tva_array as $tvakey => $tvaval) { + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = round((float) $tvakey, 2) . "%"; + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 25, $posy); + $titre = price($tvaval['tot_ht']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_ht += $tvaval['tot_ht']; + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 50, $posy); + $titre = price($tvaval['amount']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_tva += $tvaval['amount']; + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 75, $posy); + $titre = price($tvaval['tot_ht'] + $tvaval['amount']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_ttc += ($tvaval['tot_ht'] + $tvaval['amount']); + + $posy = $pdf->GetY(); + } + } + // If France, show VAT mention if not applicable if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) { $pdf->SetFont('', 'B', $default_font_size - 2); @@ -1617,6 +1683,16 @@ protected function drawTotalTable(&$pdf, $object, $deja_regle, $posy, $outputlan $pdf->MultiCell($largcol2, $tab2_hl, price($total_ht - $remise, 0, $outputlangs), 0, 'R', 1); } + if (getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + $index++; + $pdf->SetFillColor(255, 255, 255); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalVAT"), 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->total_tva), 0, 'R', 1); + } + // Show VAT by rates and total $pdf->SetFillColor(248, 248, 248); @@ -1704,49 +1780,51 @@ protected function drawTotalTable(&$pdf, $object, $deja_regle, $posy, $outputlan } //} - // VAT - $tvas = array(); - $nblines = count($object->lines); - for ($i = 0; $i < $nblines; $i++) { - $tvaligne = $object->lines[$i]->total_tva; - $vatrate = (string) $object->lines[$i]->tva_tx; + if (!getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + // VAT + $tvas = array(); + $nblines = count($object->lines); + for ($i = 0; $i < $nblines; $i++) { + $tvaligne = $object->lines[$i]->total_tva; + $vatrate = (string) $object->lines[$i]->tva_tx; - if (($object->lines[$i]->info_bits & 0x01) == 0x01) { - $vatrate .= '*'; - } - if (! isset($tvas[$vatrate])) { - $tvas[$vatrate] = 0; + if (($object->lines[$i]->info_bits & 0x01) == 0x01) { + $vatrate .= '*'; + } + if (! isset($tvas[$vatrate])) { + $tvas[$vatrate] = 0; + } + $tvas[$vatrate] += $tvaligne; } - $tvas[$vatrate] += $tvaligne; - } - foreach ($tvas as $tvakey => $tvaval) { - if ($tvakey != 0) { // On affiche pas taux 0 - $this->atleastoneratenotnull++; + foreach ($tvas as $tvakey => $tvaval) { + if ($tvakey != 0) { // On affiche pas taux 0 + $this->atleastoneratenotnull++; - $index++; - $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $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).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : ''); - $totalvat .= ' '; - if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') { - $totalvat .= vatrate($tvaval['vatrate'], 1).$tvacompl; - } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') { - $totalvat .= $tvaval['vatcode'].$tvacompl; - } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'nocodenorate') { - $totalvat .= $tvacompl; - } else { - $totalvat .= vatrate($tvaval['vatrate'], 1).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$tvacompl; - } - $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1); + $tvacompl = ''; + if (preg_match('/\*/', $tvakey)) { + $tvakey = str_replace('*', '', $tvakey); + $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; + } + $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : ''); + $totalvat .= ' '; + if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') { + $totalvat .= vatrate($tvaval['vatrate'], 1).$tvacompl; + } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') { + $totalvat .= $tvaval['vatcode'].$tvacompl; + } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'nocodenorate') { + $totalvat .= $tvacompl; + } else { + $totalvat .= vatrate($tvaval['vatrate'], 1).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$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(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1); + } } } diff --git a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php index 9a361ab0ce4f3..7fb66eb5ae38f 100644 --- a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php @@ -1,18 +1,19 @@ - * Copyright (C) 2005-2012 Regis Houssin - * Copyright (C) 2008 Raphael Bertrand - * Copyright (C) 2010-2014 Juanjo Menent - * Copyright (C) 2012 Christophe Battarel - * Copyright (C) 2012 Cédric Salvador - * Copyright (C) 2012-2014 Raphaël Doursenaud - * Copyright (C) 2015 Marcos García - * Copyright (C) 2017 Ferran Marcet - * Copyright (C) 2018-2024 Frédéric France - * Copyright (C) 2021-2024 Anthony Berton - * Copyright (C) 2022-2024 Alexandre Spangaro - * Copyright (C) 2024 MDW - * Copyright (C) 2024 Nick Fragoulis +/* Copyright (C) 2004-2024 Laurent Destailleur + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2008 Raphael Bertrand + * Copyright (C) 2010-2014 Juanjo Menent + * Copyright (C) 2012 Christophe Battarel + * Copyright (C) 2012 Cédric Salvador + * Copyright (C) 2012-2014 Raphaël Doursenaud + * Copyright (C) 2015 Marcos García + * Copyright (C) 2017 Ferran Marcet + * Copyright (C) 2018-2024 Frédéric France + * Copyright (C) 2018-2024 Anthony Berton + * Copyright (C) 2022-2024 Alexandre Spangaro + * Copyright (C) 2024 MDW + * Copyright (C) 2024 Nick Fragoulis + * Copyright (C) 2024 Franck Moreau * * 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 @@ -961,10 +962,17 @@ public function write_file($object, $outputlangs, $srctemplatepath = '', $hidede } $this->tva[$vatrate] += $tvaligne; // ->tva is abandoned, we use now ->tva_array that is more complete $vatcode = $object->lines[$i]->vat_src_code; - if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { - $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; + if (getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['tot_ht'])) { + $this->tva_array[$vatrate . ($vatcode ? ' (' . $vatcode . ')' : '')]['tot_ht'] = 0; + } + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate'=>$vatrate, 'vatcode'=>$vatcode, 'amount'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne, 'tot_ht'=> $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['tot_ht'] + $object->lines[$i]->total_ht); + } else { + if (empty($this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'])) { + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] = 0; + } + $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne); } - $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')] = array('vatrate' => $vatrate, 'vatcode' => $vatcode, 'amount' => $this->tva_array[$vatrate.($vatcode ? ' ('.$vatcode.')' : '')]['amount'] + $tvaligne); $nexY = max($nexY, $posYAfterImage); @@ -1260,6 +1268,65 @@ protected function drawInfoTable(&$pdf, $object, $posy, $outputlangs, $outputlan $pdf->SetFont('', '', $default_font_size - 1); + krsort($this->tva_array); + + // Show VAT details + if ($object->total_tva != 0 && getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + $pdf->SetFillColor(224, 224, 224); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("VAT"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 25, $posy); + $titre = $outputlangs->transnoentities("NetTotal"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 50, $posy); + $titre = $outputlangs->transnoentities("VATAmount"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 75, $posy); + $titre = $outputlangs->transnoentities("AmountTotal"); + $pdf->MultiCell(25, 4, $titre, 0, 'L', true); + + $posy = $pdf->GetY(); + $tot_ht = 0; + $tot_tva = 0; + $tot_ttc = 0; + + foreach ($this->tva_array as $tvakey => $tvaval) { + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = round((float) $tvakey, 2) . "%"; + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 25, $posy); + $titre = price($tvaval['tot_ht']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_ht += $tvaval['tot_ht']; + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 50, $posy); + $titre = price($tvaval['amount']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_tva += $tvaval['amount']; + + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche + 75, $posy); + $titre = price($tvaval['tot_ht'] + $tvaval['amount']); + $pdf->MultiCell(25, 4, $titre, 0, 'L'); + $tot_ttc += ($tvaval['tot_ht'] + $tvaval['amount']); + + $posy = $pdf->GetY(); + } + } + // If France, show VAT mention if not applicable if ($this->emetteur->country_code == 'FR' && empty($mysoc->tva_assuj)) { $pdf->SetFont('', '', $default_font_size - 2); @@ -1711,6 +1778,16 @@ protected function drawTotalTable(&$pdf, $object, $deja_regle, $posy, $outputlan $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); $pdf->MultiCell($largcol2, $tab2_hl, price($sign * ($total_ht + (!empty($object->remise) ? $object->remise : 0)), 0, $outputlangs), 0, 'R', 1); + if (getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + $index++; + $pdf->SetFillColor(255, 255, 255); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("TotalVAT"), 0, 'L', 1); + + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($sign * $object->total_tva), 0, 'R', 1); + } + // Show VAT by rates and total $pdf->SetFillColor(248, 248, 248); @@ -1821,34 +1898,36 @@ protected function drawTotalTable(&$pdf, $object, $deja_regle, $posy, $outputlan } } - // VAT - foreach ($this->tva_array as $tvakey => $tvaval) { - if ($tvakey != 0) { // On affiche pas taux 0 - $this->atleastoneratenotnull++; + if (!getDolGlobalInt('PDF_INVOICE_SHOW_VAT_ANALYSIS')) { + // VAT + foreach ($this->tva_array as $tvakey => $tvaval) { + if ($tvakey != 0) { // On affiche pas taux 0 + $this->atleastoneratenotnull++; + + $index++; + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); - $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).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : ''); + $totalvat .= ' '; + if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') { + $totalvat .= vatrate($tvaval['vatrate'], 1).$tvacompl; + } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') { + $totalvat .= $tvaval['vatcode'].$tvacompl; + } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'nocodenorate') { + $totalvat .= $tvacompl; + } else { + $totalvat .= vatrate($tvaval['vatrate'], 1).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$tvacompl; + } + $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1); - $tvacompl = ''; - if (preg_match('/\*/', $tvakey)) { - $tvakey = str_replace('*', '', $tvakey); - $tvacompl = " (".$outputlangs->transnoentities("NonPercuRecuperable").")"; - } - $totalvat = $outputlangs->transcountrynoentities("TotalVAT", $mysoc->country_code).(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transcountrynoentities("TotalVAT", $mysoc->country_code) : ''); - $totalvat .= ' '; - if (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'rateonly') { - $totalvat .= vatrate($tvaval['vatrate'], 1).$tvacompl; - } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'codeonly') { - $totalvat .= $tvaval['vatcode'].$tvacompl; - } elseif (getDolGlobalString('PDF_VAT_LABEL_IS_CODE_OR_RATE') == 'nocodenorate') { - $totalvat .= $tvacompl; - } else { - $totalvat .= vatrate($tvaval['vatrate'], 1).($tvaval['vatcode'] ? ' ('.$tvaval['vatcode'].')' : '').$tvacompl; + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1); } - $pdf->MultiCell($col2x - $col1x, $tab2_hl, $totalvat, 0, 'L', 1); - - $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); - $pdf->MultiCell($largcol2, $tab2_hl, price(price2num($tvaval['amount'], 'MT'), 0, $outputlangs), 0, 'R', 1); } } diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 93102fb68a0e2..fc741b9950917 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -2579,6 +2579,7 @@ CaptchaDesc=If you want to protect your login page with a Captcha, you can choos DolibarrStandardCaptcha=A native captcha generated by Dolibarr SALES_ORDER_SHOW_SHIPPING_ADDRESS=Show shipping address SALES_ORDER_SHOW_SHIPPING_ADDRESSMore=Compulsory indication in some countries (France, ...) +PDF_INVOICE_SHOW_VAT_ANALYSIS=Show vat analysis per rate MaxNbOfRecordOnListIsOk=You have a max size for lists is set to %s lines. This is a good value. YouHaveALargeAmountOfRecordOnLists=You have a default max size for lists set to %s lines. This is a large value that need scrolling to see all answers. It is better to have a value lower than %s and use pagination to see record over this number. Change this in menu Home - Setup - Display. RoundBorders=Round borders diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index ba2c0596421d1..41860b95bbd2e 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -1317,6 +1317,8 @@ NbRecordQualified=Number of qualified records auto=auto UploadFile=Direct import of document OrClickToSelectAFile=or click to select a file on disk +NetTotal=Net total +VATAmount=VAT amount TotalDiscount=Total discount TotalHTBeforeDiscount=Total net before discount Contains=Contains