Collections prices

Les sous-collections de prix couvrent l’ensemble du cycle tarifaire : prix catalogue, prix d’achat, conditions appliquées, prix nets résultants, kits, promotions, ristournes et dépréciations.

Collection public_prices

Prix publics (tarif catalogue) par article.

Métadonnées

Tenant scope

Partiel (company et supplier optionnels)

Soft delete

Oui (is_active)

Historisation

Oui : plusieurs versions par triplet article + company + supplier

Index principaux

article + company + supplier, article + is_active, company + is_active, article + company + supplier + is_active + valid_from + created_at

Version en vigueur

Un même triplet article + company + supplier porte plusieurs lignes : chaque mise à jour archive la précédente et insère une nouvelle version. Lecture, affichage et calculs ne retiennent qu’une seule ligne par triplet, sélectionnée ainsi :

  1. is_active: true ;

  2. valid_from absent ou ⇐ maintenant ;

  3. valid_to absent ou >= maintenant ;

  4. à égalité, la plus récente : tri valid_from décroissant puis created_at décroissant.

Un tarif propre à la société prime sur un tarif global (company: null) : le tri applique company décroissant avant la récence.

Les helpers partagés currentPublicPriceFilter, CURRENT_PRICE_SORT et keepMostRecentBy (src/shared/models/prices/public-price.query.ts) sont la source unique de cette règle — ne pas réécrire le prédicat dans un service.

Le champ public_price porté par les lignes de document (devis, factures, BL) est un instantané figé à la création et n’est jamais recalculé : un document émis conserve le tarif appliqué à ce moment-là.

Schéma

interface PublicPrice {
  _id: ObjectId;
  article: ObjectId;
  company?: ObjectId;
  supplier?: ObjectId;
  currency: string;
  value: number;
  deposit_price?: number;
  valid_from?: Date;
  valid_to?: Date;
  is_active: boolean;          // défaut : true
  expired: boolean;            // défaut : false — référence retirée du catalogue fournisseur
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

expired et is_active ne disent pas la même chose :

  • is_active: false — version de tarif remplacée par une plus récente (archivage) ;

  • valid_to dépassé — tarif arrivé au terme de sa fenêtre de validité ;

  • expired: true — le fournisseur a retiré la référence de son catalogue. La ligne est conservée pour l’historique mais ne correspond plus à un article commandable.

expired est un tag informatif : il n’entre pas dans la sélection de la version en vigueur. Une référence retirée continue d’exposer son dernier prix connu (le stock restant doit pouvoir être écoulé) ; seul l’affichage la signale.

Collection purchase_prices

Prix d’achat par article, tenant et fournisseur.

Métadonnées

Tenant scope

Oui (company requis)

Soft delete

Oui (is_active)

Index principaux

article + company + supplier, article + is_active, company + supplier

Schéma

interface PurchasePrice {
  _id: ObjectId;
  article: ObjectId;
  company: ObjectId;
  supplier: ObjectId;
  currency: string;
  value: number;
  valid_from?: Date;
  valid_to?: Date;
  is_active: boolean;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection price_conditions

Conditions tarifaires (remise, coefficient, combiné) appliquées selon la source (archétype, marque, fournisseur).

Métadonnées

Tenant scope

Oui (company requis)

Soft delete

Oui (is_active)

Index principaux

company + type + priority desc, company + supplier, company + customer, source + archetype, source + brand, tier

Schéma

interface PriceCondition {
  _id: ObjectId;
  company: ObjectId;
  supplier?: ObjectId;
  customer?: ObjectId;
  customer_model: "Company" | "Client";
  type: "purchase" | "sale";
  priority: number;            // 0–100
  source: "archetype" | "brand" | "supplier";
  archetype?: ObjectId;
  brand?: ObjectId;
  tier?: ObjectId;
  condition_type: "discount" | "coefficient" | "combined";
  discount_rate?: number;
  coefficient?: number;
  is_active: boolean;
  valid_from?: Date;
  valid_to?: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection net_prices

Prix nets résultant de l’application des conditions tarifaires.

Métadonnées

Tenant scope

Oui (company requis)

Soft delete

Oui (is_active)

Index principaux

company + article, company + article + customer, company + article + tier, article + is_active

Schéma

interface NetPrice {
  _id: ObjectId;
  company: ObjectId;
  article: ObjectId;
  supplier?: ObjectId;
  customer?: ObjectId;
  customer_model: "Company" | "Client";
  tier?: ObjectId;
  currency: string;
  value: number;
  source?: string;
  is_active: boolean;
  valid_from?: Date;
  valid_to?: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection kits

Kits regroupant plusieurs articles avec un prix global unique.

Schéma

interface Kit {
  _id: ObjectId;
  company: ObjectId;
  name: string;
  description?: string;
  articles: Array<{
    article: ObjectId;
    quantity: number;         // min : 1
  }>;                          // max 10 articles par kit
  currency: string;
  value: number;
  is_active: boolean;
  valid_from?: Date;
  valid_to?: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection promotions

Promotions applicables à un article, archétype, relation ou tier.

Métadonnées

Tenant scope

Oui (company requis)

Soft delete

Oui (is_active)

Index principaux

company + is_active, article + is_active, archetype + is_active, tier, relation

Schéma

interface Promotion {
  _id: ObjectId;
  company: ObjectId;
  article?: ObjectId;
  archetype?: ObjectId;
  relation?: ObjectId;         // ref: CompanyRelation
  tier?: ObjectId;
  name: string;
  description?: string;
  type: "Percentage" | "FixedAmount" | "Bundle" | "BuyXGetY";
  discount_rate?: number;
  amount_value?: number;
  buy_quantity?: number;
  get_quantity?: number;
  min_quantity?: number;
  is_active: boolean;
  valid_from?: Date;
  valid_to?: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection promotion_assets

Assets médias associés à une promotion.

Schéma

interface PromotionAsset {
  _id: ObjectId;
  promotion: ObjectId;
  type: "PDF" | "Image" | "Video" | "URL";
  url: string;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection rebates

Ristournes associées à une relation fournisseur ou client.

Métadonnées

Tenant scope

Oui (via relation)

Soft delete

Oui (is_active)

Index principaux

relation + is_active, source + archetype, source + brand

Schéma

interface Rebate {
  _id: ObjectId;
  relation: ObjectId;          // ref: CompanyRelation
  description?: string;
  type: "Flat" | "Volume" | "Turnover" | "TripleNet";
  threshold?: number;
  source: "archetype" | "brand" | "supplier";
  archetype?: ObjectId;
  brand?: ObjectId;
  rate: number;
  level: number;               // défaut : 1
  turnover_achieved?: number;
  volume_achieved?: number;
  rebate_amount?: number;
  is_active: boolean;
  valid_from?: Date;
  valid_to?: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection rebate_settlements

Règlements de ristournes (avoirs, paiements, écritures comptables).

Schéma

interface RebateSettlement {
  _id: ObjectId;
  relation: ObjectId;
  rebate: ObjectId;
  settlement_type: "CreditNote" | "Payment" | "AccountingEntry";
  settlement_amount: number;
  currency: string;
  reference_doc?: string;
  settlement_date: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Collection depreciations

Dépréciations de prix (réductions liées à l’ancienneté ou à des conditions spécifiques).

Métadonnées

Tenant scope

Oui (company requis)

Soft delete

Oui (is_active)

Index principaux

company + supplier + is_active, article + is_active, archetype, tier, relation

Schéma

interface Depreciation {
  _id: ObjectId;
  company: ObjectId;
  supplier: ObjectId;
  article: ObjectId;
  archetype?: ObjectId;
  relation?: ObjectId;
  tier?: ObjectId;
  type: "Flat" | "Percentage" | "AgeBased";
  discount_rate?: number;
  amount_value?: number;
  age_threshold_days?: number;
  description?: string;
  is_active: boolean;
  valid_from?: Date;
  valid_to?: Date;
  created_by?: ObjectId;
  updated_by?: ObjectId;
  created_at: Date;
  updated_at: Date;
}

Règles d’intégrité

  1. L’import CSV de prix d’achat crée ou remplace les prix existants pour le triplet article + company + supplier.

  2. L’import CSV et la création de prix publics sont versionnés : la version précédente du triplet est archivée (is_active: false, valid_to = date de prise d’effet) et une nouvelle ligne est insérée. Un import programmé (valid_from futur) borne l’ancienne version sans la désactiver, pour ne pas laisser l’article sans tarif dans l’intervalle.

  3. Les PriceCondition sont classées par priority (0–100) ; la plus haute prime.

  4. Un kit peut contenir au maximum 10 articles.

  5. Les promotions BuyXGetY requièrent buy_quantity et get_quantity.

Features qui consomment ces collections

  • Tarification catalogue (PRI)

  • Calcul du prix net dans la recherche articles (CAT)

  • Ristournes fournisseurs (SUP)

  • Promotions clients (SAL)