Initial commit
This commit is contained in:
239
services/fournisseurPhaseService.ts
Normal file
239
services/fournisseurPhaseService.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
import axios from 'axios';
|
||||
import { API_CONFIG } from '../config/api';
|
||||
import {
|
||||
FournisseurPhase,
|
||||
ApiResponse
|
||||
} from '../types/btp-extended';
|
||||
|
||||
class FournisseurPhaseService {
|
||||
private readonly basePath = '/fournisseurs-phases';
|
||||
private api = axios.create({
|
||||
baseURL: API_CONFIG.baseURL,
|
||||
timeout: API_CONFIG.timeout,
|
||||
headers: API_CONFIG.headers,
|
||||
});
|
||||
|
||||
constructor() {
|
||||
// Interceptor pour ajouter le token Keycloak
|
||||
this.api.interceptors.request.use(
|
||||
async (config) => {
|
||||
// Vérifier si Keycloak est initialisé et l'utilisateur authentifié
|
||||
if (typeof window !== 'undefined') {
|
||||
const { keycloak, KEYCLOAK_TIMEOUTS } = await import('../config/keycloak');
|
||||
|
||||
if (keycloak.authenticated) {
|
||||
try {
|
||||
// Rafraîchir le token si nécessaire
|
||||
await keycloak.updateToken(KEYCLOAK_TIMEOUTS.TOKEN_REFRESH_BEFORE_EXPIRY);
|
||||
|
||||
// Ajouter le token Bearer à l'en-tête Authorization
|
||||
if (keycloak.token) {
|
||||
config.headers['Authorization'] = `Bearer ${keycloak.token}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la mise à jour du token Keycloak:', error);
|
||||
keycloak.login();
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
// Fallback vers l'ancien système pour la rétrocompatibilité
|
||||
let token = null;
|
||||
try {
|
||||
const authTokenItem = sessionStorage.getItem('auth_token') || localStorage.getItem('auth_token');
|
||||
if (authTokenItem) {
|
||||
const parsed = JSON.parse(authTokenItem);
|
||||
token = parsed.value;
|
||||
}
|
||||
} catch (e) {
|
||||
token = localStorage.getItem('token');
|
||||
}
|
||||
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
// Interceptor pour les réponses
|
||||
this.api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('user');
|
||||
window.location.href = '/api/auth/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les fournisseurs d'une phase
|
||||
*/
|
||||
async getByPhase(phaseId: string): Promise<FournisseurPhase[]> {
|
||||
if (!phaseId || phaseId === 'undefined' || phaseId === 'null' || phaseId === 'NaN') {
|
||||
console.warn(`ID de phase invalide: ${phaseId}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.api.get(`${this.basePath}/phase/${phaseId}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.warn(`Endpoint ${this.basePath}/phase/${phaseId} non disponible:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer un fournisseur de phase par ID
|
||||
*/
|
||||
async getById(id: number): Promise<FournisseurPhase> {
|
||||
const response = await this.api.get(`${this.basePath}/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer un nouveau fournisseur de phase
|
||||
*/
|
||||
async create(fournisseurPhase: Omit<FournisseurPhase, 'id'>): Promise<FournisseurPhase> {
|
||||
console.log('Creating fournisseur phase with data:', fournisseurPhase);
|
||||
const response = await this.api.post(this.basePath, fournisseurPhase);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifier un fournisseur de phase existant
|
||||
*/
|
||||
async update(id: number, fournisseurPhase: Partial<FournisseurPhase>): Promise<FournisseurPhase> {
|
||||
const response = await this.api.put(`${this.basePath}/${id}`, fournisseurPhase);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprimer un fournisseur de phase
|
||||
*/
|
||||
async delete(id: number): Promise<void> {
|
||||
await this.api.delete(`${this.basePath}/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtenir les fournisseurs par type de contribution
|
||||
*/
|
||||
async getByTypeContribution(phaseId: string, type: string): Promise<FournisseurPhase[]> {
|
||||
try {
|
||||
const fournisseurs = await this.getByPhase(phaseId);
|
||||
return fournisseurs.filter(f => f.typeContribution === type);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération par type:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculer les économies réalisées avec les négociations
|
||||
*/
|
||||
async calculerEconomies(phaseId: string): Promise<number> {
|
||||
try {
|
||||
const fournisseurs = await this.getByPhase(phaseId);
|
||||
return fournisseurs.reduce((total, fournisseur) => {
|
||||
const prixCatalogue = fournisseur.prixCatalogue || 0;
|
||||
const prixNegocie = fournisseur.prixNegocie || prixCatalogue;
|
||||
return total + (prixCatalogue - prixNegocie);
|
||||
}, 0);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du calcul des économies:', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtenir le fournisseur principal d'une phase
|
||||
*/
|
||||
async getFournisseurPrincipal(phaseId: string): Promise<FournisseurPhase | null> {
|
||||
try {
|
||||
const fournisseurs = await this.getByPhase(phaseId);
|
||||
return fournisseurs.find(f => f.priorite === 1) || null;
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération du fournisseur principal:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lancer un appel d'offres pour une phase
|
||||
*/
|
||||
async lancerAppelOffres(phaseId: string, fournisseurIds: number[]): Promise<FournisseurPhase[]> {
|
||||
try {
|
||||
const response = await this.api.post(`${this.basePath}/appel-offres`, {
|
||||
phaseId,
|
||||
fournisseurIds
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.warn('Appel d\'offres non disponible:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparer les offres de fournisseurs
|
||||
*/
|
||||
async comparerOffres(phaseId: string): Promise<FournisseurPhase[]> {
|
||||
try {
|
||||
const fournisseurs = await this.getByPhase(phaseId);
|
||||
// Tri par prix négocié croissant
|
||||
return fournisseurs.sort((a, b) => {
|
||||
const prixA = a.prixNegocie || a.prixCatalogue || Infinity;
|
||||
const prixB = b.prixNegocie || b.prixCatalogue || Infinity;
|
||||
return prixA - prixB;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la comparaison des offres:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valider une négociation
|
||||
*/
|
||||
async validerNegociation(id: number, validePar: string): Promise<FournisseurPhase> {
|
||||
const response = await this.api.post(`${this.basePath}/${id}/valider`, {
|
||||
validePar,
|
||||
dateValidation: new Date()
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculer le score d'un fournisseur (prix, délai, qualité)
|
||||
*/
|
||||
calculerScoreFournisseur(fournisseur: FournisseurPhase): number {
|
||||
let score = 0;
|
||||
|
||||
// Score prix (40% du total)
|
||||
const prixFinal = fournisseur.prixNegocie || fournisseur.prixCatalogue || 0;
|
||||
const remise = fournisseur.remise || 0;
|
||||
const scoreRemise = Math.min(remise / 100, 0.3); // Max 30% de remise
|
||||
score += scoreRemise * 40;
|
||||
|
||||
// Score délai (30% du total)
|
||||
const delai = fournisseur.delaiLivraison || 30;
|
||||
const scoreDelai = Math.max(0, (30 - delai) / 30); // Meilleur si délai < 30 jours
|
||||
score += scoreDelai * 30;
|
||||
|
||||
// Score priorité/historique (30% du total)
|
||||
const priorite = fournisseur.priorite || 5;
|
||||
const scorePriorite = Math.max(0, (6 - priorite) / 5); // Meilleur si priorité = 1
|
||||
score += scorePriorite * 30;
|
||||
|
||||
return Math.round(score);
|
||||
}
|
||||
}
|
||||
|
||||
export default new FournisseurPhaseService();
|
||||
Reference in New Issue
Block a user