array('numeric'), 'transaction_id' => array('numeric'), 'amount' => array('money') ); var $hasMany = array( 'DebitReconciliation' => array( 'className' => 'Reconciliation', 'foreignKey' => 'debit_ledger_entry_id', ), 'CreditReconciliation' => array( 'className' => 'Reconciliation', 'foreignKey' => 'credit_ledger_entry_id', ), ); var $belongsTo = array( 'MonetarySource', 'Transaction', 'Customer', 'Lease', 'DebitLedger' => array( 'className' => 'Ledger', 'foreignKey' => 'debit_ledger_id', ), 'CreditLedger' => array( 'className' => 'Ledger', 'foreignKey' => 'credit_ledger_id', ), 'Ledger' => array( 'foreignKey' => false, // conditions will be used when JOINing tables // (such as find with LinkableBehavior) 'conditions' => array('OR' => array('%{MODEL_ALIAS}.debit_ledger_id = Ledger.id', '%{MODEL_ALIAS}.credit_ledger_id = Ledger.id')), // finderQuery will be used when tables are put // together across several querys, not with JOIN. // (such as find with ContainableBehavior) 'finderQuery' => 'NOT-IMPLEMENTED', 'counterQuery' => '' ), ); var $hasAndBelongsToMany = array( 'DebitReconciliationLedgerEntry' => array( 'className' => 'LedgerEntry', 'joinTable' => 'reconciliations', 'foreignKey' => 'credit_ledger_entry_id', 'associationForeignKey' => 'debit_ledger_entry_id', ), 'CreditReconciliationLedgerEntry' => array( 'className' => 'LedgerEntry', 'joinTable' => 'reconciliations', 'foreignKey' => 'debit_ledger_entry_id', 'associationForeignKey' => 'credit_ledger_entry_id', ), ); /************************************************************************** ************************************************************************** ************************************************************************** * function: conditionEntryAsCreditOrDebit * - returns the condition necessary to match a set of * Ledgers to all related LedgerEntries */ function conditionEntryAsCreditOrDebit($ledger_ids) { return array('OR' => array(array('debit_ledger_id' => $ledger_ids), array('credit_ledger_id' => $ledger_ids))); } /************************************************************************** ************************************************************************** ************************************************************************** * function: ledgerContext query helpers * - Returns parameters necessary to generate a query which * puts ledger entries into the context of a ledger. Since * debit/credit depends on the account type, it is required * as an argument for each function to avoid having to * query the ledger/account to find it out. */ function ledgerContextFields($ledger_id = null, $account_type = null) { $fields = array('id', 'effective_date', 'through_date', 'lease_id', 'customer_id', 'comment', 'amount'); if (isset($ledger_id)) { $fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," . " LedgerEntry.amount, NULL) AS debit"); $fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," . " LedgerEntry.amount, NULL) AS credit"); if (isset($account_type)) { if (in_array($account_type, array('ASSET', 'EXPENSE'))) $ledger_type = 'debit'; else $ledger_type = 'credit'; $fields[] = ("(IF(LedgerEntry.{$ledger_type}_ledger_id = $ledger_id," . " 1, -1) * LedgerEntry.amount) AS balance"); } } return $fields; } function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) { $fields = array('id', 'effective_date', 'through_date', 'comment', 'amount'); if (isset($ledger_id)) { $fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," . " SUM(LedgerEntry.amount), NULL) AS debit"); $fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," . " SUM(LedgerEntry.amount), NULL) AS credit"); if (isset($account_id) || isset($account_type)) { $Account = new Account(); $account_ftype = $Account->fundamentalType($account_id ? $account_id : $account_type); $fields[] = ("(IF(LedgerEntry.{$account_ftype}_ledger_id = $ledger_id," . " 1, -1) * SUM(LedgerEntry.amount)) AS balance"); } } elseif (isset($account_id)) { $fields[] = ("IF(DebitLedger.account_id = $account_id," . " SUM(LedgerEntry.amount), NULL) AS debit"); $fields[] = ("IF(CreditLedger.account_id = $account_id," . " SUM(LedgerEntry.amount), NULL) AS credit"); $Account = new Account(); $account_ftype = ucfirst($Account->fundamentalType($account_id)); $fields[] = ("(IF({$account_ftype}Ledger.account_id = $account_id," . " 1, -1) * SUM(LedgerEntry.amount)) AS balance"); } return $fields; } function ledgerContextConditions($ledger_id, $account_type) { if (isset($ledger_id)) { return array ('OR' => array(array('LedgerEntry.debit_ledger_id' => $ledger_id), array('LedgerEntry.credit_ledger_id' => $ledger_id)), ); } return array(); } /************************************************************************** ************************************************************************** ************************************************************************** * function: findInLedgerContext * - Returns an array of ledger entries that belong to a given ledger. * There is extra logic to also figure out whether the ledger_entry * amount is either a credit, or a debit, depending on how it was * written into the ledger, as well as whether the amount increases or * decreases the balance depending on the particular account type of * the ledger. */ function findInLedgerContext($ledger_id, $account_type, $cond = null, $link = null) { if (!isset($link)) $link = array('Transaction'); if (!isset($cond)) $cond = array(); $fields = $this->ledgerContextFields($ledger_id, $account_type); $cond[] = $this->ledgerContextConditions($ledger_id, $account_type); $order = array('Transaction.stamp'); $entries = $this->find ('all', array('link' => $link, 'fields' => $fields, 'conditions' => $cond, 'order' => $order, )); return $entries; } /************************************************************************** ************************************************************************** ************************************************************************** * function: findReconciledLedgerEntries * - Returns ledger entries that are reconciled to the given entry. * (such as payments towards a charge). */ function findReconciledLedgerEntries($id = null, $fundamental_type = null) { foreach (($fundamental_type ? array($fundamental_type) : array('debit', 'credit')) AS $fund) { $ucfund = ucfirst($fund); $reconciled[$fund]['entry'] = $this->find ('all', array ('link' => array ("ReconciliationLedgerEntry" => array ('class' => "{$ucfund}ReconciliationLedgerEntry", 'fields' => array ('id', "COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'", "LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'", ), ), ), 'group' => ("ReconciliationLedgerEntry.id"), 'conditions' => array('LedgerEntry.id' => $id), 'fields' => array(), )); //pr($reconciled); $balance = 0; foreach ($reconciled[$fund]['entry'] AS &$entry) { $entry = array_merge($entry["ReconciliationLedgerEntry"], $entry[0]); $balance += $entry['balance']; } $reconciled[$fund]['balance'] = $balance; } return $reconciled; } /************************************************************************** ************************************************************************** ************************************************************************** * function: reverse * - Reverses the ledger entry */ function reverse($id, $amount = null, $transaction_id = null, $rec_id = null) { /* pr(array('LedgerEntry::reverse', */ /* compact('id', 'amount', 'transaction_id', 'rec_id'))); */ // Get the LedgerEntry and related fields $entry = $this->find ('first', array('contain' => array('MonetarySource.id', 'Transaction.id', 'DebitLedger.id', 'DebitLedger.account_id', 'CreditLedger.id', 'CreditLedger.account_id', 'DebitReconciliationLedgerEntry' /* => */ /* array('DebitLedger.id', */ /* 'DebitLedger.account_id', */ /* 'CreditLedger.id', */ /* 'CreditLedger.account_id', */ /* ) */ , 'CreditReconciliationLedgerEntry' /* => */ /* array('DebitLedger.id', */ /* 'DebitLedger.account_id', */ /* 'CreditLedger.id', */ /* 'CreditLedger.account_id', */ /* ) */ , 'Customer.id', 'Lease.id', ), 'fields' => array('LedgerEntry.*'), 'conditions' => array(array('LedgerEntry.id' => $id), /* array('NOT' => */ /* array('OR' => */ /* array(array('DebitReconciliationLedgerEntry.id' => $rec_id), */ /* array('CreditReconciliationLedgerEntry.id' => $rec_id), */ /* ), */ /* ), */ /* ), */ ), )); //pr($entry); if (!isset($amount)) $amount = $entry['LedgerEntry']['amount']; $A = new Account(); $ids = $this->Ledger->Account->postLedgerEntry (array('transaction_id' => $transaction_id), null, array('debit_ledger_id' => $A->currentLedgerID($entry['CreditLedger']['account_id']), 'credit_ledger_id' => $A->currentLedgerID($entry['DebitLedger']['account_id']), 'effective_date' => $entry['LedgerEntry']['effective_date'], //'effective_date' => $entry['LedgerEntry']['effective_date'], 'amount' => $amount, 'lease_id' => $entry['Lease']['id'], 'customer_id' => $entry['Customer']['id'], 'comment' => "Reversal of Ledger Entry #{$id}", ), array('debit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'], 'amount' => $amount, ))), 'credit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'], 'amount' => $amount, ))), )); if ($ids['error']) return null; $tid = $ids['transaction_id']; /* pr(compact('entry')); */ foreach (array('Debit', 'Credit') AS $dc_type) { foreach ($entry[$dc_type . 'ReconciliationLedgerEntry'] AS $RLE) { /* pr(array('checkpoint' => "Reverse $dc_type LE", */ /* compact('id', 'RLE'))); */ if ($RLE['id'] == $rec_id) continue; if (!$this->reverse($RLE['id'], $RLE['Reconciliation']['amount'], $tid, $id)) $ids['error'] = true; /* $rids = $this->Ledger->Account->postLedgerEntry */ /* (array('transaction_id' => $tid), */ /* null, */ /* array('debit_ledger_id' => $A->currentLedgerID($RLE['CreditLedger']['account_id']), */ /* 'credit_ledger_id' => $A->currentLedgerID($RLE['DebitLedger']['account_id']), */ /* 'effective_date' => $RLE['effective_date'], */ /* //'effective_date' => $RLE['effective_date'], */ /* 'amount' => $RLE['Reconciliation']['amount'], */ /* 'lease_id' => $entry['Lease']['id'], */ /* 'customer_id' => $entry['Customer']['id'], */ /* 'comment' => "Reversal of Ledger Entry #{$RLE['id']}", */ /* ), */ /* array('debit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */ /* 'amount' => $RLE['Reconciliation']['amount'], */ /* ))), */ /* 'credit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */ /* 'amount' => $RLE['Reconciliation']['amount'], */ /* ))), */ /* )); */ /* if ($rids['error']) */ /* $ids['error'] = true; */ } } if ($ids['error']) return null; return $ids['id']; } /************************************************************************** ************************************************************************** ************************************************************************** * function: stats * - Returns summary data from the requested ledger entry */ function stats($id) { $query = array ( 'fields' => array("SUM(Reconciliation.amount) AS 'reconciled'"), 'conditions' => array(isset($cond) ? $cond : array(), array('LedgerEntry.id' => $id)), 'group' => 'LedgerEntry.id', ); // Get the applied amounts on the debit side $query['link'] = array('DebitReconciliationLedgerEntry' => array('alias' => 'DRLE', 'DRLETransaction' => array('class' => 'Transaction'))); $tmpstats = $this->find('first', $query); $stats['debit_amount_reconciled'] = $tmpstats[0]['reconciled']; // Get the applied amounts on the credit side $query['link'] = array('CreditReconciliationLedgerEntry' => array('alias' => 'CRLE', 'CRLETransaction' => array('class' => 'Transaction'))); $tmpstats = $this->find('first', $query); $stats['credit_amount_reconciled'] = $tmpstats[0]['reconciled']; return $stats; } }