Refactor order and payroll models to replace 'net_amount' with 'total_amount'. Update related methods, migrations, factories, and frontend components to ensure consistent handling of total amounts across the application.

This commit is contained in:
Yoga Pangestu 2026-06-13 00:31:53 +07:00
parent 521e8c5538
commit 64088c82bb
17 changed files with 57 additions and 52 deletions

View File

@ -20,7 +20,7 @@
'subtotal_formatted', 'subtotal_formatted',
'discount_formatted', 'discount_formatted',
'marketplace_fee_formatted', 'marketplace_fee_formatted',
'net_amount_formatted', 'total_amount_formatted',
'created_at_formatted', 'created_at_formatted',
'channel_label', 'channel_label',
'price_type_label', 'price_type_label',
@ -41,7 +41,7 @@ protected function casts(): array
'subtotal' => 'integer', 'subtotal' => 'integer',
'discount' => 'integer', 'discount' => 'integer',
'marketplace_fee' => 'integer', 'marketplace_fee' => 'integer',
'net_amount' => 'integer', 'total_amount' => 'integer',
]; ];
} }
@ -93,10 +93,10 @@ public function marketplaceFeeFormatted(): Attribute
); );
} }
public function netAmountFormatted(): Attribute public function totalAmountFormatted(): Attribute
{ {
return Attribute::make( return Attribute::make(
get: fn () => 'Rp '.number_format($this->net_amount, 0, ',', '.'), get: fn () => 'Rp '.number_format($this->total_amount, 0, ',', '.'),
); );
} }

View File

@ -19,7 +19,7 @@
'base_salary_formatted', 'base_salary_formatted',
'bonus_amount_formatted', 'bonus_amount_formatted',
'deduction_amount_formatted', 'deduction_amount_formatted',
'net_amount_formatted', 'total_amount_formatted',
'status_label', 'status_label',
'employee_name', 'employee_name',
'paid_at_formatted', 'paid_at_formatted',
@ -37,7 +37,7 @@ protected function casts(): array
'base_salary' => 'integer', 'base_salary' => 'integer',
'bonus_amount' => 'integer', 'bonus_amount' => 'integer',
'deduction_amount' => 'integer', 'deduction_amount' => 'integer',
'net_amount' => 'integer', 'total_amount' => 'integer',
'status' => PayrollStatus::class, 'status' => PayrollStatus::class,
'paid_at' => 'datetime', 'paid_at' => 'datetime',
]; ];
@ -115,10 +115,10 @@ public function employeeName(): Attribute
); );
} }
public function netAmountFormatted(): Attribute public function totalAmountFormatted(): Attribute
{ {
return Attribute::make( return Attribute::make(
get: fn () => 'Rp '.number_format($this->net_amount, 0, ',', '.'), get: fn () => 'Rp '.number_format($this->total_amount, 0, ',', '.'),
); );
} }
@ -162,6 +162,6 @@ public function recalculateAmounts(): void
$this->bonus_amount = $bonusAmount; $this->bonus_amount = $bonusAmount;
$this->deduction_amount = $kasbonDeduction + $manualDeduction; $this->deduction_amount = $kasbonDeduction + $manualDeduction;
$this->net_amount = max(0, $this->base_salary + $bonusAmount - $this->deduction_amount); $this->total_amount = max(0, $this->base_salary + $bonusAmount - $this->deduction_amount);
} }
} }

View File

@ -54,14 +54,14 @@ public function resolvePeriod(?int $periodId): ?PayrollPeriod
} }
/** /**
* @return array{total_net_amount: int, total_net_amount_formatted: string, unpaid_count: int, paid_count: int} * @return array{total_total_amount: int, total_total_amount_formatted: string, unpaid_count: int, paid_count: int}
*/ */
public function periodSummary(PayrollPeriod $period): array public function periodSummary(PayrollPeriod $period): array
{ {
$totalNetAmount = (int) Payroll::query() $totalTotalAmount = (int) Payroll::query()
->where('payroll_period_id', $period->id) ->where('payroll_period_id', $period->id)
->where('status', PayrollStatus::UNPAID) ->where('status', PayrollStatus::UNPAID)
->sum('net_amount'); ->sum('total_amount');
$unpaidCount = Payroll::query() $unpaidCount = Payroll::query()
->where('payroll_period_id', $period->id) ->where('payroll_period_id', $period->id)
@ -74,8 +74,8 @@ public function periodSummary(PayrollPeriod $period): array
->count(); ->count();
return [ return [
'total_net_amount' => $totalNetAmount, 'total_total_amount' => $totalTotalAmount,
'total_net_amount_formatted' => 'Rp '.number_format($totalNetAmount, 0, ',', '.'), 'total_total_amount_formatted' => 'Rp '.number_format($totalTotalAmount, 0, ',', '.'),
'unpaid_count' => $unpaidCount, 'unpaid_count' => $unpaidCount,
'paid_count' => $paidCount, 'paid_count' => $paidCount,
]; ];
@ -154,7 +154,7 @@ public function closePeriod(PayrollPeriod $period, User $user): void
$unpaidCount = Payroll::query() $unpaidCount = Payroll::query()
->where('payroll_period_id', $period->id) ->where('payroll_period_id', $period->id)
->where('status', PayrollStatus::UNPAID) ->where('status', PayrollStatus::UNPAID)
->where('net_amount', '>', 0) ->where('total_amount', '>', 0)
->count(); ->count();
if ($unpaidCount > 0) { if ($unpaidCount > 0) {
@ -189,7 +189,7 @@ public function generatePayrollsForPeriod(PayrollPeriod $period): void
'base_salary' => $employee->base_salary, 'base_salary' => $employee->base_salary,
'bonus_amount' => 0, 'bonus_amount' => 0,
'deduction_amount' => 0, 'deduction_amount' => 0,
'net_amount' => 0, 'total_amount' => 0,
'status' => PayrollStatus::UNPAID, 'status' => PayrollStatus::UNPAID,
]); ]);
@ -242,7 +242,7 @@ public function pay(Payroll $payroll, User $user): void
]); ]);
} }
if ($payroll->net_amount <= 0) { if ($payroll->total_amount <= 0) {
DB::transaction(function () use ($payroll, $user): void { DB::transaction(function () use ($payroll, $user): void {
$payroll->status = PayrollStatus::PAID; $payroll->status = PayrollStatus::PAID;
$payroll->paid_at = now(); $payroll->paid_at = now();
@ -264,7 +264,7 @@ public function pay(Payroll $payroll, User $user): void
$cashTransaction = $this->cashService->recordOutgoing( $cashTransaction = $this->cashService->recordOutgoing(
$payroll, $payroll,
$payroll->net_amount, $payroll->total_amount,
$description, $description,
$user, $user,
); );
@ -332,7 +332,7 @@ private function settleKasbonFromPayroll(Payroll $payroll, User $user): void
private function applySorting(Builder $query, string $sort, string $direction): void private function applySorting(Builder $query, string $sort, string $direction): void
{ {
if (in_array($sort, ['base_salary', 'bonus_amount', 'deduction_amount', 'net_amount', 'status', 'created_at'], true)) { if (in_array($sort, ['base_salary', 'bonus_amount', 'deduction_amount', 'total_amount', 'status', 'created_at'], true)) {
$query->orderBy($sort, $direction); $query->orderBy($sort, $direction);
return; return;

View File

@ -284,7 +284,7 @@ public function create(array $validated, User $user): Order
$subtotal = $draftItems->sum('subtotal'); $subtotal = $draftItems->sum('subtotal');
$discount = (int) ($validated['discount'] ?? 0); $discount = (int) ($validated['discount'] ?? 0);
$marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0); $marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0);
$netAmount = max($subtotal - $discount - $marketplaceFee, 0); $totalAmount = max($subtotal - $discount - $marketplaceFee, 0);
$order = Order::create([ $order = Order::create([
'customer_id' => $validated['customer_id'] ?? null, 'customer_id' => $validated['customer_id'] ?? null,
@ -295,7 +295,7 @@ public function create(array $validated, User $user): Order
'subtotal' => $subtotal, 'subtotal' => $subtotal,
'discount' => $discount, 'discount' => $discount,
'marketplace_fee' => $marketplaceFee, 'marketplace_fee' => $marketplaceFee,
'net_amount' => $netAmount, 'total_amount' => $totalAmount,
'notes' => $validated['notes'] ?? null, 'notes' => $validated['notes'] ?? null,
]); ]);
@ -334,7 +334,7 @@ public function update(Order $order, array $validated): void
$subtotal = array_sum(array_column($lineItems, 'subtotal')); $subtotal = array_sum(array_column($lineItems, 'subtotal'));
$discount = (int) ($validated['discount'] ?? 0); $discount = (int) ($validated['discount'] ?? 0);
$marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0); $marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0);
$netAmount = max($subtotal - $discount - $marketplaceFee, 0); $totalAmount = max($subtotal - $discount - $marketplaceFee, 0);
$order->customer_id = $validated['customer_id'] ?? null; $order->customer_id = $validated['customer_id'] ?? null;
$order->channel = $validated['channel']; $order->channel = $validated['channel'];
@ -342,7 +342,7 @@ public function update(Order $order, array $validated): void
$order->subtotal = $subtotal; $order->subtotal = $subtotal;
$order->discount = $discount; $order->discount = $discount;
$order->marketplace_fee = $marketplaceFee; $order->marketplace_fee = $marketplaceFee;
$order->net_amount = $netAmount; $order->total_amount = $totalAmount;
$order->notes = $validated['notes'] ?? null; $order->notes = $validated['notes'] ?? null;
$order->save(); $order->save();
@ -528,7 +528,7 @@ private function incrementStock(OrderItem $item): void
private function applySorting(Builder $query, string $sort, string $direction): void private function applySorting(Builder $query, string $sort, string $direction): void
{ {
if (in_array($sort, ['created_at', 'net_amount', 'discount', 'subtotal', 'order_number'], true)) { if (in_array($sort, ['created_at', 'total_amount', 'discount', 'subtotal', 'order_number'], true)) {
$query->orderBy($sort, $direction); $query->orderBy($sort, $direction);
return; return;

View File

@ -31,7 +31,7 @@ public function definition(): array
'status' => OrderStatus::PENDING->value, 'status' => OrderStatus::PENDING->value,
'subtotal' => $subtotal, 'subtotal' => $subtotal,
'discount' => $discount, 'discount' => $discount,
'net_amount' => $subtotal - $discount, 'total_amount' => $subtotal - $discount,
'notes' => fake()->optional()->sentence(), 'notes' => fake()->optional()->sentence(),
'cash_transaction_id' => null, 'cash_transaction_id' => null,
'created_by_id' => User::factory(), 'created_by_id' => User::factory(),

View File

@ -26,7 +26,7 @@ public function definition(): array
'base_salary' => $baseSalary, 'base_salary' => $baseSalary,
'bonus_amount' => $bonusAmount, 'bonus_amount' => $bonusAmount,
'deduction_amount' => $deductionAmount, 'deduction_amount' => $deductionAmount,
'net_amount' => max(0, $baseSalary + $bonusAmount - $deductionAmount), 'total_amount' => max(0, $baseSalary + $bonusAmount - $deductionAmount),
'status' => PayrollStatus::UNPAID->value, 'status' => PayrollStatus::UNPAID->value,
'paid_at' => null, 'paid_at' => null,
'paid_by_id' => null, 'paid_by_id' => null,

View File

@ -19,7 +19,7 @@ public function up(): void
$table->unsignedInteger('base_salary'); $table->unsignedInteger('base_salary');
$table->unsignedBigInteger('bonus_amount')->default(0); $table->unsignedBigInteger('bonus_amount')->default(0);
$table->unsignedBigInteger('deduction_amount')->default(0); $table->unsignedBigInteger('deduction_amount')->default(0);
$table->unsignedBigInteger('net_amount'); $table->unsignedBigInteger('total_amount');
$table->enum('status', array_column(PayrollStatus::cases(), 'value'))->default(PayrollStatus::UNPAID->value); $table->enum('status', array_column(PayrollStatus::cases(), 'value'))->default(PayrollStatus::UNPAID->value);
$table->timestamp('paid_at')->nullable(); $table->timestamp('paid_at')->nullable();

View File

@ -22,7 +22,7 @@ public function up(): void
$table->enum('status', array_column(OrderStatus::cases(), 'value'))->default(OrderStatus::PENDING->value); $table->enum('status', array_column(OrderStatus::cases(), 'value'))->default(OrderStatus::PENDING->value);
$table->unsignedBigInteger('subtotal'); $table->unsignedBigInteger('subtotal');
$table->unsignedBigInteger('discount')->default(0); $table->unsignedBigInteger('discount')->default(0);
$table->unsignedBigInteger('net_amount'); $table->unsignedBigInteger('total_amount');
$table->text('notes')->nullable(); $table->text('notes')->nullable();
$table->foreignId('cash_transaction_id')->nullable()->unique()->constrained()->restrictOnDelete(); $table->foreignId('cash_transaction_id')->nullable()->unique()->constrained()->restrictOnDelete();

View File

@ -22,7 +22,7 @@ public function run(): void
'payroll_period_id' => $period->id, 'payroll_period_id' => $period->id,
'employee_id' => $employee->id, 'employee_id' => $employee->id,
'base_salary' => $employee->base_salary, 'base_salary' => $employee->base_salary,
'net_amount' => $employee->base_salary, 'total_amount' => $employee->base_salary,
]); ]);
}); });
} }

View File

@ -34,9 +34,9 @@ export function createColumns(
header: () => h(DataTableColumnHeader, { title: 'Potongan', column: 'deduction_amount' }), header: () => h(DataTableColumnHeader, { title: 'Potongan', column: 'deduction_amount' }),
}, },
{ {
accessorKey: 'net_amount_formatted', accessorKey: 'total_amount_formatted',
enableSorting: true, enableSorting: true,
header: () => h(DataTableColumnHeader, { title: 'Gaji Bersih', column: 'net_amount' }), header: () => h(DataTableColumnHeader, { title: 'Gaji Bersih', column: 'total_amount' }),
}, },
{ {
accessorKey: 'status_label', accessorKey: 'status_label',

View File

@ -64,6 +64,6 @@ function payPayroll() {
</div> </div>
<ConfirmDialog v-if="can('payroll.pay')" v-model:open="payConfirmOpen" title="Bayar gaji?" <ConfirmDialog v-if="can('payroll.pay')" v-model:open="payConfirmOpen" title="Bayar gaji?"
:description="`Gaji ${payroll.net_amount_formatted} untuk ${payroll.employee_name} akan dibayar dari kas.`" :description="`Gaji ${payroll.total_amount_formatted} untuk ${payroll.employee_name} akan dibayar dari kas.`"
confirm-label="Bayar" cancel-label="Batal" :loading="payProcessing" @confirm="payPayroll" /> confirm-label="Bayar" cancel-label="Batal" :loading="payProcessing" @confirm="payPayroll" />
</template> </template>

View File

@ -107,13 +107,18 @@ function statusVariant(status: string): 'default' | 'secondary' | 'destructive'
<p>Oleh {{ order.created_by?.profile?.full_name ?? order.created_by?.username }}</p> <p>Oleh {{ order.created_by?.profile?.full_name ?? order.created_by?.username }}</p>
</div> </div>
<div class="flex flex-wrap gap-x-4 gap-y-1 text-sm"> <div class="flex flex-wrap gap-x-4 gap-y-1 text-sm">
<span>Tipe Harga <strong class="text-foreground">{{ order.price_type_label }}</strong></span> <span>Tipe Harga <strong class="text-foreground">{{ order.price_type_label
<span>Subtotal <strong class="text-foreground">{{ order.subtotal_formatted }}</strong></span> }}</strong></span>
<span>Diskon <strong class="text-foreground">{{ order.discount_formatted }}</strong></span> <span>Subtotal <strong class="text-foreground">{{ order.subtotal_formatted
}}</strong></span>
<span>Diskon <strong class="text-foreground">{{ order.discount_formatted
}}</strong></span>
<span v-if="order.channel !== 'store'"> <span v-if="order.channel !== 'store'">
Biaya MP <strong class="text-foreground">{{ order.marketplace_fee_formatted }}</strong> Biaya MP <strong class="text-foreground">{{ order.marketplace_fee_formatted
}}</strong>
</span> </span>
<span>Net <strong class="text-primary">{{ order.net_amount_formatted }}</strong></span> <span>Total <strong class="text-primary">{{ order.total_amount_formatted
}}</strong></span>
</div> </div>
<p v-if="order.notes" class="text-muted-foreground text-sm"> <p v-if="order.notes" class="text-muted-foreground text-sm">
{{ order.notes }} {{ order.notes }}

View File

@ -178,7 +178,7 @@ const subtotal = computed(() =>
const discountAmount = computed(() => Number(parseRupiah(form.discount)) || 0); const discountAmount = computed(() => Number(parseRupiah(form.discount)) || 0);
const marketplaceFeeAmount = computed(() => Number(parseRupiah(form.marketplace_fee)) || 0); const marketplaceFeeAmount = computed(() => Number(parseRupiah(form.marketplace_fee)) || 0);
const netAmount = computed(() => Math.max(subtotal.value - discountAmount.value - marketplaceFeeAmount.value, 0)); const totalAmount = computed(() => Math.max(subtotal.value - discountAmount.value - marketplaceFeeAmount.value, 0));
function getVariantPrice(variant: ProductVariantItem): ProductPriceItem | undefined { function getVariantPrice(variant: ProductVariantItem): ProductPriceItem | undefined {
return variant.prices.find((price) => price.type === form.price_type); return variant.prices.find((price) => price.type === form.price_type);
@ -558,8 +558,8 @@ function submit() {
<FieldError :errors="formErrors(form, 'marketplace_fee')" /> <FieldError :errors="formErrors(form, 'marketplace_fee')" />
</Field> </Field>
<div class="flex justify-between text-base font-semibold"> <div class="flex justify-between text-base font-semibold">
<span>Net</span> <span>Total</span>
<span class="text-primary">Rp {{ formatRupiah(netAmount) }}</span> <span class="text-primary">Rp {{ formatRupiah(totalAmount) }}</span>
</div> </div>
</div> </div>

View File

@ -104,7 +104,7 @@ export function encodeOrderReceipt(
summaryRows.push(['Biaya MP', order.marketplace_fee_formatted]); summaryRows.push(['Biaya MP', order.marketplace_fee_formatted]);
} }
summaryRows.push(['Total', order.net_amount_formatted]); summaryRows.push(['Total', order.total_amount_formatted]);
encoder.table( encoder.table(
[ [

View File

@ -168,7 +168,7 @@ watch(
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div class="text-3xl font-bold tracking-tight"> <div class="text-3xl font-bold tracking-tight">
{{ summary.total_net_amount_formatted }} {{ summary.total_total_amount_formatted }}
</div> </div>
<p class="mt-1 text-sm text-muted-foreground"> <p class="mt-1 text-sm text-muted-foreground">
{{ summary.unpaid_count }} slip belum dibayar {{ summary.unpaid_count }} slip belum dibayar
@ -195,9 +195,9 @@ watch(
<Card v-if="payrolls && pagination" class="min-w-0"> <Card v-if="payrolls && pagination" class="min-w-0">
<CardContent class="min-w-0 pt-6"> <CardContent class="min-w-0 pt-6">
<DataTable v-model:search="search" :columns="columns" :data="payrolls.data" <DataTable v-model:search="search" :columns="columns" :data="payrolls.data" :pagination="pagination"
:pagination="pagination" :pagination-links="payrolls.links" :sort="currentSort" :pagination-links="payrolls.links" :sort="currentSort" @sort-change="setSort"
@sort-change="setSort" @filters-reset="resetFilters" /> @filters-reset="resetFilters" />
</CardContent> </CardContent>
</Card> </Card>
@ -211,7 +211,7 @@ watch(
:payroll="adjustingPayroll" :adjustment-types="adjustmentTypes" /> :payroll="adjustingPayroll" :adjustment-types="adjustmentTypes" />
<ConfirmDialog v-if="canClosePeriod" v-model:open="closeConfirmOpen" title="Tutup periode gaji?" <ConfirmDialog v-if="canClosePeriod" v-model:open="closeConfirmOpen" title="Tutup periode gaji?"
description="Pastikan semua gaji sudah dibayar sebelum menutup periode." description="Pastikan semua gaji sudah dibayar sebelum menutup periode." confirm-label="Tutup Periode"
confirm-label="Tutup Periode" cancel-label="Batal" :loading="closeProcessing" @confirm="closePeriod" /> cancel-label="Batal" :loading="closeProcessing" @confirm="closePeriod" />
</AdminLayout> </AdminLayout>
</template> </template>

View File

@ -48,7 +48,7 @@ export type OrderListItem = {
subtotal_formatted: string; subtotal_formatted: string;
discount_formatted: string; discount_formatted: string;
marketplace_fee_formatted: string; marketplace_fee_formatted: string;
net_amount_formatted: string; total_amount_formatted: string;
notes: string | null; notes: string | null;
created_at_formatted: string; created_at_formatted: string;
customer?: { customer?: {

View File

@ -18,8 +18,8 @@ export type PayrollListItem = {
bonus_amount_formatted: string; bonus_amount_formatted: string;
deduction_amount: number; deduction_amount: number;
deduction_amount_formatted: string; deduction_amount_formatted: string;
net_amount: number; total_amount: number;
net_amount_formatted: string; total_amount_formatted: string;
status: string; status: string;
status_label: string; status_label: string;
paid_at_formatted: string | null; paid_at_formatted: string | null;
@ -28,8 +28,8 @@ export type PayrollListItem = {
}; };
export type PayrollSummary = { export type PayrollSummary = {
total_net_amount: number; total_total_amount: number;
total_net_amount_formatted: string; total_total_amount_formatted: string;
unpaid_count: number; unpaid_count: number;
paid_count: number; paid_count: number;
}; };