430 lines
17 KiB
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'));
|
|
}
|
|
}
|