This commit is contained in:
2023-07-21 23:01:56 +03:00
parent fa5a8a9001
commit d0cdaa85b6
18 changed files with 863 additions and 178 deletions
+14 -5
View File
@@ -2,13 +2,21 @@
using Currencies; using Currencies;
public enum AccountTypeEnum
{
debit,
credit,
transit,
other,
}
public class Account public class Account
{ {
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 CurrencyGlobal? CurrencyGlobal { get; set; }
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
//public bool IsRest { get; set; } public AccountTypeEnum Type { get; set; }
public IEnumerable<AccountAccess>? AccessRights { get; set; } public IEnumerable<AccountAccess>? AccessRights { get; set; }
public IEnumerable<Motion>? Motions { get; set; } public IEnumerable<Motion>? Motions { get; set; }
public IEnumerable<AccountAccountCategory>? Categories { get; set; } public IEnumerable<AccountAccountCategory>? Categories { get; set; }
@@ -26,10 +34,11 @@ public struct AccountSimple
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string CurrencyId { 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 decimal? TotalPlus { get; set; } public decimal? TotalPlus { get; set; }
public decimal? TotalMinus { get; set; } public decimal? TotalMinus { get; set; }
public decimal? Rest { get; set; } public decimal? Balance { get; set; }
} }
@@ -8,4 +8,5 @@ public class CurrencyRate
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<Currency>? Currencies { get; set; }
} }
@@ -1,6 +1,7 @@
namespace MyOffice.Data.Repositories.Account; namespace MyOffice.Data.Repositories.Account;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Models.Accounts; using Models.Accounts;
using MyOffice.Data.Models.Currencies; using MyOffice.Data.Models.Currencies;
using MyOffice.Data.Repositories; using MyOffice.Data.Repositories;
@@ -102,42 +103,65 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
public decimal GetPeriodIncome(Guid userId, DateTime from, DateTime to) public decimal GetPeriodIncome(Guid userId, DateTime from, DateTime to)
{ {
return _context.Motions return _context.Accounts
.Where(x => !x.Item.Category!.IsInternal) .Where(x => x.AccessRights!.Any(u => u.UserId == userId))
.Where(x => x.Account!.AccessRights!.Any(u => u.UserId == userId)) .Include(x => x.CurrencyGlobal!)
.Where(x => x.DateTime >= from && x.DateTime <= to) .ThenInclude(x => x.Currencies!)
.Sum(x => x.AmountPlus); .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) public decimal GetPeriodOutcome(Guid userId, DateTime from, DateTime to)
{ {
return _context.Motions return _context.Accounts
.Where(x => !x.Item.Category!.IsInternal) .Where(x => x.AccessRights!.Any(u => u.UserId == userId))
.Where(x => x.Account!.AccessRights!.Any(u => u.UserId == userId)) .Include(x => x.CurrencyGlobal!)
.Where(x => x.DateTime >= from && x.DateTime <= to) .ThenInclude(x => x.Currencies!)
.Sum(x => x.AmountMinus); .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<AccountSimple> GetRestAtDate(Guid userId, DateTime date) public List<AccountSimple> GetRestAtDate(Guid userId, DateTime date)
{ {
return _context.Motions return _context.Accounts
.Where(x => x.Account!.AccessRights!.Any(u => u.UserId == userId)) .Include(x => x.CurrencyGlobal!)
.Where(x => x.DateTime <= date) .ThenInclude(x => x.Currencies!)
.GroupBy(x => new .ThenInclude(x => x.CurrentRate!)
{ .Where(x => x.AccessRights!.Any(u => u.UserId == userId))
x.AccountId, .Select(x => new {
x.Account.Name, Id = x.Id,
CurrencyId = x.Account!.CurrencyGlobalId, Name = x.Name,
CurrencyName = x.Account!.CurrencyGlobal!.Name, Currency = x.CurrencyGlobal!.Currencies!.FirstOrDefault(x => x.UserId == userId),
Rest = x.Motions!.Sum(m => (m.AmountPlus - m.AmountMinus))
}) })
.ToList()
.Select(x => new AccountSimple .Select(x => new AccountSimple
{ {
Id = x.Key.AccountId, Id = x.Id,
Name = x.Key.Name, Name = x.Name,
CurrencyId = x.Key.CurrencyId, Balance = x.Rest,
CurrencyShortName = x.Key.CurrencyName, CurrencyName = x.Currency?.Name,
Rest = x.Sum(g => (g.AmountPlus - g.AmountMinus)) CurrencyShortName = x.Currency?.ShortName,
}) CurrencyRate = x.Currency?.CurrentRate?.Rate,
.ToList(); CurrencyQuantity = x.Currency?.CurrentRate?.Quantity,
}).ToList();
} }
} }
@@ -5,10 +5,13 @@ using Models.Currencies;
public class CurrencyRateRepository : AppRepository<CurrencyRate>, ICurrencyRateRepository public class CurrencyRateRepository : AppRepository<CurrencyRate>, ICurrencyRateRepository
{ {
public List<CurrencyRate> GetLastRates(Guid currencyId, int count = 1) public List<CurrencyRate> GetLastRates(Guid currencyId, DateTime? before = null, int count = 1)
{ {
before = before ?? DateTime.UtcNow;
return _context.CurrencyRates return _context.CurrencyRates
.Where(x => x.CurrencyId == currencyId) .Where(x => x.CurrencyId == currencyId)
.Where(x => x.DateTime <= before)
.OrderByDescending(x => x.DateTime) .OrderByDescending(x => x.DateTime)
.ThenByDescending(x => x.Id) .ThenByDescending(x => x.Id)
.Take(count) .Take(count)
@@ -4,7 +4,7 @@ using Models.Currencies;
public interface ICurrencyRateRepository public interface ICurrencyRateRepository
{ {
List<CurrencyRate> GetLastRates(Guid currencyId, int count = 1); List<CurrencyRate> GetLastRates(Guid currencyId, DateTime? before = null, int count = 1);
List<CurrencyRate> GetLastRates(List<string> currencyIds); List<CurrencyRate> GetLastRates(List<string> currencyIds);
bool AddRate(CurrencyRate currencyRate); bool AddRate(CurrencyRate currencyRate);
List<CurrencyRate> GetAtDate(Guid currencyId, DateTime date); List<CurrencyRate> GetAtDate(Guid currencyId, DateTime date);
+2 -1
View File
@@ -118,7 +118,8 @@ public class AppDbContext : DbContext
modelBuilder.Entity<Currency>() modelBuilder.Entity<Currency>()
.HasOne(x => x.CurrentRate) .HasOne(x => x.CurrentRate)
.WithOne(x => x.Currency); .WithMany(x => x.Currencies)
.HasForeignKey(x => x.CurrentRateId);
} }
private void AccountCreating(ModelBuilder modelBuilder) private void AccountCreating(ModelBuilder modelBuilder)
@@ -38,6 +38,7 @@ public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
} }
var db = new AppDbContext(providerEnum, builder.Options); var db = new AppDbContext(providerEnum, builder.Options);
db.ChangeTracker.AutoDetectChangesEnabled = false;
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
return db; return db;
+6 -3
View File
@@ -55,6 +55,9 @@ public class RepositoryInitializer
dbContext.SaveChanges(); dbContext.SaveChanges();
} }
/// <summary>
/// Update all currency - set current rate
/// </summary>
private static void UpdateCurrentRates(AppDbContext dbContext) private static void UpdateCurrentRates(AppDbContext dbContext)
{ {
var lastrates = dbContext.CurrencyRates var lastrates = dbContext.CurrencyRates
@@ -62,7 +65,7 @@ public class RepositoryInitializer
.GroupBy(x => new .GroupBy(x => new
{ {
x.CurrencyId, 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(); .ToList();
var currencies = dbContext.Currencies.ToList(); var currencies = dbContext.Currencies.ToList();
@@ -71,8 +74,8 @@ public class RepositoryInitializer
var rate = lastrates.FirstOrDefault(x => x.CurrencyId == currency.Id); var rate = lastrates.FirstOrDefault(x => x.CurrencyId == currency.Id);
if (rate != null) if (rate != null)
{ {
//currency.CurrentRateId = rate.Id; currency.CurrentRateId = rate.Id;
dbContext.Attach(currency); dbContext.Attach(currency).State = EntityState.Modified;
} }
} }
@@ -0,0 +1,633 @@
// <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("20230721180851_CurrentRateV2")]
partial class CurrentRateV2
{
/// <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.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
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<Guid>("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<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.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
}
}
}
@@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class CurrentRateV2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
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");
}
/// <inheritdoc />
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");
}
}
}
@@ -175,6 +175,9 @@ namespace MyOffice.Migrations.Postgres.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary") b.Property<bool>("IsPrimary")
.HasColumnType("boolean"); .HasColumnType("boolean");
@@ -193,6 +196,8 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.HasIndex("CurrencyGlobalId"); b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("Currencies"); b.ToTable("Currencies");
@@ -479,6 +484,10 @@ namespace MyOffice.Migrations.Postgres.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyRate", "CurrentRate")
.WithMany("Currencies")
.HasForeignKey("CurrentRateId");
b.HasOne("MyOffice.Data.Models.Users.User", "User") b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("Currencies") .WithMany("Currencies")
.HasForeignKey("UserId") .HasForeignKey("UserId")
@@ -487,6 +496,8 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Navigation("CurrencyGlobal"); b.Navigation("CurrencyGlobal");
b.Navigation("CurrentRate");
b.Navigation("User"); b.Navigation("User");
}); });
@@ -579,6 +590,11 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Navigation("Currencies"); b.Navigation("Currencies");
}); });
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Navigation("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b => modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{ {
b.Navigation("Motions"); b.Navigation("Motions");
@@ -5,109 +5,49 @@
<app-breadcrumb [title]="'Dashboad 2'" [items]="['Home']" [active_item]="'Dashboad 2'"></app-breadcrumb> <app-breadcrumb [title]="'Dashboad 2'" [items]="['Home']" [active_item]="'Dashboad 2'"></app-breadcrumb>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12 col-sm-6 col-md-3"> <div class="col-4 col-sm-4 col-md-4">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div> <div>
<h5>Income</h5> <h5>Balance</h5>
<p class="text-muted">Income last 7 days</p>
</div>
<h3 class="text-info">{{dashboardModel?.incomeLastWeek | number: '1.2-6'}}</h3>
</div>
<div class="card-content mt-2">
<div class="progress skill-progress m-b-5 w-100">
<div class="progress-bar l-bg-purple width-per-45" role="progressbar" aria-valuenow="45"
aria-valuemin="0" aria-valuemax="100">
</div> </div>
<h3 class="text-danger">{{dashboardModel?.balance | number: '0.2-2'}}</h3>
</div> </div>
</div> </div>
<div class="d-flex justify-content-between mt-2" title="{{dashboardModel?.incomePreviousWeek}}"> </div>
<p class="text-muted mb-0">Change previous 7 days</p> </div>
<p class="text-muted mb-0">{{dashboardModel?.incomeChange | number: '1.2'}}%</p>
<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</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</h5>
</div>
<h3 class="text-danger">{{dashboardModel?.balanceCredit | number: '0.2-2'}}</h3>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-sm-6 col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>Outcome</h5>
<p class="text-muted">Outcome last 7 days</p>
</div>
<h3 class="text-success">{{dashboardModel?.outcomeLastWeek | number: '1.2-6'}}</h3>
</div>
<div class="card-content mt-2">
<div class="progress skill-progress m-b-5 w-100">
<div class="progress-bar l-bg-orange width-per-45" role="progressbar" aria-valuenow="45"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-2" title="{{dashboardModel?.outcomePreviousWeek}}">
<p class="text-muted mb-0">Change previous 7 days</p>
<p class="text-muted mb-0">{{dashboardModel?.outcomeChange | number: '1.2'}}%</p>
</div>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>New Orders</h5>
<p class="text-muted">Fresh New Order</p>
</div>
<h3 class="text-danger">15</h3>
</div>
<div class="card-content mt-2">
<div class="progress skill-progress m-b-5 w-100">
<div class="progress-bar l-bg-cyan width-per-45" role="progressbar" aria-valuenow="45" aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="d-flex justify-content-between">
<p class="text-muted mb-0">Change</p>
<p class="text-muted mb-0">50%</p>
</div>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>New Users</h5>
<p class="text-muted">Joined New User</p>
</div>
<h3 class="text-secondary">12</h3>
</div>
<div class="card-content mt-2">
<div class="progress skill-progress m-b-5 w-100">
<div class="progress-bar l-bg-red width-per-45" role="progressbar" aria-valuenow="45" aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-2">
<p class="text-muted mb-0">Change</p>
<p class="text-muted mb-0">25%</p>
</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">
@@ -65,8 +65,8 @@ export class Dashboard2Component implements OnInit {
this.httpClient this.httpClient
.get<DashboardModel>(ApiRoutes.Dashboard) .get<DashboardModel>(ApiRoutes.Dashboard)
.subscribe(data => { .subscribe(data => {
data.incomeChange = this.calcChanges(data.incomeLastWeek, data.incomePreviousWeek); data.incomeChange = this.calcChanges(data.incomeLast, data.incomePrevious);
data.outcomeChange = this.calcChanges(data.outcomeLastWeek, data.outcomePreviousWeek); data.outcomeChange = this.calcChanges(data.outcomeLast, data.outcomePrevious);
this.dashboardModel = data; this.dashboardModel = data;
}); });
} }
@@ -1,8 +1,11 @@
export interface DashboardModel { export interface DashboardModel {
incomeLastWeek: number; incomeLast: number;
incomePreviousWeek: number; incomePrevious: number;
incomeChange: number; incomeChange: number;
outcomeLastWeek: number; outcomeLast: number;
outcomePreviousWeek: number; outcomePrevious: number;
outcomeChange: number; outcomeChange: number;
balance: number;
balanceDebit: number;
balanceCredit: number;
} }
+11 -4
View File
@@ -1,6 +1,7 @@
namespace MyOffice.Services.Currency; namespace MyOffice.Services.Currency;
using Core; using Core;
using Core.Extensions;
using Data.Models.Currencies; using Data.Models.Currencies;
using Data.Repositories.Currency; using Data.Repositories.Currency;
using MyOffice.Services.Currency.Domain; using MyOffice.Services.Currency.Domain;
@@ -37,7 +38,7 @@ public class CurrencyService
var list = _currencyRepository.GetAll(userId); var list = _currencyRepository.GetAll(userId);
return list.ToDictionary( return list.ToDictionary(
x => x, 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<CurrencyRate, CurrencyAddRateStatus>(CurrencyAddRateStatus.success); var result = new Exec<CurrencyRate, CurrencyAddRateStatus>(CurrencyAddRateStatus.success);
var exists = _currencyRepository.Get(userId, currencyId); var currency = _currencyRepository.Get(userId, currencyId);
if (exists == null) if (currency == null)
{ {
return result.Set(CurrencyAddRateStatus.not_found); return result.Set(CurrencyAddRateStatus.not_found);
} }
currencyRate.CurrencyId = exists.Id; currencyRate.CurrencyId = currency.Id;
currencyRate.DateTime = currencyRate.DateTime.Date; currencyRate.DateTime = currencyRate.DateTime.Date;
var rates = _currencyRateRepository.GetAtDate(currencyRate.CurrencyId, currencyRate.DateTime); var rates = _currencyRateRepository.GetAtDate(currencyRate.CurrencyId, currencyRate.DateTime);
@@ -132,6 +133,12 @@ public class CurrencyService
} }
rate = currencyRate; rate = currencyRate;
if (rate.DateTime.EndOfDay() <= DateTime.UtcNow.EndOfDay())
{
currency.CurrentRateId = rate.Id;
_currencyRepository.Update(currency);
}
} }
return result.Set(rate); return result.Set(rate);
+15 -21
View File
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Core.Extensions; using Core.Extensions;
using Data.Repositories.Account; using Data.Repositories.Account;
using Data.Repositories.Currency; using Data.Repositories.Currency;
using MyOffice.Data.Models.Accounts;
public class DashboardService public class DashboardService
{ {
@@ -26,30 +27,23 @@ public class DashboardService
public DashboardData GetDashboardRestData(Guid userId) public DashboardData GetDashboardRestData(Guid userId)
{ {
var lastDaysStart = DateTime.UtcNow.AddDays(-7).StartOfDay(); /*var lastDaysStart = DateTime.UtcNow.AddMonths(-3).StartOfDay();
var lastDaysEnd = lastDaysStart.AddDays(7).EndOfDay(); var lastDaysEnd = lastDaysStart.AddMonths(3).EndOfDay();
var previousDaysStart = lastDaysStart.AddDays(-7).StartOfDay(); var previousDaysStart = lastDaysStart.AddMonths(-3).StartOfDay();
var previousDaysEnd = previousDaysStart.AddDays(7).EndOfDay(); var previousDaysEnd = previousDaysStart.AddMonths(3).EndOfDay();*/
var rests = _accountRepository.GetRestAtDate(userId, DateTime.UtcNow); var rests = _accountRepository.GetRestAtDate(userId, DateTime.UtcNow);
var currencyIds = rests var result = new DashboardData
.Select(x => x.CurrencyId)
.Distinct()
.ToList();
var lastrates = _currencyRateRepository.GetLastRates(currencyIds);
foreach (var rest in rests)
{ {
//var rate = lastrates.FirstOrDefault(x => x.CurrencyId == rest.CurrencyId) //IncomeLast = _accountRepository.GetPeriodIncome(userId, lastDaysStart, lastDaysEnd),
} //IncomePrevious = _accountRepository.GetPeriodIncome(userId, previousDaysStart, previousDaysEnd),
//OutcomeLast = _accountRepository.GetPeriodOutcome(userId, lastDaysStart, lastDaysEnd),
return new DashboardData //OutcomePrevious = _accountRepository.GetPeriodOutcome(userId, previousDaysStart, previousDaysEnd),
{ Balance = rests.Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)),
IncomeLastWeek = _accountRepository.GetPeriodIncome(userId, lastDaysStart, lastDaysEnd), BalanceDebit = rests.Where(x => x.Balance > 0).Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)),
IncomePreviousWeek = _accountRepository.GetPeriodIncome(userId, previousDaysStart, previousDaysEnd), BalanceCredit = rests.Where(x => x.Balance < 0).Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)),
OutcomeLastWeek = _accountRepository.GetPeriodOutcome(userId, lastDaysStart, lastDaysEnd),
OutcomePreviousWeek = _accountRepository.GetPeriodOutcome(userId, previousDaysStart, previousDaysEnd),
Rests = rests,
}; };
return result;
} }
} }
@@ -1,23 +1,25 @@
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; using Data.Models.Accounts;
public class DashboardData public class DashboardData
{ {
public decimal IncomeLastWeek { get; set; } public decimal IncomeLast { get; set; }
public decimal IncomePreviousWeek { get; set; } public decimal IncomePrevious { get; set; }
public decimal OutcomeLastWeek { get; set; } public decimal OutcomeLast { get; set; }
public decimal OutcomePreviousWeek { get; set; } public decimal OutcomePrevious { get; set; }
public List<AccountSimple>? Rests { get; set; } public decimal? Balance { get; set; }
public decimal? RestTotal => Rests?.Sum(x => x.Rest); public decimal? BalanceDebit { get; set; }
public decimal? RestTotalPlus => Rests?.Where(x => x.Rest > 0).Sum(x => x.Rest); public decimal? BalanceCredit { get; set; }
public decimal? RestTotalMinus => Rests?.Where(x => x.Rest < 0).Sum(x => x.Rest);
} }
/*public class DashboardRestData public class DashboardRestData
{ {
public string AccountId { get; set; } public AccountSimple AccountSimple { get; set; }
public string AccountName { get; set; } public decimal? Balance => AccountSimple.Balance;
public decimal Rest { get; set; }
}*/ public decimal? BalanceAtRate =>
AccountSimple.Balance * (AccountSimple.CurrencyRate * AccountSimple.CurrencyQuantity);
}
@@ -28,9 +28,9 @@
Symbol = input.CurrencyGlobal!.Symbol, Symbol = input.CurrencyGlobal!.Symbol,
Name = input.Name, Name = input.Name,
ShortName = input.ShortName, ShortName = input.ShortName,
Quantity = rate?.Quantity, Quantity = input.CurrentRate?.Quantity ?? rate?.Quantity,
Rate = rate?.Rate, Rate = input.CurrentRate?.Rate ?? rate?.Rate,
RateDate = rate?.DateTime, RateDate = input.CurrentRate?.DateTime ?? rate?.DateTime,
IsPrimary = input.IsPrimary, IsPrimary = input.IsPrimary,
}; };
} }