Loading...
Searching...
No Matches
report.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2003-2025, John Wiegley. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of New Artisans LLC nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
35
42#pragma once
43
44#include "expr.h"
45#include "query.h"
46#include "chain.h"
47#include "stream.h"
48#include "option.h"
49#include "commodity.h"
50#include "annotate.h"
51#include "session.h"
52#include "format.h"
53
54namespace ledger {
55
56class session_t;
57class xact_t;
58
59// These are the elements of any report:
60//
61// 1. Formatting string used for outputting the underlying ReportedType.
62//
63// 2. Handler object for the ReportedType. This is constructed using #1, or
64// else #1 is ignored completely. This handler object is also constructed
65// with the output stream that will be used during formatting.
66//
67// --- The details of #1 and #2 together represent the ItemHandler.
68//
69// 3. Mode of the report. Currently there are four modes:
70//
71// a. Posting or commodity iteration. In this mode, all the journal's
72// xacts, the postings of a specific xact, or all the journal's
73// commodities are walked. In the first two cases, it's the underlying
74// postings which are passed to #2; in the second case, each
75// commodity is passed to #2.
76//
77// b. Account iteration. This employs step 'a', but add a prologue and
78// epilogue to it. In the prologue it "sums" all account totals and
79// subtotals; in the epilogue it calls yet another handler whose job is
80// reporting (the handler used in 'a' is only for calculation).
81//
82// There is one variation on 'b' in which a "totals" line is also
83// displayed.
84//
85// c. Write journal. In this mode, a single function is called that output
86// the journal object as a textual file. #2 is used to print out each
87// posting in the journal.
88//
89// d. Dump binary file. This is just like 'c', except that it dumps out a
90// binary file and #2 is completely ignored.
91//
92// 4. For 'a' and 'b' in #3, there is a different iteration function called,
93// depending on whether we're iterating:
94//
95// a. The postings of an xact: walk_postings.
96// b. The xacts of a journal: walk_xacts.
97// c. The commodities of a journal: walk_commodities.
98//
99// 5. Finally, for the 'a' and 'b' reporting modes, there is a variant which
100// says that the formatter should be "flushed" after the entities are
101// iterated. This does not happen for the commodities iteration, however.
102
103class report_t : public scope_t
104{
105 report_t();
106
107public:
110
111#define BUDGET_NO_BUDGET 0x00
112#define BUDGET_BUDGETED 0x01
113#define BUDGET_UNBUDGETED 0x02
114#define BUDGET_WRAP_VALUES 0x04
115
117 uint_least8_t budget_flags;
118
119 explicit report_t(session_t& _session)
120 : session(_session), terminus(CURRENT_TIME()),
122 TRACE_CTOR(report_t, "session_t&");
123 }
124 report_t(const report_t& report)
125 : scope_t(report), session(report.session),
127 terminus(report.terminus),
128 budget_flags(report.budget_flags) {
129 TRACE_CTOR(report_t, "copy");
130 }
131
132 virtual ~report_t() {
133 TRACE_DTOR(report_t);
134 output_stream.close();
135 }
136
137 void quick_close() {
138 output_stream.close();
139 }
140
141 virtual string description() {
142 return _("current report");
143 }
144
145 void normalize_options(const string& verb);
147 void parse_query_args(const value_t& args, const string& whence);
148
151 void xact_report(post_handler_ptr handler, xact_t& xact);
154
156
207
209 return terminus;
210 }
212 return terminus.date();
213 }
214
216 return scope_value(this);
217 }
218
220 if (HANDLED(format_))
221 return HANDLER(format_).str();
222 return option.str();
223 }
224
225 optional<string> maybe_format(option_t<report_t>& option) {
226 if (option)
227 return option.str();
228 return none;
229 }
230
234
236 bool lots = HANDLED(lots) || HANDLED(lots_actual);
237 return keep_details_t(lots || HANDLED(lot_prices),
238 lots || HANDLED(lot_dates),
239 lots || HANDLED(lot_notes),
240 HANDLED(lots_actual));
241 }
242
243 void report_options(std::ostream& out)
244 {
245 HANDLER(abbrev_len_).report(out);
246 HANDLER(account_).report(out);
247 HANDLER(actual).report(out);
248 HANDLER(add_budget).report(out);
249 HANDLER(align_intervals).report(out);
250 HANDLER(amount_).report(out);
251 HANDLER(amount_data).report(out);
252 HANDLER(anon).report(out);
253 HANDLER(auto_match).report(out);
254 HANDLER(aux_date).report(out);
255 HANDLER(average).report(out);
256 HANDLER(balance_format_).report(out);
257 HANDLER(base).report(out);
258 HANDLER(basis).report(out);
259 HANDLER(begin_).report(out);
260 HANDLER(budget).report(out);
261 HANDLER(budget_format_).report(out);
262 HANDLER(by_payee).report(out);
263 HANDLER(cleared).report(out);
264 HANDLER(cleared_format_).report(out);
265 HANDLER(color).report(out);
266 HANDLER(collapse).report(out);
267 HANDLER(collapse_if_zero).report(out);
268 HANDLER(columns_).report(out);
269 HANDLER(csv_format_).report(out);
270 HANDLER(current).report(out);
271 HANDLER(daily).report(out);
272 HANDLER(date_).report(out);
273 HANDLER(date_format_).report(out);
274 HANDLER(datetime_format_).report(out);
275 HANDLER(dc).report(out);
276 HANDLER(depth_).report(out);
277 HANDLER(deviation).report(out);
278 HANDLER(display_).report(out);
279 HANDLER(display_amount_).report(out);
280 HANDLER(display_total_).report(out);
281 HANDLER(dow).report(out);
282 HANDLER(empty).report(out);
283 HANDLER(end_).report(out);
284 HANDLER(equity).report(out);
285 HANDLER(exact).report(out);
286 HANDLER(exchange_).report(out);
287 HANDLER(flat).report(out);
288 HANDLER(force_color).report(out);
289 HANDLER(force_pager).report(out);
290 HANDLER(forecast_while_).report(out);
291 HANDLER(forecast_years_).report(out);
292 HANDLER(format_).report(out);
293 HANDLER(gain).report(out);
294 HANDLER(generated).report(out);
295 HANDLER(group_by_).report(out);
296 HANDLER(group_title_format_).report(out);
297 HANDLER(head_).report(out);
298 HANDLER(immediate).report(out);
299 HANDLER(inject_).report(out);
300 HANDLER(invert).report(out);
301 HANDLER(limit_).report(out);
302 HANDLER(lisp_date_format_).report(out);
303 HANDLER(lot_dates).report(out);
304 HANDLER(lot_prices).report(out);
305 HANDLER(average_lot_prices).report(out);
306 HANDLER(lot_notes).report(out);
307 HANDLER(lots).report(out);
308 HANDLER(lots_actual).report(out);
309 HANDLER(market).report(out);
310 HANDLER(meta_).report(out);
311 HANDLER(monthly).report(out);
312 HANDLER(no_pager).report(out);
313 HANDLER(no_rounding).report(out);
314 HANDLER(no_titles).report(out);
315 HANDLER(no_total).report(out);
316 HANDLER(now_).report(out);
317 HANDLER(only_).report(out);
318 HANDLER(output_).report(out);
319 HANDLER(pager_).report(out);
320 HANDLER(payee_).report(out);
321 HANDLER(pending).report(out);
322 HANDLER(percent).report(out);
323 HANDLER(period_).report(out);
324 HANDLER(pivot_).report(out);
325 HANDLER(plot_amount_format_).report(out);
326 HANDLER(plot_total_format_).report(out);
327 HANDLER(prepend_format_).report(out);
328 HANDLER(prepend_width_).report(out);
329 HANDLER(price).report(out);
330 HANDLER(prices_format_).report(out);
331 HANDLER(pricedb_format_).report(out);
332 HANDLER(primary_date).report(out);
333 HANDLER(quantity).report(out);
334 HANDLER(quarterly).report(out);
335 HANDLER(raw).report(out);
336 HANDLER(real).report(out);
337 HANDLER(register_format_).report(out);
338 HANDLER(related).report(out);
339 HANDLER(related_all).report(out);
340 HANDLER(revalued).report(out);
341 HANDLER(revalued_only).report(out);
342 HANDLER(revalued_total_).report(out);
343 HANDLER(rich_data).report(out);
344 HANDLER(seed_).report(out);
345 HANDLER(sort_).report(out);
346 HANDLER(sort_all_).report(out);
347 HANDLER(sort_xacts_).report(out);
348 HANDLER(start_of_week_).report(out);
349 HANDLER(subtotal).report(out);
350 HANDLER(tail_).report(out);
351 HANDLER(time_report).report(out);
352 HANDLER(total_).report(out);
353 HANDLER(total_data).report(out);
354 HANDLER(truncate_).report(out);
355 HANDLER(unbudgeted).report(out);
356 HANDLER(uncleared).report(out);
357 HANDLER(unrealized).report(out);
358 HANDLER(unrealized_gains_).report(out);
359 HANDLER(unrealized_losses_).report(out);
360 HANDLER(unround).report(out);
361 HANDLER(weekly).report(out);
362 HANDLER(wide).report(out);
363 HANDLER(yearly).report(out);
364 HANDLER(meta_width_).report(out);
365 HANDLER(date_width_).report(out);
366 HANDLER(payee_width_).report(out);
367 HANDLER(account_width_).report(out);
368 HANDLER(amount_width_).report(out);
369 HANDLER(total_width_).report(out);
370 HANDLER(values).report(out);
371 }
372
374
375 virtual void define(const symbol_t::kind_t kind, const string& name,
376 expr_t::ptr_op_t def);
377
379 const string& name);
380
384
386 (report_t, abbrev_len_,
387 CTOR(report_t, abbrev_len_) {
388 on(none, "2");
389 });
390
391 OPTION(report_t, account_);
392
393 OPTION_(report_t, actual, DO() { // -L
394 OTHER(limit_).on(whence, "actual");
395 });
396
397 OPTION_(report_t, add_budget, DO() {
398 parent->budget_flags |= BUDGET_BUDGETED | BUDGET_UNBUDGETED;
399 });
400
401 OPTION(report_t, align_intervals);
402
404 (report_t, amount_, // -t
405 DECL1(report_t, amount_, merged_expr_t, expr, ("amount_expr", "amount")) {}
406 DO_(str) {
407 expr.append(str);
408 });
409
410 OPTION(report_t, amount_data); // -j
411 OPTION(report_t, anon);
412 OPTION(report_t, auto_match);
413
414 OPTION_(report_t, average, DO() { // -A
415 OTHER(empty).on(whence);
416 OTHER(display_total_)
417 .on(whence, "count>0?(display_total/count):0");
418 });
419
421 (report_t, balance_format_,
422 CTOR(report_t, balance_format_) {
423 on(none,
424 "%(ansify_if("
425 " justify(scrub(display_total), max(int(amount_width),20),"
426 " max(int(amount_width),20) + int(prepend_width), true, color),"
427 " bold if should_bold))"
428 " %(!options.flat ? depth_spacer : \"\")"
429 "%-(ansify_if("
430 " ansify_if(partial_account(options.flat), blue if color),"
431 " bold if should_bold))\n%/"
432 "%$1\n%/"
433 "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
434 "%(\"-\" * max(int(amount_width),20))\n");
435 });
436
437 OPTION(report_t, base);
438
439 OPTION_(report_t, basis, DO() { // -B
440 OTHER(revalued).on(whence);
441 OTHER(amount_).expr.set_base_expr("rounded(cost)");
442 });
443
444 OPTION_(report_t, begin_, DO_(str) { // -b
445 date_interval_t interval(str);
446 if (optional<date_t> begin = interval.begin()) {
447 string predicate = "date>=[" + to_iso_extended_string(*begin) + "]";
448 OTHER(limit_).on(whence, predicate);
449 } else {
450 throw_(std::invalid_argument,
451 _f("Could not determine beginning of period '%1%'") % str);
452 }
453 });
454
455 OPTION_
456 (report_t, bold_if_,
457 expr_t expr;
458 DO_(str) {
459 expr = str;
460 });
461
462 OPTION_(report_t, budget, DO() {
463 parent->budget_flags |= BUDGET_BUDGETED;
464 });
465
467 (report_t, budget_format_,
468 CTOR(report_t, budget_format_) {
469 on(none,
470 "%(justify(scrub(get_at(display_total, 0)), int(amount_width), -1, true, color))"
471 " %(justify(-scrub(get_at(display_total, 1)), int(amount_width), "
472 " int(amount_width) + 1 + int(amount_width), true, color))"
473 " %(justify(scrub((get_at(display_total, 1) || 0) + "
474 " (get_at(display_total, 0) || 0)), int(amount_width), "
475 " int(amount_width) + 1 + int(amount_width) + 1 + int(amount_width), true, color))"
476 " %(ansify_if("
477 " justify((get_at(display_total, 1) ? "
478 " (100% * quantity(scrub(get_at(display_total, 0)))) / "
479 " -quantity(scrub(get_at(display_total, 1))) : 0), "
480 " 5, -1, true, false),"
481 " magenta if (color and get_at(display_total, 1) and "
482 " (abs(quantity(scrub(get_at(display_total, 0))) / "
483 " quantity(scrub(get_at(display_total, 1)))) >= 1))))"
484 " %(!options.flat ? depth_spacer : \"\")"
485 "%-(ansify_if(partial_account(options.flat), blue if color))\n"
486 "%/%$1 %$2 %$3 %$4\n%/"
487 "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
488 "------------ ------------ ------------ -----\n");
489 });
490
491 OPTION(report_t, by_payee); // -P
492
493 OPTION_(report_t, cleared, DO() { // -C
494 OTHER(limit_).on(whence, "cleared");
495 });
496
498 (report_t, cleared_format_,
499 CTOR(report_t, cleared_format_) {
500 on(none,
501 "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), "
502 " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, "
503 " 36 + int(prepend_width), true, color))"
504 " %(latest_cleared ? format_date(latest_cleared) : \" \")"
505 " %(!options.flat ? depth_spacer : \"\")"
506 "%-(ansify_if(partial_account(options.flat), blue if color))\n%/"
507 "%$1 %$2 %$3\n%/"
508 "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
509 "---------------- ---------------- ---------\n");
510 });
511
512 OPTION(report_t, color);
513
514 OPTION_(report_t, collapse, DO() { // -n
515 // Make sure that balance reports are collapsed too, but only apply it
516 // to account xacts
517 OTHER(display_).on(whence, "post|depth<=1");
518 });
519
520 OPTION_(report_t, collapse_if_zero, DO() {
521 OTHER(collapse).on(whence);
522 });
523
524 OPTION(report_t, columns_);
525 OPTION(report_t, count);
526
528 (report_t, csv_format_,
529 CTOR(report_t, csv_format_) {
530 on(none,
531 "%(quoted(date)),"
532 "%(quoted(code)),"
533 "%(quoted(payee)),"
534 "%(quoted(display_account)),"
535 "%(quoted(commodity(scrub(display_amount)))),"
536 "%(quoted(quantity(scrub(display_amount)))),"
537 "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\"))),"
538 "%(quoted(join(note | xact.note)))\n");
539 });
540
541 OPTION_(report_t, current, DO() { // -c
542 OTHER(limit_).on(whence, "date<=today");
543 });
544
545 OPTION_(report_t, daily, DO() { // -D
546 OTHER(period_).on(whence, "daily");
547 });
548
549 OPTION(report_t, date_);
550 OPTION(report_t, date_format_);
551 OPTION(report_t, datetime_format_);
552 OPTION(report_t, lisp_date_format_);
553
554 OPTION_(report_t, dc, DO() {
555 OTHER(amount_).expr.set_base_expr
556 ("(amount > 0 ? amount : 0, amount < 0 ? amount : 0)");
557
558 OTHER(register_format_)
559 .on(none,
560 "%(ansify_if("
561 " ansify_if(justify(format_date(date), int(date_width)),"
562 " green if color and date > today),"
563 " bold if should_bold))"
564 " %(ansify_if("
565 " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), "
566 " bold if color and !cleared and actual),"
567 " bold if should_bold))"
568 " %(ansify_if("
569 " ansify_if(justify(truncated(display_account, int(account_width), "
570 " int(abbrev_len)), int(account_width)),"
571 " blue if color),"
572 " bold if should_bold))"
573 " %(ansify_if("
574 " justify(scrub(abs(get_at(display_amount, 0))), int(amount_width), "
575 " 3 + int(meta_width) + int(date_width) + int(payee_width)"
576 " + int(account_width) + int(amount_width) + int(prepend_width),"
577 " true, color),"
578 " bold if should_bold))"
579 " %(ansify_if("
580 " justify(scrub(abs(get_at(display_amount, 1))), int(amount_width), "
581 " 4 + int(meta_width) + int(date_width) + int(payee_width)"
582 " + int(account_width) + int(amount_width) + int(amount_width) + int(prepend_width),"
583 " true, color),"
584 " bold if should_bold))"
585 " %(ansify_if("
586 " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), int(total_width), "
587 " 5 + int(meta_width) + int(date_width) + int(payee_width)"
588 " + int(account_width) + int(amount_width) + int(amount_width) + int(total_width)"
589 " + int(prepend_width), true, color),"
590 " bold if should_bold))\n%/"
591 "%(justify(\" \", int(date_width)))"
592 " %(ansify_if("
593 " justify(truncated(has_tag(\"Payee\") ? payee : \" \", "
594 " int(payee_width)), int(payee_width)),"
595 " bold if should_bold))"
596 " %$3 %$4 %$5 %$6\n");
597
598 OTHER(balance_format_)
599 .on(none,
600 "%(ansify_if("
601 " justify(scrub(abs(get_at(display_total, 0))), 14,"
602 " 14 + int(prepend_width), true, color),"
603 " bold if should_bold)) "
604 "%(ansify_if("
605 " justify(scrub(abs(get_at(display_total, 1))), 14,"
606 " 14 + 1 + int(prepend_width) + int(total_width), true, color),"
607 " bold if should_bold)) "
608 "%(ansify_if("
609 " justify(scrub(get_at(display_total, 0) + get_at(display_total, 1)), 14,"
610 " 14 + 2 + int(prepend_width) + int(total_width) + int(total_width), true, color),"
611 " bold if should_bold))"
612 " %(!options.flat ? depth_spacer : \"\")"
613 "%-(ansify_if("
614 " ansify_if(partial_account(options.flat), blue if color),"
615 " bold if should_bold))\n%/"
616 "%$1 %$2 %$3\n%/"
617 "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
618 "--------------------------------------------\n");
619 });
620
621 OPTION_(report_t, depth_, DO_(str) {
622 OTHER(display_).on(whence, string("depth<=") + str);
623 });
624
625 OPTION_(report_t, deviation, DO() {
626 OTHER(display_total_)
627 .on(whence, "display_amount-display_total");
628 });
629
630 OPTION_
631 (report_t, display_,
632 DO_(str) { // -d
633 if (handled)
634 value = string("(") + value + ")&(" + str + ")";
635 });
636
638 (report_t, display_amount_,
639 DECL1(report_t, display_amount_, merged_expr_t, expr,
640 ("display_amount", "amount_expr")) {}
641 DO_(str) {
642 expr.append(str);
643 });
644
646 (report_t, display_total_,
647 DECL1(report_t, display_total_, merged_expr_t, expr,
648 ("display_total", "total_expr")) {}
649 DO_(str) {
650 expr.append(str);
651 });
652
653 OPTION(report_t, dow);
654 OPTION(report_t, aux_date);
655 OPTION(report_t, empty); // -E
656
657 OPTION_(report_t, end_, DO_(str) { // -e
658 // Use begin() here so that if the user says --end=2008, we end on
659 // 2008/01/01 instead of 2009/01/01 (which is what end() would
660 // return).
661 date_interval_t interval(str);
662 if (optional<date_t> end = interval.begin()) {
663 string predicate = "date<[" + to_iso_extended_string(*end) + "]";
664 OTHER(limit_).on(whence, predicate);
665
666 parent->terminus = datetime_t(*end);
667 } else {
668 throw_(std::invalid_argument,
669 _f("Could not determine end of period '%1%'")
670 % str);
671 }
672 });
673
674 OPTION(report_t, equity);
675 OPTION(report_t, exact);
676
677 OPTION_(report_t, exchange_, DO_(str) { // -X
678 // Using -X implies -V. The main difference is that now
679 // HANDLER(exchange_) contains the name of a commodity, which
680 // is accessed via the "exchange" value expression function.
681 OTHER(market).on(whence);
682 });
683
684 OPTION(report_t, flat);
685 OPTION(report_t, force_color);
686 OPTION(report_t, force_pager);
687 OPTION(report_t, forecast_while_);
688 OPTION(report_t, forecast_years_);
689 OPTION(report_t, format_); // -F
690
691 OPTION_(report_t, gain, DO() { // -G
692 OTHER(revalued).on(whence);
693 OTHER(amount_).expr.set_base_expr("(amount, cost)");
694
695 // Since we are displaying the amounts of revalued postings, they
696 // will end up being composite totals, and hence a pair of pairs.
697 OTHER(display_amount_)
698 .on(whence,
699 "use_direct_amount ? amount :"
700 " (is_seq(get_at(amount_expr, 0)) ?"
701 " get_at(get_at(amount_expr, 0), 0) :"
702 " market(get_at(amount_expr, 0), value_date, exchange)"
703 " - get_at(amount_expr, 1))");
704 OTHER(revalued_total_)
705 .on(whence,
706 "(market(get_at(total_expr, 0), value_date, exchange), "
707 "get_at(total_expr, 1))");
708 OTHER(display_total_)
709 .on(whence,
710 "use_direct_amount ? total_expr :"
711 " market(get_at(total_expr, 0), value_date, exchange)"
712 " - get_at(total_expr, 1)");
713 });
714
715 OPTION(report_t, generated);
716
717 OPTION_
718 (report_t, group_by_,
719 expr_t expr;
720 DO_(str) {
721 expr = str;
722 });
723
725 (report_t, group_title_format_,
726 CTOR(report_t, group_title_format_) {
727 on(none, "%(value)\n");
728 });
729
730 OPTION(report_t, head_);
731
732 OPTION_(report_t, historical, DO() { // -H
733 OTHER(market).on(whence);
734 OTHER(amount_)
735 .on(whence, "nail_down(amount_expr, "
736 "market(amount_expr, value_date, exchange))");
737 });
738
739 OPTION(report_t, immediate);
740 OPTION(report_t, inject_);
741
742 OPTION_(report_t, invert, DO() {
743 OTHER(amount_).on(whence, "-amount_expr");
744 });
745
746 OPTION_
747 (report_t, limit_,
748 DO_(str) { // -l
749 if (handled)
750 value = string("(") + value + ")&(" + str + ")";
751 });
752
753 OPTION(report_t, lot_dates);
754 OPTION(report_t, lot_prices);
755 OPTION_(report_t, average_lot_prices, DO() {
756 OTHER(lot_prices).on(whence);
757 OTHER(display_amount_)
758 .on(whence, "averaged_lots(display_amount)");
759 OTHER(display_total_)
760 .on(whence, "averaged_lots(display_total)");
761 });
762 OPTION(report_t, lot_notes);
763 OPTION(report_t, lots);
764 OPTION(report_t, lots_actual);
765
766 OPTION_(report_t, market, DO() { // -V
767 OTHER(revalued).on(whence);
768
769 OTHER(display_amount_)
770 .on(whence, "market(display_amount, value_date, exchange)");
771 OTHER(display_total_)
772 .on(whence, "market(display_total, value_date, exchange)");
773 });
774
775 OPTION(report_t, meta_);
776
777 OPTION_(report_t, monthly, DO() { // -M
778 OTHER(period_).on(whence, "monthly");
779 });
780
781 OPTION_(report_t, no_color, DO() {
782 OTHER(color).off();
783 });
784
785 OPTION_(report_t, no_revalued, DO() {
786 OTHER(revalued).off();
787 });
788
789 OPTION(report_t, no_rounding);
790 OPTION(report_t, no_titles);
791 OPTION(report_t, no_total);
792
793 OPTION_(report_t, now_, DO_(str) {
794 date_interval_t interval(str);
795 if (optional<date_t> begin = interval.begin()) {
796 ledger::epoch = parent->terminus = datetime_t(*begin);
797 } else {
798 throw_(std::invalid_argument,
799 _f("Could not determine beginning of period '%1%'")
800 % str);
801 }
802 });
803
804 OPTION_
805 (report_t, only_,
806 DO_(str) {
807 if (handled)
808 value = string("(") + value + ")&(" + str + ")";
809 });
810
811 OPTION(report_t, output_); // -o
812
813// setenv() is not available on WIN32
814#if HAVE_ISATTY and !defined(_WIN32) and !defined(__CYGWIN__)
816 (report_t, pager_,
817 CTOR(report_t, pager_) {
818 if (isatty(STDOUT_FILENO)) {
819 if (! std::getenv("PAGER")) {
820 bool have_less = false;
821 if (exists(path("/opt/local/bin/less")) ||
822 exists(path("/usr/local/bin/less")) ||
823 exists(path("/usr/bin/less")))
824 have_less = true;
825
826 if (have_less) {
827 on(none, "less");
828 setenv("LESS", "-FRSX", 0); // don't overwrite
829 }
830 } else {
831 on(none, std::getenv("PAGER"));
832 setenv("LESS", "-FRSX", 0); // don't overwrite
833 }
834 }
835 });
836#else // HAVE_ISATTY
837 OPTION(report_t, pager_);
838#endif // HAVE_ISATTY
839
840 OPTION_(report_t, no_pager, DO() {
841 OTHER(pager_).off();
842 });
843
844 OPTION(report_t, payee_);
845
846 OPTION_(report_t, pending, DO() { // -C
847 OTHER(limit_).on(whence, "pending");
848 });
849
850 OPTION_(report_t, percent, DO() { // -%
851 OTHER(total_)
852 .on(whence,
853 "((is_account&parent&parent.total)?"
854 " percent(scrub(total), scrub(parent.total)):0)");
855 });
856
857 OPTION_
858 (report_t, period_,
859 DO_(str) { // -p
860 if (handled)
861 value += string(" ") + str;
862 });
863
864 OPTION(report_t, pivot_);
865
867 (report_t, plot_amount_format_,
868 CTOR(report_t, plot_amount_format_) {
869 on(none,
870 "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n");
871 });
872
874 (report_t, plot_total_format_,
875 CTOR(report_t, plot_total_format_) {
876 on(none,
877 "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
878 });
879
880 OPTION(report_t, prepend_format_);
881 OPTION(report_t, prepend_width_);
882
883 OPTION_(report_t, price, DO() { // -I
884 OTHER(amount_).expr.set_base_expr("price");
885 });
886
888 (report_t, prices_format_,
889 CTOR(report_t, prices_format_) {
890 on(none,
891 "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, "
892 " 2 + 9 + 8 + 12, true, color))\n");
893 });
894
896 (report_t, pricedb_format_,
897 CTOR(report_t, pricedb_format_) {
898 on(none,
899 "P %(datetime) %(display_account) %(scrub(display_amount))\n");
900 });
901
902 OPTION(report_t, primary_date);
903
904 OPTION_(report_t, quantity, DO() { // -O
905 OTHER(revalued).off();
906
907 OTHER(amount_).expr.set_base_expr("amount");
908 OTHER(total_).expr.set_base_expr("total");
909 });
910
911 OPTION_(report_t, quarterly, DO() {
912 OTHER(period_).on(whence, "quarterly");
913 });
914
915 OPTION(report_t, raw);
916
917 OPTION_(report_t, real, DO() { // -R
918 OTHER(limit_).on(whence, "real");
919 });
920
922 (report_t, register_format_,
923 CTOR(report_t, register_format_) {
924 on(none,
925 "%(ansify_if("
926 " ansify_if(justify(format_date(date), int(date_width)),"
927 " green if color and date > today),"
928 " bold if should_bold))"
929 " %(ansify_if("
930 " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), "
931 " bold if color and !cleared and actual),"
932 " bold if should_bold))"
933 " %(ansify_if("
934 " ansify_if(justify(truncated(display_account, int(account_width), "
935 " int(abbrev_len)), int(account_width)),"
936 " blue if color),"
937 " bold if should_bold))"
938 " %(ansify_if("
939 " justify(scrub(display_amount), int(amount_width), "
940 " 3 + int(meta_width) + int(date_width) + int(payee_width)"
941 " + int(account_width) + int(amount_width) + int(prepend_width),"
942 " true, color),"
943 " bold if should_bold))"
944 " %(ansify_if("
945 " justify(scrub(display_total), int(total_width), "
946 " 4 + int(meta_width) + int(date_width) + int(payee_width)"
947 " + int(account_width) + int(amount_width) + int(total_width)"
948 " + int(prepend_width), true, color),"
949 " bold if should_bold))\n%/"
950 "%(justify(\" \", int(date_width)))"
951 " %(ansify_if("
952 " justify(truncated(has_tag(\"Payee\") ? payee : \" \", "
953 " int(payee_width)), int(payee_width)),"
954 " bold if should_bold))"
955 " %$3 %$4 %$5\n");
956 });
957
958 OPTION(report_t, related); // -r
959
960 OPTION_(report_t, related_all, DO() {
961 OTHER(related).on(whence);
962 });
963
964 OPTION(report_t, revalued);
965 OPTION(report_t, revalued_only);
966
967 OPTION_
968 (report_t, revalued_total_,
969 expr_t expr;
970 DO_(str) {
971 expr = str;
972 });
973
974 OPTION(report_t, rich_data);
975
976 OPTION(report_t, seed_);
977
978 OPTION_(report_t, sort_, DO_(str) { // -S
979 OTHER(sort_xacts_).off();
980 OTHER(sort_all_).off();
981 });
982
983 OPTION_(report_t, sort_all_, DO_(str) {
984 OTHER(sort_).on(whence, str);
985 OTHER(sort_xacts_).off();
986 });
987
988 OPTION_(report_t, sort_xacts_, DO_(str) {
989 OTHER(sort_).on(whence, str);
990 OTHER(sort_all_).off();
991 });
992
993 OPTION(report_t, start_of_week_);
994 OPTION(report_t, subtotal); // -s
995 OPTION(report_t, tail_);
996
997 OPTION_(report_t, time_report, DO() {
998 OTHER(balance_format_)
999 .on(none,
1000 "%(ansify_if(justify(earliest_checkin ? "
1001 " format_datetime(earliest_checkin) : \"\", 19, -1, true),"
1002 " bold if latest_checkout_cleared)) "
1003 "%(ansify_if(justify(latest_checkout ? "
1004 " format_datetime(latest_checkout) : \"\", 19, -1, true), "
1005 " bold if latest_checkout_cleared)) "
1006 "%(latest_checkout_cleared ? \"*\" : \" \") "
1007 "%(ansify_if("
1008 " justify(scrub(display_total), 8,"
1009 " 8 + 4 + 19 * 2, true, color), bold if should_bold))"
1010 " %(!options.flat ? depth_spacer : \"\")"
1011 "%-(ansify_if("
1012 " ansify_if(partial_account(options.flat), blue if color),"
1013 " bold if should_bold))\n%/"
1014 "%$1 %$2 %$3\n%/"
1015 "%(prepend_width ? \" \" * int(prepend_width) : \"\")"
1016 "--------------------------------------------------\n");
1017 });
1018
1019 OPTION__
1020 (report_t, total_, // -T
1021 DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {}
1022 DO_(str) {
1023 expr.append(str);
1024 });
1025
1026 OPTION(report_t, total_data); // -J
1027
1028 OPTION_(report_t, truncate_, DO_(style) {
1029 if (style == "leading")
1031 else if (style == "middle")
1033 else if (style == "trailing")
1035 else
1036 throw_(std::invalid_argument,
1037 _f("Unrecognized truncation style: '%1%'") % style);
1039 });
1040
1041 OPTION_(report_t, unbudgeted, DO() {
1042 parent->budget_flags |= BUDGET_UNBUDGETED;
1043 });
1044
1045 OPTION_(report_t, uncleared, DO() { // -U
1046 OTHER(limit_).on(whence, "uncleared|pending");
1047 });
1048
1049 OPTION(report_t, unrealized);
1050
1051 OPTION(report_t, unrealized_gains_);
1052 OPTION(report_t, unrealized_losses_);
1053
1054 OPTION_(report_t, unround, DO() {
1055 OTHER(amount_).on(whence, "unrounded(amount_expr)");
1056 OTHER(total_).on(whence, "unrounded(total_expr)");
1057 });
1058
1059 OPTION_(report_t, weekly, DO() { // -W
1060 OTHER(period_).on(whence, "weekly");
1061 });
1062
1063 OPTION_(report_t, wide, DO() { // -w
1064 OTHER(columns_).on(whence, "132");
1065 });
1066
1067 OPTION_(report_t, yearly, DO() { // -Y
1068 OTHER(period_).on(whence, "yearly");
1069 });
1070
1071 OPTION(report_t, meta_width_);
1072 OPTION(report_t, date_width_);
1073 OPTION(report_t, payee_width_);
1074 OPTION(report_t, account_width_);
1075 OPTION(report_t, amount_width_);
1076 OPTION(report_t, total_width_);
1077 OPTION(report_t, values);
1078};
1079
1080template <class Type = post_t,
1081 class handler_ptr = post_handler_ptr,
1082 void (report_t::*report_method)(handler_ptr) =
1085{
1086 shared_ptr<item_handler<Type> > handler;
1087
1088 report_t& report;
1089 string whence;
1090
1091public:
1092 reporter(shared_ptr<item_handler<Type> > _handler,
1093 report_t& _report, const string& _whence)
1094 : handler(_handler), report(_report), whence(_whence) {
1095 TRACE_CTOR(reporter, "item_handler<Type>, report_t&, string");
1096 }
1097 reporter(const reporter& other)
1098 : handler(other.handler), report(other.report), whence(other.whence) {
1099 TRACE_CTOR(reporter, "copy");
1100 }
1101 ~reporter() throw() {
1103 }
1104
1106 {
1107 if (args.size() > 0)
1108 report.parse_query_args(args.value(), whence);
1109
1110 (report.*report_method)(handler_ptr(handler));
1111
1112 return true;
1113 }
1114};
1115
1116} // namespace ledger
#define CURRENT_TIME()
Definition times.h:75
#define throw_(cls, msg)
Definition error.h:55
Basic type and macros for handling command-line options.
#define DECL1(type, name, vartype, var, value)
Definition option.h:214
#define OTHER(name)
Definition option.h:288
#define HANDLER(name)
Definition option.h:263
#define DO_(var)
Definition option.h:219
#define OPTION(type, name)
Definition option.h:266
#define HANDLED(name)
Definition option.h:264
#define CTOR(type, name)
Definition option.h:210
#define OPTION_(type, name, body)
Definition option.h:273
#define DO()
Definition option.h:218
#define OPTION__(type, name, body)
Definition option.h:281
#define BUDGET_UNBUDGETED
Definition filters.h:968
#define BUDGET_BUDGETED
Definition filters.h:967
#define BUDGET_NO_BUDGET
Definition filters.h:966
Types for annotating commodities.
A utility class for abstracting an output stream.
Types for handling commodities.
#define TRACE_DTOR(cls)
Definition utils.h:144
#define TRACE_CTOR(cls, args)
Definition utils.h:143
optional< datetime_t > epoch
balance_t average_lot_prices(const balance_t &bal)
shared_ptr< item_handler< post_t > > post_handler_ptr
Definition chain.h:90
boost::filesystem::path path
Definition utils.h:68
std::string string
Definition utils.h:59
value_t scope_value(scope_t *val)
Definition value.h:991
boost::posix_time::ptime datetime_t
Definition times.h:53
shared_ptr< item_handler< account_t > > acct_handler_ptr
Definition chain.h:91
intrusive_ptr< op_t > ptr_op_t
Definition expr.h:57
static enum ledger::format_t::elision_style_t default_style
static bool default_style_changed
Definition format.h:117
void on(const char *whence)
Definition option.h:137
string str() const
Definition option.h:130
void commodities_report(post_handler_ptr handler)
value_t fn_lot_date(call_scope_t &scope)
virtual void define(const symbol_t::kind_t kind, const string &name, expr_t::ptr_op_t def)
value_t fn_amount_expr(call_scope_t &scope)
value_t fn_commodity_price(call_scope_t &scope)
value_t fn_rounded(call_scope_t &scope)
void normalize_period()
value_t fn_total_expr(call_scope_t &scope)
report_t(const report_t &report)
Definition report.h:124
value_t fn_truncated(call_scope_t &scope)
value_t fn_to_balance(call_scope_t &scope)
keep_details_t what_to_keep()
Definition report.h:235
value_t fn_quoted_rfc(call_scope_t &scope)
value_t fn_round(call_scope_t &scope)
value_t fn_get_at(call_scope_t &scope)
value_t fn_quoted(call_scope_t &scope)
value_t fn_to_amount(call_scope_t &scope)
value_t fn_to_mask(call_scope_t &scope)
value_t fn_to_sequence(call_scope_t &scope)
void report_options(std::ostream &out)
Definition report.h:243
value_t fn_unround(call_scope_t &scope)
void xact_report(post_handler_ptr handler, xact_t &xact)
value_t fn_lot_tag(call_scope_t &scope)
value_t fn_to_string(call_scope_t &scope)
session_t & session
Definition report.h:108
report_t(session_t &_session)
Definition report.h:119
void quick_close()
Definition report.h:137
value_t echo_command(call_scope_t &scope)
value_t fn_unrounded(call_scope_t &scope)
void normalize_options(const string &verb)
value_t fn_is_seq(call_scope_t &scope)
value_t fn_format_datetime(call_scope_t &scope)
value_t fn_to_int(call_scope_t &scope)
value_t pricemap_command(call_scope_t &scope)
void accounts_report(acct_handler_ptr handler)
uint_least8_t budget_flags
Definition report.h:117
value_t fn_commodity(call_scope_t &scope)
virtual ~report_t()
Definition report.h:132
virtual string description()
Definition report.h:141
value_t fn_ansify_if(call_scope_t &scope)
option_t< report_t > * lookup_option(const char *p)
value_t fn_now(call_scope_t &)
Definition report.h:208
value_t fn_today(call_scope_t &)
Definition report.h:211
value_t display_value(const value_t &val)
value_t fn_justify(call_scope_t &scope)
value_t fn_to_boolean(call_scope_t &scope)
value_t fn_to_date(call_scope_t &scope)
value_t fn_set_commodity_price(call_scope_t &scope)
value_t fn_abs(call_scope_t &scope)
value_t fn_display_amount(call_scope_t &scope)
value_t fn_trim(call_scope_t &scope)
value_t fn_display_total(call_scope_t &scope)
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string &name)
value_t fn_market(call_scope_t &scope)
value_t fn_to_datetime(call_scope_t &scope)
value_t fn_print(call_scope_t &scope)
value_t fn_averaged_lots(call_scope_t &scope)
value_t fn_lot_price(call_scope_t &scope)
value_t fn_quantity(call_scope_t &scope)
value_t fn_clear_commodity(call_scope_t &scope)
void generate_report(post_handler_ptr handler)
value_t fn_top_amount(call_scope_t &val)
value_t fn_floor(call_scope_t &scope)
value_t fn_options(call_scope_t &)
Definition report.h:215
value_t fn_nail_down(call_scope_t &scope)
value_t fn_strip(call_scope_t &scope)
string report_format(option_t< report_t > &option)
Definition report.h:219
datetime_t terminus
Definition report.h:116
value_t fn_format_date(call_scope_t &scope)
value_t fn_join(call_scope_t &scope)
void posts_report(post_handler_ptr handler)
value_t fn_format(call_scope_t &scope)
value_t fn_ceiling(call_scope_t &scope)
value_t fn_should_bold(call_scope_t &scope)
value_t reload_command(call_scope_t &)
value_t fn_roundto(call_scope_t &scope)
optional< string > maybe_format(option_t< report_t > &option)
Definition report.h:225
output_stream_t output_stream
Definition report.h:109
value_t fn_percent(call_scope_t &scope)
void parse_query_args(const value_t &args, const string &whence)
value_t fn_scrub(call_scope_t &scope)
reporter(const reporter &other)
Definition report.h:1097
value_t operator()(call_scope_t &args)
Definition report.h:1105
reporter(shared_ptr< item_handler< Type > > _handler, report_t &_report, const string &_whence)
Definition report.h:1092
value_t & value()
Definition scope.h:333
std::size_t size() const
Definition scope.h:393
An output stream.
Definition stream.h:66
optional< date_t > begin() const
Definition times.h:492
Dynamic type representing various numeric types.
Definition value.h:86