/// <summary> /// Ported from posts_as_equity::report_subtotal /// </summary> public void ReportSubtotal() { Date finish = default(Date); foreach (Post post in ComponentPosts) { Date date = post.GetDate(); if (!finish.IsValid() || date > finish) { finish = date; } } Xact xact = Temps.CreateXact(); xact.Payee = "Opening Balances"; xact.Date = finish; Value total = Value.Get(0); foreach (KeyValuePair <string, AcctValue> pair in Values) { Value value = pair.Value.Value.StripAnnotations(Report.WhatToKeep()); if (!Value.IsNullOrEmpty(value)) { if (value.Type == ValueTypeEnum.Balance) { foreach (KeyValuePair <Commodity, Amount> amountPair in value.AsBalance.Amounts) { if (!amountPair.Value.IsZero) { FiltersCommon.HandleValue( /* value= */ Value.Get(amountPair.Value), /* account= */ pair.Value.Account, /* xact= */ xact, /* temps= */ Temps, /* handler= */ (PostHandler)Handler, /* date= */ finish, /* act_date_p= */ false); } } } else { FiltersCommon.HandleValue( /* value= */ Value.Get(value.AsAmount), /* account= */ pair.Value.Account, /* xact= */ xact, /* temps= */ Temps, /* handler= */ (PostHandler)Handler, /* date= */ finish, /* act_date_p= */ false); } } if (!pair.Value.IsVirtual || pair.Value.MustBalance) { total.InPlaceAdd(value); } } Values.Clear(); // This last part isn't really needed, since an Equity:Opening // Balances posting with a null amount will automatically balance with // all the other postings generated. But it does make the full // balancing amount clearer to the user. if (!total.IsZero) { Action <Amount> postCreator = amt => { Post balancePost = Temps.CreatePost(xact, BalanceAccount); balancePost.Amount = amt.Negated(); Handler.Handle(balancePost); }; if (total.Type == ValueTypeEnum.Balance) { total.AsBalance.MapSortedAmounts(postCreator); } else { postCreator(total.AsAmount); } } }
public override void Flush() { if (Interval.Duration == null) { base.Flush(); return; } // Sort all the postings we saw by date ascending // [DM] Enumerable.OrderBy is a stable sort that preserve original positions for equal items AllPosts = AllPosts.OrderBy(p => p, new IntervalPostCompare()).ToList(); // only if the interval has no start use the earliest post if (!(Interval.Begin.HasValue && Interval.FindPeriod(Interval.Begin.Value))) { // Determine the beginning interval by using the earliest post if (AllPosts.Any() && !Interval.FindPeriod(AllPosts.First().GetDate())) { throw new LogicError(LogicError.ErrorMessageFailedToFindPeriodForIntervalReport); } } // Walk the interval forward reporting all posts within each one // before moving on, until we reach the end of all_posts bool sawPosts = false; for (int i = 0; i < AllPosts.Count;) { Post post = AllPosts[i]; Logger.Current.Debug("filters.interval", () => String.Format("Considering post {0} = {1}", post.GetDate(), post.Amount)); Logger.Current.Debug("filters.interval", () => String.Format("interval is:{0}", DebugInterval(Interval))); if (Interval.Finish.HasValue && post.GetDate() >= Interval.Finish.Value) { throw new InvalidOperationException("assert(! interval.finish || post->date() < *interval.finish)"); } if (Interval.WithinPeriod(post.GetDate())) { Logger.Current.Debug("filters.interval", () => "Calling subtotal_posts::operator()"); base.Handle(post); ++i; sawPosts = true; } else { if (sawPosts) { Logger.Current.Debug("filters.interval", () => "Calling subtotal_posts::report_subtotal()"); ReportSubtotal(Interval); sawPosts = false; } else if (GenerateEmptyPosts) { // Generate a null posting, so the intervening periods can be // seen when -E is used, or if the calculated amount ends up // being non-zero Xact nullXact = Temps.CreateXact(); nullXact.Date = Interval.InclusiveEnd.Value; Post nullPost = Temps.CreatePost(nullXact, EmptytAccount); nullPost.Flags |= SupportsFlagsEnum.POST_CALCULATED; nullPost.Amount = new Amount(0); base.Handle(nullPost); ReportSubtotal(Interval); } Logger.Current.Debug("filters.interval", () => "Advancing interval"); ++Interval; } } // If the last postings weren't reported, do so now. if (sawPosts) { Logger.Current.Debug("filters.interval", () => "Calling subtotal_posts::report_subtotal() at end"); ReportSubtotal(Interval); } // Tell our parent class to flush base.Flush(); }