464 lines
12 KiB
TypeScript
Executable File
464 lines
12 KiB
TypeScript
Executable File
import ApiService from './ApiService';
|
|
import { MaterielBTP, RechercheMaterielParams } from './materielBTPService';
|
|
import { ZoneClimatique } from './zoneClimatiqueService';
|
|
import { ResultatCalculBriques, ResultatCalculBetonArme } from './calculsTechniquesService';
|
|
|
|
/**
|
|
* Service d'export pour les données BTP ultra-détaillées
|
|
* Permet l'export en CSV, Excel et PDF des matériaux, calculs et zones climatiques
|
|
*/
|
|
|
|
export type FormatExport = 'CSV' | 'EXCEL' | 'PDF';
|
|
|
|
export interface OptionsExport {
|
|
format: FormatExport;
|
|
includeImages?: boolean;
|
|
includeCharts?: boolean;
|
|
filtres?: any;
|
|
colonnesPersonnalisees?: string[];
|
|
template?: 'STANDARD' | 'DETAILLE' | 'RESUME';
|
|
}
|
|
|
|
export interface ResultatExport {
|
|
filename: string;
|
|
blob: Blob;
|
|
size: number;
|
|
format: FormatExport;
|
|
nbLignes: number;
|
|
dateGeneration: string;
|
|
}
|
|
|
|
export class ExportBTPService {
|
|
|
|
/**
|
|
* Export des matériaux BTP avec filtres
|
|
*/
|
|
static async exporterMateriaux(options: OptionsExport & {
|
|
filtres?: RechercheMaterielParams;
|
|
}): Promise<ResultatExport> {
|
|
try {
|
|
const blob = await ApiService.post<Blob>(
|
|
'/calculs-techniques/materiaux/export',
|
|
{
|
|
format: options.format,
|
|
filtres: options.filtres,
|
|
template: options.template || 'STANDARD',
|
|
colonnesPersonnalisees: options.colonnesPersonnalisees
|
|
},
|
|
{ responseType: 'blob' }
|
|
);
|
|
|
|
const filename = this.genererNomFichier('materiaux', options.format);
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format: options.format,
|
|
nbLignes: 0, // Sera calculé côté serveur
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Erreur export matériaux:', error);
|
|
throw new Error('Impossible d\'exporter les matériaux');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export des zones climatiques
|
|
*/
|
|
static async exporterZonesClimatiques(options: OptionsExport): Promise<ResultatExport> {
|
|
try {
|
|
const blob = await ApiService.post<Blob>(
|
|
'/calculs-techniques/zones-climatiques/export',
|
|
{
|
|
format: options.format,
|
|
template: options.template || 'STANDARD'
|
|
},
|
|
{ responseType: 'blob' }
|
|
);
|
|
|
|
const filename = this.genererNomFichier('zones-climatiques', options.format);
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format: options.format,
|
|
nbLignes: 0,
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Erreur export zones climatiques:', error);
|
|
throw new Error('Impossible d\'exporter les zones climatiques');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export des résultats de calculs techniques
|
|
*/
|
|
static async exporterCalculs(
|
|
calculs: Array<{
|
|
type: 'BRIQUES' | 'BETON' | 'MORTIER';
|
|
resultat: ResultatCalculBriques | ResultatCalculBetonArme | any;
|
|
parametres: any;
|
|
}>,
|
|
options: OptionsExport
|
|
): Promise<ResultatExport> {
|
|
try {
|
|
const data = {
|
|
calculs: calculs,
|
|
format: options.format,
|
|
template: options.template || 'DETAILLE',
|
|
includeCharts: options.includeCharts || false
|
|
};
|
|
|
|
const blob = await ApiService.post<Blob>(
|
|
'/calculs-techniques/export-calculs',
|
|
data,
|
|
{ responseType: 'blob' }
|
|
);
|
|
|
|
const filename = this.genererNomFichier('calculs-techniques', options.format);
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format: options.format,
|
|
nbLignes: calculs.length,
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
|
|
} catch (error) {
|
|
// Fallback : export côté client si serveur indisponible
|
|
console.warn('Export serveur indisponible, génération côté client');
|
|
return this.exporterCalculsCoteClient(calculs, options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export complet du projet BTP (matériaux + zones + calculs)
|
|
*/
|
|
static async exporterProjetComplet(
|
|
chantierId: string,
|
|
options: OptionsExport & {
|
|
includeMateriaux?: boolean;
|
|
includeZones?: boolean;
|
|
includeCalculs?: boolean;
|
|
includePhases?: boolean;
|
|
}
|
|
): Promise<ResultatExport> {
|
|
try {
|
|
const data = {
|
|
chantierId,
|
|
format: options.format,
|
|
template: options.template || 'DETAILLE',
|
|
sections: {
|
|
materiaux: options.includeMateriaux !== false,
|
|
zones: options.includeZones !== false,
|
|
calculs: options.includeCalculs !== false,
|
|
phases: options.includePhases !== false
|
|
},
|
|
includeCharts: options.includeCharts || false,
|
|
includeImages: options.includeImages || false
|
|
};
|
|
|
|
const blob = await ApiService.post<Blob>(
|
|
'/calculs-techniques/export-projet-complet',
|
|
data,
|
|
{ responseType: 'blob' }
|
|
);
|
|
|
|
const filename = this.genererNomFichier(`projet-${chantierId}`, options.format);
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format: options.format,
|
|
nbLignes: 0,
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Erreur export projet complet:', error);
|
|
throw new Error('Impossible d\'exporter le projet complet');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Génération de devis BTP détaillé avec matériaux et calculs
|
|
*/
|
|
static async genererDevisBTP(
|
|
devisData: {
|
|
chantierId: string;
|
|
phases: Array<{
|
|
nom: string;
|
|
materiaux: Array<{
|
|
code: string;
|
|
quantite: number;
|
|
prixUnitaire?: number;
|
|
}>;
|
|
calculs?: any;
|
|
}>;
|
|
client: {
|
|
nom: string;
|
|
adresse: string;
|
|
telephone?: string;
|
|
email?: string;
|
|
};
|
|
options: {
|
|
margeCommerciale: number;
|
|
tva: number;
|
|
delaiExecution: number;
|
|
validiteDevis: number; // jours
|
|
conditionsPaiement: string;
|
|
};
|
|
},
|
|
format: FormatExport = 'PDF'
|
|
): Promise<ResultatExport> {
|
|
try {
|
|
const blob = await ApiService.post<Blob>(
|
|
'/calculs-techniques/generer-devis',
|
|
{
|
|
...devisData,
|
|
format,
|
|
template: 'DEVIS_PROFESSIONNEL'
|
|
},
|
|
{ responseType: 'blob' }
|
|
);
|
|
|
|
const filename = this.genererNomFichier(`devis-${devisData.chantierId}`, format);
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format,
|
|
nbLignes: devisData.phases.length,
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Erreur génération devis:', error);
|
|
throw new Error('Impossible de générer le devis');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export des matériaux avec QR codes pour traçabilité
|
|
*/
|
|
static async exporterMateriauxAvecQRCodes(
|
|
materiaux: MaterielBTP[],
|
|
options: OptionsExport
|
|
): Promise<ResultatExport> {
|
|
try {
|
|
const data = {
|
|
materiaux: materiaux.map(m => ({
|
|
id: m.id,
|
|
code: m.code,
|
|
nom: m.nom,
|
|
categorie: m.categorie,
|
|
specifications: {
|
|
resistance: m.resistanceCompression,
|
|
densite: m.densite,
|
|
norme: m.normePrincipale
|
|
}
|
|
})),
|
|
format: options.format,
|
|
includeQRCodes: true,
|
|
template: 'TRACABILITE'
|
|
};
|
|
|
|
const blob = await ApiService.post<Blob>(
|
|
'/calculs-techniques/export-tracabilite',
|
|
data,
|
|
{ responseType: 'blob' }
|
|
);
|
|
|
|
const filename = this.genererNomFichier('tracabilite-materiaux', options.format);
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format: options.format,
|
|
nbLignes: materiaux.length,
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('Erreur export traçabilité:', error);
|
|
throw new Error('Impossible d\'exporter la traçabilité');
|
|
}
|
|
}
|
|
|
|
// =================== MÉTHODES UTILITAIRES ===================
|
|
|
|
/**
|
|
* Télécharge automatiquement le fichier exporté
|
|
*/
|
|
static telechargerFichier(resultat: ResultatExport): void {
|
|
const url = window.URL.createObjectURL(resultat.blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = resultat.filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
document.body.removeChild(a);
|
|
}
|
|
|
|
/**
|
|
* Prévisualise le contenu avant export (pour PDF)
|
|
*/
|
|
static async previsualiserExport(
|
|
type: 'MATERIAUX' | 'ZONES' | 'CALCULS' | 'DEVIS',
|
|
donnees: any,
|
|
options: OptionsExport
|
|
): Promise<string> {
|
|
try {
|
|
const response = await ApiService.post<{ previewUrl: string }>(
|
|
'/calculs-techniques/previsualiser-export',
|
|
{
|
|
type,
|
|
donnees,
|
|
options
|
|
}
|
|
);
|
|
|
|
return response.previewUrl;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur prévisualisation:', error);
|
|
throw new Error('Impossible de générer la prévisualisation');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtient les templates d'export disponibles
|
|
*/
|
|
static async getTemplatesDisponibles(): Promise<Array<{
|
|
id: string;
|
|
nom: string;
|
|
description: string;
|
|
formatsSupportes: FormatExport[];
|
|
sections: string[];
|
|
}>> {
|
|
try {
|
|
const response = await ApiService.get<Array<{
|
|
id: string;
|
|
nom: string;
|
|
description: string;
|
|
formatsSupportes: FormatExport[];
|
|
sections: string[];
|
|
}>>('/calculs-techniques/templates-export');
|
|
|
|
return response;
|
|
|
|
} catch (error) {
|
|
// Templates par défaut
|
|
return [
|
|
{
|
|
id: 'STANDARD',
|
|
nom: 'Standard',
|
|
description: 'Export standard avec informations essentielles',
|
|
formatsSupportes: ['CSV', 'EXCEL', 'PDF'],
|
|
sections: ['donnees_base']
|
|
},
|
|
{
|
|
id: 'DETAILLE',
|
|
nom: 'Détaillé',
|
|
description: 'Export complet avec toutes les spécifications techniques',
|
|
formatsSupportes: ['EXCEL', 'PDF'],
|
|
sections: ['donnees_base', 'specifications', 'calculs', 'normes']
|
|
},
|
|
{
|
|
id: 'RESUME',
|
|
nom: 'Résumé',
|
|
description: 'Export synthétique pour présentation',
|
|
formatsSupportes: ['PDF'],
|
|
sections: ['resume', 'graphiques']
|
|
}
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation des options d'export
|
|
*/
|
|
static validerOptionsExport(options: OptionsExport): {
|
|
valide: boolean;
|
|
erreurs: string[];
|
|
} {
|
|
const erreurs: string[] = [];
|
|
|
|
if (!options.format) {
|
|
erreurs.push('Format d\'export requis');
|
|
}
|
|
|
|
if (options.format === 'PDF' && options.colonnesPersonnalisees?.length > 20) {
|
|
erreurs.push('Maximum 20 colonnes pour export PDF');
|
|
}
|
|
|
|
if (options.includeImages && options.format === 'CSV') {
|
|
erreurs.push('Images non supportées en format CSV');
|
|
}
|
|
|
|
return {
|
|
valide: erreurs.length === 0,
|
|
erreurs
|
|
};
|
|
}
|
|
|
|
// =================== MÉTHODES PRIVÉES ===================
|
|
|
|
/**
|
|
* Génère un nom de fichier unique
|
|
*/
|
|
private static genererNomFichier(prefix: string, format: FormatExport): string {
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
|
|
const extension = format.toLowerCase();
|
|
return `${prefix}_${timestamp}.${extension}`;
|
|
}
|
|
|
|
/**
|
|
* Export côté client en cas de problème serveur
|
|
*/
|
|
private static async exporterCalculsCoteClient(
|
|
calculs: any[],
|
|
options: OptionsExport
|
|
): Promise<ResultatExport> {
|
|
let contenu = '';
|
|
|
|
if (options.format === 'CSV') {
|
|
// Génération CSV simple
|
|
contenu = 'Type,Résultat,Date\n';
|
|
calculs.forEach(calcul => {
|
|
contenu += `${calcul.type},"${JSON.stringify(calcul.resultat)}",${new Date().toISOString()}\n`;
|
|
});
|
|
} else {
|
|
// Format texte pour autres formats
|
|
contenu = 'Export des calculs techniques BTP\n\n';
|
|
calculs.forEach(calcul => {
|
|
contenu += `Type: ${calcul.type}\n`;
|
|
contenu += `Résultat: ${JSON.stringify(calcul.resultat, null, 2)}\n\n`;
|
|
});
|
|
}
|
|
|
|
const blob = new Blob([contenu], { type: 'text/plain;charset=utf-8' });
|
|
const filename = this.genererNomFichier('calculs-fallback', 'CSV');
|
|
|
|
return {
|
|
filename,
|
|
blob,
|
|
size: blob.size,
|
|
format: 'CSV',
|
|
nbLignes: calculs.length,
|
|
dateGeneration: new Date().toISOString()
|
|
};
|
|
}
|
|
} |