akuntansi-sia/app/Http/Controllers/FinancialReportController.php

430 lines
17 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Sale;
use App\Models\Purchase;
use App\Models\Branch;
use App\Models\JournalEntry;
use App\Models\JournalDetail;
use App\Models\Stock;
class FinancialReportController extends Controller
{
public function profitAndLoss(Request $request)
{
// Define branches dynamically
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) {
$branches = ['Cabang 1', 'Cabang 2', 'Cabang 3']; // Fallback
}
$month = $request->get('month');
$year = $request->get('year');
// 1. Data Gabungan
$jQuery = JournalDetail::with('journalEntry', 'account')
->whereHas('journalEntry', function($q) use ($month, $year) {
if ($month) $q->whereMonth('date', $month);
if ($year) $q->whereYear('date', $year);
});
$penjualan = (clone $jQuery)->whereHas('account', function($q) { $q->where('code', '401'); })->sum('credit');
$hpp = (clone $jQuery)->whereHas('account', function($q) { $q->where('code', '501'); })->sum('debit');
$labaKotor = $penjualan - $hpp;
$opCostsDetails = (clone $jQuery)->whereHas('account', function($q) { $q->where('code', '502'); })->get();
// Format to match existing view
$opCostsTotal = $opCostsDetails->map(function($d) {
$desc = str_replace('Biaya Operasional: ', '', $d->journalEntry->description);
return (object)[
'label' => $desc,
'branch' => $d->journalEntry->branch,
'amount' => $d->debit
];
});
$totalOpAmount = $opCostsTotal->sum('amount');
$labaBersih = $labaKotor - $totalOpAmount;
$dataTotal = [
'penjualan' => $penjualan,
'pembelian' => $hpp,
'laba_kotor' => $labaKotor,
'operational' => $opCostsTotal,
'total_operational' => $totalOpAmount,
'laba_bersih' => $labaBersih
];
// 2. Per Branch Data
$branchData = [];
foreach ($branches as $branch) {
$bJQuery = JournalDetail::with('journalEntry', 'account')
->whereHas('journalEntry', function($q) use ($month, $year, $branch) {
$q->where('branch', $branch);
if ($month) $q->whereMonth('date', $month);
if ($year) $q->whereYear('date', $year);
});
$bPenjualan = (clone $bJQuery)->whereHas('account', function($q) { $q->where('code', '401'); })->sum('credit');
$bHpp = (clone $bJQuery)->whereHas('account', function($q) { $q->where('code', '501'); })->sum('debit');
$bLabaKotor = $bPenjualan - $bHpp;
$bOpCostsDetails = (clone $bJQuery)->whereHas('account', function($q) { $q->where('code', '502'); })->get();
$bOpCostsTotal = $bOpCostsDetails->map(function($d) {
$desc = str_replace('Biaya Operasional: ', '', $d->journalEntry->description);
return (object)[
'label' => $desc,
'branch' => $d->journalEntry->branch,
'amount' => $d->debit
];
});
$bTotalOpAmount = $bOpCostsTotal->sum('amount');
$bLabaBersih = $bLabaKotor - $bTotalOpAmount;
$branchData[$branch] = [
'penjualan' => $bPenjualan,
'pembelian' => $bHpp,
'laba_kotor' => $bLabaKotor,
'operational' => $bOpCostsTotal,
'total_operational' => $bTotalOpAmount,
'laba_bersih' => $bLabaBersih
];
}
return view('neraca_laba_rugi', compact('dataTotal', 'branchData', 'branches'));
}
public function exportProfitAndLossPdf(Request $request)
{
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) {
$branches = ['Cabang 1', 'Cabang 2', 'Cabang 3']; // Fallback
}
$month = $request->get('month');
$year = $request->get('year');
$branchFilter = $request->get('branch');
// Target branch calculation
$targetBranch = $branchFilter ?: 'Total Gabungan';
$jQuery = JournalDetail::with('journalEntry', 'account')
->whereHas('journalEntry', function($q) use ($month, $year, $branchFilter) {
if ($branchFilter && $branchFilter !== 'Total Gabungan') {
$q->where('branch', $branchFilter);
}
if ($month) $q->whereMonth('date', $month);
if ($year) $q->whereYear('date', $year);
});
$penjualan = (clone $jQuery)->whereHas('account', function($q) { $q->where('code', '401'); })->sum('credit');
$hpp = (clone $jQuery)->whereHas('account', function($q) { $q->where('code', '501'); })->sum('debit');
$labaKotor = $penjualan - $hpp;
$opCostsDetails = (clone $jQuery)->whereHas('account', function($q) { $q->where('code', '502'); })->get();
// Format to match existing view expectation
$opCostsTotalData = $opCostsDetails->map(function($d) {
$desc = str_replace('Biaya Operasional: ', '', $d->journalEntry->description);
return (object)[
'label' => $desc,
'branch' => $d->journalEntry->branch,
'amount' => $d->debit
];
});
$totalOpAmount = $opCostsTotalData->sum('amount');
$labaBersih = $labaKotor - $totalOpAmount;
$data = [
'penjualan' => $penjualan,
'pembelian' => $hpp,
'laba_kotor' => $labaKotor,
'operational' => $opCostsTotalData,
'total_operational' => $totalOpAmount,
'laba_bersih' => $labaBersih,
'branch' => $targetBranch,
'month' => $month,
'year' => $year
];
$pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('reports.neraca_laba_rugi_pdf', $data);
return $pdf->download('laporan_neraca_laba_rugi_' . date('Y-m-d') . '.pdf');
}
public function generalLedger()
{
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) {
$branches = ['Cabang 1', 'Cabang 2', 'Cabang 3']; // Fallback
}
// 1. Total Gabungan
// Filter only 'Kas' account (101) to match existing simplified Ledger layout
$kasDetails = JournalDetail::whereHas('account', function($q) {
$q->where('code', '101');
})->with('journalEntry')->get();
$transactionsTotal = $kasDetails->map(function($d) {
return [
'date' => $d->journalEntry->date,
'account' => 'Kas',
'desc' => $d->journalEntry->description,
'debit' => $d->debit,
'credit' => $d->credit
];
});
// Initial Balance dari nilai stok saat ini
$initialBalance = $this->calculateModalFromStock();
$ledgerTotal = $this->processLedger($transactionsTotal, $initialBalance, 'Modal dari Stok');
// 2. Per Branch
$branchLedgers = [];
foreach ($branches as $branch) {
$initialBranchBalance = $this->calculateModalFromStock($branch);
$bKasDetails = JournalDetail::whereHas('account', function($q) {
$q->where('code', '101');
})->whereHas('journalEntry', function($q) use ($branch) {
$q->where('branch', $branch);
})->with('journalEntry')->get();
$bTransactions = $bKasDetails->map(function($d) {
return [
'date' => $d->journalEntry->date,
'account' => 'Kas',
'desc' => $d->journalEntry->description,
'debit' => $d->debit,
'credit' => $d->credit
];
});
$branchLedgers[$branch] = $this->processLedger($bTransactions, $initialBranchBalance, 'Modal dari Stok ' . $branch);
}
return view('buku_besar', compact('ledgerTotal', 'branchLedgers', 'branches'));
}
private function processLedger($transactions, $initialBalance, $initialDesc)
{
$ledger = collect();
// Add Initial Transaction
$ledger->push([
'date' => '2025-09-01', // Example date matching mockup
'account' => 'Kas',
'desc' => $initialDesc,
'debit' => $initialBalance,
'credit' => 0,
'balance' => $initialBalance
]);
$sortedTrans = $transactions->sortBy('date');
$currentBalance = $initialBalance;
foreach ($sortedTrans as $t) {
$currentBalance += $t['debit'];
$currentBalance -= $t['credit'];
$ledger->push([
'date' => $t['date'],
'account' => $t['account'],
'desc' => $t['desc'],
'debit' => $t['debit'],
'credit' => $t['credit'],
'balance' => $currentBalance
]);
}
return $ledger;
}
public function generalJournal()
{
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) {
$branches = ['Cabang 1', 'Cabang 2', 'Cabang 3']; // Fallback
}
// 1. Total Gabungan
$journalTotal = collect();
$details = JournalDetail::with('journalEntry', 'account')
->join('journal_entries', 'journal_details.journal_entry_id', '=', 'journal_entries.id')
->orderBy('journal_entries.date')
->orderBy('journal_entries.id')
->orderBy('journal_details.credit') // debit first
->select('journal_details.*')
->get();
foreach($details as $d) {
$journalTotal->push([
'date' => $d->journalEntry->date,
'desc' => $d->credit > 0 ? ' ' . $d->account->name : $d->account->name,
'reff' => $d->account->code,
'debit' => $d->debit,
'credit' => $d->credit
]);
}
// 2. Per Branch Data
$branchJournals = [];
foreach ($branches as $branch) {
$journal = collect();
$bDetails = JournalDetail::with('journalEntry', 'account')
->join('journal_entries', 'journal_details.journal_entry_id', '=', 'journal_entries.id')
->where('journal_entries.branch', $branch)
->orderBy('journal_entries.date')
->orderBy('journal_entries.id')
->orderBy('journal_details.credit') // debit first
->select('journal_details.*')
->get();
foreach($bDetails as $d) {
$journal->push([
'date' => $d->journalEntry->date,
'desc' => $d->credit > 0 ? ' ' . $d->account->name : $d->account->name,
'reff' => $d->account->code,
'debit' => $d->debit,
'credit' => $d->credit
]);
}
$branchJournals[$branch] = $journal;
}
return view('jurnal_umum', compact('journalTotal', 'branchJournals', 'branches'));
}
public function equityStatement(Request $request)
{
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) $branches = ['Cabang 1', 'Cabang 2', 'Cabang 3'];
// Laba Bersih = Penjualan (401) - HPP (501) - Biaya (502)
$penjualan = JournalDetail::whereHas('account', function($q) { $q->where('code', '401'); })->sum('credit');
$hpp = JournalDetail::whereHas('account', function($q) { $q->where('code', '501'); })->sum('debit');
$biaya = JournalDetail::whereHas('account', function($q) { $q->where('code', '502'); })->sum('debit');
$labaBersih = $penjualan - $hpp - $biaya;
// Modal berasal dari nilai stok saat ini berdasarkan biaya perolehan (harga beli),
// karena sistem belum punya akun modal nyata.
$modalAwal = $this->calculateModalFromStock();
$modalAkhir = $modalAwal + $labaBersih;
$data = [
'modal_awal' => $modalAwal,
'laba_bersih' => $labaBersih,
'prive' => 0,
'modal_akhir' => $modalAkhir
];
return view('perubahan_modal', compact('data', 'branches'));
}
public function balanceSheet(Request $request)
{
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) $branches = ['Cabang 1', 'Cabang 2', 'Cabang 3'];
// Assets
$kas = JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })->sum('debit') - JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })->sum('credit');
// Persediaan dari journal (Debit = Awal Persediaan, Credit = Penjualan HPP)
$persediaan = JournalDetail::whereHas('account', function($q) { $q->where('code', '102'); })->sum('debit') - JournalDetail::whereHas('account', function($q) { $q->where('code', '102'); })->sum('credit');
$totalAset = $kas + $persediaan;
// Liabilities
$hutang = 0; // We don't have liability accounts seeded yet
// Equity
$penjualan = JournalDetail::whereHas('account', function($q) { $q->where('code', '401'); })->sum('credit');
$hpp = JournalDetail::whereHas('account', function($q) { $q->where('code', '501'); })->sum('debit');
$biaya = JournalDetail::whereHas('account', function($q) { $q->where('code', '502'); })->sum('debit');
$labaBersih = $penjualan - $hpp - $biaya;
// Modal berasal dari nilai stok saat ini berdasarkan biaya perolehan (harga beli),
// karena sistem belum punya akun modal nyata.
$modalAwal = $this->calculateModalFromStock();
$modalAkhir = $modalAwal + $labaBersih;
$totalPasiva = $hutang + $modalAkhir;
$data = [
'kas' => $kas,
'persediaan' => $persediaan,
'total_aset' => $totalAset,
'hutang' => $hutang,
'modal_akhir' => $modalAkhir,
'total_pasiva' => $totalPasiva
];
return view('neraca_keuangan', compact('data', 'branches'));
}
private function calculateModalFromStock($branch = null)
{
$query = Stock::query()->with('product');
if ($branch) {
$query->where('branch', $branch);
}
return (float) $query->get()->sum(function ($stock) {
$product = $stock->product;
if (!$product) {
return 0;
}
$quantity = (float) $stock->quantity;
$purchasePrice = (float) $product->purchase_price;
return $quantity > 0 ? $quantity * $purchasePrice : 0;
});
}
public function cashFlow(Request $request)
{
$branches = Branch::pluck('name')->toArray();
if (empty($branches)) $branches = ['Cabang 1', 'Cabang 2', 'Cabang 3'];
// Penerimaan dari Penjualan (Debit Kas dari Jurnal Penjualan)
$penerimaanPenjualan = JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })
->whereHas('journalEntry', function($q) { $q->where('reference_type', 'Sale'); })->sum('debit');
// Pengeluaran Pembelian (Kredit Kas dari Pembelian)
$pengeluaranPembelian = JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })
->whereHas('journalEntry', function($q) { $q->where('reference_type', 'Purchase'); })->sum('credit');
// Pengeluaran Biaya Operasional (Kredit Kas dari Biaya Operasional)
$pengeluaranOperasional = JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })
->whereHas('journalEntry', function($q) { $q->where('reference_type', 'OperationalCost'); })->sum('credit');
// Modal berasal dari stok, sehingga tidak ada setoran modal terpisah dalam arus kas
$setoranModal = 0;
$arusKasOperasi = $penerimaanPenjualan - $pengeluaranPembelian - $pengeluaranOperasional;
$arusKasPendanaan = $setoranModal;
$kenaikanKas = $arusKasOperasi + $arusKasPendanaan;
// Saldo Kas Akhir
$saldoKas = JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })->sum('debit') - JournalDetail::whereHas('account', function($q) { $q->where('code', '101'); })->sum('credit');
$data = [
'penerimaan_penjualan' => $penerimaanPenjualan,
'pengeluaran_pembelian' => $pengeluaranPembelian,
'pengeluaran_operasional' => $pengeluaranOperasional,
'arus_kas_operasi' => $arusKasOperasi,
'setoran_modal' => $setoranModal,
'arus_kas_pendanaan' => $arusKasPendanaan,
'kenaikan_kas' => $kenaikanKas,
'saldo_kas' => $saldoKas
];
return view('arus_kas', compact('data', 'branches'));
}
}