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:
parent
64088c82bb
commit
584387504c
@ -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',
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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' },
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user