From d0cdaa85b6b6f02d6a26c228330785a2266339c5 Mon Sep 17 00:00:00 2001 From: Alexandr Sulimov Date: Fri, 21 Jul 2023 23:01:56 +0300 Subject: [PATCH] fix --- MyOffice.Data.Models/Accounts/Account.cs | 19 +- .../Currencies/CurrencyRate.cs | 1 + .../Account/AccountRepository.cs | 76 ++- .../Currency/CurrencyRateRepository.cs | 5 +- .../Currency/ICurrencyRateRepository.cs | 2 +- MyOffice.DbContext/AppDbContext.cs | 3 +- MyOffice.DbContext/AppDbContextFactory.cs | 1 + MyOffice.DbContext/RepositoryInitializer.cs | 9 +- .../20230721180851_CurrentRateV2.Designer.cs | 633 ++++++++++++++++++ .../20230721180851_CurrentRateV2.cs | 48 ++ .../Migrations/AppDbContextModelSnapshot.cs | 16 + .../dashboard2/dashboard2.component.html | 128 +--- .../dashboard2/dashboard2.component.ts | 4 +- MyOffice.SPA/src/app/model/dashboard.model.ts | 11 +- MyOffice.Services/Currency/CurrencyService.cs | 15 +- .../Dashboard/DashboardService.cs | 36 +- .../Dashboard/Domain/DashboardData.cs | 28 +- .../Models/Currency/CurrencyViewModel.cs | 6 +- 18 files changed, 863 insertions(+), 178 deletions(-) create mode 100644 MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.Designer.cs create mode 100644 MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.cs diff --git a/MyOffice.Data.Models/Accounts/Account.cs b/MyOffice.Data.Models/Accounts/Account.cs index 3340c81..a8c3e28 100644 --- a/MyOffice.Data.Models/Accounts/Account.cs +++ b/MyOffice.Data.Models/Accounts/Account.cs @@ -2,13 +2,21 @@ using Currencies; +public enum AccountTypeEnum +{ + debit, + credit, + transit, + other, +} + public class Account { public Guid Id { get; set; } public string CurrencyGlobalId { get; set; } = null!; public CurrencyGlobal? CurrencyGlobal { get; set; } public string Name { get; set; } = null!; - //public bool IsRest { get; set; } + public AccountTypeEnum Type { get; set; } public IEnumerable? AccessRights { get; set; } public IEnumerable? Motions { get; set; } public IEnumerable? Categories { get; set; } @@ -26,10 +34,11 @@ public struct AccountSimple { public Guid Id { get; set; } public string Name { get; set; } - public string CurrencyId { get; set; } - public string CurrencyShortName { get; set; } - public decimal CurrencyRate { get; set; } + public string? CurrencyName { get; set; } + public string? CurrencyShortName { get; set; } + public decimal? CurrencyRate { get; set; } + public int? CurrencyQuantity { get; set; } public decimal? TotalPlus { get; set; } public decimal? TotalMinus { get; set; } - public decimal? Rest { get; set; } + public decimal? Balance { get; set; } } diff --git a/MyOffice.Data.Models/Currencies/CurrencyRate.cs b/MyOffice.Data.Models/Currencies/CurrencyRate.cs index 9df18be..8d20bbe 100644 --- a/MyOffice.Data.Models/Currencies/CurrencyRate.cs +++ b/MyOffice.Data.Models/Currencies/CurrencyRate.cs @@ -8,4 +8,5 @@ public class CurrencyRate public DateTime DateTime { get; set; } public int Quantity { get; set; } public decimal Rate { get; set; } + public IEnumerable? Currencies { get; set; } } \ No newline at end of file diff --git a/MyOffice.Data.Repositories/Account/AccountRepository.cs b/MyOffice.Data.Repositories/Account/AccountRepository.cs index 311a4cc..51ffb16 100644 --- a/MyOffice.Data.Repositories/Account/AccountRepository.cs +++ b/MyOffice.Data.Repositories/Account/AccountRepository.cs @@ -1,6 +1,7 @@ namespace MyOffice.Data.Repositories.Account; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Models.Accounts; using MyOffice.Data.Models.Currencies; using MyOffice.Data.Repositories; @@ -102,42 +103,65 @@ public class AccountRepository : AppRepository, IAccountRepository public decimal GetPeriodIncome(Guid userId, DateTime from, DateTime to) { - return _context.Motions - .Where(x => !x.Item.Category!.IsInternal) - .Where(x => x.Account!.AccessRights!.Any(u => u.UserId == userId)) - .Where(x => x.DateTime >= from && x.DateTime <= to) - .Sum(x => x.AmountPlus); + return _context.Accounts + .Where(x => x.AccessRights!.Any(u => u.UserId == userId)) + .Include(x => x.CurrencyGlobal!) + .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) { - return _context.Motions - .Where(x => !x.Item.Category!.IsInternal) - .Where(x => x.Account!.AccessRights!.Any(u => u.UserId == userId)) - .Where(x => x.DateTime >= from && x.DateTime <= to) - .Sum(x => x.AmountMinus); + return _context.Accounts + .Where(x => x.AccessRights!.Any(u => u.UserId == userId)) + .Include(x => x.CurrencyGlobal!) + .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.AmountMinus) + }) + .ToList() + .Select(x => x.Amount * (x.Currency?.CurrentRate?.Rate * x.Currency?.CurrentRate?.Quantity)) + .Sum() ?? 0; } public List GetRestAtDate(Guid userId, DateTime date) { - return _context.Motions - .Where(x => x.Account!.AccessRights!.Any(u => u.UserId == userId)) - .Where(x => x.DateTime <= date) - .GroupBy(x => new - { - x.AccountId, - x.Account.Name, - CurrencyId = x.Account!.CurrencyGlobalId, - CurrencyName = x.Account!.CurrencyGlobal!.Name, + return _context.Accounts + .Include(x => x.CurrencyGlobal!) + .ThenInclude(x => x.Currencies!) + .ThenInclude(x => x.CurrentRate!) + .Where(x => x.AccessRights!.Any(u => u.UserId == userId)) + .Select(x => new { + Id = x.Id, + Name = x.Name, + Currency = x.CurrencyGlobal!.Currencies!.FirstOrDefault(x => x.UserId == userId), + Rest = x.Motions!.Sum(m => (m.AmountPlus - m.AmountMinus)) }) + .ToList() .Select(x => new AccountSimple { - Id = x.Key.AccountId, - Name = x.Key.Name, - CurrencyId = x.Key.CurrencyId, - CurrencyShortName = x.Key.CurrencyName, - Rest = x.Sum(g => (g.AmountPlus - g.AmountMinus)) - }) - .ToList(); + Id = x.Id, + Name = x.Name, + Balance = x.Rest, + CurrencyName = x.Currency?.Name, + CurrencyShortName = x.Currency?.ShortName, + CurrencyRate = x.Currency?.CurrentRate?.Rate, + CurrencyQuantity = x.Currency?.CurrentRate?.Quantity, + }).ToList(); } } \ No newline at end of file diff --git a/MyOffice.Data.Repositories/Currency/CurrencyRateRepository.cs b/MyOffice.Data.Repositories/Currency/CurrencyRateRepository.cs index 7f0fd0f..ddbcd08 100644 --- a/MyOffice.Data.Repositories/Currency/CurrencyRateRepository.cs +++ b/MyOffice.Data.Repositories/Currency/CurrencyRateRepository.cs @@ -5,10 +5,13 @@ using Models.Currencies; public class CurrencyRateRepository : AppRepository, ICurrencyRateRepository { - public List GetLastRates(Guid currencyId, int count = 1) + public List GetLastRates(Guid currencyId, DateTime? before = null, int count = 1) { + before = before ?? DateTime.UtcNow; + return _context.CurrencyRates .Where(x => x.CurrencyId == currencyId) + .Where(x => x.DateTime <= before) .OrderByDescending(x => x.DateTime) .ThenByDescending(x => x.Id) .Take(count) diff --git a/MyOffice.Data.Repositories/Currency/ICurrencyRateRepository.cs b/MyOffice.Data.Repositories/Currency/ICurrencyRateRepository.cs index b853e5b..e8806ac 100644 --- a/MyOffice.Data.Repositories/Currency/ICurrencyRateRepository.cs +++ b/MyOffice.Data.Repositories/Currency/ICurrencyRateRepository.cs @@ -4,7 +4,7 @@ using Models.Currencies; public interface ICurrencyRateRepository { - List GetLastRates(Guid currencyId, int count = 1); + List GetLastRates(Guid currencyId, DateTime? before = null, int count = 1); List GetLastRates(List currencyIds); bool AddRate(CurrencyRate currencyRate); List GetAtDate(Guid currencyId, DateTime date); diff --git a/MyOffice.DbContext/AppDbContext.cs b/MyOffice.DbContext/AppDbContext.cs index 574ae2f..f90fa64 100644 --- a/MyOffice.DbContext/AppDbContext.cs +++ b/MyOffice.DbContext/AppDbContext.cs @@ -118,7 +118,8 @@ public class AppDbContext : DbContext modelBuilder.Entity() .HasOne(x => x.CurrentRate) - .WithOne(x => x.Currency); + .WithMany(x => x.Currencies) + .HasForeignKey(x => x.CurrentRateId); } private void AccountCreating(ModelBuilder modelBuilder) diff --git a/MyOffice.DbContext/AppDbContextFactory.cs b/MyOffice.DbContext/AppDbContextFactory.cs index 9edac7c..f765aa1 100644 --- a/MyOffice.DbContext/AppDbContextFactory.cs +++ b/MyOffice.DbContext/AppDbContextFactory.cs @@ -38,6 +38,7 @@ public class AppDbContextFactory : IDesignTimeDbContextFactory } var db = new AppDbContext(providerEnum, builder.Options); + db.ChangeTracker.AutoDetectChangesEnabled = false; db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; return db; diff --git a/MyOffice.DbContext/RepositoryInitializer.cs b/MyOffice.DbContext/RepositoryInitializer.cs index da7001a..96f962b 100644 --- a/MyOffice.DbContext/RepositoryInitializer.cs +++ b/MyOffice.DbContext/RepositoryInitializer.cs @@ -55,6 +55,9 @@ public class RepositoryInitializer dbContext.SaveChanges(); } + /// + /// Update all currency - set current rate + /// private static void UpdateCurrentRates(AppDbContext dbContext) { var lastrates = dbContext.CurrencyRates @@ -62,7 +65,7 @@ public class RepositoryInitializer .GroupBy(x => new { x.CurrencyId, - }, (key, g) => g.OrderByDescending(x => x.DateTime).First()) + }, (key, g) => g.Where(x => x.DateTime <= DateTime.UtcNow).OrderByDescending(x => x.DateTime).First()) .ToList(); var currencies = dbContext.Currencies.ToList(); @@ -71,8 +74,8 @@ public class RepositoryInitializer var rate = lastrates.FirstOrDefault(x => x.CurrencyId == currency.Id); if (rate != null) { - //currency.CurrentRateId = rate.Id; - dbContext.Attach(currency); + currency.CurrentRateId = rate.Id; + dbContext.Attach(currency).State = EntityState.Modified; } } diff --git a/MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.Designer.cs b/MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.Designer.cs new file mode 100644 index 0000000..aa4d8f7 --- /dev/null +++ b/MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.Designer.cs @@ -0,0 +1,633 @@ +// +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("20230721180851_CurrentRateV2")] + partial class CurrentRateV2 + { + /// + 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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CurrencyGlobalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CurrencyGlobalId"); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("IsAllowManage") + .HasColumnType("boolean"); + + b.Property("IsAllowRead") + .HasColumnType("boolean"); + + b.Property("IsAllowWrite") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("UserId"); + + b.ToTable("AccountAccesses"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccountCategories"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("AmountMinus") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("AmountPlus") + .HasPrecision(18, 6) + .HasColumnType("numeric(18,6)"); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("DateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ItemId") + .HasColumnType("integer"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CurrencyGlobalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CurrentRateId") + .HasColumnType("integer"); + + b.Property("IsPrimary") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("text"); + + b.Property("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("Id") + .HasColumnType("text"); + + b.Property("DefaultQuantity") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Symbol") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("CurrencyGlobals"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CurrencyId") + .HasColumnType("uuid"); + + b.Property("DateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("CurrencyId"); + + b.ToTable("CurrencyRates"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("uuid"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsInternal") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("ItemCategories"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("ItemGlobals"); + }); + + modelBuilder.Entity("MyOffice.Data.Models.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CurrencyId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .UseCollation("my_ci_collation"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("FullName") + .HasColumnType("text"); + + b.Property("IsEmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("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("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedOn") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .UseCollation("my_ci_collation"); + + b.Property("ExternalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Provider") + .IsRequired() + .HasColumnType("text") + .UseCollation("my_ci_collation"); + + b.Property("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.Navigation("CurrencyGlobal"); + }); + + 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", "User") + .WithMany("AccountAccess") + .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("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("AccountCategories"); + + b.Navigation("AccountMotions"); + + b.Navigation("Currencies"); + + b.Navigation("ItemCategories"); + + b.Navigation("UserClaims"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.cs b/MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.cs new file mode 100644 index 0000000..e7bb75c --- /dev/null +++ b/MyOffice.Migration.Postgres/Migrations/20230721180851_CurrentRateV2.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MyOffice.Migrations.Postgres.Migrations +{ + /// + public partial class CurrentRateV2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CurrentRateId", + table: "Currencies", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Currencies_CurrentRateId", + table: "Currencies", + column: "CurrentRateId"); + + migrationBuilder.AddForeignKey( + name: "FK_Currencies_CurrencyRates_CurrentRateId", + table: "Currencies", + column: "CurrentRateId", + principalTable: "CurrencyRates", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Currencies_CurrencyRates_CurrentRateId", + table: "Currencies"); + + migrationBuilder.DropIndex( + name: "IX_Currencies_CurrentRateId", + table: "Currencies"); + + migrationBuilder.DropColumn( + name: "CurrentRateId", + table: "Currencies"); + } + } +} diff --git a/MyOffice.Migration.Postgres/Migrations/AppDbContextModelSnapshot.cs b/MyOffice.Migration.Postgres/Migrations/AppDbContextModelSnapshot.cs index bb7b45b..2e03a1e 100644 --- a/MyOffice.Migration.Postgres/Migrations/AppDbContextModelSnapshot.cs +++ b/MyOffice.Migration.Postgres/Migrations/AppDbContextModelSnapshot.cs @@ -175,6 +175,9 @@ namespace MyOffice.Migrations.Postgres.Migrations .IsRequired() .HasColumnType("text"); + b.Property("CurrentRateId") + .HasColumnType("integer"); + b.Property("IsPrimary") .HasColumnType("boolean"); @@ -193,6 +196,8 @@ namespace MyOffice.Migrations.Postgres.Migrations b.HasIndex("CurrencyGlobalId"); + b.HasIndex("CurrentRateId"); + b.HasIndex("UserId"); b.ToTable("Currencies"); @@ -479,6 +484,10 @@ namespace MyOffice.Migrations.Postgres.Migrations .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") @@ -487,6 +496,8 @@ namespace MyOffice.Migrations.Postgres.Migrations b.Navigation("CurrencyGlobal"); + b.Navigation("CurrentRate"); + b.Navigation("User"); }); @@ -579,6 +590,11 @@ namespace MyOffice.Migrations.Postgres.Migrations 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"); diff --git a/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.html b/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.html index 9562a7b..163023c 100644 --- a/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.html +++ b/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.html @@ -5,109 +5,49 @@ -
+
-
-
-
-
-
-
Income
-

Income last 7 days

-
-

{{dashboardModel?.incomeLastWeek | number: '1.2-6'}}

-
-
-
-
+
+
+
+
+
+
Balance
+

{{dashboardModel?.balance | number: '0.2-2'}}

-
-

Change previous 7 days

-

{{dashboardModel?.incomeChange | number: '1.2'}}%

+
+
+ +
+
+
+
+
+
Debit balance
+

+
+

{{dashboardModel?.balanceDebit | number: '0.2-2'}}

+
+
+
+
+ +
+
+
+
+
+
Credit balance
+
+

{{dashboardModel?.balanceCredit | number: '0.2-2'}}

+
-
-
-
-
-
-
Outcome
-

Outcome last 7 days

-
-

{{dashboardModel?.outcomeLastWeek | number: '1.2-6'}}

-
-
-
-
-
-
-
-
-

Change previous 7 days

-

{{dashboardModel?.outcomeChange | number: '1.2'}}%

-
-
-
-
- -
-
-
-
-
-
New Orders
-

Fresh New Order

-
-

15

-
-
-
-
-
-
-
-
-

Change

-

50%

-
-
-
-
- -
-
-
-
-
-
New Users
-

Joined New User

-
-

12

-
-
-
-
-
-
-
-
-

Change

-

25%

-
-
-
-
-
-
diff --git a/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.ts b/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.ts index fe03afa..6615bb9 100644 --- a/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.ts +++ b/MyOffice.SPA/src/app/dashboard/dashboard2/dashboard2.component.ts @@ -65,8 +65,8 @@ export class Dashboard2Component implements OnInit { this.httpClient .get(ApiRoutes.Dashboard) .subscribe(data => { - data.incomeChange = this.calcChanges(data.incomeLastWeek, data.incomePreviousWeek); - data.outcomeChange = this.calcChanges(data.outcomeLastWeek, data.outcomePreviousWeek); + data.incomeChange = this.calcChanges(data.incomeLast, data.incomePrevious); + data.outcomeChange = this.calcChanges(data.outcomeLast, data.outcomePrevious); this.dashboardModel = data; }); } diff --git a/MyOffice.SPA/src/app/model/dashboard.model.ts b/MyOffice.SPA/src/app/model/dashboard.model.ts index 304d339..6311c36 100644 --- a/MyOffice.SPA/src/app/model/dashboard.model.ts +++ b/MyOffice.SPA/src/app/model/dashboard.model.ts @@ -1,8 +1,11 @@ export interface DashboardModel { - incomeLastWeek: number; - incomePreviousWeek: number; + incomeLast: number; + incomePrevious: number; incomeChange: number; - outcomeLastWeek: number; - outcomePreviousWeek: number; + outcomeLast: number; + outcomePrevious: number; outcomeChange: number; + balance: number; + balanceDebit: number; + balanceCredit: number; } diff --git a/MyOffice.Services/Currency/CurrencyService.cs b/MyOffice.Services/Currency/CurrencyService.cs index e8175bb..79f3f3b 100644 --- a/MyOffice.Services/Currency/CurrencyService.cs +++ b/MyOffice.Services/Currency/CurrencyService.cs @@ -1,6 +1,7 @@ namespace MyOffice.Services.Currency; using Core; +using Core.Extensions; using Data.Models.Currencies; using Data.Repositories.Currency; using MyOffice.Services.Currency.Domain; @@ -37,7 +38,7 @@ public class CurrencyService var list = _currencyRepository.GetAll(userId); return list.ToDictionary( x => x, - x => _currencyRateRepository.GetLastRates(x.Id, 1).FirstOrDefault() + x => _currencyRateRepository.GetLastRates(x.Id).FirstOrDefault() ); } @@ -113,13 +114,13 @@ public class CurrencyService var result = new Exec(CurrencyAddRateStatus.success); - var exists = _currencyRepository.Get(userId, currencyId); - if (exists == null) + var currency = _currencyRepository.Get(userId, currencyId); + if (currency == null) { return result.Set(CurrencyAddRateStatus.not_found); } - currencyRate.CurrencyId = exists.Id; + currencyRate.CurrencyId = currency.Id; currencyRate.DateTime = currencyRate.DateTime.Date; var rates = _currencyRateRepository.GetAtDate(currencyRate.CurrencyId, currencyRate.DateTime); @@ -132,6 +133,12 @@ public class CurrencyService } rate = currencyRate; + + if (rate.DateTime.EndOfDay() <= DateTime.UtcNow.EndOfDay()) + { + currency.CurrentRateId = rate.Id; + _currencyRepository.Update(currency); + } } return result.Set(rate); diff --git a/MyOffice.Services/Dashboard/DashboardService.cs b/MyOffice.Services/Dashboard/DashboardService.cs index f5d85e4..11a6c4f 100644 --- a/MyOffice.Services/Dashboard/DashboardService.cs +++ b/MyOffice.Services/Dashboard/DashboardService.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Core.Extensions; using Data.Repositories.Account; using Data.Repositories.Currency; +using MyOffice.Data.Models.Accounts; public class DashboardService { @@ -26,30 +27,23 @@ public class DashboardService public DashboardData GetDashboardRestData(Guid userId) { - var lastDaysStart = DateTime.UtcNow.AddDays(-7).StartOfDay(); - var lastDaysEnd = lastDaysStart.AddDays(7).EndOfDay(); - var previousDaysStart = lastDaysStart.AddDays(-7).StartOfDay(); - var previousDaysEnd = previousDaysStart.AddDays(7).EndOfDay(); + /*var lastDaysStart = DateTime.UtcNow.AddMonths(-3).StartOfDay(); + var lastDaysEnd = lastDaysStart.AddMonths(3).EndOfDay(); + var previousDaysStart = lastDaysStart.AddMonths(-3).StartOfDay(); + var previousDaysEnd = previousDaysStart.AddMonths(3).EndOfDay();*/ var rests = _accountRepository.GetRestAtDate(userId, DateTime.UtcNow); - var currencyIds = rests - .Select(x => x.CurrencyId) - .Distinct() - .ToList(); - - var lastrates = _currencyRateRepository.GetLastRates(currencyIds); - foreach (var rest in rests) + var result = new DashboardData { - //var rate = lastrates.FirstOrDefault(x => x.CurrencyId == rest.CurrencyId) - } - - return new DashboardData - { - IncomeLastWeek = _accountRepository.GetPeriodIncome(userId, lastDaysStart, lastDaysEnd), - IncomePreviousWeek = _accountRepository.GetPeriodIncome(userId, previousDaysStart, previousDaysEnd), - OutcomeLastWeek = _accountRepository.GetPeriodOutcome(userId, lastDaysStart, lastDaysEnd), - OutcomePreviousWeek = _accountRepository.GetPeriodOutcome(userId, previousDaysStart, previousDaysEnd), - Rests = rests, + //IncomeLast = _accountRepository.GetPeriodIncome(userId, lastDaysStart, lastDaysEnd), + //IncomePrevious = _accountRepository.GetPeriodIncome(userId, previousDaysStart, previousDaysEnd), + //OutcomeLast = _accountRepository.GetPeriodOutcome(userId, lastDaysStart, lastDaysEnd), + //OutcomePrevious = _accountRepository.GetPeriodOutcome(userId, previousDaysStart, previousDaysEnd), + Balance = rests.Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)), + BalanceDebit = rests.Where(x => x.Balance > 0).Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)), + BalanceCredit = rests.Where(x => x.Balance < 0).Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)), }; + + return result; } } diff --git a/MyOffice.Services/Dashboard/Domain/DashboardData.cs b/MyOffice.Services/Dashboard/Domain/DashboardData.cs index 01e2595..5c31403 100644 --- a/MyOffice.Services/Dashboard/Domain/DashboardData.cs +++ b/MyOffice.Services/Dashboard/Domain/DashboardData.cs @@ -1,23 +1,25 @@ namespace MyOffice.Services.Dashboard.Domain; using System.Collections.Generic; +using System.Text.Json.Serialization; using Data.Models.Accounts; public class DashboardData { - public decimal IncomeLastWeek { get; set; } - public decimal IncomePreviousWeek { get; set; } - public decimal OutcomeLastWeek { get; set; } - public decimal OutcomePreviousWeek { get; set; } - public List? Rests { get; set; } - public decimal? RestTotal => Rests?.Sum(x => x.Rest); - public decimal? RestTotalPlus => Rests?.Where(x => x.Rest > 0).Sum(x => x.Rest); - public decimal? RestTotalMinus => Rests?.Where(x => x.Rest < 0).Sum(x => x.Rest); + public decimal IncomeLast { get; set; } + public decimal IncomePrevious { get; set; } + public decimal OutcomeLast { get; set; } + public decimal OutcomePrevious { get; set; } + public decimal? Balance { get; set; } + public decimal? BalanceDebit { get; set; } + public decimal? BalanceCredit { get; set; } } -/*public class DashboardRestData +public class DashboardRestData { - public string AccountId { get; set; } - public string AccountName { get; set; } - public decimal Rest { get; set; } -}*/ \ No newline at end of file + public AccountSimple AccountSimple { get; set; } + public decimal? Balance => AccountSimple.Balance; + + public decimal? BalanceAtRate => + AccountSimple.Balance * (AccountSimple.CurrencyRate * AccountSimple.CurrencyQuantity); +} \ No newline at end of file diff --git a/MyOffice.Web/Models/Currency/CurrencyViewModel.cs b/MyOffice.Web/Models/Currency/CurrencyViewModel.cs index 0e5fcf2..287f473 100644 --- a/MyOffice.Web/Models/Currency/CurrencyViewModel.cs +++ b/MyOffice.Web/Models/Currency/CurrencyViewModel.cs @@ -28,9 +28,9 @@ Symbol = input.CurrencyGlobal!.Symbol, Name = input.Name, ShortName = input.ShortName, - Quantity = rate?.Quantity, - Rate = rate?.Rate, - RateDate = rate?.DateTime, + Quantity = input.CurrentRate?.Quantity ?? rate?.Quantity, + Rate = input.CurrentRate?.Rate ?? rate?.Rate, + RateDate = input.CurrentRate?.DateTime ?? rate?.DateTime, IsPrimary = input.IsPrimary, }; }