diff --git a/app/Services/Manage/OrderService.php b/app/Services/Manage/OrderService.php index 8b1d48e..d9681fc 100644 --- a/app/Services/Manage/OrderService.php +++ b/app/Services/Manage/OrderService.php @@ -299,7 +299,11 @@ public function create(array $validated, User $user): Order 'created_by_id' => $user->id, 'subtotal' => $subtotal, 'discount' => $discount, - 'marketplace_settings_snapshot' => $this->marketplaceService->snapshotForChannel($channel), + 'marketplace_settings_snapshot' => $this->marketplaceService->buildOrderSnapshot( + $channel, + $totalAmount, + $this->lineItemsForSnapshot($draftItems), + ), 'total_amount' => $totalAmount, 'notes' => $validated['notes'] ?? null, ]); @@ -346,7 +350,11 @@ public function update(Order $order, array $validated): void $order->price_type = $priceType; $order->subtotal = $subtotal; $order->discount = $discount; - $order->marketplace_settings_snapshot = $this->marketplaceService->snapshotForChannel($channel); + $order->marketplace_settings_snapshot = $this->marketplaceService->buildOrderSnapshot( + $channel, + $totalAmount, + $this->lineItemsForSnapshot($lineItems), + ); $order->total_amount = $totalAmount; $order->notes = $validated['notes'] ?? null; $order->save(); @@ -531,6 +539,21 @@ private function incrementStock(OrderItem $item): void ->increment('stock', $item->quantity); } + /** + * @param EloquentCollection|list $items + * @return list + */ + private function lineItemsForSnapshot(EloquentCollection|array $items): array + { + return collect($items) + ->map(fn (OrderItem|array $item) => [ + 'quantity' => (int) (is_array($item) ? $item['quantity'] : $item->quantity), + 'subtotal' => (int) (is_array($item) ? $item['subtotal'] : $item->subtotal), + ]) + ->values() + ->all(); + } + private function applySorting(Builder $query, string $sort, string $direction): void { if (in_array($sort, ['created_at', 'total_amount', 'discount', 'subtotal', 'order_number'], true)) { diff --git a/app/Services/System/Setting/MarketplaceService.php b/app/Services/System/Setting/MarketplaceService.php index 87f9c23..2c86e6e 100644 --- a/app/Services/System/Setting/MarketplaceService.php +++ b/app/Services/System/Setting/MarketplaceService.php @@ -4,6 +4,7 @@ use App\Enums\OrderChannel; use App\Settings\MarketplaceSettings; +use App\Support\Marketplace\MarketplaceFeeCalculator; use App\Support\Marketplace\MarketplaceFeeRule; class MarketplaceService @@ -76,9 +77,37 @@ public function updateMarketplace(array $validated): void } /** + * @param list $lineItems * @return array|null */ - public function snapshotForChannel(OrderChannel $channel): ?array + public function buildOrderSnapshot(OrderChannel $channel, int $totalAmount, array $lineItems): ?array + { + $feeSnapshot = $this->feeRulesForChannel($channel); + + if ($feeSnapshot === null) { + return null; + } + + $calculation = app(MarketplaceFeeCalculator::class)->calculate( + $feeSnapshot['fees'], + $totalAmount, + $lineItems, + ); + + return [ + 'platform' => $feeSnapshot['platform'], + 'fees' => $feeSnapshot['fees'], + 'base_amount' => $totalAmount, + 'results' => $calculation['results'], + 'total_fee_amount' => $calculation['total_fee_amount'], + 'net_amount' => $calculation['net_amount'], + ]; + } + + /** + * @return array{platform: string, fees: array}|null + */ + private function feeRulesForChannel(OrderChannel $channel): ?array { $settings = app(MarketplaceSettings::class); diff --git a/app/Support/Marketplace/MarketplaceFeeCalculator.php b/app/Support/Marketplace/MarketplaceFeeCalculator.php new file mode 100644 index 0000000..e772a30 --- /dev/null +++ b/app/Support/Marketplace/MarketplaceFeeCalculator.php @@ -0,0 +1,77 @@ + $fees + * @param list $lineItems + * @return array{ + * results: array, + * total_fee_amount: int, + * net_amount: int + * } + */ + public function calculate(array $fees, int $totalAmount, array $lineItems): array + { + $results = []; + $balance = $totalAmount; + $totalFeeAmount = 0; + + foreach ($fees as $key => $ruleData) { + $amount = $this->calculateFeeAmount( + MarketplaceFeeRule::fromArray($ruleData), + $totalAmount, + $lineItems, + ); + + $balance = max($balance - $amount, 0); + $totalFeeAmount += $amount; + + $results[$key] = [ + 'amount' => $amount, + 'balance_after' => $balance, + ]; + } + + return [ + 'results' => $results, + 'total_fee_amount' => $totalFeeAmount, + 'net_amount' => max($totalAmount - $totalFeeAmount, 0), + ]; + } + + /** + * @param list $lineItems + */ + private function calculateFeeAmount(MarketplaceFeeRule $rule, int $totalAmount, array $lineItems): int + { + if ($rule->scope === MarketplaceFeeScope::TRANSACTION) { + return $this->calculateFromBasis( + $rule, + $totalAmount, + ); + } + + return (int) collect($lineItems)->sum(function (array $item) use ($rule): int { + if ($rule->valueType === MarketplaceFeeValueType::FLAT) { + return (int) round($rule->value * $item['quantity']); + } + + return (int) round($item['subtotal'] * $rule->value / 100); + }); + } + + private function calculateFromBasis(MarketplaceFeeRule $rule, int $basis): int + { + if ($rule->valueType === MarketplaceFeeValueType::FLAT) { + return (int) round($rule->value); + } + + return (int) round($basis * $rule->value / 100); + } +}