Пример #1
0
        public override void Clear()
        {
            PendingPosts.Clear();
            Temps.Clear();

            base.Clear();
        }
Пример #2
0
        /// <summary>
        /// Ported from report_budget_items
        /// </summary>
        public void ReportBudgetItems(Date date)
        {
            if (!PendingPosts.Any())
            {
                return;
            }

            bool reported;

            do
            {
                IList <PendingPostsPair> postsToErase = new List <PendingPostsPair>();
                reported = false;

                foreach (PendingPostsPair pair in PendingPosts)
                {
                    Date?begin = pair.DateInterval.Start;
                    if (!begin.HasValue)
                    {
                        Date?rangeBegin = null;
                        if (pair.DateInterval.Range != null)
                        {
                            rangeBegin = pair.DateInterval.Range.Begin;
                        }

                        Logger.Debug(DebugBudgetGenerate, () => "Finding period for pending post");
                        if (!pair.DateInterval.FindPeriod(rangeBegin ?? date))
                        {
                            continue;
                        }
                        if (!pair.DateInterval.Start.HasValue)
                        {
                            throw new LogicError(LogicError.ErrorMessageFailedToFindPeriodForPeriodicTransaction);
                        }
                        begin = pair.DateInterval.Start;
                    }

                    Logger.Debug(DebugBudgetGenerate, () => String.Format("begin = {0}", begin));
                    Logger.Debug(DebugBudgetGenerate, () => String.Format("date  = {0}", date));
                    if (pair.DateInterval.Finish.HasValue)
                    {
                        Logger.Debug(DebugBudgetGenerate, () => String.Format("pair.first.finish = {0}", pair.DateInterval.Finish));
                    }

                    if (begin <= date && (!pair.DateInterval.Finish.HasValue || begin < pair.DateInterval.Finish))
                    {
                        Post post = pair.Post;

                        pair.DateInterval++;
                        if (!pair.DateInterval.Start.HasValue)
                        {
                            postsToErase.Add(pair);
                        }

                        Logger.Debug(DebugBudgetGenerate, () => "Reporting budget for " + post.ReportedAccount.FullName);

                        Xact xact = Temps.CreateXact();
                        xact.Payee = "Budget transaction";
                        xact.Date  = begin.Value;

                        Post temp = Temps.CopyPost(post, xact);
                        temp.Amount.InPlaceNegate();

                        if (Flags.HasFlag(ReportBudgetFlags.BUDGET_WRAP_VALUES))
                        {
                            Value seq = new Value();
                            seq.PushBack(Value.Get(0));
                            seq.PushBack(Value.Get(temp.Amount));

                            temp.XData.CompoundValue = seq;
                            temp.XData.Compound      = true;
                        }

                        base.Handle(temp);

                        reported = true;
                    }
                }

                foreach (PendingPostsPair pair in postsToErase)
                {
                    PendingPosts.Remove(pair);
                }
            }while (reported);
        }
Пример #3
0
 public virtual void AddPost(DateInterval period, Post post)
 {
     PendingPosts.Add(new PendingPostsPair(new DateInterval(period), post));
 }
Пример #4
0
        /// <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();
        }