Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
71.79% covered (warning)
71.79%
28 / 39
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CommissionService
71.79% covered (warning)
71.79%
28 / 39
50.00% covered (danger)
50.00%
2 / 4
28.98
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 calculateRate
28.57% covered (danger)
28.57%
4 / 14
0.00% covered (danger)
0.00%
0 / 1
64.48
 recordCommission
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 recordOrderCommissions
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
1<?php
2
3namespace App\Services\Marketplace;
4
5use App\Models\Commission;
6use App\Models\Order;
7use App\Models\Vendor;
8use 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 */
18class 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}