public IDictionary <IFormattedClassification, double> AggregatePostings(IEnumerable <IPosting> postings,
                                                                                IList <IPostingClassification> postingClassifications, IErrorsAndInfos errorsAndInfos)
        {
            var result          = new Dictionary <IFormattedClassification, double>(FormattedClassificationComparer);
            var resultDrillDown = new Dictionary <string, IList <IPosting> >();

            foreach (var posting in postings)
            {
                var classifications         = postingClassifications.Where(c => PostingClassificationMatcher.DoesPostingMatchClassification(posting, c)).ToList();
                var combinedClassifications = classifications.Select(c => PostingClassificationFormatter.Format(c).CombinedClassification).Distinct().ToList();
                switch (combinedClassifications.Count)
                {
                case 0 when Math.Abs(posting.Amount) <= 70:
                    continue;

                case 0:
                    errorsAndInfos.Errors.Add($"Amount of {posting.Amount} ('{posting.Remark}') could not be classified");
                    continue;

                case > 1 when posting.Amount != 0:
                    var combinedClassification0 = PostingClassificationFormatter.Format(classifications[0]).CombinedClassification;
                    var combinedClassification1 = PostingClassificationFormatter.Format(classifications[1]).CombinedClassification;
                    errorsAndInfos.Errors.Add($"Classification of '{posting.Remark}' is ambiguous between '{combinedClassification0}' and '{combinedClassification1}'");
                    break;
                }

                var classification                  = classifications[0];
                var formattedClassification         = PostingClassificationFormatter.Format(classification);
                var formattedClassificationToString = formattedClassification.ToString() ?? "";
                if (!result.ContainsKey(formattedClassification))
                {
                    result[formattedClassification] = 0;
                    resultDrillDown[formattedClassificationToString] = new List <IPosting>();
                }

                var amount = classification.IsMonthClassification ? posting.Amount : Math.Abs(posting.Amount);
                CalculationLogger.RegisterContribution(formattedClassificationToString, amount, posting);
                result[formattedClassification] += amount;
                resultDrillDown[formattedClassificationToString].Add(posting);
            }
            return(result);
        }
Exemple #2
0
        private bool IsPostingRelevantHere(IPosting posting, IEnumerable <IPostingClassification> postingClassifications, DateTime minDate, double minAmount, out IPostingClassification classification)
        {
            classification = null;
            if (Math.Abs(posting.Amount) < minAmount)
            {
                return(false);
            }
            if (posting.Date < minDate)
            {
                return(false);
            }

            var classifications = postingClassifications.Where(c => PostingClassificationMatcher.DoesPostingMatchClassification(posting, c)).ToList();

            if (classifications.Count != 1)
            {
                return(false);
            }

            classification = classifications[0];
            return(true);
        }
Exemple #3
0
        public async Task CalculateAndShowSummaryAsync(IList <IPosting> allPostings, IList <IPostingClassification> postingClassifications)
        {
            var errorsAndInfos             = new ErrorsAndInfos();
            var fairPostings               = allPostings.Where(p => postingClassifications.FirstOrDefault(c => PostingClassificationMatcher.DoesPostingMatchClassification(p, c))?.Unfair != true).ToList();
            var pureDebitCreditAggregation = PostingAggregator.AggregatePostings(fairPostings, new List <IPostingClassification> {
                new PostingClassification {
                    Credit = false, Clue = "", Classification = "Debit"
                },
                new PostingClassification {
                    Credit = true, Clue = "", Classification = "Credit"
                }
            }, errorsAndInfos);

            if (errorsAndInfos.AnyErrors())
            {
                await DataPresenter.WriteErrorsAsync(errorsAndInfos);

                return;
            }

            var overallSumList = pureDebitCreditAggregation.Select(
                result => new TypeItemSum {
                Type = result.Key.Sign, Item = result.Key.Classification, Sum = result.Value
            }
                ).Cast <ICollectionViewSourceEntity>().ToList();
            await DataPresenter.Handlers.OverallSumsHandler.CollectionChangedAsync(overallSumList);

            errorsAndInfos = new ErrorsAndInfos();
            var detailedAggregation = PostingAggregator.AggregatePostings(allPostings, postingClassifications, errorsAndInfos).OrderBy(result => result.Key.CombinedClassification).ToList();

            if (errorsAndInfos.AnyErrors())
            {
                await DataPresenter.WriteErrorsAsync(errorsAndInfos);

                return;
            }

            if (detailedAggregation.Any())
            {
                var classificationSumList = detailedAggregation.Select(
                    result => new TypeItemSum {
                    Type = result.Key.Sign, Item = result.Key.Classification, Sum = result.Value
                }
                    ).Cast <ICollectionViewSourceEntity>().ToList();
                await DataPresenter.Handlers.ClassificationSumsHandler.CollectionChangedAsync(classificationSumList);
            }
        }
        public async Task CalculateAndShowMonthlyDeltaAsync(IList <IPosting> allPostings, IList <IPostingClassification> postingClassifications)
        {
            var fairPostings          = allPostings.Where(p => postingClassifications.FirstOrDefault(c => PostingClassificationMatcher.DoesPostingMatchClassification(p, c))?.Unfair != true).ToList();
            var minYear               = fairPostings.Min(p => p.Date.Year);
            var years                 = Enumerable.Range(minYear, DateTime.Today.Year - minYear + 1);
            var monthsClassifications = new List <IPostingClassification>();

            foreach (var year in years)
            {
                for (var month = 1; month <= 12; month++)
                {
                    var classification = postingClassifications.FirstOrDefault(c => c.Month == month && c.Year == year);
                    if (classification != null)
                    {
                        monthsClassifications.Add(classification);
                    }
                    else
                    {
                        monthsClassifications.Add(new PostingClassification {
                            Month          = month,
                            Year           = year,
                            Classification = $"Δ {year}-{month:00}"
                        });
                    }
                }
            }

            var errorsAndInfos = new ErrorsAndInfos();
            var monthlyDeltas  = PostingAggregator.AggregatePostings(fairPostings, monthsClassifications, errorsAndInfos).OrderByDescending(result => result.Key.CombinedClassification).ToList();

            if (errorsAndInfos.AnyErrors())
            {
                await DataPresenter.WriteErrorsAsync(errorsAndInfos);

                return;
            }

            var monthlyDeltasList = monthlyDeltas.Select(
                result => new TypeMonthDelta {
                Type = "Δ", Month = result.Key.Classification.Replace("Δ", "").Trim(), Delta = result.Value
            }
                ).Cast <ICollectionViewSourceEntity>().ToList();
            await DataPresenter.Handlers.MonthlyDeltasHandler.CollectionChangedAsync(monthlyDeltasList);
        }