09-moteur-tarification
<?php
class PricingEngine {
private array $config;
public function __construct() {
$this->config = json_decode(file_get_contents('/etc/pricing.json'), true);
}
public function calculatePrice(Product $product, Customer $customer, int $quantity): float {
$basePrice = $product->getPrice();
$finalPrice = $basePrice * $quantity;
// Application des remises client
$customerType = $customer->getType();
if($customerType === 'vip') {
$finalPrice *= 0.85;
} elseif($customerType === 'regular' && $customer->getYearsOfMembership() > 2) {
$finalPrice *= 0.90;
} elseif($customerType === 'new') {
if($customer->hasReferralCode()) {
$finalPrice *= 0.95;
}
}
// Remise volume
if($quantity >= 100) {
$finalPrice *= 0.80;
} elseif($quantity >= 50) {
$finalPrice *= 0.85;
} elseif($quantity >= 20) {
$finalPrice *= 0.90;
} elseif($quantity >= 10) {
$finalPrice *= 0.95;
}
// Remise catégorie produit
$category = $product->getCategory();
if($category === 'electronics' && $this->isBlackFriday()) {
$finalPrice *= 0.70;
} elseif($category === 'clothing' && $this->isSummer()) {
$finalPrice *= 0.80;
} elseif($category === 'books') {
$finalPrice *= 0.95; // Toujours 5% sur les livres
}
// Prix spéciaux pour zones géographiques
$country = $customer->getCountry();
if($country === 'FR' && $product->isMadeInFrance()) {
$finalPrice *= 0.90;
} elseif(in_array($country, ['DE', 'IT', 'ES'])) {
$finalPrice *= 1.05; // Frais de douane
}
// Programmes de fidélité
$loyaltyPoints = $customer->getLoyaltyPoints();
if($loyaltyPoints >= 1000) {
$discount = min($loyaltyPoints / 100, $finalPrice * 0.50);
$finalPrice -= $discount;
$db = new PDO('mysql:host=localhost;dbname=shop','root','');
$stmt = $db->prepare('UPDATE customers SET loyalty_points = loyalty_points - ? WHERE id = ?');
$stmt->execute([$discount * 100, $customer->getId()]);
}
// Promotions temporaires
$promos = $this->getActivePromotions();
foreach($promos as $promo) {
if($this->promoApplies($promo, $product, $customer)) {
$finalPrice *= (1 - $promo['discount_percent'] / 100);
}
}
// Logging et analytics
$db = new PDO('mysql:host=localhost;dbname=shop','root','');
$stmt = $db->prepare('INSERT INTO price_calculations (product_id,customer_id,base_price,final_price,timestamp) VALUES (?,?,?,?,?)');
$stmt->execute([$product->getId(), $customer->getId(), $basePrice, $finalPrice, time()]);
// Notification si grosse commande
if($finalPrice > 10000) {
mail('sales@example.com', 'Large order', "Customer {$customer->getId()} is ordering for {$finalPrice} EUR");
}
return round($finalPrice, 2);
}
private function isBlackFriday(): bool {
$date = new DateTime();
return $date->format('m') === '11' && $date->format('d') >= '20';
}
private function isSummer(): bool {
$month = (int)(new DateTime())->format('m');
return $month >= 6 && $month <= 8;
}
private function getActivePromotions(): array {
$db = new PDO('mysql:host=localhost;dbname=shop','root','');
$stmt = $db->query('SELECT * FROM promotions WHERE active = 1 AND start_date <= NOW() AND end_date >= NOW()');
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
private function promoApplies(array $promo, Product $product, Customer $customer): bool {
if(isset($promo['product_category']) && $product->getCategory() !== $promo['product_category']) {
return false;
}
if(isset($promo['customer_type']) && $customer->getType() !== $promo['customer_type']) {
return false;
}
return true;
}
}
class Product {
public function getId(): int { return 1; }
public function getPrice(): float { return 100.0; }
public function getCategory(): string { return 'electronics'; }
public function isMadeInFrance(): bool { return false; }
}
class Customer {
public function getId(): int { return 1; }
public function getType(): string { return 'regular'; }
public function getYearsOfMembership(): int { return 3; }
public function hasReferralCode(): bool { return false; }
public function getCountry(): string { return 'FR'; }
public function getLoyaltyPoints(): int { return 500; }
}
/*
=== USER STORIES ===
US1: En tant que marketing manager, je veux ajouter un nouveau type de remise
"membre premium" avec 20% de réduction sur tout, sans modifier le code existant.
US2: En tant que développeur, je veux tester la logique de calcul des remises
volume indépendamment des autres règles de pricing.
US3: En tant que product owner, je veux que les règles de tarification soient
configurables via une interface admin plutôt qu'en dur dans le code.
US4: En tant que data analyst, je veux envoyer les logs de pricing vers Google
Analytics au lieu de la BDD MySQL.
US5: En tant que business analyst, je veux pouvoir simuler différents scénarios
de pricing sans appliquer réellement les remises et sans notifier personne.
US6: En tant que international manager, je veux ajouter des règles spécifiques
par pays (taxes, frais, remises locales) de manière extensible.
US7: En tant que compliance officer, je veux m'assurer que le cumul des remises
ne dépasse jamais 70% du prix de base, quelle que soit la combinaison de règles.
US8: En tant que product manager, je veux pouvoir A/B tester différentes stratégies
de pricing sur des cohortes de clients sans dupliquer le code.
*/