Initial commit

This commit is contained in:
dahoud
2025-10-01 01:39:07 +00:00
commit b430bf3b96
826 changed files with 255287 additions and 0 deletions

View File

@@ -0,0 +1,484 @@
import ApiService from './ApiService';
/**
* Service pour les calculs techniques ultra-détaillés BTP
* Le plus ambitieux système de calculs BTP d'Afrique
*/
// =================== INTERFACES PARAMÈTRES ===================
export interface ParametresCalculBriques {
surface: number;
epaisseurMur: number;
codeBrique: string;
zoneClimatique: string;
typeAppareillage: 'DROIT' | 'QUINCONCE' | 'FLAMAND' | 'ANGLAIS';
jointHorizontal: number;
jointVertical: number;
ouvertures: Ouverture[];
}
export interface Ouverture {
largeur: number;
hauteur: number;
}
export interface ParametresCalculMortier {
volumeMaconnerie: number;
typeMortier: 'POSE_BRIQUES' | 'JOINTOIEMENT' | 'ENDUIT_BASE' | 'ENDUIT_FINITION' | 'STANDARD';
zoneClimatique: string;
}
export interface ParametresCalculBetonArme {
volume: number;
classeBeton: 'C20/25' | 'C25/30' | 'C30/37' | 'C35/45';
classeExposition: 'XC1' | 'XC3' | 'XC4' | 'XS1' | 'XS3';
typeOuvrage: 'DALLE' | 'POUTRE' | 'POTEAU' | 'VOILE';
epaisseur: number;
zoneClimatique: string;
}
// =================== INTERFACES RÉSULTATS ===================
export interface ResultatCalculBriques {
nombreBriques: number;
nombrePalettes: number;
briquesParM2: number;
surfaceNette: number;
mortier: ResultatCalculMortier;
facteurPerte: number;
facteurClimatique: number;
nombreCouches: number;
recommendationsZone: string[];
}
export interface ResultatCalculMortier {
volumeTotal: number;
cimentKg: number;
sableLitres: number;
eauLitres: number;
sacs50kg: number;
}
export interface ResultatCalculBetonArme {
volume: number;
cimentKg: number;
cimentSacs50kg: number;
sableKg: number;
sableM3: number;
graviersKg: number;
graviersM3: number;
eauLitres: number;
acierKgTotal: number;
repartitionAcier: Record<number, number>; // diamètre -> poids en kg
enrobage: number;
dosageAdapte: DosageBeton;
adaptationsClimatiques: string[];
}
export interface DosageBeton {
ciment: number; // kg/m³
eau: number; // L/m³
graviers: number; // kg/m³
sable: number; // kg/m³
}
export interface DosageBetonInfo {
usage: string;
ciment: string;
resistance: string;
exposition: string;
}
// =================== CLASSES DE SERVICE ===================
export class CalculsTechniquesService {
private static readonly BASE_PATH = '/api/v1/calculs-techniques';
// =================== CALCULS MAÇONNERIE ===================
/**
* Calcul ultra-précis quantité briques pour mur
* Prend en compte dimensions exactes, joints, appareillage, pertes, zone climatique
*/
static async calculerBriquesMur(params: ParametresCalculBriques): Promise<ResultatCalculBriques> {
const response = await ApiService.post<ResultatCalculBriques>(
`${this.BASE_PATH}/briques-mur`,
params
);
return response;
}
/**
* Calcul mortier pour maçonnerie traditionnelle
*/
static async calculerMortierMaconnerie(params: ParametresCalculMortier): Promise<ResultatCalculMortier> {
const response = await ApiService.post<ResultatCalculMortier>(
`${this.BASE_PATH}/mortier-maconnerie`,
params
);
return response;
}
/**
* Estimation rapide briques pour surface donnée
*/
static async estimationRapideBriques(surface: number, typeBrique: string = 'brique-rouge-15x10x5'): Promise<{
estimationBasse: number;
estimationHaute: number;
estimationMoyenne: number;
baseCalcul: string;
}> {
// Calcul côté client pour estimation rapide
const briquesParM2Moyen = 67; // Moyenne pour brique 15x10x5cm
const facteurPerteMoyen = 1.08; // 8% de perte moyenne
const estimationMoyenne = Math.ceil(surface * briquesParM2Moyen * facteurPerteMoyen);
const estimationBasse = Math.ceil(estimationMoyenne * 0.85);
const estimationHaute = Math.ceil(estimationMoyenne * 1.25);
return {
estimationBasse,
estimationHaute,
estimationMoyenne,
baseCalcul: `Surface: ${surface}× ${briquesParM2Moyen} briques/m² × ${facteurPerteMoyen} (pertes)`
};
}
// =================== CALCULS BÉTON ARMÉ ===================
/**
* Calcul béton armé avec adaptation climatique africaine
*/
static async calculerBetonArme(params: ParametresCalculBetonArme): Promise<ResultatCalculBetonArme> {
const response = await ApiService.post<ResultatCalculBetonArme>(
`${this.BASE_PATH}/beton-arme`,
params
);
return response;
}
/**
* Récupère les dosages béton standard avec adaptations climatiques
*/
static async getDosagesBeton(): Promise<{
dosages: Record<string, DosageBetonInfo>;
notes: string[];
}> {
const response = await ApiService.get<{
dosages: Record<string, DosageBetonInfo>;
notes: string[];
}>(`${this.BASE_PATH}/dosages-beton`);
return response;
}
/**
* Estimation rapide béton pour volume donné
*/
static async estimationRapideBeton(volume: number, classeBeton: string = 'C25/30'): Promise<{
cimentSacs: number;
sableM3: number;
graviersM3: number;
eauLitres: number;
coutEstime: number;
}> {
// Dosages moyens selon classe
const dosages = {
'C20/25': { ciment: 300, sable: 1100, graviers: 650, eau: 165 },
'C25/30': { ciment: 350, sable: 1050, graviers: 600, eau: 175 },
'C30/37': { ciment: 385, sable: 1000, graviers: 580, eau: 180 },
'C35/45': { ciment: 420, sable: 950, graviers: 550, eau: 185 }
};
const dosage = dosages[classeBeton] || dosages['C25/30'];
const cimentKg = volume * dosage.ciment;
const cimentSacs = Math.ceil(cimentKg / 50);
const sableKg = volume * dosage.sable;
const sableM3 = sableKg / 1600; // densité sable
const graviersKg = volume * dosage.graviers;
const graviersM3 = graviersKg / 1500; // densité graviers
const eauLitres = volume * dosage.eau;
// Estimation coût (prix moyens Afrique de l'Ouest)
const coutEstime = (cimentSacs * 8000) + // 8000 FCFA/sac
(sableM3 * 25000) + // 25000 FCFA/m³
(graviersM3 * 30000) + // 30000 FCFA/m³
(eauLitres * 2); // 2 FCFA/L
return {
cimentSacs,
sableM3: Math.ceil(sableM3 * 100) / 100, // 2 décimales
graviersM3: Math.ceil(graviersM3 * 100) / 100,
eauLitres,
coutEstime
};
}
// =================== CALCULS COMPLEXES ===================
/**
* Calcul complet d'un mur (briques + mortier + enduit)
*/
static async calculerMurComplet(params: {
surface: number;
epaisseurMur: number;
codeBrique: string;
zoneClimatique: string;
typeAppareillage: string;
avecEnduit: boolean;
typeEnduit?: 'CIMENT' | 'CHAUX' | 'PLATRE';
}): Promise<{
briques: ResultatCalculBriques;
enduit?: {
mortierM3: number;
cimentKg: number;
sableKg: number;
eauLitres: number;
};
coutTotal: number;
tempsTotal: number; // en heures
}> {
// Calcul briques
const paramsB: ParametresCalculBriques = {
surface: params.surface,
epaisseurMur: params.epaisseurMur,
codeBrique: params.codeBrique,
zoneClimatique: params.zoneClimatique,
typeAppareillage: params.typeAppareillage as any,
jointHorizontal: 10, // défaut 10mm
jointVertical: 10, // défaut 10mm
ouvertures: []
};
const briques = await this.calculerBriquesMur(paramsB);
let enduit;
if (params.avecEnduit) {
// Calcul enduit (15mm d'épaisseur moyenne)
const volumeEnduit = params.surface * 0.015; // 1.5cm
const dosageEnduit = params.typeEnduit === 'CHAUX' ? 250 : 350; // kg/m³
enduit = {
mortierM3: volumeEnduit,
cimentKg: volumeEnduit * dosageEnduit,
sableKg: volumeEnduit * 800, // 800kg sable/m³ mortier
eauLitres: volumeEnduit * 200 // 200L eau/m³ mortier
};
}
// Estimation coûts et temps
const coutBriques = briques.nombreBriques * 250; // 250 FCFA/brique
const coutMortier = briques.mortier.cimentKg * 160; // 160 FCFA/kg ciment
const coutEnduit = enduit ? enduit.cimentKg * 160 : 0;
const coutTotal = coutBriques + coutMortier + coutEnduit;
const tempsBriques = briques.nombreBriques * 3 / 60; // 3min/brique
const tempsEnduit = params.avecEnduit ? params.surface * 45 / 60 : 0; // 45min/m²
const tempsTotal = tempsBriques + tempsEnduit;
return {
briques,
enduit,
coutTotal,
tempsTotal
};
}
/**
* Calcul dalle béton complète (béton + armatures + coffrages)
*/
static async calculerDalleComplete(params: {
surface: number;
epaisseur: number;
classeBeton: string;
zoneClimatique: string;
typeArmature: 'LEGERE' | 'NORMALE' | 'RENFORCEE';
avecCoffrage: boolean;
}): Promise<{
beton: ResultatCalculBetonArme;
coffrage?: {
planchesM2: number;
etaisNombre: number;
};
coutTotal: number;
tempsTotal: number;
}> {
const volume = params.surface * (params.epaisseur / 100); // épaisseur en cm -> m
const paramsBA: ParametresCalculBetonArme = {
volume,
classeBeton: params.classeBeton as any,
classeExposition: 'XC3', // défaut intérieur humide
typeOuvrage: 'DALLE',
epaisseur: params.epaisseur,
zoneClimatique: params.zoneClimatique
};
const beton = await this.calculerBetonArme(paramsBA);
let coffrage;
if (params.avecCoffrage) {
// Surface coffrante = surface dalle + rives
const perimetre = 2 * Math.sqrt(params.surface * 4); // approximation carré
const surfaceCoffrante = params.surface + (perimetre * params.epaisseur / 100);
coffrage = {
planchesM2: surfaceCoffrante * 1.15, // 15% majoration
etaisNombre: Math.ceil(params.surface / 2) // 1 étai par 2m²
};
}
// Estimation coûts
const coutBeton = (beton.cimentSacs50kg * 8000) +
(beton.sableM3.valueOf() * 25000) +
(beton.graviersM3.valueOf() * 30000);
const coutAcier = beton.acierKgTotal * 1200; // 1200 FCFA/kg acier
const coutCoffrage = coffrage ? (coffrage.planchesM2 * 5000) + (coffrage.etaisNombre * 15000) : 0;
const coutTotal = coutBeton + coutAcier + coutCoffrage;
// Estimation temps
const tempsBeton = volume * 2; // 2h/m³
const tempsArmature = beton.acierKgTotal * 0.5; // 30min/kg
const tempsCoffrage = coffrage ? coffrage.planchesM2 * 0.25 : 0; // 15min/m²
const tempsTotal = tempsBeton + tempsArmature + tempsCoffrage;
return {
beton,
coffrage,
coutTotal,
tempsTotal
};
}
// =================== OUTILS UTILITAIRES ===================
/**
* Conversion d'unités de mesure BTP
*/
static convertirUnites(valeur: number, uniteSource: string, uniteDestination: string): number {
const conversions: Record<string, Record<string, number>> = {
'm': { 'cm': 100, 'mm': 1000, 'km': 0.001 },
'm²': { 'cm²': 10000, 'mm²': 1000000, 'ha': 0.0001 },
'm³': { 'l': 1000, 'cm³': 1000000, 'mm³': 1000000000 },
'kg': { 'g': 1000, 't': 0.001, 'quintal': 0.01 },
'MPa': { 'kPa': 1000, 'Pa': 1000000, 'bar': 10 }
};
if (conversions[uniteSource]?.[uniteDestination]) {
return valeur * conversions[uniteSource][uniteDestination];
}
throw new Error(`Conversion non supportée: ${uniteSource} vers ${uniteDestination}`);
}
/**
* Validation des paramètres de calcul
*/
static validerParametres(type: 'BRIQUES' | 'MORTIER' | 'BETON', params: any): {
valide: boolean;
erreurs: string[];
avertissements: string[];
} {
const erreurs: string[] = [];
const avertissements: string[] = [];
switch (type) {
case 'BRIQUES':
if (!params.surface || params.surface <= 0) erreurs.push('Surface requise et > 0');
if (!params.epaisseurMur || params.epaisseurMur <= 0) erreurs.push('Épaisseur mur requise et > 0');
if (!params.codeBrique) erreurs.push('Code brique requis');
if (!params.zoneClimatique) erreurs.push('Zone climatique requise');
if (params.surface > 1000) avertissements.push('Surface très importante (>1000m²)');
if (params.epaisseurMur > 30) avertissements.push('Mur très épais (>30cm)');
break;
case 'BETON':
if (!params.volume || params.volume <= 0) erreurs.push('Volume requis et > 0');
if (!params.classeBeton) erreurs.push('Classe béton requise');
if (!params.typeOuvrage) erreurs.push('Type ouvrage requis');
if (params.volume > 500) avertissements.push('Volume très important (>500m³)');
if (params.epaisseur && params.epaisseur < 10) avertissements.push('Épaisseur faible (<10cm)');
break;
}
return {
valide: erreurs.length === 0,
erreurs,
avertissements
};
}
/**
* Génération de devis détaillé
*/
static async genererDevis(calculs: {
briques?: ResultatCalculBriques;
mortier?: ResultatCalculMortier;
beton?: ResultatCalculBetonArme;
}, options?: {
margeEntreprise?: number; // %
tva?: number; // %
delaiExecution?: number; // jours
}): Promise<{
lignesDevis: Array<{
designation: string;
quantite: number;
unite: string;
prixUnitaire: number;
montantHT: number;
}>;
totalHT: number;
totalTTC: number;
delaiExecution: number;
}> {
const lignesDevis: Array<{
designation: string;
quantite: number;
unite: string;
prixUnitaire: number;
montantHT: number;
}> = [];
// Ajout lignes selon calculs
if (calculs.briques) {
lignesDevis.push({
designation: 'Briques terre cuite',
quantite: calculs.briques.nombreBriques,
unite: 'unité',
prixUnitaire: 250,
montantHT: calculs.briques.nombreBriques * 250
});
}
if (calculs.beton) {
lignesDevis.push({
designation: 'Ciment Portland CEM I',
quantite: calculs.beton.cimentSacs50kg,
unite: 'sac 50kg',
prixUnitaire: 8000,
montantHT: calculs.beton.cimentSacs50kg * 8000
});
}
const totalHT = lignesDevis.reduce((sum, ligne) => sum + ligne.montantHT, 0);
const marge = (options?.margeEntreprise || 20) / 100;
const tva = (options?.tva || 18) / 100;
const totalAvecMarge = totalHT * (1 + marge);
const totalTTC = totalAvecMarge * (1 + tva);
return {
lignesDevis,
totalHT: totalAvecMarge,
totalTTC,
delaiExecution: options?.delaiExecution || 15
};
}
}