Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
71.79% |
28 / 39 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
| CommissionService | |
71.79% |
28 / 39 |
|
50.00% |
2 / 4 |
28.98 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| calculateRate | |
28.57% |
4 / 14 |
|
0.00% |
0 / 1 |
64.48 | |||
| recordCommission | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
3 | |||
| recordOrderCommissions | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
4.01 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Services\Marketplace; |
| 4 | |
| 5 | use App\Models\Commission; |
| 6 | use App\Models\Order; |
| 7 | use App\Models\Vendor; |
| 8 | use App\Services\Setting\SettingService; |
| 9 | |
| 10 | /** |
| 11 | * Calculates and records per-line commissions for vendor sales. |
| 12 | * |
| 13 | * Rate hierarchy (first non-null wins): |
| 14 | * 1. Per-category override (marketplace.commission_cat_{id}) |
| 15 | * 2. Per-vendor rate (vendors.commission_pct) |
| 16 | * 3. Global default (marketplace.default_commission, fallback 10%) |
| 17 | */ |
| 18 | class CommissionService |
| 19 | { |
| 20 | private SettingService $settings; |
| 21 | |
| 22 | public function __construct(SettingService $settings) |
| 23 | { |
| 24 | $this->settings = $settings; |
| 25 | } |
| 26 | |
| 27 | /** |
| 28 | * Resolve the commission rate for a vendor + optional product/category context. |
| 29 | */ |
| 30 | public function calculateRate(Vendor $vendor, $product = null, $category = null): float |
| 31 | { |
| 32 | // 1. Per-category override |
| 33 | if ($category) { |
| 34 | $catId = is_object($category) ? $category->id : $category; |
| 35 | $catRate = $this->settings->get("marketplace.commission_cat_{$catId}"); |
| 36 | if ($catRate !== null && $catRate !== '') { |
| 37 | return (float) $catRate; |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | // Also try the product's primary category |
| 42 | if ($product && !$category && method_exists($product, 'categories')) { |
| 43 | $primaryCat = $product->categories()->first(); |
| 44 | if ($primaryCat) { |
| 45 | $catRate = $this->settings->get("marketplace.commission_cat_{$primaryCat->id}"); |
| 46 | if ($catRate !== null && $catRate !== '') { |
| 47 | return (float) $catRate; |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | // 2. Per-vendor rate |
| 53 | if ($vendor->commission_pct > 0) { |
| 54 | return (float) $vendor->commission_pct; |
| 55 | } |
| 56 | |
| 57 | // 3. Global default |
| 58 | return (float) $this->settings->get('marketplace.default_commission', 10); |
| 59 | } |
| 60 | |
| 61 | /** |
| 62 | * Record a single commission row for a vendor order line. |
| 63 | */ |
| 64 | public function recordCommission(Vendor $vendor, $order, $orderDetail, float $saleAmount): Commission |
| 65 | { |
| 66 | $product = $orderDetail->product ?? null; |
| 67 | $rate = $this->calculateRate($vendor, $product); |
| 68 | |
| 69 | $commissionAmount = round($saleAmount * ($rate / 100), 2); |
| 70 | |
| 71 | return Commission::create([ |
| 72 | 'vendor_id' => $vendor->id, |
| 73 | 'order_id' => is_object($order) ? $order->id : $order, |
| 74 | 'order_detail_id' => is_object($orderDetail) ? $orderDetail->id : $orderDetail, |
| 75 | 'sale_amount' => $saleAmount, |
| 76 | 'commission_rate' => $rate, |
| 77 | 'commission_amount' => $commissionAmount, |
| 78 | 'status' => 'pending', |
| 79 | ]); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Process all vendor lines in an order and record commissions. |
| 84 | * Only processes lines that have a vendor_id (multi-vendor items). |
| 85 | */ |
| 86 | public function recordOrderCommissions(Order $order): void |
| 87 | { |
| 88 | $details = $order->details()->whereNotNull('vendor_id')->get(); |
| 89 | |
| 90 | foreach ($details as $detail) { |
| 91 | $vendor = Vendor::find($detail->vendor_id); |
| 92 | if (!$vendor) { |
| 93 | continue; |
| 94 | } |
| 95 | |
| 96 | // Avoid duplicate commissions |
| 97 | $exists = Commission::where('order_detail_id', $detail->id) |
| 98 | ->where('vendor_id', $vendor->id) |
| 99 | ->exists(); |
| 100 | |
| 101 | if ($exists) { |
| 102 | continue; |
| 103 | } |
| 104 | |
| 105 | $lineTotal = ($detail->qty ?? 1) * ($detail->price ?? 0); |
| 106 | $this->recordCommission($vendor, $order, $detail, $lineTotal); |
| 107 | } |
| 108 | } |
| 109 | } |