예제 #1
0
        private void processAccount(GncAccount acct, int depth)
        {
            var balsnaps = acct.EnumSplits(false).Where(spl => spl.IsBalsnap).ToArray();

            if (!balsnaps.Any())
            {
                _report.SetValue(acct, Tr.PgLastBalsnap.ColLast, Tr.PgLastBalsnap.LastNever, "lastbalsnap_never");
            }
            else
            {
                var lastsnap  = balsnaps.OrderBy(spl => spl.Transaction.DatePosted).Last();
                var lastsplit = acct.EnumSplits(false).Last();
                if (lastsnap.Balsnap == 0 && object.ReferenceEquals(lastsplit, lastsnap) && lastsnap.AccountBalanceAfter == 0)
                {
                    _report.SetValue(acct, Tr.PgLastBalsnap.ColLast, Tr.PgLastBalsnap.LastZero, "lastbalsnap_zero");
                }
                else
                {
                    int days = (int)(DateTime.Today - lastsnap.Transaction.DatePosted).TotalDays;
                    _report.SetValue(acct, Tr.PgLastBalsnap.ColLast, Tr.PgLastBalsnap.LastNDaysAgo.Fmt(Tr.Language.GetNumberSystem(), days), makeCss(days));
                }
            }

            foreach (var acctChild in acct.EnumChildren())
            {
                processAccount(acctChild, depth + 1);
            }
        }
예제 #2
0
 protected override AccountValueInfo GetAccountValue(GncAccount account, int depth)
 {
     return(new AccountValueInfo
     {
         Amount = ConvertTo == null?account.GetBalanceWithSubaccounts(_date) : account.GetBalanceConverted(_date, true, ConvertTo),
     });
 }
예제 #3
0
        protected override void ProcessAccount(GncAccount acct, int depth)
        {
            var intervalTotal = new GncMultiAmount();
            int intervalDays = 0, intervalCount = 0;
            var earliest = acct.Book.EarliestDate;
            var latest = acct.Book.LatestDate;
            foreach (var interval in EnumIntervals())
            {
                var tot = ConvertTo == null ? acct.GetTotalWithSubaccounts(interval) : acct.GetTotalConverted(interval, true, ConvertTo);
                if (Negate)
                    tot.NegateInplace();
                // Count only the full months for the purpose of averaging
                if (interval.Start >= earliest && interval.End <= latest)
                {
                    intervalTotal.AddInplace(tot);
                    intervalCount++;
                    intervalDays += (int) (interval.End - interval.Start).TotalDays + 1;
                }

                SetReportAmount(Report, acct, interval, tot, ConvertTo != null, "/Trns?Acct={0}&Fr={1}&To={2}{3}".Fmt(
                            acct.Path(":").UrlEscape(),
                            interval.Start.Date.ToIsoStringOptimal(),
                            interval.End.Date.ToIsoStringOptimal(),
                            acct.EnumChildren().Any() ? "&SubAccts=true" : ""));
            }

            if (intervalCount > 1)
            {
                if (!Report.ContainsCol("average"))
                    Report.AddCol("average", Tr.PgMonthlyTotals.ColAverage, "aw-col-average");
                intervalTotal.MultiplyInplace(30.43m / intervalDays); // daily average * days in month
                SetReportAmount(Report, acct, "average", intervalTotal, ConvertTo != null);
            }
        }
예제 #4
0
        private IEnumerable <object> generateAcctPath(GncAccount acct)
        {
            var path = acct.PathAsList(_account);

            if (path.Count == 0)
            {
                yield return(new I(Tr.PgTrns.ThisAccount));
            }
            else
            {
                for (int i = 0; i < path.Count - 1; i++)
                {
                    yield return(new A(path[i].Name)
                    {
                        class_ = "nocolor", href = Request.Url.WithQuery("Acct", path[i].Path(":")).ToHref()
                    });

                    yield return(" : ");
                }
                if (path.Count > 0)
                {
                    var pathlast = path[path.Count - 1];
                    yield return(new A(pathlast.Name)
                    {
                        class_ = "nocolor", href = Request.Url.WithQuery("Acct", pathlast.Path(":")).ToHref()
                    });
                }
            }
        }
예제 #5
0
        public override object GetContent()
        {
            _account = GetAccount("Acct");

            _report = new ReportAccounts(_account, Request, true, true);
            processAccount(_account, 0);

            return(new DIV(_report.GetHtml()));
        }
예제 #6
0
 private void doAccount(GncAccount account, int depth)
 {
     ProcessAccount(account, depth);
     if (depth < MaxDepth || MaxDepth == -1)
     {
         foreach (var acctChild in account.EnumChildren())
         {
             doAccount(acctChild, depth + 1);
         }
     }
 }
예제 #7
0
 public ReportAccounts(GncAccount baseacct, HttpRequest request, bool autoAddAcct, bool autoAddCol)
 {
     _baseAcct    = baseacct;
     _request     = request;
     _autoAddAcct = autoAddAcct;
     _autoAddCol  = autoAddCol;
     Table        = new ReportTable();
     _rowMap      = new Dictionary <GncAccount, ReportTable.Row>();
     _colMap      = new Dictionary <object, ReportTable.Col>();
     _colAcctName = Table.AddCol(Program.Tr.ReportTable_ColAccount, "acct_name");
 }
예제 #8
0
        private IEnumerable <object> listAccount(GncAccount acct, int depth)
        {
            yield return(new P("\u2003\u2003".Repeat(depth), new A(acct.Name)
            {
                href = Request.Url.WithQuery("Acct", acct.Path(":")).ToHref()
            }));

            foreach (var subacct in acct.EnumChildren())
            {
                yield return(listAccount(subacct, depth + 1));
            }
        }
예제 #9
0
        protected override AccountValueInfo GetAccountValue(GncAccount account, int depth)
        {
            var interval = new DateInterval(_dateFr, _dateTo);

            return(new AccountValueInfo
            {
                Amount = ConvertTo == null?account.GetTotalWithSubaccounts(interval) : account.GetTotalConverted(interval, true, ConvertTo),
                             Url = "/Trns?Acct={0}&Fr={1}&To={2}{3}".Fmt(
                                 account.Path(":").UrlEscape(),
                                 _dateFr.ToIsoStringOptimal(),
                                 _dateTo.ToIsoStringOptimal(),
                                 account.EnumChildren().Any() ? "&SubAccts=true" : ""),
            });
        }
예제 #10
0
        private void doAccount(GncAccount account, int depth)
        {
            var value = GetAccountValue(account, depth);

            SetReportAmount(Report, account, GetColumnCaption(), value.Amount, ConvertTo != null, value.Url);

            if (depth < MaxDepth || MaxDepth == -1)
            {
                foreach (var acctChild in account.EnumChildren())
                {
                    doAccount(acctChild, depth + 1);
                }
            }
        }
예제 #11
0
 private void ensureAcct(GncAccount acct)
 {
     if (_rowMap.ContainsKey(acct))
     {
         return;
     }
     if (_autoAddAcct)
     {
         AddAcct(acct);
     }
     else
     {
         throw new RTException("ReportAccounts: account \"{0}\" not yet defined and AutoAdd is disabled.".Fmt(acct.Path(":")));
     }
 }
예제 #12
0
 protected static void SetReportAmount(ReportAccounts report, GncAccount acct, object colref, GncMultiAmount amount, bool isConverted, string url = null)
 {
     if (amount.Count == 0)
     {
         report.SetValue(acct, colref, "-", ReportTable.CssClassNumber(0));
     }
     else
     {
         var val = FormatCcys(amount, isConverted, whole: true);
         report.SetValue(acct, colref, url == null ? val : new A(val)
         {
             href = url
         },
                         isConverted ? ReportTable.CssClassNumber(amount.Single().Quantity) : "");
     }
 }
예제 #13
0
        protected override void ProcessAccount(GncAccount acct, int depth)
        {
            var earliest = acct.Book.EarliestDate;

            foreach (var interval in EnumIntervals())
            {
                var bal = ConvertTo == null?acct.GetBalanceWithSubaccounts(interval.End) : acct.GetBalanceConverted(interval.End, true, ConvertTo);

                if (Negate)
                {
                    bal.NegateInplace();
                }

                SetReportAmount(Report, acct, interval, bal, ConvertTo != null);
            }
        }
예제 #14
0
        public void AddAcct(GncAccount acct)
        {
            var row = Table.AddRow();

            _rowMap.Add(acct, row);

            row.Depth = acct.Depth - _baseAcct.Depth;

            string indent = "\u2003\u2003".Repeat(acct.Depth - _baseAcct.Depth - 1);
            string name   = (acct == _baseAcct) ? (Program.Tr.ReportTable_GrandTotal.Fmt(acct.Name)) : acct.Name;

            if (acct.EnumChildren().Any())
            {
                row.SetValue(_colAcctName, new object[] { indent, new A(name)
                                                          {
                                                              class_ = "nocolor", href = _request.Url.WithQuery("Acct", acct.Path(":")).ToHref()
                                                          } });
            }
            else
            {
                row.SetValue(_colAcctName, indent + name);
            }
        }
예제 #15
0
        public IEnumerable <object> GetAccountBreadcrumbs(string acctArgName, GncAccount account)
        {
            var path = account == null ? null : account.PathAsList();

            if (account == null || path.Count == 0)
            {
                yield return new SPAN(account.Book.AccountRoot.Name)
                       {
                           class_ = "aw-breadcrumbs aw-current"
                       }
            }
            ;
            else
            {
                yield return(new A(account.Book.AccountRoot.Name)
                {
                    class_ = "aw-breadcrumbs", href = Request.Url.WithQuery(acctArgName, "").ToHref()
                });

                yield return(" : ");

                foreach (var item in path.Take(path.Count - 1))
                {
                    yield return(new A(item.Name)
                    {
                        class_ = "aw-breadcrumbs", href = Request.Url.WithQuery(acctArgName, item.Path(":")).ToHref()
                    });

                    yield return(" : ");
                }
                yield return(new SPAN(path.Last().Name)
                {
                    class_ = "aw-breadcrumbs aw-current"
                });
            }
        }
예제 #16
0
 public void SetValue(GncAccount acct, object colref, object content, string cssclass)
 {
     ensureAcct(acct);
     ensureCol(colref);
     _rowMap[acct].SetValue(_colMap[colref], content, cssclass);
 }
예제 #17
0
        private object renderFormWithResults()
        {
            _account = GetAccount("Acct");
            var amtFmt = Request.GetValidated("AmtFmt", "#,0.00");

            var statementText = "";
            var regexText     = "";
            var btnPressed    = btn.None;

            if (Request.Method == HttpMethod.Post)
            {
                statementText = Request.Post["stmt"].Value;
                if (Request.Post["rec_custom"].Value == Tr.PgReconcile.BtnReconcile)
                {
                    btnPressed = btn.ReconcileCustom;
                    regexText  = Request.Post["rec_custom_regex"].Value;
                }
                else
                {
                    btnPressed = Request.Post["rec_preset"].Value == Tr.PgReconcile.BtnReconcile ? btn.ReconcilePreset : btn.DeletePreset;
                    regexText  = Request.Post["rec_preset_regex"].Value;
                }
            }

            string        regexName = "";
            List <object> content   = new List <object>();

            if (btnPressed != btn.None)
            {
                var regexSplit = Regex.Match(regexText, @"^(.*):\s*(.*)$");
                if (!regexSplit.Success)
                {
                    throw new ValidationException(Tr.PgReconcile.Validation_ReconcilePreset, regexText, Tr.PgReconcile.Validation_ReconcilePresetMsg);
                }
                regexName = regexSplit.Groups[1].Value;
                string regexValue = regexSplit.Groups[2].Value;
                var    regex      = new Regex(regexValue);

                if (btnPressed == btn.ReconcileCustom)
                {
                    Program.Settings.ReconcileRegexes[regexName] = regexValue;
                    Program.Settings.SaveQuiet();
                }
                else if (btnPressed == btn.DeletePreset)
                {
                    Program.Settings.ReconcileRegexes.Remove(regexName);
                    Program.Settings.SaveQuiet();
                }

                if (btnPressed == btn.ReconcilePreset || btnPressed == btn.ReconcileCustom)
                {
                    // Parse lines into transactions and create the augmented statement for display
                    var statementSplits = new List <statementSplit>();
                    var linesContent    = statementText.Split('\n').Select(line =>
                    {
                        var m = regex.Match(line);

                        if (m.Success)
                        {
                            try
                            {
                                var statementSplit = new statementSplit
                                {
                                    Timestamp = parseapproxDate(m.Groups["date"]),
                                    Comment   = m.Groups["comment"].Value,
                                    Amount    = parseapproxAmount(m.Groups["dr"], m.Groups["cr"]),
                                    Balance   = parseapproxBalance(m.Groups["balance"])
                                };
                                statementSplits.Add(statementSplit);
                                try
                                {
                                    var stmtDateM = Regex.Match(statementSplit.Comment, @"(\d\d)(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(\d\d)");
                                    if (stmtDateM.Success)
                                    {
                                        var stmtDate = DateTime.ParseExact(stmtDateM.Value, "ddMMMyy", null);
                                        if (Math.Abs((stmtDate - statementSplit.Timestamp).TotalDays) < 7)
                                        {
                                            statementSplit.Timestamp = stmtDate;
                                        }
                                    }
                                }
                                catch { }     // ignore: we already have the primary date
                            }
                            catch (Exception e)
                            {
                                return(new P(line, new SPAN(" - " + e.Message)
                                {
                                    style = "color: #f00"
                                })
                                {
                                    class_ = "reconcile-match"
                                });
                            }
                        }

                        if (!m.Success)
                        {
                            return new P(line)
                            {
                                class_ = "reconcile-nomatch"
                            }
                        }
                        ;
                        else
                        {
                            return new P(line)
                            {
                                class_ = "reconcile-match"
                            }
                        };
                    }).ToArray();
                    content.Add(new H1(Tr.PgReconcile.HeaderParsedStatement));
                    content.Add(new DIV(linesContent)
                    {
                        class_ = "reconcile-parsed"
                    });
                    statementSplits.Sort((spl1, spl2) => spl1.Timestamp.CompareTo(spl2.Timestamp));

                    if (statementSplits.Count == 0)
                    {
                        content.Add(new P("Warning: no valid transactions were extracted from the statement."));
                    }
                    else
                    {
                        // Group splits in statement and in account by amount
                        var fromDate      = statementSplits.Min(tx => tx.Timestamp) - TimeSpan.FromDays(7);
                        var toDate        = statementSplits.Max(tx => tx.Timestamp) + TimeSpan.FromDays(7);
                        var accountSplits = _account.EnumSplits(false).Where(spl => !spl.IsBalsnap && spl.Transaction.DatePosted >= fromDate && spl.Transaction.DatePosted <= toDate).ToList();
                        var groups        = accountSplits
                                            .Select(spl => spl.Amount.Quantity).Concat(statementSplits.Select(tx => tx.Amount))
                                            .Distinct()
                                            .ToDictionary(
                            amount => amount,
                            amount => new
                        {
                            InAccount   = accountSplits.Where(spl => spl.Amount.Quantity == amount).ToList(),
                            InStatement = statementSplits.Where(spl => spl.Amount == amount).ToList()
                        });

                        // Match up the entries in each amount group and create a flat list
                        var entries = new List <entry>();
                        foreach (var kvp in groups)
                        {
                            var val = kvp.Value;
                            val.InAccount.Sort((spl1, spl2) => spl1.Transaction.DatePosted.CompareTo(spl2.Transaction.DatePosted));
                            val.InStatement.Sort((spl1, spl2) => spl1.Timestamp.CompareTo(spl2.Timestamp));

                            if (val.InAccount.Count == 1 && val.InStatement.Count == 1 && Math.Abs((val.InAccount[0].Transaction.DatePosted - val.InStatement[0].Timestamp).TotalDays) < 30)
                            {
                                // just treat as a match
                                entries.Add(new entry {
                                    Amount = kvp.Key, InAccount = val.InAccount[0], InStatement = val.InStatement[0]
                                });
                            }
                            else if (val.InAccount.Count == 0 || val.InStatement.Count == 0)
                            {
                                // no match
                                entries.AddRange(val.InAccount.Select(spl => new entry {
                                    Amount = kvp.Key, InAccount = spl
                                }));
                                entries.AddRange(val.InStatement.Select(spl => new entry {
                                    Amount = kvp.Key, InStatement = spl
                                }));
                            }
                            else
                            {
                                // both lists have at least one entry, one of them has at least two.

                                // First try an order-based approach
                                bool done = false;
                                if (val.InAccount.Count == val.InStatement.Count)
                                {
                                    var diffs = val.InStatement.Select(spl => spl.Timestamp)
                                                .Zip(val.InAccount.Select(spl => spl.Transaction.DatePosted), (item1, item2) => Math.Abs((item1 - item2).TotalDays));
                                    if (diffs.Max() <= 2)
                                    {
                                        entries.AddRange(val.InAccount.Zip(val.InStatement, (inAccount, inStatement) => new entry {
                                            Amount = kvp.Key, InAccount = inAccount, InStatement = inStatement
                                        }));
                                        done = true;
                                    }
                                }

                                // If didn't work fall back onto a more generic algorithm that's prone to re-order matches
                                if (!done)
                                {
                                    var bestmatchesS2A =
                                        val.InStatement.Select(inst =>
                                                               val.InAccount.Select((inacc, index) =>
                                                                                    new { Index = index, Diff = Math.Abs((inacc.Transaction.DatePosted - inst.Timestamp).TotalDays) }
                                                                                    )
                                                               .Where(v => v.Diff < 30)
                                                               .OrderBy(v => v.Diff)
                                                               .ToList()
                                                               )
                                        .Select(options => (options.Count == 0) ? -1 : (options.Count == 1)
                                            ? options[0].Index
                                            : (options[1].Diff / options[0].Diff >= 2.5) ? options[0].Index : -1)
                                        .ToList();

                                    var bestmatchesA2S =
                                        val.InAccount.Select(inacc =>
                                                             val.InStatement.Select((inst, index) =>
                                                                                    new { Index = index, Diff = Math.Abs((inacc.Transaction.DatePosted - inst.Timestamp).TotalDays) }
                                                                                    )
                                                             .Where(v => v.Diff < 30)
                                                             .OrderBy(v => v.Diff)
                                                             .ToList()
                                                             )
                                        .Select(options => (options.Count == 0) ? -1 : (options.Count == 1)
                                            ? options[0].Index
                                            : (options[1].Diff / options[0].Diff >= 2.5) ? options[0].Index : -1)
                                        .ToList();

                                    // Pick any matches that were optimal in both directions
                                    var bestmatches = bestmatchesS2A
                                                      .Select((indexAcct, indexStmt) => new { indexAcct, indexStmt })
                                                      .Where(m => m.indexAcct >= 0 && bestmatchesA2S[m.indexAcct] == m.indexStmt)
                                                      .ToList();

                                    entries.AddRange(bestmatches.Select(m => new entry {
                                        Amount = kvp.Key, InAccount = val.InAccount[m.indexAcct], InStatement = val.InStatement[m.indexStmt]
                                    }));
                                    entries.AddRange(val.InAccount.Where((spl, index) => !bestmatches.Any(m => m.indexAcct == index)).Select(spl => new entry {
                                        Amount = kvp.Key, InAccount = spl
                                    }));
                                    entries.AddRange(val.InStatement.Where((spl, index) => !bestmatches.Any(m => m.indexStmt == index)).Select(spl => new entry {
                                        Amount = kvp.Key, InStatement = spl
                                    }));
                                }
                            }
                        }

                        // Sort the entries so as to preserve the original account split ordering
                        entries.Sort((e1, e2) =>
                        {
                            if (e1.InAccount != null && e2.InAccount != null)
                            {
                                return(accountSplits.IndexOf(e1.InAccount).CompareTo(accountSplits.IndexOf(e2.InAccount)));
                            }
                            else if (e1.InAccount == null && e2.InAccount != null)
                            {
                                return(e1.InStatement.Timestamp.CompareTo(e2.InAccount.Transaction.DatePosted));
                            }
                            else if (e1.InAccount != null && e2.InAccount == null)
                            {
                                return(e1.InAccount.Transaction.DatePosted.CompareTo(e2.InStatement.Timestamp));
                            }
                            else
                            {
                                return(e1.InStatement.Timestamp.CompareTo(e2.InStatement.Timestamp));
                            }
                        });

                        // Create the table summarising the results
                        var table       = new ReportTable();
                        var colAcctDate = table.AddCol(Tr.PgReconcile.ColAcctDate);
                        var colStmtDate = table.AddCol(Tr.PgReconcile.ColStmtDate);
                        var colComment  = table.AddCol(Tr.PgReconcile.ColComment);
                        var colAmount   = table.AddCol(Tr.PgReconcile.ColAmount);
                        var colBalance  = table.AddCol(Tr.PgReconcile.ColBalance);

                        int nonMatches          = 0;
                        var lastMatchingBalance = default(DateTime);

                        foreach (var entry in entries)
                        {
                            string type;
                            if (entry.InAccount == null || entry.InStatement == null)
                            {
                                type = "none";
                            }
                            else if (Math.Abs((entry.InAccount.Transaction.DatePosted - entry.InStatement.Timestamp).TotalDays) <= 3)
                            {
                                type = "perfect";
                            }
                            else
                            {
                                type = "uncertain";
                            }
                            if (type == "none")
                            {
                                nonMatches++;
                            }
                            var row     = table.AddRow();
                            var cellcss = "reconcile-match-" + type;
                            if (entry.InAccount != null)
                            {
                                row.SetValue(colAcctDate, entry.InAccount.Transaction.DatePosted.ToShortDateString(), cellcss);
                            }
                            if (entry.InStatement != null)
                            {
                                row.SetValue(colStmtDate, entry.InStatement.Timestamp.ToShortDateString(), cellcss);
                            }
                            row.SetValue(colComment, new object[] { entry.InAccount == null ? null : new object[] { entry.InAccount.ReadableDescAndMemo, new BR() }, entry.InStatement == null ? null : entry.InStatement.Comment }, cellcss);
                            row.SetValue(colAmount, entry.Amount.ToString(amtFmt), cellcss);

                            if (entry.InAccount != null)
                            {
                                var  balance = entry.InAccount.AccountBalanceAfter;
                                var  matchingBalanceSplits = statementSplits.Where(spl => spl.Balance != null && spl.Balance.Value == balance);
                                bool matchingBalance       = false;
                                foreach (var matchingBalanceSplit in matchingBalanceSplits)
                                {
                                    // Verify that the InStatement entries up to now and the statement splits up to the matching split are exactly the same set.
                                    var balanceContributingStmtStmt = statementSplits.TakeWhile(spl => spl != matchingBalanceSplit).Concat(matchingBalanceSplit).ToList();
                                    var balanceContributingStmtAcct = entries.TakeWhile(e => e != entry).Concat(entry).Where(e => e.InStatement != null).Select(e => e.InStatement).ToList();
                                    //if (balanceContributingStmtStmt.Count != balanceContributingStmtAcct.Count)
                                    //    continue;
                                    //if (balanceContributingStmtAcct.Any(spl => !balanceContributingStmtStmt.Contains(spl)))
                                    //    continue;
                                    //if (balanceContributingStmtStmt.Any(spl => !balanceContributingStmtAcct.Contains(spl)))
                                    //    continue;
                                    matchingBalance = true;
                                    break;
                                }
                                row.SetValue(colBalance, balance.ToString(amtFmt), cellcss + (matchingBalance ? " reconcile-balance-match" : ""));
                                if (matchingBalance && entry.InAccount.Transaction.DatePosted > lastMatchingBalance)
                                {
                                    lastMatchingBalance = entry.InAccount.Transaction.DatePosted;
                                }
                            }
                        }

                        content.Add(new H1(Tr.PgReconcile.HeaderReconciledTransactions));
                        content.Add(table.GetHtml());
                        var titleBalance = (lastMatchingBalance == default(DateTime) ? "no balance match" : $"balance match {(DateTime.Today - lastMatchingBalance).TotalDays:0} days ago");
                        content.Add(new SCRIPTLiteral($"document.title = 'Reconcile – {nonMatches} non-matches remaining – {titleBalance}';"));
                    }
                }
            }

            return(new object[]
            {
                generateForm(statementText, regexName, regexText),
                (btnPressed != btn.ReconcilePreset && btnPressed != btn.ReconcileCustom)
                    ? null
                    : content
            });
        }
예제 #18
0
 protected abstract void ProcessAccount(GncAccount account, int depth);
예제 #19
0
 protected abstract AccountValueInfo GetAccountValue(GncAccount account, int depth);
예제 #20
0
        public override object GetContent()
        {
            MaxDepth = Request.GetValidated <int>("MaxDepth", -1, x => x >= 0, Tr.PgMonthly.Validation_NonNegative);
            Account  = GetAccount("Acct");
            {
                var ccys = Program.CurFile.Book.EnumCommodities();
                var ccy  = Request.GetValidated <string>("Ccy", null, x => x == null || ccys.Any(c => c.Identifier == x), Tr.PgMonthly.Validation_OneOfCommodities.Fmt(ccys.Select(c => c.Identifier).Order().JoinString(", ")));
                ConvertTo = ccy == null ? null : ccys.Single(c => c.Identifier == ccy);
            }
            InitRequestOptions();

            Report = new ReportAccounts(Account, Request, true, false);

            Report.AddCol(GetColumnCaption(), GetColumnCaption());

            doAccount(Account, 0);

            // MaxDepth UI
            var maxdepthUi = new List <object>();
            {
                maxdepthUi.Add(Tr.PgMonthly.SubAcctsDepth);
                for (int i = 0; i <= 5; i++)
                {
                    var label = i == 0 ? Tr.PgMonthly.SubAcctsNone.Translation : i.ToString();
                    if (MaxDepth == i)
                    {
                        maxdepthUi.Add(new SPAN(label)
                        {
                            class_ = "aw-current"
                        });
                    }
                    else
                    {
                        maxdepthUi.Add(new A(label)
                        {
                            href = Request.Url.WithQuery("MaxDepth", i.ToString()).ToHref()
                        });
                    }
                    maxdepthUi.Add(" · ");
                }
                if (MaxDepth == -1)
                {
                    maxdepthUi.Add(new SPAN(Tr.PgMonthly.SubAcctsAll)
                    {
                        class_ = "aw-current"
                    });
                }
                else
                {
                    maxdepthUi.Add(new A(Tr.PgMonthly.SubAcctsAll)
                    {
                        href = Request.Url.WithoutQuery("MaxDepth").ToHref()
                    });
                }
            }

            // ConvertTo currency UI
            var convertToUI = new List <object>();
            {
                var ccys = Program.CurFile.Book.EnumCommodities().OrderBy(c => c.Identifier);
                convertToUI.Add(Program.Tr.PgMonthly.ConvertTo);
                foreach (var ccy in ((GncCommodity)null).Concat(ccys))
                {
                    if (ccy != null)
                    {
                        convertToUI.Add(" · ");
                    }
                    if (ConvertTo == ccy)
                    {
                        convertToUI.Add(new SPAN(ccy?.Identifier ?? Program.Tr.PgMonthly.ConvertToNone)
                        {
                            class_ = "aw-current"
                        });
                    }
                    else if (ccy == null)
                    {
                        convertToUI.Add(new A(Program.Tr.PgMonthly.ConvertToNone)
                        {
                            href = Request.Url.WithoutQuery("Ccy").ToHref()
                        });
                    }
                    else
                    {
                        convertToUI.Add(new A(ccy)
                        {
                            href = Request.Url.WithQuery("Ccy", ccy.Identifier).ToHref()
                        });
                    }
                }
            }

            var html = new DIV(
                new P(Tr.PgMonthly.CurAccount, GetAccountBreadcrumbs("Acct", Account)),
                new P(maxdepthUi),
                new P(convertToUI),
                Report.GetHtml()
                );

            return(html);
        }
예제 #21
0
        public override object GetContent()
        {
            GroupMonths = Request.GetValidated <int>("Group", 1, x => x >= 1, Tr.PgMonthly.Validation_1OrGreater);
            int pastYears  = Request.GetValidated <int>("Years", 1, x => x >= 1, Tr.PgMonthly.Validation_1OrGreater);
            int pastGroups = (12 * pastYears) / GroupMonths + 1;

            // Default to the last N months such that we see M whole groups, where M = number of groups per year + 1
            {
                var earliest  = Program.CurFile.Book.EarliestDate;
                var toDefault = GroupMonths == 1  // exclude current month iff it's incomplete for all groupings other than single month
                    ? DateTime.Today.EndOfMonth().AssumeUtc()
                    : DateTime.Today == DateTime.Today.EndOfMonth() ? DateTime.Today : DateTime.Today.AddMonths(-1).EndOfMonth().AssumeUtc();
                var frDefault = toDefault.AddMonths(1 - pastGroups * GroupMonths).StartOfMonth().AssumeUtc();
                if (frDefault < earliest)
                {
                    frDefault = earliest;
                }
                var fy = Request.GetValidated <int>("FrYr", frDefault.Year);
                var fm = Request.GetValidated <int>("FrMo", frDefault.Month, x => x >= 1 && x <= 12, Tr.PgMonthly.Validation_Between1and12);
                var ty = Request.GetValidated <int>("ToYr", toDefault.Year, x => x >= fy, Tr.PgMonthly.Validation_NotSmallerYear.Fmt(fy));
                var tm = Request.GetValidated <int>("ToMo", toDefault.Month, x => x >= 1 && x <= 12 && (fy < ty || x >= fm), Tr.PgMonthly.Validation_Between1and12_NotSmallerMonth.Fmt(fm));
                Interval = new DateInterval(fy, fm, 1, ty, tm, DateTime.DaysInMonth(ty, tm));
            }

            MaxDepth = Request.GetValidated <int>("MaxDepth", -1, x => x >= 0, Tr.PgMonthly.Validation_NonNegative);
            Negate   = Request.GetValidated <bool>("Neg", false);
            Account  = GetAccount("Acct");

            {
                var ccys = Program.CurFile.Book.EnumCommodities();
                var ccy  = Request.GetValidated <string>("Ccy", null, x => x == null || ccys.Any(c => c.Identifier == x), Tr.PgMonthly.Validation_OneOfCommodities.Fmt(ccys.Select(c => c.Identifier).Order().JoinString(", ")));
                ConvertTo = ccy == null ? null : ccys.Single(c => c.Identifier == ccy);
            }

            Report = new ReportAccounts(Account, Request, true, false);
            foreach (var interval in EnumIntervals())
            {
                double months = interval.TotalMonths;
                string capt   = (months <= 1) ? interval.Start.ToString("MMM\nyy") : (interval.Start.ToString("MMMyy\n") + Tr.PgMonthly.MonthGroupJoiner + interval.End.ToString("\nMMMyy"));
                if (months != GroupMonths)
                {
                    capt += "\n({0:0.#} {1})".Fmt(months, Tr.PgMonthly.MoSuffix);
                }
                Report.AddCol(interval, capt);
            }
            doAccount(Account, 0);

            // MaxDepth UI
            var maxdepthUi = new List <object>();
            {
                maxdepthUi.Add(Tr.PgMonthly.SubAcctsDepth);
                for (int i = 0; i <= 5; i++)
                {
                    var label = i == 0 ? Tr.PgMonthly.SubAcctsNone.Translation : i.ToString();
                    if (MaxDepth == i)
                    {
                        maxdepthUi.Add(new SPAN(label)
                        {
                            class_ = "aw-current"
                        });
                    }
                    else
                    {
                        maxdepthUi.Add(new A(label)
                        {
                            href = Request.Url.WithQuery("MaxDepth", i.ToString()).ToHref()
                        });
                    }
                    maxdepthUi.Add(" · ");
                }
                if (MaxDepth == -1)
                {
                    maxdepthUi.Add(new SPAN(Tr.PgMonthly.SubAcctsAll)
                    {
                        class_ = "aw-current"
                    });
                }
                else
                {
                    maxdepthUi.Add(new A(Tr.PgMonthly.SubAcctsAll)
                    {
                        href = Request.Url.WithoutQuery("MaxDepth").ToHref()
                    });
                }
            }

            // Group months UI
            var groupMonthsUi = new List <object>();
            {
                groupMonthsUi.Add(Tr.PgMonthly.GroupMonthsCount);
                foreach (var len in new[] { 1, 2, 3, 4, 6, 12 })
                {
                    if (len != 1)
                    {
                        groupMonthsUi.Add(" · ");
                    }
                    if (GroupMonths == len)
                    {
                        groupMonthsUi.Add(new SPAN(len)
                        {
                            class_ = "aw-current"
                        });
                    }
                    else if (len == 1)
                    {
                        groupMonthsUi.Add(new A(len)
                        {
                            href = Request.Url.WithoutQuery("Group").ToHref()
                        });
                    }
                    else
                    {
                        groupMonthsUi.Add(new A(len)
                        {
                            href = Request.Url.WithQuery("Group", len.ToString()).ToHref()
                        });
                    }
                }
            }

            // Years to show UI
            var yearsUi = new List <object>();
            {
                yearsUi.Add(Tr.PgMonthly.PastYears);
                foreach (var yrs in Enumerable.Range(1, (int)Math.Ceiling((DateTime.Now - Program.CurFile.Book.EarliestDate).TotalDays / 365)))
                {
                    if (yrs != 1)
                    {
                        yearsUi.Add(" · ");
                    }
                    if (pastYears == yrs)
                    {
                        yearsUi.Add(new SPAN(Tr.PgMonthly.YearsMonths.Fmt(Tr.Ns, yrs, GroupMonths))
                        {
                            class_ = "aw-current"
                        });
                    }
                    else if (yrs == 1)
                    {
                        yearsUi.Add(new A(yrs)
                        {
                            href = Request.Url.WithoutQuery("Years").ToHref()
                        });
                    }
                    else
                    {
                        yearsUi.Add(new A(yrs)
                        {
                            href = Request.Url.WithQuery("Years", yrs.ToString()).ToHref()
                        });
                    }
                }
            }

            // ConvertTo currency UI
            var convertToUI = new List <object>();
            {
                var ccys = Program.CurFile.Book.EnumCommodities().OrderBy(c => c.Identifier);
                convertToUI.Add(Program.Tr.PgMonthly.ConvertTo);
                foreach (var ccy in ((GncCommodity)null).Concat(ccys))
                {
                    if (ccy != null)
                    {
                        convertToUI.Add(" · ");
                    }
                    if (ConvertTo == ccy)
                    {
                        convertToUI.Add(new SPAN(ccy?.Identifier ?? Program.Tr.PgMonthly.ConvertToNone)
                        {
                            class_ = "aw-current"
                        });
                    }
                    else if (ccy == null)
                    {
                        convertToUI.Add(new A(Program.Tr.PgMonthly.ConvertToNone)
                        {
                            href = Request.Url.WithoutQuery("Ccy").ToHref()
                        });
                    }
                    else
                    {
                        convertToUI.Add(new A(ccy)
                        {
                            href = Request.Url.WithQuery("Ccy", ccy.Identifier).ToHref()
                        });
                    }
                }
            }

            // Mode
            var modeUi = new List <object>();
            {
                if (this is PageMonthlyTotals)
                {
                    modeUi.Add(new SPAN(Tr.PgMonthly.ViewModeTotals)
                    {
                        class_ = "aw-current"
                    });
                }
                else
                {
                    modeUi.Add(new A(Tr.PgMonthly.ViewModeTotals)
                    {
                        href = "/MonthlyTotals" + Request.Url.QueryString
                    });
                }
                modeUi.Add(" · ");
                if (this is PageMonthlyBalances)
                {
                    modeUi.Add(new SPAN(Tr.PgMonthly.ViewModeBalances)
                    {
                        class_ = "aw-current"
                    });
                }
                else
                {
                    modeUi.Add(new A(Tr.PgMonthly.ViewModeBalances)
                    {
                        href = "/MonthlyBalances" + Request.Url.QueryString
                    });
                }
            }

            var html = new DIV(
                new P(Tr.PgMonthly.CurAccount, GetAccountBreadcrumbs("Acct", Account)),
                new P(maxdepthUi),
                new P(groupMonthsUi, new RawTag("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"), yearsUi),
                new P(modeUi, new RawTag("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"), convertToUI),
                Report.GetHtml()
                );

            return(html);
        }
예제 #22
0
        public override object GetContent()
        {
            var minDate = DateTime.MinValue;
            var maxDate = DateTime.MaxValue - TimeSpan.FromDays(1);
            var frDate  = Request.GetValidated("Fr", minDate);
            var toDate  = Request.GetValidated("To", maxDate, dt => dt >= frDate, Tr.PgTrns.Validation_NotBeforeFr);
            var amtFmt  = Request.GetValidated("AmtFmt", "#,0");

            _interval = new DateInterval(frDate.Date, toDate.Date + TimeSpan.FromDays(1) - TimeSpan.FromTicks(1));
            _account  = GetAccount("Acct");
            var subAccts    = _subaccts = Request.GetValidated("SubAccts", false);
            var showBalance = Request.GetValidated("ShowBal", false, val => !(val && _subaccts), Tr.PgTrns.Validation_ShowBalVsSubAccts);

            _subaccts &= _account.EnumChildren().Any();

            ReportTable table     = new ReportTable();
            var         colDate   = table.AddCol(Tr.PgTrns.ColDate);
            var         colDesc   = table.AddCol(Tr.PgTrns.ColDescription);
            var         colQty    = table.AddCol(Tr.PgTrns.ColQuantity);
            var         colCcy    = table.AddCol(Tr.PgTrns.ColCurrency);
            var         colBal    = table.AddCol(Tr.PgTrns.ColBalance);
            var         colAcct   = table.AddCol(Tr.PgTrns.ColAccount);
            var         colInBase = table.AddCol(Tr.PgTrns.ColInBaseCcy.Fmt(Program.CurFile.Book.BaseCurrencyId));

            if (!_subaccts)
            {
                table.Cols.Remove(colAcct);
            }
            if (showBalance)
            {
                table.Cols.Remove(colInBase);
            }
            else
            {
                table.Cols.Remove(colBal);
            }

            var splits = _account.EnumSplits(_subaccts);

            splits = splits.Where(split => split.Transaction.DatePosted >= frDate && split.Transaction.DatePosted <= toDate);
            splits = splits.OrderBy(split => split.Transaction);

            foreach (var split in splits)
            {
                var trn = split.Transaction;
                var row = table.AddRow();

                row.SetValue(colDate, trn.DatePosted.ToShortDateString());
                row.SetValue(colDesc, (split.Memo == null || split.Memo == "" || split.Memo == trn.Description)
                    ? trn.Description
                    : (trn.Description + " [" + split.Memo + "]"));
                row.SetValue(colQty, split.Quantity.ToString(amtFmt), ReportTable.CssClassNumber(split.Quantity));
                row.SetValue(colCcy, split.Account.Commodity.Identifier, "ccy_name ccy_" + split.Account.Commodity.Identifier.Replace(":", "_"));
                if (!showBalance)
                {
                    row.SetValue(colInBase, split.AmountConverted(Program.CurFile.Book.BaseCurrency).Quantity.ToString(amtFmt), ReportTable.CssClassNumber(split.Quantity));
                }

                if (showBalance)
                {
                    row.SetValue(colBal, split.AccountBalanceAfter.ToString(amtFmt), ReportTable.CssClassNumber(split.AccountBalanceAfter));
                }

                if (_subaccts)
                {
                    row.SetValue(colAcct, generateAcctPath(split.Account));
                }

                if (split.IsBalsnap)
                {
                    try { row.CssClass = split.AccountBalanceAfter == split.Balsnap ? "balsnap_ok" : "balsnap_wrong"; }
                    catch (GncBalsnapParseException) { row.CssClass = "balsnap_error"; }
                }
            }

            return(Ut.NewArray(
                       new P(Tr.PgMonthly.CurAccount, GetAccountBreadcrumbs("Acct", _account)),
                       new P(Tr.PgTrns.ModeCaption,
                             showBalance && !subAccts ? (object)new SPAN(Tr.PgTrns.ModeWithBalance)
            {
                class_ = "aw-current"
            } : new A(Tr.PgTrns.ModeWithBalance)
            {
                href = Request.Url.WithQuery("ShowBal", "true").WithoutQuery("SubAccts").ToHref()
            },
                             " · ",
                             !showBalance && subAccts ? (object)new SPAN(Tr.PgTrns.ModeWithSubaccts)
            {
                class_ = "aw-current"
            } : new A(Tr.PgTrns.ModeWithSubaccts)
            {
                href = Request.Url.WithoutQuery("ShowBal").WithQuery("SubAccts", "true").ToHref()
            }
                             ),
                       new P(Tr.PgTrns.RoundingCaption,
                             amtFmt == "#,0" ? (object)new SPAN(Tr.PgTrns.RoundingWhole)
            {
                class_ = "aw-current"
            } : new A(Tr.PgTrns.RoundingWhole)
            {
                href = Request.Url.WithoutQuery("AmtFmt").ToHref()
            },
                             " · ",
                             amtFmt == "#,0.00" ? (object)new SPAN(Tr.PgTrns.RoundingDecimals)
            {
                class_ = "aw-current"
            } : new A(Tr.PgTrns.RoundingDecimals)
            {
                href = Request.Url.WithQuery("AmtFmt", "#,0.00").ToHref()
            }
                             ),
                       new P(Tr.PgTrns.DateCaption,
                             (frDate == minDate && toDate == maxDate) ? (object)Tr.PgTrns.ShowingAll :
                             (frDate == minDate) ? Tr.PgTrns.ShowingOnOrBefore.FmtEnumerable(
                                 (object)Ut.NewArray <object>(new SPAN(toDate.ToShortDateString())
            {
                class_ = "filter_hilite"
            }, " (", new A(Tr.PgTrns.DateRemove)
            {
                href = Request.Url.WithoutQuery("To").ToHref()
            }, ")")
                                 ) :
                             (toDate == maxDate) ? Tr.PgTrns.ShowingOnOrAfter.FmtEnumerable(
                                 (object)Ut.NewArray <object>(new SPAN(frDate.ToShortDateString())
            {
                class_ = "filter_hilite"
            }, " (", new A(Tr.PgTrns.DateRemove)
            {
                href = Request.Url.WithoutQuery("Fr").ToHref()
            }, ")")
                                 ) :
                             Tr.PgTrns.ShowingBetween.FmtEnumerable(
                                 Ut.NewArray <object>(new SPAN(frDate.ToShortDateString())
            {
                class_ = "filter_hilite"
            }, " (", new A(Tr.PgTrns.DateRemove)
            {
                href = Request.Url.WithoutQuery("Fr").ToHref()
            }, ")"),
                                 Ut.NewArray <object>(new SPAN(toDate.ToShortDateString())
            {
                class_ = "filter_hilite"
            }, " (", new A(Tr.PgTrns.DateRemove)
            {
                href = Request.Url.WithoutQuery("To").ToHref()
            }, ")")
                                 )
                             ),
                       table.GetHtml()
                       ));
        }