namespace MyOffice.Services.Account { using AutoMapper; using Microsoft.Extensions.Logging; using System.Collections.Generic; using Identity; using Core; using Core.Extensions; using Data.Models.Accounts; using Data.Repositories.Account; using Data.Repositories.Currency; using Data.Repositories.Item; using Domain; using Item; using Item.Domain; public class AccountService { private ILogger _logger; private readonly IMapper _mapper; private readonly IAccountCategoryRepository _accountCategoryRepository; private readonly IAccountAccessRepository _accountAccessRepository; private readonly IAccountAccessInviteRepository _accountAccessInviteRepository; private readonly IAccountRepository _accountRepository; private readonly ICurrencyRepository _currencyRepository; private readonly IAccountAccountCategoryRepository _accountAccountCategoryRepository; private readonly IMotionRepository _motionRepository; private readonly IItemRepository _itemRepository; private readonly IItemGlobalRepository _itemGlobalRepository; private readonly ItemService _itemService; private readonly IContextProvider _contextProvider; public AccountService( ILogger logger, IMapper mapper, IAccountCategoryRepository accountCategoryRepository, IAccountAccessRepository accountAccessRepository, IAccountAccessInviteRepository accountAccessInviteRepository, IAccountRepository accountRepository, ICurrencyRepository currencyRepository, IAccountAccountCategoryRepository accountAccountCategoryRepository, IMotionRepository motionRepository, IItemRepository itemRepository, IItemGlobalRepository itemGlobalRepository, ItemService itemService, IContextProvider contextProvider ) { _logger = logger; _mapper = mapper; _accountCategoryRepository = accountCategoryRepository; _accountAccessRepository = accountAccessRepository; _accountAccessInviteRepository = accountAccessInviteRepository; _accountRepository = accountRepository; _currencyRepository = currencyRepository; _accountAccountCategoryRepository = accountAccountCategoryRepository; _motionRepository = motionRepository; _itemRepository = itemRepository; _itemGlobalRepository = itemGlobalRepository; _itemService = itemService; _contextProvider = contextProvider; } public List GetAllCategories(Guid userId) { var categories = _accountCategoryRepository.GetAll(userId); return _mapper.Map>(categories); } public Exec GetCategory(Guid userId, Guid id) { var result = new Exec(GeneralExecStatus.success); var category = _accountCategoryRepository.Get(userId, id); if (category == null) { return result.Set(GeneralExecStatus.not_found); } return result.Set(_mapper.Map(category)); } public Exec CategoryAdd(Guid userId, AccountCategoryDto input) { if (input == null) throw new ArgumentNullException(nameof(input)); var result = new Exec(GeneralExecStatus.success); var category = new AccountCategory { Id = Guid.NewGuid(), UserId = userId, Name = input.Name, }; if (!_accountCategoryRepository.Add(category)) { return result.Set(GeneralExecStatus.failure); } return result.Set(_mapper.Map(category)); } public Exec CategoryUpdate(Guid userId, Guid id, AccountCategoryDto input) { if (input == null) throw new ArgumentNullException(nameof(input)); var result = new Exec(GeneralExecStatus.success); var exists = _accountCategoryRepository.Get(userId, id); if (exists == null) { return result.Set(GeneralExecStatus.not_found); } exists.Name = input.Name; if (!_accountCategoryRepository.Update(exists)) { return result.Set(GeneralExecStatus.failure); } return result.Set(_mapper.Map(exists)); } public Exec CategoryRemove(Guid userId, Guid id) { var result = new Exec(AccountCategoryRemoveResult.success); var exists = _accountCategoryRepository.Get(userId, id); if (exists == null) { return result.Set(AccountCategoryRemoveResult.not_found); } if (exists.Accounts?.Any() == true) { return result.Set(AccountCategoryRemoveResult.accounts_exists); } if (!_accountCategoryRepository.Remove(exists)) { return result.Set(AccountCategoryRemoveResult.failure); } return result.Set(_mapper.Map(exists)); } public List GetAllAccounts(Guid userId) { var accounts = _accountRepository.GetAll(userId); return _mapper.Map>(accounts, o => o.Items.Add("UserId", userId)); } public List GetByCategory(Guid userId, Guid categoryId) { return _mapper.Map>(_accountRepository.GetByCategory(userId, categoryId)); } public List GetByCategoryDetailed(Guid userId, Guid categoryId) { return _mapper.Map>(_accountRepository.GetByCategoryDetailed(userId, categoryId)); } public Exec GetByIdDetailed(Guid userId, Guid id) { var result = new Exec(GeneralExecStatus.success); var account = _accountRepository.GetByIdDetailed(userId, id); if (account == null) { return result.Set(GeneralExecStatus.not_found); } return result.Set(_mapper.Map(account)); } public Exec AccountAdd(Guid userId, AccountAdd input) { if (input == null) throw new ArgumentNullException(nameof(input)); var result = new Exec(AccountAddStatus.success); var category = _accountCategoryRepository.Get(userId, input.CategoryId); if (category == null) { return result.Set(AccountAddStatus.category_not_found); } var currency = _currencyRepository.GetByGlobalCurrency(userId, input.CurrencyId); if (currency == null) { return result.Set(AccountAddStatus.currency_not_found); } var account = new Account { Id = Guid.NewGuid(), Name = input.Name, CurrencyGlobalId = currency.CurrencyGlobalId, OwnerId = userId, }; account.Categories = new List { new() { AccountId = account.Id, CategoryId = category.Id, } }; account.AccessRights = new List { new() { AccountId = account.Id, UserId = userId, OwnerId = userId, IsAllowManage = true, IsAllowRead = true, IsAllowWrite = true, } }; if (!_accountRepository.Add(account)) { return result.Set(AccountAddStatus.failure); } var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId); if (access != null && access.Type.ToString() != input.Type) { if (Enum.TryParse(input.Type, out var enumType)) { access.Type = enumType; _accountAccessRepository.Update(access); } } return result.Set(account); } public Exec AccountDelete(Guid userId, string id) { var result = new Exec(GeneralExecStatus.success); var account = _accountRepository.Get(userId, id.AsGuid()); if (account == null) { return result.Set(GeneralExecStatus.not_found); } if (account.Motions!.Any()) { return result.Set(GeneralExecStatus.not_found); } _accountRepository.Delete(account); result.Set(account); return result; } public Exec AccountUpdate(Guid userId, Guid accountId, AccountEdit input) { if (input == null) throw new ArgumentNullException(nameof(input)); var result = new Exec(AccountEditStatus.success); AccountCategory? category = null; if (input.CategoryId.HasValue) { category = _accountCategoryRepository.Get(userId, input.CategoryId.Value); if (category == null) { return result.Set(AccountEditStatus.category_not_found); } } var currency = _currencyRepository.GetByGlobalCurrency(userId, input.CurrencyId); if (currency == null) { return result.Set(AccountEditStatus.currency_not_found); } var account = _accountRepository.Get(userId, accountId); if (account == null) { return result.Set(AccountEditStatus.not_found); } account.Name = input.Name; if (account.CurrencyGlobalId != currency.CurrencyGlobalId) { account.CurrencyGlobalId = currency.CurrencyGlobalId; } if (category != null && account.Categories!.All(x => x.CategoryId != category.Id)) { _accountAccountCategoryRepository.Add(new AccountAccountCategory { AccountId = account.Id, CategoryId = category.Id, }); } if (!_accountRepository.Update(account)) { return result.Set(AccountEditStatus.failure); } var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId); if (access != null && access.Type.ToString() != input.Type) { if (Enum.TryParse(input.Type, out var enumType)) { access.Type = enumType; _accountAccessRepository.Update(access); } } return result.Set(account); } public Exec, GeneralExecStatus> AccountCategoryRemove(Guid userId, Guid accountId, Guid categoryId) { var result = new Exec, GeneralExecStatus>(GeneralExecStatus.success); var categories = _accountAccountCategoryRepository.Get(userId, accountId, categoryId); if (categories.Count == 0) { return result.Set(GeneralExecStatus.not_found); } foreach (var category in categories) { _accountAccountCategoryRepository.Remove(category); } return result.Set(categories); } public Exec, MotionAddStatus> MotionAdd( Guid userId, Guid accountId, MotionAddUpdate motion ) { if (motion == null) throw new ArgumentNullException(nameof(motion)); if (motion.Item == null) throw new ArgumentNullException(nameof(motion.Item)); var result = new Exec, MotionAddStatus>(MotionAddStatus.success); var account = _accountRepository.Get(userId, accountId); if (account == null) { return result.Set(MotionAddStatus.account_not_found); } var itemExec = _itemService.GetOrCreate(userId, motion.Item); if (itemExec.Status != ItemGetOrAddResult.success) { return result.Set(MotionAddStatus.failure); } var motionDb = new Motion { Id = Guid.NewGuid(), CreatedOn = DateTime.UtcNow, DateTime = motion.Date, AccountId = account.Id, ItemId = itemExec.Result!.Id, Description = motion.Description, AmountPlus = motion.Plus, AmountMinus = motion.Minus, }; if (!_motionRepository.Add(motionDb)) { return result.Set(MotionAddStatus.failure); } result.Set(new List()); result.Result!.Add(_mapper.Map(motionDb)); if (motion.AccountId.IsPresent() && motion.AmountBalancing != 0) { var accountBalancing = _accountRepository.Get(userId, motion.AccountId!.AsGuid()); if (accountBalancing != null) { var balancingName = $"+{account.Name}"; var itemBalancingExec = _itemService.GetOrCreate(userId, balancingName); var plus = motion.AmountBalancing; var minus = 0m; if (motion.Plus != 0) { plus = 0; minus = motion.AmountBalancing; } var motionBalancing = new Motion { Id = Guid.NewGuid(), CreatedOn = DateTime.UtcNow, DateTime = motion.Date, AccountId = accountBalancing.Id, ItemId = itemBalancingExec.Result!.Id, Description = motion.Description, AmountPlus = plus, AmountMinus = minus, }; if (_motionRepository.Add(motionBalancing)) { result.Result!.Add(_mapper.Map(motionBalancing)); } } } motionDb.Item = itemExec.Result!; return result; } public Exec MotionUpdate( Guid userId, Guid accountId, Guid motionId, MotionAddUpdate motion ) { if (motion == null) throw new ArgumentNullException(nameof(motion)); if (motion.Item == null) throw new ArgumentNullException(nameof(motion.Item)); var result = new Exec(MotionUpdateStatus.success); var exists = _motionRepository.Get(userId, motionId); if (exists == null || exists.AccountId != accountId) { return result.Set(MotionUpdateStatus.not_found); } var itemExec = _itemService.GetOrCreate(userId, motion.Item); if (itemExec.Status != ItemGetOrAddResult.success) { return result.Set(MotionUpdateStatus.failure); } exists.Item.Id = itemExec.Result!.Id; exists.DateTime = motion.Date; exists.Description = motion.Description; exists.AmountPlus = motion.Plus; exists.AmountMinus = motion.Minus; if (!_motionRepository.Update(exists)) { return result.Set(MotionUpdateStatus.failure); } return result.Set(_mapper.Map(exists)); } public Exec, GeneralExecStatus> GetMotions( Guid userId, Guid accountId, DateTime dateFrom, DateTime dateTo ) { var result = new Exec, GeneralExecStatus>(GeneralExecStatus.success); var account = _accountRepository.Get(userId, accountId); if (account == null) { return result.Set(GeneralExecStatus.not_found); } var motions = _motionRepository.GetByAccount(accountId, dateFrom.ToUtc(), dateTo.ToUtc()); return result.Set(motions); } public Exec MotionRemove( Guid userId, Guid accountId, Guid motionId ) { var result = new Exec(MotionDeleteStatus.success); var exists = _motionRepository.Get(userId, motionId); if (exists == null || exists.AccountId != accountId) { return result.Set(MotionDeleteStatus.not_found); } if (!_motionRepository.Remove(exists)) { return result.Set(MotionDeleteStatus.failure); } return result.Set(exists); } public List FindAccounts(Guid userId, string term) { return _accountRepository.FindAccounts(userId, term); } public Exec AccessInvite(Guid userId, Guid accountId, string email, bool isAllowWrite) { if (email == null) throw new ArgumentNullException(nameof(email)); var result = new Exec(AccessInviteStatus.success); var account = _accountRepository.Get(userId, accountId); if (account == null) { return result.Set(AccessInviteStatus.account_not_found); } if (account.AccessRights!.Any(x => x.User!.Email.EqualsIgnoreCase(email))) { return result.Set(AccessInviteStatus.access_exists); } var access = _accountAccessInviteRepository.Get(userId, email); if (access != null) { return result.Set(AccessInviteStatus.invite_exists); } var invite = new AccountAccessInvite { Id = Guid.NewGuid(), CreatedOn = DateTime.UtcNow, UserId = userId, AccountId = account.Id, Email = email.SafeTrim()!, IsAllowWrite = isAllowWrite, }; _accountAccessInviteRepository.Add(invite); return result.Set(invite); } public Exec AccessUpdate(Guid userId, Guid accountId, List accesses) { if (accesses == null) throw new ArgumentNullException(nameof(accesses)); var result = new Exec(GeneralExecStatus.success); var account = _accountRepository.Get(userId, accountId); if (account == null) { return result.Set(GeneralExecStatus.not_found); } foreach (var access in account.AccessRights!) { var newAccess = accesses.FirstOrDefault(x => x.UserId == access.UserId); if (newAccess != null && newAccess.IsAllowWrite != access.IsAllowWrite) { access.IsAllowWrite = newAccess.IsAllowWrite; _accountAccessRepository.Update(access); } } account = _accountRepository.Get(userId, accountId); return result.Set(_mapper.Map(account)); } public Exec AccessDelete(Guid userId, Guid accountId, Guid accessUserId) { var result = new Exec(GeneralExecStatus.success); var account = _accountRepository.Get(userId, accountId); if (account == null || !account.AccessRights!.Any() || account.AccessRights!.Count() == 1) { return result.Set(GeneralExecStatus.not_found); } var access = account.AccessRights!.FirstOrDefault(x => x.UserId == accessUserId); if (access == null) { return result.Set(GeneralExecStatus.not_found); } if (access.OwnerId == access.UserId) { return result.Set(GeneralExecStatus.not_found); } _accountAccessRepository.Delete(access); account = _accountRepository.Get(userId, accountId); return result.Set(_mapper.Map(account)); } public List InvitesGet(string email) { return _mapper.Map>(_accountAccessInviteRepository.GetActive(email).ToList()); } public Exec InviteAccept(Guid userId, Guid id, string name) { var result = new Exec(InviteAcceptStatus.success); var invite = _accountAccessInviteRepository.Get(id); if (invite == null || !invite.Email.EqualsIgnoreCase(_contextProvider.User.Email) || invite.AcceptedOn.HasValue || invite.RejectedOn.HasValue ) { return result.Set(InviteAcceptStatus.invite_not_found); } var account = _accountRepository.Get(invite.UserId, invite.AccountId); if (account == null) { return result.Set(InviteAcceptStatus.account_not_found); } if (account.AccessRights!.Any(x => x.UserId == userId)) { result.Set(InviteAcceptStatus.already_accepted); } else { _accountAccessRepository.Add(new AccountAccess { UserId = userId, AccountId = account.Id, OwnerId = invite.UserId, Name = name, IsAllowWrite = invite.IsAllowWrite, Type = AccountAccessTypeEnum.external, }); } invite.AcceptedOn = DateTime.UtcNow; _accountAccessInviteRepository.Update(invite); account = _accountRepository.Get(userId, account.Id); return result.Set(_mapper.Map(account)); } public Exec InviteReject(Guid id) { var result = new Exec(GeneralExecStatus.success); var invite = _accountAccessInviteRepository.Get(id); if (invite == null || !invite.Email.EqualsIgnoreCase(_contextProvider.User.Email) || invite.AcceptedOn.HasValue || invite.RejectedOn.HasValue ) { return result.Set(GeneralExecStatus.not_found); } invite.RejectedOn = DateTime.UtcNow; _accountAccessInviteRepository.Update(invite); return result.Set(_mapper.Map(invite)); } } }