protected virtual void OnDebtDealReceived( object o_debtDealsRegister, DebtDealReceivedEventData evData ) { this.UpdateTotalsPerPerson(evData); this.UpdateStatisticsPerPerson(evData); }
private void SetIdsOfPeopleWhoCreditedMaxHistoricalTotal( AchieversDoc achieversDoc, DebtDealReceivedEventData evData ) { HashSet <string> previousIds = this.currentAchieversDoc .HistoricallyCreditedMaxTotalPersonIds; var result = new HashSet <string>(1 + previousIds?.Count ?? 0); decimal initialMax = 0; if (previousIds != null && previousIds.Count > 0) { initialMax = GetDbTotalForPerson(previousIds.First()); } if (evData.Analysis.IsPayback == false) { decimal newGiverTotal = GetDbTotalForPerson(evData.Deal.GiverId); if (newGiverTotal > initialMax && newGiverTotal > DebtConstants.MaxZeroEquivalent ) { result.Add(evData.Deal.GiverId); } else if (Math.Abs(newGiverTotal - initialMax) <= DebtConstants.ValueRelativeError * initialMax && initialMax > DebtConstants.MaxZeroEquivalent ) { if (previousIds != null) { result.UnionWith(previousIds); } result.Add(evData.Deal.GiverId); } } if (result.Count == 0 && previousIds != null) { result.UnionWith(previousIds); } achieversDoc.HistoricallyCreditedMaxTotalPersonIds = result.Count > 0 ? result : null; decimal GetDbTotalForPerson(string personId) { return(this.dbc.CurrentTotalsPerPerson .Where(ct => ct.PersonId == personId) .Select(ct => ct.HistoricallyCreditedInTotal) .FirstOrDefault()); } }
private void UpdateStatisticsPerPerson( DebtDealReceivedEventData evData ) { if (evData.Analysis.IsPayback) { return; } /* * *** Notes on parallel execution *** * * Need to consider: * * parallel changes of: * + takerStatistics from CurrentStatisticsPerPerson table * + takerTotals from CurrentTotalsPerPerson table * * Result - serialize for operations with the same takerId in: * + CurrentStatisticsPerPerson table * + CurrentTotalsPerPerson table */ string takerId = evData.Deal.TakerId; PersonTotalsRow takerTotals = this.rdbc.CurrentTotalsPerPerson .Where(ct => ct.PersonId == takerId) .FirstOrDefault() ?? new PersonTotalsRow(); PersonStatisticsRow takerStatistics = this.dbc.CurrentStatisticsPerPerson .Where(s => s.PersonId == takerId) .FirstOrDefault() ?? new PersonStatisticsRow(); takerStatistics.PersonId = takerId; takerStatistics.HistoricalDebtAverageThroughCasesOfTaking = takerTotals.HistoricallyOwedInTotal / takerTotals.HistoricalCountOfCreditsTaken; if (this.dbc.CurrentStatisticsPerPerson .Count(s => s.PersonId == takerId) == 0 ) { this.dbc.CurrentStatisticsPerPerson.Add(takerStatistics); } this.dbc.SaveChanges(); }
protected virtual void OnDebtDealReceived( object o_debtDealsRegister, DebtDealReceivedEventData evData ) { var newDoc = new AchieversDoc() { Id = ObjectId.GenerateNewId() }; this.SetIdsOfPeopleWithMaxTotalDueDebts(newDoc, evData); this.SetIdsOfPeopleWhoCreditedMaxHistoricalTotal(newDoc, evData); this.SetIdsOfBestDebtorsByRepaidFractionThenTotal(newDoc, evData); if (this.IsDocumentContentDifferent(newDoc, this.currentAchieversDoc)) { this.SaveDocumentInDB(newDoc); PeopleStatisticsDocumentRow achieversDocRow = this.dbc.CurrentPeopleStatisticsDocuments .Where(sd => sd.DocumentName == AchieversDoc.Name) .FirstOrDefault(); bool shouldAddNewRow = false; if (achieversDocRow == null) { shouldAddNewRow = true; achieversDocRow = new PeopleStatisticsDocumentRow(); } achieversDocRow.DocumentName = AchieversDoc.Name; achieversDocRow.DocumentId = newDoc.Id.ToString(); if (shouldAddNewRow) { this.dbc.CurrentPeopleStatisticsDocuments .Add(achieversDocRow); } this.dbc.SaveChanges(); this.currentAchieversDoc = newDoc; } // keep execution serialized until DebtDealAdded event is handled. }
/// <summary> /// /// </summary> /// <param name="evData"></param> /// <returns>new taker totals</returns> private void UpdateTotalsPerPerson(DebtDealReceivedEventData evData) { /* * *** Notes on parallel execution *** * * Need to consider: * * parallel changes of the same * giverTotals and takerTotals red from * CurrentTotalsPerPerson table. * * * parallel attempts to add new totals * for the same giver or taker. * * Result - serialize for operations in CurrentTotalsPerPerson * table with the same giverId or takerId. */ DebtDealRow deal = evData.Deal; PersonTotalsRow giverTotals = this.dbc.CurrentTotalsPerPerson .Where(ct => ct.PersonId == deal.GiverId) .FirstOrDefault() ?? new PersonTotalsRow(); giverTotals.PersonId = deal.GiverId; PersonTotalsRow takerTotals = this.dbc.CurrentTotalsPerPerson .Where(ct => ct.PersonId == deal.TakerId) .FirstOrDefault() ?? new PersonTotalsRow(); takerTotals.PersonId = deal.TakerId; if (evData.Analysis.IsPayback) { giverTotals.DueDebtsTotal = Math.Max( 0, giverTotals.DueDebtsTotal - deal.Amount); } else { giverTotals.HistoricallyCreditedInTotal += deal.Amount; takerTotals.DueDebtsTotal += deal.Amount; takerTotals.HistoricallyOwedInTotal += deal.Amount; takerTotals.HistoricalCountOfCreditsTaken++; } if (this.dbc.CurrentTotalsPerPerson .Count(ct => ct.PersonId == deal.GiverId) == 0 ) { this.dbc.CurrentTotalsPerPerson.Add(giverTotals); } if (this.dbc.CurrentTotalsPerPerson .Count(ct => ct.PersonId == deal.TakerId) == 0 ) { this.dbc.CurrentTotalsPerPerson.Add(takerTotals); } this.dbc.SaveChanges(); }
protected virtual void OnDebtDealReceived( object o_debtDealsRegister, DebtDealReceivedEventData evData ) { /* * *** Notes on parallel execution *** * * Similar to DebtDealsRegister Add method, * consider only these cases: * * two unique involved id-s: * * same giver, same taker * * D1.giver = D2.taker, D1.taker = D2.giver * * Need to consider parallel changes of the same debt * in CurrentDebts table. * * Result - serialize always when * two deals have only two unique involved id-s. */ DebtDealRow deal = evData.Deal; DebtRow initialGiverDebtToTaker = this.dbc.CurrentDebts.Where(cd => cd.CreditorId == deal.TakerId && cd.DebtorId == deal.GiverId).FirstOrDefault(); if (initialGiverDebtToTaker == null) { initialGiverDebtToTaker = new DebtRow { CreditorId = deal.TakerId, DebtorId = deal.GiverId } } ; DebtRow initialTakerDebtToGiver = this.dbc.CurrentDebts.Where(cd => cd.CreditorId == deal.GiverId && cd.DebtorId == deal.TakerId).FirstOrDefault(); if (initialTakerDebtToGiver == null) { initialTakerDebtToGiver = new DebtRow { CreditorId = deal.GiverId, DebtorId = deal.TakerId } } ; DebtRow debtToChange; if (evData.Analysis.IsPayback) { debtToChange = initialGiverDebtToTaker; } else { debtToChange = initialTakerDebtToGiver; } decimal initialDebtAmount = debtToChange?.DebtTotal ?? 0m; decimal resultingDebtAmount; if (evData.Analysis.IsPayback) { resultingDebtAmount = Math.Max(0m, initialDebtAmount - deal.Amount); } else { resultingDebtAmount = initialDebtAmount + deal.Amount; } debtToChange.DebtTotal = resultingDebtAmount; if (resultingDebtAmount > DebtConstants.MaxZeroEquivalent) { if (this.dbc.CurrentDebts.Count(cd => cd.CreditorId == debtToChange.CreditorId && cd.DebtorId == debtToChange.DebtorId ) == 0 ) { this.dbc.CurrentDebts.Add(debtToChange); } } else { this.dbc.CurrentDebts.Remove(debtToChange); } this.dbc.SaveChanges(); }
private void SetIdsOfBestDebtorsByRepaidFractionThenTotal( AchieversDoc achieversDoc, DebtDealReceivedEventData evData ) { if (evData.Analysis.IsPayback != true) { return; } var previousIds = this.currentAchieversDoc .BestDebtorIdsByRepaidFractionThenTotal; var potentialBestDebtors = new HashSet <string>( 1 + previousIds?.Count ?? 0) { evData.Deal.GiverId }; if (previousIds != null) { potentialBestDebtors.UnionWith(previousIds); } HashSet <string> result = GetBestDebtors(potentialBestDebtors); achieversDoc.BestDebtorIdsByRepaidFractionThenTotal = result.Count > 0 ? result : null; HashSet <string> GetBestDebtors(HashSet <string> input) { if (input.Count == 0) { return(input); } var table = input.Select(id => new { Id = id, RepaidFraction = this.personDebtsRegister .GetRepaidFraction(id), HistoricallyOwedInTotal = this.personDebtsRegister .GetTotals(id).HistoricallyOwedInTotal }) .OrderByDescending(r => r.RepaidFraction ?? 0) .ThenByDescending(r => r.HistoricallyOwedInTotal); var firstBestRow = table.First(); decimal bestRFValue = firstBestRow.RepaidFraction.Value; decimal maxTotal = firstBestRow.HistoricallyOwedInTotal; if (bestRFValue <= DebtConstants.ValueRelativeError || maxTotal <= DebtConstants.MaxZeroEquivalent ) { return(new HashSet <string>(0)); } return(table .Where(r => r.RepaidFraction == bestRFValue && r.HistoricallyOwedInTotal == maxTotal) .Select(r => r.Id).ToHashSet()); } }
private void SetIdsOfPeopleWithMaxTotalDueDebts( AchieversDoc achieversDoc, DebtDealReceivedEventData evData ) { HashSet <string> previousIds = this.currentAchieversDoc.MaxDueDebtsTotalPersonIds; var result = new HashSet <string>(1 + previousIds?.Count ?? 0); decimal initialMax = 0; if (previousIds != null && previousIds.Count > 0) { initialMax = GetDbDueDebtsTotalForPerson(previousIds.First()); } if (evData.Analysis.IsPayback == false) { decimal newTakerDebtsTotal = GetDbDueDebtsTotalForPerson(evData.Deal.TakerId); if (newTakerDebtsTotal > initialMax && newTakerDebtsTotal > DebtConstants.MaxZeroEquivalent ) { result.Add(evData.Deal.TakerId); } else if (Math.Abs(newTakerDebtsTotal - initialMax) <= DebtConstants.ValueRelativeError * initialMax && initialMax > DebtConstants.MaxZeroEquivalent ) { if (previousIds != null) { result.UnionWith(previousIds); } result.Add(evData.Deal.TakerId); } } else if (previousIds != null && previousIds.Count == 1 && previousIds.Contains(evData.Deal.GiverId) ) { decimal newMaxTotal = 0; foreach (PersonTotalsRow t in this.rdbc.CurrentTotalsPerPerson .OrderByDescending(ct => ct.DueDebtsTotal) ) { if (t.DueDebtsTotal <= DebtConstants.MaxZeroEquivalent) { break; } if (result.Count == 0) { result.Add(t.PersonId); newMaxTotal = t.DueDebtsTotal; continue; } if (t.DueDebtsTotal - newMaxTotal <= DebtConstants.ValueRelativeError * newMaxTotal ) { result.Add(t.PersonId); } else { break; } } } achieversDoc.MaxDueDebtsTotalPersonIds = result.Count > 0 ? result : null; decimal GetDbDueDebtsTotalForPerson(string personId) { return(this.dbc.CurrentTotalsPerPerson .Where(ct => ct.PersonId == personId) .Select(ct => ct.DueDebtsTotal) .FirstOrDefault()); } }