public async Task UpdateSpending(long userId, SpendingUpdate spendingUpdate) { var daoGroup = await GroupService.GetGroupOfUser(userId, spendingUpdate.GroupId); if (!spendingUpdate.Debtors.All(x => daoGroup.Members.Any(m => m.UserId == x.DebtorId))) { throw new BusinessException("debtor_not_member"); } var currentSpending = await Context.Spendings .Include(x => x.Debtors) .SingleOrDefaultAsync(x => x.Id == spendingUpdate.Id); if (currentSpending == null) { throw new ResourceGoneException("spending"); } if (currentSpending.CreditorUserId != userId) { throw new ResourceForbiddenException("not_creditor"); } var oldDebts = currentSpending.Debtors .ToDictionary(debtor => debtor.DebtorUserId, debtor => debtor.Debt); var newDebts = spendingUpdate.Debtors .ToDictionary(debtor => debtor.DebtorId, debtor => debtor.Debt); await HistoryService.LogSpendingUpdate(userId, currentSpending, spendingUpdate); currentSpending.Name = spendingUpdate.Name; currentSpending.MoneyOwed = spendingUpdate.MoneySpent; Context.Debtors.RemoveRange(currentSpending.Debtors); var debtors = spendingUpdate.Debtors.Select(x => new DaoDebtor() { Spending = currentSpending, DebtorUserId = x.DebtorId, Debt = x.Debt }).ToList(); currentSpending.Debtors = debtors.ToList(); await OptimizedService.OptimizeForUpdateSpending(spendingUpdate.GroupId, userId, oldDebts, newDebts); await Context.SaveChangesAsync(); }
public async Task LogSpendingUpdate(long userId, DaoSpending oldSpending, SpendingUpdate newSpending) { dynamic historyEntry = new ExpandoObject(); // Make name delta if (oldSpending.Name != newSpending.Name) { historyEntry.oldName = oldSpending.Name; historyEntry.newName = newSpending.Name; } // Make money delta if (oldSpending.MoneyOwed != newSpending.MoneySpent) { historyEntry.oldMoney = oldSpending.MoneyOwed; historyEntry.newMoney = newSpending.MoneySpent; } // Record removed debts var removedDebts = oldSpending.Debtors .Select(x => x.DebtorUserId) .Except(newSpending.Debtors.Select(x => x.DebtorId)) .Join(oldSpending.Debtors.Select(x => (x.Debt, x.DebtorUserId)), (oldD) => oldD, (newD) => newD.DebtorUserId, (id, olddebt) => new { id = id, debt = olddebt.Debt }); if (removedDebts.Any()) { historyEntry.removedDebts = removedDebts; } // Record added debts var addedDebts = newSpending.Debtors .Select(x => x.DebtorId) .Except(oldSpending.Debtors.Select(x => x.DebtorUserId)) .Join(newSpending.Debtors.Select(x => (x.Debt, x.DebtorId)), (oldD) => oldD, (newD) => newD.DebtorId, (id, olddebt) => new { id = id, debt = olddebt.Debt }); if (addedDebts.Any()) { historyEntry.addedDebts = addedDebts; } // Record updated debts var updatedDebts = newSpending.Debtors .Select(x => (x.Debt, x.DebtorId)) .Join(oldSpending.Debtors.Select(x => (x.Debt, x.DebtorUserId)), (oldD) => oldD.DebtorId, (newD) => newD.DebtorUserId, (newdebt, olddebt) => new { id = olddebt.DebtorUserId, // should be same as newdebt.DebtorUserId! oldDebt = olddebt.Debt, newDebt = newdebt.Debt }) .Where(x => x.oldDebt != x.newDebt); if (updatedDebts.Any()) { historyEntry.updatedDebts = updatedDebts; } // Log await LogHistory(userId, oldSpending.GroupId, oldSpending.Debtors.Select(x => x.DebtorUserId).Union(newSpending.Debtors.Select(x => x.DebtorId)).ToArray(), DaoLogType.Type.UPDATE, DaoLogSubType.Type.SPENDING, historyEntry); }
public async Task UpdateSpending(SpendingUpdate spendingUpdate, long userId) { var currentUser = await DbContext.Users.FindAsync(userId); if (currentUser == null) { throw new ResourceGoneException("current_user_gone"); } var currentGroup = await DbContext.Groups .Include(x => x.Members) .SingleOrDefaultAsync(x => x.Id == spendingUpdate.GroupId); if (currentGroup == null) { throw new ResourceGoneException("group_gone"); } if (spendingUpdate.Debtors.Any(x => spendingUpdate.Debtors.Count(d => d.DebtorId == x.DebtorId) > 1)) { throw new BusinessException("duplicate_debtor_id_found"); } if (!currentGroup.Members.Any(x => x.UserId == userId)) { throw new ResourceForbiddenException("user_not_member"); } if (spendingUpdate.Debtors.Any() && !spendingUpdate.Debtors.All(x => currentGroup.Members.Any(m => m.UserId == x.DebtorId))) { throw new BusinessException("not_all_debtors_are_members"); } if (spendingUpdate.Debtors.Any() && !spendingUpdate.Debtors.All(x => DbContext.Users.Find(x.DebtorId) != null)) { throw new ResourceGoneException("debtor_gone"); } var currentSpending = await DbContext.Spendings .Include(x => x.Debtors) .SingleOrDefaultAsync(x => x.Id == spendingUpdate.Id); if (currentSpending == null) { throw new ResourceForbiddenException("spending_gone"); } if (currentSpending.CreditorUserId != userId) { throw new ResourceForbiddenException("user_not_creditor"); } using (var transaction = DbContext.Database.BeginTransaction()) { try { await _loggingService.LogForGroup(userId, currentSpending.GroupId, currentSpending); currentSpending.Name = spendingUpdate.Name; currentSpending.MoneyOwed = spendingUpdate.MoneySpent; DbContext.Debtors.RemoveRange(currentSpending.Debtors); var debtors = spendingUpdate.Debtors.Select(x => new DaoDebtor() { Spending = currentSpending, DebtorUserId = x.DebtorId, Debt = x.Debt }).ToList(); currentSpending.Debtors = debtors.ToList(); if (await DbContext.SaveChangesAsync() == 0) { throw new DatabaseException("spending_not_updated"); } transaction.Commit(); } catch { transaction.Rollback(); throw; } } }