From ede4831f96da368df16fb609955ada029dd5ebb4 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Wed, 29 Apr 2026 20:42:33 +0700 Subject: [PATCH] feat: add subtotal field to orders and include it in print receipts and UI displays --- .../Controllers/Admin/Manage/OrderController.php | 2 ++ .../Requests/Admin/Manage/Order/OrderRequest.php | 1 + app/Models/Order.php | 13 +++++++++++-- database/factories/OrderFactory.php | 1 + .../2026_04_21_193515_create_orders_table.php | 1 + database/seeders/OrderSeeder.php | 1 + resources/js/pages/admin/manage/order/create.tsx | 2 ++ resources/js/pages/admin/manage/order/edit.tsx | 8 +++++++- .../admin/manage/order/hooks/use-order-print.ts | 5 +++++ .../pages/admin/manage/order/partials/columns.tsx | 8 +++++++- resources/js/types/order.ts | 2 ++ 11 files changed, 40 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Admin/Manage/OrderController.php b/app/Http/Controllers/Admin/Manage/OrderController.php index 8b2c260..7f45dab 100644 --- a/app/Http/Controllers/Admin/Manage/OrderController.php +++ b/app/Http/Controllers/Admin/Manage/OrderController.php @@ -69,6 +69,7 @@ public function store(OrderRequest $request): RedirectResponse 'order_number' => $orderNumber, 'customer_name' => $validated['customer_name'], 'cogs' => $cogs, + 'subtotal' => $totalItemsPrice, 'discount' => $validated['discount'], 'payment' => $validated['payment'], 'total' => $totalItemsPrice - $validated['discount'], @@ -148,6 +149,7 @@ public function update(OrderRequest $request, Order $order): RedirectResponse $order->update([ 'customer_name' => $validated['customer_name'], 'cogs' => $cogs, + 'subtotal' => $totalItemsPrice, 'discount' => $validated['discount'], 'payment' => $validated['payment'], 'total' => $totalItemsPrice - $validated['discount'], diff --git a/app/Http/Requests/Admin/Manage/Order/OrderRequest.php b/app/Http/Requests/Admin/Manage/Order/OrderRequest.php index f299aab..1a40f4d 100644 --- a/app/Http/Requests/Admin/Manage/Order/OrderRequest.php +++ b/app/Http/Requests/Admin/Manage/Order/OrderRequest.php @@ -29,6 +29,7 @@ public function rules(): array { return [ 'customer_name' => ['required', 'string', 'max:100'], + 'subtotal' => ['required', 'integer', 'min:0'], 'discount' => ['required', 'integer', 'min:0'], 'payment' => ['required', 'integer', 'min:0'], 'payment_method' => ['required', Rule::enum(PaymentMethod::class)], diff --git a/app/Models/Order.php b/app/Models/Order.php index 21a2617..91e9a17 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -18,7 +18,7 @@ use Spatie\Activitylog\Traits\LogsActivity; #[Guarded(['id'])] -#[Appends(['hpp_formatted', 'discount_formatted', 'payment_formatted', 'total_formatted'])] +#[Appends(['hpp_formatted', 'subtotal_formatted', 'discount_formatted', 'payment_formatted', 'total_formatted'])] class Order extends Model { use HasFactory, LogsActivity, SoftDeletes; @@ -27,6 +27,7 @@ protected function casts(): array { return [ 'cogs' => 'integer', + 'subtotal' => 'integer', 'discount' => 'integer', 'payment' => 'integer', 'total' => 'integer', @@ -43,6 +44,13 @@ protected function hppFormatted(): Attribute ); } + protected function subtotalFormatted(): Attribute + { + return Attribute::make( + get: fn () => 'Rp '.number_format($this->subtotal, 0, ',', '.'), + ); + } + protected function discountFormatted(): Attribute { return Attribute::make( @@ -77,7 +85,7 @@ public function items(): HasMany public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() - ->logOnly(['order_number', 'customer_name', 'cogs', 'discount', 'payment', 'total', 'payment_method', 'order_status', 'order_channel']) + ->logOnly(['order_number', 'customer_name', 'cogs', 'subtotal', 'discount', 'payment', 'total', 'payment_method', 'order_status', 'order_channel']) ->logOnlyDirty() ->useLogName('Pesanan'); } @@ -96,6 +104,7 @@ public function tapActivity(Activity $activity, string $eventName) 'order_number' => 'Nomor Order', 'customer_name' => 'Nama Pelanggan', 'cogs' => 'Modal', + 'subtotal' => 'Subtotal', 'discount' => 'Diskon', 'payment' => 'Bayar', 'total' => 'Total', diff --git a/database/factories/OrderFactory.php b/database/factories/OrderFactory.php index 271fb4b..c3afa2c 100644 --- a/database/factories/OrderFactory.php +++ b/database/factories/OrderFactory.php @@ -35,6 +35,7 @@ public function definition(): array 'order_number' => 'ORD-'.$date->format('YmdHi').'-'.$sequence, 'customer_name' => $this->faker->name(), 'cogs' => 0, + 'subtotal' => 0, 'discount' => 0, 'payment' => 0, 'total' => 0, diff --git a/database/migrations/2026_04_21_193515_create_orders_table.php b/database/migrations/2026_04_21_193515_create_orders_table.php index 723c002..f52e96d 100644 --- a/database/migrations/2026_04_21_193515_create_orders_table.php +++ b/database/migrations/2026_04_21_193515_create_orders_table.php @@ -20,6 +20,7 @@ public function up(): void $table->string('order_number', 30); $table->string('customer_name', 100); $table->unsignedInteger('cogs'); + $table->unsignedInteger('subtotal'); $table->unsignedInteger('discount'); $table->unsignedInteger('payment'); $table->unsignedInteger('total'); diff --git a/database/seeders/OrderSeeder.php b/database/seeders/OrderSeeder.php index 87170a0..e30e50e 100644 --- a/database/seeders/OrderSeeder.php +++ b/database/seeders/OrderSeeder.php @@ -74,6 +74,7 @@ private function createOrderWithItems(Order $order, $users, $products): void $order->update([ 'cogs' => $totalOrderHpp, + 'subtotal' => $totalOrderPrice, 'discount' => $discount, 'total' => $finalTotal, 'payment' => $finalTotal, // Assume fully paid diff --git a/resources/js/pages/admin/manage/order/create.tsx b/resources/js/pages/admin/manage/order/create.tsx index db9f3d9..340c1f0 100644 --- a/resources/js/pages/admin/manage/order/create.tsx +++ b/resources/js/pages/admin/manage/order/create.tsx @@ -64,6 +64,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha const { data, setData, post, processing, errors, transform } = useForm({ customer_name: '', + subtotal: 0, discount: 0, payment: 0, payment_method: 'cash', @@ -167,6 +168,7 @@ return; transform((data) => ({ ...data, + subtotal: subtotal, items: cartItems.map(item => ({ product_id: item.product_id, qty: item.qty, diff --git a/resources/js/pages/admin/manage/order/edit.tsx b/resources/js/pages/admin/manage/order/edit.tsx index 9362b3b..96a59e3 100644 --- a/resources/js/pages/admin/manage/order/edit.tsx +++ b/resources/js/pages/admin/manage/order/edit.tsx @@ -58,8 +58,9 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels, return Array.from(map.entries()).map(([id, name]) => ({ id, name })); }, [products]); - const { data, setData, patch, processing, errors } = useForm({ + const { data, setData, patch, processing, errors, transform } = useForm({ customer_name: order.customer_name, + subtotal: order.subtotal, discount: order.discount, payment: order.payment, payment_method: order.payment_method, @@ -187,6 +188,11 @@ return; return; } + transform((data) => ({ + ...data, + subtotal: subtotal, + })); + patch(orderRoutes.update(order.id).url, { onSuccess: (response: any) => { toast.success(response.props.flash.success); diff --git a/resources/js/pages/admin/manage/order/hooks/use-order-print.ts b/resources/js/pages/admin/manage/order/hooks/use-order-print.ts index c73b92b..963c7a4 100644 --- a/resources/js/pages/admin/manage/order/hooks/use-order-print.ts +++ b/resources/js/pages/admin/manage/order/hooks/use-order-print.ts @@ -59,6 +59,11 @@ export function useOrderPrint() { result.line(line); + const subtotalLabel = 'Subtotal:'; + const subtotalVal = order.subtotal_formatted; + const sSpaces = width - subtotalLabel.length - subtotalVal.length; + result.line(subtotalLabel + ' '.repeat(Math.max(0, sSpaces)) + subtotalVal); + const discountAmount = Number(order.discount || 0); if (discountAmount > 0) { diff --git a/resources/js/pages/admin/manage/order/partials/columns.tsx b/resources/js/pages/admin/manage/order/partials/columns.tsx index 330b9a6..7ca3b19 100644 --- a/resources/js/pages/admin/manage/order/partials/columns.tsx +++ b/resources/js/pages/admin/manage/order/partials/columns.tsx @@ -105,7 +105,13 @@ export const getColumns = ({ ), cell: ({ row }) => ( -
+
+ + {row.original.subtotal_formatted} + + + - {row.original.discount_formatted} + {row.original.total_formatted} diff --git a/resources/js/types/order.ts b/resources/js/types/order.ts index 579df8d..f7ff2c0 100644 --- a/resources/js/types/order.ts +++ b/resources/js/types/order.ts @@ -5,6 +5,8 @@ export interface Order { order_number: string; customer_name: string; cogs: number; + subtotal: number; + subtotal_formatted: string; discount: number; payment: number; total: number;