public
Description: A double-entry accounting system with a command-line reporting interface
Homepage: http://www.newartisans.com/software/ledger.html
Clone URL: git://github.com/jwiegley/ledger.git
Parsing now works again.  And there was much rejoicing.
jwiegley (author)
Thu Jul 24 08:36:40 -0700 2008
commit  ee396957226e2273bc60ede7192c27038c432f24
tree    e87839dd6949461f5e3c9fb34071e7cea412b761
parent  171f79dda268b59d708f5002d1d616a71a30ecb5
...
855
856
857
858
 
859
860
861
862
863
864
 
 
 
865
866
867
868
869
870
 
 
871
872
873
...
1097
1098
1099
1100
1101
 
 
 
 
1102
1103
1104
...
855
856
857
 
858
859
860
861
862
863
 
864
865
866
867
 
 
 
 
 
868
869
870
871
872
...
1096
1097
1098
 
 
1099
1100
1101
1102
1103
1104
1105
0
@@ -855,19 +855,18 @@ bool amount_t::commodity_annotated() const
0
   return commodity().annotated;
0
 }
0
 
0
-annotation_t amount_t::annotation_details() const
0
+annotation_t& amount_t::annotation_details()
0
 {
0
   if (! quantity)
0
     throw_(amount_error,
0
    "Cannot return commodity annotation details of an uninitialized amount");
0
 
0
- assert(! commodity().annotated || as_annotated_commodity(commodity()).details);
0
+ if (! commodity().is_annotated())
0
+ throw_(amount_error,
0
+ "Request for annotation details from an unannotated amount");
0
 
0
- if (commodity().annotated) {
0
- annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
0
- return ann_comm.details;
0
- }
0
- return annotation_t();
0
+ annotated_commodity_t& ann_comm(as_annotated_commodity(commodity()));
0
+ return ann_comm.details;
0
 }
0
 
0
 amount_t amount_t::strip_annotations(const bool _keep_price,
0
@@ -1097,8 +1096,10 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
0
 {
0
   assert(valid());
0
 
0
- if (! quantity)
0
- throw_(amount_error, "Cannot write out an uninitialized amount");
0
+ if (! quantity) {
0
+ _out << "<null>";
0
+ return;
0
+ }
0
 
0
   amount_t base(*this);
0
   if (! amount_t::keep_base)
...
538
539
540
541
542
543
544
545
546
 
 
 
 
 
 
 
 
 
 
 
547
548
549
...
538
539
540
 
 
 
 
 
 
541
542
543
544
545
546
547
548
549
550
551
552
553
554
0
@@ -538,12 +538,17 @@ public:
0
    * amount_t::keep_price, amount_t::keep_date and amount_t::keep_tag
0
    * have been set to (which all default to false).
0
    */
0
- void annotate_commodity(const annotation_t& details);
0
- bool commodity_annotated() const;
0
- annotation_t annotation_details() const;
0
- amount_t strip_annotations(const bool _keep_price = keep_price,
0
- const bool _keep_date = keep_date,
0
- const bool _keep_tag = keep_tag) const;
0
+ void annotate_commodity(const annotation_t& details);
0
+ bool commodity_annotated() const;
0
+
0
+ annotation_t& annotation_details();
0
+ const annotation_t& annotation_details() const {
0
+ return const_cast<amount_t&>(*this).annotation_details();
0
+ }
0
+
0
+ amount_t strip_annotations(const bool _keep_price = keep_price,
0
+ const bool _keep_date = keep_date,
0
+ const bool _keep_tag = keep_tag) const;
0
 
0
   /**
0
    * Parsing methods. The method `parse' is used to parse an amount
...
118
119
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
122
123
...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
0
@@ -118,6 +118,75 @@ optional<amount_t> commodity_t::value(const optional<datetime_t>& moment)
0
   return price;
0
 }
0
 
0
+amount_t commodity_t::exchange(const amount_t& amount,
0
+ amount_t& final_cost, // out
0
+ amount_t& basis_cost, // out
0
+ const optional<amount_t>& total_cost_,
0
+ const optional<amount_t>& per_unit_cost_,
0
+ const optional<datetime_t>& moment,
0
+ const optional<string>& tag)
0
+{
0
+ // (assert (or (and total-cost (not per-unit-cost))
0
+ // (and per-unit-cost (not total-cost))))
0
+
0
+ assert((total_cost_ && ! per_unit_cost_) || (per_unit_cost_ && ! total_cost_));
0
+
0
+ // (let* ((commodity (amount-commodity amount))
0
+ // (current-annotation
0
+ // (and (annotated-commodity-p commodity)
0
+ // (commodity-annotation commodity)))
0
+ // (base-commodity (if (annotated-commodity-p commodity)
0
+ // (get-referent commodity)
0
+ // commodity))
0
+ // (per-unit-cost (or per-unit-cost
0
+ // (divide total-cost amount)))
0
+ // (total-cost (or total-cost
0
+ // (multiply per-unit-cost amount))))
0
+
0
+ commodity_t& commodity(amount.commodity());
0
+
0
+ annotation_t * current_annotation = NULL;
0
+ if (commodity.annotated)
0
+ current_annotation = &as_annotated_commodity(commodity).details;
0
+
0
+ commodity_t& base_commodity
0
+ (current_annotation ?
0
+ as_annotated_commodity(commodity).referent() : commodity);
0
+
0
+ amount_t per_unit_cost(per_unit_cost_ ?
0
+ *per_unit_cost_ : *total_cost_ / amount);
0
+ final_cost = total_cost_ ? *total_cost_ : *per_unit_cost_ * amount;
0
+
0
+ // Add a price history entry for this conversion if we know when it took
0
+ // place
0
+
0
+ // (if (and moment (not (commodity-no-market-price-p base-commodity)))
0
+ // (add-price base-commodity per-unit-cost moment))
0
+
0
+ if (moment && ! commodity.has_flags(COMMODITY_STYLE_NOMARKET))
0
+ base_commodity.add_price(*moment, per_unit_cost);
0
+
0
+ // ;; returns: ANNOTATED-AMOUNT TOTAL-COST BASIS-COST
0
+ // (values (annotate-commodity
0
+ // amount
0
+ // (make-commodity-annotation :price per-unit-cost
0
+ // :date moment
0
+ // :tag tag))
0
+ // total-cost
0
+ // (if current-annotation
0
+ // (multiply (annotation-price current-annotation) amount)
0
+ // total-cost))))
0
+
0
+ if (current_annotation && current_annotation->price)
0
+ basis_cost = *current_annotation->price * amount;
0
+ else
0
+ basis_cost = final_cost;
0
+
0
+ amount_t ann_amount(amount);
0
+ ann_amount.annotate_commodity(annotation_t(per_unit_cost, moment, tag));
0
+ return ann_amount;
0
+}
0
+
0
 commodity_t::operator bool() const
0
 {
0
   return this != parent().null_commodity;
...
117
118
119
 
 
 
 
120
121
122
...
185
186
187
 
 
 
 
 
 
 
 
188
189
190
...
117
118
119
120
121
122
123
124
125
126
...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
0
@@ -117,6 +117,10 @@ public:
0
 
0
   operator bool() const;
0
 
0
+ bool is_annotated() const {
0
+ return annotated;
0
+ }
0
+
0
   virtual bool operator==(const commodity_t& comm) const {
0
     if (comm.annotated)
0
       return comm == *this;
0
@@ -185,6 +189,14 @@ public:
0
 
0
   optional<amount_t> value(const optional<datetime_t>& moment = none);
0
 
0
+ static amount_t exchange(const amount_t& amount,
0
+ amount_t& final_cost, // out
0
+ amount_t& basis_cost, // out
0
+ const optional<amount_t>& total_cost,
0
+ const optional<amount_t>& per_unit_cost = none,
0
+ const optional<datetime_t>& moment = none,
0
+ const optional<string>& tag = none);
0
+
0
   static void parse_symbol(std::istream& in, string& symbol);
0
   static void parse_symbol(char *& p, string& symbol);
0
   static string parse_symbol(std::istream& in) {
...
118
119
120
121
122
123
124
125
126
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
129
130
131
132
133
 
134
135
136
 
137
138
139
 
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
 
 
 
 
 
155
156
157
158
159
160
161
162
163
 
164
165
166
 
 
 
 
 
 
 
 
 
 
 
 
 
167
168
169
170
171
172
173
174
 
 
 
 
 
175
176
177
178
179
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
221
222
223
224
225
 
 
226
227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
279
 
 
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
 
 
 
 
 
 
 
 
 
 
 
 
296
297
298
299
300
301
302
...
118
119
120
 
 
 
 
 
 
 
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
 
 
147
148
149
 
150
151
 
 
152
153
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
156
157
158
159
160
161
162
 
163
164
 
 
 
165
166
 
 
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
 
 
 
 
 
 
182
183
184
185
186
187
188
 
 
 
 
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
 
257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
 
292
 
 
293
294
295
 
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
 
343
344
345
 
 
 
 
 
 
 
 
 
 
 
346
347
348
 
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
 
364
365
366
0
@@ -118,185 +118,249 @@ bool entry_base_t::remove_transaction(transaction_t * xact)
0
 
0
 bool entry_base_t::finalize()
0
 {
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
-
0
- value_t balance;
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
+
0
+ // (let ((balance 0)
0
+ // null-xact)
0
+
0
+ value_t balance;
0
+ transaction_t * null_xact = NULL;
0
+
0
+ // (do-transactions (xact entry)
0
+ // (when (xact-must-balance-p xact)
0
+ // (let ((amt (xact-amount* xact)))
0
+ // (if amt
0
+ // (setf balance (add balance (or (xact-cost xact) amt)))
0
+ // (if null-xact
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
+ //
0
 
0
   for (transactions_list::const_iterator x = transactions.begin();
0
        x != transactions.end();
0
        x++) {
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 (! p.is_null()) {
0
- if (no_amounts) {
0
+ if (balance.is_null())
0
    balance = p;
0
- no_amounts = false;
0
- } else {
0
+ else
0
    balance += p;
0
- }
0
-
0
- assert(! (*x)->amount.is_null());
0
-
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
- *((*x)->cost));
0
- }
0
       } else {
0
- saw_null = true;
0
+ if (null_xact)
0
+ throw_(std::logic_error,
0
+ "Only one transaction with null amount allowed per entry");
0
+ else
0
+ null_xact = *x;
0
       }
0
     }
0
   }
0
-
0
   assert(balance.valid());
0
 
0
- // If it's a null entry, then let the user have their fun
0
- if (no_amounts)
0
- return true;
0
+ DEBUG("ledger.journal.finalize", "initial balance = " << balance);
0
 
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
+ // one has been set.
0
+
0
+ // (when (= 1 (length (entry-transactions entry)))
0
+ // (if-let ((default-account
0
+ // (journal-default-account (entry-journal entry))))
0
+ // (setf null-xact
0
+ // (make-transaction :entry entry
0
+ // :status (xact-status
0
+ // (first (entry-transactions entry)))
0
+ // :account default-account
0
+ // :generatedp t))
0
+ // (add-transaction entry null-xact)))
0
 
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
   }
0
 
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
+
0
+ // (progn
0
+ // (if (balance-p balance)
0
+ // (let ((first t))
0
+ // (dolist (amount (balance-amounts balance))
0
+ // (if first
0
+ // (setf (xact-amount* null-xact) (negate amount)
0
+ // first nil)
0
+ // (add-transaction
0
+ // entry
0
+ // (make-transaction :entry entry
0
+ // :account (xact-account null-xact)
0
+ // :amount (negate amount)
0
+ // :generatedp t)))))
0
+ // (setf (xact-amount* null-xact) (negate balance)
0
+ // (xact-calculatedp null-xact) t))
0
+ //
0
+ // (setf balance 0))
0
+
0
+ if (balance.is_balance()) {
0
+ bool first = true;
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
+ i++) {
0
+ if (first) {
0
+ null_xact->amount = (*i).second.negate();
0
+ first = false;
0
+ } else {
0
+ add_transaction(new transaction_t(null_xact->account,
0
+ (*i).second.negate(),
0
+ TRANSACTION_GENERATED));
0
+ }
0
+ }
0
+ } else {
0
+ null_xact->amount = balance.as_amount().negate();
0
+ null_xact->add_flags(TRANSACTION_CALCULATED);
0
+ }
0
+ balance = NULL_VALUE;
0
+
0
+ }
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
+ // commodities.
0
+
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
+ // a-commodity))
0
+ // (setf balance (subtract balance amount)
0
+ // (xact-cost xact) (multiply per-unit-cost amount)
0
+ // balance (add balance (xact-cost xact))))))))))
0
 
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
-
0
- balance_t::amounts_map::const_iterator this_bal =
0
- bal.amounts.find(&this_comm);
0
- assert(this_bal != bal.amounts.end());
0
-
0
- balance_t::amounts_map::const_iterator other_bal =
0
- bal.amounts.begin();
0
-
0
- if (this_bal == other_bal)
0
- other_bal++;
0
-
0
- amount_t per_unit_cost =
0
- ((*other_bal).second / (*this_bal).second.number()).unround();
0
-
0
- for (; x != transactions.end(); x++) {
0
- if ((*x)->cost || (*x)->has_flags(TRANSACTION_VIRTUAL) ||
0
- (*x)->amount.commodity() != this_comm)
0
- continue;
0
-
0
- balance -= (*x)->amount;
0
-
0
- entry_t * entry = dynamic_cast<entry_t *>(this);
0
-
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
-
0
- (*x)->cost = - (per_unit_cost * (*x)->amount.number());
0
- balance += *(*x)->cost;
0
+
0
+ balance_t::amounts_map::const_iterator a = bal.amounts.begin();
0
+
0
+ const amount_t& x((*a++).second);
0
+ const amount_t& y((*a++).second);
0
+
0
+ if (! y.is_realzero()) {
0
+ amount_t per_unit_cost = (x / y).abs();
0
+
0
+ commodity_t& comm(x.commodity());
0
+
0
+ for (transactions_list::const_iterator x = transactions.begin();
0
+ x != transactions.end();
0
+ x++) {
0
+ const amount_t& x_amt((*x)->amount);
0
+
0
+ if (! ((*x)->cost ||
0
+ ! (*x)->must_balance() ||
0
+ x_amt.commodity() == comm)) {
0
+ DEBUG("ledger.journal.finalize", "before operation 1 = " << balance);
0
+ balance -= x_amt;
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
+
0
+ (*x)->cost = per_unit_cost * x_amt;
0
+ DEBUG("ledger.journal.finalize", "*(*x)->cost = " << *(*x)->cost);
0
+
0
+ balance += *(*x)->cost;
0
+ DEBUG("ledger.journal.finalize", "after operation 2 = " << balance);
0
+ }
0
+
0
       }
0
     }
0
- }
0
 
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
+ }
0
 
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
+ // amounts.
0
+
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
+ // (setf balance
0
+ // (add balance (subtract basis-cost total-cost))))
0
+ // (setf (xact-amount* xact) annotated-amount))))))
0
 
0
   for (transactions_list::const_iterator x = transactions.begin();
0
        x != transactions.end();
0
        x++) {
0
- if (! (*x)->amount.is_null() ||
0
- ((*x)->has_flags(TRANSACTION_VIRTUAL) &&
0
- ! (*x)->has_flags(TRANSACTION_BALANCE)))
0
- continue;
0
-
0
- if (! empty_allowed)
0
- throw_(std::logic_error,
0
- "Only one transaction with null amount allowed per entry");
0
- empty_allowed = false;
0
-
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
-
0
- const balance_t * bal = NULL;
0
- switch (balance.type()) {
0
- case value_t::BALANCE_PAIR:
0
- bal = &balance.as_balance_pair().quantity();
0
- // fall through...
0
-
0
- case value_t::BALANCE:
0
- if (! bal)
0
- bal = &balance.as_balance();
0
-
0
- if (bal->amounts.size() < 2) {
0
- balance.cast(value_t::AMOUNT);
0
- } else {
0
- bool first = true;
0
- for (balance_t::amounts_map::const_iterator
0
- i = bal->amounts.begin();
0
- i != bal->amounts.end();
0
- i++) {
0
- amount_t amt = (*i).second.negate();
0
-
0
- if (first) {
0
- (*x)->amount = amt;
0
- first = false;
0
- } else {
0
- transaction_t * nxact = new transaction_t((*x)->account);
0
- add_transaction(nxact);
0
- nxact->add_flags(TRANSACTION_CALCULATED);
0
- nxact->amount = amt;
0
- }
0
-
0
- balance += amt;
0
+ if ((*x)->cost) {
0
+ const amount_t& x_amt((*x)->amount);
0
+
0
+ assert(x_amt.commodity() != (*x)->cost->commodity());
0
+
0
+ entry_t * entry = dynamic_cast<entry_t *>(this);
0
+
0
+ // jww (2008-07-24): Pass the entry's code here if we can, as the
0
+ // auto-tag
0
+ amount_t final_cost;
0
+ amount_t basis_cost;
0
+ amount_t ann_amount =
0
+ commodity_t::exchange(x_amt, final_cost, basis_cost,
0
+ (*x)->cost, none, (*x)->actual_date(),
0
+ entry ? entry->code : optional<string>());
0
+
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
+ else
0
+ balance += basis_cost - final_cost;
0
   }
0
- break;
0
+ } else {
0
+ (*x)->amount = ann_amount;
0
       }
0
- // fall through...
0
-
0
- case value_t::AMOUNT:
0
- (*x)->amount = balance.as_amount().negate();
0
- (*x)->add_flags(TRANSACTION_CALCULATED);
0
-
0
- balance += (*x)->amount;
0
- break;
0
-
0
- default:
0
- break;
0
     }
0
   }
0
 
0
- if (balance) {
0
+ DEBUG("ledger.journal.finalize", "final balance = " << balance);
0
+
0
+ // (if (value-zerop balance)
0
+ // (prog1
0
+ // entry
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
+
0
+ if (! balance.is_null() && ! balance.is_zero()) {
0
     error * err =
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
     balance.round();
0
     err->context.push_front
0
       (new value_context(balance, "Unbalanced remainder is:"));
...
46
47
48
 
49
50
51
...
73
74
75
76
77
 
 
 
78
79
80
...
120
121
122
 
 
 
 
123
124
125
...
46
47
48
49
50
51
52
...
74
75
76
 
 
77
78
79
80
81
82
...
122
123
124
125
126
127
128
129
130
131
0
@@ -46,6 +46,7 @@ namespace ledger {
0
 #define TRANSACTION_AUTO 0x0004
0
 #define TRANSACTION_BULK_ALLOC 0x0008
0
 #define TRANSACTION_CALCULATED 0x0010
0
+#define TRANSACTION_GENERATED 0x0020
0
 
0
 class entry_t;
0
 class account_t;
0
@@ -73,8 +74,9 @@ class transaction_t : public supports_flags<>
0
   mutable void * data;
0
   static bool   use_effective_date;
0
 
0
- transaction_t(account_t * _account = NULL)
0
- : supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
0
+ transaction_t(account_t * _account = NULL,
0
+ unsigned int _flags = TRANSACTION_NORMAL)
0
+ : supports_flags<>(_flags), entry(NULL),
0
       state(UNCLEARED), account(_account),
0
       beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
0
   {
0
@@ -120,6 +122,10 @@ class transaction_t : public supports_flags<>
0
       return actual_date();
0
   }
0
 
0
+ bool must_balance() const {
0
+ return ! has_flags(TRANSACTION_VIRTUAL) || has_flags(TRANSACTION_BALANCE);
0
+ }
0
+
0
   bool valid() const;
0
 };
0
 
...
205
206
207
208
 
 
209
210
211
212
213
 
 
214
215
216
...
205
206
207
 
208
209
210
211
212
213
214
215
216
217
218
219
0
@@ -205,12 +205,15 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
0
 
0
   journal_t& journal(*session.create_journal());
0
 
0
- if (! session.read_data(journal, report.account))
0
+ std::size_t count = session.read_data(journal, report.account);
0
+ if (count == 0)
0
     throw_(parse_error, "Failed to locate any journal entries; "
0
    "did you specify a valid file with -f?");
0
 
0
   INFO_FINISH(journal);
0
 
0
+ INFO("Found " << count << " entries");
0
+
0
   TRACE_FINISH(entry_text, 1);
0
   TRACE_FINISH(entry_date, 1);
0
   TRACE_FINISH(entry_details, 1);
...
73
74
75
76
 
77
78
79
...
73
74
75
 
76
77
78
79
0
@@ -73,7 +73,7 @@ value_t report_t::abbrev(expr::call_scope_t& args)
0
   return value_t(abbreviate(str, wid, style, true,
0
        static_cast<int>(abbrev_len)), true);
0
 #else
0
- return value_t();
0
+ return NULL_VALUE;
0
 #endif
0
 }
0
 
...
87
88
89
90
 
91
92
93
...
135
136
137
138
 
139
140
141
...
87
88
89
 
90
91
92
93
...
135
136
137
 
138
139
140
141
0
@@ -87,7 +87,7 @@ value_t scope_t::resolve(const string& name) {
0
   if (definition)
0
     return definition->calc(*this);
0
   else
0
- return value_t();
0
+ return NULL_VALUE;
0
 }
0
 
0
 void symbol_scope_t::define(const string& name, ptr_op_t def)
0
@@ -135,7 +135,7 @@ namespace {
0
   temp.reset(expr);
0
       } else {
0
   temp.reset(new op_t(op_t::VALUE));
0
- temp->set_value(value_t());
0
+ temp->set_value(NULL_VALUE);
0
   expr->compute(temp->as_value_lval(), details, context);
0
       }
0
     } else {