From c30fe015e248ad246f61c50f283edf5b995a0256 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sat, 13 Jun 2026 01:27:53 +0700 Subject: [PATCH] Refactor order service to utilize new marketplace snapshot structure. Update create and update methods to call buildOrderSnapshot with total amount and line items for improved fee calculation. Introduce lineItemsForSnapshot method for consistent item formatting in snapshots. --- app/Services/Manage/OrderService.php | 27 ++++++- .../System/Setting/MarketplaceService.php | 31 +++++++- .../Marketplace/MarketplaceFeeCalculator.php | 77 +++++++++++++++++++ 3 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 app/Support/Marketplace/MarketplaceFeeCalculator.php 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); + } +}