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',
'discount_formatted',
'marketplace_fee_formatted',
'net_amount_formatted',
'total_amount_formatted',
'created_at_formatted',
'channel_label',
'price_type_label',
@ -41,7 +41,7 @@ protected function casts(): array
'subtotal' => 'integer',
'discount' => '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(
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',
'bonus_amount_formatted',
'deduction_amount_formatted',
'net_amount_formatted',
'total_amount_formatted',
'status_label',
'employee_name',
'paid_at_formatted',
@ -37,7 +37,7 @@ protected function casts(): array
'base_salary' => 'integer',
'bonus_amount' => 'integer',
'deduction_amount' => 'integer',
'net_amount' => 'integer',
'total_amount' => 'integer',
'status' => PayrollStatus::class,
'paid_at' => 'datetime',
];
@ -115,10 +115,10 @@ public function employeeName(): Attribute
);
}
public function netAmountFormatted(): Attribute
public function totalAmountFormatted(): Attribute
{
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->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
{
$totalNetAmount = (int) Payroll::query()
$totalTotalAmount = (int) Payroll::query()
->where('payroll_period_id', $period->id)
->where('status', PayrollStatus::UNPAID)
->sum('net_amount');
->sum('total_amount');
$unpaidCount = Payroll::query()
->where('payroll_period_id', $period->id)
@ -74,8 +74,8 @@ public function periodSummary(PayrollPeriod $period): array
->count();
return [
'total_net_amount' => $totalNetAmount,
'total_net_amount_formatted' => 'Rp '.number_format($totalNetAmount, 0, ',', '.'),
'total_total_amount' => $totalTotalAmount,
'total_total_amount_formatted' => 'Rp '.number_format($totalTotalAmount, 0, ',', '.'),
'unpaid_count' => $unpaidCount,
'paid_count' => $paidCount,
];
@ -154,7 +154,7 @@ public function closePeriod(PayrollPeriod $period, User $user): void
$unpaidCount = Payroll::query()
->where('payroll_period_id', $period->id)
->where('status', PayrollStatus::UNPAID)
->where('net_amount', '>', 0)
->where('total_amount', '>', 0)
->count();
if ($unpaidCount > 0) {
@ -189,7 +189,7 @@ public function generatePayrollsForPeriod(PayrollPeriod $period): void
'base_salary' => $employee->base_salary,
'bonus_amount' => 0,
'deduction_amount' => 0,
'net_amount' => 0,
'total_amount' => 0,
'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 {
$payroll->status = PayrollStatus::PAID;
$payroll->paid_at = now();
@ -264,7 +264,7 @@ public function pay(Payroll $payroll, User $user): void
$cashTransaction = $this->cashService->recordOutgoing(
$payroll,
$payroll->net_amount,
$payroll->total_amount,
$description,
$user,
);
@ -332,7 +332,7 @@ private function settleKasbonFromPayroll(Payroll $payroll, User $user): 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);
return;

View File

@ -284,7 +284,7 @@ public function create(array $validated, User $user): Order
$subtotal = $draftItems->sum('subtotal');
$discount = (int) ($validated['discount'] ?? 0);
$marketplaceFee = (int) ($validated['marketplace_fee'] ?? 0);
$netAmount = max($subtotal - $discount - $marketplaceFee, 0);
$totalAmount = max($subtotal - $discount - $marketplaceFee, 0);
$order = Order::create([
'customer_id' => $validated['customer_id'] ?? null,
@ -295,7 +295,7 @@ public function create(array $validated, User $user): Order
'subtotal' => $subtotal,
'discount' => $discount,
'marketplace_fee' => $marketplaceFee,
'net_amount' => $netAmount,
'total_amount' => $totalAmount,
'notes' => $validated['notes'] ?? null,
]);
@ -334,7 +334,7 @@ public function update(Order $order, array $validated): void
$subtotal = array_sum(array_column($lineItems, 'subtotal'));
$discount = (int) ($validated['discount'] ?? 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->channel = $validated['channel'];
@ -342,7 +342,7 @@ public function update(Order $order, array $validated): void
$order->subtotal = $subtotal;
$order->discount = $discount;
$order->marketplace_fee = $marketplaceFee;
$order->net_amount = $netAmount;
$order->total_amount = $totalAmount;
$order->notes = $validated['notes'] ?? null;
$order->save();
@ -528,7 +528,7 @@ private function incrementStock(OrderItem $item): 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);
return;

View File

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

View File

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

View File

@ -19,7 +19,7 @@ public function up(): void
$table->unsignedInteger('base_salary');
$table->unsignedBigInteger('bonus_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->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->unsignedBigInteger('subtotal');
$table->unsignedBigInteger('discount')->default(0);
$table->unsignedBigInteger('net_amount');
$table->unsignedBigInteger('total_amount');
$table->text('notes')->nullable();
$table->foreignId('cash_transaction_id')->nullable()->unique()->constrained()->restrictOnDelete();

View File

@ -22,7 +22,7 @@ public function run(): void
'payroll_period_id' => $period->id,
'employee_id' => $employee->id,
'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' }),
},
{
accessorKey: 'net_amount_formatted',
accessorKey: 'total_amount_formatted',
enableSorting: true,
header: () => h(DataTableColumnHeader, { title: 'Gaji Bersih', column: 'net_amount' }),
header: () => h(DataTableColumnHeader, { title: 'Gaji Bersih', column: 'total_amount' }),
},
{
accessorKey: 'status_label',

View File

@ -64,6 +64,6 @@ function payPayroll() {
</div>
<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" />
</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>
</div>
<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>Subtotal <strong class="text-foreground">{{ order.subtotal_formatted }}</strong></span>
<span>Diskon <strong class="text-foreground">{{ order.discount_formatted }}</strong></span>
<span>Tipe Harga <strong class="text-foreground">{{ order.price_type_label
}}</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'">
Biaya MP <strong class="text-foreground">{{ order.marketplace_fee_formatted }}</strong>
Biaya MP <strong class="text-foreground">{{ order.marketplace_fee_formatted
}}</strong>
</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>
<p v-if="order.notes" class="text-muted-foreground text-sm">
{{ order.notes }}

View File

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

View File

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

View File

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

View File

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

View File

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