Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PayoutService
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 3
12
0.00% covered (danger)
0.00%
0 / 1
 createPayout
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 markPaid
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 cancel
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace App\Services\Marketplace;
4
5use App\Models\Commission;
6use App\Models\Payout;
7use App\Models\Vendor;
8use Illuminate\Support\Facades\DB;
9
10/**
11 * Creates and settles vendor payouts.
12 * No money movement — admin marks a payout as "paid" manually.
13 */
14class PayoutService
15{
16    /**
17     * Build a payout for a vendor covering a date range.
18     * Sums up all unpaid commissions in that period, creates the Payout,
19     * and links those commission rows to it.
20     */
21    public function createPayout(Vendor $vendor, string $periodStart, string $periodEnd, ?int $markedBy = null): Payout
22    {
23        return DB::transaction(function () use ($vendor, $periodStart, $periodEnd, $markedBy) {
24            // Grab unpaid commissions in the period
25            $commissions = Commission::where('vendor_id', $vendor->id)
26                ->unpaid()
27                ->whereBetween('created_at', [$periodStart, $periodEnd . ' 23:59:59'])
28                ->get();
29
30            $grossAmount      = $commissions->sum('sale_amount');
31            $commissionAmount = $commissions->sum('commission_amount');
32            $netAmount        = $grossAmount - $commissionAmount;
33
34            $payout = Payout::create([
35                'vendor_id'         => $vendor->id,
36                'period_start'      => $periodStart,
37                'period_end'        => $periodEnd,
38                'gross_amount'      => $grossAmount,
39                'commission_amount' => $commissionAmount,
40                'net_amount'        => $netAmount,
41                'status'            => 'pending',
42                'marked_by'         => $markedBy,
43            ]);
44
45            // Link commissions to this payout
46            Commission::whereIn('id', $commissions->pluck('id'))
47                ->update(['payout_id' => $payout->id]);
48
49            return $payout;
50        });
51    }
52
53    /**
54     * Mark a payout (and its commissions) as paid.
55     */
56    public function markPaid(Payout $payout, string $method, ?string $reference = null, ?int $markedBy = null): Payout
57    {
58        return DB::transaction(function () use ($payout, $method, $reference, $markedBy) {
59            $payout->update([
60                'status'            => 'paid',
61                'payment_method'    => $method,
62                'payment_reference' => $reference,
63                'marked_by'         => $markedBy,
64                'paid_at'           => now(),
65            ]);
66
67            // Mark all linked commissions as paid
68            $payout->commissions()->update(['status' => 'paid']);
69
70            return $payout->fresh();
71        });
72    }
73
74    /**
75     * Cancel a pending payout — unlink commissions so they can be re-batched.
76     */
77    public function cancel(Payout $payout): Payout
78    {
79        return DB::transaction(function () use ($payout) {
80            $payout->commissions()->update(['payout_id' => null]);
81
82            $payout->update(['status' => 'cancelled']);
83
84            return $payout->fresh();
85        });
86    }
87}