コード例 #1
0
        public override void Handle(Post post)
        {
            foreach (Tuple <string, Account, ISet <Xact> > pair in TagsList)
            {
                Value tagValue = post.GetTag(pair.Item1, false);
                // When checking if the transaction has the tag, only inject once
                // per transaction.
                if (Value.IsNullOrEmptyOrFalse(tagValue) && !pair.Item3.Contains(post.Xact))
                {
                    tagValue = post.Xact.GetTag(pair.Item1);
                    if (!Value.IsNullOrEmptyOrFalse(tagValue))
                    {
                        pair.Item3.Add(post.Xact);
                    }
                }

                if (!Value.IsNullOrEmptyOrFalse(tagValue))
                {
                    Xact xact = Temps.CopyXact(post.Xact);
                    xact.Date   = post.GetDate();
                    xact.Flags |= SupportsFlagsEnum.ITEM_GENERATED;
                    Post temp = Temps.CopyPost(post, xact);

                    temp.Account = pair.Item2;
                    temp.Amount  = tagValue.AsAmount;
                    temp.Flags  |= SupportsFlagsEnum.ITEM_GENERATED;

                    base.Handle(temp);
                }
            }

            base.Handle(post);
        }
コード例 #2
0
        /// <summary>
        /// Ported from void transfer_details::operator()(post_t& post)
        /// </summary>
        public override void Handle(Post post)
        {
            Xact xact = Temps.CopyXact(post.Xact);

            xact.Date = post.GetDate();

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

            temp.State = post.State;

            BindScope boundScope = new BindScope(Scope, temp);
            Value     substitute = Expr.Calc(boundScope);

            if (!Value.IsNullOrEmpty(substitute))
            {
                switch (WhichElement)
                {
                case TransferDetailsElementEnum.SET_DATE:
                    temp.Date = substitute.AsDate;
                    break;

                case TransferDetailsElementEnum.SET_ACCOUNT:
                {
                    string accountName = substitute.AsString;
                    if (!String.IsNullOrEmpty(accountName) && !accountName.EndsWith(":"))
                    {
                        Account prevAccount = temp.Account;
                        temp.Account.RemovePost(temp);

                        accountName += ":" + prevAccount.FullName;

                        string[] accountNames = accountName.Split(':');
                        temp.Account = FiltersCommon.CreateTempAccountFromPath(accountNames, Temps, xact.Journal.Master);
                        temp.Account.AddPost(temp);

                        temp.Account.SetFlags(prevAccount);
                        if (prevAccount.HasXData)
                        {
                            temp.Account.XData.SetFlags(prevAccount.XData);
                        }
                    }
                    break;
                }

                case TransferDetailsElementEnum.SET_PAYEE:
                    xact.Payee = substitute.AsString;
                    break;
                }
            }

            base.Handle(temp);
        }
コード例 #3
0
        public override void Handle(Post post)
        {
            bool copyXactDetails = false;

            if (LastXact != post.Xact)
            {
                Temps.CopyXact(post.Xact);
                LastXact        = post.Xact;
                copyXactDetails = true;
            }
            Xact xact = Temps.LastXact;

            xact.Code = null;

            if (copyXactDetails)
            {
                xact.CopyDetails(post.Xact);

                string buf = String.Format("{0}{1}{0}", post.Xact.Payee, IntegerGen.Value());

                xact.Payee = SHA1.GetHash(buf);
                xact.Note  = null;
            }
            else
            {
                xact.Journal = post.Xact.Journal;
            }

            IList <string> accountNames = new List <string>();

            for (Account acct = post.Account; acct != null; acct = acct.Parent)
            {
                string buf = String.Format("{0}{1}{2}", IntegerGen.Value(), acct, acct.FullName);

                accountNames.Add(SHA1.GetHash(buf));
            }

            Account newAccount = FiltersCommon.CreateTempAccountFromPath(accountNames, Temps, xact.Journal.Master);
            Post    temp       = Temps.CopyPost(post, xact, newAccount);

            temp.Note   = null;
            temp.Flags |= SupportsFlagsEnum.POST_ANONYMIZED;

            RenderCommodity(temp.Amount);
            if (temp.Amount.HasAnnotation)
            {
                temp.Amount.Annotation.Tag = null;
                if (temp.Amount.Annotation.Price != null)
                {
                    RenderCommodity(temp.Amount.Annotation.Price);
                }
            }

            if (temp.Cost != null)
            {
                RenderCommodity(temp.Cost);
            }
            if (temp.AssignedAmount != null)
            {
                RenderCommodity(temp.AssignedAmount);
            }

            base.Handle(temp);
        }
コード例 #4
0
        /// <summary>
        /// Ported from changed_value_posts::output_intermediate_prices
        /// </summary>
        public void OutputIntermediatePrices(Post post, Date current)
        {
            // To fix BZ#199, examine the balance of last_post and determine whether the
            // price of that amount changed after its date and before the new post's
            // date.  If so, generate an output_revaluation for that price change.
            // Mostly this is only going to occur if the user has a series of pricing
            // entries, since a posting-based revaluation would be seen here as a post.

            Value displayTotal = Value.Clone(LastTotal);

            if (displayTotal.Type == ValueTypeEnum.Sequence)
            {
                Xact xact = Temps.CreateXact();
                xact.Payee = "Commodities revalued";
                xact.Date  = current.IsValid() ? current : post.ValueDate;

                Post temp = Temps.CopyPost(post, xact);
                temp.Flags |= SupportsFlagsEnum.ITEM_GENERATED;

                PostXData xdata = temp.XData;
                if (current.IsValid())
                {
                    xdata.Date = current;
                }

                Logger.Current.Debug("filters.revalued", () => String.Format("intermediate last_total = {0}", LastTotal));

                switch (LastTotal.Type)
                {
                case ValueTypeEnum.Boolean:
                case ValueTypeEnum.Integer:
                    LastTotal.InPlaceCast(ValueTypeEnum.Amount);
                    temp.Amount = LastTotal.AsAmount;
                    break;

                case ValueTypeEnum.Amount:
                    temp.Amount = LastTotal.AsAmount;
                    break;

                case ValueTypeEnum.Balance:
                case ValueTypeEnum.Sequence:
                    xdata.CompoundValue = LastTotal;
                    xdata.Compound      = true;
                    break;

                default:
                    throw new InvalidOperationException();
                }

                BindScope innerScope = new BindScope(Report, temp);
                displayTotal = DisplayTotalExpr.Calc(innerScope);

                Logger.Current.Debug("filters.revalued", () => String.Format("intermediate display_total = {0}", displayTotal));
            }

            switch (displayTotal.Type)
            {
            case ValueTypeEnum.Void:
            case ValueTypeEnum.Integer:
            case ValueTypeEnum.Sequence:
                break;

            case ValueTypeEnum.Amount:
            case ValueTypeEnum.Balance:
            {
                if (displayTotal.Type == ValueTypeEnum.Amount)
                {
                    displayTotal.InPlaceCast(ValueTypeEnum.Balance);
                }

                IDictionary <DateTime, Amount> allPrices = new SortedDictionary <DateTime, Amount>();

                foreach (KeyValuePair <Commodity, Amount> amtComm in displayTotal.AsBalance.Amounts)
                {
                    amtComm.Key.MapPrices((d, a) => allPrices[d] = a, current, post.ValueDate, true);
                }

                // Choose the last price from each day as the price to use
                IDictionary <Date, bool> pricingDates = new SortedDictionary <Date, bool>();

                foreach (KeyValuePair <DateTime, Amount> price in allPrices.Reverse())
                {
                    // This insert will fail if a later price has already been inserted
                    // for that date.
                    var priceDate = (Date)price.Key.Date;
                    Logger.Current.Debug("filters.revalued", () => String.Format("re-inserting {0} at {1}", price.Value, priceDate));
                    pricingDates[priceDate] = true;
                }

                // Go through the time-sorted prices list, outputting a revaluation for
                // each price difference.
                foreach (KeyValuePair <Date, bool> price in pricingDates)
                {
                    OutputRevaluation(post, price.Key);
                    LastTotal = RepricedTotal;
                }
                break;
            }

            default:
                throw new InvalidOperationException();
            }
        }
コード例 #5
0
ファイル: BudgetPosts.cs プロジェクト: wittyansh/nledger
        /// <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);
        }
コード例 #6
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();
        }