Skip to main content

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:

MethodCancellation TypeNotes
handleSaleCancellationWithSavFull saleSubtracts commercial offer from refund
handleSaleCancellationWithGiftCardFull sale (gift card)Subtracts commercial offer before 115% calculation
handleProductsCancellationWithSavPartial/Complete productsUses calculatePartialRefundAmount for partial, direct subtraction for complete
handleProductsCancellationWithGiftCardPartial 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​

  1. No commercial offer: Returns full refund amount
  2. Commercial offer equals product price: Returns 0
  3. Very small commercial offer: Properly handled with float precision
  4. Very large commercial offer: Returns 0 (never negative)
  5. With existing pre-refunds: Pre-refunds are subtracted first, then commercial offer is applied
  6. All zeros: Returns 0
  7. 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 produit
  • modification 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​

  1. Commercial offer is stored in order_info['discount_amount'] or similar fields
  2. The commercial offer represents a global discount on the entire order
  3. The commercial offer is subtracted in full from each cancellation
  4. Multiple separate cancellation requests each get the full commercial offer deduction
  5. Refund policies may change in the future (code is designed for easy modification)
  6. The calculatePartialRefundAmount function is isolated for easy modification

Future Considerations​

Potential Policy Changes​

  1. Proportional Distribution: Instead of full deduction, distribute the commercial offer proportionally across cancelled products
  2. Tracking Used Portions: Track which portions of the commercial offer have been used across multiple cancellations
  3. 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​

  1. src/AppBundle/Services/SavWorkflowService.php
    • Added $excludedRefundPatterns property 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()

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