197 lines
7.1 KiB
PHP
197 lines
7.1 KiB
PHP
<?php
|
|
/**
|
|
* Accounting Helper for Automatic Journal Entries
|
|
*/
|
|
|
|
function createJournalEntry($date, $description, $reference, $source_type, $source_id, $items) {
|
|
$db = db();
|
|
try {
|
|
$stmt = $db->prepare("INSERT INTO acc_journal_entries (entry_date, description, reference, source_type, source_id) VALUES (?, ?, ?, ?, ?)");
|
|
$stmt->execute([$date, $description, $reference, $source_type, $source_id]);
|
|
$entryId = $db->lastInsertId();
|
|
|
|
$stmtLedger = $db->prepare("INSERT INTO acc_ledger (journal_entry_id, account_id, debit, credit) VALUES (?, ?, ?, ?)");
|
|
foreach ($items as $item) {
|
|
// Find account ID by code
|
|
$stmtAcc = $db->prepare("SELECT id FROM acc_accounts WHERE code = ?");
|
|
$stmtAcc->execute([$item['code']]);
|
|
$accountId = $stmtAcc->fetchColumn();
|
|
|
|
if ($accountId) {
|
|
$stmtLedger->execute([$entryId, $accountId, $item['debit'] ?? 0, $item['credit'] ?? 0]);
|
|
}
|
|
}
|
|
return $entryId;
|
|
} catch (Exception $e) {
|
|
error_log("Accounting Error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Record a Sale
|
|
*/
|
|
function recordSaleJournal($invoice_id, $amount, $date, $items_data = [], $vat_amount = 0) {
|
|
$subtotal = $amount - $vat_amount;
|
|
$entries = [
|
|
['code' => '1300', 'debit' => $amount], // Accounts Receivable (Asset increases)
|
|
['code' => '4100', 'credit' => $subtotal] // Sales Revenue (Revenue increases)
|
|
];
|
|
|
|
if ($vat_amount > 0) {
|
|
$entries[] = ['code' => '2300', 'credit' => $vat_amount]; // VAT Payable (Liability increases)
|
|
}
|
|
|
|
// Inventory & COGS
|
|
$total_cogs = 0;
|
|
foreach ($items_data as $item) {
|
|
$stmt = db()->prepare("SELECT purchase_price FROM stock_items WHERE id = ?");
|
|
$stmt->execute([$item['id']]);
|
|
$cost = (float)$stmt->fetchColumn();
|
|
$total_cogs += ($cost * $item['qty']);
|
|
}
|
|
|
|
if ($total_cogs > 0) {
|
|
$entries[] = ['code' => '5100', 'debit' => $total_cogs]; // COGS (Expense increases)
|
|
$entries[] = ['code' => '1400', 'credit' => $total_cogs]; // Inventory (Asset decreases)
|
|
}
|
|
|
|
return createJournalEntry($date, "Sale Invoice #$invoice_id", "INV-$invoice_id", 'invoice', $invoice_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record a Purchase
|
|
*/
|
|
function recordPurchaseJournal($invoice_id, $amount, $date, $items_data = [], $vat_amount = 0) {
|
|
$subtotal = $amount - $vat_amount;
|
|
$entries = [
|
|
['code' => '1400', 'debit' => $subtotal], // Inventory (Asset increases)
|
|
['code' => '2100', 'credit' => $amount] // Accounts Payable (Liability increases)
|
|
];
|
|
|
|
if ($vat_amount > 0) {
|
|
$entries[] = ['code' => '1500', 'debit' => $vat_amount]; // VAT Input (Asset increases)
|
|
}
|
|
|
|
return createJournalEntry($date, "Purchase Invoice #$invoice_id", "PINV-$invoice_id", 'invoice', $invoice_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record a Payment Received (from Customer)
|
|
*/
|
|
function recordPaymentReceivedJournal($payment_id, $amount, $date, $method) {
|
|
$code = ($method === 'Bank' || $method === 'Transfer') ? '1200' : '1100';
|
|
$entries = [
|
|
['code' => $code, 'debit' => $amount], // Cash/Bank (Asset increases)
|
|
['code' => '1300', 'credit' => $amount] // Accounts Receivable (Asset decreases)
|
|
];
|
|
return createJournalEntry($date, "Payment Received #$payment_id", "PAY-$payment_id", 'payment', $payment_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record a Payment Made (to Supplier)
|
|
*/
|
|
function recordPaymentMadeJournal($payment_id, $amount, $date, $method) {
|
|
$code = ($method === 'Bank' || $method === 'Transfer') ? '1200' : '1100';
|
|
$entries = [
|
|
['code' => '2100', 'debit' => $amount], // Accounts Payable (Liability decreases)
|
|
['code' => $code, 'credit' => $amount] // Cash/Bank (Asset decreases)
|
|
];
|
|
return createJournalEntry($date, "Payment Made #$payment_id", "PAY-$payment_id", 'payment', $payment_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record a Sales Return
|
|
*/
|
|
function recordSalesReturnJournal($return_id, $amount, $date) {
|
|
$entries = [
|
|
['code' => '4100', 'debit' => $amount], // Sales Revenue (Revenue decreases) - ideally a "Sales Returns" account
|
|
['code' => '1300', 'credit' => $amount] // Accounts Receivable (Asset decreases)
|
|
];
|
|
return createJournalEntry($date, "Sales Return #$return_id", "SRET-$return_id", 'sales_return', $return_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record a Purchase Return
|
|
*/
|
|
function recordPurchaseReturnJournal($return_id, $amount, $date) {
|
|
$entries = [
|
|
['code' => '2100', 'debit' => $amount], // Accounts Payable (Liability decreases)
|
|
['code' => '1400', 'credit' => $amount] // Inventory (Asset decreases)
|
|
];
|
|
return createJournalEntry($date, "Purchase Return #$return_id", "PRET-$return_id", 'purchase_return', $return_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record an Expense
|
|
*/
|
|
function recordExpenseJournal($expense_id, $amount, $date, $description, $method = 'Cash') {
|
|
$paymentCode = ($method === 'Bank' || $method === 'Transfer') ? '1200' : '1100';
|
|
$entries = [
|
|
['code' => '5200', 'debit' => $amount], // Operating Expenses (Expense increases)
|
|
['code' => $paymentCode, 'credit' => $amount] // Cash/Bank (Asset decreases)
|
|
];
|
|
return createJournalEntry($date, "Expense: $description", "EXP-$expense_id", 'expense', $expense_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Record Payroll Payment
|
|
*/
|
|
function recordPayrollJournal($payroll_id, $amount, $date, $emp_name) {
|
|
$entries = [
|
|
['code' => '5200', 'debit' => $amount], // Using 5200 for now, or could use 5300 if added
|
|
['code' => '1100', 'credit' => $amount] // Paid in Cash
|
|
];
|
|
return createJournalEntry($date, "Payroll Payment - $emp_name", "PAYROLL-$payroll_id", 'payroll', $payroll_id, $entries);
|
|
}
|
|
|
|
/**
|
|
* Get Account Balance
|
|
*/
|
|
function getAccountBalance($code, $start_date = null, $end_date = null) {
|
|
$db = db();
|
|
$sql = "SELECT SUM(l.debit) - SUM(l.credit) as balance
|
|
FROM acc_ledger l
|
|
JOIN acc_accounts a ON l.account_id = a.id
|
|
JOIN acc_journal_entries e ON l.journal_entry_id = e.id
|
|
WHERE a.code LIKE ?";
|
|
$params = [$code . '%'];
|
|
|
|
if ($start_date) {
|
|
$sql .= " AND e.entry_date >= ?";
|
|
$params[] = $start_date;
|
|
}
|
|
if ($end_date) {
|
|
$sql .= " AND e.entry_date <= ?";
|
|
$params[] = $end_date;
|
|
}
|
|
|
|
$stmt = $db->prepare($sql);
|
|
$stmt->execute($params);
|
|
$balance = (float)$stmt->fetchColumn();
|
|
|
|
// For Liability, Equity, Revenue: Balance = Credit - Debit
|
|
$stmtType = $db->prepare("SELECT type FROM acc_accounts WHERE code = ?");
|
|
$stmtType->execute([$code]);
|
|
$type = $stmtType->fetchColumn();
|
|
|
|
if (in_array($type, ['liability', 'equity', 'revenue'])) {
|
|
return -$balance;
|
|
}
|
|
return $balance;
|
|
}
|
|
|
|
/**
|
|
* Get VAT Report
|
|
*/
|
|
function getVatReport($start_date = null, $end_date = null) {
|
|
$input_vat = getAccountBalance('1500', $start_date, $end_date);
|
|
$output_vat = getAccountBalance('2300', $start_date, $end_date);
|
|
|
|
return [
|
|
'input_vat' => $input_vat,
|
|
'output_vat' => $output_vat,
|
|
'net_vat' => $output_vat - $input_vat
|
|
];
|
|
}
|