accounting modules
This commit is contained in:
@@ -264,13 +264,27 @@ class OrderManager {
|
||||
if (!$row) {
|
||||
throw new Exception("Order not found.");
|
||||
}
|
||||
if ((int)$row['status'] !== 0) {
|
||||
throw new Exception("Only draft orders can be edited.");
|
||||
$cur_status = (int)$row['status'];
|
||||
if ($cur_status !== 0 && $cur_status !== -2) {
|
||||
throw new Exception("Only draft or pending orders can be edited.");
|
||||
}
|
||||
|
||||
$log = json_decode($row['log'] ?? '[]', true) ?: [];
|
||||
$log[] = $logging;
|
||||
|
||||
// Auto-promote Pending → Draft when all items have a warehouse assigned
|
||||
$new_status = $cur_status;
|
||||
if ($cur_status === -2) {
|
||||
$all_have_warehouse = !empty($items) && array_reduce(
|
||||
$items,
|
||||
fn($ok, $item) => $ok && (int)($item['warehouse_id'] ?? 0) > 0,
|
||||
true
|
||||
);
|
||||
if ($all_have_warehouse) {
|
||||
$new_status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pdo->prepare(
|
||||
"UPDATE td_order SET
|
||||
contact_id = :contact_id,
|
||||
@@ -283,6 +297,7 @@ class OrderManager {
|
||||
shipping_tracking_number = :shipping_tracking_number,
|
||||
grand_total = :grand_total,
|
||||
notes = :notes,
|
||||
status = :status,
|
||||
`log` = :log
|
||||
WHERE id = :id AND company_id = :company_id"
|
||||
)->execute([
|
||||
@@ -296,6 +311,7 @@ class OrderManager {
|
||||
':shipping_tracking_number' => $tracking_no,
|
||||
':grand_total' => $grand_total,
|
||||
':notes' => $data['notes'] ?? '',
|
||||
':status' => $new_status,
|
||||
':log' => json_encode($log),
|
||||
':id' => $id,
|
||||
':company_id' => $this->company_id,
|
||||
@@ -305,23 +321,28 @@ class OrderManager {
|
||||
|
||||
} else {
|
||||
|
||||
$log = [$logging];
|
||||
$log = [$logging];
|
||||
$quotation_id = (int)($data['quotation_id'] ?? 0);
|
||||
// Quotation-converted orders start as Pending (-2); direct orders as Draft (0)
|
||||
$init_status = $quotation_id > 0 ? -2 : 0;
|
||||
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO td_order
|
||||
(company_id, uuid, order_number, contact_id, order_date,
|
||||
(company_id, uuid, quotation_id, order_number, contact_id, order_date,
|
||||
status, payment_status, subtotal, discount, tax,
|
||||
shipping_fee, shipping_tracking_number, grand_total, items, notes, `log`, created_at)
|
||||
VALUES
|
||||
(:company_id, :uuid, :order_number, :contact_id, :order_date,
|
||||
0, 0, :subtotal, :discount, :tax,
|
||||
(:company_id, :uuid, :quotation_id, :order_number, :contact_id, :order_date,
|
||||
:status, 0, :subtotal, :discount, :tax,
|
||||
:shipping_fee, :shipping_tracking_number, :grand_total, :items, :notes, :log, :created_at)"
|
||||
)->execute([
|
||||
':company_id' => $this->company_id,
|
||||
':uuid' => bin2hex(random_bytes(16)),
|
||||
':quotation_id' => $quotation_id,
|
||||
':order_number' => $this->generateOrderNumber(),
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':order_date' => $data['order_date'] ?? date('Y-m-d'),
|
||||
':status' => $init_status,
|
||||
':subtotal' => $subtotal,
|
||||
':discount' => $discount,
|
||||
':tax' => $tax,
|
||||
@@ -539,6 +560,7 @@ class OrderManager {
|
||||
"Cannot cancel an order that is processing or completed."
|
||||
);
|
||||
}
|
||||
// Pending orders (-2) have no stock rows — skip the stock reversal loop below
|
||||
|
||||
// ── Guard: no active invoice ──────────────────────────────────────
|
||||
$sth = $this->pdo->prepare(
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* PostingManager
|
||||
*
|
||||
* The ONLY bridge between WMS and the accounting engine.
|
||||
* WMS API endpoints call methods here — nothing in WMS imports
|
||||
* accounting classes directly.
|
||||
*
|
||||
* Usage in a WMS API endpoint (add at the end, after business doc saves):
|
||||
* require_once '../../../assets/utils/classes/PostingManager.php';
|
||||
* (new PostingManager($pdo2, $company_id))->postInvoice($invoice_id);
|
||||
*
|
||||
* If posting fails it logs silently — the business document is already saved.
|
||||
*/
|
||||
class PostingManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: postInvoice(int $invoice_id): void
|
||||
// TODO: voidInvoice(int $invoice_id): void
|
||||
|
||||
// TODO: postPurchaseReceive(int $po_id): void
|
||||
// TODO: voidPurchaseReceive(int $po_id): void
|
||||
|
||||
// TODO: postReceipt(int $receipt_id): void
|
||||
// TODO: voidReceipt(int $receipt_id): void
|
||||
|
||||
// TODO: postPayment(int $payment_id): void
|
||||
// TODO: voidPayment(int $payment_id): void
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountingEngine
|
||||
*
|
||||
* Core double-entry validation and rules.
|
||||
* Ensures every journal entry balances (total debit = total credit).
|
||||
* All other accounting classes depend on this for validation.
|
||||
*/
|
||||
class AccountingEngine
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: validate journal lines balance (sum debit = sum credit)
|
||||
// TODO: validate account codes exist and are posting accounts
|
||||
// TODO: validate period is open
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
class ChartOfAccounts
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid
|
||||
ORDER BY account_code ASC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getByCode(string $account_code): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid AND account_code = :code
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':code' => $account_code]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function getByType(string $account_type): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid AND account_type = :type AND status = 1
|
||||
ORDER BY account_code ASC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':type' => $account_type]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById(int $id): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_account
|
||||
WHERE company_id = :cid AND id = :id
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function create(array $data): int
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"INSERT INTO ac_account
|
||||
(company_id, account_code, account_name, account_type, parent_code, is_posting, status)
|
||||
VALUES
|
||||
(:cid, :code, :name, :type, :parent, :posting, :status)"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':code' => trim($data['account_code']),
|
||||
':name' => trim($data['account_name']),
|
||||
':type' => $data['account_type'],
|
||||
':parent' => !empty($data['parent_code']) ? trim($data['parent_code']) : null,
|
||||
':posting' => (int)($data['is_posting'] ?? 1),
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
return (int)$this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
public function update(int $id, array $data): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_account SET
|
||||
account_code = :code,
|
||||
account_name = :name,
|
||||
account_type = :type,
|
||||
parent_code = :parent,
|
||||
is_posting = :posting,
|
||||
status = :status
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':id' => $id,
|
||||
':code' => trim($data['account_code']),
|
||||
':name' => trim($data['account_name']),
|
||||
':type' => $data['account_type'],
|
||||
':parent' => !empty($data['parent_code']) ? trim($data['parent_code']) : null,
|
||||
':posting' => (int)($data['is_posting'] ?? 1),
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_account SET status = 0
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function isPostingAccount(string $account_code): bool
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT is_posting FROM ac_account
|
||||
WHERE company_id = :cid AND account_code = :code AND status = 1
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':code' => $account_code]);
|
||||
return (bool)$sth->fetchColumn();
|
||||
}
|
||||
|
||||
public function getStats(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(status = 1) AS active,
|
||||
SUM(status = 0) AS inactive,
|
||||
SUM(account_type = 'asset') AS asset,
|
||||
SUM(account_type = 'liability') AS liability,
|
||||
SUM(account_type = 'equity') AS equity,
|
||||
SUM(account_type = 'revenue') AS revenue,
|
||||
SUM(account_type = 'expense') AS expense
|
||||
FROM ac_account
|
||||
WHERE company_id = :cid"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
class DepartmentManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_department
|
||||
WHERE company_id = :cid
|
||||
ORDER BY dept_code ASC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById(int $id): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_department
|
||||
WHERE company_id = :cid AND id = :id LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function create(array $data): int
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"INSERT INTO ac_department (company_id, dept_code, dept_name, description, status)
|
||||
VALUES (:cid, :code, :name, :desc, :status)"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':code' => trim($data['dept_code']),
|
||||
':name' => trim($data['dept_name']),
|
||||
':desc' => $data['description'] ?? null,
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
return (int)$this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
public function update(int $id, array $data): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_department SET
|
||||
dept_code = :code,
|
||||
dept_name = :name,
|
||||
description = :desc,
|
||||
status = :status
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':id' => $id,
|
||||
':code' => trim($data['dept_code']),
|
||||
':name' => trim($data['dept_name']),
|
||||
':desc' => $data['description'] ?? null,
|
||||
':status' => (int)($data['status'] ?? 1),
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"UPDATE ac_department SET status = 0
|
||||
WHERE company_id = :cid AND id = :id"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function getStats(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) AS total,
|
||||
SUM(status = 1) AS active,
|
||||
SUM(status = 0) AS inactive
|
||||
FROM ac_department WHERE company_id = :cid"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* FinancialReports
|
||||
*
|
||||
* Generates all financial statements from ac_balance + ac_journal_line.
|
||||
* Read-only — no writes.
|
||||
*/
|
||||
class FinancialReports
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: getTrialBalance(string $from_period, string $to_period, ?int $dept_id): array
|
||||
// TODO: getProfitAndLoss(string $from_period, string $to_period, ?int $dept_id): array
|
||||
// TODO: getBalanceSheet(string $as_of_period, ?int $dept_id): array
|
||||
// TODO: getGLMovement(string $account_code, string $from_period, string $to_period): array
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* JournalManager
|
||||
*
|
||||
* Create, read, and void journal entries in ac_journal + ac_journal_line.
|
||||
* Updates ac_balance after each post.
|
||||
* Never called directly by WMS — goes through PostingManager.
|
||||
*/
|
||||
class JournalManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: create(array $header, array $lines): int — returns journal_id
|
||||
// TODO: void(int $journal_id): void
|
||||
// TODO: getBySource(string $source_type, int $source_id): ?array
|
||||
// TODO: getById(int $journal_id): ?array
|
||||
// TODO: updateBalance(string $period, string $account_code, ?int $dept_id, float $debit, float $credit): void
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
class PeriodManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_period
|
||||
WHERE company_id = :cid
|
||||
ORDER BY period DESC"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getById(int $id): ?array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_period WHERE company_id = :cid AND id = :id LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function isOpen(string $period): bool
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT status FROM ac_period
|
||||
WHERE company_id = :cid AND period = :period LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
$status = $sth->fetchColumn();
|
||||
return $status === 'open';
|
||||
}
|
||||
|
||||
// Get existing period or auto-create it as open
|
||||
public function getOrCreate(string $period): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT * FROM ac_period WHERE company_id = :cid AND period = :period LIMIT 1"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row) return $row;
|
||||
|
||||
$this->pdo->prepare(
|
||||
"INSERT INTO ac_period (company_id, period, status) VALUES (:cid, :period, 'open')"
|
||||
)->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
|
||||
return $this->getOrCreate($period);
|
||||
}
|
||||
|
||||
// Create a single period (manual)
|
||||
public function create(string $period): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"INSERT IGNORE INTO ac_period (company_id, period, status) VALUES (:cid, :period, 'open')"
|
||||
)->execute([':cid' => $this->companyId, ':period' => $period]);
|
||||
}
|
||||
|
||||
// Bulk-create all 12 months of a year
|
||||
public function createYear(int $year): void
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"INSERT IGNORE INTO ac_period (company_id, period, status) VALUES (:cid, :period, 'open')"
|
||||
);
|
||||
for ($m = 1; $m <= 12; $m++) {
|
||||
$sth->execute([
|
||||
':cid' => $this->companyId,
|
||||
':period' => sprintf('%04d-%02d', $year, $m),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function open(int $id): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"UPDATE ac_period SET status = 'open', closed_at = NULL
|
||||
WHERE company_id = :cid AND id = :id AND status = 'closed'"
|
||||
)->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function close(int $id): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"UPDATE ac_period SET status = 'closed', closed_at = NOW()
|
||||
WHERE company_id = :cid AND id = :id AND status = 'open'"
|
||||
)->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function lock(int $id): void
|
||||
{
|
||||
$this->pdo->prepare(
|
||||
"UPDATE ac_period SET status = 'locked', locked_at = NOW()
|
||||
WHERE company_id = :cid AND id = :id AND status = 'closed'"
|
||||
)->execute([':cid' => $this->companyId, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function getStats(): array
|
||||
{
|
||||
$sth = $this->pdo->prepare(
|
||||
"SELECT COUNT(*) AS total,
|
||||
SUM(status = 'open') AS open_count,
|
||||
SUM(status = 'closed') AS closed_count,
|
||||
SUM(status = 'locked') AS locked_count
|
||||
FROM ac_period WHERE company_id = :cid"
|
||||
);
|
||||
$sth->execute([':cid' => $this->companyId]);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* TaxReportManager
|
||||
*
|
||||
* Generates VAT and withholding tax reports from journal lines
|
||||
* tagged to tax accounts (output VAT, input VAT, WHT).
|
||||
* Read-only — no writes.
|
||||
*/
|
||||
class TaxReportManager
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: getOutputVAT(string $from_period, string $to_period): array
|
||||
// TODO: getInputVAT(string $from_period, string $to_period): array
|
||||
// TODO: getWithholdingTax(string $from_period, string $to_period): array
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* InvoicePosting
|
||||
*
|
||||
* Builds the journal entry for a posted sales invoice.
|
||||
* DR: Accounts Receivable
|
||||
* CR: Revenue + Output VAT
|
||||
*/
|
||||
class InvoicePosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $invoice_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* PaymentPosting
|
||||
*
|
||||
* Builds the journal entry when a supplier payment is recorded.
|
||||
* DR: Accounts Payable
|
||||
* CR: Bank / Cash
|
||||
*/
|
||||
class PaymentPosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $payment_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* PurchasePosting
|
||||
*
|
||||
* Builds the journal entry when a Purchase Order is received (GRN).
|
||||
* DR: Inventory
|
||||
* CR: Accounts Payable
|
||||
*/
|
||||
class PurchasePosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $po_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* ReceiptPosting
|
||||
*
|
||||
* Builds the journal entry when a customer receipt is recorded.
|
||||
* DR: Bank / Cash
|
||||
* CR: Accounts Receivable
|
||||
*/
|
||||
class ReceiptPosting
|
||||
{
|
||||
private PDO $pdo;
|
||||
private int $companyId;
|
||||
|
||||
public function __construct(PDO $pdo, int $company_id)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
$this->companyId = $company_id;
|
||||
}
|
||||
|
||||
// TODO: build(int $receipt_id): array — returns [header, lines] ready for JournalManager
|
||||
}
|
||||
Reference in New Issue
Block a user