Пример #1
0
        public async Task TestCreateMatched()
        {
            var files = Enumerable.Range(2010, 10).Select(o => $"output_{o}.se");
            var roots = await TestingTools.ReadSIEFiles(files);

            var allAccountTypes = roots.SelectMany(o => o.Children).OfType <AccountRecord>().GroupBy(o => o.AccountId).ToDictionary(o => o.Key, o => string.Join(" | ", o.Select(o => o.AccountName).Distinct()));

            var allVouchers = roots.SelectMany(o => o.Children).OfType <VoucherRecord>();

            var transactionsWithUndefinedAccounts = allVouchers.SelectMany(o => o.Transactions.Where(tx => !allAccountTypes.ContainsKey(tx.AccountId)).Select(tx => new { tx.CompanyName, tx.AccountId }));

            Assert.False(transactionsWithUndefinedAccounts.Any());

            var matchResult = MatchSLRResult.MatchSLRVouchers(allVouchers, VoucherRecord.DefaultIgnoreVoucherTypes);

            //All must have a single (24400|15200) transaction
            var transactionsMissingRequired = matchResult.Matches.SelectMany(o => new[] { o.SLR, o.Other })
                                              .Where(o => o.Transactions.Count(tx => TransactionMatched.RequiredAccountIds.Contains(tx.AccountId)) != 1);

            Assert.False(transactionsMissingRequired.Any());

            Assert.Equal(0, matchResult.Matches.Count(o => o.Other.TransactionsNonAdminOrCorrections.Count() > 1));

            var txs = TransactionMatched.FromVoucherMatches(matchResult, TransactionMatched.RequiredAccountIds);

            var dbg = string.Join("\n", txs.OrderBy(o => o.DateRegistered ?? LocalDate.MinIsoValue));
        }
Пример #2
0
        public async Task Test1()
        {
            var files = Enumerable.Range(2010, 10).Select(o => $"output_{o}.se");
            var roots = await TestingTools.ReadSIEFiles(files);             // new[] { "output_2016.se", "output_2017.se", "output_2018.se" });

            var allVouchers = roots.SelectMany(o => o.Children).OfType <VoucherRecord>();

            var annoyingAccountIds = new[] { 24400, 26410 }.ToList();

            IEnumerable <TransactionRecord> txFilter(IEnumerable <TransactionRecord> txs) =>
            TransactionRecord.PruneCorrections(txs).Where(t => !annoyingAccountIds.Contains(t.AccountId));

            var multi = allVouchers.Where(o => !(new[] { "FAS", "LAN", "LON", "MA", "BS", "RV" }.Contains(o.VoucherTypeCode)) &&
                                          TransactionRecord.PruneCorrections(o.Transactions).Count(t => !(new[] { '1', '2' }.Contains(t.AccountId.ToString()[0]))) > 1).ToList();

            var matchResult = MatchSLRResult.MatchSLRVouchers(allVouchers, VoucherRecord.DefaultIgnoreVoucherTypes);

            var multiAccount = matchResult.Matches.Where(mv => txFilter(mv.SLR.Transactions).Count() > 1);

            //var dbg2 = string.Join("\n\n", matchResult.Matched.OrderBy(o => o.other.Date)
            //	.Select(mv => $"{PrintVoucher(mv.slr, txFilter)}\n{PrintVoucher(mv.other, txFilter)}"));

            Assert.DoesNotContain(matchResult.Matches, o => !o.SLR.Transactions.Any());
            Assert.DoesNotContain(matchResult.NotMatchedOther, o => !o.Transactions.Any());
            Assert.DoesNotContain(matchResult.NotMatchedSLR, o => !o.Transactions.Any());

            var cc = matchResult.Matches
                     .Select(o => new
            {
                o.Other.Date,
                txFilter(o.SLR.Transactions).First().Amount,
                txFilter(o.SLR.Transactions).First().AccountId,
                txFilter(o.SLR.Transactions).First().CompanyName,
                Comment = "",
            })
                     .Concat((matchResult.NotMatchedSLR.Concat(matchResult.NotMatchedOther))
                             .Select(o => new
            {
                o.Date,
                Amount      = txFilter(o.Transactions).FirstOrDefault()?.Amount ?? 0,
                AccountId   = 0,
                CompanyName = txFilter(o.Transactions).FirstOrDefault()?.CompanyName ?? "N/A",
                Comment     = o.VoucherTypeCode,
            }));

            //cc = cc.Where(o => !string.IsNullOrEmpty(o.Comment) && o.Comment != "LON" && o.Comment != "LAN");
            cc = cc.OrderBy(o => o.Date).ToList();

            //var dbg = string.Join("\n", cc.Select(o => $"{o.Date.AtMidnight().ToDateTimeUnspecified():yyyy-MM-dd}\t{o.Comment}\t{o.Amount}\t{o.AccountId}\t{o.CompanyName}"));

            //string PrintVoucher(VoucherRecord voucher, Func<IEnumerable<TransactionRecord>, IEnumerable<TransactionRecord>> funcModifyTransactions = null)
            //{
            //	if (funcModifyTransactions == null)
            //		funcModifyTransactions = val => val;
            //	return voucher.ToString() + "\n\t" + string.Join("\n\t", funcModifyTransactions(voucher.Transactions).Select(t => t.ToString()));
            //}
        }
Пример #3
0
        public async Task TestMethod1()
        {
            Func <int, bool> accountFilter = accountId => accountId.ToString().StartsWith("45");

            //Load SBC invoices
            var fromSBC = (await Tools.LoadSBCInvoices(accountFilter)).Select(o => new SBCVariant {
                AccountId      = o.AccountId,
                Amount         = o.Amount,
                CompanyName    = o.Supplier,
                DateRegistered = NodaTime.LocalDate.FromDateTime(o.RegisteredDate),
                DateFinalized  = o.PaymentDate.HasValue ? NodaTime.LocalDate.FromDateTime(o.PaymentDate.Value) : (NodaTime.LocalDate?)null,
                Source         = o,
            }).ToList();

            //Load SIE vouchers
            List <TransactionMatched> fromSIE;
            {
                var files  = Enumerable.Range(2010, 10).Select(o => $"output_{o}.se");
                var sieDir = Tools.GetOutputFolder("SIE");
                var roots  = await SBCExtensions.ReadSIEFiles(files.Select(file => Path.Combine(sieDir, file)));

                var allVouchers = roots.SelectMany(o => o.Children).OfType <VoucherRecord>();

                var matchResult = MatchSLRResult.MatchSLRVouchers(allVouchers, VoucherRecord.DefaultIgnoreVoucherTypes);
                fromSIE = TransactionMatched.FromVoucherMatches(matchResult, TransactionMatched.RequiredAccountIds).Where(o => accountFilter(o.AccountId)).ToList();
            }

            var sbcByName = fromSBC.GroupBy(o => o.CompanyName).ToDictionary(o => o.Key, o => o.ToList());
            var sieByName = fromSIE.GroupBy(o => o.CompanyName).ToDictionary(o => o.Key, o => o.ToList());

            //Create name lookup (can be truncated in one source but not the other):
            Dictionary <string, string> nameLookup;
            {
                var(Intersection, OnlyInA, OnlyInB) = IntersectInfo(sbcByName.Keys, sieByName.Keys);
                nameLookup = Intersection.ToDictionary(o => o, o => o);

                AddLookups(OnlyInA, OnlyInB);
                AddLookups(OnlyInB, OnlyInA);
                void AddLookups(List <string> enumA, List <string> enumB)
                {
                    for (int i = enumA.Count - 1; i >= 0; i--)
                    {
                        var itemA = enumA[i];
                        var itemB = enumB.FirstOrDefault(o => o.StartsWith(itemA));
                        if (itemB != null)
                        {
                            enumB.Remove(itemB);
                            enumA.Remove(itemA);
                            nameLookup.Add(itemB, itemA);
                            nameLookup.Add(itemA, itemB);
                        }
                    }
                }

                //Non-matched: intersectInfo.OnlyInA and intersectInfo.OnlyInB
            }

            var matches = new List <(TransactionMatched, SBCVariant)>();

            foreach (var(sieName, sieList) in sieByName)
            {
                if (nameLookup.ContainsKey(sieName))
                {
                    var inSbc = sbcByName[nameLookup[sieName]].GroupBy(o => o.Amount).ToDictionary(o => o.Key, o => o.ToList());

                    //Multiple passes of the following until no more matches
                    while (true)
                    {
                        var newMatches = new List <(TransactionMatched, SBCVariant)>();
                        for (int sieIndex = sieList.Count - 1; sieIndex >= 0; sieIndex--)
                        {
                            var item = sieList[sieIndex];
                            if (inSbc.TryGetValue(item.Amount, out var sbcSameAmount))
                            {
                                var sbcSameAmountAccount = sbcSameAmount.Where(o => o.AccountId == item.AccountId);
                                //Find those with same register date (could be many)
                                //If multiple or none, take those with closest payment date.
                                //Remove match from inSbc so it can't be matched again
                                var found = new List <SBCVariant>();
                                if (item.DateRegistered is NodaTime.LocalDate dateRegistered)
                                {
                                    found = sbcSameAmountAccount.Where(o => (dateRegistered - item.DateRegistered.Value).Days <= 1).ToList();
                                }
                                else
                                {
                                    found = sbcSameAmountAccount.Where(o => (o.DateFinalized.HasValue && item.DateFinalized.HasValue) &&
                                                                       (o.DateFinalized.Value - item.DateFinalized.Value).Days <= 1).ToList();
                                }

                                if (found.Count > 1)
                                {
                                    var orderByDateDiff = found
                                                          .Where(o => (o.DateFinalized.HasValue && item.DateFinalized.HasValue))
                                                          .Select(o => new
                                    {
#pragma warning disable CS8629 // Nullable value type may be null.
                                        Diff = Math.Abs((o.DateFinalized.Value - item.DateFinalized.Value).Days),
#pragma warning restore CS8629 // Nullable value type may be null.
                                        Object = o
                                    })
                                                          .OrderBy(o => o.Diff);
                                    var minDiff = orderByDateDiff.First().Diff;
                                    if (orderByDateDiff.Count(o => o.Diff == minDiff) == 1)
                                    {
                                        found = orderByDateDiff.Take(1).Select(o => o.Object).ToList();
                                    }
                                }
                                if (found.Count == 1)
                                {
                                    newMatches.Add((item, found.Single()));
                                    sieList.RemoveAt(sieIndex);
                                    sbcSameAmount.Remove(found.First());
                                }
                            }
                        }
                        if (!newMatches.Any())
                        {
                            break;
                        }
                        matches.AddRange(newMatches);
                    }
                }
            }

            var nonmatchedSBC = sbcByName.Values.SelectMany(o => o).Except(matches.Select(o => o.Item2));
            //Remove cancelled-out pairs (same everything but opposite amount):
            var cancelling = nonmatchedSBC.GroupBy(o => $"{o.CompanyName} {Math.Abs(o.Amount)} {o.DateRegistered?.ToSimpleDateString()} {o.DateFinalized?.ToSimpleDateString()}")
                             .Where(o => o.Count() == 2 && o.Sum(o => o.Amount) == 0).SelectMany(o => o);

            nonmatchedSBC = nonmatchedSBC.Except(cancelling);

            var nonmatched = nonmatchedSBC.Concat(sieByName.Values.SelectMany(o => o).Except(matches.Select(o => o.Item1)));

            var all = matches.Select(o => o.Item1).Concat(nonmatched);

            var sss = string.Join("\n",
                                  all
                                  .OrderBy(o => o.CompanyName).ThenBy(o => o.DateRegistered)
                                  .Select(o => $"{o.CompanyName}\t{o.Amount}\t{o.AccountId}\t{o.DateRegistered?.ToSimpleDateString()}\t{o.DateFinalized?.ToSimpleDateString()}\t{(nonmatched.Contains(o) ? "X" : "")}\t{((o is SBCVariant) ? "SBC" : "")}")
                                  );
        }