Aller au contenu

Module Catalogue main-d'oeuvre

Objectif

Le module Catalogue main-d'oeuvre separe les prestations de main-d'oeuvre du catalogue produit standard.

Il couvre :

  • un type de catalogue dedie main_oeuvre ;
  • une navigation de premier niveau dans le catalogue ;
  • des familles et categories metier ;
  • des prestations sans fournisseur ni image obligatoire ;
  • des options configurables ;
  • le choix entre options multipliees par quantite et options forfaitaires ;
  • l'utilisation dans les devis, factures et avoirs ;
  • le rendu PDF correct des lignes et options.

Emplacement du code

Fichiers principaux :

app/controllers/CatalogueController.php
app/models/CatalogueModel.php
app/models/CatalogueTaxonomyModel.php
app/views/catalogue.php
app/views/layout/catalogue.php
app/views/partials/catalogue.php
app/views/partials/modals/create-product-form-shared.php
app/views/partials/modals/modifier-produit-pour-catalogue.php
app/views/partials/modals/choisir-produit-pour-devis.php
app/views/partials/modals/price-simulator-modal.php
app/helpers/pdfDocumentGenerator.php
app/models/BaseDocumentModel.php

Migrations :

app/bd/catalogue-main-oeuvre.sql
app/bd/catalogue-taxonomies.sql

Tests de garde identifies :

tests/catalogue_main_oeuvre_top_tab_test.php
tests/catalogue_toolbar_layout_test.php
tests/main_oeuvre_option_quantity_multiplier_test.php

Vue d'ensemble architecture

Catalogue UI
  |
  | catalogue_kind=main_oeuvre
  v
CatalogueController
  |
  v
CatalogueModel
  - catalogue
  - catalogue_options
  - catalogue_taxonomy_families
  - catalogue_taxonomy_categories
  |
  v
Documents
  - devis
  - factures
  - avoirs
  - PDF

Le point cle est catalogue_kind. Une ligne catalogue peut etre :

  • standard : produit/prestation du catalogue classique ;
  • main_oeuvre : prestation de main-d'oeuvre dediee.

Schema de donnees

Catalogue

Migration :

  • app/bd/catalogue-main-oeuvre.sql

Ajouts sur catalogue :

catalogue_kind ENUM('standard','main_oeuvre') NOT NULL DEFAULT 'standard'
idx_catalogue_kind
taxonomy_family_id
taxonomy_category_id
idx_catalogue_taxonomy

catalogue_kind isole les prestations main-d'oeuvre sans creer une table separee.

Options

Ajouts sur les options :

catalogue_options.image_option VARCHAR(500) NULL DEFAULT NULL
catalogue_options.multiply_with_quantity TINYINT(1) NOT NULL DEFAULT 1
devis_ligne_options.multiply_with_quantity TINYINT(1) NOT NULL DEFAULT 1
facture_ligne_options.multiply_with_quantity TINYINT(1) NOT NULL DEFAULT 1

multiply_with_quantity est le flag critique :

  • 1 : l'option suit la quantite de la ligne ;
  • 0 : l'option est forfaitaire et ne doit pas etre remultipliee par la quantite.

Le defaut 1 conserve le comportement historique.

Taxonomies

Migration :

  • app/bd/catalogue-taxonomies.sql

Tables :

catalogue_taxonomy_families
catalogue_taxonomy_categories

Colonnes structurantes :

  • societe_id ;
  • scope : main_oeuvre ou ouvrage ;
  • name ;
  • sort_order ;
  • active.

Contraintes importantes :

  • unicite famille : societe_id, scope, name ;
  • unicite categorie : family_id, name ;
  • suppression cascade des categories quand une famille est supprimee.

Ces taxonomies sont partagees par la logique main-d'oeuvre et ouvrages, mais chaque ligne porte un scope.

Controleur

Fichier :

  • app/controllers/CatalogueController.php

Liste

Le controleur lit catalogue_kind depuis la query string.

Regle de normalisation :

catalogue_kind=main_oeuvre -> main_oeuvre
autre valeur ou absence     -> standard

Cette regle evite que le catalogue standard affiche la main-d'oeuvre par defaut.

Le controleur fournit aussi les taxonomies main-d'oeuvre a la vue :

listByScope('main_oeuvre')

Creation

Endpoint logique :

CatalogueController::add()

Quand catalogue_kind = main_oeuvre, le controleur force :

  • type = prestation ;
  • marque = null ;
  • reference_fournisseur = null ;
  • fournisseur_id = null ;
  • recherche_fournisseur = '' ;
  • image ignoree meme si un fichier est envoye.

Les champs obligatoires restent :

nom
prix_achat_ht
prix_vente_ht
marge_value
type
unite

Le controleur decode aussi options si le payload en contient. Les options vides sont filtrees selon leur nom.

Edition

Endpoint logique :

CatalogueController::update()

L'edition reprend la meme normalisation catalogue_kind. Si le produit est une main-d'oeuvre, les champs fournisseur/marque/image sont neutralises et les options peuvent etre remplacees.

Taxonomies

Endpoints logique :

taxonomyList()
taxonomyCreateFamily()
taxonomyCreateCategory()

Ces actions deleguent a CatalogueTaxonomyModel.

Modele catalogue

Fichier :

  • app/models/CatalogueModel.php

Filtrage

getPaginatedCatalogue() accepte :

catalogue_kind
family_id
category_id
prix_min
prix_max
type
search
created_by
date_debut
date_fin

Les filtres famille/categorie ne s'appliquent que pour catalogue_kind = main_oeuvre.

Le modele joint :

catalogue_taxonomy_families
catalogue_taxonomy_categories

en filtrant explicitement scope = 'main_oeuvre'.

Lecture d'une fiche

getById($id, true) peut charger :

  • la ligne catalogue ;
  • les taxonomies ;
  • les options ;
  • options_count.

Les options lues contiennent :

id
catalogue_id
nom
description
signe
unite
valeur
obligatoire
active
image_option
multiply_with_quantity

Remplacement des options

La methode interne de remplacement supprime les options existantes du catalogue puis insere les nouvelles.

Regles appliquees a chaque option :

  • nom vide : option ignoree ;
  • signe limite a plus ou moins ;
  • unite limitee a euro ou pourcent ;
  • valeur forcee a un nombre positif ;
  • obligatoire converti en booleen ;
  • active converti en booleen ;
  • multiply_with_quantity conserve le defaut historique si absent.

Taxonomy model

Fichier :

  • app/models/CatalogueTaxonomyModel.php

Responsabilites :

  • lister familles et categories actives par scope ;
  • creer/reactiver une famille ;
  • creer/reactiver une categorie ;
  • normaliser un couple famille/categorie ;
  • verifier que la famille et la categorie appartiennent a la societe active.

Regle de scope :

scope=ouvrage -> ouvrage
autre valeur  -> main_oeuvre

normalizePair() retourne null pour la famille et la categorie si la famille est invalide. Si la famille est valide mais la categorie invalide, seule la categorie est remise a null.

Point important : le modele utilise $_SESSION['active_societe_id']. Les tests unitaires purs doivent donc initialiser la session ou passer par des tests de presence de code.

UI et navigation

La main-d'oeuvre est promue en onglet principal du catalogue.

Test de garde :

  • tests/catalogue_main_oeuvre_top_tab_test.php

Ce test verifie que :

  • layout/catalogue.php contient id="main-oeuvre-btn" ;
  • l'onglet pointe vers catalogue_kind=main_oeuvre ;
  • l'onglet main-d'oeuvre est place avant Ouvrages ;
  • la main-d'oeuvre n'apparait plus comme sous-onglet du catalogue standard ;
  • une ancienne URL Prestation sans catalogue_kind garde un comportement compatible.

La toolbar catalogue a aussi ete simplifiee.

Test de garde :

  • tests/catalogue_toolbar_layout_test.php

Il verifie que les vues catalogue, ouvrages et bibliotheques utilisent une structure :

catalogue-toolbar
catalogue-toolbar-title-row
catalogue-toolbar-search-row
catalogue-toolbar-actions-row

Interaction avec devis, factures et PDF

La main-d'oeuvre est selectionnable depuis les modales de document via :

  • choisir-produit-pour-devis.php ;
  • price-simulator-modal.php.

Le simulateur doit distinguer :

  • les options unitaires ;
  • les options forfaitaires.

Test de garde :

  • tests/main_oeuvre_option_quantity_multiplier_test.php

Ce test verifie notamment que :

  • la migration ajoute multiply_with_quantity ;
  • le modele lit et sauvegarde ce flag ;
  • les formulaires creation/edition exposent le switch ;
  • le simulateur transmet multiply_with_quantity aux lignes document ;
  • le PDF ne remultiplie pas une option forfaitaire ;
  • BaseDocumentModel persiste le flag dans les options de ligne.

Point critique : si une option est forfaitaire, le prix unitaire transmis au document doit integrer correctement le total de ligne sans provoquer une double multiplication dans le PDF.

Invariants de reprise

  • catalogue_kind est la separation fonctionnelle centrale. Ne pas se baser uniquement sur type = prestation.
  • La main-d'oeuvre ne doit pas conserver de fournisseur, marque, reference fournisseur ou image produit.
  • Les taxonomies doivent toujours etre verifiees par societe_id et scope.
  • Une categorie ne doit etre acceptee que si elle appartient a la famille selectionnee.
  • multiply_with_quantity doit etre transporte du catalogue vers les lignes document puis jusqu'au PDF.
  • Les anciennes donnees doivent rester compatibles grace aux valeurs par defaut standard et multiply_with_quantity = 1.

Points d'attention pour reprise

  • La creation et l'edition remplacent les options en bloc. Si un futur besoin demande l'historique option par option, il faudra changer ce contrat.
  • Les migrations utilisent ADD COLUMN IF NOT EXISTS dans catalogue-taxonomies.sql, mais pas partout dans catalogue-main-oeuvre.sql. Verifier l'idempotence avant execution sur une base deja partiellement migree.
  • Les tests identifies sont surtout des tests de presence de code et d'invariants structurels. Ils protegent les regressions grossieres, mais ne remplacent pas un test bout-en-bout avec base SQL.
  • Les fonctions de taxonomie dependent de la societe active en session. Une reprise en CLI ou API devra fournir ce contexte explicitement.
  • Le scope ouvrage partage les tables de taxonomie. Une modification de schema pour la main-d'oeuvre peut donc toucher les ouvrages.