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

This commit is contained in:
Yoga Pangestu 2026-07-05 02:24:24 +07:00
parent 02fb2045d2
commit 352599bf00
5 changed files with 48 additions and 10 deletions

View File

@ -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(

View File

@ -35,7 +35,7 @@ defineProps<{
canComplete: boolean;
canCreate: boolean;
cartEmpty: boolean;
submitLabel: string;
isCashierUser: boolean;
}>();
const printAfterSave = defineModel<boolean>('printAfterSave', { required: true });
@ -70,7 +70,7 @@ const isUploading = computed(() => photoState.value.pendingUploads > 0);
</div>
</div>
<Field v-if="canComplete">
<Field v-if="canComplete && !isCashierUser">
<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" />

View File

@ -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<MediaUploadState>(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"
/>
</FieldSet>
</FieldGroup>

View File

@ -107,7 +107,7 @@ const emit = defineEmits<{
<FieldError :errors="formErrors(form, 'shopee_order_id')" />
</Field>
<Field v-if="!isCashierUser">
<Field>
<FieldLabel for="payment_type" required>Tipe Pembayaran</FieldLabel>
<Select v-model="form.payment_type" :disabled="isMarketplaceChannel">
<SelectTrigger id="payment_type" class="w-full">
@ -163,7 +163,7 @@ const emit = defineEmits<{
<FieldError :errors="formErrors(form, 'customer_id')" />
</Field>
<Field>
<Field v-if="!isCashierUser">
<FieldLabel for="marketing">Marketing</FieldLabel>
<Select v-model="form.marketing_id" :disabled="isMarketingUser">
<SelectTrigger id="marketing" class="w-full">

View File

@ -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 ─────────────────────────────────────────────────