0
@@ -118,185 +118,249 @@ bool entry_base_t::remove_transaction(transaction_t * xact)
0
bool entry_base_t::finalize()
0
- // Scan through and compute the total balance for the entry. This
0
- // is used for auto-calculating the value of entries with no cost,
0
- // and the per-unit price of unpriced commodities.
0
- bool no_amounts = true;
0
- bool saw_null = false;
0
+ // Scan through and compute the total balance for the entry. This is used
0
+ // for auto-calculating the value of entries with no cost, and the per-unit
0
+ // price of unpriced commodities.
0
+ transaction_t * null_xact = NULL;
0
+ // (do-transactions (xact entry)
0
+ // (when (xact-must-balance-p xact)
0
+ // (let ((amt (xact-amount* xact)))
0
+ // (setf balance (add balance (or (xact-cost xact) amt)))
0
+ // (error "Only one transaction with null amount allowed ~
0
+ // per entry (beg ~S end ~S)"
0
+ // (item-position-begin-line (entry-position entry))
0
+ // (item-position-end-line (entry-position entry)))
0
+ // (setf null-xact xact))))))
0
for (transactions_list::const_iterator x = transactions.begin();
0
x != transactions.end();
0
- if (! (*x)->has_flags(TRANSACTION_VIRTUAL) ||
0
- (*x)->has_flags(TRANSACTION_BALANCE)) {
0
+ if ((*x)->must_balance()) {
0
amount_t& p((*x)->cost ? *(*x)->cost : (*x)->amount);
0
+ if (
balance.is_null())0
- assert(! (*x)->amount.is_null());
0
- if ((*x)->cost && (*x)->amount.commodity().annotated) {
0
- annotated_commodity_t&
0
- ann_comm(static_cast<annotated_commodity_t&>
0
- ((*x)->amount.commodity()));
0
- if (ann_comm.details.price)
0
- balance += (*ann_comm.details.price * (*x)->amount.number() -
0
+ throw_(std::logic_error,
0
+ "Only one transaction with null amount allowed per entry");
0
assert(balance.valid());
0
- // If it's a null entry, then let the user have their fun
0
+ DEBUG("ledger.journal.finalize", "initial balance = " << balance);
0
- // If there is only one transaction, balance against the basket
0
- // account if one has been set.
0
+ // If there is only one transaction, balance against the default account if
0
+ // (when (= 1 (length (entry-transactions entry)))
0
+ // (if-let ((default-account
0
+ // (journal-default-account (entry-journal entry))))
0
+ // (make-transaction :entry entry
0
+ // :status (xact-status
0
+ // (first (entry-transactions entry)))
0
+ // :account default-account
0
+ // (add-transaction entry null-xact)))
0
if (journal && journal->basket && transactions.size() == 1) {
0
- assert(balance.is_amount());
0
- transaction_t * nxact = new transaction_t(journal->basket);
0
- // The amount doesn't need to be set because the code below will
0
- // balance this transaction against the other.
0
- add_transaction(nxact);
0
- nxact->add_flags(TRANSACTION_CALCULATED);
0
+ // jww (2008-07-24): Need to make the rest of the code aware of what to do
0
+ // when it sees a generated transaction.
0
+ null_xact = new transaction_t(journal->basket, TRANSACTION_GENERATED);
0
+ null_xact->state = (*transactions.begin())->state;
0
+ add_transaction(null_xact);
0
- // If the first transaction of a two-transaction entry is of a
0
- // different commodity than the other, and it has no per-unit price,
0
- // determine its price by dividing the unit count into the value of
0
- // the balance. This is done for the last eligible commodity.
0
+ if (null_xact != NULL) {
0
+ // If one transaction has no value at all, its value will become the
0
+ // inverse of the rest. If multiple commodities are involved, multiple
0
+ // transactions are generated to balance them all.
0
+ // (if (balance-p balance)
0
+ // (dolist (amount (balance-amounts balance))
0
+ // (setf (xact-amount* null-xact) (negate amount)
0
+ // (make-transaction :entry entry
0
+ // :account (xact-account null-xact)
0
+ // :amount (negate amount)
0
+ // (setf (xact-amount* null-xact) (negate balance)
0
+ // (xact-calculatedp null-xact) t))
0
+ if (balance.is_balance()) {
0
+ const balance_t& bal(balance.as_balance());
0
+ for (balance_t::amounts_map::const_iterator i = bal.amounts.begin();
0
+ i != bal.amounts.end();
0
+ null_xact->amount = (*i).second.negate();
0
+ add_transaction(new transaction_t(null_xact->account,
0
+ TRANSACTION_GENERATED));
0
+ null_xact->amount = balance.as_amount().negate();
0
+ null_xact->add_flags(TRANSACTION_CALCULATED);
0
+ else if (balance.is_balance() &&
0
+ balance.as_balance().amounts.size() == 2) {
0
+ // When an entry involves two different commodities (regardless of how
0
+ // many transactions there are) determine the conversion ratio by dividing
0
+ // the total value of one commodity by the total value of the other. This
0
+ // establishes the per-unit cost for this transaction for both
0
+ // (when (and (balance-p balance)
0
+ // (= 2 (balance-commodity-count balance)))
0
+ // (destructuring-bind (x y) (balance-amounts balance)
0
+ // (let ((a-commodity (amount-commodity x))
0
+ // (per-unit-cost (value-abs (divide x y))))
0
+ // (do-transactions (xact entry)
0
+ // (let ((amount (xact-amount* xact)))
0
+ // (unless (or (xact-cost xact)
0
+ // (not (xact-must-balance-p xact))
0
+ // (commodity-equal (amount-commodity amount)
0
+ // (setf balance (subtract balance amount)
0
+ // (xact-cost xact) (multiply per-unit-cost amount)
0
+ // balance (add balance (xact-cost xact))))))))))
0
- if (! saw_null && balance && balance.is_balance()) {
0
const balance_t& bal(balance.as_balance());
0
- if (bal.amounts.size() == 2) {
0
- transactions_list::const_iterator x = transactions.begin();
0
- assert(! (*x)->amount.is_null());
0
- commodity_t& this_comm = (*x)->amount.commodity();
0
- balance_t::amounts_map::const_iterator this_bal =
0
- bal.amounts.find(&this_comm);
0
- assert(this_bal != bal.amounts.end());
0
- balance_t::amounts_map::const_iterator other_bal =
0
- if (this_bal == other_bal)
0
- amount_t per_unit_cost =
0
- ((*other_bal).second / (*this_bal).second.number()).unround();
0
- for (; x != transactions.end(); x++) {
0
- if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
0
- (*x)->amount.commodity() != this_comm)
0
- balance -= (*x)->amount;
0
- entry_t * entry = dynamic_cast<entry_t *>(this);
0
- if ((*x)->amount.commodity() &&
0
- ! (*x)->amount.commodity().annotated)
0
- (*x)->amount.annotate_commodity
0
- (annotation_t(per_unit_cost.abs(),
0
- entry ? entry->actual_date() : optional<datetime_t>(),
0
- entry ? entry->code : optional<string>()));
0
- (*x)->cost = - (per_unit_cost * (*x)->amount.number());
0
- balance += *(*x)->cost;
0
+ balance_t::amounts_map::const_iterator a = bal.amounts.begin();
0
+ const amount_t& x((*a++).second);
0
+ const amount_t& y((*a++).second);
0
+ if (! y.is_realzero()) {
0
+ amount_t per_unit_cost = (x / y).abs();
0
+ commodity_t& comm(x.commodity());
0
+ for (transactions_list::const_iterator x = transactions.begin();
0
+ x != transactions.end();
0
+ const amount_t& x_amt((*x)->amount);
0
+ ! (*x)->must_balance() ||
0
+ x_amt.commodity() == comm)) {
0
+ DEBUG("ledger.journal.finalize", "before operation 1 = " << balance);
0
+ DEBUG("ledger.journal.finalize", "after operation 1 = " << balance);
0
+ DEBUG("ledger.journal.finalize", "x_amt = " << x_amt);
0
+ DEBUG("ledger.journal.finalize", "per_unit_cost = " << per_unit_cost);
0
+ (*x)->cost = per_unit_cost * x_amt;
0
+ DEBUG("ledger.journal.finalize", "*(*x)->cost = " << *(*x)->cost);
0
+ balance += *(*x)->cost;
0
+ DEBUG("ledger.journal.finalize", "after operation 2 = " << balance);
0
- // Walk through each of the transactions, fixing up any that we
0
- // can, and performing any on-the-fly calculations.
0
+ DEBUG("ledger.journal.finalize", "resolved balance = " << balance);
0
- bool empty_allowed = true;
0
+ // Now that the transaction list has its final form, calculate the balance
0
+ // once more in terms of total cost, accounting for any possible gain/loss
0
+ // (do-transactions (xact entry)
0
+ // (when (xact-cost xact)
0
+ // (let ((amount (xact-amount* xact)))
0
+ // (assert (not (commodity-equal (amount-commodity amount)
0
+ // (amount-commodity (xact-cost xact)))))
0
+ // (multiple-value-bind (annotated-amount total-cost basis-cost)
0
+ // (exchange-commodity amount :total-cost (xact-cost xact)
0
+ // :moment (entry-date entry)
0
+ // :tag (entry-code entry))
0
+ // (if (annotated-commodity-p (amount-commodity amount))
0
+ // (if-let ((price (annotation-price
0
+ // (commodity-annotation
0
+ // (amount-commodity amount)))))
0
+ // (add balance (subtract basis-cost total-cost))))
0
+ // (setf (xact-amount* xact) annotated-amount))))))
0
for (transactions_list::const_iterator x = transactions.begin();
0
x != transactions.end();
0
- if (! (*x)->amount.is_null() ||
0
- ((*x)->has_flags(TRANSACTION_VIRTUAL) &&
0
- ! (*x)->has_flags(TRANSACTION_BALANCE)))
0
- throw_(std::logic_error,
0
- "Only one transaction with null amount allowed per entry");
0
- empty_allowed = false;
0
- // If one transaction gives no value at all, its value will become
0
- // the inverse of the value of the others. If multiple
0
- // commodities are involved, multiple transactions will be
0
- // generated to balance them all.
0
- const balance_t * bal = NULL;
0
- switch (balance.type()) {
0
- case value_t::BALANCE_PAIR:
0
- bal = &balance.as_balance_pair().quantity();
0
- case value_t::BALANCE:
0
- bal = &balance.as_balance();
0
- if (bal->amounts.size() < 2) {
0
- balance.cast(value_t::AMOUNT);
0
- for (balance_t::amounts_map::const_iterator
0
- i = bal->amounts.begin();
0
- i != bal->amounts.end();
0
- amount_t amt = (*i).second.negate();
0
- transaction_t * nxact = new transaction_t((*x)->account);
0
- add_transaction(nxact);
0
- nxact->add_flags(TRANSACTION_CALCULATED);
0
+ const amount_t& x_amt((*x)->amount);
0
+ assert(x_amt.commodity() != (*x)->cost->commodity());
0
+ entry_t * entry = dynamic_cast<entry_t *>(this);
0
+ // jww (2008-07-24): Pass the entry's code here if we can, as the
0
+ commodity_t::exchange(x_amt, final_cost, basis_cost,
0
+ (*x)->cost, none, (*x)->actual_date(),
0
+ entry ? entry->code : optional<string>());
0
+ if ((*x)->amount.commodity_annotated()) {
0
+ if (ann_amount.annotation_details().price) {
0
+ if (balance.is_null())
0
+ balance = basis_cost - final_cost;
0
+ balance += basis_cost - final_cost;
0
+ (*x)->amount = ann_amount;
0
- (*x)->amount = balance.as_amount().negate();
0
- (*x)->add_flags(TRANSACTION_CALCULATED);
0
- balance += (*x)->amount;
0
+ DEBUG("ledger.journal.finalize", "final balance = " << balance);
0
+ // (if (value-zerop balance)
0
+ // (setf (entry-normalizedp entry) t))
0
+ // (error "Entry does not balance (beg ~S end ~S); remaining balance is:~%~A"
0
+ // (item-position-begin-line (entry-position entry))
0
+ // (item-position-end-line (entry-position entry))
0
+ // (format-value balance :width 20)))
0
+ if (! balance.is_null() && ! balance.is_zero()) {
0
new balance_error("Entry does not balance",
0
new entry_context(*this, "While balancing entry:"));
0
- DEBUG("ledger.journal.unbalanced_remainder", "balance = " << balance);
0
err->context.push_front
0
(new value_context(balance, "Unbalanced remainder is:"));