Compare commits

...
10 Commits
Author SHA1 Message Date
alexandr 033b4fc247 fix 2026-06-04 09:44:23 +03:00
alexandr 4ddab34ad1 fix 2023-12-17 11:37:30 +02:00
alexandr f495fc9b8f fix 2023-12-16 21:19:41 +02:00
alexandr 2feaf50802 fix 2023-12-16 21:18:01 +02:00
alexandr 775146ee86 fix 2023-12-05 20:21:21 +02:00
alexandr 045f60e373 fix 2023-12-01 20:47:42 +02:00
alexandr a09fc7ece3 fix 2023-08-10 21:57:55 +03:00
alexandr 25184e9edc fix 2023-08-04 21:32:40 +03:00
alexandr 0d45061d81 fix 2023-08-04 20:10:57 +03:00
alexandr 70edf34cf6 fix 2023-08-04 17:53:15 +03:00
131 changed files with 7100 additions and 1519 deletions
+16
View File
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = tabs
indent_size = 4
insert_final_newline = false
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
+3
View File
@@ -359,6 +359,8 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder # Ionide (cross platform F# VS Code tools) working folder
.ionide/ .ionide/
.vscode/
/MyOffice.SPA/.vscode /MyOffice.SPA/.vscode
/MyOffice.SPA/dist/ /MyOffice.SPA/dist/
/MyOffice.SPA/src/environments/environment.ts /MyOffice.SPA/src/environments/environment.ts
@@ -382,3 +384,4 @@ package-lock.json
/MyOffice.Shared/appsettings.shared.Production.json /MyOffice.Shared/appsettings.shared.Production.json
/linux_deploy_mybank.bat /linux_deploy_mybank.bat
/linux_deploy_office.bat /linux_deploy_office.bat
linux_deploy_office_public.bat
-35
View File
@@ -1,35 +0,0 @@
namespace MyOffice.Core.Attributes;
using System;
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class LinkAttribute : Attribute
{
public LinkAttribute(Type type)
{
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class LinkFromAttribute : Attribute
{
public LinkFromAttribute(Type type)
{
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class LinkToAttribute : Attribute
{
public LinkToAttribute(Type type)
{
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class LinkWithAttribute : Attribute
{
public LinkWithAttribute(Type type)
{
}
}
+23
View File
@@ -0,0 +1,23 @@
namespace MyOffice.Core;
public class Error
{
private Exception? _exception;
public Error(string message)
{
Message = message;
}
public Error(Exception exception): this(exception.GetType().Name)
{
_exception = exception;
}
public Error(string message, Exception exception): this(message)
{
_exception = exception;
}
public string Message { get; private set;}
}
@@ -0,0 +1,18 @@
namespace MyOffice.Core.Helpers;
using MyOffice.Core.Extensions;
public class RandomizationHelper
{
public static string Generate(int length)
{
var result = "";
while(result.Length < length)
{
result += Guid.NewGuid().ToShort();
}
return result.Substring(0, length);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace MyOffice.Core;
public interface IDataModel
{
}
@@ -0,0 +1,6 @@
namespace MyOffice.Core;
public interface IDataModelDto<TSource> where TSource : IDataModel
{
}
+7 -6
View File
@@ -1,9 +1,10 @@
namespace MyOffice.Data.Models.Accounts; namespace MyOffice.Data.Models.Accounts;
using Currencies; using Currencies;
using MyOffice.Core;
using MyOffice.Data.Models.Users; using MyOffice.Data.Models.Users;
public class Account public class Account: IDataModel
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string CurrencyGlobalId { get; set; } = null!; public string CurrencyGlobalId { get; set; } = null!;
@@ -17,7 +18,7 @@ public class Account
public IEnumerable<AccountAccessInvite>? Invites { get; set; } public IEnumerable<AccountAccessInvite>? Invites { get; set; }
} }
public class AccountDetailed public class AccountDetailed: IDataModel
{ {
public Account Account { get; set; } = null!; public Account Account { get; set; } = null!;
public decimal TotalPlus { get; set; } public decimal TotalPlus { get; set; }
@@ -28,10 +29,10 @@ public class AccountDetailed
public class AccountSimple public class AccountSimple
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; } = null!;
public AccountAccessTypeEnum Type { get; set; } public AccountAccessTypeEnum Type { get; set; }
public string CurrencyName { get; set; } public string? CurrencyName { get; set; }
public string CurrencyShortName { get; set; } public string? CurrencyShortName { get; set; }
public decimal? CurrencyRate { get; set; } public decimal? CurrencyRate { get; set; }
public int? CurrencyQuantity { get; set; } public int? CurrencyQuantity { get; set; }
public decimal? TotalPlus { get; set; } public decimal? TotalPlus { get; set; }
@@ -42,7 +43,7 @@ public class AccountSimple
public class MotionTotalSimple public class MotionTotalSimple
{ {
public Guid? Id { get; set; } public Guid? Id { get; set; }
public string Name { get; set; } public string Name { get; set; } = null!;
public string CurrencyId { get; set; } = null!; public string CurrencyId { get; set; } = null!;
public string CurrencyName { get; set; } = null!; public string CurrencyName { get; set; } = null!;
public decimal CurrencyRate { get; set; } public decimal CurrencyRate { get; set; }
@@ -1,6 +1,5 @@
namespace MyOffice.Data.Models.Accounts; namespace MyOffice.Data.Models.Accounts;
using Core.Attributes;
using Users; using Users;
public class AccountAccessInvite public class AccountAccessInvite
@@ -0,0 +1,10 @@
namespace MyOffice.Data.Models.Accounts.Domain;
using Currencies;
public class AccountWithRate
{
public Account Account { get; set; } = null!;
public Currency? Currency { get; set; }
public CurrencyRate? Rate { get; set; }
}
@@ -0,0 +1,17 @@
namespace MyOffice.Data.Models.Notifications;
public enum EmailTemplateEnum
{
PasswordRestore,
}
public class EmailTemplate
{
public string Id { get; set; }
public DateTime CreatedOn { get; set; }
public string Description { get; set; }
public string Subject { get; set; }
public string Sender { get; set; }
public string SenderName { get; set; }
public string Template { get; set; }
}
@@ -0,0 +1,18 @@
namespace MyOffice.Data.Models.Verifications;
using MyOffice.Data.Models.Users;
public class VerificationCode
{
public int Id { get; set; }
public Guid UserId { get; set; }
public User? User { get; set; }
public string Code { get; set; } = null!;
public string DestinationType { get; set; } = null!;
public string Destination { get; set; } = null!;
public DateTime CreatedOn { get; set; }
public DateTime ExpiresOn { get; set; }
public DateTime? VerifiedOn { get; set; }
public string Template { get; set; } = null!;
public string? Metadata { get; set; }
}
@@ -0,0 +1,7 @@
namespace MyOffice.Data.Models.Verifications;
public enum VerificationCodeTemplateEnum
{
password_restore,
email_confirm,
}
@@ -0,0 +1,7 @@
namespace MyOffice.Data.Models.Verifications;
public enum VerificationCodeTypeEnum
{
email,
phone,
}
@@ -3,6 +3,8 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Models.Accounts; using Models.Accounts;
using MyOffice.Data.Models.Accounts.Domain;
using MyOffice.Data.Models.Currencies;
using MyOffice.Data.Repositories; using MyOffice.Data.Repositories;
public class AccountRepository : AppRepository<Account>, IAccountRepository public class AccountRepository : AppRepository<Account>, IAccountRepository
@@ -19,11 +21,15 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
{ {
return _context.Accounts! return _context.Accounts!
.Include(x => x.CurrencyGlobal) .Include(x => x.CurrencyGlobal)
.Include(x => x.Categories!.Where(x => x.Category.UserId == userId))! // categories created with current user
.Include(x => x.Categories!.Where(c => c.Category.UserId == userId))!
.ThenInclude(x => x.Category) .ThenInclude(x => x.Category)
.Include(x => x.AccessRights)! // access rights created with current user
.Include(x => x.AccessRights!.Where(a => a.OwnerId == userId))!
.ThenInclude(x => x.User) .ThenInclude(x => x.User)
.Include(x => x.Motions)! // require only one motions to check if can to delete account
.Include(x => x.Motions!.Take(1))!
// only accounts with access to current user
.Where(x => x.AccessRights!.Any(a => a.UserId == userId)) .Where(x => x.AccessRights!.Any(a => a.UserId == userId))
.ToList(); .ToList();
} }
@@ -116,42 +122,40 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
.ToList(); .ToList();
} }
public decimal GetPeriodIncome(Guid userId, DateTime from, DateTime to) public List<AccountWithRate> GetAccountsWithRate(Guid userId, DateTime date)
{ {
return _context.Accounts var accounts = _context.Accounts
.Where(x => x.AccessRights!.Any(u => u.UserId == userId)) .Where(x => x.AccessRights!.Any(a => a.UserId == userId))
.Include(x => x.CurrencyGlobal!) .ToList();
.ThenInclude(x => x.Currencies!)
.ThenInclude(x => x.CurrentRate!)
.Select(x => new {
Currency = x.CurrencyGlobal!.Currencies!.FirstOrDefault(x => x.UserId == userId),
Amount = x.Motions!
.Where(x => !x.Item!.Category!.IsInternal)
.Where(x => x.DateTime >= from && x.DateTime <= to)
.Sum(m => m.AmountPlus)
})
.ToList()
.Select(x => x.Amount * (x.Currency?.CurrentRate?.Rate * x.Currency?.CurrentRate?.Quantity))
.Sum() ?? 0;
}
public decimal GetPeriodOutcome(Guid userId, DateTime from, DateTime to) var currencyIds = accounts
.Select(x => x.CurrencyGlobalId)
.ToList();
var rates = _context.CurrencyRates
.Include(x => x.Currency)
.Where(x => x.Currency!.UserId == userId)
.Where(x => x.DateTime <= date)
.Where(x => currencyIds.Contains(x.Currency!.CurrencyGlobalId))
.GroupBy(x => x.Currency!.CurrencyGlobalId)
.Select(x => x.OrderByDescending(r => r.DateTime).First())
.ToList();
return accounts
.Select(account =>
{ {
return _context.Accounts var rate = rates.FirstOrDefault(rate => rate.Currency!.CurrencyGlobalId == account.CurrencyGlobalId);
.Where(x => x.AccessRights!.Any(u => u.UserId == userId))
.Include(x => x.CurrencyGlobal!) var result = new AccountWithRate
.ThenInclude(x => x.Currencies!) {
.ThenInclude(x => x.CurrentRate!) Account = account,
.Select(x => new { Currency = rate?.Currency,
Currency = x.CurrencyGlobal!.Currencies!.FirstOrDefault(x => x.UserId == userId), Rate = rate,
Amount = x.Motions! };
.Where(x => !x.Item!.Category!.IsInternal)
.Where(x => x.DateTime >= from && x.DateTime <= to) return result;
.Sum(m => m.AmountMinus)
}) })
.ToList() .ToList();
.Select(x => x.Amount * (x.Currency?.CurrentRate?.Rate * x.Currency?.CurrentRate?.Quantity))
.Sum() ?? 0;
} }
public List<AccountSimple> GetRestAtDate(Guid userId, DateTime date) public List<AccountSimple> GetRestAtDate(Guid userId, DateTime date)
@@ -184,8 +188,8 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
Id = x.Id, Id = x.Id,
Name = x.Name, Name = x.Name,
Type = x.Type, Type = x.Type,
CurrencyName = x.Currency!.Name, CurrencyName = x.Currency?.Name,
CurrencyShortName = x.Currency!.ShortName, CurrencyShortName = x.Currency?.ShortName,
CurrencyRate = x.CurrentRate?.Rate, CurrencyRate = x.CurrentRate?.Rate,
CurrencyQuantity = x.CurrentRate?.Quantity, CurrencyQuantity = x.CurrentRate?.Quantity,
TotalMinus = x.Minus, TotalMinus = x.Minus,
@@ -197,63 +201,112 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
public List<MotionTotalSimple> GetIncomeByCategories(Guid userId, DateTime from, DateTime to) public List<MotionTotalSimple> GetIncomeByCategories(Guid userId, DateTime from, DateTime to)
{ {
var accounts = GetAccountsWithRate(userId, to);
var accountIds = accounts.Select(x => x.Account.Id);
return _context.Motions return _context.Motions
.Where(x => x.DateTime >= from && x.DateTime <= to) .Where(x => x.DateTime >= from && x.DateTime <= to)
.Where(x => x.Item.Category!.UserId == userId) .Where(x => accountIds.Contains(x.AccountId))
.Where(x => !x.Item.Category!.IsInternal) .Where(x => !x.Item.Category!.IsInternal)
.GroupBy(x => new { x.Item.CategoryId, x.Item.Category!.Name }) .GroupBy(x => new
{
Id = x.Item.CategoryId,
Name = x.Item.Category!.Name,
IsUserCategory = x.Item.Category!.UserId == userId,
CurrencyId = x.Account.CurrencyGlobalId,
CurrencyName = x.Account.CurrencyGlobal!.Name,
})
.ToList()
.Select(x => new MotionTotalSimple .Select(x => new MotionTotalSimple
{ {
Id = x.Key.CategoryId, Id = x.Key.IsUserCategory ? x.Key.Id : userId,
Name = x.Key.Name, Name = x.Key.Name,
Amount = x.Sum(a => a.AmountPlus) CurrencyId = x.Key.CurrencyId,
}).ToList(); CurrencyName = x.Key.CurrencyName,
Amount = x.Sum(a => a.AmountPlus),
})
.ToList();
} }
public List<MotionTotalSimple> GetIncomeByCategory(Guid userId, Guid categoryId, DateTime from, DateTime to) public List<MotionTotalSimple> GetIncomeByCategory(Guid userId, Guid categoryId, DateTime from, DateTime to)
{ {
return _context.Motions return _context.Motions
.Where(x => x.DateTime >= from && x.DateTime <= to) .Where(x => x.DateTime >= from && x.DateTime <= to)
.Where(x => x.Item.Category!.UserId == userId)
.Where(x => x.Item.CategoryId == categoryId) .Where(x => x.Item.CategoryId == categoryId)
.Where(x => !x.Item.Category!.IsInternal) .Where(x => !x.Item.Category!.IsInternal)
.Where(x => x.Item.Category!.UserId == userId) .Where(x => x.Item.Category!.UserId == userId)
.GroupBy(x => new { Id = x.Item.ItemGlobalId, x.Item.ItemGlobal.Name }) .GroupBy(x => new
{
Id = x.Item.ItemGlobalId,
Name = x.Item.ItemGlobal!.Name,
CurrencyId = x.Account.CurrencyGlobalId,
CurrencyName = x.Account.CurrencyGlobal!.Name,
})
.Select(x => new MotionTotalSimple .Select(x => new MotionTotalSimple
{ {
Name = x.Key.Name, Name = x.Key.Name,
CurrencyId = x.Key.CurrencyId,
CurrencyName = x.Key.CurrencyName,
Amount = x.Sum(a => a.AmountPlus), Amount = x.Sum(a => a.AmountPlus),
}).ToList(); })
.ToList();
} }
public List<MotionTotalSimple> GetOutcomeByCategories(Guid userId, DateTime from, DateTime to) public List<MotionTotalSimple> GetOutcomeByCategories(Guid userId, DateTime from, DateTime to)
{ {
var accounts = GetAccountsWithRate(userId, to);
var accountIds = accounts.Select(x => x.Account.Id);
return _context.Motions return _context.Motions
.Where(x => x.DateTime >= from && x.DateTime <= to) .Where(x => x.DateTime >= from && x.DateTime <= to)
.Where(x => x.Item.Category!.UserId == userId) .Where(x => accountIds.Contains(x.AccountId))
.Where(x => !x.Item.Category!.IsInternal) .Where(x => !x.Item.Category!.IsInternal)
.GroupBy(x => new { x.Item.CategoryId, x.Item.Category!.Name }) .GroupBy(x => new
{
Id = x.Item.CategoryId,
Name = x.Item.Category!.Name,
IsUserCategory = x.Item.Category!.UserId == userId,
CurrencyId = x.Account.CurrencyGlobalId,
CurrencyName = x.Account.CurrencyGlobal!.Name,
})
.Select(x => new MotionTotalSimple .Select(x => new MotionTotalSimple
{ {
Id = x.Key.CategoryId, Id = x.Key.IsUserCategory ? x.Key.Id : userId,
Name = x.Key.Name, Name = x.Key.IsUserCategory ? x.Key.Name : "UnCategorized",
Amount = x.Sum(a => a.AmountMinus) //Name = x.Key.Name,
CurrencyId = x.Key.CurrencyId,
CurrencyName = x.Key.CurrencyName,
Amount = x.Sum(a => a.AmountMinus),
}).ToList(); }).ToList();
} }
public List<MotionTotalSimple> GetOutcomeByCategory(Guid userId, Guid categoryId, DateTime from, DateTime to) public List<MotionTotalSimple> GetOutcomeByCategory(Guid userId, Guid categoryId, DateTime from, DateTime to)
{ {
var accounts = GetAccountsWithRate(userId, to);
var accountIds = accounts.Select(x => x.Account.Id);
return _context.Motions return _context.Motions
.Where(x => x.DateTime >= from && x.DateTime <= to) .Where(x => x.DateTime >= from && x.DateTime <= to)
.Where(x => x.Item.CategoryId == categoryId) .Where(x => accountIds.Contains(x.AccountId))
.Where(x => !x.Item.Category!.IsInternal) .Where(x => !x.Item.Category!.IsInternal)
.Where(x => x.Item.Category!.UserId == userId) .Where(x => x.Item.CategoryId == categoryId)
.GroupBy(x => new { Id = x.Item.ItemGlobalId, x.Item.ItemGlobal.Name }) .GroupBy(x => new
{
Id = x.Item.ItemGlobalId,
Name = x.Item.ItemGlobal!.Name,
CurrencyId = x.Account.CurrencyGlobalId,
CurrencyName = x.Account.CurrencyGlobal!.Name,
})
.Select(x => new MotionTotalSimple .Select(x => new MotionTotalSimple
{ {
CurrencyId = x.Key.CurrencyId,
CurrencyName = x.Key.CurrencyName,
Name = x.Key.Name, Name = x.Key.Name,
Amount = x.Sum(a => a.AmountMinus) Amount = x.Sum(a => a.AmountMinus),
}).ToList(); })
.ToList();
} }
} }
@@ -15,8 +15,6 @@ public interface IAccountRepository
bool Remove(Account account); bool Remove(Account account);
List<Account> FindAccounts(Guid userId, string term); List<Account> FindAccounts(Guid userId, string term);
decimal GetPeriodIncome(Guid userId, DateTime from, DateTime to);
decimal GetPeriodOutcome(Guid userId, DateTime from, DateTime to);
List<AccountSimple> GetRestAtDate(Guid userId, DateTime date); List<AccountSimple> GetRestAtDate(Guid userId, DateTime date);
List<MotionTotalSimple> GetIncomeByCategories(Guid userId, DateTime from, DateTime to); List<MotionTotalSimple> GetIncomeByCategories(Guid userId, DateTime from, DateTime to);
List<MotionTotalSimple> GetIncomeByCategory(Guid userId, Guid categoryId, DateTime from, DateTime to); List<MotionTotalSimple> GetIncomeByCategory(Guid userId, Guid categoryId, DateTime from, DateTime to);
@@ -1,5 +1,6 @@
namespace MyOffice.Data.Repositories.Currency; namespace MyOffice.Data.Repositories.Currency;
using System.Xml.Schema;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Models.Currencies; using Models.Currencies;
@@ -18,18 +19,23 @@ public class CurrencyRateRepository : AppRepository<CurrencyRate>, ICurrencyRate
.ToList(); .ToList();
} }
public List<CurrencyRate> GetLastRates(List<string> currencyIds) public Dictionary<string, CurrencyRate> GetLastRates(Guid userId, List<string> currencyIds, DateTime? before = null)
{ {
return _context.CurrencyRates return _context.CurrencyRates
.Include(x => x.Currency) .Include(x => x.Currency)
.Include(x => x.Currency!.CurrencyGlobal)
.Where(x => x.DateTime <= before)
.Where(x => x.Currency!.UserId == userId)
.Where(x => currencyIds.Contains(x.Currency!.CurrencyGlobalId)) .Where(x => currencyIds.Contains(x.Currency!.CurrencyGlobalId))
.GroupBy(x => new .GroupBy(x => new
{ {
x.CurrencyId, x.Currency!.CurrencyGlobalId
//x.Currency!.Name, }, (key, g) => new
//x.Currency!.CurrencyGlobalId, {
}, (key, g) => g.OrderByDescending(x => x.DateTime).First()) key.CurrencyGlobalId,
.ToList(); rate = g.OrderByDescending(x => x.DateTime).First()
})
.ToDictionary(x => x.CurrencyGlobalId, x => x.rate);
} }
public bool AddRate(CurrencyRate currencyRate) public bool AddRate(CurrencyRate currencyRate)
@@ -5,7 +5,7 @@ using Models.Currencies;
public interface ICurrencyRateRepository public interface ICurrencyRateRepository
{ {
List<CurrencyRate> GetLastRates(Guid currencyId, DateTime? before = null, int count = 1); List<CurrencyRate> GetLastRates(Guid currencyId, DateTime? before = null, int count = 1);
List<CurrencyRate> GetLastRates(List<string> currencyIds); Dictionary<string, CurrencyRate> GetLastRates(Guid userId, List<string> currencyIds, DateTime? before = null);
bool AddRate(CurrencyRate currencyRate); bool AddRate(CurrencyRate currencyRate);
List<CurrencyRate> GetAtDate(Guid currencyId, DateTime date); List<CurrencyRate> GetAtDate(Guid currencyId, DateTime date);
} }
@@ -7,4 +7,5 @@ public interface IItemGlobalRepository
ItemGlobal? Get(Guid id); ItemGlobal? Get(Guid id);
ItemGlobal? GetByName(string name); ItemGlobal? GetByName(string name);
bool Add(ItemGlobal itemGlobal); bool Add(ItemGlobal itemGlobal);
List<ItemGlobal> GetAvailableToUser(Guid userId);
} }
@@ -6,8 +6,7 @@ public interface IItemRepository
{ {
List<Item> GetAll(Guid userId); List<Item> GetAll(Guid userId);
List<Item> GetByCategory(Guid userId, Guid categoryId); List<Item> GetByCategory(Guid userId, Guid categoryId);
Item? GetByGlobal(Guid userId, Guid globalItemId);
Item? GetByGlobal(Guid userId, Guid globalMotionId);
bool Add(Item item); bool Add(Item item);
bool Update(Item item); bool Update(Item item);
List<Item> Find(Guid userId, string term, int limit); List<Item> Find(Guid userId, string term, int limit);
@@ -1,5 +1,6 @@
namespace MyOffice.Data.Repositories.Item; namespace MyOffice.Data.Repositories.Item;
using Microsoft.EntityFrameworkCore;
using MyOffice.Data.Models.Items; using MyOffice.Data.Models.Items;
public class ItemGlobalRepository : AppRepository<ItemGlobal>, IItemGlobalRepository public class ItemGlobalRepository : AppRepository<ItemGlobal>, IItemGlobalRepository
@@ -18,4 +19,19 @@ public class ItemGlobalRepository : AppRepository<ItemGlobal>, IItemGlobalReposi
{ {
return AddBase(itemGlobal) > 0; return AddBase(itemGlobal) > 0;
} }
public List<ItemGlobal> GetAvailableToUser(Guid userId)
{
return _context.Motions
.Include(x => x.Item)
.ThenInclude(x => x.ItemGlobal)
.ThenInclude(x => x.Items)
.ThenInclude(x => x.Category)
.Where(x => x.Account.AccessRights!.Any(a => a.UserId == userId))
.Where(x => x.Item.ItemGlobal.Items.All(g => g.Category!.UserId != userId))
.Select(x => x.Item.ItemGlobal)
.GroupBy(x => x.Id)
.Select(x => x.First())
.ToList();
}
} }
@@ -25,13 +25,13 @@ public class ItemRepository : AppRepository<Item>, IItemRepository
.ToList(); .ToList();
} }
public Item? GetByGlobal(Guid userId, Guid globalMotionId) public Item? GetByGlobal(Guid userId, Guid globalItemId)
{ {
return _context.Items return _context.Items
.Include(x => x.Motions) .Include(x => x.Motions)
.Include(x => x.Category) .Include(x => x.Category)
.Include(x => x.ItemGlobal) .Include(x => x.ItemGlobal)
.FirstOrDefault(x => x.Category!.UserId == userId && x.ItemGlobalId == globalMotionId); .FirstOrDefault(x => x.Category!.UserId == userId && x.ItemGlobalId == globalItemId);
} }
public bool Update(Item item) public bool Update(Item item)
@@ -0,0 +1,12 @@
namespace MyOffice.Data.Repositories.Item;
using MyOffice.Data.Models.Notifications;
public class EmailTemplateRepository : AppRepository<EmailTemplate>, IEmailTemplateRepository
{
public EmailTemplate? Get(EmailTemplateEnum emailTemplate)
{
var emailTemplateStr = emailTemplate.ToString();
return _context.EmailTemplates.FirstOrDefault(x => x.Id == emailTemplateStr);
}
}
@@ -0,0 +1,8 @@
namespace MyOffice.Data.Repositories.Item;
using MyOffice.Data.Models.Notifications;
public interface IEmailTemplateRepository
{
EmailTemplate? Get(EmailTemplateEnum emailTemplate);
}
@@ -5,7 +5,6 @@ using DbContext;
public class RepositoryBase<TEntity> where TEntity : class public class RepositoryBase<TEntity> where TEntity : class
{ {
//TODO: Rename
protected readonly AppDbContext _context; protected readonly AppDbContext _context;
protected RepositoryBase( protected RepositoryBase(
@@ -52,7 +51,6 @@ public class RepositoryBase<TEntity> where TEntity : class
protected int AddBase(TEntity entity) protected int AddBase(TEntity entity)
{ {
//_context.Add(entity);
_context.Entry(entity).State = EntityState.Added; _context.Entry(entity).State = EntityState.Added;
var result = SaveChanges(); var result = SaveChanges();
@@ -75,7 +73,6 @@ public class RepositoryBase<TEntity> where TEntity : class
protected int UpdateBase(TEntity entity) protected int UpdateBase(TEntity entity)
{ {
//_context.Attach(entity);
_context.Entry(entity).State = EntityState.Modified; _context.Entry(entity).State = EntityState.Modified;
var result = SaveChanges(); var result = SaveChanges();
@@ -0,0 +1,11 @@
using MyOffice.Data.Models.Verifications;
namespace MyOffice.Data.Repositories.Item;
public interface IVerificationCodeRepository
{
VerificationCode? GetByCode(string code);
bool Add(VerificationCode entity);
bool Update(VerificationCode entity);
}
@@ -0,0 +1,22 @@
namespace MyOffice.Data.Repositories.Item;
using Microsoft.EntityFrameworkCore;
using MyOffice.Data.Models.Verifications;
public class VerificationCodeRepository : AppRepository<VerificationCode>, IVerificationCodeRepository
{
public bool Add(VerificationCode entity)
{
return AddBase(entity) > 0;
}
public bool Update(VerificationCode entity)
{
return UpdateBase(entity) > 0;
}
public VerificationCode? GetByCode(string code)
{
return _context.Verifications.FirstOrDefault(x => x.Code == code);
}
}
+84 -19
View File
@@ -6,6 +6,8 @@ using Data.Models.Items;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Data.Models.Users; using Data.Models.Users;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MyOffice.Data.Models.Verifications;
using MyOffice.Data.Models.Notifications;
public enum AppDbContextProvidersEnum public enum AppDbContextProvidersEnum
{ {
@@ -46,6 +48,9 @@ public class AppDbContext : DbContext
public DbSet<Item> Items { get; set; } = null!; public DbSet<Item> Items { get; set; } = null!;
public DbSet<Motion> Motions { get; set; } = null!; public DbSet<Motion> Motions { get; set; } = null!;
public DbSet<VerificationCode> Verifications { get; set; } = null!;
public DbSet<EmailTemplate> EmailTemplates { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
var noCaseCollation = _noCaseCollation[_provider]; var noCaseCollation = _noCaseCollation[_provider];
@@ -82,10 +87,29 @@ public class AppDbContext : DbContext
CurrencyCreating(modelBuilder); CurrencyCreating(modelBuilder);
AccountCreating(modelBuilder); AccountCreating(modelBuilder);
MotionsCreating(modelBuilder); MotionsCreating(modelBuilder);
VerificationsCreating(modelBuilder);
EmailTemplateCreating(modelBuilder);
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
private void VerificationsCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<VerificationCode>()
.HasKey(x => x.Id);
modelBuilder.Entity<VerificationCode>()
.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId);
}
private void EmailTemplateCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EmailTemplate>()
.HasKey(x => x.Id);
}
private void UserCreating(ModelBuilder modelBuilder) private void UserCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.Entity<User>() modelBuilder.Entity<User>()
@@ -128,24 +152,11 @@ public class AppDbContext : DbContext
private void AccountCreating(ModelBuilder modelBuilder) private void AccountCreating(ModelBuilder modelBuilder)
{ {
#region Account
modelBuilder.Entity<Account>() modelBuilder.Entity<Account>()
.HasKey(x => x.Id); .HasKey(x => x.Id);
modelBuilder.Entity<AccountAccess>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountAccessInvite>()
.HasKey(x => x.Id);
modelBuilder.Entity<Motion>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountCategory>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountAccountCategory>()
.HasKey(x => x.Id);
modelBuilder.Entity<Account>() modelBuilder.Entity<Account>()
.HasOne(x => x.CurrencyGlobal) .HasOne(x => x.CurrencyGlobal)
.WithMany(x => x.Accounts) .WithMany(x => x.Accounts)
@@ -156,6 +167,13 @@ public class AppDbContext : DbContext
.WithMany(x => x.Accounts) .WithMany(x => x.Accounts)
.HasForeignKey(x => x.OwnerId); .HasForeignKey(x => x.OwnerId);
#endregion Account
#region AccountAccess
modelBuilder.Entity<AccountAccess>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountAccess>() modelBuilder.Entity<AccountAccess>()
.HasOne(x => x.Account) .HasOne(x => x.Account)
.WithMany(x => x.AccessRights) .WithMany(x => x.AccessRights)
@@ -171,6 +189,26 @@ public class AppDbContext : DbContext
.WithMany(x => x.AccountAccessOwners) .WithMany(x => x.AccountAccessOwners)
.HasForeignKey(x => x.OwnerId); .HasForeignKey(x => x.OwnerId);
modelBuilder
.Entity<AccountAccess>()
.Property(d => d.Type)
.HasConversion(new EnumToStringConverter<AccountAccessTypeEnum>());
modelBuilder.Entity<AccountAccess>()
.HasIndex(p => new { p.AccountId, p.UserId })
.IsUnique();
modelBuilder.Entity<AccountAccess>()
.HasIndex(p => new { p.AccountId, p.OwnerId })
.IsUnique();
#endregion AccountAccess
#region AccountAccessInvite
modelBuilder.Entity<AccountAccessInvite>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountAccessInvite>() modelBuilder.Entity<AccountAccessInvite>()
.HasOne(x => x.User) .HasOne(x => x.User)
.WithMany(x => x.AccountAccessInvites) .WithMany(x => x.AccountAccessInvites)
@@ -181,16 +219,37 @@ public class AppDbContext : DbContext
.WithMany(x => x.Invites) .WithMany(x => x.Invites)
.HasForeignKey(x => x.AccountId); .HasForeignKey(x => x.AccountId);
#endregion AccountAccessInvite
#region Motion
modelBuilder.Entity<Motion>()
.HasKey(x => x.Id);
modelBuilder.Entity<Motion>() modelBuilder.Entity<Motion>()
.HasOne(x => x.Account) .HasOne(x => x.Account)
.WithMany(x => x.Motions) .WithMany(x => x.Motions)
.HasForeignKey(x => x.AccountId); .HasForeignKey(x => x.AccountId);
#endregion Motion
#region AccountCategory
modelBuilder.Entity<AccountCategory>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountCategory>() modelBuilder.Entity<AccountCategory>()
.HasOne(x => x.User) .HasOne(x => x.User)
.WithMany(x => x.AccountCategories) .WithMany(x => x.AccountCategories)
.HasForeignKey(x => x.UserId); .HasForeignKey(x => x.UserId);
#endregion AccountCategory
#region AccountAccountCategory
modelBuilder.Entity<AccountAccountCategory>()
.HasKey(x => x.Id);
modelBuilder.Entity<AccountAccountCategory>() modelBuilder.Entity<AccountAccountCategory>()
.HasOne(x => x.Account) .HasOne(x => x.Account)
.WithMany(x => x.Categories) .WithMany(x => x.Categories)
@@ -201,10 +260,16 @@ public class AppDbContext : DbContext
.WithMany(x => x.Accounts) .WithMany(x => x.Accounts)
.HasForeignKey(x => x.CategoryId); .HasForeignKey(x => x.CategoryId);
modelBuilder modelBuilder.Entity<AccountAccountCategory>()
.Entity<AccountAccess>() .HasIndex(p => new { p.AccountId, p.CategoryId })
.Property(d => d.Type) .IsUnique();
.HasConversion(new EnumToStringConverter<AccountAccessTypeEnum>());
modelBuilder.Entity<AccountAccountCategory>()
.HasOne(x => x.Category)
.WithMany(x => x.Accounts)
.HasForeignKey(x => x.CategoryId);
#endregion AccountAccountCategory
} }
private void MotionsCreating(ModelBuilder modelBuilder) private void MotionsCreating(ModelBuilder modelBuilder)
@@ -0,0 +1,729 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MyOffice.DbContext;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20230804145453_AccountAccesUnique")]
partial class AccountAccesUnique
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<bool>("IsAllowManage")
.HasColumnType("boolean");
b.Property<bool>("IsAllowRead")
.HasColumnType("boolean");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("UserId");
b.HasIndex("AccountId", "UserId")
.IsUnique();
b.ToTable("AccountAccesses");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("UserId");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("CategoryId");
b.ToTable("AccountAccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<decimal>("AmountMinus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<decimal>("AmountPlus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("ItemId")
.HasColumnType("integer");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("ItemId");
b.HasIndex("UserId");
b.ToTable("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ShortName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId");
b.ToTable("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("DefaultQuantity")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("CurrencyGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CurrencyId")
.HasColumnType("uuid");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Quantity")
.HasColumnType("integer");
b.Property<decimal>("Rate")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyRates");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.Property<Guid>("ItemGlobalId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("ItemGlobalId");
b.ToTable("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("IsInternal")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ItemCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ItemGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("IsEmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.HasColumnType("text");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("Users");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Provider")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Accounts")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("Accounts")
.HasForeignKey("OwnerId");
b.Navigation("CurrencyGlobal");
b.Navigation("Owner");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("AccessRights")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Invites")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccessInvites")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Categories")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Accounts.AccountCategory", "Category")
.WithMany("Accounts")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Category");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Motions")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.Item", "Item")
.WithMany("Motions")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", null)
.WithMany("AccountMotions")
.HasForeignKey("UserId");
b.Navigation("Account");
b.Navigation("Item");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Currencies")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyRate", "CurrentRate")
.WithMany("Currencies")
.HasForeignKey("CurrentRateId");
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("Currencies")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrencyGlobal");
b.Navigation("CurrentRate");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.Currency", "Currency")
.WithMany("Rates")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.HasOne("MyOffice.Data.Models.Items.ItemCategory", "Category")
.WithMany("Items")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.ItemGlobal", "ItemGlobal")
.WithMany("Items")
.HasForeignKey("ItemGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
b.Navigation("ItemGlobal");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("ItemCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("UserClaims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Navigation("AccessRights");
b.Navigation("Categories");
b.Navigation("Invites");
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Navigation("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Navigation("Rates");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Navigation("Accounts");
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Navigation("AccountAccess");
b.Navigation("AccountAccessInvites");
b.Navigation("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccountAccesUnique : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AccountAccesses_AccountId",
table: "AccountAccesses");
migrationBuilder.CreateIndex(
name: "IX_AccountAccesses_AccountId_UserId",
table: "AccountAccesses",
columns: new[] { "AccountId", "UserId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AccountAccesses_AccountId_UserId",
table: "AccountAccesses");
migrationBuilder.CreateIndex(
name: "IX_AccountAccesses_AccountId",
table: "AccountAccesses",
column: "AccountId");
}
}
}
@@ -0,0 +1,730 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MyOffice.DbContext;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20231217163807_acc_cat_unique")]
partial class acccatunique
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<bool>("IsAllowManage")
.HasColumnType("boolean");
b.Property<bool>("IsAllowRead")
.HasColumnType("boolean");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("UserId");
b.HasIndex("AccountId", "UserId")
.IsUnique();
b.ToTable("AccountAccesses");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("UserId");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("AccountId", "CategoryId")
.IsUnique();
b.ToTable("AccountAccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<decimal>("AmountMinus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<decimal>("AmountPlus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("ItemId")
.HasColumnType("integer");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("ItemId");
b.HasIndex("UserId");
b.ToTable("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ShortName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId");
b.ToTable("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("DefaultQuantity")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("CurrencyGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CurrencyId")
.HasColumnType("uuid");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Quantity")
.HasColumnType("integer");
b.Property<decimal>("Rate")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyRates");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.Property<Guid>("ItemGlobalId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("ItemGlobalId");
b.ToTable("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("IsInternal")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ItemCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ItemGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("IsEmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.HasColumnType("text");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("Users");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Provider")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Accounts")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("Accounts")
.HasForeignKey("OwnerId");
b.Navigation("CurrencyGlobal");
b.Navigation("Owner");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("AccessRights")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Invites")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccessInvites")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Categories")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Accounts.AccountCategory", "Category")
.WithMany("Accounts")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Category");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Motions")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.Item", "Item")
.WithMany("Motions")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", null)
.WithMany("AccountMotions")
.HasForeignKey("UserId");
b.Navigation("Account");
b.Navigation("Item");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Currencies")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyRate", "CurrentRate")
.WithMany("Currencies")
.HasForeignKey("CurrentRateId");
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("Currencies")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrencyGlobal");
b.Navigation("CurrentRate");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.Currency", "Currency")
.WithMany("Rates")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.HasOne("MyOffice.Data.Models.Items.ItemCategory", "Category")
.WithMany("Items")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.ItemGlobal", "ItemGlobal")
.WithMany("Items")
.HasForeignKey("ItemGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
b.Navigation("ItemGlobal");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("ItemCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("UserClaims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Navigation("AccessRights");
b.Navigation("Categories");
b.Navigation("Invites");
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Navigation("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Navigation("Rates");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Navigation("Accounts");
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Navigation("AccountAccess");
b.Navigation("AccountAccessInvites");
b.Navigation("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class acccatunique : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AccountAccountCategories_AccountId",
table: "AccountAccountCategories");
migrationBuilder.CreateIndex(
name: "IX_AccountAccountCategories_AccountId_CategoryId",
table: "AccountAccountCategories",
columns: new[] { "AccountId", "CategoryId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AccountAccountCategories_AccountId_CategoryId",
table: "AccountAccountCategories");
migrationBuilder.CreateIndex(
name: "IX_AccountAccountCategories_AccountId",
table: "AccountAccountCategories",
column: "AccountId");
}
}
}
@@ -0,0 +1,733 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MyOffice.DbContext;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20231217185452_acc_access_unique")]
partial class accaccessunique
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<bool>("IsAllowManage")
.HasColumnType("boolean");
b.Property<bool>("IsAllowRead")
.HasColumnType("boolean");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("UserId");
b.HasIndex("AccountId", "OwnerId")
.IsUnique();
b.HasIndex("AccountId", "UserId")
.IsUnique();
b.ToTable("AccountAccesses");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("UserId");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("AccountId", "CategoryId")
.IsUnique();
b.ToTable("AccountAccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<decimal>("AmountMinus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<decimal>("AmountPlus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("ItemId")
.HasColumnType("integer");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("ItemId");
b.HasIndex("UserId");
b.ToTable("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ShortName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId");
b.ToTable("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("DefaultQuantity")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("CurrencyGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CurrencyId")
.HasColumnType("uuid");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Quantity")
.HasColumnType("integer");
b.Property<decimal>("Rate")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyRates");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.Property<Guid>("ItemGlobalId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("ItemGlobalId");
b.ToTable("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("IsInternal")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ItemCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ItemGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("IsEmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.HasColumnType("text");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("Users");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Provider")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Accounts")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("Accounts")
.HasForeignKey("OwnerId");
b.Navigation("CurrencyGlobal");
b.Navigation("Owner");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("AccessRights")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Invites")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccessInvites")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Categories")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Accounts.AccountCategory", "Category")
.WithMany("Accounts")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Category");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Motions")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.Item", "Item")
.WithMany("Motions")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", null)
.WithMany("AccountMotions")
.HasForeignKey("UserId");
b.Navigation("Account");
b.Navigation("Item");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Currencies")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyRate", "CurrentRate")
.WithMany("Currencies")
.HasForeignKey("CurrentRateId");
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("Currencies")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrencyGlobal");
b.Navigation("CurrentRate");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.Currency", "Currency")
.WithMany("Rates")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.HasOne("MyOffice.Data.Models.Items.ItemCategory", "Category")
.WithMany("Items")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.ItemGlobal", "ItemGlobal")
.WithMany("Items")
.HasForeignKey("ItemGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
b.Navigation("ItemGlobal");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("ItemCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("UserClaims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Navigation("AccessRights");
b.Navigation("Categories");
b.Navigation("Invites");
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Navigation("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Navigation("Rates");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Navigation("Accounts");
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Navigation("AccountAccess");
b.Navigation("AccountAccessInvites");
b.Navigation("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class accaccessunique : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_AccountAccesses_AccountId_OwnerId",
table: "AccountAccesses",
columns: new[] { "AccountId", "OwnerId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AccountAccesses_AccountId_OwnerId",
table: "AccountAccesses");
}
}
}
@@ -0,0 +1,790 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MyOffice.DbContext;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20240107073407_VerificationCode")]
partial class VerificationCode
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<bool>("IsAllowManage")
.HasColumnType("boolean");
b.Property<bool>("IsAllowRead")
.HasColumnType("boolean");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("UserId");
b.HasIndex("AccountId", "OwnerId")
.IsUnique();
b.HasIndex("AccountId", "UserId")
.IsUnique();
b.ToTable("AccountAccesses");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("UserId");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("AccountId", "CategoryId")
.IsUnique();
b.ToTable("AccountAccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<decimal>("AmountMinus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<decimal>("AmountPlus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("ItemId")
.HasColumnType("integer");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("ItemId");
b.HasIndex("UserId");
b.ToTable("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ShortName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId");
b.ToTable("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("DefaultQuantity")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("CurrencyGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CurrencyId")
.HasColumnType("uuid");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Quantity")
.HasColumnType("integer");
b.Property<decimal>("Rate")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyRates");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.Property<Guid>("ItemGlobalId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("ItemGlobalId");
b.ToTable("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("IsInternal")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ItemCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ItemGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("IsEmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.HasColumnType("text");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("Users");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Provider")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims");
});
modelBuilder.Entity("MyOffice.Data.Models.Verifications.VerificationCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Destination")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DestinationType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("ExpiresOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Metadata")
.HasColumnType("text");
b.Property<string>("Template")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<DateTime?>("VerifiedOn")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Verifications");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Accounts")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("Accounts")
.HasForeignKey("OwnerId");
b.Navigation("CurrencyGlobal");
b.Navigation("Owner");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("AccessRights")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Invites")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccessInvites")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Categories")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Accounts.AccountCategory", "Category")
.WithMany("Accounts")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Category");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Motions")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.Item", "Item")
.WithMany("Motions")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", null)
.WithMany("AccountMotions")
.HasForeignKey("UserId");
b.Navigation("Account");
b.Navigation("Item");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Currencies")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyRate", "CurrentRate")
.WithMany("Currencies")
.HasForeignKey("CurrentRateId");
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("Currencies")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrencyGlobal");
b.Navigation("CurrentRate");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.Currency", "Currency")
.WithMany("Rates")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.HasOne("MyOffice.Data.Models.Items.ItemCategory", "Category")
.WithMany("Items")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.ItemGlobal", "ItemGlobal")
.WithMany("Items")
.HasForeignKey("ItemGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
b.Navigation("ItemGlobal");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("ItemCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("UserClaims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Verifications.VerificationCode", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Navigation("AccessRights");
b.Navigation("Categories");
b.Navigation("Invites");
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Navigation("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Navigation("Rates");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Navigation("Accounts");
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Navigation("AccountAccess");
b.Navigation("AccountAccessInvites");
b.Navigation("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,55 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class VerificationCode : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Verifications",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
Code = table.Column<string>(type: "text", nullable: false),
DestinationType = table.Column<string>(type: "text", nullable: false),
Destination = table.Column<string>(type: "text", nullable: false),
CreatedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
ExpiresOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
VerifiedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Template = table.Column<string>(type: "text", nullable: false),
Metadata = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Verifications", x => x.Id);
table.ForeignKey(
name: "FK_Verifications_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Verifications_UserId",
table: "Verifications",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Verifications");
}
}
}
@@ -0,0 +1,790 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MyOffice.DbContext;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20240107075033_VerificationCode2")]
partial class VerificationCode2
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<bool>("IsAllowManage")
.HasColumnType("boolean");
b.Property<bool>("IsAllowRead")
.HasColumnType("boolean");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("UserId");
b.HasIndex("AccountId", "OwnerId")
.IsUnique();
b.HasIndex("AccountId", "UserId")
.IsUnique();
b.ToTable("AccountAccesses");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("UserId");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("AccountId", "CategoryId")
.IsUnique();
b.ToTable("AccountAccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<decimal>("AmountMinus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<decimal>("AmountPlus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("ItemId")
.HasColumnType("integer");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("ItemId");
b.HasIndex("UserId");
b.ToTable("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ShortName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId");
b.ToTable("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("DefaultQuantity")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("CurrencyGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CurrencyId")
.HasColumnType("uuid");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Quantity")
.HasColumnType("integer");
b.Property<decimal>("Rate")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyRates");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.Property<Guid>("ItemGlobalId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("ItemGlobalId");
b.ToTable("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("IsInternal")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ItemCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ItemGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("IsEmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.HasColumnType("text");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("Users");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Provider")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims");
});
modelBuilder.Entity("MyOffice.Data.Models.Verifications.VerificationCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Destination")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DestinationType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("ExpiresOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Metadata")
.HasColumnType("text");
b.Property<string>("Template")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<DateTime?>("VerifiedOn")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Verifications");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Accounts")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("Accounts")
.HasForeignKey("OwnerId");
b.Navigation("CurrencyGlobal");
b.Navigation("Owner");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("AccessRights")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Invites")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccessInvites")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Categories")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Accounts.AccountCategory", "Category")
.WithMany("Accounts")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Category");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("Motions")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.Item", "Item")
.WithMany("Motions")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", null)
.WithMany("AccountMotions")
.HasForeignKey("UserId");
b.Navigation("Account");
b.Navigation("Item");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Currencies")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyRate", "CurrentRate")
.WithMany("Currencies")
.HasForeignKey("CurrentRateId");
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("Currencies")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrencyGlobal");
b.Navigation("CurrentRate");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.Currency", "Currency")
.WithMany("Rates")
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.HasOne("MyOffice.Data.Models.Items.ItemCategory", "Category")
.WithMany("Items")
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Items.ItemGlobal", "ItemGlobal")
.WithMany("Items")
.HasForeignKey("ItemGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
b.Navigation("ItemGlobal");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("ItemCategories")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "Currency")
.WithMany()
.HasForeignKey("CurrencyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Currency");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("UserClaims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Verifications.VerificationCode", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Navigation("AccessRights");
b.Navigation("Categories");
b.Navigation("Invites");
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Navigation("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Navigation("Rates");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Navigation("Accounts");
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Navigation("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Navigation("AccountAccess");
b.Navigation("AccountAccessInvites");
b.Navigation("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class VerificationCode2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -84,12 +84,16 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("OwnerId"); b.HasIndex("OwnerId");
b.HasIndex("UserId"); b.HasIndex("UserId");
b.HasIndex("AccountId", "OwnerId")
.IsUnique();
b.HasIndex("AccountId", "UserId")
.IsUnique();
b.ToTable("AccountAccesses"); b.ToTable("AccountAccesses");
}); });
@@ -146,10 +150,11 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("CategoryId"); b.HasIndex("CategoryId");
b.HasIndex("AccountId", "CategoryId")
.IsUnique();
b.ToTable("AccountAccountCategories"); b.ToTable("AccountAccountCategories");
}); });
@@ -447,6 +452,52 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.ToTable("UserClaims"); b.ToTable("UserClaims");
}); });
modelBuilder.Entity("MyOffice.Data.Models.Verifications.VerificationCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Destination")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DestinationType")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("ExpiresOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Metadata")
.HasColumnType("text");
b.Property<string>("Template")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<DateTime?>("VerifiedOn")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Verifications");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b => modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{ {
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal") b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
@@ -651,6 +702,17 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("MyOffice.Data.Models.Verifications.VerificationCode", b =>
{
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b => modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{ {
b.Navigation("AccessRights"); b.Navigation("AccessRights");
@@ -45,7 +45,7 @@ export class LockedComponent implements OnInit {
if (this.authForm.invalid) { if (this.authForm.invalid) {
return; return;
} else { } else {
this.router.navigate(['/dashboard/dashboard']); this.router.navigate(['/dashboard/rests']);
} }
} }
} }
@@ -24,14 +24,13 @@ export class ErrorInterceptor implements HttpInterceptor {
this.authenticationService.logout(); this.authenticationService.logout();
location.reload(); location.reload();
} }
let error = err.message || err.statusText;
error = !err.error ? error : err.error.message || err.error.title; let error = err.error || err.message || err.statusText;
if (!error) { if (!error) {
console.log('not parsed error', err); console.log('not parsed error', err);
} }
return throwError(error); return throwError(() => error);
//return throwError(err.error || err);
}) })
); );
} }
@@ -0,0 +1,7 @@
import { SelectableModel } from './selectable.model';
describe('SelectableModel', () => {
it('should create an instance', () => {
expect(new SelectableModel()).toBeTruthy();
});
});
@@ -0,0 +1,4 @@
export interface ISelectableModel<T> {
model: T;
selected: boolean;
}
@@ -5,50 +5,20 @@
<app-breadcrumb [title]="'MENUITEMS.DASHBOARD.LIST.INCOME'" [items]="['HOME']" [active_item]="'MENUITEMS.DASHBOARD.LIST.INCOME'"></app-breadcrumb> <app-breadcrumb [title]="'MENUITEMS.DASHBOARD.LIST.INCOME'" [items]="['HOME']" [active_item]="'MENUITEMS.DASHBOARD.LIST.INCOME'"></app-breadcrumb>
</div> </div>
<!--
<div class="row"> <div class="row">
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12">
<div class="col-4 col-sm-4 col-md-4">
<div class="card"> <div class="card">
<div class="card-body"> <div class="header">
<div class="d-flex justify-content-between"> <h2>{{'PERIOD' | translate}}</h2>
<div>
<h5>{{'BALANCE' | translate}}</h5>
</div> </div>
<h3 class="text-danger">{{dashboardModel?.balance | number: '0.2-2'}}</h3> <div class="body">
</div> <div class="example-container">
</div> <app-date-period [from]="dateFrom" [to]="dateTo" (onChange)="onChangePeriod($event)"></app-date-period>
</div>
</div>
<div class="col-4 col-sm-4 col-md-4">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>{{'DEBIT.BALANCE' | translate}}</h5>
<p class="text-muted"></p>
</div>
<h3 class="text-success">{{dashboardModel?.balanceDebit | number: '0.2-2'}}</h3>
</div>
</div>
</div>
</div>
<div class="col-4 col-sm-4 col-md-4">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>{{'CREDIT.BALANCE' | translate}}</h5>
</div>
<h3 class="text-danger">{{dashboardModel?.balanceCredit | number: '0.2-2'}}</h3>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
-->
<div class="row clearfix"> <div class="row clearfix">
<!-- Bar chart with line --> <!-- Bar chart with line -->
@@ -67,16 +37,31 @@
</div> </div>
<div class="body"> <div class="body">
<div id="chart"> <div id="chart">
<apx-chart #chart *ngIf="chartOptions" class="apex-pie-center" <apx-chart #chart *ngIf="chartOptions" class="apex-pie-center" (contextmenu)="onRightClick($event)"
[series]="chartOptions.series!" [series]="chartOptions.series!"
[chart]="chartOptions.chart!" [chart]="chartOptions.chart!"
[dataLabels]="chartOptions.dataLabels!" [dataLabels]="chartOptions.dataLabels!"
[plotOptions]="chartOptions.plotOptions!" [plotOptions]="chartOptions.plotOptions!"
[title]="chartOptions.title!" [title]="chartOptions.title!"
[legend]="chartOptions.legend!" [legend]="chartOptions.legend!">
>
</apx-chart> </apx-chart>
</div> </div>
<div class="table-responsive m-t-15" *ngIf="dashboardModel">
<table class="table align-items-center">
<tbody>
<tr *ngFor="let item of dashboardModel!.details">
<td><i class="fa fa-circle col-cyan msr-2"></i> {{item.name}}</td>
<td [ngClass]="{ 'col-green': item.value != 0, 'col-red': item.value == 0}" style="text-align: right;">{{item.valueRaw | number: '1.2'}}</td>
<td [ngClass]="{ 'col-green': item.value != 0, 'col-red': item.value == 0}" style="text-align: right;">{{item.value | number: '1.2'}}</td>
</tr>
<tr>
<td class="col-cyan">{{'TOTAL' | translate}}</td>
<td class="col-cyan" style="text-align: right;"></td>
<td class="col-cyan" style="text-align: right;">{{total | number: '1.2'}}</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -16,6 +16,7 @@ import {
ApexPlotOptions, ApexPlotOptions,
ApexLegend ApexLegend
} from "ng-apexcharts"; } from "ng-apexcharts";
import * as moment from 'moment'
// app // app
import { ApiRoutes } from '../../api-routes'; import { ApiRoutes } from '../../api-routes';
@@ -40,6 +41,9 @@ export class DashboardIncomeComponent implements OnInit {
@ViewChild("chart") chart!: ChartComponent; @ViewChild("chart") chart!: ChartComponent;
public chartOptions!: Partial<ChartOptions>; public chartOptions!: Partial<ChartOptions>;
dashboardModel?: DashboardInOutModel; dashboardModel?: DashboardInOutModel;
total?: number;
public dateFrom: Date = moment(new Date).add(-30, 'days').toDate();
public dateTo: Date = moment(new Date).add(0, 'days').toDate();
colors: string[] = ["#fd7f6f", "#7eb0d5", "#b2e061", "#bd7ebe", "#ffb55a", "#ffee65", "#beb9db", "#fdcce5", "#8bd3c7"]; colors: string[] = ["#fd7f6f", "#7eb0d5", "#b2e061", "#bd7ebe", "#ffb55a", "#ffee65", "#beb9db", "#fdcce5", "#8bd3c7"];
@@ -51,15 +55,29 @@ export class DashboardIncomeComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.loadData();
}
onChangePeriod(dates: Date[]) {
this.dateFrom = dates[0];
this.dateTo = dates[1];
this.loadData();
}
loadData(update?: boolean, category?: string) {
let params = new HttpParams() let params = new HttpParams()
.set('from', '2023-07-01') .set('from', moment(this.dateFrom).format('YYYY-MM-DD'))
.set('to', '2023-08-01'); .set('to', moment(this.dateTo).format('YYYY-MM-DD'))
.set('category', category || '')
;
this.httpClient this.httpClient
.get<DashboardInOutModel>(ApiRoutes.DashboardIncome, { params: params }) .get<DashboardInOutModel>(ApiRoutes.DashboardIncome, { params: params })
.subscribe(response => { .subscribe(response => {
this.dashboardModel = response;
var series = response.data.map(x => ({ var series = response.data.map(x => ({
x: x.name + '(' + this._decimalPipe.transform(x.value, '1.2-2') || "" + ')', x: x.name + '(' + (this._decimalPipe.transform(x.value, '1.2-2') || "") + ')',
y: x.value y: x.value
})); }));
var min = Math.min(...response.data.map(x => x.value)); var min = Math.min(...response.data.map(x => x.value));
@@ -73,10 +91,24 @@ export class DashboardIncomeComponent implements OnInit {
color: this.colors[this.colors.length - i - 1], color: this.colors[this.colors.length - i - 1],
}); });
} }
this.total = response.data.reduce((sum, current) => sum + current.value, 0);
if (update) {
this.chart.updateSeries([{
data: series
}]);
} else {
this.setChartOptions(series, ranges); this.setChartOptions(series, ranges);
}
}); });
} }
onRightClick(event?: Event) {
event?.preventDefault();
this.loadData(true);
}
private setChartOptions(data: any[], ranges: any[]) { private setChartOptions(data: any[], ranges: any[]) {
var self = this; var self = this;
@@ -88,6 +120,12 @@ export class DashboardIncomeComponent implements OnInit {
type: 'treemap', type: 'treemap',
width: 600, width: 600,
height: 600, height: 600,
events: {
click: function (event, chartContext, config) {
var data = self.dashboardModel!.data[config.dataPointIndex];
self.loadData(true, data.id);
}
}
}, },
plotOptions: { plotOptions: {
treemap: { treemap: {
@@ -19,6 +19,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row clearfix"> <div class="row clearfix">
<!-- Bar chart with line --> <!-- Bar chart with line -->
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12"> <div class="col-xl-12 col-lg-12 col-md-12 col-sm-12">
@@ -37,7 +38,7 @@
--> -->
</div> </div>
<div class="body"> <div class="body">
<apx-chart #chart *ngIf="chartOptions" class="apex-pie-center" <apx-chart #chart *ngIf="chartOptions" class="apex-pie-center" (contextmenu)="onRightClick($event)"
[series]="chartOptions.series!" [series]="chartOptions.series!"
[chart]="chartOptions.chart!" [chart]="chartOptions.chart!"
[dataLabels]="chartOptions.dataLabels!" [dataLabels]="chartOptions.dataLabels!"
@@ -45,9 +46,27 @@
[title]="chartOptions.title!" [title]="chartOptions.title!"
[legend]="chartOptions.legend!"> [legend]="chartOptions.legend!">
</apx-chart> </apx-chart>
<div class="table-responsive m-t-15" *ngIf="dashboardModel">
<table class="table align-items-center">
<tbody>
<tr *ngFor="let item of dashboardModel!.details">
<td><i class="fa fa-circle col-cyan msr-2"></i> {{item.name}}</td>
<td [ngClass]="{ 'col-green': item.value != 0, 'col-red': item.value == 0}" style="text-align: right;">{{item.valueRaw | number: '1.2'}}</td>
<td [ngClass]="{ 'col-green': item.value != 0, 'col-red': item.value == 0}" style="text-align: right;">{{item.value | number: '1.2'}}</td>
</tr>
<tr>
<td class="col-cyan">{{'TOTAL' | translate}}</td>
<td class="col-cyan" style="text-align: right;"></td>
<td class="col-cyan" style="text-align: right;">{{total | number: '1.2'}}</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</section> </section>
@@ -6,8 +6,6 @@ import { HttpClient } from '@angular/common/http';
import { HttpParams } from '@angular/common/http'; import { HttpParams } from '@angular/common/http';
import { DecimalPipe } from '@angular/common'; import { DecimalPipe } from '@angular/common';
import { UntypedFormBuilder } from '@angular/forms'; import { UntypedFormBuilder } from '@angular/forms';
import { UntypedFormGroup } from '@angular/forms';
import { Validators } from '@angular/forms';
// libs // libs
import { import {
@@ -44,7 +42,7 @@ export class DashboardOutcomeComponent implements OnInit {
@ViewChild("chart") chart!: ChartComponent; @ViewChild("chart") chart!: ChartComponent;
public chartOptions!: Partial<ChartOptions>; public chartOptions!: Partial<ChartOptions>;
dashboardModel?: DashboardInOutModel; dashboardModel?: DashboardInOutModel;
public form!: UntypedFormGroup; total?: number;
public dateFrom: Date = moment(new Date).add(-30, 'days').toDate(); public dateFrom: Date = moment(new Date).add(-30, 'days').toDate();
public dateTo: Date = moment(new Date).add(0, 'days').toDate(); public dateTo: Date = moment(new Date).add(0, 'days').toDate();
@@ -58,8 +56,6 @@ export class DashboardOutcomeComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.form = this.fb.group({
});
this.loadData(); this.loadData();
} }
@@ -69,6 +65,11 @@ export class DashboardOutcomeComponent implements OnInit {
this.loadData(); this.loadData();
} }
onRightClick(event?: Event) {
event?.preventDefault();
this.loadData(true);
}
private loadData(update?: boolean, category?: string) { private loadData(update?: boolean, category?: string) {
let params = new HttpParams() let params = new HttpParams()
.set('from', moment(this.dateFrom).format('YYYY-MM-DD')) .set('from', moment(this.dateFrom).format('YYYY-MM-DD'))
@@ -96,6 +97,9 @@ export class DashboardOutcomeComponent implements OnInit {
color: this.colors[this.colors.length - i - 1], color: this.colors[this.colors.length - i - 1],
}); });
} }
this.total = response.data.reduce((sum, current) => sum + current.value, 0);
if (update) { if (update) {
this.chart.updateSeries([{ this.chart.updateSeries([{
data: series data: series
@@ -126,7 +126,7 @@ export class HeaderComponent extends UnsubscribeOnDestroyAdapter implements OnIn
this.setUser(); this.setUser();
this.homePage = 'dashboard/dashboard'; this.homePage = 'dashboard/rests';
this.langStoreValue = localStorage.getItem('lang') as string; this.langStoreValue = localStorage.getItem('lang') as string;
const val = this.listLang.filter((x) => x.lang === this.langStoreValue); const val = this.listLang.filter((x) => x.lang === this.langStoreValue);
@@ -4,7 +4,7 @@
<div class="navbar-header"> <div class="navbar-header">
<ul class="nav navbar-nav flex-row"> <ul class="nav navbar-nav flex-row">
<li class="nav-item logo"> <li class="nav-item logo">
<a class="navbar-brand" routerLink="dashboard/dashboard"> <a class="navbar-brand" routerLink="dashboard/rests">
<img src="assets/images/logo.png" alt=""/> <img src="assets/images/logo.png" alt=""/>
<span class="logo-name">ase.com.ua</span> <span class="logo-name">ase.com.ua</span>
</a> </a>
@@ -7,6 +7,7 @@ export interface AccountModel {
currencyId?: string, currencyId?: string,
currencyName?: string, currencyName?: string,
type?: string, type?: string,
allowWrite: boolean,
allowDelete: boolean, allowDelete: boolean,
allowManage: boolean, allowManage: boolean,
categories?: AccountCategoryModel[], categories?: AccountCategoryModel[],
@@ -26,10 +26,13 @@ export interface DashboardRestModel {
export interface DashboardInOutModel { export interface DashboardInOutModel {
data: DashboardInOutItemModel[]; data: DashboardInOutItemModel[];
details: DashboardInOutItemModel[];
} }
export interface DashboardInOutItemModel { export interface DashboardInOutItemModel {
id: string; id: string;
currency: string;
name: string; name: string;
value: number; value: number;
valueRaw: number;
} }
@@ -1,7 +1,15 @@
account-motion:nth-child(even) { :host-context(body.dark)account-motion:nth-child(even) {
filter: brightness(1) filter: brightness(1);
} }
account-motion:nth-child(odd) { :host-context(body.dark)account-motion:nth-child(odd) {
filter: brightness(1.75) filter: brightness(1.75);
}
:host-context(body.light)account-motion:nth-child(even) {
filter: brightness(0.75);
}
:host-context(body.light)account-motion:nth-child(odd) {
filter: brightness(1);
} }
@@ -47,6 +47,10 @@ export class AccountComponent {
} }
ngOnInit(): void { ngOnInit(): void {
/*this.activatedRoute.params.subscribe(params => {
console.log(params);
this.loadMotions();
});*/
this.loadMotions(); this.loadMotions();
this.reloadEvent.subscribe(x => { this.reloadEvent.subscribe(x => {
@@ -86,13 +90,11 @@ export class AccountComponent {
url += '?from=' + moment(this.dateFrom).format('yyyy-MM-DD'); url += '?from=' + moment(this.dateFrom).format('yyyy-MM-DD');
url += '&to=' + moment(this.dateTo).format('yyyy-MM-DD'); url += '&to=' + moment(this.dateTo).format('yyyy-MM-DD');
this.activatedRoute.params.subscribe(params => {
this.httpClient this.httpClient
.get<MotionModel[]>(url) .get<MotionModel[]>(url)
.subscribe(data => { .subscribe(data => {
this.motions = data; this.motions = data;
}); });
});
} }
afterExpand() { afterExpand() {
@@ -129,6 +129,7 @@ export class MotionComponent {
this.setInProgress(); this.setInProgress();
var data: MotionModel = { var data: MotionModel = {
date: this.form.value.date,
description: this.form.value.description, description: this.form.value.description,
plus: this.form.value.plus || 0, plus: this.form.value.plus || 0,
minus: this.form.value.minus || 0, minus: this.form.value.minus || 0,
@@ -38,7 +38,7 @@
<td>{{account.currencyId}}</td> <td>{{account.currencyId}}</td>
<td>{{account.type}}</td> <td>{{account.type}}</td>
<td> <td>
<button class="btn-space" (click)="edit(account)" mat-raised-button color="primary"> <button class="btn-space" *ngIf="account.allowWrite" (click)="edit(account)" mat-raised-button color="primary">
Edit Edit
</button> </button>
<button class="btn-space" *ngIf="account.allowManage" (click)="access(account)" mat-raised-button color="primary"> <button class="btn-space" *ngIf="account.allowManage" (click)="access(account)" mat-raised-button color="primary">
@@ -44,6 +44,9 @@
<button class="btn-space" (click)="setRate(item)" mat-raised-button color="primary"> <button class="btn-space" (click)="setRate(item)" mat-raised-button color="primary">
Edit Edit
</button> </button>
<button class="btn-space" (click)="delete(item)" mat-raised-button color="warn">
Delete
</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -4,6 +4,7 @@ import { HttpClient } from '@angular/common/http';
// libs // libs
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import Swal from 'sweetalert2';
// app // app
import { ApiRoutes } from '../../../api-routes'; import { ApiRoutes } from '../../../api-routes';
@@ -60,6 +61,33 @@ export class SettingsCurrencyComponent {
}); });
} }
delete(currency: CurrencyModel) {
Swal.fire({
title: 'Delete currency ' + currency.name,
showCancelButton: true,
confirmButtonText: 'Delete',
showLoaderOnConfirm: true,
preConfirm: (name) => {
return new Promise((resolve, reject) => {
this.httpClient.delete(ApiRoutes.SettingsCurrency.replace(':id', currency.id!))
.subscribe(data => {
resolve(data);
}, error => {
Swal.showValidationMessage(error.detail);
reject();
});
}).catch(x => {
return false;
});
},
allowOutsideClick: () => !Swal.isLoading(),
}).then((result) => {
this.load();
});
}
private load() { private load() {
this.httpClient this.httpClient
.get<CurrencyModel[]>(ApiRoutes.GeneralCurrencies) .get<CurrencyModel[]>(ApiRoutes.GeneralCurrencies)
@@ -73,7 +101,9 @@ export class SettingsCurrencyComponent {
this.httpClient this.httpClient
.get<CurrencyModel[]>(ApiRoutes.SettingsCurrencies) .get<CurrencyModel[]>(ApiRoutes.SettingsCurrencies)
.subscribe(data => { .subscribe(data => {
this.myCurrencies = data.map(myCurrency => { this.myCurrencies = data
.sort((a, b) => a.code!.localeCompare(b.code!))
.map(myCurrency => {
var globalCurrency = this.currencies?.find(x => x.id === myCurrency.code); var globalCurrency = this.currencies?.find(x => x.id === myCurrency.code);
return { return {
@@ -41,7 +41,8 @@
<div class="text-inside"> <div class="text-inside">
<mat-form-field class="example-full-width" appearance="fill"> <mat-form-field class="example-full-width" appearance="fill">
<mat-label>Rate</mat-label> <mat-label>Rate</mat-label>
<input matInput [value]="currency.rate | number:'0.2-2'" formControlName="rate" currencyMask [options]="{ prefix: '', precision: 4, inputMode: 1 }" required> <input matInput [value]="currency.rate | number:'0.2-2'" formControlName="rate" currencyMask
[options]="{ prefix: '', precision: 4, inputMode: 1 }" required>
<mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')"> <mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')">
Please enter rate Please enter rate
</mat-error> </mat-error>
@@ -52,7 +53,8 @@
<div class="text-inside"> <div class="text-inside">
<mat-form-field class="example-full-width" appearance="fill"> <mat-form-field class="example-full-width" appearance="fill">
<mat-label>Quantity</mat-label> <mat-label>Quantity</mat-label>
<input matInput [value]="currency.rate | number:'0.0-0'" formControlName="quantity" currencyMask [options]="{ prefix: '', precision: 0 }" required> <input matInput [value]="currency.rate | number:'0.0-0'" formControlName="quantity"
currencyMask [options]="{ prefix: '', precision: 0 }" required>
<mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')"> <mat-error *ngIf="addForm.controls?.['rate']?.hasError('required')">
Please enter rate Please enter rate
</mat-error> </mat-error>
@@ -63,7 +65,8 @@
<div class="text-inside"> <div class="text-inside">
<mat-form-field class="example-full-width"> <mat-form-field class="example-full-width">
<mat-label>Rate Date</mat-label> <mat-label>Rate Date</mat-label>
<input matInput [matDatepicker]="picker3" (focus)="picker3.open()" value={{currency.rateDate}} formControlName="rateDate" required> <input matInput [matDatepicker]="picker3" (focus)="picker3.open()"
value={{currency.rateDate}} formControlName="rateDate" required>
<mat-hint>DD/MM/YYYY</mat-hint> <mat-hint>DD/MM/YYYY</mat-hint>
<mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>
<mat-datepicker #picker3></mat-datepicker> <mat-datepicker #picker3></mat-datepicker>
@@ -85,8 +88,11 @@
</div> </div>
<mat-dialog-actions class="mat-dialog-actions"> <mat-dialog-actions class="mat-dialog-actions">
<div class="mat-dialog-left"> <div class="mat-dialog-left">
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage"> <div class="alert alert-danger mat-dialog-error" *ngIf="error">
{{errorMessage}} <p *ngIf="error.title">{{error.title}}</p>
<ul *ngIf="error.errors">
<li *ngFor="let error of error.errors | keyvalue">{{error.value}}</li>
</ul>
</div> </div>
</div> </div>
<div class="mat-dialog-right"> <div class="mat-dialog-right">
@@ -21,7 +21,7 @@ import { CurrencyModel } from '../../../model/currency.model';
}) })
export class SettingsRateCurrencyComponent { export class SettingsRateCurrencyComponent {
public addForm!: UntypedFormGroup; public addForm!: UntypedFormGroup;
public errorMessage?: string; public error?: any;
constructor( constructor(
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
@@ -71,20 +71,26 @@ export class SettingsRateCurrencyComponent {
} }
onSubmitClick() { onSubmitClick() {
this.error = undefined;
if (this.addForm.valid) { if (this.addForm.valid) {
this.errorMessage = undefined;
this.httpClient this.httpClient
.put<CurrencyModel>(ApiRoutes.SettingsCurrency.replace(':id', this.addForm.value.id), this.addForm.value) .put<CurrencyModel>(ApiRoutes.SettingsCurrency.replace(':id', this.addForm.value.id), this.addForm.value)
.subscribe(response => { .subscribe({
next: (response) => {
this.httpClient this.httpClient
.post<CurrencyModel[]>(ApiRoutes.SettingsCurrenciesRate.replace(':id', this.addForm.value.id), this.addForm.value) .post<CurrencyModel[]>(ApiRoutes.SettingsCurrenciesRate.replace(':id', this.addForm.value.id), this.addForm.value)
.subscribe(response => { .subscribe({
next: (response) => {
this.dialogRef.close({ refresh: true }); this.dialogRef.close({ refresh: true });
}, error => { },
this.errorMessage = error.detail; error: (error) => {
this.error = error;
},
}); });
}, error => { },
this.errorMessage = error.detail; error: (error) => {
this.error = error;
}
}); });
} }
} }
@@ -10,11 +10,30 @@
<div class="card"> <div class="card">
<div class="body"> <div class="body">
<div id="mail-nav"> <div id="mail-nav">
<ul>
<mat-form-field class="example-full-width right-content" *ngIf="selectedCount > 0">
<mat-label>Move to category</mat-label>
<mat-select [(ngModel)]="category" (selectionChange)="onCategoryChange($event)">
<mat-option *ngFor="let category of categories" [value]="category.id">
{{category.name}}
</mat-option>
</mat-select>
</mat-form-field>
</ul>
<ul class="" id="mail-folders"> <ul class="" id="mail-folders">
<li *ngFor="let category of categories" [class.active]="selectedCategory && selectedCategory.id == category.id"> <li *ngFor="let category of categories" [class.active]="selectedCategory && selectedCategory.id == category.id">
<a href="javascript:;" (click)="selectCategory(category)" title="{{category.name}}">{{category.name}}</a> <a href="javascript:;" (click)="selectCategory(category)" title="{{category.name}}">{{category.name}}</a>
</li> </li>
</ul> </ul>
<ul *ngIf="categoryAddShow">
<mat-form-field class="example-full-width">
<mat-label>Code</mat-label>
<input matInput [(ngModel)]="categoryAddName">
</mat-form-field>
</ul>
<ul *ngIf="!categoryAddShow">
</ul>
</div> </div>
</div> </div>
</div> </div>
@@ -36,9 +55,13 @@
<table class="table"> <table class="table">
<tbody> <tbody>
<tr *ngFor="let item of items"> <tr *ngFor="let item of items">
<td>{{item.name}}</td>
<td> <td>
<button class="btn-space" (click)="edit(item)" mat-raised-button color="primary"> <mat-checkbox [(ngModel)]="item.selected" (change)="onCheckboxChange(item)">
{{item.model.name}}
</mat-checkbox>
</td>
<td>
<button class="btn-space" (click)="edit(item.model)" mat-raised-button color="primary">
Edit Edit
</button> </button>
</td> </td>
@@ -0,0 +1,11 @@
.card-title {
display: flex;
}
.left-content {
flex: 1;
}
.right-content {
margin-left: auto;
}
@@ -10,6 +10,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ApiRoutes } from '../../../api-routes'; import { ApiRoutes } from '../../../api-routes';
import { SettingsItemEditComponent } from './edit.item.component'; import { SettingsItemEditComponent } from './edit.item.component';
import { ItemService } from '../../../services/item.service'; import { ItemService } from '../../../services/item.service';
import { ISelectableModel } from '../../../core/models/selectable.model';
import { ItemModel } from '../../../model/item.model'; import { ItemModel } from '../../../model/item.model';
import { ItemCategoryModel } from '../../../model/item.category.model'; import { ItemCategoryModel } from '../../../model/item.category.model';
@@ -18,9 +19,13 @@ import { ItemCategoryModel } from '../../../model/item.category.model';
styleUrls: ['./item.component.scss'], styleUrls: ['./item.component.scss'],
}) })
export class SettingsItemComponent { export class SettingsItemComponent {
public items?: ItemModel[]; public items?: ISelectableModel<ItemModel>[];
public categories?: ItemCategoryModel[]; public categories?: ItemCategoryModel[];
public selectedCategory?: ItemCategoryModel; public selectedCategory?: ItemCategoryModel;
public selectedCount: number = 0;
public category?: ItemCategoryModel;
public categoryAddShow: boolean = false;
public categoryAddName?: string;
constructor( constructor(
private dialogModel: MatDialog, private dialogModel: MatDialog,
@@ -53,7 +58,7 @@ export class SettingsItemComponent {
this.selectedCategory = category; this.selectedCategory = category;
this.itemService.getItems(category.id) this.itemService.getItems(category.id)
.subscribe(data => { .subscribe(data => {
this.items = data; this.items = data.map<ISelectableModel<ItemModel>>(x => { return { model: x, selected: false } });
}); });
} }
@@ -95,4 +100,22 @@ export class SettingsItemComponent {
this.load(); this.load();
}); });
} }
onCheckboxChange(item: ISelectableModel<ItemModel>) {
this.selectedCount += item.selected ? 1 : -1;
}
onCategoryChange(item: any) {
var data = {
category: item.value,
items: this.items!.filter(x => x.selected).map(x => x.model.id)
}
this.httpClient.post(ApiRoutes.SettingsItems, data)
.subscribe(data => {
this.category = undefined;
this.selectedCount = 0;
this.load();
}, error => {
});
}
} }
+2 -1
View File
@@ -103,5 +103,6 @@
"OTHER": "Інші", "OTHER": "Інші",
"TOTAL": "Всього", "TOTAL": "Всього",
"INCOME": "Надходження", "INCOME": "Надходження",
"OUTCOME": "Витрати" "OUTCOME": "Витрати",
"PERIOD": "Період"
} }
@@ -0,0 +1,176 @@
namespace MyOffice.Services.Account;
using System.Collections.Generic;
using Core;
using Core.Extensions;
using Data.Models.Accounts;
using Domain;
public partial class AccountService
{
public Exec<AccountAccessInviteDto, AccessInviteStatus> AccessInvite(Guid userId, Guid accountId, string email, bool isAllowWrite)
{
if (email == null)
throw new ArgumentNullException(nameof(email));
var result = new Exec<AccountAccessInviteDto, AccessInviteStatus>(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(_mapper.Map<AccountAccessInviteDto>(invite));
}
public Exec<AccountDto, GeneralExecStatus> AccessUpdate(Guid userId, Guid accountId, List<AccountAccessDto> accesses)
{
if (accesses == null)
throw new ArgumentNullException(nameof(accesses));
var result = new Exec<AccountDto, GeneralExecStatus>(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<AccountDto>(account));
}
public Exec<AccountDto, GeneralExecStatus> AccessDelete(Guid userId, Guid accountId, Guid accessUserId)
{
var result = new Exec<AccountDto, GeneralExecStatus>(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<AccountDto>(account));
}
public List<AccountAccessInviteDto> InvitesGet(string email)
{
return _mapper.Map<List<AccountAccessInviteDto>>(_accountAccessInviteRepository.GetActive(email).ToList());
}
public Exec<AccountDto, InviteAcceptStatus> InviteAccept(Guid userId, Guid id, string name)
{
var result = new Exec<AccountDto, InviteAcceptStatus>(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<AccountDto>(account));
}
public Exec<AccountAccessInviteDto, GeneralExecStatus> InviteReject(Guid id)
{
var result = new Exec<AccountAccessInviteDto, GeneralExecStatus>(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<AccountAccessInviteDto>(invite));
}
}
@@ -0,0 +1,209 @@
namespace MyOffice.Services.Account;
using MyOffice.Core;
using MyOffice.Core.Extensions;
using MyOffice.Data.Models.Accounts;
using MyOffice.Services.Account.Domain;
public partial class AccountService
{
public List<AccountDto> GetAllAccounts(Guid userId)
{
return _accountRepository
.GetAll(userId)
.ToDto<AccountDto>(_mapper);
}
public List<AccountDto> GetByCategory(Guid userId, Guid categoryId)
{
return _accountRepository
.GetByCategory(userId, categoryId)
.ToDto<AccountDto>(_mapper);
}
public List<AccountDto> FindAccounts(Guid userId, string term)
{
return _accountRepository
.FindAccounts(userId, term)
.ToDto<AccountDto>(_mapper);
}
public List<AccountDetailedDto> GetByCategoryDetailed(Guid userId, Guid categoryId)
{
return _accountRepository
.GetByCategoryDetailed(userId, categoryId)
.ToDto<AccountDetailedDto>(_mapper);
}
public Exec<AccountDetailedDto, GeneralExecStatus> GetByIdDetailed(Guid userId, Guid id)
{
var result = new Exec<AccountDetailedDto, GeneralExecStatus>(GeneralExecStatus.success);
var account = _accountRepository.GetByIdDetailed(userId, id);
if (account == null)
{
return result.Set(GeneralExecStatus.not_found);
}
return result.Set(account.ToDto<AccountDetailedDto>(_mapper));;
}
public Exec<AccountDto, AccountAddStatus> AccountAdd(Guid userId, AccountAdd input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<AccountDto, AccountAddStatus>(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<AccountAccountCategory>
{
new()
{
AccountId = account.Id,
CategoryId = category.Id,
}
};
account.AccessRights = new List<AccountAccess>
{
new()
{
AccountId = account.Id,
UserId = userId,
OwnerId = userId,
IsAllowManage = true,
IsAllowRead = true,
IsAllowWrite = true,
}
};
if (!_accountRepository.Add(account))
{
return result.Set(AccountAddStatus.failure);
}
// TODO: Add category refactoring
if (!_accountAccountCategoryRepository.Add(account.Categories.FirstOrDefault()!))
{
return result.Set(AccountAddStatus.failure);
}
// TODO: Add access refactoring
if (!_accountAccessRepository.Add(account.AccessRights.FirstOrDefault()!))
{
return result.Set(AccountAddStatus.failure);
}
// TODO: Add account update AccessRights ???
var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId);
if (access != null && access.Type.ToString() != input.Type)
{
if (Enum.TryParse<AccountAccessTypeEnum>(input.Type, out var enumType))
{
access.Type = enumType;
_accountAccessRepository.Update(access);
}
}
return result.Set(account.ToDto<AccountDto>(_mapper));
}
public Exec<AccountDto, GeneralExecStatus> AccountDelete(Guid userId, string id)
{
var result = new Exec<AccountDto, GeneralExecStatus>(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.ToDto<AccountDto>(_mapper));
return result;
}
public Exec<AccountDto, AccountEditStatus> AccountUpdate(Guid userId, Guid accountId, AccountEdit input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<AccountDto, AccountEditStatus>(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<AccountAccessTypeEnum>(input.Type, out var enumType))
{
access.Type = enumType;
_accountAccessRepository.Update(access);
}
}
return result.Set(account.ToDto<AccountDto>(_mapper));
}
}
@@ -0,0 +1,114 @@
namespace MyOffice.Services.Account;
using MyOffice.Core;
using MyOffice.Data.Models.Accounts;
using MyOffice.Services.Account.Domain;
public partial class AccountService
{
public List<AccountCategoryDto> GetAllCategories(Guid userId)
{
var categories = _accountCategoryRepository.GetAll(userId);
return _mapper.Map<List<AccountCategoryDto>>(categories);
}
public Exec<AccountCategoryDto, GeneralExecStatus> GetCategory(Guid userId, Guid id)
{
var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);
var category = _accountCategoryRepository.Get(userId, id);
if (category == null)
{
return result.Set(GeneralExecStatus.not_found);
}
return result.Set(_mapper.Map<AccountCategoryDto>(category));
}
public Exec<AccountCategoryDto, GeneralExecStatus> CategoryAdd(Guid userId, AccountCategoryDto input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<AccountCategoryDto, GeneralExecStatus>(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<AccountCategoryDto>(category));
}
public Exec<AccountCategoryDto, GeneralExecStatus> CategoryUpdate(Guid userId, Guid id, AccountCategoryDto input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<AccountCategoryDto, GeneralExecStatus>(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<AccountCategoryDto>(exists));
}
public Exec<AccountCategoryDto, AccountCategoryRemoveResult> CategoryRemove(Guid userId, Guid id)
{
var result = new Exec<AccountCategoryDto, AccountCategoryRemoveResult>(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<AccountCategoryDto>(exists));
}
public Exec<List<AccountAccountCategoryDto>, GeneralExecStatus> AccountCategoryRemove(Guid userId, Guid accountId, Guid categoryId)
{
var result = new Exec<List<AccountAccountCategoryDto>, 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(_mapper.Map<List<AccountAccountCategoryDto>>(categories));
}
}
@@ -0,0 +1,178 @@
namespace MyOffice.Services.Account;
using System.Collections.Generic;
using Core;
using Core.Extensions;
using Data.Models.Accounts;
using Domain;
using Item.Domain;
public partial class AccountService
{
public Exec<List<MotionDto>, 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<List<MotionDto>, 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<MotionDto>());
result.Result!.Add(_mapper.Map<MotionDto>(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<MotionDto>(motionBalancing));
}
}
}
motionDb.Item = itemExec.Result!;
return result;
}
public Exec<MotionDto, MotionUpdateStatus> 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<MotionDto, MotionUpdateStatus>(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<MotionDto>(exists));
}
public Exec<List<MotionDto>, GeneralExecStatus> GetMotions(
Guid userId,
Guid accountId,
DateTime dateFrom,
DateTime dateTo
)
{
var result = new Exec<List<MotionDto>, 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(_mapper.Map<List<MotionDto>>(motions));
}
public Exec<MotionDto, MotionDeleteStatus> MotionRemove(
Guid userId,
Guid accountId,
Guid motionId
)
{
var result = new Exec<MotionDto, MotionDeleteStatus>(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(_mapper.Map<MotionDto>(exists));
}
}
+12 -639
View File
@@ -1,22 +1,16 @@
namespace MyOffice.Services.Account namespace MyOffice.Services.Account;
using AutoMapper;
using Microsoft.Extensions.Logging;
using Identity;
using Data.Repositories.Account;
using Data.Repositories.Currency;
using Data.Repositories.Item;
using Item;
public partial class AccountService
{ {
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<AccountService> _logger; private ILogger<AccountService> _logger;
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly IAccountCategoryRepository _accountCategoryRepository; private readonly IAccountCategoryRepository _accountCategoryRepository;
@@ -61,625 +55,4 @@
_itemService = itemService; _itemService = itemService;
_contextProvider = contextProvider; _contextProvider = contextProvider;
} }
public List<AccountCategoryDto> GetAllCategories(Guid userId)
{
var categories = _accountCategoryRepository.GetAll(userId);
return _mapper.Map<List<AccountCategoryDto>>(categories);
}
public Exec<AccountCategoryDto, GeneralExecStatus> GetCategory(Guid userId, Guid id)
{
var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);
var category = _accountCategoryRepository.Get(userId, id);
if (category == null)
{
return result.Set(GeneralExecStatus.not_found);
}
return result.Set(_mapper.Map<AccountCategoryDto>(category));
}
public Exec<AccountCategoryDto, GeneralExecStatus> CategoryAdd(Guid userId, AccountCategoryDto input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<AccountCategoryDto, GeneralExecStatus>(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<AccountCategoryDto>(category));
}
public Exec<AccountCategoryDto, GeneralExecStatus> CategoryUpdate(Guid userId, Guid id, AccountCategoryDto input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<AccountCategoryDto, GeneralExecStatus>(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<AccountCategoryDto>(exists));
}
public Exec<AccountCategoryDto, AccountCategoryRemoveResult> CategoryRemove(Guid userId, Guid id)
{
var result = new Exec<AccountCategoryDto, AccountCategoryRemoveResult>(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<AccountCategoryDto>(exists));
}
public List<AccountDto> GetAllAccounts(Guid userId)
{
var accounts = _accountRepository.GetAll(userId);
return _mapper.Map<List<AccountDto>>(accounts, o => o.Items.Add("UserId", userId));
}
public List<AccountDto> GetByCategory(Guid userId, Guid categoryId)
{
return _mapper.Map<List<AccountDto>>(_accountRepository.GetByCategory(userId, categoryId));
}
public List<AccountDetailedDto> GetByCategoryDetailed(Guid userId, Guid categoryId)
{
return _mapper.Map<List<AccountDetailedDto>>(_accountRepository.GetByCategoryDetailed(userId, categoryId));
}
public Exec<AccountDetailedDto, GeneralExecStatus> GetByIdDetailed(Guid userId, Guid id)
{
var result = new Exec<AccountDetailedDto, GeneralExecStatus>(GeneralExecStatus.success);
var account = _accountRepository.GetByIdDetailed(userId, id);
if (account == null)
{
return result.Set(GeneralExecStatus.not_found);
}
return result.Set(_mapper.Map<AccountDetailedDto>(account));
}
public Exec<Account, AccountAddStatus> AccountAdd(Guid userId, AccountAdd input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<Account, AccountAddStatus>(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<AccountAccountCategory>
{
new()
{
AccountId = account.Id,
CategoryId = category.Id,
}
};
account.AccessRights = new List<AccountAccess>
{
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<AccountAccessTypeEnum>(input.Type, out var enumType))
{
access.Type = enumType;
_accountAccessRepository.Update(access);
}
}
return result.Set(account);
}
public Exec<Account, GeneralExecStatus> AccountDelete(Guid userId, string id)
{
var result = new Exec<Account, GeneralExecStatus>(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<Account, AccountEditStatus> AccountUpdate(Guid userId, Guid accountId, AccountEdit input)
{
if (input == null)
throw new ArgumentNullException(nameof(input));
var result = new Exec<Account, AccountEditStatus>(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<AccountAccessTypeEnum>(input.Type, out var enumType))
{
access.Type = enumType;
_accountAccessRepository.Update(access);
}
}
return result.Set(account);
}
public Exec<List<AccountAccountCategory>, GeneralExecStatus> AccountCategoryRemove(Guid userId, Guid accountId, Guid categoryId)
{
var result = new Exec<List<AccountAccountCategory>, 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<List<MotionDto>, 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<List<MotionDto>, 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<MotionDto>());
result.Result!.Add(_mapper.Map<MotionDto>(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<MotionDto>(motionBalancing));
}
}
}
motionDb.Item = itemExec.Result!;
return result;
}
public Exec<MotionDto, MotionUpdateStatus> 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<MotionDto, MotionUpdateStatus>(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<MotionDto>(exists));
}
public Exec<List<Motion>, GeneralExecStatus> GetMotions(
Guid userId,
Guid accountId,
DateTime dateFrom,
DateTime dateTo
)
{
var result = new Exec<List<Motion>, 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<Motion, MotionDeleteStatus> MotionRemove(
Guid userId,
Guid accountId,
Guid motionId
)
{
var result = new Exec<Motion, MotionDeleteStatus>(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<Account> FindAccounts(Guid userId, string term)
{
return _accountRepository.FindAccounts(userId, term);
}
public Exec<AccountAccessInvite, AccessInviteStatus> AccessInvite(Guid userId, Guid accountId, string email, bool isAllowWrite)
{
if (email == null)
throw new ArgumentNullException(nameof(email));
var result = new Exec<AccountAccessInvite, AccessInviteStatus>(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<AccountDto, GeneralExecStatus> AccessUpdate(Guid userId, Guid accountId, List<AccountAccessDto> accesses)
{
if (accesses == null)
throw new ArgumentNullException(nameof(accesses));
var result = new Exec<AccountDto, GeneralExecStatus>(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<AccountDto>(account));
}
public Exec<AccountDto, GeneralExecStatus> AccessDelete(Guid userId, Guid accountId, Guid accessUserId)
{
var result = new Exec<AccountDto, GeneralExecStatus>(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<AccountDto>(account));
}
public List<AccountAccessInviteDto> InvitesGet(string email)
{
return _mapper.Map<List<AccountAccessInviteDto>>(_accountAccessInviteRepository.GetActive(email).ToList());
}
public Exec<AccountDto, InviteAcceptStatus> InviteAccept(Guid userId, Guid id, string name)
{
var result = new Exec<AccountDto, InviteAcceptStatus>(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<AccountDto>(account));
}
public Exec<AccountAccessInviteDto, GeneralExecStatus> InviteReject(Guid id)
{
var result = new Exec<AccountAccessInviteDto, GeneralExecStatus>(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<AccountAccessInviteDto>(invite));
}
}
} }
@@ -1,19 +1,11 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
using Core.Attributes; using AutoMapper;
using Data.Models.Accounts; using Data.Models.Accounts;
using Data.Models.Users; using MyOffice.Services.Identity;
using Mapper;
[Link(typeof(AccountAccess))]
[Link(typeof(AccountServiceProfile))]
public class AccountAccessDto public class AccountAccessDto
{ {
/// <summary>
/// Current user
/// </summary>
public Guid CurrenUserId { get; set; }
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
public AccountDto? Account { get; set; } public AccountDto? Account { get; set; }
@@ -21,15 +13,41 @@ public class AccountAccessDto
/// User can access to account /// User can access to account
/// </summary> /// </summary>
public Guid UserId { get; set; } public Guid UserId { get; set; }
public User? User { get; set; } public UserDto? User { get; set; }
/// <summary>
/// Who add access
/// </summary>
public Guid OwnerId { get; set; }
public User? Owner { get; set; }
public bool IsAllowRead { get; set; } public bool IsAllowRead { get; set; }
public bool IsAllowWrite { get; set; } public bool IsAllowWrite { get; set; }
public bool IsAllowManage { get; set; } public bool IsAllowManage { get; set; }
public bool IsOwner { get; set; }
public AccountAccessTypeEnum Type { get; set; } public AccountAccessTypeEnum Type { get; set; }
/// <summary>
/// Who add access
/// </summary>
public Guid OwnerId { get; set; }
public UserDto? Owner { get; set; }
public string? Name { get; set; }
}
public class AccountAccessDtoProfile : Profile
{
public AccountAccessDtoProfile()
{
CreateMap<AccountAccess, AccountAccessDto>()
.AfterMap<AccountAccessDtoMappingAction>()
;
}
}
public class AccountAccessDtoMappingAction : IMappingAction<AccountAccess, AccountAccessDto>
{
private readonly IContextProvider _contextProvider;
public AccountAccessDtoMappingAction(IContextProvider contextProvider)
{
_contextProvider = contextProvider;
}
public void Process(AccountAccess source, AccountAccessDto destination, ResolutionContext context)
{
destination.IsOwner = destination.OwnerId == _contextProvider.UserId;
}
} }
@@ -1,12 +1,22 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Accounts.AccountAccessInvite"/>
namespace MyOffice.Services.Account.Domain;
using Core.Attributes; using AutoMapper;
using Data.Models.Accounts; using Data.Models.Accounts;
[Link(typeof(AccountAccessInvite))]
public class AccountAccessInviteDto public class AccountAccessInviteDto
{ {
public string Id { get; set; } = null!; public string Id { get; set; } = null!;
public string Account { get; set; } = null!; public string Account { get; set; } = null!;
public bool IsAllowWrite { get; set; } public bool IsAllowWrite { get; set; }
} }
public class AccountAccessInviteDtoProfile: Profile
{
public AccountAccessInviteDtoProfile()
{
CreateMap<AccountAccessInvite, AccountAccessInviteDto>()
.ForMember(x => x.Account, o => o.MapFrom(x => x.Account!.Name))
;
}
}
@@ -1,7 +1,19 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Accounts.AccountAccountCategory"/>
namespace MyOffice.Services.Account.Domain;
using AutoMapper;
using MyOffice.Data.Models.Accounts;
public class AccountAccountCategoryDto public class AccountAccountCategoryDto
{ {
public Guid CategoryId { get; set; } public Guid CategoryId { get; set; }
public AccountCategoryDto? Category { get; set; } = null!; public AccountCategoryDto? Category { get; set; } = null!;
} }
public class AccountAccountCategoryDtoProfile: Profile
{
public AccountAccountCategoryDtoProfile()
{
CreateMap<AccountAccountCategory, AccountAccountCategoryDto>();
}
}
@@ -1,4 +1,8 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Accounts.AccountCategory"/>
namespace MyOffice.Services.Account.Domain;
using AutoMapper;
using MyOffice.Data.Models.Accounts;
public class AccountCategoryDto public class AccountCategoryDto
{ {
@@ -7,3 +11,14 @@ public class AccountCategoryDto
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public bool AllowDelete { get; set; } public bool AllowDelete { get; set; }
} }
public class AccountCategoryDtoProfile: Profile
{
public AccountCategoryDtoProfile()
{
CreateMap<AccountCategory, AccountCategoryDto>()
.ForMember(x => x.Id, o => o.MapFrom(x => x.Id))
.ForMember(x => x.AllowDelete, o => o.MapFrom(x => !x.Accounts!.Any()))
;
}
}
@@ -1,9 +1,22 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Accounts.Account"/>
namespace MyOffice.Services.Account.Domain;
public class AccountDetailedDto using AutoMapper;
using MyOffice.Core;
using MyOffice.Data.Models.Accounts;
public class AccountDetailedDto: IDataModelDto<AccountDetailed>
{ {
public AccountDto Account { get; set; } = null!; public AccountDto Account { get; set; } = null!;
public decimal TotalPlus { get; set; } public decimal TotalPlus { get; set; }
public decimal TotalMinus { get; set; } public decimal TotalMinus { get; set; }
public decimal Rest => TotalPlus - TotalMinus; public decimal Rest => TotalPlus - TotalMinus;
} }
public class AccountDetailedDtoProfile: Profile
{
public AccountDetailedDtoProfile()
{
CreateMap<AccountDetailed, AccountDetailedDto>();
}
}
+64 -12
View File
@@ -1,27 +1,79 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
using MyOffice.Data.Models.Currencies;
using System; using System;
using Data.Models.Accounts; using Data.Models.Accounts;
using MyOffice.Services.Currency.Domain;
using AutoMapper;
using MyOffice.Services.Identity;
using MyOffice.Core;
using MyOffice.Services.Mapper;
public class AccountDto [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public class GenerateMappedDtoAttribute: Attribute
{ {
public GenerateMappedDtoAttribute()
{
}
}
[GenerateMappedDto]
public class AccountDto : IDataModelDto<Account>
{
#region Account properties
public Guid Id { get; set; } public Guid Id { get; set; }
public string CurrencyGlobalId { get; set; } = null!; public string CurrencyGlobalId { get; set; } = null!;
public CurrencyGlobal? CurrencyGlobal { get; set; } public CurrencyGlobalDto? CurrencyGlobal { get; set; }
public Guid CurrencyId { get; set; } public string Name { get; set; } = null!;
public Currency? Currency { get; set; }
public Guid UserId { get; set; }
public UserDto? User { get; set; }
public Guid OwnerId { get; set; } public Guid OwnerId { get; set; }
public UserDto? Owner { get; set; } public UserDto? Owner { get; set; }
public string Name { get; set; } = null!;
public bool HasMotions { get; set; } #endregion Account properties
#region Dto properties
public Guid CurrencyId { get; set; }
public CurrencyDto? Currency { get; set; }
public string Type { get; set; } = null!; public string Type { get; set; } = null!;
public List<AccountAccessDto>? AccessRights { get; set; }
public List<AccountAccountCategoryDto>? Categories { get; set; } public List<AccountAccountCategoryDto>? Categories { get; set; }
public bool HasMotions { get; set; }
public Guid CurrentUserId { get; set; } #endregion Dto properties
public AccountAccess? AccountAccess { get; set; }
#region Permissions
public List<AccountAccessDto>? AccessRights { get; set; }
public bool AllowRead { get; set; }
public bool AllowWrite { get; set; }
public bool AllowDelete { get; set; }
public bool AllowManage { get; set; }
#endregion Permissions
}
public class AccountDtoProfile : BaseProfile<Account, AccountDto>
{
public AccountDtoProfile()
{
Mapping
.ForMember(x => x.HasMotions, o => o.MapFrom(x => x.Motions!.Any()))
.AfterMap<AccountDtoMappingAction>();
}
}
public class AccountDtoMappingAction : BaseMappingAction, IMappingAction<Account, AccountDto>
{
public AccountDtoMappingAction(IContextProvider contextProvider) : base(contextProvider) { }
public void Process(Account source, AccountDto destination, ResolutionContext context)
{
var accessRight = destination.AccessRights?.FirstOrDefault(x => x.UserId == ContextProvider.UserId);
destination.AllowRead = accessRight?.IsAllowRead ?? false;
destination.AllowWrite = accessRight?.IsAllowWrite ?? false;
destination.AllowManage = accessRight?.IsAllowManage ?? false;
destination.AllowDelete = (accessRight?.IsOwner ?? false) && !destination.HasMotions;
destination.Name = accessRight?.Name ?? destination.Name;
destination.Type = accessRight?.Name ?? destination.Type;
}
} }
@@ -1,14 +1,23 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Items.ItemCategory"/>
namespace MyOffice.Services.Account.Domain;
using MyOffice.Data.Models.Users; using AutoMapper;
using MyOffice.Data.Models.Items;
public class ItemCategoryDto public class ItemCategoryDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
public User User { get; set; } = null!; public UserDto User { get; set; } = null!;
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public List<ItemDto> Items { get; set; } = null!; public List<ItemDto> Items { get; set; } = null!;
public bool IsInternal { get; set; } public bool IsInternal { get; set; }
} }
public class ItemCategoryDtoProfile: Profile
{
public ItemCategoryDtoProfile()
{
CreateMap<ItemCategory, ItemCategoryDto>();
}
}
+15 -1
View File
@@ -1,5 +1,7 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Items.Item"/>
namespace MyOffice.Services.Account.Domain;
using AutoMapper;
using MyOffice.Data.Models.Items; using MyOffice.Data.Models.Items;
public class ItemDto public class ItemDto
@@ -11,3 +13,15 @@ public class ItemDto
public Guid CategoryId { get; set; } public Guid CategoryId { get; set; }
public ItemCategoryDto? Category { get; set; } public ItemCategoryDto? Category { get; set; }
} }
public class ItemDtoProfile: Profile
{
public ItemDtoProfile()
{
CreateMap<Item, ItemDto>()
.ForMember(x => x.Id, o => o.MapFrom(x => x.ItemGlobalId))
.ForMember(x => x.Name, o => o.MapFrom(x => x.ItemGlobal.Name))
.ForMember(x => x.AllowDelete, o => o.MapFrom(x => !x.Motions!.Any()))
;
}
}
@@ -1,7 +1,9 @@
namespace MyOffice.Services.Account.Domain namespace MyOffice.Services.Account.Domain;
using AutoMapper;
public class MotionAddUpdate
{ {
public class MotionAddUpdate
{
public DateTime Date { get; set; } public DateTime Date { get; set; }
public string Item { get; set; } = null!; public string Item { get; set; } = null!;
public string? ItemId { get; set; } = null!; public string? ItemId { get; set; } = null!;
@@ -10,5 +12,11 @@
public decimal Plus { get; set; } public decimal Plus { get; set; }
public decimal Minus { get; set; } public decimal Minus { get; set; }
public decimal AmountBalancing { get; set; } public decimal AmountBalancing { get; set; }
}
public class MotionAddUpdateProfile: Profile
{
public MotionAddUpdateProfile()
{
} }
} }
@@ -1,5 +1,8 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
using AutoMapper;
using MyOffice.Data.Models.Accounts;
public class MotionDto public class MotionDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
@@ -14,3 +17,11 @@ public class MotionDto
public decimal AmountMinus { get; set; } public decimal AmountMinus { get; set; }
public DateTime? DeletedOn { get; set; } public DateTime? DeletedOn { get; set; }
} }
public class MotionDtoProfile: Profile
{
public MotionDtoProfile()
{
CreateMap<Motion, MotionDto>();
}
}
+14 -3
View File
@@ -1,6 +1,9 @@
namespace MyOffice.Services.Account.Domain; /// <see cref="MyOffice.Data.Models.Users.User"/>
namespace MyOffice.Services.Account.Domain;
using Data.Models.Currencies; using AutoMapper;
using MyOffice.Data.Models.Users;
using MyOffice.Services.Currency.Domain;
public class UserDto public class UserDto
{ {
@@ -13,5 +16,13 @@ public class UserDto
public string? Phone { get; set; } public string? Phone { get; set; }
public string CurrencyId { get; set; } = null!; public string CurrencyId { get; set; } = null!;
public CurrencyGlobal? Currency { get; set; } public CurrencyGlobalDto? Currency { get; set; }
}
public class UserDtoProfile : Profile
{
public UserDtoProfile()
{
CreateMap<User, UserDto>();
}
} }
@@ -163,4 +163,22 @@ public class CurrencyService
return result.Set(_mapper.Map<CurrencyRateDto>(rate)); return result.Set(_mapper.Map<CurrencyRateDto>(rate));
} }
public Exec<CurrencyDto, GeneralExecStatus> Remove(Guid userId, Guid id)
{
var result = new Exec<CurrencyDto, GeneralExecStatus>(GeneralExecStatus.success);
var currency = _currencyRepository.Get(userId, id);
if (currency == null)
{
return result.Set(GeneralExecStatus.not_found);
}
if (!_currencyRepository.Remove(currency))
{
return result.Set(GeneralExecStatus.failure);
}
return result.Set(_mapper.Map<CurrencyDto>(currency));
}
} }
@@ -1,22 +1,32 @@
namespace MyOffice.Services.Currency.Domain; /// <see cref="MyOffice.Data.Models.Currencies.Currency"/>
namespace MyOffice.Services.Currency.Domain;
using AutoMapper;
using MyOffice.Data.Models.Currencies; using MyOffice.Data.Models.Currencies;
using MyOffice.Data.Models.Users; using MyOffice.Services.Account.Domain;
public class CurrencyDto public class CurrencyDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string CurrencyGlobalId { get; set; } = null!; public string CurrencyGlobalId { get; set; } = null!;
public CurrencyGlobal? CurrencyGlobal { get; set; } public CurrencyGlobalDto? CurrencyGlobal { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
public User? User { get; set; } public UserDto? User { get; set; }
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public string ShortName { get; set; } = null!; public string ShortName { get; set; } = null!;
public IEnumerable<CurrencyRate>? Rates { get; set; } public List<CurrencyRateDto>? Rates { get; set; }
public int? CurrentRateId { get; set; } public int? CurrentRateId { get; set; }
public CurrencyRate? CurrentRate { get; set; } public CurrencyRateDto? CurrentRate { get; set; }
public bool IsPrimary { get; set; } public bool IsPrimary { get; set; }
} }
public class CurrencyDtoProfile: Profile
{
public CurrencyDtoProfile()
{
CreateMap<Currency, CurrencyDto>();
}
}
@@ -1,4 +1,8 @@
namespace MyOffice.Services.Currency.Domain; /// <see cref="MyOffice.Data.Models.Currencies.CurrencyGlobal"/>
namespace MyOffice.Services.Currency.Domain;
using AutoMapper;
using MyOffice.Data.Models.Currencies;
public class CurrencyGlobalDto public class CurrencyGlobalDto
{ {
@@ -7,3 +11,11 @@ public class CurrencyGlobalDto
public string Symbol { get; set; } = null!; public string Symbol { get; set; } = null!;
public int DefaultQuantity { get; set; } public int DefaultQuantity { get; set; }
} }
public class CurrencyGlobalDtoProfile: Profile
{
public CurrencyGlobalDtoProfile()
{
CreateMap<CurrencyGlobal, CurrencyGlobalDto>();
}
}
@@ -1,4 +1,8 @@
namespace MyOffice.Services.Currency.Domain; /// <see cref="MyOffice.Data.Models.Currencies.CurrencyRate"/>
namespace MyOffice.Services.Currency.Domain;
using AutoMapper;
using MyOffice.Data.Models.Currencies;
public class CurrencyRateDto public class CurrencyRateDto
{ {
@@ -8,5 +12,13 @@ public class CurrencyRateDto
public DateTime DateTime { get; set; } public DateTime DateTime { get; set; }
public int Quantity { get; set; } public int Quantity { get; set; }
public decimal Rate { get; set; } public decimal Rate { get; set; }
public IEnumerable<CurrencyDto>? Currencies { get; set; } public List<CurrencyDto>? Currencies { get; set; }
}
public class CurrencyRateDtoProfile : Profile
{
public CurrencyRateDtoProfile()
{
CreateMap<CurrencyRate, CurrencyRateDto>();
}
} }
@@ -1,5 +1,7 @@
namespace MyOffice.Services.Currency.Domain; namespace MyOffice.Services.Currency.Domain;
using MyOffice.Data.Models.Currencies;
public class CurrencyWithRateDto public class CurrencyWithRateDto
{ {
public CurrencyDto Currency { get; set; } = null!; public CurrencyDto Currency { get; set; } = null!;
+111 -10
View File
@@ -11,6 +11,7 @@ using Core.Extensions;
using Data.Repositories.Account; using Data.Repositories.Account;
using Data.Repositories.Currency; using Data.Repositories.Currency;
using MyOffice.Data.Models.Accounts; using MyOffice.Data.Models.Accounts;
using System.Xml.Linq;
public class DashboardService public class DashboardService
{ {
@@ -52,8 +53,8 @@ public class DashboardService
Id = x.Id, Id = x.Id,
Name = x.Name, Name = x.Name,
Balance = x.Balance, Balance = x.Balance,
CurrencyName = x.CurrencyName, CurrencyName = x.CurrencyName!,
CurrencyShortName = x.CurrencyShortName, CurrencyShortName = x.CurrencyShortName!,
CurrencyRate = x.CurrencyRate, CurrencyRate = x.CurrencyRate,
CurrencyQuantity = x.CurrencyQuantity, CurrencyQuantity = x.CurrencyQuantity,
}) })
@@ -70,14 +71,64 @@ public class DashboardService
? _accountRepository.GetIncomeByCategory(userId, category.Value, from.ToUtc(), to.ToUtc()) ? _accountRepository.GetIncomeByCategory(userId, category.Value, from.ToUtc(), to.ToUtc())
: _accountRepository.GetIncomeByCategories(userId, from.ToUtc(), to.ToUtc()); : _accountRepository.GetIncomeByCategories(userId, from.ToUtc(), to.ToUtc());
data = data
.Where(x => x.Amount != 0)
.ToList();
var currencies = data
.Select(x => x.CurrencyId)
.Distinct()
.ToList();
var rates = _currencyRateRepository
.GetLastRates(userId, currencies, to.ToUtc())
.ToDictionary(x => x.Key, x => x.Value.Rate * x.Value.Quantity);
return new DashboardIncomeData return new DashboardIncomeData
{ {
Data = data.Select(x => new DashboardIncomeDataItem Data = data.Select(x => new
{ {
Id = x.Id?.ToShort(), Id = x.Id.ToShort(),
Name = x.Name, Name = x.Name,
Value = x.Amount, ValueRaw = x.Amount,
}).ToList() Value = (x.Amount * rates.FirstOrDefault(r => r.Key == x.CurrencyId).Value),
})
.GroupBy(x => new
{
x.Id,
x.Name,
})
.Select(x => new DashboardIncomeDataItem
{
Id = x.Key.Id,
Name = x.Key.Name,
Value = x.Sum(s => s.Value),
ValueRaw = x.Sum(s => s.ValueRaw),
})
.OrderBy(x => x.Name)
.ToList(),
Details = data.Select(x => new
{
Id = x.CurrencyId,
Name = x.CurrencyName,
ValueRaw = x.Amount,
Value = (x.Amount * rates.FirstOrDefault(r => r.Key == x.CurrencyId).Value),
})
.GroupBy(x => new
{
x.Id,
x.Name,
})
.Select(x => new DashboardIncomeDataItem
{
Id = x.Key.Id,
Name = x.Key.Name,
Value = x.Sum(s => s.Value),
ValueRaw = x.Sum(s => s.ValueRaw),
})
.OrderBy(x => x.Name)
.ToList(),
}; };
} }
@@ -87,14 +138,64 @@ public class DashboardService
? _accountRepository.GetOutcomeByCategory(userId, category.Value, from.ToUtc(), to.ToUtc()) ? _accountRepository.GetOutcomeByCategory(userId, category.Value, from.ToUtc(), to.ToUtc())
: _accountRepository.GetOutcomeByCategories(userId, from.ToUtc(), to.ToUtc()); : _accountRepository.GetOutcomeByCategories(userId, from.ToUtc(), to.ToUtc());
data = data
.Where(x => x.Amount != 0)
.ToList();
var currencies = data
.Select(x => x.CurrencyId)
.Distinct()
.ToList();
var rates = _currencyRateRepository
.GetLastRates(userId, currencies, to.ToUtc())
.ToDictionary(x => x.Key, x => x.Value.Rate * x.Value.Quantity);
return new DashboardIncomeData return new DashboardIncomeData
{ {
Data = data.Select(x => new DashboardIncomeDataItem Data = data.Select(x => new
{ {
Id = x.Id?.ToShort(), Id = x.Id.ToShort(),
Name = x.Name, Name = x.Name,
Value = x.Amount, ValueRaw = x.Amount,
}).ToList() Value = (x.Amount * rates.FirstOrDefault(r => r.Key == x.CurrencyId).Value),
})
.GroupBy(x => new
{
x.Id,
x.Name,
})
.Select(x => new DashboardIncomeDataItem
{
Id = x.Key.Id,
Name = x.Key.Name,
Value = x.Sum(s => s.Value),
ValueRaw = x.Sum(s => s.ValueRaw),
})
.OrderBy(x => x.Name)
.ToList(),
Details = data.Select(x => new
{
Id = x.CurrencyId,
Name = x.CurrencyName,
ValueRaw = x.Amount,
Value = (x.Amount * rates.FirstOrDefault(r => r.Key == x.CurrencyId).Value),
})
.GroupBy(x => new
{
x.Id,
x.Name,
})
.Select(x => new DashboardIncomeDataItem
{
Id = x.Key.Id,
Name = x.Key.Name,
Value = x.Sum(s => s.Value),
ValueRaw = x.Sum(s => s.ValueRaw),
})
.OrderBy(x => x.Name)
.ToList(),
}; };
} }
} }
@@ -1,8 +1,6 @@
namespace MyOffice.Services.Dashboard.Domain; namespace MyOffice.Services.Dashboard.Domain;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization;
using Data.Models.Accounts;
public class DashboardData public class DashboardData
{ {
@@ -32,11 +30,14 @@ public class DashboardRestData
public class DashboardIncomeData public class DashboardIncomeData
{ {
public List<DashboardIncomeDataItem> Data { get; set; } = null!; public List<DashboardIncomeDataItem> Data { get; set; } = null!;
public List<DashboardIncomeDataItem> Details { get; set; } = null!;
} }
public class DashboardIncomeDataItem public class DashboardIncomeDataItem
{ {
public string? Id { get; set; } = null!; public string? Id { get; set; }
public string Currency { get; set; } = null!;
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public decimal Value { get; set; } public decimal Value { get; set; }
public decimal ValueRaw { get; set; }
} }
+32 -1
View File
@@ -30,7 +30,6 @@ public class ItemService
_itemRepository = itemRepository; _itemRepository = itemRepository;
_itemGlobalRepository = itemGlobalRepository; _itemGlobalRepository = itemGlobalRepository;
_mapper = mapper; _mapper = mapper;
} }
public List<ItemCategoryDto> GetAllCategories(Guid userId) public List<ItemCategoryDto> GetAllCategories(Guid userId)
@@ -130,6 +129,17 @@ public class ItemService
return _mapper.Map<List<ItemDto>>(_itemRepository.GetByCategory(userId, categoryId)); return _mapper.Map<List<ItemDto>>(_itemRepository.GetByCategory(userId, categoryId));
} }
public List<ItemDto> GetByUncategorized(Guid userId)
{
var itemGlobals = _itemGlobalRepository.GetAvailableToUser(userId);
foreach (var itemGlobal in itemGlobals)
{
GetOrCreate(userId, itemGlobal);
}
return GetByCategory(userId, userId);
}
public Exec<ItemDto, GeneralExecStatus> Update(Guid userId, Guid motionId, Guid categoryId) public Exec<ItemDto, GeneralExecStatus> Update(Guid userId, Guid motionId, Guid categoryId)
{ {
var result = new Exec<ItemDto, GeneralExecStatus>(GeneralExecStatus.success); var result = new Exec<ItemDto, GeneralExecStatus>(GeneralExecStatus.success);
@@ -204,4 +214,25 @@ public class ItemService
return _mapper.Map<List<ItemDto>>(_itemRepository.Find(userId, term, limit)); return _mapper.Map<List<ItemDto>>(_itemRepository.Find(userId, term, limit));
} }
public GeneralExecStatus UpdateItemsCategory(Guid userId, Guid categoryId, List<Guid> items)
{
var category = _itemCategoryRepository.Get(userId, categoryId);
if (category == null)
{
return GeneralExecStatus.not_found;
}
foreach (var item in items)
{
var dbItem = _itemRepository.GetByGlobal(userId, item);
if (dbItem != null)
{
dbItem.CategoryId = category.Id;
_itemRepository.Update(dbItem);
}
}
return GeneralExecStatus.success;
}
} }
@@ -1,87 +0,0 @@
namespace MyOffice.Services.Mapper;
using Account.Domain;
using AutoMapper;
using Data.Models.Accounts;
using Data.Models.Currencies;
using Data.Models.Items;
using Data.Models.Users;
using MyOffice.Services.Currency.Domain;
public class AccountServiceProfile : Profile
{
public AccountServiceProfile()
{
MapUser();
MapAccount();
MapCurrency();
MapItems();
CreateMap<Motion, MotionDto>();
}
private void MapUser()
{
CreateMap<User, UserDto>();
}
private void MapAccount()
{
CreateMap<Account, AccountDto>()
.ForMember(x => x.HasMotions, o => o.MapFrom(x => x.Motions!.Any()))
.ForMember(x => x.CurrentUserId, o => o.MapFrom<UserIdResolver>())
.ForMember(x => x.AccountAccess, o =>
{
o.MapFrom((src, dst) => src.AccessRights!.FirstOrDefault(x => x.UserId == dst.CurrentUserId));
})
.ForMember(x => x.Type, o =>
{
o.SetMappingOrder(1);
o.MapFrom((src, dst) => dst.AccountAccess?.Type.ToString());
})
.ForMember(x => x.Name, o =>
{
o.SetMappingOrder(1);
o.MapFrom((src, dst) => dst.AccountAccess?.Name ?? src.Name);
})
;
CreateMap<AccountDetailed, AccountDetailedDto>();
CreateMap<AccountAccess, AccountAccessDto>();
CreateMap<AccountAccountCategory, AccountAccountCategoryDto>();
CreateMap<AccountCategory, AccountCategoryDto>()
.ForMember(x => x.Id, o => o.MapFrom(x => x.Id))
.ForMember(x => x.AllowDelete, o => o.MapFrom(x => !x.Accounts!.Any()))
;
CreateMap<AccountAccessInvite, AccountAccessInviteDto>()
.ForMember(x => x.Account, o => o.MapFrom(x => x.Account!.Name))
;
}
private void MapCurrency()
{
CreateMap<CurrencyGlobal, CurrencyGlobalDto>();
CreateMap<Currency, CurrencyDto>();
CreateMap<CurrencyRate, CurrencyRateDto>();
}
private void MapItems()
{
CreateMap<ItemCategory, ItemCategoryDto>();
CreateMap<Item, ItemDto>()
.ForMember(x => x.Id, o => o.MapFrom(x => x.ItemGlobalId))
.ForMember(x => x.Name, o => o.MapFrom(x => x.ItemGlobal.Name))
.ForMember(x => x.AllowDelete, o => o.MapFrom(x => !x.Motions!.Any()))
;
}
}
@@ -0,0 +1,17 @@
using AutoMapper;
using MyOffice.Core;
namespace MyOffice.Services;
public static class AutomapperExtensions
{
public static List<TTo> ToDto<TTo>(this IEnumerable<IDataModel> list, IMapper mapper)
{
return mapper.Map<List<TTo>>(list);
}
public static TTo ToDto<TTo>(this IDataModel item, IMapper mapper)
{
return mapper.Map<TTo>(item);
}
}
@@ -0,0 +1,12 @@
using MyOffice.Services.Identity;
namespace MyOffice.Services.Mapper;
public class BaseMappingAction
{
protected readonly IContextProvider ContextProvider;
public BaseMappingAction(IContextProvider contextProvider)
{
ContextProvider = contextProvider;
}
}
+13
View File
@@ -0,0 +1,13 @@
using AutoMapper;
namespace MyOffice.Services.Mapper;
public class BaseProfile<TSource, TDestination> : Profile
{
protected IMappingExpression<TSource, TDestination> Mapping;
public BaseProfile()
{
Mapping = CreateMap<TSource, TDestination>();
}
}
@@ -14,5 +14,4 @@
<ProjectReference Include="..\MyOffice.Core\MyOffice.Core.csproj" /> <ProjectReference Include="..\MyOffice.Core\MyOffice.Core.csproj" />
<ProjectReference Include="..\MyOffice.Data.Repositories\MyOffice.Data.Repositories.csproj" /> <ProjectReference Include="..\MyOffice.Data.Repositories\MyOffice.Data.Repositories.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -0,0 +1,80 @@
namespace MyOffice.Services.Notifications;
using MyOffice.Core;
using MyOffice.Data.Models.Notifications;
using MyOffice.Data.Models.Users;
using MyOffice.Data.Models.Verifications;
using MyOffice.Data.Repositories.Item;
using MyOffice.Services.Verifications;
public class EmailNotificationService
{
private readonly VerificationService _verificationService;
public EmailNotificationService(
VerificationService verificationService
)
{
_verificationService = verificationService;
}
public void PasswordResetEmail(User user)
{
var code = _verificationService.Add(
user,
user.Email,
VerificationCodeTemplateEnum.password_restore,
VerificationCodeTypeEnum.email
);
var subject = "";
var body = "";
}
}
public class TemplateSevice
{
private readonly IEmailTemplateRepository _emailTemplateRepository;
public TemplateSevice(
IEmailTemplateRepository emailTemplateRepository
)
{
_emailTemplateRepository = emailTemplateRepository;
}
public Exec<EmailTemplateFormated, GeneralExecStatus> GetTemplate(
EmailTemplateEnum template,
Dictionary<string, string> tokens
)
{
var result = new Exec<EmailTemplateFormated, GeneralExecStatus>(GeneralExecStatus.success);
var emailTemplate = _emailTemplateRepository.Get(template);
if (emailTemplate == null)
{
return result.Set(GeneralExecStatus.not_found);
}
result.Result = new EmailTemplateFormated
{
Subject = emailTemplate.Subject
Body = emailTemplate.Template,
};
return result;
}
}
public class EmailTemplateFormated
{
public string Sender { get; set; }
public string SenderName { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
public interface IEmailSender
{
//Exec<bool, GeneralExecStatus> Send(string from, string[] to, string subject, string body);
}
@@ -0,0 +1,51 @@
namespace MyOffice.Services.Verifications;
using MyOffice.Core.Helpers;
using MyOffice.Data.Models.Users;
using MyOffice.Data.Models.Verifications;
using MyOffice.Data.Repositories.Item;
public class VerificationService
{
private readonly TimeSpan _defaultExpires = TimeSpan.FromDays(1);
private readonly IVerificationCodeRepository _verificationCodeRepository;
public VerificationService(
IVerificationCodeRepository verificationCodeRepository
)
{
_verificationCodeRepository = verificationCodeRepository;
}
public VerificationCode Add(
User user,
string destination,
VerificationCodeTemplateEnum template,
VerificationCodeTypeEnum type,
DateTime? expiresOn = null,
string? metadata = null
)
{
if (user == null)
throw new ArgumentNullException(nameof(user));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
var verificationCode = new VerificationCode
{
UserId = user.Id,
Code = RandomizationHelper.Generate(40),
CreatedOn = DateTime.UtcNow,
ExpiresOn = expiresOn ?? DateTime.UtcNow.Add(_defaultExpires),
Template = template.ToString(),
DestinationType = type.ToString(),
Destination = destination,
Metadata = metadata,
};
_verificationCodeRepository.Add(verificationCode);
return verificationCode;
}
}
@@ -9,13 +9,13 @@ using Microsoft.Extensions.Logging;
using Models.Account; using Models.Account;
using Models.Item; using Models.Item;
using Models.Motion; using Models.Motion;
using MyOffice.Web.Infrastructure.Attributes;
using Services.Account; using Services.Account;
using Services.Account.Domain; using Services.Account.Domain;
using Services.Item; using Services.Item;
[Authorize] [Authorize]
[ApiController] [DefaultFromBody]
[Route("api/[controller]")]
public class AccountController : BaseApiController public class AccountController : BaseApiController
{ {
private readonly ILogger<AccountController> _logger; private readonly ILogger<AccountController> _logger;
@@ -36,7 +36,6 @@ public class AccountController : BaseApiController
_itemService = itemService; _itemService = itemService;
} }
[HttpGet("~/api/accounts")]
public ObjectResult AccountsGet(string category) public ObjectResult AccountsGet(string category)
{ {
var list = _accountService.GetByCategoryDetailed(UserId, category!.AsGuid()); var list = _accountService.GetByCategoryDetailed(UserId, category!.AsGuid());
@@ -44,7 +43,6 @@ public class AccountController : BaseApiController
return OkResponse(_mapper.Map<List<AccountDetailedViewModel>>(list.OrderBy(x => x.Account.Name).ToList())); return OkResponse(_mapper.Map<List<AccountDetailedViewModel>>(list.OrderBy(x => x.Account.Name).ToList()));
} }
[HttpGet("~/api/accounts/{id}")]
public ObjectResult AccountGet(string id) public ObjectResult AccountGet(string id)
{ {
var exec = _accountService.GetByIdDetailed(UserId, id!.AsGuid()); var exec = _accountService.GetByIdDetailed(UserId, id!.AsGuid());
@@ -63,7 +61,6 @@ public class AccountController : BaseApiController
} }
} }
[HttpGet("~/api/accounts/{id}/motions")]
public ObjectResult MotionsGet(string id, [FromQuery] MotionsGetRequest request) public ObjectResult MotionsGet(string id, [FromQuery] MotionsGetRequest request)
{ {
var exec = _accountService.GetMotions(UserId, id!.AsGuid(), request.From.StartOfDay(), request.To.EndOfDay()); var exec = _accountService.GetMotions(UserId, id!.AsGuid(), request.From.StartOfDay(), request.To.EndOfDay());
@@ -82,7 +79,6 @@ public class AccountController : BaseApiController
} }
} }
[HttpPost("~/api/accounts/{id}/motions")]
public object MotionsPost(string id, MotionRequest motion) public object MotionsPost(string id, MotionRequest motion)
{ {
var exec = _accountService.MotionAdd(UserId, id.AsGuid(), _mapper.Map<MotionAddUpdate>(motion)); var exec = _accountService.MotionAdd(UserId, id.AsGuid(), _mapper.Map<MotionAddUpdate>(motion));
@@ -101,7 +97,6 @@ public class AccountController : BaseApiController
} }
} }
[HttpPut("~/api/accounts/{id}/motions/{motionId}")]
public ObjectResult MotionsPut(string id, string motionId, MotionRequest motion) public ObjectResult MotionsPut(string id, string motionId, MotionRequest motion)
{ {
var exec = _accountService.MotionUpdate(UserId, id.AsGuid(), motionId.AsGuid(), _mapper.Map<MotionAddUpdate>(motion)); var exec = _accountService.MotionUpdate(UserId, id.AsGuid(), motionId.AsGuid(), _mapper.Map<MotionAddUpdate>(motion));
@@ -120,7 +115,6 @@ public class AccountController : BaseApiController
} }
} }
[HttpDelete("~/api/accounts/{id}/motions/{motionId}")]
public ObjectResult MotionsDelete(string id, string motionId) public ObjectResult MotionsDelete(string id, string motionId)
{ {
var exec = _accountService.MotionRemove(UserId, id.AsGuid(), motionId.AsGuid()); var exec = _accountService.MotionRemove(UserId, id.AsGuid(), motionId.AsGuid());
@@ -139,7 +133,6 @@ public class AccountController : BaseApiController
} }
} }
[HttpGet("~/api/items")]
public ObjectResult FindItems(string term) public ObjectResult FindItems(string term)
{ {
var itemsDto = _itemService.FindItems(UserId, term); var itemsDto = _itemService.FindItems(UserId, term);
@@ -1,12 +1,13 @@
namespace MyOffice.Web.Controllers; namespace MyOffice.Web.Controllers;
using System.Security.Authentication; using System.Security.Authentication;
using Microsoft.AspNetCore.Mvc;
using Core.Extensions; using Core.Extensions;
using IdentityModel; using IdentityModel;
using Microsoft.AspNetCore.Mvc; using MyOffice.Web.Infrastructure.Attributes;
using MyOffice.Web.Models; using MyOffice.Web.Models;
using static IdentityServer4.Models.IdentityResources;
[DefaultFromBody]
public class BaseApiController : ControllerBase public class BaseApiController : ControllerBase
{ {
public Guid UserId public Guid UserId
+13 -12
View File
@@ -1,51 +1,52 @@
namespace MyOffice.Web.Controllers; namespace MyOffice.Web.Controllers;
using AutoMapper;
using Core.Extensions; using Core.Extensions;
using Infrastructure.Attributes; using Infrastructure.Attributes;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MyOffice.Web.Models.Dashboard;
using Services.Dashboard; using Services.Dashboard;
[Authorize] [Authorize]
[ApiController] [DefaultFromBody]
[Route("api/[controller]")]
public class DashboardController : BaseApiController public class DashboardController : BaseApiController
{ {
private readonly ILogger<DashboardController> _logger; private readonly ILogger<DashboardController> _logger;
private readonly DashboardService _dashboardService; private readonly DashboardService _dashboardService;
private readonly IMapper _mapper;
public DashboardController( public DashboardController(
ILogger<DashboardController> logger, ILogger<DashboardController> logger,
DashboardService dashboardService DashboardService dashboardService,
IMapper mapper
) )
{ {
_logger = logger; _logger = logger;
_dashboardService = dashboardService; _dashboardService = dashboardService;
_mapper = mapper;
} }
[HttpGet("~/api/dashboard")] public ObjectResult Index()
public object Index()
{ {
var data = _dashboardService.GetDashboardRestData(UserId); var data = _dashboardService.GetDashboardRestData(UserId);
return data; return OkResponse(_mapper.Map<DashboardViewModel>(data));
} }
[HttpGet("~/api/dashboard/income")] public ObjectResult Income(DateTime from, DateTime to, [AsGuid(true)] string? category)
public object Income(DateTime from, DateTime to, [AsGuid(true)] string? category)
{ {
var data = _dashboardService.GetDashboardIncomeData(UserId, from.StartOfDay(), to.EndOfDay(), category?.AsGuidNull()); var data = _dashboardService.GetDashboardIncomeData(UserId, from.StartOfDay(), to.EndOfDay(), category?.AsGuidNull());
return data; return OkResponse(_mapper.Map<DashboardIncomeDataViewModel>(data));
} }
[HttpGet("~/api/dashboard/outcome")] public ObjectResult Outcome(DateTime from, DateTime to, [AsGuid(true)] string? category)
public object Outcome(DateTime from, DateTime to, [AsGuid(true)] string? category)
{ {
var data = _dashboardService.GetDashboardOutcomeData(UserId, from.StartOfDay(), to.EndOfDay(), category?.AsGuidNull()); var data = _dashboardService.GetDashboardOutcomeData(UserId, from.StartOfDay(), to.EndOfDay(), category?.AsGuidNull());
return data; return OkResponse(_mapper.Map<DashboardIncomeDataViewModel>(data));
} }
} }
@@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Models.Currency; using Models.Currency;
using Services.Currency; using Services.Currency;
using MyOffice.Web.Infrastructure.Attributes;
[Authorize] [Authorize]
[ApiController] [DefaultFromBody]
[Route("api/[controller]")]
public class GeneralController : BaseApiController public class GeneralController : BaseApiController
{ {
private readonly CurrencyService _currencyService; private readonly CurrencyService _currencyService;
@@ -23,8 +23,7 @@ public class GeneralController : BaseApiController
_mapper = mapper; _mapper = mapper;
} }
[HttpGet("~/api/general/currencies")] public ObjectResult GetGlobalCurrencies()
public ObjectResult Get()
{ {
var list = _currencyService.GetGlobalAll(); var list = _currencyService.GetGlobalAll();
@@ -0,0 +1,115 @@
namespace MyOffice.Web.Controllers;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Core;
using Core.Extensions;
using Models.Account;
using Services.Account;
using Services.Account.Domain;
using Infrastructure.Attributes;
using Services.Identity;
using Microsoft.AspNetCore.Mvc;
[Authorize]
[DefaultFromBody]
public class SettingsAccountCategoryController : BaseApiController
{
private readonly ILogger<SettingsAccountController> _logger;
private readonly IMapper _mapper;
private readonly AccountService _accountService;
private readonly IContextProvider _contextProvider;
public SettingsAccountCategoryController(
ILogger<SettingsAccountController> logger,
IMapper mapper,
AccountService accountService,
IContextProvider contextProvider
)
{
_logger = logger;
_mapper = mapper;
_accountService = accountService;
_contextProvider = contextProvider;
}
public ObjectResult AccountCategories()
{
var list = _accountService.GetAllCategories(UserId);
return OkResponse(_mapper.Map<List<AccountCategoryViewModel>>(list).OrderBy(x => x.Name));
}
public ObjectResult AccountCategory([AsGuid] string id)
{
var exec = _accountService.GetCategory(UserId, id.AsGuid());
switch (exec.Status)
{
case GeneralExecStatus.not_found:
case GeneralExecStatus.failure:
return ProblemBadResponse("Account category not found.");
case GeneralExecStatus.success:
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
public ObjectResult AccountCategoriesAdd(AccountCategoryViewModel request)
{
var exec = _accountService.CategoryAdd(UserId, new AccountCategoryDto { Name = request.Name! });
switch (exec.Status)
{
case GeneralExecStatus.success:
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
case GeneralExecStatus.failure:
case GeneralExecStatus.not_found:
return ProblemBadResponse("Adding account category failed.");
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
public ObjectResult AccountCategoriesUpdate([AsGuid] string id, AccountCategoryViewModel request)
{
var exec = _accountService.CategoryUpdate(UserId, id.AsGuid(), new AccountCategoryDto { Name = request.Name! });
switch (exec.Status)
{
case GeneralExecStatus.success:
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
case GeneralExecStatus.failure:
case GeneralExecStatus.not_found:
return ProblemBadResponse("Update account category failed.");
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
public ObjectResult AccountCategoriesDelete([AsGuid] string id)
{
var exec = _accountService.CategoryRemove(UserId, id.AsGuid());
switch (exec.Status)
{
case AccountCategoryRemoveResult.success:
return OkResponse(_mapper.Map<AccountCategoryViewModel>(exec.Result!));
case AccountCategoryRemoveResult.failure:
return ProblemBadResponse("Remove account category failed.");
case AccountCategoryRemoveResult.not_found:
return ProblemBadResponse("Account category not found.");
case AccountCategoryRemoveResult.accounts_exists:
return ProblemBadResponse("Account category have accounts.");
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
}
@@ -13,8 +13,7 @@ using Infrastructure.Attributes;
using Services.Identity; using Services.Identity;
[Authorize] [Authorize]
[ApiController] [DefaultFromBody]
[Route("api/[controller]")]
public class SettingsAccountController : BaseApiController public class SettingsAccountController : BaseApiController
{ {
private readonly ILogger<SettingsAccountController> _logger; private readonly ILogger<SettingsAccountController> _logger;
@@ -35,102 +34,16 @@ public class SettingsAccountController : BaseApiController
_contextProvider = contextProvider; _contextProvider = contextProvider;
} }
[HttpGet("~/api/settings/account-categories")] public ObjectResult AccountsGet([AsGuid(true)] string? category)
public object AccountCategories()
{
var list = _accountService.GetAllCategories(UserId);
return _mapper.Map<AccountCategoryViewModel[]>(list).OrderBy(x => x.Name);
}
[HttpGet("~/api/settings/account-categories/{id}")]
public object AccountCategory([AsGuid] string id)
{
var exec = _accountService.GetCategory(UserId, id.AsGuid());
switch (exec.Status)
{
case GeneralExecStatus.not_found:
case GeneralExecStatus.failure:
return ProblemBadResponse("Account category not found.");
case GeneralExecStatus.success:
return _mapper.Map<AccountCategoryViewModel>(exec.Result!);
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpPost("~/api/settings/account-categories")]
public object AccountCategoriesAdd(AccountCategoryViewModel request)
{
var exec = _accountService.CategoryAdd(UserId, new AccountCategoryDto { Name = request.Name! });
switch (exec.Status)
{
case GeneralExecStatus.success:
return exec.Result!;
case GeneralExecStatus.failure:
case GeneralExecStatus.not_found:
return ProblemBadResponse("Adding account category failed.");
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpPut("~/api/settings/account-categories/{id}")]
public object AccountCategoriesEdit([AsGuid] string id, AccountCategoryViewModel request)
{
var exec = _accountService.CategoryUpdate(UserId, id.AsGuid(), new AccountCategoryDto { Name = request.Name! });
switch (exec.Status)
{
case GeneralExecStatus.success:
return exec.Result!;
case GeneralExecStatus.failure:
case GeneralExecStatus.not_found:
return ProblemBadResponse("Update account category failed.");
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpDelete("~/api/settings/account-categories/{id}")]
public object AccountCategoriesDelete([AsGuid] string id)
{
var exec = _accountService.CategoryRemove(UserId, id.AsGuid());
switch (exec.Status)
{
case AccountCategoryRemoveResult.success:
return exec.Result!;
case AccountCategoryRemoveResult.failure:
return ProblemBadResponse("Remove account category failed.");
case AccountCategoryRemoveResult.not_found:
return ProblemBadResponse("Account category not found.");
case AccountCategoryRemoveResult.accounts_exists:
return ProblemBadResponse("Account category have accounts.");
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpGet("~/api/settings/accounts")]
public List<AccountViewModel> AccountsGet([AsGuid(true)] string? category)
{ {
var list = category.IsPresent() var list = category.IsPresent()
? _accountService.GetByCategory(UserId, category!.AsGuid()) ? _accountService.GetByCategory(UserId, category!.AsGuid())
: _accountService.GetAllAccounts(UserId); : _accountService.GetAllAccounts(UserId);
return _mapper.Map<List<AccountViewModel>>(list.OrderBy(x => x.Name)); return OkResponse(_mapper.Map<List<AccountViewModel>>(list.OrderBy(x => x.Name)));
} }
[HttpPost("~/api/settings/accounts")] public ObjectResult AccountsAdd(AccountViewModel request)
public object AccountsAdd(AccountViewModel request)
{ {
var exec = _accountService.AccountAdd(UserId, new AccountAdd var exec = _accountService.AccountAdd(UserId, new AccountAdd
{ {
@@ -149,16 +62,14 @@ public class SettingsAccountController : BaseApiController
case AccountAddStatus.failure: case AccountAddStatus.failure:
return ProblemBadResponse("Adding account failed."); return ProblemBadResponse("Adding account failed.");
case AccountAddStatus.success: case AccountAddStatus.success:
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
public ObjectResult AccountsUpdate([AsGuid] string id, AccountEditRequestModel request)
[HttpPut("~/api/settings/accounts/{id}")]
public object AccountsAdd([AsGuid] string id, AccountEditRequestModel request)
{ {
var exec = _accountService.AccountUpdate(UserId, id.AsGuid(), new AccountEdit var exec = _accountService.AccountUpdate(UserId, id.AsGuid(), new AccountEdit
{ {
@@ -180,15 +91,14 @@ public class SettingsAccountController : BaseApiController
case AccountEditStatus.failure: case AccountEditStatus.failure:
return ProblemBadResponse("Adding account failed."); return ProblemBadResponse("Adding account failed.");
case AccountEditStatus.success: case AccountEditStatus.success:
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpDelete("~/api/settings/accounts/{id}")] public ObjectResult AccountsDelete([AsGuid] string id)
public object AccountsDelete([AsGuid] string id)
{ {
var exec = _accountService.AccountDelete(UserId, id); var exec = _accountService.AccountDelete(UserId, id);
switch (exec.Status) switch (exec.Status)
@@ -196,15 +106,14 @@ public class SettingsAccountController : BaseApiController
case GeneralExecStatus.not_found: case GeneralExecStatus.not_found:
return ProblemBadResponse("Account not found."); return ProblemBadResponse("Account not found.");
case GeneralExecStatus.success: case GeneralExecStatus.success:
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpDelete("~/api/settings/accounts/{id}/category/{categoryId}")] public ObjectResult AccountsCategoryDelete([AsGuid] string id, [AsGuid] string categoryId)
public object AccountsCategoryRemove([AsGuid] string id, [AsGuid] string categoryId)
{ {
var exec = _accountService.AccountCategoryRemove(UserId, id.AsGuid(), categoryId.AsGuid()); var exec = _accountService.AccountCategoryRemove(UserId, id.AsGuid(), categoryId.AsGuid());
@@ -215,15 +124,14 @@ public class SettingsAccountController : BaseApiController
return ProblemBadResponse("Category not found."); return ProblemBadResponse("Category not found.");
case GeneralExecStatus.success: case GeneralExecStatus.success:
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpPost("~/api/settings/accounts/{id}/access")] public ObjectResult AccountsAccessAdd([AsGuid] string id, AccountAccessViewModel request)
public object AccountsAdd([AsGuid] string id, AccountAccessViewModel request)
{ {
var model = _mapper.Map<List<AccountAccessDto>>(request.Accesses); var model = _mapper.Map<List<AccountAccessDto>>(request.Accesses);
@@ -244,7 +152,7 @@ public class SettingsAccountController : BaseApiController
if (!request.Email.IsPresent()) if (!request.Email.IsPresent())
{ {
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
} }
var inviteExec = _accountService.AccessInvite(UserId, id.AsGuid(), request.Email!, request.AllowWrite); var inviteExec = _accountService.AccessInvite(UserId, id.AsGuid(), request.Email!, request.AllowWrite);
@@ -257,15 +165,14 @@ public class SettingsAccountController : BaseApiController
case AccessInviteStatus.access_exists: case AccessInviteStatus.access_exists:
case AccessInviteStatus.invite_exists: case AccessInviteStatus.invite_exists:
case AccessInviteStatus.success: case AccessInviteStatus.success:
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpDelete("~/api/settings/accounts/{id}/access/{userId}")] public ObjectResult AccountsAccessDelete([AsGuid] string id, [AsGuid] string userId)
public object AccountsAdd([AsGuid] string id, [AsGuid] string userId)
{ {
var exec = _accountService.AccessDelete(UserId, id.AsGuid(), userId.AsGuid()); var exec = _accountService.AccessDelete(UserId, id.AsGuid(), userId.AsGuid());
@@ -276,23 +183,21 @@ public class SettingsAccountController : BaseApiController
return ProblemBadResponse("Account not found."); return ProblemBadResponse("Account not found.");
case GeneralExecStatus.success: case GeneralExecStatus.success:
return exec.Result!; return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpGet("~/api/settings/accounts/invites")] public ObjectResult AccountInvites()
public List<AccountAccessInviteViewModel> AccountInvites()
{ {
var invites = _accountService.InvitesGet(_contextProvider.User.Email); var invites = _accountService.InvitesGet(_contextProvider.User.Email);
return _mapper.Map<List<AccountAccessInviteViewModel>>(invites); return OkResponse(_mapper.Map<List<AccountAccessInviteViewModel>>(invites));
} }
[HttpPost("~/api/settings/accounts/invites/{id}/accept")] public ObjectResult AccountInviteAccept([AsGuid] string id, AccountInviteAcceptRequest request)
public object AccountInviteAccept([AsGuid]string id, AccountInviteAcceptRequest request)
{ {
var exec = _accountService.InviteAccept(UserId, id.AsGuid(), request.Name); var exec = _accountService.InviteAccept(UserId, id.AsGuid(), request.Name);
@@ -305,15 +210,14 @@ public class SettingsAccountController : BaseApiController
case InviteAcceptStatus.already_accepted: case InviteAcceptStatus.already_accepted:
case InviteAcceptStatus.success: case InviteAcceptStatus.success:
return _mapper.Map<AccountViewModel>(exec.Result!); return OkResponse(_mapper.Map<AccountViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpPost("~/api/settings/accounts/invites/{id}/reject")] public ObjectResult AccountInviteReject([AsGuid] string id)
public object AccountInviteReject([AsGuid] string id)
{ {
var exec = _accountService.InviteReject(id.AsGuid()); var exec = _accountService.InviteReject(id.AsGuid());
@@ -324,7 +228,7 @@ public class SettingsAccountController : BaseApiController
return ProblemBadResponse("Invite not found."); return ProblemBadResponse("Invite not found.");
case GeneralExecStatus.success: case GeneralExecStatus.success:
return _mapper.Map<AccountAccessInviteViewModel>(exec.Result!); return OkResponse(_mapper.Map<AccountAccessInviteViewModel>(exec.Result!));
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());

Some files were not shown because too many files have changed in this diff Show More