From b369576a9d84665f8f31b974e876c5f09494fe04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Tue, 6 Jan 2026 15:58:06 +0100 Subject: [PATCH] NEW add support of subtotals on more objects (#34125) * add more subtotals * add more subtotals * class * class * wip * wip * wip * wip * doc * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * clean code * fix * fix * fix * fix * fix * fix * clean code * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * fix * fix * fix * fix * fix * fix * fetch extraparams * add confirms * add options * add options * add options * add options * add options * add options * add options * fix * wip * wip * fix * fix * fix * fix * fix * fix --- htdocs/admin/subtotals.php | 19 +- htdocs/comm/propal/class/propal.class.php | 2 +- htdocs/commande/class/commande.class.php | 2 +- htdocs/compta/facture/class/facture.class.php | 2 +- .../doc/pdf_canelle.modules.php | 28 ++ .../supplier_order/doc/pdf_cornas.modules.php | 28 ++ .../doc/pdf_zenith.modules.php | 54 +++- htdocs/core/tpl/subtotal_create.tpl.php | 2 +- htdocs/core/tpl/subtotal_view.tpl.php | 8 + htdocs/core/tpl/subtotalline_select.tpl.php | 17 + .../class/fournisseur.commande.class.php | 12 +- .../fourn/class/fournisseur.facture.class.php | 7 +- htdocs/fourn/commande/card.php | 292 +++++++++++++++++- htdocs/fourn/facture/card.php | 44 ++- .../subtotals/class/commonsubtotal.class.php | 161 +++++++++- .../substitutions/functions_subtotals.lib.php | 10 +- htdocs/supplier_proposal/card.php | 290 ++++++++++++++++- .../class/supplier_proposal.class.php | 4 +- 18 files changed, 942 insertions(+), 40 deletions(-) diff --git a/htdocs/admin/subtotals.php b/htdocs/admin/subtotals.php index 4fc20357891..924ae671178 100644 --- a/htdocs/admin/subtotals.php +++ b/htdocs/admin/subtotals.php @@ -3,7 +3,7 @@ * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2012-2013 Juanjo Menent * Copyright (C) 2019 Christophe Battarel - * Copyright (C) 2024 Frédéric France + * Copyright (C) 2024-2025 Frédéric France * Copyright (C) 2024 MDW * Copyright (C) 2025 Charlene Benke * @@ -63,6 +63,20 @@ $modules = [ 'FICHINTER' => array('lang' => 'interventions', 'key' => 'Intervention', 'old_pdf' => '(soleil model)'), 'FACTURE' => array('lang' => 'bills', 'key' => 'CustomerInvoice', 'old_pdf' => '(crabe model)'), 'FACTUREREC' => array('lang' => 'bills', 'key' => 'RecurringInvoiceTemplate'), + 'SUPPLIER_PROPOSAL' => [ + 'lang' => 'supplier_proposal', + 'key' => 'SupplierProposal', + 'old_pdf' => '(aurore model)', + ], + 'ORDER_SUPPLIER' => [ + 'lang' => 'orders', + 'key' => 'SupplierOrder', + 'old_pdf' => '(muscadet model)', + ], + 'INVOICE_SUPPLIER' => [ + 'lang' => 'bills', + 'key' => 'SupplierInvoice', + ], ]; // Conditions for the option to be offered $conditions = [ @@ -71,6 +85,9 @@ $conditions = [ 'FICHINTER' => (isModEnabled("intervention")), 'FACTURE' => isModEnabled("invoice"), 'FACTUREREC' => isModEnabled("invoice"), + 'SUPPLIER_PROPOSAL' => isModEnabled("supplier_proposal"), + 'ORDER_SUPPLIER' => isModEnabled("supplier_order"), + 'INVOICE_SUPPLIER' => isModEnabled("supplier_invoice"), ]; $max_depth = 0; diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 6bcc3a71ebb..89a439f8729 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -615,7 +615,7 @@ class Propal extends CommonObject * @param float $txlocaltax2 Local tax 2 rate (deprecated, use instead txtva with code inside) * @param int $fk_product Product/Service ID predefined * @param float $remise_percent Pourcentage de remise de la ligne - * @param string $price_base_type HT or TTC + * @param 'HT'|'TTC'|'' $price_base_type HT or TTC or '' for subtotals * @param float $pu_ttc Prix unitaire TTC * @param int $info_bits Bits for type of lines * @param int $type Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used. diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index 9a3ecaa9aab..0db2f4de14e 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -1563,7 +1563,7 @@ class Commande extends CommonOrder * @param float $remise_percent Percentage discount of the line * @param int $info_bits Bits of type of lines * @param int $fk_remise_except Id remise - * @param string $price_base_type HT or TTC + * @param 'HT'|'TTC'|'' $price_base_type HT or TTC or '' for subtotals * @param float $pu_ttc Prix unitaire TTC * @param int|'' $date_start Start date of the line * @param int|'' $date_end End date of the line diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index 0785c0da166..a9244ac6b2f 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -4138,7 +4138,7 @@ class Facture extends CommonInvoice * @param int $fk_code_ventilation Code of dispatching into accountancy * @param int $info_bits Bits of type of lines * @param int $fk_remise_except Id discount used - * @param string $price_base_type 'HT' or 'TTC' + * @param 'HT'|'TTC'|'' $price_base_type HT or TTC or '' for subtotals * @param float $pu_ttc Unit price with tax (> 0 even for credit note) * @param int $type Type of line (0=product, 1=service). Not used if fk_product is defined, the type of product is used. * @param int $rang Position of line (-1 means last value + 1) diff --git a/htdocs/core/modules/supplier_invoice/doc/pdf_canelle.modules.php b/htdocs/core/modules/supplier_invoice/doc/pdf_canelle.modules.php index d9ea548c74f..9a471de4d30 100644 --- a/htdocs/core/modules/supplier_invoice/doc/pdf_canelle.modules.php +++ b/htdocs/core/modules/supplier_invoice/doc/pdf_canelle.modules.php @@ -362,9 +362,37 @@ class pdf_canelle extends ModelePDFSuppliersInvoices $iniY = $tab_top + 7; $curY = $tab_top + 7; $nexY = $tab_top + 7; + $pdf_sub_options = [ + 'titleshowuponpdf' => 1, + 'titleshowtotalexludingvatonpdf' => 1, + ]; // Loop on each lines for ($i = 0; $i < $nblines; $i++) { + $sub_options = $object->lines[$i]->extraparams["subtotal"] ?? array(); + + if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE) { + $level = $object->lines[$i]->qty; + if ($sub_options) { + if (isset($sub_options['titleshowuponpdf'])) { + $pdf_sub_options['titleshowuponpdf'] = isset($pdf_sub_options['titleshowuponpdf']) && $pdf_sub_options['titleshowuponpdf'] < $level ? $pdf_sub_options['titleshowuponpdf'] : $level; + } elseif (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) { + unset($pdf_sub_options['titleshowuponpdf']); + } + if (isset($sub_options['titleshowtotalexludingvatonpdf'])) { + $pdf_sub_options['titleshowtotalexludingvatonpdf'] = isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && $pdf_sub_options['titleshowtotalexludingvatonpdf'] < $level ? $pdf_sub_options['titleshowtotalexludingvatonpdf'] : $level; + } elseif (isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && abs($level) <= $pdf_sub_options['titleshowtotalexludingvatonpdf']) { + unset($pdf_sub_options['titleshowtotalexludingvatonpdf']); + } + } else { + if (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) { + unset($pdf_sub_options['titleshowuponpdf']); + } + if (isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && abs($level) <= $pdf_sub_options['titleshowtotalexludingvatonpdf']) { + unset($pdf_sub_options['titleshowtotalexludingvatonpdf']); + } + } + } $curY = $nexY; $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage $pdf->SetTextColor(0, 0, 0); diff --git a/htdocs/core/modules/supplier_order/doc/pdf_cornas.modules.php b/htdocs/core/modules/supplier_order/doc/pdf_cornas.modules.php index cbf70cf318d..6f8ca32125a 100644 --- a/htdocs/core/modules/supplier_order/doc/pdf_cornas.modules.php +++ b/htdocs/core/modules/supplier_order/doc/pdf_cornas.modules.php @@ -499,9 +499,37 @@ class pdf_cornas extends ModelePDFSuppliersOrders $pageposbeforeprintlines = $pdf->getPage(); $pagenb = $pageposbeforeprintlines; + $pdf_sub_options = [ + 'titleshowuponpdf' => 1, + 'titleshowtotalexludingvatonpdf' => 1, + ]; // Loop on each lines for ($i = 0; $i < $nblines; $i++) { + $sub_options = $object->lines[$i]->extraparams["subtotal"] ?? array(); + + if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE) { + $level = $object->lines[$i]->qty; + if ($sub_options) { + if (isset($sub_options['titleshowuponpdf'])) { + $pdf_sub_options['titleshowuponpdf'] = isset($pdf_sub_options['titleshowuponpdf']) && $pdf_sub_options['titleshowuponpdf'] < $level ? $pdf_sub_options['titleshowuponpdf'] : $level; + } elseif (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) { + unset($pdf_sub_options['titleshowuponpdf']); + } + if (isset($sub_options['titleshowtotalexludingvatonpdf'])) { + $pdf_sub_options['titleshowtotalexludingvatonpdf'] = isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && $pdf_sub_options['titleshowtotalexludingvatonpdf'] < $level ? $pdf_sub_options['titleshowtotalexludingvatonpdf'] : $level; + } elseif (isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && abs($level) <= $pdf_sub_options['titleshowtotalexludingvatonpdf']) { + unset($pdf_sub_options['titleshowtotalexludingvatonpdf']); + } + } else { + if (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) { + unset($pdf_sub_options['titleshowuponpdf']); + } + if (isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && abs($level) <= $pdf_sub_options['titleshowtotalexludingvatonpdf']) { + unset($pdf_sub_options['titleshowtotalexludingvatonpdf']); + } + } + } $curY = $nexY; $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage $pdf->SetTextColor(0, 0, 0); diff --git a/htdocs/core/modules/supplier_proposal/doc/pdf_zenith.modules.php b/htdocs/core/modules/supplier_proposal/doc/pdf_zenith.modules.php index 8e13c399491..70fe565f575 100644 --- a/htdocs/core/modules/supplier_proposal/doc/pdf_zenith.modules.php +++ b/htdocs/core/modules/supplier_proposal/doc/pdf_zenith.modules.php @@ -485,10 +485,39 @@ class pdf_zenith extends ModelePDFSupplierProposal $nexY = $tab_top + $this->tabTitleHeight; - // Loop on each lines $pageposbeforeprintlines = $pdf->getPage(); $pagenb = $pageposbeforeprintlines; + $pdf_sub_options = [ + 'titleshowuponpdf' => 1, + 'titleshowtotalexludingvatonpdf' => 1, + ]; + + // Loop on each lines for ($i = 0; $i < $nblines; $i++) { + $sub_options = $object->lines[$i]->extraparams["subtotal"] ?? array(); + + if ($object->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE) { + $level = $object->lines[$i]->qty; + if ($sub_options) { + if (isset($sub_options['titleshowuponpdf'])) { + $pdf_sub_options['titleshowuponpdf'] = isset($pdf_sub_options['titleshowuponpdf']) && $pdf_sub_options['titleshowuponpdf'] < $level ? $pdf_sub_options['titleshowuponpdf'] : $level; + } elseif (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) { + unset($pdf_sub_options['titleshowuponpdf']); + } + if (isset($sub_options['titleshowtotalexludingvatonpdf'])) { + $pdf_sub_options['titleshowtotalexludingvatonpdf'] = isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && $pdf_sub_options['titleshowtotalexludingvatonpdf'] < $level ? $pdf_sub_options['titleshowtotalexludingvatonpdf'] : $level; + } elseif (isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && abs($level) <= $pdf_sub_options['titleshowtotalexludingvatonpdf']) { + unset($pdf_sub_options['titleshowtotalexludingvatonpdf']); + } + } else { + if (isset($pdf_sub_options['titleshowuponpdf']) && abs($level) <= $pdf_sub_options['titleshowuponpdf']) { + unset($pdf_sub_options['titleshowuponpdf']); + } + if (isset($pdf_sub_options['titleshowtotalexludingvatonpdf']) && abs($level) <= $pdf_sub_options['titleshowtotalexludingvatonpdf']) { + unset($pdf_sub_options['titleshowtotalexludingvatonpdf']); + } + } + } $curY = $nexY; $pdf->SetFont('', '', $default_font_size - 1); // Into loop to work with multipage $pdf->SetTextColor(0, 0, 0); @@ -588,14 +617,14 @@ class pdf_zenith extends ModelePDFSupplierProposal $pdf->SetFont('', '', $default_font_size - 1); // On repositionne la police par default // VAT Rate - if ($this->getColumnStatus('vat')) { + if ($this->getColumnStatus('vat') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) { $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')) { + if ($this->getColumnStatus('subprice') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowuponpdf'])) { $up_excl_tax = pdf_getlineupexcltax($object, $i, $outputlangs, $hidedetails); $this->printStdColumnContent($pdf, $curY, 'subprice', $up_excl_tax); $nexY = max($pdf->GetY(), $nexY); @@ -603,7 +632,7 @@ class pdf_zenith extends ModelePDFSupplierProposal // Quantity // Enough for 6 chars - if ($this->getColumnStatus('qty')) { + if ($this->getColumnStatus('qty') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) { $qty = pdf_getlineqty($object, $i, $outputlangs, $hidedetails); $this->printStdColumnContent($pdf, $curY, 'qty', $qty); $nexY = max($pdf->GetY(), $nexY); @@ -611,14 +640,14 @@ class pdf_zenith extends ModelePDFSupplierProposal // Unit - if ($this->getColumnStatus('unit')) { + if ($this->getColumnStatus('unit') && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) { $unit = pdf_getlineunit($object, $i, $outputlangs, $hidedetails); $this->printStdColumnContent($pdf, $curY, 'unit', $unit); $nexY = max($pdf->GetY(), $nexY); } // Discount on line - if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent) { + if ($this->getColumnStatus('discount') && $object->lines[$i]->remise_percent && $object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE) { $remise_percent = pdf_getlineremisepercent($object, $i, $outputlangs, $hidedetails); $this->printStdColumnContent($pdf, $curY, 'discount', $remise_percent); $nexY = max($pdf->GetY(), $nexY); @@ -626,8 +655,17 @@ class pdf_zenith extends ModelePDFSupplierProposal // Total HT line if ($this->getColumnStatus('totalexcltax')) { - $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails); - $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax); + if ($object->lines[$i]->special_code != SUBTOTALS_SPECIAL_CODE && isset($pdf_sub_options['titleshowtotalexludingvatonpdf'])) { + $total_excl_tax = pdf_getlinetotalexcltax($object, $i, $outputlangs, $hidedetails); + $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax); + } elseif ($object->lines[$i]->qty < 0 && isset($sub_options['subtotalshowtotalexludingvatonpdf'])) { + if (isModEnabled('multicurrency') && $object->multicurrency_code != $conf->currency) { + $total_excl_tax = $object->getSubtotalLineMulticurrencyAmount($object->lines[$i]); + } else { + $total_excl_tax = $object->getSubtotalLineAmount($object->lines[$i]); + } + $this->printStdColumnContent($pdf, $curY, 'totalexcltax', $total_excl_tax); + } $nexY = max($pdf->GetY(), $nexY); } diff --git a/htdocs/core/tpl/subtotal_create.tpl.php b/htdocs/core/tpl/subtotal_create.tpl.php index 88d60291677..c3c428349af 100644 --- a/htdocs/core/tpl/subtotal_create.tpl.php +++ b/htdocs/core/tpl/subtotal_create.tpl.php @@ -33,7 +33,7 @@ */ ' -@phan-var-force CommonObject $this +@phan-var-force Propal|Commande|Facture|FactureRec|Expedition|SupplierProposal|CommandeFournisseur|FactureFournisseur $this '; $depth_array = $depth_array ?? array(); diff --git a/htdocs/core/tpl/subtotal_view.tpl.php b/htdocs/core/tpl/subtotal_view.tpl.php index de0138aa2fc..f448a3a570c 100644 --- a/htdocs/core/tpl/subtotal_view.tpl.php +++ b/htdocs/core/tpl/subtotal_view.tpl.php @@ -69,6 +69,10 @@ if ($line->qty > 0) { ?> if (array_key_exists('titleforcepagebreak', $line_options)) { echo ' ' . img_picto($langs->trans("ForcePageBreak"), 'file'); } + // Handling td for ref supplier + if (in_array($object->element, ['supplier_proposal'])) { + echo ''; + } ?> @@ -202,6 +206,10 @@ if ($line->qty > 0) { ?> if (getDolGlobalString('PRODUCT_USE_UNITS')) { $colspan += 1; } + // Handling colspan if supplier object + if (in_array($object->element, ['supplier_proposal'])) { + $colspan += 1; + } ?> colspan=""> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ /** * @var CommonObject $object diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index ce9908469f4..8e6b6ec3bfa 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -42,6 +42,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.orderline.class.php'; require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php'; +require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php'; if (isModEnabled('productbatch')) { require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php'; } @@ -52,6 +53,8 @@ if (isModEnabled('productbatch')) { */ class CommandeFournisseur extends CommonOrder { + use CommonSubtotal; + /** * @var string ID to identify managed object */ @@ -2005,7 +2008,7 @@ class CommandeFournisseur extends CommonOrder * @param int $fk_prod_fourn_price Id supplier price * @param string $ref_supplier Supplier reference price * @param float $remise_percent Remise - * @param 'HT'|'TTC' $price_base_type HT or TTC + * @param 'HT'|'TTC'|'' $price_base_type HT or TTC or '' for subtotals * @param float $pu_ttc Unit price TTC (used if $price_base_type is 'TTC') * @param int<0,1> $type Type of line (0=product, 1=service) * @param int $info_bits More information @@ -2277,6 +2280,13 @@ class CommandeFournisseur extends CommonOrder } $this->lines[] = $this->line; + } else { + foreach ($this->lines as $line) { + if ($line->id == $origin_id) { + $this->line->extraparams = $line->extraparams; + $this->line->setExtraParameters(); + } + } } $this->db->commit(); diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php index f83b0bfaa26..059355c79b5 100644 --- a/htdocs/fourn/class/fournisseur.facture.class.php +++ b/htdocs/fourn/class/fournisseur.facture.class.php @@ -41,6 +41,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php'; require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.ligne.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php'; if (isModEnabled('accounting')) { require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; @@ -52,6 +53,8 @@ if (isModEnabled('accounting')) { */ class FactureFournisseur extends CommonInvoice { + use CommonSubtotal; + /** * @var string ID to identify managed object */ @@ -2442,10 +2445,10 @@ class FactureFournisseur extends CommonInvoice } $tabprice = calcul_price_total((float) $qty, (float) $pu, $remise_percent, $vatrate, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, (float) $pu_devise); - $total_ht = $tabprice[0]; + $total_ht = $tabprice[0]; $total_tva = $tabprice[1]; $total_ttc = $tabprice[2]; - $pu_ht = $tabprice[3]; + $pu_ht = $tabprice[3]; $pu_tva = $tabprice[4]; $pu_ttc = $tabprice[5]; $total_localtax1 = $tabprice[9]; diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index b4faa479cfd..c79b3447c9c 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -292,6 +292,56 @@ if (empty($reshook)) { } } + // Subtotal + if ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $usercancreate) { + // Delete line + $object->fetch($id); + $object->fetch_thirdparty(); + + $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline')); + if ($result > 0) { + // reorder lines + $object->line_order(true); + // Define output language + $outputlangs = $langs; + $newlang = ''; + if (getDolGlobalInt('MAIN_MULTILANGS') && GETPOST('lang_id')) { + $newlang = GETPOST('lang_id'); + } + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outputlangs->load('products'); + } + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + $ret = $object->fetch($id); // Reload to get new records + $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + if ($result >= 0) { + header('Location: '.dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $id])); + exit(); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + $action = ''; + } + } + if ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) { + $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0; + $result = $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva); + if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); + } + } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) { + $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0; + $result = $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount); + if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); + } + } // Set tags if ($action == 'settags' && isModEnabled('category') && $usercancreate) { $result = $object->setCategories(GETPOST('categories', 'array')); @@ -386,9 +436,7 @@ if (empty($reshook)) { if ($ret < 0) { setEventMessages($object->error, $object->errors, 'errors'); } - } - - if ($action == 'classifyunbilled' && $usercancreate) { + } elseif ($action == 'classifyunbilled' && $usercancreate) { $ret = $object->classifyUnBilled($user); if ($ret < 0) { setEventMessages($object->error, $object->errors, 'errors'); @@ -412,8 +460,109 @@ if (empty($reshook)) { $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc); $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc); foreach ($object->lines as $line) { + if ($line->special_code == SUBTOTALS_SPECIAL_CODE) { + continue; + } $result = $object->updateline($line->id, $line->desc, $line->subprice, $line->qty, $line->remise_percent, $vat_rate, $localtax1_rate, $localtax2_rate, 'HT', $line->info_bits, $line->product_type, 0, $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice, $line->ref_supplier); } + } elseif ($action == 'confirm_addtitleline' && $usercancreate) { + // Handling adding a new title line for subtotals module + + $langs->load('subtotals'); + + $desc = GETPOST('subtotallinedesc', 'alphanohtml'); + $depth = GETPOSTINT('subtotallinelevel') ?? 1; + + $subtotal_options = array(); + + foreach (CommandeFournisseur::$TITLE_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Insert line + $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options); + + if ($result >= 0) { + if ($result == 0) { + setEventMessages($object->error, $object->errors, 'warnings'); + } + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + header('Location: '.dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $id])); + exit(); + } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) { + // Handling adding a new subtotal line for subtotals module + + $langs->load('subtotals'); + + $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml'); + foreach ($object->lines as $line) { + if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) { + $desc = $line->desc; + $depth = -$line->qty; + } + } + + $subtotal_options = array(); + + foreach (CommandeFournisseur::$SUBTOTAL_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Insert line + if (isset($desc) && isset($depth)) { + $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options); + } else { + $object->errors[] = $langs->trans("CorrespondingTitleNotFound"); + } + + if (isset($result) && $result >= 0) { + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + header('Location: '.dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $id])); + exit(); } elseif ($action == 'addline' && $usercancreate) { $db->begin(); @@ -738,12 +887,93 @@ if (empty($reshook)) { } $action = ''; - } + } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) { + // Handling updating a title line for subtotals module - /* - * Updating a line in the order - */ - if ($action == 'updateline' && $usercancreate && !GETPOST('cancel', 'alpha')) { + $langs->load('subtotals'); + + $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title"); + $depth = GETPOSTINT('line_depth') ?? 1; + + $subtotal_options = array(); + + foreach (CommandeFournisseur::$TITLE_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Update line + $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options); + + if ($result >= 0) { + if ($result == 0) { + setEventMessages($object->error, $object->errors, 'warnings'); + } + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) { + // Handling updating a subtotal line for subtotals module + + $langs->load('subtotals'); + + $desc = GETPOST('line_desc', 'alphanohtml'); + $depth = GETPOSTINT('line_depth'); + + $subtotal_options = array(); + + foreach (CommandeFournisseur::$SUBTOTAL_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Update line + $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options); + + if ($result > 0) { + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } elseif ($action == 'updateline' && $usercancreate && !GETPOST('cancel', 'alpha')) { + // Updating a line in the order $db->begin(); $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); @@ -2149,6 +2379,20 @@ if ($action == 'create') { $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1); } + // Subtotal line form + if ($action == 'add_title_line') { + $langs->load('subtotals'); + $type = 'title'; + $depth_array = $object->getPossibleLevels($langs); + require DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_create.tpl.php'; + } elseif ($action == 'add_subtotal_line') { + $langs->load('subtotals'); + $type = 'subtotal'; + $titles = $object->getPossibleTitles(); + require DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_create.tpl.php'; + } + + // Call Hook formConfirm $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid); $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { @@ -2527,8 +2771,12 @@ if ($action == 'create') { '; - if (!empty($conf->use_javascript_ajax) && $object->status == 0) { - include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php'; + if (!empty($conf->use_javascript_ajax) && $object->status == CommandeFournisseur::STATUS_DRAFT) { + if (isModEnabled('subtotals')) { + include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php'; + } else { + include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php'; + } } print '
'; @@ -2587,6 +2835,30 @@ if ($action == 'create') { if (empty($reshook)) { $object->fetchObjectLinked(); // Links are used to show or not button, so we load them now. + // Subtotal + if ($object->status == CommandeFournisseur::STATUS_DRAFT && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) { + $langs->load('subtotals'); + + $url_button = array(); + + $url_button[] = array( + 'lang' => 'subtotals', + 'enabled' => $object->status == CommandeFournisseur::STATUS_DRAFT, + 'perm' => (bool) $usercancreate, + 'label' => $langs->trans('AddTitleLine'), + 'url' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'action' => 'add_title_line'], true) + ); + + $url_button[] = array( + 'lang' => 'subtotals', + 'enabled' => $object->status == CommandeFournisseur::STATUS_DRAFT, + 'perm' => (bool) $usercancreate, + 'label' => $langs->trans('AddSubtotalLine'), + 'url' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'action' => 'add_subtotal_line'], true) + ); + + print dolGetButtonAction('', $langs->trans('Subtotal'), 'default', $url_button, '', true); + } // Validate if ($object->status == 0 && $num > 0) { if ($usercanvalidate) { diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index a5b1cccc533..0d828a0368e 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -3311,6 +3311,20 @@ if ($action == 'create') { $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1); } + // Subtotal line form + if ($action == 'add_title_line') { + $langs->load('subtotals'); + $type = 'title'; + $depth_array = $object->getPossibleLevels($langs); + require DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_create.tpl.php'; + } elseif ($action == 'add_subtotal_line') { + $langs->load('subtotals'); + $type = 'subtotal'; + $titles = $object->getPossibleTitles(); + require DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_create.tpl.php'; + } + + // Call Hook formConfirm $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid); $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { @@ -4110,7 +4124,11 @@ if ($action == 'create') { print ''; if (!empty($conf->use_javascript_ajax) && $object->status == FactureFournisseur::STATUS_DRAFT) { - include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php'; + if (isModEnabled('subtotals')) { + include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php'; + } else { + include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php'; + } } print '
'; @@ -4165,6 +4183,30 @@ if ($action == 'create') { $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been // modified by hook if (empty($reshook)) { + // Subtotal + if ($object->status === FactureFournisseur::STATUS_DRAFT && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) { + $langs->load('subtotals'); + + $url_button = array(); + + $url_button[] = array( + 'lang' => 'subtotals', + 'enabled' => true, + 'perm' => (bool) $usercancreate, + 'label' => $langs->trans('AddTitleLine'), + 'url' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'action' => 'add_title_line'], true) + ); + + $url_button[] = array( + 'lang' => 'subtotals', + 'enabled' => true, + 'perm' => (bool) $usercancreate, + 'label' => $langs->trans('AddSubtotalLine'), + 'url' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'action' => 'add_subtotal_line'], true) + ); + + print dolGetButtonAction('', $langs->trans('Subtotal'), 'default', $url_button, '', true); + } // Modify a validated invoice with no payments if ($object->status == FactureFournisseur::STATUS_VALIDATED && $action != 'confirm_edit' && $object->getSommePaiement() == 0 && $usercancreate) { // We check if lines of invoice are not already transferred into accountancy diff --git a/htdocs/subtotals/class/commonsubtotal.class.php b/htdocs/subtotals/class/commonsubtotal.class.php index 5481f0a9077..bd8e77157bf 100644 --- a/htdocs/subtotals/class/commonsubtotal.class.php +++ b/htdocs/subtotals/class/commonsubtotal.class.php @@ -59,8 +59,12 @@ trait CommonSubtotal 'facture', 'facturerec', 'shipping', + 'supplier_proposal', + 'order_supplier', + 'invoice_supplier', ]; + /** * Adds a subtotals line to a document. * This function inserts a subtotal line based on the given parameters. @@ -212,6 +216,78 @@ trait CommonSubtotal SUBTOTALS_SPECIAL_CODE // Special code ); $this->fetch_lines(); + } elseif ($current_module == 'supplier_proposal' && $this instanceof SupplierProposal) { + $rang = $rang == -1 ? $rang : $rang-1; + $result = $this->addline( + $desc, // Description + 0, // Unit price + $depth, // Quantity + 0, // VAT rate + 0, // Local tax 1 + 0, // Local tax 2 + 0, // FK product + 0, // Discount percentage + '', // Price base type + 0, // PU ttc + 0, // Info bits + self::$PRODUCT_TYPE, // Type + $rang, // Rang + SUBTOTALS_SPECIAL_CODE // Special code + ); + } elseif ($current_module == 'order_supplier' && $this instanceof CommandeFournisseur) { + $rang = $rang == -1 ? $rang : $rang-1; + $result = $this->addline( + $desc, // Description + 0, // Unit price + $depth, // Quantity + 0, // VAT rate + 0, // Local tax 1 + 0, // Local tax 2 + 0, // FK product + 0, // fk fourn price + '', // ref supplier + 0, // Remise percent + '', // Price base type + 0, // PU ttc + self::$PRODUCT_TYPE, // Type + 0, // info bits + 0, // no trigger + null, // Date start + null, // Date end + [], // array_options + null, // fk_unit + 0, // pu ht devise + '', // origin type + 0, // origin id + $rang, // Rang + SUBTOTALS_SPECIAL_CODE // Special code + ); + } elseif ($current_module == 'invoice_supplier' && $this instanceof FactureFournisseur) { + $rang = $rang == -1 ? $rang : $rang-1; + $result = $this->addline( + $desc, // Description + 0, // Unit price + 0, // VAT rate + 0, // Local tax 1 + 0, // Local tax 2 + $depth, // Quantity + 0, // FK product + 0, // Remise percent + '', // Date start + '', // Date end + 0, // Code ventilation + 0, // info bits + '', // Price base type + self::$PRODUCT_TYPE, // Type + $rang, // Rang + 0, // no trigger + [], // array_options + null, // fk_unit + 0, // origin id + 0, // pu ht devise + '', // ref supplier + SUBTOTALS_SPECIAL_CODE // Special code + ); } elseif ($current_module == 'fichinter' && $this instanceof Fichinter) { global $user; $result = $this->addline( @@ -303,6 +379,18 @@ trait CommonSubtotal $line = new ExpeditionLigne($this->db); $line->id = $id; $result = $line->delete($user); + } elseif ($current_module == 'supplier_proposal') { + $line = new SupplierProposalLine($this->db); + $line->id = $id; + $result = $line->delete($user); + } elseif ($current_module == 'order_supplier') { + $line = new CommandeFournisseurLigne($this->db); + $line->id = $id; + $result = $line->delete($user); + } elseif ($current_module == 'invoice_supplier') { + $line = new SupplierInvoiceLine($this->db); + $line->id = $id; + $result = $line->delete(); } return $result >= 0 ? $result : -1; // Return line ID or false @@ -453,6 +541,70 @@ trait CommonSubtotal $line_rang, // Rang SUBTOTALS_SPECIAL_CODE // Special code ); + } elseif ($current_module == 'supplier_proposal' && $this instanceof SupplierProposal) { + $objectline = new SupplierProposalLine($this->db); + $objectline->fetch($lineid); + $line_rang = $objectline->rang; + $result = $this->updateline( + $lineid, // ID of line to change + 0, // Unit price + $depth, // Quantity + 0, // Discount percentage + 0, // VAT rate + 0, // Local tax 1 + 0, // Local tax 2 + $desc, // Description + '', // Price base type + 0, // Info bits + SUBTOTALS_SPECIAL_CODE, // Special code + 0, // FK parent line + 0, // + 0, // + 0, // + '', // + self::$PRODUCT_TYPE // Type + ); + } elseif ($current_module == 'order_supplier' && $this instanceof CommandeFournisseur) { + $objectline = new CommandeFournisseurLigne($this->db); + $objectline->fetch($lineid); + $line_rang = $objectline->rang; + // special code comes from old line + $result = $this->updateline( + $lineid, // ID of line to change + $desc, // Description + 0, // Unit price + $depth, // Quantity + 0, // Discount percentage + 0, // VAT rate + 0, // Local tax 1 + 0, // Local tax 2 + '', // Price base type + 0, // Info bits + self::$PRODUCT_TYPE, // Type + 0, // no trigger + 0, // + 0, // + [], // + null // + ); + } elseif ($current_module == 'invoice_supplier' && $this instanceof FactureFournisseur) { + $objectline = new SupplierInvoiceLine($this->db); + $objectline->fetch($lineid); + $line_rang = $objectline->rang; + $result = $this->updateline( + $lineid, // ID of line to change + $desc, // Description + 0, // Unit price + 0, // VAT rate + 0, // Local tax 1 + 0, // Local tax 2 + $depth, // Quantity + 0, // product id + '', // Price base type + 0, // Info bits + self::$PRODUCT_TYPE, // Type + 0 // Discount percentage + ); } foreach ($this->lines as $line) { @@ -518,7 +670,8 @@ trait CommonSubtotal 'HT', $this->lines[$i]->info_bits, $this->lines[$i]->product_type, - $this->lines[$i]->fk_parent_line, 0, + $this->lines[$i]->fk_parent_line, + 0, $this->lines[$i]->fk_fournprice, $this->lines[$i]->pa_ht, $this->lines[$i]->label, @@ -543,7 +696,8 @@ trait CommonSubtotal $this->lines[$i]->date_start, $this->lines[$i]->date_end, $this->lines[$i]->product_type, - $this->lines[$i]->fk_parent_line, 0, + $this->lines[$i]->fk_parent_line, + 0, $this->lines[$i]->fk_fournprice, $this->lines[$i]->pa_ht, $this->lines[$i]->label, @@ -565,7 +719,8 @@ trait CommonSubtotal 'HT', $this->lines[$i]->info_bits, $this->lines[$i]->special_code, - $this->lines[$i]->fk_parent_line, 0, + $this->lines[$i]->fk_parent_line, + 0, $this->lines[$i]->fk_fournprice, $this->lines[$i]->pa_ht, $this->lines[$i]->label, diff --git a/htdocs/subtotals/core/substitutions/functions_subtotals.lib.php b/htdocs/subtotals/core/substitutions/functions_subtotals.lib.php index 39a76dc8441..42ef175805d 100644 --- a/htdocs/subtotals/core/substitutions/functions_subtotals.lib.php +++ b/htdocs/subtotals/core/substitutions/functions_subtotals.lib.php @@ -22,11 +22,11 @@ * functions xxx_completesubstitutionarray are called by make_substitutions() if file * is inside directory htdocs/core/substitutions * -* @param array $substitutionarray Array with substitution key=>val -* @param Translate $langs Output langs -* @param CommonObject $object Object to use to get values -* @param CommonObjectLine $line Line to use to get values -* @return void The entry parameter $substitutionarray is modified +* @param array $substitutionarray Array with substitution key=>val +* @param Translate $langs Output langs +* @param Propal|Commande|Facture|FactureRec|Expedition|SupplierProposal|CommandeFournisseur|FactureFournisseur $object Object to use to get values +* @param CommonObjectLine $line Line to use to get values +* @return void The entry parameter $substitutionarray is modified */ function subtotals_completesubstitutionarray_lines(&$substitutionarray, $langs, $object, $line) { diff --git a/htdocs/supplier_proposal/card.php b/htdocs/supplier_proposal/card.php index 868b2adbba6..0d10b6bc324 100644 --- a/htdocs/supplier_proposal/card.php +++ b/htdocs/supplier_proposal/card.php @@ -242,6 +242,41 @@ if (empty($reshook)) { header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id); exit(); + } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $usercancreate) { + // Delete line + $object->fetch($id); + $object->fetch_thirdparty(); + + $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline')); + if ($result > 0) { + // reorder lines + $object->line_order(true); + // Define output language + $outputlangs = $langs; + $newlang = ''; + if (getDolGlobalInt('MAIN_MULTILANGS') && GETPOST('lang_id')) { + $newlang = GETPOST('lang_id'); + } + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outputlangs->load('products'); + } + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + $ret = $object->fetch($id); // Reload to get new records + $result = $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + if ($result >= 0) { + header('Location: '.dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $id])); + exit(); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + $action = ''; + } } elseif ($action == 'confirm_validate' && $confirm == 'yes' && $usercanvalidate) { // Validation $result = $object->valid($user); @@ -558,6 +593,12 @@ if (empty($reshook)) { $object->cloture($user, GETPOSTINT('statut'), GETPOST('note', 'restricthtml')); } } + } elseif ($action == 'addline' && GETPOST('updateallvatlinesblock', 'alpha') && GETPOST('vatforblocklines', 'alpha') !== '' && $usercancreate) { + $tx_tva = GETPOST('vatforblocklines') ? GETPOST('vatforblocklines') : 0; + $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'tva', $tx_tva); + } elseif ($action == 'addline' && GETPOST('updatealldiscountlinesblock', 'alpha') && GETPOST('discountforblocklines', 'alpha') !== '' && $usercancreate) { + $discount = GETPOST('discountforblocklines') ? GETPOST('discountforblocklines') : 0; + $object->updateSubtotalLineBlockLines($langs, $object->getRangOfLine($lineid), 'discount', $discount); } elseif ($action == 'settags' && isModEnabled('category') && $usercancreate) { // Set tags $result = $object->setCategories(GETPOST('categories', 'array')); } @@ -575,7 +616,6 @@ if (empty($reshook)) { $upload_dir = $conf->supplier_proposal->dir_output; include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; - // Go back to draft if ($action == 'modif' && $usercancreate) { $object->setDraft($user); @@ -613,8 +653,109 @@ if (empty($reshook)) { $localtax1_rate = get_localtax($vat_rate, 1, $object->thirdparty, $mysoc); $localtax2_rate = get_localtax($vat_rate, 2, $object->thirdparty, $mysoc); foreach ($object->lines as $line) { + if ($line->special_code == SUBTOTALS_SPECIAL_CODE) { + continue; + } $result = $object->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $vat_rate, $localtax1_rate, $localtax2_rate, $line->desc, 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 0, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options, $line->ref_fourn, $line->fk_unit, $line->multicurrency_subprice); } + } elseif ($action == 'confirm_addtitleline' && $usercancreate) { + // Handling adding a new title line for subtotals module + + $langs->load('subtotals'); + + $desc = GETPOST('subtotallinedesc', 'alphanohtml'); + $depth = GETPOSTINT('subtotallinelevel') ?? 1; + + $subtotal_options = array(); + + foreach (SupplierProposal::$TITLE_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Insert line + $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options); + + if ($result >= 0) { + if ($result == 0) { + setEventMessages($object->error, $object->errors, 'warnings'); + } + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$id); + exit(); + } elseif ($action == 'confirm_addsubtotalline' && $usercancreate) { + // Handling adding a new subtotal line for subtotals module + + $langs->load('subtotals'); + + $choosen_line = GETPOST('subtotaltitleline', 'alphanohtml'); + foreach ($object->lines as $line) { + if ($line->desc == $choosen_line && $line->special_code == SUBTOTALS_SPECIAL_CODE) { + $desc = $line->desc; + $depth = -$line->qty; + } + } + + $subtotal_options = array(); + + foreach (SupplierProposal::$SUBTOTAL_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Insert line + if (isset($desc) && isset($depth)) { + $result = $object->addSubtotalLine($langs, $desc, (int) $depth, $subtotal_options); + } else { + $object->errors[] = $langs->trans("CorrespondingTitleNotFound"); + } + + if (isset($result) && $result >= 0) { + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + header('Location: '.$_SERVER["PHP_SELF"].'?id='.$id); + exit(); } elseif ($action == 'addline' && $usercancreate) { $langs->load('errors'); $error = 0; @@ -982,6 +1123,91 @@ if (empty($reshook)) { setEventMessages($object->error, $object->errors, 'errors'); } } + } elseif ($action == 'updatetitleline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) { + // Handling updating a title line for subtotals module + + $langs->load('subtotals'); + + $desc = GETPOST('line_desc', 'alphanohtml') ?? $langs->trans("Title"); + $depth = GETPOSTINT('line_depth') ?? 1; + + $subtotal_options = array(); + + foreach (SupplierProposal::$TITLE_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Update line + $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options); + + if ($result >= 0) { + if ($result == 0) { + setEventMessages($object->error, $object->errors, 'warnings'); + } + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } elseif ($action == 'updatesubtotalline' && GETPOSTISSET("save") && $usercancreate && !GETPOST('cancel', 'alpha')) { + // Handling updating a subtotal line for subtotals module + + $langs->load('subtotals'); + + $desc = GETPOST('line_desc', 'alphanohtml'); + $depth = GETPOSTINT('line_depth'); + + $subtotal_options = array(); + + foreach (SupplierProposal::$SUBTOTAL_OPTIONS as $option) { + $value = GETPOST($option, 'alphanohtml'); + if ($value) { + $subtotal_options[$option] = $value == 'on' ? 1 : $value; + } + } + + // Update line + $result = $object->updateSubtotalLine($langs, GETPOSTINT('lineid'), $desc, $depth, $subtotal_options); + + if ($result > 0) { + $ret = $object->fetch($object->id); // Reload to get new records + $object->fetch_thirdparty(); + + if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) { + // Define output language + $outputlangs = $langs; + $newlang = GETPOST('lang_id', 'alpha'); + if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + } + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } } elseif ($action == 'updateline' && $usercancreate && GETPOST('save') == $langs->trans("Save")) { // Update a line within proposal $vat_rate = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); @@ -1631,6 +1857,24 @@ if ($action == 'create') { } elseif ($action == 'ask_deleteline') { // Confirmation delete product/service line $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteProductLine'), $langs->trans('ConfirmDeleteProductLine'), 'confirm_deleteline', '', 0, 1); + } elseif ($action == 'ask_subtotal_deleteline') { + // Confirmation de la suppression d'une ligne subtotal + $langs->load("subtotals"); + $title = "DeleteSubtotalLine"; + $question = "ConfirmDeleteSubtotalLine"; + if (GETPOST('type') == 'title') { + $formconfirm = [ + [ + 'type' => 'checkbox', + 'name' => 'deletecorrespondingsubtotalline', + 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), + 'value' => 0, + ], + ]; + $title = "DeleteTitleLine"; + $question = "ConfirmDeleteTitleLine"; + } + $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1); } elseif ($action == 'validate') { // Confirm validate askprice $error = 0; @@ -1659,6 +1903,18 @@ if ($action == 'create') { $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ValidateAsk'), $text, 'confirm_validate', '', 0, 1); } } + // Subtotal line form + if ($action == 'add_title_line') { + $langs->load('subtotals'); + $type = 'title'; + $depth_array = $object->getPossibleLevels($langs); + require dol_buildpath('/core/tpl/subtotal_create.tpl.php'); + } elseif ($action == 'add_subtotal_line') { + $langs->load('subtotals'); + $type = 'subtotal'; + $titles = $object->getPossibleTitles(); + require dol_buildpath('/core/tpl/subtotal_create.tpl.php'); + } // Call Hook formConfirm $parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid); @@ -1964,11 +2220,15 @@ if ($action == 'create') { - + '; if (!empty($conf->use_javascript_ajax) && $object->status == SupplierProposal::STATUS_DRAFT) { - include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php'; + if (isModEnabled('subtotals')) { + include DOL_DOCUMENT_ROOT . '/core/tpl/subtotal_ajaxrow.tpl.php'; + } else { + include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php'; + } } print '
'; @@ -2050,6 +2310,30 @@ if ($action == 'create') { // modified by hook if (empty($reshook)) { if ($action != 'statut' && $action != 'editline') { + // Subtotal + if ($object->status == SupplierProposal::STATUS_DRAFT && isModEnabled('subtotals') && getDolGlobalString('SUBTOTAL_TITLE_'.strtoupper($object->element))) { + $langs->load('subtotals'); + + $url_button = array(); + + $url_button[] = array( + 'lang' => 'subtotals', + 'enabled' => $object->status == SupplierProposal::STATUS_DRAFT, + 'perm' => (bool) $usercancreate, + 'label' => $langs->trans('AddTitleLine'), + 'url' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'action' => 'add_title_line'], true) + ); + + $url_button[] = array( + 'lang' => 'subtotals', + 'enabled' => $object->status == SupplierProposal::STATUS_DRAFT, + 'perm' => (bool) $usercancreate, + 'label' => $langs->trans('AddSubtotalLine'), + 'url' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'action' => 'add_subtotal_line'], true) + ); + + print dolGetButtonAction('', $langs->trans('Subtotal'), 'default', $url_button, '', true); + } // Validate if ($object->status == SupplierProposal::STATUS_DRAFT && $object->total_ttc >= 0 && count($object->lines) > 0 && $usercanvalidate) { if (count($object->lines) > 0) { diff --git a/htdocs/supplier_proposal/class/supplier_proposal.class.php b/htdocs/supplier_proposal/class/supplier_proposal.class.php index 819657f9c95..f57765007de 100644 --- a/htdocs/supplier_proposal/class/supplier_proposal.class.php +++ b/htdocs/supplier_proposal/class/supplier_proposal.class.php @@ -44,13 +44,13 @@ require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php'; require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php'; - +require_once DOL_DOCUMENT_ROOT.'/subtotals/class/commonsubtotal.class.php'; /** * Class to manage price ask supplier */ class SupplierProposal extends CommonObject { - use CommonIncoterm; + use CommonIncoterm, CommonSubtotal; /** * @var string ID to identify managed object