Example #1
0
        public void ExtendXact(XactBase xact, ParseContext context)
        {
            IList <Post> initialPosts = xact.Posts.ToList();

            try
            {
                bool needsFurtherVerification = false;

                foreach (Post initialPost in initialPosts)
                {
                    if (initialPost.Flags.HasFlag(SupportsFlagsEnum.ITEM_GENERATED))
                    {
                        continue;
                    }

                    BindScope boundScope = new BindScope(Scope.DefaultScope, initialPost);

                    bool matchesPredicate = false;
                    if (TryQuickMatch)
                    {
                        try
                        {
                            bool foundMemoizedResult = MemoizedResults.TryGetValue(initialPost.Account.FullName, out matchesPredicate);

                            // Since the majority of people who use automated transactions simply
                            // match against account names, try using a *much* faster version of
                            // the predicate evaluator.
                            if (!foundMemoizedResult)
                            {
                                matchesPredicate = PostPred(Predicate.Op, initialPost);
                                MemoizedResults.Add(initialPost.Account.FullName, matchesPredicate);
                            }
                        }
                        catch
                        {
                            Logger.Current.Debug("xact.extend.fail", () => "The quick matcher failed, going back to regular eval");
                            TryQuickMatch    = false;
                            matchesPredicate = Predicate.Calc(boundScope).AsBoolean;
                        }
                    }
                    else
                    {
                        matchesPredicate = Predicate.Calc(boundScope).AsBoolean;
                    }

                    if (matchesPredicate)
                    {
                        if (DeferredNotes != null)
                        {
                            foreach (DeferredTagData data in DeferredNotes)
                            {
                                if (data.ApplyToPost == null)
                                {
                                    initialPost.AppendNote(ApplyFormat(data.TagData, boundScope), boundScope, data.OverwriteExisting);
                                }
                            }
                        }

                        if (CheckExprs != null)
                        {
                            foreach (CheckExprPair pair in CheckExprs)
                            {
                                if (pair.CheckExprKind == CheckExprKindEnum.EXPR_GENERAL)
                                {
                                    pair.Expr.Calc(boundScope);
                                }
                                else if (!pair.Expr.Calc(boundScope).AsBoolean)
                                {
                                    if (pair.CheckExprKind == CheckExprKindEnum.EXPR_ASSERTION)
                                    {
                                        throw new ParseError(String.Format(ParseError.ParseError_TransactionAssertionFailed, pair.Expr));
                                    }
                                    else
                                    {
                                        context.Warning(String.Format(ParseError.ParseError_TransactionCheckFailed, pair.Expr));
                                    }
                                }
                            }
                        }

                        foreach (Post post in Posts)
                        {
                            Amount postAmount;
                            if (post.Amount.IsEmpty)
                            {
                                if (post.AmountExpr == null)
                                {
                                    throw new AmountError(AmountError.ErrorMessageAutomatedTransactionsPostingHasNoAmount);
                                }

                                Value result = post.AmountExpr.Calc(boundScope);
                                if (result.Type == ValueTypeEnum.Integer)
                                {
                                    postAmount = result.AsAmount;
                                }
                                else
                                {
                                    if (result.Type != ValueTypeEnum.Amount)
                                    {
                                        throw new AmountError(AmountError.ErrorMessageAmountExpressionsMustResultInASimpleAmount);
                                    }
                                    postAmount = result.AsAmount;
                                }
                            }
                            else
                            {
                                postAmount = post.Amount;
                            }

                            Amount amt;
                            if (!postAmount.HasCommodity)
                            {
                                amt = initialPost.Amount * postAmount;
                            }
                            else
                            {
                                amt = postAmount;
                            }

                            Account account  = post.Account;
                            string  fullName = account.FullName;
                            if (String.IsNullOrEmpty(fullName))
                            {
                                throw new InvalidOperationException("fullName");
                            }

                            if (Logger.Current.ShowDebug("xact.extend"))
                            {
                                Logger.Current.Debug("xact.extend", () => String.Format("Initial post on line {0}: amount {1} (precision {2})",
                                                                                        initialPost.Pos.BegLine, initialPost.Amount, initialPost.Amount.DisplayPrecision));

                                if (initialPost.Amount.KeepPrecision)
                                {
                                    Logger.Current.Debug("xact.extend", () => "  precision is kept");
                                }

                                Logger.Current.Debug("xact.extend", () => String.Format("Posting on line {0}: amount {1}, amt {2} (precision {3} != {4})",
                                                                                        post.Pos.BegLine, postAmount, amt, postAmount.DisplayPrecision, amt.DisplayPrecision));

                                if (postAmount.KeepPrecision)
                                {
                                    Logger.Current.Debug("xact.extend", () => "  precision is kept");
                                }
                                if (amt.KeepPrecision)
                                {
                                    Logger.Current.Debug("xact.extend", () => "  amt precision is kept");
                                }
                            }

                            if (String.IsNullOrEmpty(post.Account.FullName))
                            {
                                throw new AssertionFailedError("assert(! fullname.empty());");
                            }

                            if (fullName.Contains("$account"))
                            {
                                fullName = AccountRegex.Replace(fullName, initialPost.Account.FullName);
                                while (account.Parent != null)
                                {
                                    account = account.Parent;
                                }
                                account = account.FindAccount(fullName);
                            }
                            else if (fullName.Contains("%("))
                            {
                                Format accountName = new Format(fullName);
                                while (account.Parent != null)
                                {
                                    account = account.Parent;
                                }
                                account = account.FindAccount(accountName.Calc(boundScope));
                            }

                            // Copy over details so that the resulting post is a mirror of
                            // the automated xact's one.
                            Post newPost = new Post(account, amt);
                            newPost.CopyDetails(post);

                            // A Cleared transaction implies all of its automatic posting are cleared
                            // CPR 2012/10/23
                            if (xact.State == ItemStateEnum.Cleared)
                            {
                                Logger.Current.Debug("xact.extend.cleared", () => "CLEARED");
                                newPost.State = ItemStateEnum.Cleared;
                            }

                            newPost.Flags   = newPost.Flags | SupportsFlagsEnum.ITEM_GENERATED;
                            newPost.Account = Journal.RegisterAccount(account.FullName, newPost, Journal.Master);

                            if (DeferredNotes != null)
                            {
                                foreach (DeferredTagData data in DeferredNotes)
                                {
                                    if (data.ApplyToPost == null || data.ApplyToPost == post)
                                    {
                                        newPost.AppendNote(ApplyFormat(data.TagData, boundScope), boundScope, data.OverwriteExisting);
                                    }
                                }
                            }

                            Post.ExtendPost(newPost, Journal);

                            xact.AddPost(newPost);
                            newPost.Account.AddPost(newPost);

                            // Add flag so this post updates the account balance
                            newPost.XData.Visited = true; // POST_EXT_VISITED

                            if (newPost.MustBalance)
                            {
                                needsFurtherVerification = true;
                            }
                        }
                    }
                }

                if (needsFurtherVerification)
                {
                    xact.Verify();
                }
            }
            catch
            {
                ErrorContext.Current.AddErrorContext(Item.ItemContext(this, "While applying automated transaction"));
                ErrorContext.Current.AddErrorContext(Item.ItemContext(xact, "While extending transaction"));
                throw;
            }
        }
Example #2
0
 public XactBase(XactBase xactBase)
     : base(xactBase)
 {
     Posts   = new List <Post>();
     Journal = xactBase.Journal;
 }
Example #3
0
 public AddBalancingPost(XactBase xact, Post nullPost) : this()
 {
     First    = true;
     Xact     = xact;
     NullPost = nullPost;
 }