Commit 89695e0e authored by DevPilot's avatar DevPilot

feat(accounting): add Cash Flow Statement and Statement of Changes in Equity

Rounds out the القوائم المالية group alongside the existing income
statement, balance sheet, and consolidated balance sheet. Cash flow uses
the indirect method with a reconciling line so it always foots to the
real cash-account movement; the equity statement ties exactly to the
balance sheet's total_equity at both ends of the period.
parent 5c5a5d2c
...@@ -422,6 +422,65 @@ class ReportController extends Controller ...@@ -422,6 +422,65 @@ class ReportController extends Controller
exit; exit;
} }
public function cashFlowStatement(Request $request): Response
{
$this->authorize('accounting.reports.cash_flow');
$dateFrom = $request->get('date_from', self::getFiscalYearStartForRequest());
$dateTo = $request->get('date_to', date('Y-m-d'));
$branchId = $request->get('branch_id') ? (int) $request->get('branch_id') : null;
$result = FinancialReportService::getCashFlowStatement($dateFrom, $dateTo, $branchId);
$db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Accounting/Views/reports/cash_flow_statement', [
'result' => $result,
'date_from' => $dateFrom,
'date_to' => $dateTo,
'branches' => $branches,
'filters' => ['branch_id' => $branchId],
]);
}
public function equityStatement(Request $request): Response
{
$this->authorize('accounting.reports.equity_statement');
$dateFrom = $request->get('date_from', self::getFiscalYearStartForRequest());
$dateTo = $request->get('date_to', date('Y-m-d'));
$branchId = $request->get('branch_id') ? (int) $request->get('branch_id') : null;
$result = FinancialReportService::getStatementOfChangesInEquity($dateFrom, $dateTo, $branchId);
$db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Accounting/Views/reports/equity_statement', [
'result' => $result,
'date_from' => $dateFrom,
'date_to' => $dateTo,
'branches' => $branches,
'filters' => ['branch_id' => $branchId],
]);
}
private static function getFiscalYearStartForRequest(): string
{
$config = App::getInstance()->config('app');
$startMonth = (int) ($config['financial_year_start_month'] ?? 7);
$startDay = (int) ($config['financial_year_start_day'] ?? 1);
$year = (int) date('Y');
$month = (int) date('m');
if ($month < $startMonth) {
$year--;
}
return sprintf('%04d-%02d-%02d', $year, $startMonth, $startDay);
}
public function consolidatedBalanceSheet(Request $request): Response public function consolidatedBalanceSheet(Request $request): Response
{ {
$this->authorize('accounting.reports.consolidated'); $this->authorize('accounting.reports.consolidated');
......
...@@ -132,6 +132,8 @@ return [ ...@@ -132,6 +132,8 @@ return [
['GET', '/accounting/reports/balance-sheet', 'Accounting\Controllers\ReportController@balanceSheet', ['auth'], 'accounting.reports.balance_sheet'], ['GET', '/accounting/reports/balance-sheet', 'Accounting\Controllers\ReportController@balanceSheet', ['auth'], 'accounting.reports.balance_sheet'],
['GET', '/accounting/reports/balance-sheet/export-csv', 'Accounting\Controllers\ReportController@balanceSheetExportCsv', ['auth'], 'accounting.reports.balance_sheet'], ['GET', '/accounting/reports/balance-sheet/export-csv', 'Accounting\Controllers\ReportController@balanceSheetExportCsv', ['auth'], 'accounting.reports.balance_sheet'],
['GET', '/accounting/reports/consolidated-balance-sheet', 'Accounting\Controllers\ReportController@consolidatedBalanceSheet', ['auth'], 'accounting.reports.consolidated'], ['GET', '/accounting/reports/consolidated-balance-sheet', 'Accounting\Controllers\ReportController@consolidatedBalanceSheet', ['auth'], 'accounting.reports.consolidated'],
['GET', '/accounting/reports/cash-flow', 'Accounting\Controllers\ReportController@cashFlowStatement', ['auth'], 'accounting.reports.cash_flow'],
['GET', '/accounting/reports/equity-statement', 'Accounting\Controllers\ReportController@equityStatement', ['auth'], 'accounting.reports.equity_statement'],
['GET', '/accounting/reports/accounts-receivable', 'Accounting\Controllers\ReportController@accountsReceivable', ['auth'], 'accounting.reports.ar'], ['GET', '/accounting/reports/accounts-receivable', 'Accounting\Controllers\ReportController@accountsReceivable', ['auth'], 'accounting.reports.ar'],
['GET', '/accounting/reports/accounts-payable', 'Accounting\Controllers\ReportController@accountsPayable', ['auth'], 'accounting.reports.ap'], ['GET', '/accounting/reports/accounts-payable', 'Accounting\Controllers\ReportController@accountsPayable', ['auth'], 'accounting.reports.ap'],
['GET', '/accounting/reports/member-statement', 'Accounting\Controllers\ReportController@memberStatement', ['auth'], 'accounting.reports.member_statement'], ['GET', '/accounting/reports/member-statement', 'Accounting\Controllers\ReportController@memberStatement', ['auth'], 'accounting.reports.member_statement'],
......
...@@ -74,6 +74,8 @@ ...@@ -74,6 +74,8 @@
<a href="/accounting/reports/trial-balance" class="btn btn-outline">ميزان المراجعة</a> <a href="/accounting/reports/trial-balance" class="btn btn-outline">ميزان المراجعة</a>
<a href="/accounting/reports/income-statement" class="btn btn-outline">قائمة الدخل</a> <a href="/accounting/reports/income-statement" class="btn btn-outline">قائمة الدخل</a>
<a href="/accounting/reports/balance-sheet" class="btn btn-outline">الميزانية العمومية</a> <a href="/accounting/reports/balance-sheet" class="btn btn-outline">الميزانية العمومية</a>
<a href="/accounting/reports/cash-flow" class="btn btn-outline">قائمة التدفقات النقدية</a>
<a href="/accounting/reports/equity-statement" class="btn btn-outline">التغيرات في حقوق الملكية</a>
<a href="/accounting/reports/treasury" class="btn btn-outline">الخزينة والمدفوعات</a> <a href="/accounting/reports/treasury" class="btn btn-outline">الخزينة والمدفوعات</a>
<a href="/accounting/reports/revenue-analysis" class="btn btn-outline">تحليل الإيرادات</a> <a href="/accounting/reports/revenue-analysis" class="btn btn-outline">تحليل الإيرادات</a>
<?php if (!empty($is_super_admin)): ?> <?php if (!empty($is_super_admin)): ?>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>قائمة التدفقات النقدية<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Filters -->
<div class="card" style="margin-bottom:15px;">
<div style="padding:15px 20px;">
<form method="GET" action="/accounting/reports/cash-flow" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;">
<div>
<label class="form-label" style="font-size:12px;">من تاريخ</label>
<input type="date" name="date_from" class="form-input" value="<?= e($date_from) ?>">
</div>
<div>
<label class="form-label" style="font-size:12px;">إلى تاريخ</label>
<input type="date" name="date_to" class="form-input" value="<?= e($date_to) ?>">
</div>
<div>
<label class="form-label" style="font-size:12px;">الفرع</label>
<select name="branch_id" class="form-select">
<option value="">الكل</option>
<?php foreach ($branches as $b): ?>
<option value="<?= (int)$b['id'] ?>" <?= (int)($filters['branch_id'] ?? 0) === (int)$b['id'] ? 'selected' : '' ?>><?= e($b['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">عرض</button>
</form>
</div>
</div>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;text-align:center;">
<h2 style="margin:0;">قائمة التدفقات النقدية</h2>
<p style="color:#6B7280;margin:5px 0 0;">من <?= e($date_from) ?> إلى <?= e($date_to) ?></p>
</div>
<div style="padding:20px;max-width:700px;margin:0 auto;">
<h3 style="color:#0D7377;border-bottom:2px solid #0D7377;padding-bottom:8px;">الأنشطة التشغيلية</h3>
<table style="width:100%;">
<tr><td style="padding:5px 0;">صافي الدخل للفترة</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['operating']['net_income']) ?></td></tr>
<tr><td style="padding:5px 0;">إضافة: مصروف الإهلاك (غير نقدي)</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['operating']['depreciation_add_back']) ?></td></tr>
<tr><td style="padding:5px 0;">التغير في الأصول المتداولة (غير النقدية)</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['operating']['working_capital_assets']) ?></td></tr>
<tr><td style="padding:5px 0;">التغير في الخصوم التشغيلية</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['operating']['operating_liabilities']) ?></td></tr>
<tr style="font-weight:700;border-top:1px solid #0D7377;">
<td style="padding:10px 0;">صافي النقد من الأنشطة التشغيلية</td>
<td style="padding:10px 0;direction:ltr;text-align:left;color:#0D7377;"><?= money($result['operating']['total']) ?></td>
</tr>
</table>
<h3 style="color:#EA580C;border-bottom:2px solid #EA580C;padding-bottom:8px;margin-top:20px;">الأنشطة الاستثمارية</h3>
<table style="width:100%;">
<tr><td style="padding:5px 0;">شراء أصول ثابتة (صافي)</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['investing']['fixed_assets_purchased_net']) ?></td></tr>
<tr style="font-weight:700;border-top:1px solid #EA580C;">
<td style="padding:10px 0;">صافي النقد من الأنشطة الاستثمارية</td>
<td style="padding:10px 0;direction:ltr;text-align:left;color:#EA580C;"><?= money($result['investing']['total']) ?></td>
</tr>
</table>
<h3 style="color:#7C3AED;border-bottom:2px solid #7C3AED;padding-bottom:8px;margin-top:20px;">الأنشطة التمويلية</h3>
<table style="width:100%;">
<tr><td style="padding:5px 0;">صافي حركة القروض</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['financing']['loans_net']) ?></td></tr>
<tr><td style="padding:5px 0;">صافي حركة رأس المال</td><td style="padding:5px 0;direction:ltr;text-align:left;"><?= money($result['financing']['equity_net']) ?></td></tr>
<tr style="font-weight:700;border-top:1px solid #7C3AED;">
<td style="padding:10px 0;">صافي النقد من الأنشطة التمويلية</td>
<td style="padding:10px 0;direction:ltr;text-align:left;color:#7C3AED;"><?= money($result['financing']['total']) ?></td>
</tr>
</table>
<?php if (bccomp($result['reconciling_item'], '0.00', 2) !== 0): ?>
<table style="width:100%;margin-top:15px;">
<tr>
<td style="padding:5px 0;color:#6B7280;">بنود أخرى غير مصنفة (تسوية)</td>
<td style="padding:5px 0;direction:ltr;text-align:left;color:#6B7280;"><?= money($result['reconciling_item']) ?></td>
</tr>
</table>
<?php endif; ?>
<div style="font-weight:700;border-top:3px double #374151;padding:12px 0;margin-top:15px;display:flex;justify-content:space-between;">
<span>صافي التغير في النقدية</span>
<span style="direction:ltr;font-size:20px;"><?= money($result['net_change_in_cash']) ?></span>
</div>
<table style="width:100%;margin-top:10px;color:#6B7280;font-size:13px;">
<tr><td style="padding:3px 0;">النقدية في بداية الفترة</td><td style="padding:3px 0;direction:ltr;text-align:left;"><?= money($result['cash_start']) ?></td></tr>
<tr><td style="padding:3px 0;">النقدية في نهاية الفترة</td><td style="padding:3px 0;direction:ltr;text-align:left;"><?= money($result['cash_end']) ?></td></tr>
</table>
</div>
</div>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>قائمة التغيرات في حقوق الملكية<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Filters -->
<div class="card" style="margin-bottom:15px;">
<div style="padding:15px 20px;">
<form method="GET" action="/accounting/reports/equity-statement" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;">
<div>
<label class="form-label" style="font-size:12px;">من تاريخ</label>
<input type="date" name="date_from" class="form-input" value="<?= e($date_from) ?>">
</div>
<div>
<label class="form-label" style="font-size:12px;">إلى تاريخ</label>
<input type="date" name="date_to" class="form-input" value="<?= e($date_to) ?>">
</div>
<div>
<label class="form-label" style="font-size:12px;">الفرع</label>
<select name="branch_id" class="form-select">
<option value="">الكل</option>
<?php foreach ($branches as $b): ?>
<option value="<?= (int)$b['id'] ?>" <?= (int)($filters['branch_id'] ?? 0) === (int)$b['id'] ? 'selected' : '' ?>><?= e($b['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">عرض</button>
</form>
</div>
</div>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;text-align:center;">
<h2 style="margin:0;">قائمة التغيرات في حقوق الملكية</h2>
<p style="color:#6B7280;margin:5px 0 0;">من <?= e($date_from) ?> إلى <?= e($date_to) ?></p>
</div>
<div style="padding:20px;">
<div class="table-responsive">
<table class="data-table" style="width:100%;">
<thead>
<tr>
<th>الحساب</th>
<th>الرصيد الافتتاحي</th>
<th>الحركة خلال الفترة</th>
<th>الرصيد الختامي</th>
</tr>
</thead>
<tbody>
<?php foreach ($result['accounts'] as $acc): ?>
<tr>
<td><span style="color:#6B7280;font-size:12px;direction:ltr;"><?= e($acc['account_code']) ?></span> <?= e($acc['name_ar']) ?></td>
<td style="direction:ltr;text-align:left;"><?= money($acc['opening']) ?></td>
<td style="direction:ltr;text-align:left;"><?= money($acc['movement']) ?></td>
<td style="direction:ltr;text-align:left;font-weight:600;"><?= money($acc['closing']) ?></td>
</tr>
<?php endforeach; ?>
<tr style="font-weight:600;border-top:1px solid #7C3AED;">
<td>إجمالي حسابات رأس المال المسجلة</td>
<td style="direction:ltr;text-align:left;"><?= money($result['total_opening']) ?></td>
<td style="direction:ltr;text-align:left;"><?= money($result['capital_movement']) ?></td>
<td style="direction:ltr;text-align:left;"><?= money($result['total_closing']) ?></td>
</tr>
<tr>
<td colspan="2"></td>
<td style="padding-top:10px;">صافي ربح الفترة (غير مقفل)</td>
<td style="direction:ltr;text-align:left;padding-top:10px;"><?= money($result['net_income_period']) ?></td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="card" style="padding:15px 20px;background:#F9FAFB;">
<div style="color:#6B7280;font-size:13px;">إجمالي حقوق الملكية — بداية الفترة</div>
<div style="font-size:22px;font-weight:700;direction:ltr;text-align:left;"><?= money($result['opening_equity_total']) ?></div>
</div>
<div class="card" style="padding:15px 20px;background:#F0FDF4;">
<div style="color:#6B7280;font-size:13px;">إجمالي حقوق الملكية — نهاية الفترة</div>
<div style="font-size:22px;font-weight:700;direction:ltr;text-align:left;color:#059669;"><?= money($result['closing_equity_total']) ?></div>
</div>
</div>
</div>
</div>
<?php $__template->endSection(); ?>
...@@ -20,6 +20,8 @@ PermissionRegistry::register('accounting', [ ...@@ -20,6 +20,8 @@ PermissionRegistry::register('accounting', [
'accounting.reports.income_statement' => ['ar' => 'قائمة الدخل', 'en' => 'Income Statement'], 'accounting.reports.income_statement' => ['ar' => 'قائمة الدخل', 'en' => 'Income Statement'],
'accounting.reports.balance_sheet' => ['ar' => 'الميزانية العمومية', 'en' => 'Balance Sheet'], 'accounting.reports.balance_sheet' => ['ar' => 'الميزانية العمومية', 'en' => 'Balance Sheet'],
'accounting.reports.consolidated' => ['ar' => 'ميزانية موحدة', 'en' => 'Consolidated Balance Sheet'], 'accounting.reports.consolidated' => ['ar' => 'ميزانية موحدة', 'en' => 'Consolidated Balance Sheet'],
'accounting.reports.cash_flow' => ['ar' => 'قائمة التدفقات النقدية', 'en' => 'Cash Flow Statement'],
'accounting.reports.equity_statement' => ['ar' => 'قائمة التغيرات في حقوق الملكية', 'en' => 'Statement of Changes in Equity'],
'accounting.reports.ar' => ['ar' => 'تقرير المدينين', 'en' => 'Accounts Receivable Report'], 'accounting.reports.ar' => ['ar' => 'تقرير المدينين', 'en' => 'Accounts Receivable Report'],
'accounting.reports.ap' => ['ar' => 'تقرير الدائنين', 'en' => 'Accounts Payable Report'], 'accounting.reports.ap' => ['ar' => 'تقرير الدائنين', 'en' => 'Accounts Payable Report'],
'accounting.reports.member_statement' => ['ar' => 'كشف حساب عضو', 'en' => 'Member Statement'], 'accounting.reports.member_statement' => ['ar' => 'كشف حساب عضو', 'en' => 'Member Statement'],
...@@ -185,6 +187,8 @@ MenuRegistry::register('accounting', [ ...@@ -185,6 +187,8 @@ MenuRegistry::register('accounting', [
['label_ar' => 'قائمة الدخل', 'label_en' => 'Income Statement', 'route' => '/accounting/reports/income-statement', 'permission' => 'accounting.reports.income_statement','order' => 11], ['label_ar' => 'قائمة الدخل', 'label_en' => 'Income Statement', 'route' => '/accounting/reports/income-statement', 'permission' => 'accounting.reports.income_statement','order' => 11],
['label_ar' => 'الميزانية العمومية', 'label_en' => 'Balance Sheet', 'route' => '/accounting/reports/balance-sheet', 'permission' => 'accounting.reports.balance_sheet', 'order' => 12], ['label_ar' => 'الميزانية العمومية', 'label_en' => 'Balance Sheet', 'route' => '/accounting/reports/balance-sheet', 'permission' => 'accounting.reports.balance_sheet', 'order' => 12],
['label_ar' => 'ميزانية موحدة', 'label_en' => 'Consolidated BS', 'route' => '/accounting/reports/consolidated-balance-sheet', 'permission' => 'accounting.reports.consolidated', 'order' => 13], ['label_ar' => 'ميزانية موحدة', 'label_en' => 'Consolidated BS', 'route' => '/accounting/reports/consolidated-balance-sheet', 'permission' => 'accounting.reports.consolidated', 'order' => 13],
['label_ar' => 'قائمة التدفقات النقدية', 'label_en' => 'Cash Flow Statement', 'route' => '/accounting/reports/cash-flow', 'permission' => 'accounting.reports.cash_flow', 'order' => 14],
['label_ar' => 'قائمة التغيرات في حقوق الملكية', 'label_en' => 'Statement of Changes in Equity', 'route' => '/accounting/reports/equity-statement', 'permission' => 'accounting.reports.equity_statement', 'order' => 15],
['label_ar' => 'المدينون (AR)', 'label_en' => 'Accounts Receivable', 'route' => '/accounting/reports/accounts-receivable', 'permission' => 'accounting.reports.ar', 'order' => 14], ['label_ar' => 'المدينون (AR)', 'label_en' => 'Accounts Receivable', 'route' => '/accounting/reports/accounts-receivable', 'permission' => 'accounting.reports.ar', 'order' => 14],
['label_ar' => 'الدائنون (AP)', 'label_en' => 'Accounts Payable', 'route' => '/accounting/reports/accounts-payable', 'permission' => 'accounting.reports.ap', 'order' => 15], ['label_ar' => 'الدائنون (AP)', 'label_en' => 'Accounts Payable', 'route' => '/accounting/reports/accounts-payable', 'permission' => 'accounting.reports.ap', 'order' => 15],
['label_ar' => 'كشف حساب عضو', 'label_en' => 'Member Statement', 'route' => '/accounting/reports/member-statement', 'permission' => 'accounting.reports.member_statement','order' => 16], ['label_ar' => 'كشف حساب عضو', 'label_en' => 'Member Statement', 'route' => '/accounting/reports/member-statement', 'permission' => 'accounting.reports.member_statement','order' => 16],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment