Commercial Offer Deduction Implementation
Overviewβ
This document describes the implementation of the commercial offer deduction business rule for partial product cancellations in the SAV (Service Après-Vente) workflow.
Business Ruleβ
For Full Order Cancellationβ
When a customer receives a commercial gesture (coupon/discount/commercial offer) and later requests a FULL ORDER cancellation/refund:
refund = total_order_amount - commercial_offer_amount
For Partial Product Cancellationβ
When a customer receives a commercial offer and later cancels only one product (not the full order):
refund = max(0, cancelled_product_amount - commercial_offer_amount)
Key Requirementsβ
- Refund amount must never be negative
- The commercial offer is considered globally for the order
- If multiple products are cancelled separately, apply the rule independently for each cancellation request
- If multiple products are cancelled together in one request, the commercial offer is applied once to the total
Implementationβ
1. New Helper Methodsβ
getCommercialOfferAmount($sale): floatβ
Retrieves the commercial offer amount from the order. Checks multiple possible fields:
discount_amount
Returns the sum of all commercial offers, using absolute values to handle negative entries.
calculatePartialRefundAmount(float $cancelledProductAmount, float $commercialOfferAmount): floatβ
Calculates the refund amount for partial cancellations:
$refundAmount = $cancelledProductAmount - $commercialOfferAmount;
return max(0.0, $refundAmount);
2. Updated Methodsβ
The following methods now include commercial offer deduction:
| Method | Cancellation Type | Notes |
|---|---|---|
handleSaleCancellationWithSav | Full sale | Subtracts commercial offer from refund |
handleSaleCancellationWithGiftCard | Full sale (gift card) | Subtracts commercial offer before 115% calculation |
handleProductsCancellationWithSav | Partial/Complete products | Uses calculatePartialRefundAmount for partial, direct subtraction for complete |
handleProductsCancellationWithGiftCard | Partial products (gift card) | Subtracts commercial offer before 115% calculation |
Examplesβ
Example 1: Commercial offer exceeds cancelled product priceβ
Order total = 200
Commercial offer = 100
Cancelled product price = 50
Refund = max(0, 50 - 100) = 0
Example 2: Commercial offer less than cancelled product priceβ
Order total = 300
Commercial offer = 40
Cancelled product price = 120
Refund = max(0, 120 - 40) = 80
Example 3: Multiple products cancelled in single requestβ
Order total = 500
Commercial offer = 100
Product 1 price = 80
Product 2 price = 120
Total cancelled = 200
Refund = max(0, 200 - 100) = 100
Example 4: Multiple products cancelled separately (sequential)β
Commercial offer = 100
First cancellation: Product A = 50
Refund = max(0, 50 - 100) = 0
Second cancellation: Product B = 80
Refund = max(0, 80 - 100) = 0
Note: When products are cancelled separately, the commercial offer is applied independently to each request. This may result in the commercial offer being "used" multiple times.
Validation & Edge Casesβ
Edge Cases Handledβ
- No commercial offer: Returns full refund amount
- Commercial offer equals product price: Returns 0
- Very small commercial offer: Properly handled with float precision
- Very large commercial offer: Returns 0 (never negative)
- With existing pre-refunds: Pre-refunds are subtracted first, then commercial offer is applied
- All zeros: Returns 0
- Float precision: Results rounded to 2 decimal places for currency
Example with Pre-refundsβ
Cancelled product price = 100
Existing pre-refund amount = 20
Commercial offer = 30
Calculation:
1. Subtract pre-refunds: 100 - 20 = 80
2. Apply commercial offer: max(0, 80 - 30) = 50
Refund = 50
Refund Pattern Exclusionsβ
Some refund types are excluded from the pre-refund calculation. Refunds whose pattern contains specific keywords are NOT subtracted from the new refund amount.
Excluded Patterns (case-insensitive, configured in $excludedRefundPatterns):
modification produitmodification de produit
Examples of excluded refund patterns:
- "modifiction produit" β (excluded)
- "Modification de produit" β (excluded)
- "MODIFICATION PRODUIT" β (excluded)
- "remboursement standard" β (included)
Use Case: When a customer received a refund for product modification (e.g., wrong size, wrong color) and later cancels the order, the modification refund should not reduce the cancellation refund amount.
Configuration: Add new patterns to the $excludedRefundPatterns property in the SavWorkflowService class.
Loggingβ
All refund calculations include detailed logging for debugging and audit purposes:
SavWorkflowService: Partial refund calculation [
'cancelled_product_amount' => 50.0,
'commercial_offer_amount' => 100.0,
'refund_amount' => 0.0,
'calculation' => "max(0, 50.0 - 100.0) = 0.0"
]
Assumptionsβ
- Commercial offer is stored in
order_info['discount_amount']or similar fields - The commercial offer represents a global discount on the entire order
- The commercial offer is subtracted in full from each cancellation
- Multiple separate cancellation requests each get the full commercial offer deduction
- Refund policies may change in the future (code is designed for easy modification)
- The
calculatePartialRefundAmountfunction is isolated for easy modification
Future Considerationsβ
Potential Policy Changesβ
- Proportional Distribution: Instead of full deduction, distribute the commercial offer proportionally across cancelled products
- Tracking Used Portions: Track which portions of the commercial offer have been used across multiple cancellations
- Different Offer Types: Handle different types of commercial offers with different rules
Code Adaptabilityβ
To modify the refund policy, update the calculatePartialRefundAmount method. The logic is isolated to minimize impact on other parts of the codebase.
Files Modifiedβ
- src/AppBundle/Services/SavWorkflowService.php
- Added
$excludedRefundPatternsproperty for configurable refund pattern exclusions - Added
getCommercialOfferAmount()method - Added
calculatePartialRefundAmount()method - Updated
getExistingPreRefundAmount()to exclude refunds with specific patterns - Updated
handleSaleCancellationWithSav() - Updated
handleProductsCancellationWithSav() - Updated
handleSaleCancellationWithGiftCard() - Updated
handleProductsCancellationWithGiftCard()
- Added
Verification Checklistβ
- Commercial offer amount is correctly retrieved from order data
- Full commercial offer is subtracted from partial cancellations
- Refund amount is never negative (max with 0)
- Multiple product cancellations in one request handled correctly
- Existing pre-refunds are subtracted before commercial offer
- Refunds with excluded patterns are NOT subtracted from refund calculation
- Logging is comprehensive for debugging
- Code is maintainable and easy to modify
- Unit test examples provided
- Edge cases documented and handled