feat: add subtotal field to orders and include it in print receipts and UI displays

This commit is contained in:
Yoga Pangestu 2026-04-29 20:42:33 +07:00
parent cf0d60f74b
commit ede4831f96
11 changed files with 40 additions and 4 deletions

View File

@ -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'],

View File

@ -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)],

View File

@ -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',

View File

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

View File

@ -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');

View File

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

View File

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

View File

@ -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);

View File

@ -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) {

View File

@ -105,7 +105,13 @@ export const getColumns = ({
<DataTableColumnHeader column={column} title="Total" />
),
cell: ({ row }) => (
<div className="flex flex-col items-end gap-1">
<div className="flex flex-col gap-1">
<span className="font-medium text-primary">
{row.original.subtotal_formatted}
</span>
<span className="text-xs text-muted-foreground">
- {row.original.discount_formatted}
</span>
<span className="font-bold text-primary">
{row.original.total_formatted}
</span>

View File

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