From 352599bf00e03bec26ba3cef9f8f30a5f99109c8 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 5 Jul 2026 02:24:24 +0700 Subject: [PATCH] feat: update OrderService and frontend components to automatically set order status to completed for cashiers; adjust payment and marketing fields accordingly; enhance tests to verify new behavior --- app/Services/Manage/OrderService.php | 4 ++- .../orders/form/OrderPosCheckoutSection.vue | 4 +-- .../admin/manage/orders/form/OrderPosForm.vue | 16 ++++++---- .../orders/form/OrderPosMetadataFields.vue | 4 +-- tests/Feature/Admin/Manage/OrderTest.php | 30 +++++++++++++++++++ 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/app/Services/Manage/OrderService.php b/app/Services/Manage/OrderService.php index fe46683..9c2d045 100644 --- a/app/Services/Manage/OrderService.php +++ b/app/Services/Manage/OrderService.php @@ -393,8 +393,10 @@ public function create(array $validated, User $user): Order if ($user->hasRole('cashier')) { $validated['channel'] = 'store'; $validated['price_type'] = 'retail'; - $validated['payment_type'] = 'cash'; + $validated['payment_type'] = $validated['payment_type'] ?? 'cash'; + $validated['status'] = OrderStatus::COMPLETED->value; unset($validated['customer_id']); + unset($validated['marketing_id']); } $order = $this->runInTransaction( diff --git a/resources/js/pages/admin/manage/orders/form/OrderPosCheckoutSection.vue b/resources/js/pages/admin/manage/orders/form/OrderPosCheckoutSection.vue index c0fcc81..5e51e1a 100644 --- a/resources/js/pages/admin/manage/orders/form/OrderPosCheckoutSection.vue +++ b/resources/js/pages/admin/manage/orders/form/OrderPosCheckoutSection.vue @@ -35,7 +35,7 @@ defineProps<{ canComplete: boolean; canCreate: boolean; cartEmpty: boolean; - submitLabel: string; + isCashierUser: boolean; }>(); const printAfterSave = defineModel('printAfterSave', { required: true }); @@ -70,7 +70,7 @@ const isUploading = computed(() => photoState.value.pendingUploads > 0); - +
diff --git a/resources/js/pages/admin/manage/orders/form/OrderPosForm.vue b/resources/js/pages/admin/manage/orders/form/OrderPosForm.vue index 566438d..c204e43 100644 --- a/resources/js/pages/admin/manage/orders/form/OrderPosForm.vue +++ b/resources/js/pages/admin/manage/orders/form/OrderPosForm.vue @@ -84,7 +84,7 @@ const form = useForm({ discount: '', nego_price: '', notes: '', - status: OrderStatus.PENDING as string, + status: (isCashierUser.value ? OrderStatus.COMPLETED : OrderStatus.PENDING) as string, }); const isStoreChannel = computed(() => form.channel === 'store'); @@ -95,7 +95,7 @@ const showPhotoInput = computed(() => const customerFormOpen = ref(false); const cartDetailOpen = ref(false); -const printAfterSave = ref(false); +const printAfterSave = ref(isCashierUser.value); const selectedPaperSize = ref<'58mm' | '80mm'>(PaperSize.MM_80); const photoState = ref(createMediaUploadState()); @@ -203,7 +203,7 @@ function buildFormData(): FormData { if (isCashierUser.value) { formData.append('channel', 'store'); formData.append('price_type', 'retail'); - formData.append('payment_type', OrderPaymentType.CASH); + formData.append('payment_type', form.payment_type); } else { formData.append('channel', form.channel); formData.append('price_type', form.price_type); @@ -227,13 +227,18 @@ function buildFormData(): FormData { formData.append('discount', parseRupiah(form.discount)); formData.append('nego_price', parseRupiah(form.nego_price)); formData.append('notes', form.notes); - formData.append('status', form.status); + + if (isCashierUser.value) { + formData.append('status', OrderStatus.COMPLETED); + } else { + formData.append('status', form.status); + } if (showPhotoInput.value) { appendRootPhotosToFormData(formData, photoState.value); } - if (isCreateMode.value && printAfterSave.value) { + if (isCreateMode.value && (isCashierUser.value || printAfterSave.value)) { formData.append('print', '1'); formData.append('paper_size', selectedPaperSize.value); } @@ -340,6 +345,7 @@ function submit() { :can-create="can('orders.create')" :cart-empty="cart.length === 0" :submit-label="submitLabel" + :is-cashier-user="isCashierUser" /> diff --git a/resources/js/pages/admin/manage/orders/form/OrderPosMetadataFields.vue b/resources/js/pages/admin/manage/orders/form/OrderPosMetadataFields.vue index 087997b..09abd6c 100644 --- a/resources/js/pages/admin/manage/orders/form/OrderPosMetadataFields.vue +++ b/resources/js/pages/admin/manage/orders/form/OrderPosMetadataFields.vue @@ -107,7 +107,7 @@ const emit = defineEmits<{ - + Tipe Pembayaran diff --git a/tests/Feature/Admin/Manage/OrderTest.php b/tests/Feature/Admin/Manage/OrderTest.php index 658b089..0f3ceea 100644 --- a/tests/Feature/Admin/Manage/OrderTest.php +++ b/tests/Feature/Admin/Manage/OrderTest.php @@ -248,6 +248,36 @@ function setupOrderDraftItems(User $user): ProductVariant expect($variant->fresh()->stock)->toBe($initialStock - 5); }); + + test('cashier creates order automatically sets status to completed and unsets marketing and customer', function () { + $user = User::factory()->create(); + $user->assignRole('cashier'); + $user->givePermissionTo(PermissionEnum::ORDERS_CREATE->value); + $user->forgetCachedPermissions(); + + setupOrderDraftItems($user); + + $marketing = User::factory()->create(); + $customer = Customer::factory()->create(); + + $this->actingAs($user) + ->post(route('admin.manage.orders.store'), [ + 'customer_id' => $customer->id, + 'marketing_id' => $marketing->id, + 'channel' => OrderChannel::STORE->value, + 'price_type' => PriceType::RETAIL->value, + 'payment_type' => PaymentType::CASH->value, + 'status' => OrderStatus::PENDING->value, + 'discount' => 0, + ]) + ->assertRedirect(route('admin.manage.orders.index')); + + $order = Order::query()->latest()->first(); + expect($order)->not->toBeNull(); + expect($order->status)->toBe(OrderStatus::COMPLETED); + expect($order->marketing_id)->toBeNull(); + expect($order->customer_id)->toBeNull(); + }); }); // ─── Show ─────────────────────────────────────────────────