public override void Handle(Post post) { BindScope boundScope = new BindScope(Context, post); if (Pred.Calc(boundScope).Bool) { post.XData.Matches = true; base.Handle(post); } }
/// <summary> /// Ported from forecast_posts::flush /// </summary> public override void Flush() { IList <Post> passed = new List <Post>(); Date last = TimesCommon.Current.CurrentDate; // If there are period transactions to apply in a continuing series until // the forecast condition is met, generate those transactions now. Note // that no matter what, we abandon forecasting beyond the next 5 years. // // It works like this: // // Earlier, in forecast_posts::add_period_xacts, we cut up all the periodic // transactions into their components postings, so that we have N "periodic // postings". For example, if the user had this: // // ~ daily // Expenses:Food $10 // Expenses:Auto:Gas $20 // ~ monthly // Expenses:Food $100 // Expenses:Auto:Gas $200 // // We now have 4 periodic postings in `pending_posts'. // // Each periodic postings gets its own copy of its parent transaction's // period, which is modified as we go. This is found in the second member // of the pending_posts_list for each posting. // // The algorithm below works by iterating through the N periodic postings // over and over, until each of them mets the termination critera for the // forecast and is removed from the set. while (PendingPosts.Any()) { // At each step through the loop, we find the first periodic posting whose // period contains the earliest starting date. PendingPostsPair least = PendingPosts.First(); foreach (PendingPostsPair i in PendingPosts) { if (!i.DateInterval.Start.HasValue) { throw new InvalidOperationException("Start is empty"); } if (!least.DateInterval.Start.HasValue) { throw new InvalidOperationException("least.Start is empty"); } if (i.DateInterval.Start < least.DateInterval.Start) { least = i; } } // If the next date in the series for this periodic posting is more than 5 // years beyond the last valid post we generated, drop it from further // consideration. Date next = least.DateInterval.Next.Value; if (next <= least.DateInterval.Start) { throw new InvalidOperationException("next <= least.DateInterval.Start"); } if (((next - last).Days) > (365 * ForecastYears)) { Logger.Current.Debug("filters.forecast", () => String.Format("Forecast transaction exceeds {0} years beyond today", ForecastYears)); PendingPosts.Remove(least); continue; } // `post' refers to the posting defined in the period transaction. We // make a copy of it within a temporary transaction with the payee // "Forecast transaction". Post post = least.Post; Xact xact = Temps.CreateXact(); xact.Payee = "Forecast transaction"; xact.Date = next; Post temp = Temps.CopyPost(post, xact); // Submit the generated posting Logger.Current.Debug("filters.forecast", () => String.Format("Forecast transaction: {0} {1} {2}", temp.GetDate(), temp.Account.FullName, temp.Amount)); base.Handle(temp); // If the generated posting matches the user's report query, check whether // it also fails to match the continuation condition for the forecast. If // it does, drop this periodic posting from consideration. if (temp.HasXData && temp.XData.Matches) { Logger.Current.Debug("filters.forecast", () => " matches report query"); BindScope boundScope = new BindScope(Context, temp); if (!Pred.Calc(boundScope).Bool) { Logger.Current.Debug("filters.forecast", () => " fails to match continuation criteria"); PendingPosts.Remove(least); continue; } } // Increment the 'least', but remove it from pending_posts if it // exceeds its own boundaries. ++least.DateInterval; if (!least.DateInterval.Start.HasValue) { PendingPosts.Remove(least); continue; } } base.Flush(); }