feat: update AnalysisService to include employee advance status filtering and adjust calculations for total amounts; enhance rupiah parsing to handle negative values; refine Analysis.vue for improved display of profit metrics and expenses

This commit is contained in:
Yoga Pangestu 2026-07-01 20:11:50 +07:00
parent fbd5bd7b6d
commit 070e1e52d1
3 changed files with 26 additions and 24 deletions

View File

@ -2,6 +2,7 @@
namespace App\Services\System;
use App\Enums\EmployeeAdvanceStatus;
use App\Enums\OrderStatus;
use App\Enums\PriceType;
use App\Enums\Role;
@ -19,6 +20,7 @@
use App\Models\RawMaterialPrice;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class AnalysisService
{
@ -352,9 +354,10 @@ public function getExpenseSummary(?Carbon $startDate = null, ?Carbon $endDate =
->first();
$employeeAdvance = EmployeeAdvance::query()
->whereIn('status', [EmployeeAdvanceStatus::APPROVED, EmployeeAdvanceStatus::PARTIALLY_PAID])
->when($startDate && $endDate, fn ($q) => $q->whereBetween('employee_advances.created_at', [$startDate, $endDate]))
->when(! $isSuper, fn ($q) => $q->where('employee_id', $user->employee?->id))
->selectRaw('COALESCE(SUM(amount), 0) as total')
->selectRaw('COALESCE(SUM(amount - paid_amount), 0) as total')
->first();
$purchaseTotal = (int) ($purchase->total ?? 0);
@ -395,9 +398,10 @@ public function getMonthlyExpense(?Carbon $startDate = null, ?Carbon $endDate =
->get();
$advances = EmployeeAdvance::query()
->whereIn('status', [EmployeeAdvanceStatus::APPROVED, EmployeeAdvanceStatus::PARTIALLY_PAID])
->when($startDate && $endDate, fn ($q) => $q->whereBetween('employee_advances.created_at', [$startDate, $endDate]))
->when(! $isSuper, fn ($q) => $q->where('employee_id', $user->employee?->id))
->selectRaw("DATE_FORMAT(employee_advances.created_at, '%Y-%m') as month_key, COALESCE(SUM(amount), 0) as total")
->selectRaw("DATE_FORMAT(employee_advances.created_at, '%Y-%m') as month_key, COALESCE(SUM(amount - paid_amount), 0) as total")
->groupBy('month_key')
->orderBy('month_key')
->get();
@ -524,9 +528,10 @@ public function getProfitMetrics(?Carbon $startDate = null, ?Carbon $endDate = n
->sum('amount');
$advanceTotal = EmployeeAdvance::query()
->whereIn('status', [EmployeeAdvanceStatus::APPROVED, EmployeeAdvanceStatus::PARTIALLY_PAID])
->when($startDate && $endDate, fn ($q) => $q->whereBetween('employee_advances.created_at', [$startDate, $endDate]))
->when(! $isSuper, fn ($q) => $q->where('employee_id', $user->employee?->id))
->sum('amount');
->sum(DB::raw('amount - paid_amount'));
$totalExpenses = (int) $purchaseTotal + (int) $expenseTotal + (int) $advanceTotal;

View File

@ -1,5 +1,8 @@
export function parseRupiah(value: string | number): string {
return String(value).replace(/\D/g, '');
const str = String(value);
const isNegative = str.startsWith('-') || (typeof value === 'number' && value < 0);
return isNegative ? '-' + str.replace(/[^0-9]/g, '') : str.replace(/[^0-9]/g, '');
}
export function formatRupiah(value: string | number): string {

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { Head, router } from '@inertiajs/vue3';
import { Banknote, Package, Percent, ShoppingCart, TrendingUp, UserCheck } from '@lucide/vue';
import { Banknote, Package, ShoppingCart, TrendingUp, UserCheck } from '@lucide/vue';
import { GroupedBar } from '@unovis/ts';
import { VisAxis, VisGroupedBar, VisTooltip, VisXYContainer } from '@unovis/vue';
import { computed, ref, watch } from 'vue';
@ -716,7 +716,7 @@ watch([startDate, endDate], () => {
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
<StatCard v-if="can('analysis.profit_orders')" title="Total Order" :icon="ShoppingCart"
main-label="Pesanan Selesai" :main-value="profitMetrics.total_orders.toLocaleString('id-ID')"
:sub-label="'AOV: Rp' + formatRupiah(profitMetrics.aov)" :items="[
:items="[
{
label: 'Produk Terjual',
value: profitMetrics.total_products_sold.toLocaleString('id-ID'),
@ -725,33 +725,27 @@ watch([startDate, endDate], () => {
label: 'Item/Transaksi',
value: profitMetrics.items_per_transaction,
},
{
label: 'AOV',
value: 'Rp' + formatRupiah(profitMetrics.aov),
},
]" />
<StatCard v-if="can('analysis.profit_gross') || can('analysis.profit_hpp')" title="Laba Kotor"
:icon="TrendingUp" main-label="Gross Profit"
:main-value="'Rp' + formatRupiah(profitMetrics.laba_kotor)"
:sub-label="profitMetrics.laba_kotor >= 0 ? 'Positif' : 'Negatif'" :items="[
:icon="TrendingUp" main-label="Laba Kotor"
:main-value="'Rp' + formatRupiah(profitMetrics.laba_kotor)" :items="[
{
label: 'Revenue',
label: 'Pendapatan',
value: 'Rp' + formatRupiah(revenueSummary.total_revenue),
},
{
label: 'Laba Bersih',
value: 'Rp' + formatRupiah(profitMetrics.laba_bersih),
},
]" />
<StatCard v-if="can('analysis.profit_margin')" title="Profit Margin" :icon="Percent" main-label="Margin"
:main-value="profitMetrics.profit_margin + '%'"
:sub-label="'Laba Bersih: Rp' + formatRupiah(profitMetrics.laba_bersih)" :items="[
{
label: 'Laba Kotor',
value: 'Rp' + formatRupiah(profitMetrics.laba_kotor),
},
{
label: 'Total Pengeluaran',
label: 'Pengeluaran',
value: 'Rp' + formatRupiah(expenseSummary.total),
},
{
label: 'Laba Bersih',
value: 'Rp' + formatRupiah(profitMetrics.laba_bersih) + ' (' + profitMetrics.profit_margin + '%)',
},
]" />
</div>