Refactor order management to remove marketplace fee handling. Update OrderRequest, Order model, and related services to utilize marketplace settings snapshot instead. Adjust frontend components and TypeScript types accordingly for consistency in order processing.

This commit is contained in:
Yoga Pangestu 2026-06-13 00:56:24 +07:00
parent 64088c82bb
commit 584387504c
10 changed files with 49 additions and 45 deletions

View File

@ -31,7 +31,6 @@ public function rules(): array
'channel' => ['required', Rule::enum(OrderChannel::class)],
'price_type' => ['required', Rule::enum(PriceType::class)],
'discount' => ['nullable', 'integer', 'min:0'],
'marketplace_fee' => ['nullable', 'integer', 'min:0'],
'notes' => ['nullable', 'string'],
];
@ -58,7 +57,6 @@ public function attributes(): array
'channel' => 'channel',
'price_type' => 'tipe harga',
'discount' => 'diskon',
'marketplace_fee' => 'biaya marketplace',
'notes' => 'keterangan',
'items' => 'produk',
'items.*.product_variant_id' => 'varian produk',

View File

@ -19,7 +19,6 @@
#[Appends([
'subtotal_formatted',
'discount_formatted',
'marketplace_fee_formatted',
'total_amount_formatted',
'created_at_formatted',
'channel_label',
@ -40,7 +39,7 @@ protected function casts(): array
'status' => OrderStatus::class,
'subtotal' => 'integer',
'discount' => 'integer',
'marketplace_fee' => 'integer',
'marketplace_settings_snapshot' => 'array',
'total_amount' => 'integer',
];
}
@ -86,13 +85,6 @@ public function discountFormatted(): Attribute
);
}
public function marketplaceFeeFormatted(): Attribute
{
return Attribute::make(
get: fn () => 'Rp '.number_format($this->marketplace_fee, 0, ',', '.'),
);
}
public function totalAmountFormatted(): Attribute
{
return Attribute::make(

View File

@ -12,6 +12,7 @@
use App\Models\ProductPrice;
use App\Models\ProductVariant;
use App\Models\User;
use App\Services\System\Setting\MarketplaceService;
use App\Support\Media\MediaPresenter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
@ -22,6 +23,10 @@
class OrderService
{
public function __construct(
private readonly MarketplaceService $marketplaceService,
) {}
/**
* @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery
*/
@ -283,18 +288,18 @@ public function create(array $validated, User $user): Order
$subtotal = $draftItems->sum('subtotal');
$discount = (int) ($validated['discount'] ?? 0);
$marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0);
$totalAmount = max($subtotal - $discount - $marketplaceFee, 0);
$totalAmount = max($subtotal - $discount, 0);
$channel = OrderChannel::from($validated['channel']);
$order = Order::create([
'customer_id' => $validated['customer_id'] ?? null,
'channel' => $validated['channel'],
'channel' => $channel,
'price_type' => $priceType,
'status' => OrderStatus::PENDING,
'created_by_id' => $user->id,
'subtotal' => $subtotal,
'discount' => $discount,
'marketplace_fee' => $marketplaceFee,
'marketplace_settings_snapshot' => $this->marketplaceService->snapshotForChannel($channel),
'total_amount' => $totalAmount,
'notes' => $validated['notes'] ?? null,
]);
@ -333,15 +338,15 @@ public function update(Order $order, array $validated): void
$lineItems = $this->buildLineItems($validated['items'], $priceType);
$subtotal = array_sum(array_column($lineItems, 'subtotal'));
$discount = (int) ($validated['discount'] ?? 0);
$marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0);
$totalAmount = max($subtotal - $discount - $marketplaceFee, 0);
$totalAmount = max($subtotal - $discount, 0);
$channel = OrderChannel::from($validated['channel']);
$order->customer_id = $validated['customer_id'] ?? null;
$order->channel = $validated['channel'];
$order->channel = $channel;
$order->price_type = $priceType;
$order->subtotal = $subtotal;
$order->discount = $discount;
$order->marketplace_fee = $marketplaceFee;
$order->marketplace_settings_snapshot = $this->marketplaceService->snapshotForChannel($channel);
$order->total_amount = $totalAmount;
$order->notes = $validated['notes'] ?? null;
$order->save();

View File

@ -2,6 +2,7 @@
namespace App\Services\System\Setting;
use App\Enums\OrderChannel;
use App\Settings\MarketplaceSettings;
class MarketplaceService
@ -52,4 +53,35 @@ public function updateMarketplace(array $validated): void
$settings->save();
}
/**
* @return array<string, mixed>|null
*/
public function snapshotForChannel(OrderChannel $channel): ?array
{
$settings = app(MarketplaceSettings::class);
return match ($channel) {
OrderChannel::SHOPEE => [
'platform' => 'shopee',
'commission_fee' => $settings->shopee_commission_fee,
'transaction_fee' => $settings->shopee_transaction_fee,
'service_fee' => $settings->shopee_service_fee,
'payment_fee' => $settings->shopee_payment_fee,
'affiliate_commission' => $settings->shopee_affiliate_commission,
'shipping_subsidy' => $settings->shopee_shipping_subsidy,
'voucher_fee' => $settings->shopee_voucher_fee,
],
OrderChannel::TIKTOK => [
'platform' => 'tiktok',
'admin_fee' => $settings->tiktok_shop_admin_fee,
'transaction_fee' => $settings->tiktok_shop_transaction_fee,
'payment_fee' => $settings->tiktok_shop_payment_fee,
'affiliate_commission' => $settings->tiktok_shop_affiliate_commission,
'shipping_subsidy' => $settings->tiktok_shop_shipping_subsidy,
'vat_rate' => $settings->tiktok_shop_vat_rate,
],
OrderChannel::STORE => null,
};
}
}

View File

@ -22,6 +22,7 @@ public function up(): void
$table->enum('status', array_column(OrderStatus::cases(), 'value'))->default(OrderStatus::PENDING->value);
$table->unsignedBigInteger('subtotal');
$table->unsignedBigInteger('discount')->default(0);
$table->json('marketplace_settings_snapshot')->nullable();
$table->unsignedBigInteger('total_amount');
$table->text('notes')->nullable();

View File

@ -113,10 +113,6 @@ function statusVariant(status: string): 'default' | 'secondary' | 'destructive'
}}</strong></span>
<span>Diskon <strong class="text-foreground">{{ order.discount_formatted
}}</strong></span>
<span v-if="order.channel !== 'store'">
Biaya MP <strong class="text-foreground">{{ order.marketplace_fee_formatted
}}</strong>
</span>
<span>Total <strong class="text-primary">{{ order.total_amount_formatted
}}</strong></span>
</div>

View File

@ -49,7 +49,6 @@ const props = defineProps<{
channel: string;
price_type: string;
discount: string;
marketplace_fee: string;
notes: string;
items: OrderCartItem[];
};
@ -69,12 +68,10 @@ const form = useForm({
channel: 'store',
price_type: 'ecer',
discount: '',
marketplace_fee: '',
notes: '',
});
const isStoreChannel = computed(() => form.channel === 'store');
const isMarketplaceChannel = computed(() => form.channel === 'shopee' || form.channel === 'tiktok');
function populateForm() {
if (!props.initialData) {
@ -85,7 +82,6 @@ function populateForm() {
form.channel = props.initialData.channel;
form.price_type = props.initialData.price_type;
form.discount = props.initialData.discount;
form.marketplace_fee = props.initialData.marketplace_fee;
form.notes = props.initialData.notes;
cart.value = props.initialData.items.map((item) => ({ ...item }));
}
@ -177,8 +173,7 @@ const subtotal = computed(() =>
);
const discountAmount = computed(() => Number(parseRupiah(form.discount)) || 0);
const marketplaceFeeAmount = computed(() => Number(parseRupiah(form.marketplace_fee)) || 0);
const totalAmount = computed(() => Math.max(subtotal.value - discountAmount.value - marketplaceFeeAmount.value, 0));
const totalAmount = computed(() => Math.max(subtotal.value - discountAmount.value, 0));
function getVariantPrice(variant: ProductVariantItem): ProductPriceItem | undefined {
return variant.prices.find((price) => price.type === form.price_type);
@ -319,7 +314,6 @@ function buildFormData(): FormData {
formData.append('channel', form.channel);
formData.append('price_type', form.price_type);
formData.append('discount', parseRupiah(form.discount));
formData.append('marketplace_fee', parseRupiah(form.marketplace_fee));
formData.append('notes', form.notes);
if (props.method === 'put') {
@ -552,11 +546,6 @@ function submit() {
<RupiahInput id="discount" v-model="form.discount" placeholder="0" />
<FieldError :errors="formErrors(form, 'discount')" />
</Field>
<Field v-if="isMarketplaceChannel">
<FieldLabel for="marketplace_fee">Biaya Marketplace</FieldLabel>
<RupiahInput id="marketplace_fee" v-model="form.marketplace_fee" placeholder="0" />
<FieldError :errors="formErrors(form, 'marketplace_fee')" />
</Field>
<div class="flex justify-between text-base font-semibold">
<span>Total</span>
<span class="text-primary">Rp {{ formatRupiah(totalAmount) }}</span>

View File

@ -40,7 +40,6 @@ export function encodeOrderReceipt(
const createdBy = order.created_by?.profile?.full_name
?? order.created_by?.username
?? '-';
const isMarketplace = order.channel !== 'store';
encoder.initialize()
.align('center')
@ -98,14 +97,9 @@ export function encodeOrderReceipt(
const summaryRows: string[][] = [
['Subtotal', order.subtotal_formatted],
['Diskon', order.discount_formatted],
['Total', order.total_amount_formatted],
];
if (isMarketplace) {
summaryRows.push(['Biaya MP', order.marketplace_fee_formatted]);
}
summaryRows.push(['Total', order.total_amount_formatted]);
encoder.table(
[
{ width: labelColumnWidth, align: 'left' },

View File

@ -20,7 +20,6 @@ const initialData = computed(() => ({
channel: props.order.channel,
price_type: props.order.price_type,
discount: String(props.order.discount),
marketplace_fee: String(props.order.marketplace_fee),
notes: props.order.notes ?? '',
items: props.order.items.map((item) => ({
product_variant_id: item.product_variant_id,

View File

@ -47,7 +47,6 @@ export type OrderListItem = {
available_actions: OrderStatusAction[];
subtotal_formatted: string;
discount_formatted: string;
marketplace_fee_formatted: string;
total_amount_formatted: string;
notes: string | null;
created_at_formatted: string;
@ -81,7 +80,6 @@ export type OrderEditItem = {
price_type: string;
status: string;
discount: number;
marketplace_fee: number;
notes: string | null;
items: Array<{
product_variant_id: number;