Commit 6d44324b authored by Mahmoud Aglan's avatar Mahmoud Aglan

Kokowawa

parent 6291dcf2
...@@ -64,30 +64,49 @@ final class AccountingIntegrationService ...@@ -64,30 +64,49 @@ final class AccountingIntegrationService
}; };
// Determine credit account (revenue type) // Determine credit account (revenue type)
// Uses granular sub-accounts (4110+) with fallback to legacy accounts (4101+)
$creditAccountCode = match ($type) { $creditAccountCode = match ($type) {
'form_fee', 'membership_fee', 'addition_fee', 'separation_fee', 'form_fee' => '4110', // Form Fee Revenue
'divorce_fee', 'death_fee', 'waiver_fee', 'sports_conversion', 'membership_fee' => '4111', // Membership Value Revenue
'carnet_replacement', 'seasonal_fee' 'addition_fee' => '4112', // Dependent Addition Revenue
=> '4101', // Membership Revenue 'separation_fee' => '4120', // Separation Fee Revenue
'divorce_fee' => '4121', // Divorce Fee Revenue
'annual_subscription', 'development_fee' 'death_fee' => '4122', // Death Transfer Fee Revenue
=> '4102', // Subscription Revenue 'waiver_fee' => '4123', // Waiver Fee Revenue
'sports_conversion' => '4124', // Sports Conversion Revenue
'down_payment', 'installment' 'seasonal_fee' => '4130', // Seasonal Membership Revenue
=> '1103', // Accounts Receivable (reducing AR) 'carnet_replacement' => '4131', // Carnet Replacement Revenue
'annual_subscription' => '4140', // Annual Subscription Revenue
'fine' 'development_fee' => '4141', // Development Fee Revenue
=> '4104', // Fine Revenue 'activity_subscription' => '4150', // Activity Subscription Revenue
'down_payment' => '1103', // Accounts Receivable (reducing AR)
'inventory_sale' 'installment' => '1103', // Accounts Receivable (reducing AR)
=> '4103', // Sales Revenue 'fine' => '4104', // Fine Revenue
'inventory_sale' => '4103', // Sales Revenue
default default => '4105', // Service Revenue (catch-all)
=> '4105', // Service Revenue
}; };
$debitAccount = self::getAccountByCode($debitAccountCode); // Fallback: if the granular account doesn't exist yet, use legacy grouping
$creditAccount = self::getAccountByCode($creditAccountCode); $creditAccount = self::getAccountByCode($creditAccountCode);
if (!$creditAccount) {
$fallbackCode = match ($type) {
'form_fee', 'membership_fee', 'addition_fee', 'separation_fee',
'divorce_fee', 'death_fee', 'waiver_fee', 'sports_conversion',
'carnet_replacement', 'seasonal_fee'
=> '4101', // Membership Revenue (legacy)
'annual_subscription', 'development_fee'
=> '4102', // Subscription Revenue (legacy)
'activity_subscription'
=> '4105', // Service Revenue (legacy)
default => null,
};
if ($fallbackCode) {
$creditAccountCode = $fallbackCode;
$creditAccount = self::getAccountByCode($creditAccountCode);
}
}
$debitAccount = self::getAccountByCode($debitAccountCode);
if (!$debitAccount || !$creditAccount) { if (!$debitAccount || !$creditAccount) {
Logger::error("Accounting auto-post failed: account not found", [ Logger::error("Accounting auto-post failed: account not found", [
...@@ -1198,23 +1217,24 @@ final class AccountingIntegrationService ...@@ -1198,23 +1217,24 @@ final class AccountingIntegrationService
private static function getPaymentTypeLabel(string $type): string private static function getPaymentTypeLabel(string $type): string
{ {
return match ($type) { return match ($type) {
'form_fee' => 'رسوم استمارة', 'form_fee' => 'رسوم استمارة',
'membership_fee' => 'قيمة العضوية', 'membership_fee' => 'قيمة العضوية',
'addition_fee' => 'رسوم إضافة', 'addition_fee' => 'رسوم إضافة',
'annual_subscription' => 'اشتراك سنوي', 'annual_subscription' => 'اشتراك سنوي',
'development_fee' => 'رسوم تنمية', 'development_fee' => 'رسوم تنمية',
'down_payment' => 'مقدم تقسيط', 'down_payment' => 'مقدم تقسيط',
'installment' => 'قسط شهري', 'installment' => 'قسط شهري',
'fine' => 'غرامة', 'fine' => 'غرامة',
'separation_fee' => 'رسوم فصل', 'separation_fee' => 'رسوم فصل',
'divorce_fee' => 'رسوم طلاق', 'divorce_fee' => 'رسوم طلاق',
'death_fee' => 'رسوم نقل وفاة', 'death_fee' => 'رسوم نقل وفاة',
'waiver_fee' => 'رسوم تنازل', 'waiver_fee' => 'رسوم تنازل',
'carnet_replacement' => 'بدل فاقد كارنيه', 'carnet_replacement' => 'بدل فاقد كارنيه',
'seasonal_fee' => 'رسوم عضوية موسمية', 'seasonal_fee' => 'رسوم عضوية موسمية',
'sports_conversion' => 'رسوم تحويل رياضي', 'sports_conversion' => 'رسوم تحويل رياضي',
'inventory_sale' => 'مبيعات مخزون', 'inventory_sale' => 'مبيعات مخزون',
default => $type, 'activity_subscription' => 'اشتراك نشاط',
default => $type,
}; };
} }
} }
<?php
declare(strict_types=1);
use App\Core\Database;
/**
* Expand Chart of Accounts to cover ALL modules properly.
*
* Adds granular revenue sub-accounts under 4100 and new expense/asset/liability
* accounts to match every module in the system.
*
* Existing accounts (4101-4109, etc.) are NOT touched — only new ones are inserted.
*/
return function (Database $db): void {
$now = date('Y-m-d H:i:s');
// Helper: resolve account code to ID
$resolveId = function (string $code) use ($db): ?int {
$row = $db->selectOne("SELECT id FROM chart_of_accounts WHERE account_code = ?", [$code]);
return $row ? (int) $row['id'] : null;
};
$accounts = [
// ══════════════════════════════════════════════════════════
// REVENUE — Granular sub-accounts under 4100
// ══════════════════════════════════════════════════════════
// 4110-series: Membership Fee Revenue (detail breakdown of 4101)
['account_code' => '4110', 'name_ar' => 'إيرادات رسوم استمارات', 'name_en' => 'Form Fee Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4111', 'name_ar' => 'إيرادات قيمة عضوية', 'name_en' => 'Membership Value Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4112', 'name_ar' => 'إيرادات رسوم إضافة (ملحقين)', 'name_en' => 'Dependent Addition Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// 4120-series: Transfer/Case Revenue
['account_code' => '4120', 'name_ar' => 'إيرادات رسوم فصل', 'name_en' => 'Separation Fee Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4121', 'name_ar' => 'إيرادات رسوم طلاق', 'name_en' => 'Divorce Fee Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4122', 'name_ar' => 'إيرادات رسوم نقل وفاة', 'name_en' => 'Death Transfer Fee Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4123', 'name_ar' => 'إيرادات رسوم تنازل', 'name_en' => 'Waiver Fee Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4124', 'name_ar' => 'إيرادات تحويل رياضي', 'name_en' => 'Sports Conversion Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// 4130-series: Special Membership Revenue
['account_code' => '4130', 'name_ar' => 'إيرادات عضوية موسمية', 'name_en' => 'Seasonal Membership Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4131', 'name_ar' => 'إيرادات بدل فاقد كارنيه', 'name_en' => 'Carnet Replacement Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// 4140-series: Subscription detail
['account_code' => '4140', 'name_ar' => 'إيرادات اشتراك سنوي', 'name_en' => 'Annual Subscription Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
['account_code' => '4141', 'name_ar' => 'إيرادات رسوم تنمية', 'name_en' => 'Development Fee Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// Activity subscriptions (under Service Revenue)
['account_code' => '4150', 'name_ar' => 'إيرادات اشتراكات أنشطة', 'name_en' => 'Activity Subscription Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// Installment-specific revenue
['account_code' => '4160', 'name_ar' => 'إيرادات مقدم تقسيط', 'name_en' => 'Down Payment Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
['account_code' => '4161', 'name_ar' => 'إيرادات أقساط شهرية', 'name_en' => 'Installment Revenue', 'account_type' => 'revenue', 'account_nature' => 'credit', 'parent_code' => '4100', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
// ══════════════════════════════════════════════════════════
// ASSETS — Additional
// ══════════════════════════════════════════════════════════
// Tax input (procurement VAT)
['account_code' => '1109', 'name_ar' => 'ضريبة مدخلات (مشتريات)', 'name_en' => 'Input Tax (Purchases)', 'account_type' => 'asset', 'account_nature' => 'debit', 'parent_code' => '1100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// HR Loans
['account_code' => '1110', 'name_ar' => 'قروض موظفين', 'name_en' => 'Employee Loans', 'account_type' => 'asset', 'account_nature' => 'debit', 'parent_code' => '1100', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
// ══════════════════════════════════════════════════════════
// LIABILITIES — Additional
// ══════════════════════════════════════════════════════════
// Rental deposits specifically
['account_code' => '2108', 'name_ar' => 'تأمينات إيجارات مستحقة', 'name_en' => 'Rental Deposits Liability', 'account_type' => 'liability', 'account_nature' => 'credit', 'parent_code' => '2100', 'level' => 3, 'is_header' => 0, 'is_system' => 1],
// End of service accrual
['account_code' => '2109', 'name_ar' => 'مخصص مكافأة نهاية خدمة', 'name_en' => 'End of Service Provision', 'account_type' => 'liability', 'account_nature' => 'credit', 'parent_code' => '2100', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
// ══════════════════════════════════════════════════════════
// EXPENSES — Additional
// ══════════════════════════════════════════════════════════
// Sports & Activities
['account_code' => '5300', 'name_ar' => 'مصروفات أنشطة ورياضة', 'name_en' => 'Sports & Activities Expenses', 'account_type' => 'expense', 'account_nature' => 'debit', 'parent_code' => '5000', 'level' => 2, 'is_header' => 1, 'is_system' => 0],
['account_code' => '5301', 'name_ar' => 'مصروفات أكاديميات رياضية', 'name_en' => 'Sports Academy Expenses', 'account_type' => 'expense', 'account_nature' => 'debit', 'parent_code' => '5300', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
['account_code' => '5302', 'name_ar' => 'مصروفات مرافق ومعدات رياضية', 'name_en' => 'Sports Facilities Expenses', 'account_type' => 'expense', 'account_nature' => 'debit', 'parent_code' => '5300', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
['account_code' => '5303', 'name_ar' => 'مصروفات فعاليات وبطولات', 'name_en' => 'Events & Tournaments Expenses', 'account_type' => 'expense', 'account_nature' => 'debit', 'parent_code' => '5300', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
// Procurement-specific
['account_code' => '5216', 'name_ar' => 'مصروفات شحن ونقل مشتريات', 'name_en' => 'Shipping & Freight Expenses', 'account_type' => 'expense', 'account_nature' => 'debit', 'parent_code' => '5200', 'level' => 3, 'is_header' => 0, 'is_system' => 0],
];
foreach ($accounts as $acct) {
// Skip if already exists
$existing = $db->selectOne(
"SELECT id FROM chart_of_accounts WHERE account_code = ?",
[$acct['account_code']]
);
if ($existing) {
continue;
}
$parentId = null;
$parentCode = $acct['parent_code'] ?? null;
if ($parentCode !== null) {
$parentId = $resolveId($parentCode);
}
unset($acct['parent_code']);
$db->insert('chart_of_accounts', array_merge($acct, [
'parent_id' => $parentId,
'opening_balance' => '0.00',
'current_balance' => '0.00',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
]));
}
};
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