feat: add order status management to OrderRequest and OrderService, update frontend forms for status handling
This commit is contained in:
parent
60c58a9709
commit
fe1549e0a5
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Requests\Admin\Manage;
|
||||
|
||||
use App\Enums\OrderChannel;
|
||||
use App\Enums\OrderStatus;
|
||||
use App\Enums\PaymentType;
|
||||
use App\Enums\Permission;
|
||||
use App\Enums\PriceType;
|
||||
@ -39,6 +40,7 @@ public function rules(): array
|
||||
'shopee_order_id' => [Rule::requiredIf(fn () => $this->channel === OrderChannel::SHOPEE->value), 'string', 'max:100'],
|
||||
'discount' => ['nullable', 'integer', 'min:0'],
|
||||
'notes' => ['nullable', 'string'],
|
||||
'status' => ['nullable', Rule::enum(OrderStatus::class)],
|
||||
];
|
||||
|
||||
if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
|
||||
@ -71,6 +73,7 @@ public function attributes(): array
|
||||
'shopee_order_id' => 'ID pesanan Shopee',
|
||||
'discount' => 'diskon',
|
||||
'notes' => 'keterangan',
|
||||
'status' => 'status pesanan',
|
||||
'items' => 'produk',
|
||||
'items.*.product_variant_id' => 'varian produk',
|
||||
'items.*.quantity' => 'jumlah',
|
||||
@ -85,6 +88,12 @@ public function withValidator(Validator $validator): void
|
||||
$validator->errors()->add('customer_id', 'Pelanggan wajib diisi jika marketing dipilih.');
|
||||
}
|
||||
|
||||
if ($this->filled('status') && $this->status === OrderStatus::COMPLETED->value) {
|
||||
if (! $this->user()?->can(Permission::ORDERS_COMPLETE->value)) {
|
||||
$validator->errors()->add('status', 'Anda tidak memiliki izin untuk menandai pesanan sebagai selesai.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
|
||||
/** @var Order $order */
|
||||
$order = $this->route('order');
|
||||
|
||||
@ -421,6 +421,10 @@ public function create(array $validated, User $user): Order
|
||||
$totalAmount = max($subtotal - $discount, 0);
|
||||
$channel = OrderChannel::from($validated['channel']);
|
||||
|
||||
$status = isset($validated['status'])
|
||||
? OrderStatus::from($validated['status'])
|
||||
: OrderStatus::PENDING;
|
||||
|
||||
$order = Order::create([
|
||||
'customer_id' => $validated['customer_id'] ?? null,
|
||||
'marketing_id' => $validated['marketing_id'] ?? null,
|
||||
@ -428,7 +432,7 @@ public function create(array $validated, User $user): Order
|
||||
'price_type' => $priceType,
|
||||
'payment_type' => PaymentType::from($validated['payment_type']),
|
||||
'is_affiliate' => $validated['is_affiliate'] ?? false,
|
||||
'status' => OrderStatus::PENDING,
|
||||
'status' => $status,
|
||||
'tiktok_order_id' => $validated['tiktok_order_id'] ?? null,
|
||||
'shopee_order_id' => $validated['shopee_order_id'] ?? null,
|
||||
'created_by_id' => $user->id,
|
||||
@ -547,6 +551,15 @@ public function update(Order $order, array $validated): void
|
||||
);
|
||||
$order->total_amount = $totalAmount;
|
||||
$order->notes = $validated['notes'] ?? null;
|
||||
|
||||
if (isset($validated['status'])) {
|
||||
$newStatus = OrderStatus::from($validated['status']);
|
||||
|
||||
if ($order->status->canTransitionTo($newStatus) || $newStatus === $order->status) {
|
||||
$order->status = $newStatus;
|
||||
}
|
||||
}
|
||||
|
||||
$order->save();
|
||||
|
||||
foreach ($lineItems as $itemData) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export const OrderStatus = {
|
||||
PENDING: 'pending',
|
||||
PROCESSING: 'processing',
|
||||
COMPLETED: 'completed',
|
||||
CANCELLED: 'cancelled',
|
||||
|
||||
@ -37,6 +37,7 @@ const initialData = computed(() => ({
|
||||
shopee_order_id: props.order.shopee_order_id ?? '',
|
||||
discount: String(props.order.discount),
|
||||
notes: props.order.notes ?? '',
|
||||
status: props.order.status,
|
||||
items: props.order.items.map((item) => ({
|
||||
product_variant_id: item.product_variant_id,
|
||||
product_name: item.product_name,
|
||||
|
||||
@ -42,6 +42,7 @@ import { Textarea } from '@/components/ui/textarea';
|
||||
import { useCan } from '@/composables/useCan';
|
||||
import { OrderChannel } from '@/constants/order-channel';
|
||||
import { OrderPaymentType } from '@/constants/order-payment-type';
|
||||
import { OrderStatus } from '@/constants/order-status';
|
||||
import { PaperSize } from '@/constants/paper-size';
|
||||
import { StockQuality } from '@/constants/stock-quality';
|
||||
import { apiFetch } from '@/lib/api';
|
||||
@ -75,6 +76,7 @@ const props = defineProps<{
|
||||
shopee_order_id: string;
|
||||
discount: string;
|
||||
notes: string;
|
||||
status: string;
|
||||
items: OrderCartItem[];
|
||||
};
|
||||
draftItems?: OrderCartItem[];
|
||||
@ -126,6 +128,7 @@ const form = useForm({
|
||||
shopee_order_id: '',
|
||||
discount: '',
|
||||
notes: '',
|
||||
status: OrderStatus.PENDING,
|
||||
});
|
||||
|
||||
const isStoreChannel = computed(() => form.channel === 'store');
|
||||
@ -146,6 +149,7 @@ function populateForm() {
|
||||
form.shopee_order_id = props.initialData.shopee_order_id;
|
||||
form.discount = props.initialData.discount;
|
||||
form.notes = props.initialData.notes;
|
||||
form.status = props.initialData.status || OrderStatus.PENDING;
|
||||
cart.value = props.initialData.items.map((item) => ({
|
||||
...item,
|
||||
stock_quality: item.stock_quality ?? StockQuality.GOOD,
|
||||
@ -457,6 +461,7 @@ function buildFormData(): FormData {
|
||||
|
||||
formData.append('discount', parseRupiah(form.discount));
|
||||
formData.append('notes', form.notes);
|
||||
formData.append('status', form.status);
|
||||
|
||||
if (isCreateMode.value && printAfterSave.value) {
|
||||
formData.append('print', '1');
|
||||
@ -823,6 +828,20 @@ function submit() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Field v-if="can('orders.complete')">
|
||||
<div class="flex items-center gap-3">
|
||||
<Switch id="status-completed" :model-value="form.status === OrderStatus.COMPLETED"
|
||||
@update:model-value="form.status = $event ? OrderStatus.COMPLETED : OrderStatus.PENDING" />
|
||||
<Label for="status-completed" class="cursor-pointer text-sm">
|
||||
Pesanan selesai
|
||||
</Label>
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Aktifkan jika pesanan sudah selesai diproses.
|
||||
</p>
|
||||
<FieldError :errors="formErrors(form, 'status')" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<FieldLabel for="notes">Keterangan</FieldLabel>
|
||||
<Textarea id="notes" v-model="form.notes" placeholder="Contoh: Pesanan walk-in"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user