AgentSkillsCN

ssii-business-rules

IT服务公司的业务规则与KPI标准。适用于“business rules”、“kpi”、“occupancy rate”、“margin”、“staffing”、“intercontract”、“forecast”、“utilization”等场景。

SKILL.md
--- frontmatter
name: ssii-business-rules
description: Règles métier et KPIs pour société de service IT. Use when "business rules", "kpi", "taux occupation", "marge", "staffing", "intercontrat", "forecast", "utilization".
allowed-tools: Read, Grep, Glob

Règles Métier SSII/ESN

Purpose

Fournir les connaissances métier essentielles pour développer des fonctionnalités business dans une application de gestion de consultants.

Glossaire Métier

TJM (Taux Journalier Moyen)

Prix facturé au client par jour de prestation d'un consultant.

  • Fourchette: 300€ - 1000€/jour
  • Facteurs: seniority, compétences rares, marché, client

Taux d'Occupation (Utilization Rate)

code
Taux = (Jours Facturables / Jours Ouvrés) × 100

Exemple: 18 jours facturés sur 22 jours ouvrés = 81.8%

Benchmarks:

  • < 70%: Sous-activité, problème commercial
  • 75-85%: Objectif optimal
  • 90%: Surcharge, risque burnout

Intercontrat (Bench)

Période où un consultant n'est pas en mission cliente.

Causes:

  • Fin de mission sans enchaînement
  • Rupture de contrat client
  • Période de formation

Coûts:

  • Salaire versé sans revenu généré
  • Impact direct sur la rentabilité

Gestion:

  • Objectif: < 15 jours moyens
  • Utilisation: formation, avant-vente, R&D interne

Marge Brute

code
Marge Brute = CA - Coûts Salariaux
Taux de Marge = (Marge / CA) × 100

Objectif: 30-40%

Facteurs d'optimisation:

  • TJM élevé
  • Coûts salariaux maîtrisés
  • Taux d'occupation élevé

Pipeline Commercial

Ensemble des opportunités commerciales en cours.

Étapes:

  1. Lead (contact initial)
  2. Prospect (besoin qualifié)
  3. Proposition (offre commerciale)
  4. Négociation
  5. Signé / Perdu

Taux de conversion typique: 20-30%

Règles Métier Critiques

Règle 1: Unicité Mission Active

Un consultant ne peut avoir qu'UNE SEULE mission active à la fois.

Validation:

typescript
// Vérifier pas de chevauchement avant création
const overlap = await prisma.mission.findFirst({
  where: {
    consultantId: mission.consultantId,
    OR: [
      {
        dateDebut: { lte: mission.dateFin },
        dateFin: { gte: mission.dateDebut }
      }
    ]
  }
});

if (overlap) {
  throw new Error('Consultant déjà en mission sur cette période');
}

Règle 2: Cohérence des Dates

dateFin doit toujours être postérieure à dateDebut.

Validation:

typescript
if (mission.dateFin <= mission.dateDebut) {
  throw new Error('Date de fin doit être après date de début');
}

Règle 3: Préservation Historique

Les missions terminées ne doivent jamais être supprimées.

Raisons:

  • Calcul du CA historique
  • Reporting client
  • Audit et conformité

Solution: Soft delete ou statut "TERMINEE"

Règle 4: Calcul du Statut en Temps Réel

Le statut d'un consultant est calculé basé sur ses missions actives, pas stocké.

Logique:

typescript
function calculateStatus(consultant: ConsultantWithMissions): string {
  const now = new Date();
  const activeMission = consultant.missions.find(m =>
    m.dateDebut <= now && now <= m.dateFin
  );

  return activeMission ? 'EN_MISSION' : consultant.statut;
}

Règle 5: TJM Cohérent

Le TJM appliqué sur une mission doit être cohérent avec le TJM du consultant.

Warning si:

  • TJM appliqué < 80% du TJM consultant
  • TJM appliqué > 150% du TJM consultant
typescript
if (mission.tjmApplique < consultant.tjm * 0.8) {
  console.warn('TJM appliqué anormalement bas');
}

KPIs Essentiels

1. Taux d'Occupation Global

typescript
async function calculateTauxOccupation(debut: Date, fin: Date) {
  const consultants = await prisma.consultant.findMany({
    include: { missions: true }
  });

  const joursOuvres = calculateBusinessDays(debut, fin);
  let joursFacturables = 0;

  for (const consultant of consultants) {
    for (const mission of consultant.missions) {
      const joursMission = calculateOverlap(
        mission.dateDebut,
        mission.dateFin,
        debut,
        fin
      );
      joursFacturables += joursMission;
    }
  }

  const totalJours = consultants.length * joursOuvres;
  return (joursFacturables / totalJours) * 100;
}

2. CA Réalisé

typescript
async function getCARealisePeriode(debut: Date, fin: Date) {
  const missions = await prisma.mission.findMany({
    where: {
      dateDebut: { lte: fin },
      dateFin: { gte: debut }
    }
  });

  return missions.reduce((ca, mission) => {
    const joursFact = calculateBillableDays(mission, debut, fin);
    return ca + (mission.tjmApplique * joursFact);
  }, 0);
}

3. Consultants Disponibles

typescript
async function getConsultantsDisponibles(date: Date) {
  const consultants = await prisma.consultant.findMany({
    include: {
      missions: {
        where: {
          dateDebut: { lte: date },
          dateFin: { gte: date }
        }
      }
    }
  });

  return consultants.filter(c => c.missions.length === 0);
}

4. Missions Se Terminant Bientôt

typescript
async function getMissionsEndingSoon(jours: number = 30) {
  const now = new Date();
  const limite = new Date();
  limite.setDate(limite.getDate() + jours);

  return await prisma.mission.findMany({
    where: {
      dateFin: {
        gte: now,
        lte: limite
      }
    },
    include: { consultant: true },
    orderBy: { dateFin: 'asc' }
  });
}

5. Durée Moyenne Intercontrat

typescript
async function getDureeIntercontratMoyenne() {
  const consultants = await prisma.consultant.findMany({
    include: {
      missions: {
        orderBy: { dateFin: 'desc' }
      }
    }
  });

  let totalJours = 0;
  let nbPeriodes = 0;

  for (const consultant of consultants) {
    for (let i = 0; i < consultant.missions.length - 1; i++) {
      const finMission = consultant.missions[i].dateFin;
      const debutSuivante = consultant.missions[i + 1].dateDebut;
      const joursBench = calculateDays(finMission, debutSuivante);

      if (joursBench > 0) {
        totalJours += joursBench;
        nbPeriodes++;
      }
    }
  }

  return nbPeriodes > 0 ? totalJours / nbPeriodes : 0;
}

Cycles de Gestion

Cycle Mensuel (1er de chaque mois)

  1. Facturation du mois écoulé

    • Missions facturées → Statut "FACTUREE"
    • Calcul CA réalisé
  2. Staffing du mois à venir

    • Identifier fins de mission (J+30)
    • Matcher consultants disponibles / opportunités
    • Planifier les intercontrats
  3. Calcul des KPIs

    • Taux d'occupation du mois
    • CA vs objectifs
    • Intercontrat moyen

Cycle Trimestriel

  1. Bilan KPIs trimestriels

    • Évolution taux d'occupation
    • Croissance CA
    • Marge brute
  2. Forecast trimestre suivant

    • Pipeline × taux de conversion
    • Missions signées à démarrer
    • Estimation CA
  3. Ajustements

    • Plan commercial
    • Recrutements
    • Formations

Alertes Business

Alertes Critiques (Action Immédiate)

  • Consultant en intercontrat > 30 jours
  • Mission sans facturation > 90 jours
  • Taux d'occupation < 60%
  • Consultant sans mission dans 15 jours

Alertes Warning (Surveillance)

  • Mission se termine dans 30 jours
  • Intercontrat > 15 jours
  • TJM < seuil minimum

Formules de Calcul

Jours Ouvrés entre 2 Dates

typescript
function calculateBusinessDays(start: Date, end: Date): number {
  let count = 0;
  const current = new Date(start);

  while (current <= end) {
    const day = current.getDay();
    if (day !== 0 && day !== 6) { // Pas weekend
      count++;
    }
    current.setDate(current.getDate() + 1);
  }

  return count;
}

Revenus Générés Mission

typescript
function calculateRevenue(tjm: number, debut: Date, fin: Date): number {
  const jours = calculateBusinessDays(debut, fin);
  return tjm * jours;
}

Chevauchement entre 2 Périodes

typescript
function calculateOverlap(
  start1: Date,
  end1: Date,
  start2: Date,
  end2: Date
): number {
  const overlapStart = new Date(Math.max(start1.getTime(), start2.getTime()));
  const overlapEnd = new Date(Math.min(end1.getTime(), end2.getTime()));

  if (overlapStart > overlapEnd) return 0;

  return calculateBusinessDays(overlapStart, overlapEnd);
}

Features Business Prioritaires

🔥 Haute Priorité

1. Forecast de CA

  • Calcul CA réalisé + prévu + potentiel
  • Visibilité 3-6 mois
  • Aide à la prise de décision

2. Pipeline Commercial

  • Gestion des opportunités
  • Calcul CA prévisionnel × probabilité
  • Alerte staffing requis

3. Alertes Proactives

  • Missions se terminant
  • Intercontrat prolongé
  • Taux d'occupation faible

📊 Moyenne Priorité

4. Reporting & Analytics

  • Export Excel missions/consultants
  • Graphiques évolution CA
  • Comparaisons MoM/YoY

5. Gestion Intercontrat

  • Tracking des périodes bench
  • Activités (formation, avant-vente)
  • Coût total intercontrat

6. Analyse Marges

  • Marge par client
  • Marge par consultant
  • Optimisation TJM

💡 Basse Priorité

7. Multi-Clients Avancé

  • Contrats cadres
  • Tarifs négociés par client
  • Historique négociations

8. Gestion des Compétences

  • Matrice compétences × consultants
  • Gaps de compétences
  • Plan de formation

Validations Recommandées

Frontend (UX)

typescript
// Validation formulaire mission
const validateMission = (data: MissionForm) => {
  const errors: string[] = [];

  if (data.dateFin <= data.dateDebut) {
    errors.push('Date de fin doit être après date de début');
  }

  if (data.tjmApplique <= 0) {
    errors.push('TJM doit être positif');
  }

  const duree = calculateDays(data.dateDebut, data.dateFin);
  if (duree > 730) { // 2 ans
    errors.push('Mission > 2 ans, vérifier les dates');
  }

  return errors;
};

Backend (Business Logic)

typescript
// Validation métier complète
const validateNewMission = async (mission: MissionInput) => {
  // 1. Dates cohérentes
  if (mission.dateFin <= mission.dateDebut) {
    throw new BusinessError('Dates incohérentes');
  }

  // 2. Pas de chevauchement
  const overlap = await checkMissionOverlap(mission);
  if (overlap) {
    throw new BusinessError('Consultant déjà en mission');
  }

  // 3. Consultant existe et actif
  const consultant = await prisma.consultant.findUnique({
    where: { id: mission.consultantId }
  });
  if (!consultant) {
    throw new BusinessError('Consultant introuvable');
  }

  // 4. TJM cohérent (warning)
  if (mission.tjmApplique < consultant.tjm * 0.8) {
    console.warn(`TJM bas: ${mission.tjmApplique} vs ${consultant.tjm}`);
  }

  return true;
};

Anti-Patterns à Éviter

❌ Mauvais: Calculs Frontend

typescript
// Ne PAS faire les calculs métier dans le frontend
const ca = (dateFin - dateDebut) * tjm; // Faux!

✅ Bon: Calculs Backend

typescript
// API retourne les calculs
GET /api/missions/:id
{
  "revenusGeneres": 50000, // Calculé par backend
  "dureeJours": 100
}

❌ Mauvais: Supprimer Missions

typescript
// Ne JAMAIS supprimer une mission terminée
await prisma.mission.delete({ where: { id } });

✅ Bon: Soft Delete ou Archiver

typescript
// Marquer comme archivée
await prisma.mission.update({
  where: { id },
  data: { archived: true }
});

❌ Mauvais: Statut Stocké

typescript
// Ne pas stocker le statut dérivé
await prisma.consultant.update({
  data: { statut: 'EN_MISSION' }
});

✅ Bon: Statut Calculé

typescript
// Calculer à la volée depuis les missions
const statut = calculateConsultantStatus(consultant);

Checklist Feature Business

Avant de livrer une feature métier:

  • Règles métier validées (CLAUDE.md)
  • Calculs testés (formules correctes)
  • Edge cases couverts (dates limites, etc.)
  • Pas de suppression de données historiques
  • KPIs impactés identifiés
  • UX validée par users métier
  • Performance acceptable (< 2s)
  • Documentation des formules
  • Tests des règles critiques

Ressources

  • Project Rules: /CLAUDE.md
  • Agent Expert: @ssii-expert - Pour features complexes
  • Technical Skills:
    • @prisma-conventions - Modèles de données
    • @api-conventions - Design API
    • @typescript-standards - Types métier