Ajout des "Total Produit ( %)" et "Total Service ( %)" et leur pourcentage respectif

Bonjour à tous,

J’ai apporté des modifications à mon Dolibarr et je partage ici pour ceux que ça intéresse.
Il m’est utile d’avoir un visu de la part Service/Produit sur un devis.
Les multi-devises ne sont pas gérées

Voici la procédure en version 21 pour avoir le même rendu.

Pour la page devis :

Dans le fichier :

/htdocs/comm/propal/card.php

Ligne 2967, AVANT :

		print '<tr>';
		print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';

AJOUTER :

        // BEGIN Custom: Totaux produits/services avec pourcentage
        // -----------------------------------------------------------
        // Compute and display product/service totals with percentage of the global amount
        // Calcul et affichage des totaux produits/services avec pourcentage du montant global

        $total_products = 0;
        $total_services = 0;
        $total_products_ttc = 0;
        $total_services_ttc = 0;

        foreach ($object->lines as $line) {
            if ($line->fk_product_type == 0) {
                $total_products += $line->total_ht;
                $total_products_ttc += $line->total_ttc;
            } elseif ($line->fk_product_type == 1) {
                $total_services += $line->total_ht;
                $total_services_ttc += $line->total_ttc;
            }
        }

        // Determine if we show HT or TTC (same logic as standard totals)
        $show_ht = true;
        if (!empty($conf->global->PROPOSAL_TOTAL_SHOW_TTC)) {
            $show_ht = false;
        }
        // On se base sur l'affichage principal
        if ($show_ht) {
            $total_general = $object->total_ht;
        } else {
            $total_general = $object->total_ttc;
        }

        // Avoid division by zero
        $percent_products = $total_general != 0 ? round(100 * ($show_ht ? $total_products : $total_products_ttc) / $total_general, 2) : 0;
        $percent_services = $total_general != 0 ? round(100 * ($show_ht ? $total_services : $total_services_ttc) / $total_general, 2) : 0;

        // Affichage dans le tableau des totaux
        print '<tr>';
        print '<td class="titlefieldmiddle">'.$langs->trans("TotalProducts").' ('.$percent_products.'%)</td>';
        print '<td class="nowrap amountcard right">'.price($show_ht ? $total_products : $total_products_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</td>';
        if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
            // Multidevise non gérée pour le moment, éventuel à ajouter ici
            print '<td class="nowrap amountcard right"></td>';
        }
        print '</tr>';

        print '<tr>';
        print '<td class="titlefieldmiddle">'.$langs->trans("TotalServices").' ('.$percent_services.'%)</td>';
        print '<td class="nowrap amountcard right">'.price($show_ht ? $total_services : $total_services_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</td>';
        if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
            // Multidevise non gérée pour le moment, éventuel à ajouter ici
            print '<td class="nowrap amountcard right"></td>';
        }
        print '</tr>';
        // END Custom: Totaux produits/services avec pourcentage
        // -----------------------------------------------------------

Dans le fichier (si non ajouté par défaut sera affiché TotalProducts et TotalServices) :

htdocs/langs/fr_FR/bills.lang

AJOUTER :

TotalProducts=Total des produits
TotalServices=Total des services

Dans le fichier (pas indispensable, c’est pour la version anglaise de base) :

htdocs/langs/en_US/bills.lang

AJOUTER :

TotalProducts=Total products
TotalServices=Total services

Pour la page facture :
Dans le fichier :

htdocs/compta/facture/card.php

Ligne 5092, AVANT :

		print '<tr>';
		// Amount HT
		print '<td class="titlefieldmiddle">' . $langs->trans('AmountHT') . '</td>';

AJOUTER :

		// BEGIN Custom: Totaux produits/services avec pourcentage
		// -----------------------------------------------------------
		// Compute and display product/service totals with percentage of the global amount
		// Calcul et affichage des totaux produits/services avec pourcentage du montant global

		$total_products = 0;
		$total_services = 0;
		$total_products_ttc = 0;
		$total_services_ttc = 0;

		foreach ($object->lines as $line) {
			if ($line->fk_product_type == 0) {
				$total_products += $line->total_ht;
				$total_products_ttc += $line->total_ttc;
			} elseif ($line->fk_product_type == 1) {
				$total_services += $line->total_ht;
				$total_services_ttc += $line->total_ttc;
			}
		}

		// Determine if we show HT or TTC (same logic as standard totals)
		$show_ht = true;
		if (!empty($conf->global->FACTURE_TOTAL_SHOW_TTC)) {
			$show_ht = false;
		}
		// On se base sur l'affichage principal
		if ($show_ht) {
			$total_general = $object->total_ht;
		} else {
			$total_general = $object->total_ttc;
		}

		// Avoid division by zero
		$percent_products = $total_general != 0 ? round(100 * ($show_ht ? $total_products : $total_products_ttc) / $total_general, 2) : 0;
		$percent_services = $total_general != 0 ? round(100 * ($show_ht ? $total_services : $total_services_ttc) / $total_general, 2) : 0;

		// Affichage dans le tableau des totaux
		print '<tr>';
		print '<td class="titlefieldmiddle">'.$langs->trans("TotalProducts").' ('.$percent_products.'%)</td>';
		print '<td class="nowrap amountcard right">'.price($show_ht ? $total_products : $total_products_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</td>';
		if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
			// Multidevise non gérée pour le moment, à ajouter si besoin
			print '<td class="nowrap amountcard right"></td>';
		}
		print '</tr>';

		print '<tr>';
		print '<td class="titlefieldmiddle">'.$langs->trans("TotalServices").' ('.$percent_services.'%)</td>';
		print '<td class="nowrap amountcard right">'.price($show_ht ? $total_services : $total_services_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</td>';
		if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
			print '<td class="nowrap amountcard right"></td>';
		}
		print '</tr>';
		// END Custom: Totaux produits/services avec pourcentage
		// -----------------------------------------------------------

1 « J'aime »

Bonjour @Fafab
Dans domaine appliquez vous ce besoin ?
Il serait intéressant de le proposer sur GitHub si c’est utile à certains métiers ou domaines
Merci pour votre partage en tout cas :wink:
@+

La ventilation entre main d’œuvre et produit sur une facture est selon le corps de métier et la prestation dans une certaine proportion. S’en éloigné peu mettre la puce à l’oreille pour réviser sa façon de calculer ses marges et optimiser les cotisation URSSAF.

Et plus basiquement, quelqu’un en micro-entreprise non assujetti à la TVA qui a peu de facture, peu utiliser ces valeurs pour ces déclarations. C’est à la base ce qui m’a amené à faire cette modification.

1 « J'aime »

Je ne peux pas éditer mon propre message, le code bugué va donc perdurer en tête de topic.

Petites modifications car il y a des données mal récupérées dans la base de donnée.

		// BEGIN Custom: Totaux produits/services avec pourcentage
		// -----------------------------------------------------------
		// Compute and display product/service totals with percentage of the global amount
		// Calcul et affichage des totaux produits/services avec pourcentage du montant global

		// ------------------- Fonction utilitaire -------------------
		/**
		 * Corrige une erreur interne de mapping quand fk_product_type a été
		 * déterminé par une saisie libre du service/produit
		 * Détermine le type de la ligne (0=produit, 1=service).
		 * Retourne null si le type ne peut pas être déterminé.
		 * @param object $line
		 * @return int|null
		 */
		function getLineProductType($line) {
			if (isset($line->fk_product_type) && $line->fk_product_type !== '' && $line->fk_product_type !== null) {
				return (int)$line->fk_product_type;
			}
			if (isset($line->product_type) && $line->product_type !== '' && $line->product_type !== null) {
				return (int)$line->product_type;
			}
			return null; // Type indéterminé
		}
		// ------------------- /Fonction utilitaire -------------------


		$total_products = 0;
		$total_services = 0;
		$total_products_ttc = 0;
		$total_services_ttc = 0;
		$unknown_type_lines = [];

		foreach ($object->lines as $idx => $line) {
			$type = getLineProductType($line);

			if ($type === 0) {
				$total_products += $line->total_ht;
				$total_products_ttc += $line->total_ttc;
			}
			elseif ($type === 1) {
				$total_services += $line->total_ht;
				$total_services_ttc += $line->total_ttc;
			}
			else {
				// Ligne de type indéterminé : on remonte pour debug
				$unknown_type_lines[] = $idx + 1; // +1 car affichage humain
				// Option : on peux aussi logger ici ou afficher un avertissement visuel dans le tableau
			}
		}

		// Determine if we show HT or TTC (same logic as standard totals)
		$show_ht = true;
		if (!empty($conf->global->PROPOSAL_TOTAL_SHOW_TTC)) {
			$show_ht = false;
		}
		// On se base sur l'affichage principal
		if ($show_ht) {
			$total_general = $object->total_ht;
		} else {
			$total_general = $object->total_ttc;
		}

		// Avoid division by zero
		$percent_products = $total_general != 0 ? round(100 * ($show_ht ? $total_products : $total_products_ttc) / $total_general, 2) : 0;
		$percent_services = $total_general != 0 ? round(100 * ($show_ht ? $total_services : $total_services_ttc) / $total_general, 2) : 0;

		// Affichage dans le tableau des totaux, même style que les autres lignes
		print '<tr>';
		print '<td class="titlefieldmiddle">'.$langs->trans("TotalProducts").' ('.$percent_products.'%)</td>';
		print '<td class="nowrap amountcard right">'.price($show_ht ? $total_products : $total_products_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</td>';
		if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
			// Multidevise non gérée pour le moment, à ajouter si besoin
			print '<td class="nowrap amountcard right"></td>';
		}
		print '</tr>';

		print '<tr>';
		print '<td class="titlefieldmiddle">'.$langs->trans("TotalServices").' ('.$percent_services.'%)</td>';
		print '<td class="nowrap amountcard right">'.price($show_ht ? $total_services : $total_services_ttc, 0, $langs, 1, -1, -1, $conf->currency).'</td>';
		if (isModEnabled("multicurrency") && ($object->multicurrency_code && $object->multicurrency_code != $conf->currency)) {
			print '<td class="nowrap amountcard right"></td>';
		}
		print '</tr>';

		// Affiche un avertissement si lignes inconnues
		if (!empty($unknown_type_lines)) {
			print '<tr><td colspan="3" style="color:red;"><strong> '.count($unknown_type_lines).' ligne(s) avec type produit/service indéterminé (ligne(s) : '.implode(', ', $unknown_type_lines).'). Vérifiez vos données ou le mapping.</strong></td></tr>';
		}
		// END Custom: Totaux produits/services avec pourcentage
		// -----------------------------------------------------------