This commit is contained in:
2023-07-23 20:27:23 +03:00
parent d0cdaa85b6
commit 96eca795ab
62 changed files with 2934 additions and 847 deletions
+6 -11
View File
@@ -1,22 +1,16 @@
namespace MyOffice.Data.Models.Accounts;
using Currencies;
public enum AccountTypeEnum
{
debit,
credit,
transit,
other,
}
using MyOffice.Data.Models.Users;
public class Account
{
public Guid Id { get; set; }
public string CurrencyGlobalId { get; set; } = null!;
public CurrencyGlobal? CurrencyGlobal { get; set; }
public Guid? OwnerId { get; set; }
public User? Owner { get; set; }
public string Name { get; set; } = null!;
public AccountTypeEnum Type { get; set; }
public IEnumerable<AccountAccess>? AccessRights { get; set; }
public IEnumerable<Motion>? Motions { get; set; }
public IEnumerable<AccountAccountCategory>? Categories { get; set; }
@@ -34,8 +28,9 @@ public struct AccountSimple
{
public Guid Id { get; set; }
public string Name { get; set; }
public string? CurrencyName { get; set; }
public string? CurrencyShortName { get; set; }
public AccountAccessTypeEnum Type { 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; }
@@ -2,6 +2,14 @@
using MyOffice.Data.Models.Users;
public enum AccountAccessTypeEnum
{
balance,
credit,
external,
other,
}
public class AccountAccess
{
public int Id { get; set; }
@@ -13,4 +21,5 @@ public class AccountAccess
public bool IsAllowRead { get; set; }
public bool IsAllowWrite { get; set; }
public bool IsAllowManage { get; set; }
public AccountAccessTypeEnum Type { get; set; }
}
@@ -2,6 +2,19 @@
using MyOffice.Data.Models.Accounts;
public enum CurrencyGlobalIdEnum
{
UAH,
USD,
EUR,
GBP,
RUB,
BTC,
ETH,
TON,
OTHER,
}
public class CurrencyGlobal
{
public string Id { get; set; } = null!;
+1
View File
@@ -29,4 +29,5 @@ public class User
public IEnumerable<Motion>? AccountMotions { get; set; }
public IEnumerable<AccountCategory>? AccountCategories { get; set; }
public IEnumerable<ItemCategory>? ItemCategories { get; set; }
public IEnumerable<Account>? Accounts { get; set; }
}
@@ -0,0 +1,13 @@
namespace MyOffice.Data.Repositories.Account;
using Models.Accounts;
using MyOffice.Data.Repositories;
public class AccountAccessRepository : AppRepository<AccountAccess>, IAccountAccessRepository
{
public bool Update(AccountAccess accountAccess)
{
return UpdateBase(accountAccess) > 0;
}
}
@@ -1,9 +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;
public class AccountRepository : AppRepository<Account>, IAccountRepository
@@ -16,6 +14,7 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
.ThenInclude(x => x.Category)
.Include(x => x.AccessRights)!
.ThenInclude(x => x.User)
.Include(x => x.Motions)!
.Where(x => x.AccessRights!.Any(a => a.UserId == userId))
.ToList();
}
@@ -28,6 +27,7 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
.ThenInclude(x => x.Category)
.Include(x => x.AccessRights)!
.ThenInclude(x => x.User)
.Include(x => x.Motions)!
.Where(x => x.Categories!.Any(c => c.CategoryId == categoryId) && x.AccessRights!.Any(a => a.UserId == userId))
.ToList();
}
@@ -73,6 +73,11 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
return AddBase(account) > 0;
}
public bool Delete(Account account)
{
return RemoveBase(account) > 0;
}
public Account? Get(Guid userId, Guid id)
{
return _context.Accounts!
@@ -80,6 +85,7 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
.ThenInclude(x => x.Category)
.Include(x => x.AccessRights)!
.ThenInclude(x => x.User)
.Include(x => x.Motions)!
.FirstOrDefault(x => x.Id == id && x.AccessRights!.Any(r => r.UserId == userId));
}
@@ -141,27 +147,42 @@ public class AccountRepository : AppRepository<Account>, IAccountRepository
public List<AccountSimple> GetRestAtDate(Guid userId, DateTime date)
{
return _context.Accounts
.Include(x => x.CurrencyGlobal!)
return _context.AccountAccesses!
.Where(x => x.UserId == userId)
.Include(x => x.Account!)
.ThenInclude(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))
.Include(x => x.Account)
.ThenInclude(x => x!.Motions)
.Select(x => new
{
Id = x.AccountId,
Name = x.Account!.Name,
Type = x.Type,
Currency = x.Account!.CurrencyGlobal!.Currencies!.FirstOrDefault(c => c.UserId == userId),
CurrentRate = x.Account!.CurrencyGlobal!.Currencies!.FirstOrDefault(c => c.UserId == userId)!.CurrentRate,
Plus = x.Account.Motions!
.Where(x => !x.DeletedOn.HasValue)
.Sum(m => m.AmountPlus),
Minus = x.Account.Motions!
.Where(x => !x.DeletedOn.HasValue)
.Sum(m => m.AmountMinus),
})
.ToList()
.Select(x => new AccountSimple
{
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();
Type = x.Type,
CurrencyName = x.Currency!.Name,
CurrencyShortName = x.Currency!.ShortName,
CurrencyRate = x.CurrentRate?.Rate,
CurrencyQuantity = x.CurrentRate?.Quantity,
TotalMinus = x.Minus,
TotalPlus = x.Plus,
Balance = x.Plus - x.Minus,
})
.ToList();
}
}
@@ -0,0 +1,8 @@
namespace MyOffice.Data.Repositories.Account;
using Models.Accounts;
public interface IAccountAccessRepository
{
bool Update(AccountAccess accountAccess);
}
@@ -9,6 +9,7 @@ public interface IAccountRepository
List<AccountDetailed> GetByCategoryDetailed(Guid userId, Guid categoryId);
AccountDetailed? GetByIdDetailed(Guid userId, Guid id);
bool Add(Account account);
bool Delete(Account account);
Account? Get(Guid userId, Guid id);
bool Update(Account account);
bool Remove(Account account);
@@ -11,7 +11,7 @@ public class ItemRepository : AppRepository<Item>, IItemRepository
.Include(x => x.Motions)
.Include(x => x.Category)
.Include(x => x.ItemGlobal)
.Where(x => x.Category.UserId == userId)
.Where(x => x.Category!.UserId == userId)
.ToList();
}
@@ -21,7 +21,7 @@ public class ItemRepository : AppRepository<Item>, IItemRepository
.Include(x => x.Motions)
.Include(x => x.Category)
.Include(x => x.ItemGlobal)
.Where(x => x.Category.UserId == userId && x.CategoryId == categoryId)
.Where(x => x.Category!.UserId == userId && x.CategoryId == categoryId)
.ToList();
}
@@ -50,7 +50,7 @@ public class ItemRepository : AppRepository<Item>, IItemRepository
.Items
.Include(x => x.ItemGlobal)
.Where(x => x.Category!.UserId == userId && x.ItemGlobal.Name.Contains(term))
.OrderByDescending(x => x.Motions.Count())
.OrderByDescending(x => x!.Motions!.Count())
.Take(limit)
.ToList();
}
@@ -44,6 +44,11 @@ public class RepositoryBase<TEntity> where TEntity : class
return await _context.Set<TEntity>().FindAsync(id);
}
protected TEntity? GetBase(Guid id)
{
return _context.Set<TEntity>().Find(id);
}
protected async Task<int> AddBaseAsync(TEntity entity)
{
//await _context.Set<TEntity>().AddAsync(entity);
@@ -5,6 +5,7 @@ using MyOffice.Data.Models.Users;
public interface IUserRepository
{
Task<User?> GetUserAsync(Guid id);
User? GetUser(Guid id);
Task<User?> GetByUserUserNameAsync(string userName);
User? GetByUserUserName(string userName);
@@ -11,6 +11,11 @@ public class UserRepository : AppRepository<User>, IUserRepository
return await GetBaseAsync(id);
}
public User? GetUser(Guid id)
{
return GetBase(id);
}
public async Task<User?> GetByUserUserNameAsync(string userName)
{
return await _context.Users.FirstOrDefaultAsync(x => x.UserName == userName);
+11
View File
@@ -5,6 +5,7 @@ using Data.Models.Currencies;
using Data.Models.Items;
using Microsoft.EntityFrameworkCore;
using Data.Models.Users;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
public enum AppDbContextProvidersEnum
{
@@ -144,6 +145,11 @@ public class AppDbContext : DbContext
.WithMany(x => x.Accounts)
.HasForeignKey(x => x.CurrencyGlobalId);
modelBuilder.Entity<Account>()
.HasOne(x => x.Owner)
.WithMany(x => x.Accounts)
.HasForeignKey(x => x.OwnerId);
modelBuilder.Entity<AccountAccess>()
.HasOne(x => x.Account)
.WithMany(x => x.AccessRights)
@@ -173,6 +179,11 @@ public class AppDbContext : DbContext
.HasOne(x => x.Category)
.WithMany(x => x.Accounts)
.HasForeignKey(x => x.CategoryId);
modelBuilder
.Entity<AccountAccess>()
.Property(d => d.Type)
.HasConversion(new EnumToStringConverter<AccountAccessTypeEnum>());
}
private void MotionsCreating(ModelBuilder modelBuilder)
+9 -9
View File
@@ -32,15 +32,15 @@ public class RepositoryInitializer
// CurrencyGlobals
var predefinedCurrencyGlobals = new List<CurrencyGlobal>()
{
new() { Id = "UAH", DefaultQuantity = 1, Symbol = "₴", Name = "Ukrainian hryvnias" },
new() { Id = "USD", DefaultQuantity = 1, Symbol = "$", Name = "US Dollar" },
new() { Id = "EUR", DefaultQuantity = 1, Symbol = "€", Name = "Euros" },
new() { Id = "GBP", DefaultQuantity = 1, Symbol = "£", Name = "British pounds sterling" },
new() { Id = "RUB", DefaultQuantity = 10, Symbol = "₽", Name = "Russia Ruble" },
new() { Id = "BTC", DefaultQuantity = 10, Symbol = "btc", Name = "Bitcoin" },
new() { Id = "ETH", DefaultQuantity = 10, Symbol = "eth", Name = "Ethereum" },
new() { Id = "TON", DefaultQuantity = 10, Symbol = "ton", Name = "TON" },
new() { Id = "OTHER", DefaultQuantity = 1, Symbol = "", Name = "Other" },
new() { Id = CurrencyGlobalIdEnum.UAH.ToString(), DefaultQuantity = 1, Symbol = "₴", Name = "Ukrainian hryvnias" },
new() { Id = CurrencyGlobalIdEnum.USD.ToString(), DefaultQuantity = 1, Symbol = "$", Name = "US Dollar" },
new() { Id = CurrencyGlobalIdEnum.EUR.ToString(), DefaultQuantity = 1, Symbol = "€", Name = "Euros" },
new() { Id = CurrencyGlobalIdEnum.GBP.ToString(), DefaultQuantity = 1, Symbol = "£", Name = "British pounds sterling" },
new() { Id = CurrencyGlobalIdEnum.RUB.ToString(), DefaultQuantity = 10, Symbol = "₽", Name = "Russia Ruble" },
new() { Id = CurrencyGlobalIdEnum.BTC.ToString(), DefaultQuantity = 10, Symbol = "btc", Name = "Bitcoin" },
new() { Id = CurrencyGlobalIdEnum.ETH.ToString(), DefaultQuantity = 10, Symbol = "eth", Name = "Ethereum" },
new() { Id = CurrencyGlobalIdEnum.TON.ToString(), DefaultQuantity = 10, Symbol = "ton", Name = "TON" },
new() { Id = CurrencyGlobalIdEnum.OTHER.ToString(), DefaultQuantity = 1, Symbol = "", Name = "Other" },
};
var currencyGlobals = dbContext.CurrencyGlobals.ToList();
@@ -0,0 +1,636 @@
// <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("20230721200424_AccountType")]
partial class AccountType
{
/// <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<int>("Type")
.HasColumnType("integer");
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,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccountType : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "Type",
table: "Accounts",
type: "integer",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Type",
table: "Accounts");
}
}
}
@@ -0,0 +1,637 @@
// <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("20230722051335_AccountType2")]
partial class AccountType2
{
/// <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<string>("Type")
.IsRequired()
.HasColumnType("text");
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,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccountType2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Type",
table: "AccountAccesses",
type: "text",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Type",
table: "AccountAccesses");
}
}
}
@@ -0,0 +1,650 @@
// <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("20230722052053_AccountOwner")]
partial class AccountOwner
{
/// <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>("Type")
.IsRequired()
.HasColumnType("text");
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.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", "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("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,49 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccountOwner : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "OwnerId",
table: "Accounts",
type: "uuid",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Accounts_OwnerId",
table: "Accounts",
column: "OwnerId");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_Users_OwnerId",
table: "Accounts",
column: "OwnerId",
principalTable: "Users",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Accounts_Users_OwnerId",
table: "Accounts");
migrationBuilder.DropIndex(
name: "IX_Accounts_OwnerId",
table: "Accounts");
migrationBuilder.DropColumn(
name: "OwnerId",
table: "Accounts");
}
}
}
@@ -37,10 +37,15 @@ namespace MyOffice.Migrations.Postgres.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
@@ -64,6 +69,10 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
@@ -401,7 +410,13 @@ namespace MyOffice.Migrations.Postgres.Migrations
.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 =>
@@ -618,6 +633,8 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
+2
View File
@@ -23,6 +23,7 @@ import { AuthCodeFlowConfig, AuthModuleConfig } from '../config.oidc';
import { SubjectExtensions } from './extensions/general.extensions';
import { ExternalLoginConfig } from '../config.external-login';
import { AccountCategoryService } from '../services/account.category.service';
import { AccountService } from '../services/account.service';
export function storageFactory(): OAuthStorage {
return localStorage;
@@ -49,6 +50,7 @@ export function storageFactory(): OAuthStorage {
OidcHelperService,
SubjectExtensions,
AccountCategoryService,
AccountService,
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
@@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core';
providedIn: 'root',
})
export class LanguageService {
languages: string[] = ['en', 'es', 'de'];
languages: string[] = ['en', 'es', 'de', 'ua'];
constructor(public translate: TranslateService) {
let browserLang: string;
@@ -16,7 +16,8 @@ export class LanguageService {
} else {
browserLang = translate.getBrowserLang() as string;
}
translate.use(browserLang.match(/en|es|de/) ? browserLang : 'en');
console.log(browserLang.match(/en|es|de|ua/));
translate.use(browserLang.match(/en|es|de|ua/) ? browserLang : 'en');
}
setLanguage(lang: string) {
@@ -1,9 +1,9 @@
// angular
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DecimalPipe } from '@angular/common';
import { DashboardRoutingModule } from './dashboard-routing.module';
import { Dashboard1Component } from './dashboard1/dashboard1.component';
import { Dashboard2Component } from './dashboard2/dashboard2.component';
// libs
import { NgScrollbarModule } from 'ngx-scrollbar';
import { NgChartsModule } from 'ng2-charts';
import { MatIconModule } from '@angular/material/icon';
@@ -14,6 +14,12 @@ import { DragDropModule } from '@angular/cdk/drag-drop';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatTooltipModule } from '@angular/material/tooltip';
import { NgApexchartsModule } from 'ng-apexcharts';
import { TranslateModule } from '@ngx-translate/core';
// app
import { DashboardRoutingModule } from './dashboard-routing.module';
import { Dashboard1Component } from './dashboard1/dashboard1.component';
import { Dashboard2Component } from './dashboard2/dashboard2.component';
import { ComponentsModule } from 'src/app/shared/components/components.module';
import { SharedModule } from '../shared/shared.module';
@@ -34,7 +40,11 @@ import { SharedModule } from '../shared/shared.module';
MatProgressBarModule,
ComponentsModule,
SharedModule,
TranslateModule,
],
providers: [
DecimalPipe,
]
})
export class DashboardModule {
}
@@ -2,7 +2,7 @@
<div class="content-block">
<div class="block-header">
<!-- breadcrumb -->
<app-breadcrumb [title]="'Dashboad 2'" [items]="['Home']" [active_item]="'Dashboad 2'"></app-breadcrumb>
<app-breadcrumb [title]="'MENUITEMS.DASHBOARD.LIST.DASHBOARD1'" [items]="['HOME']" [active_item]="'MENUITEMS.DASHBOARD.LIST.DASHBOARD1'"></app-breadcrumb>
</div>
<div class="row">
@@ -12,7 +12,7 @@
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>Balance</h5>
<h5>{{'BALANCE' | translate}}</h5>
</div>
<h3 class="text-danger">{{dashboardModel?.balance | number: '0.2-2'}}</h3>
</div>
@@ -25,7 +25,7 @@
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>Debit balance</h5>
<h5>{{'DEBIT.BALANCE' | translate}}</h5>
<p class="text-muted"></p>
</div>
<h3 class="text-success">{{dashboardModel?.balanceDebit | number: '0.2-2'}}</h3>
@@ -39,7 +39,7 @@
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>Credit balance</h5>
<h5>{{'CREDIT.BALANCE' | translate}}</h5>
</div>
<h3 class="text-danger">{{dashboardModel?.balanceCredit | number: '0.2-2'}}</h3>
</div>
@@ -53,7 +53,7 @@
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12">
<div class="card">
<div class="header">
<h2>Current rest</h2>
<h2>{{'CURRENT.BALANCE' | translate}}</h2>
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
@@ -65,491 +65,32 @@
</div>
<div class="body">
<div id="chart">
<apx-chart [series]="pieChartOptions.series2!" [chart]="pieChartOptions.chart!"
[labels]="pieChartOptions.labels!" [responsive]="pieChartOptions.responsive!"
[dataLabels]="pieChartOptions.dataLabels!" [legend]="pieChartOptions.legend!" class="apex-pie-center">
<apx-chart #chart *ngIf="pieChartOptions" class="apex-pie-center"
[series]="pieChartOptions.series2!"
[chart]="pieChartOptions.chart!"
[labels]="pieChartOptions.labels!"
[responsive]="pieChartOptions.responsive!"
[tooltip]="pieChartOptions.tooltip!"
>
</apx-chart>
</div>
<div class="table-responsive m-t-15">
<table class="table align-items-center">
<tbody>
<tr>
<td><i class="fa fa-circle col-cyan msr-2"></i> India</td>
<td>23</td>
<td class="col-green">+32%</td>
<tr *ngFor="let item of top10Balance">
<td><i class="fa fa-circle col-cyan msr-2"></i> {{item.name}}</td>
<td class="col-green" style="text-align: right;">{{item.balance | number: '1.2'}} ({{item.currencyShortName}})</td>
<td class="col-green" style="text-align: right;">{{item.balanceAtRate | number: '1.2'}}</td>
</tr>
<tr>
<td><i class="fa fa-circle col-blue msr-2"></i>USA</td>
<td>32</td>
<td class="col-green">+12%</td>
<td><i class="fa fa-circle col-green msr-2"></i> {{'OTHER' | translate}}</td>
<td></td>
<td class="col-green" style="text-align: right;">{{top10BalanceOther | number: '1.2'}}</td>
</tr>
<tr>
<td><i class="fa fa-circle col-orange msr-2"></i>Shrilanka</td>
<td>12</td>
<td class="col-orange">-12%</td>
</tr>
<tr>
<td><i class="fa fa-circle col-green msr-2"></i>Australia</td>
<td>32</td>
<td class="col-green">+3%</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
<div class="card">
<div class="header">
<h2>Notice Board</h2>
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Action</button>
<button mat-menu-item>Another action</button>
<button mat-menu-item>Something else here</button>
</mat-menu>
</div>
<div class="body">
<ng-scrollbar style="height: 380px" visibility="hover">
<div class="recent-comment">
<div class="notice-board">
<div class="table-img">
<img class="notice-object" src="assets/images/user/user6.jpg" alt="...">
</div>
<div class="notice-body">
<h6 class="notice-heading col-green">Airi Satou</h6>
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
<small class="text-muted">7 hours ago</small>
</div>
</div>
<div class="notice-board">
<div class="table-img">
<img class="notice-object" src="assets/images/user/user4.jpg" alt="...">
</div>
<div class="notice-body">
<h6 class="notice-heading color-primary col-indigo">Sarah Smith</h6>
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
<p class="comment-date">1 hour ago</p>
</div>
</div>
<div class="notice-board">
<div class="table-img">
<img class="notice-object" src="assets/images/user/user3.jpg" alt="...">
</div>
<div class="notice-body">
<h6 class="notice-heading color-danger col-cyan">Cara Stevens</h6>
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
<div class="comment-date">Yesterday</div>
</div>
</div>
<div class="notice-board no-border">
<div class="table-img">
<img class="notice-object" src="assets/images/user/user7.jpg" alt="...">
</div>
<div class="notice-body">
<h6 class="notice-heading color-info col-orange">Ashton Cox</h6>
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
<div class="comment-date">Yesterday</div>
</div>
</div>
<div class="notice-board">
<div class="table-img">
<img class="notice-object" src="assets/images/user/user9.jpg" alt="...">
</div>
<div class="notice-body">
<h6 class="notice-heading color-primary col-red">Mark Hay</h6>
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
<p class="comment-date">1 hour ago</p>
</div>
</div>
<div class="notice-board">
<div class="table-img">
<img class="notice-object" src="assets/images/user/user8.jpg" alt="...">
</div>
<div class="notice-body">
<h6 class="notice-heading color-primary col-green">Jay Pandya</h6>
<p>Lorem ipsum dolor sit amet, id quo eruditi eloquentiam.</p>
<p class="comment-date">3 hour ago</p>
</div>
</div>
</div>
</ng-scrollbar>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-4 col-md-12 col-sm-12">
<div class="card">
<div class="header">
<h2>Project Status</h2>
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Action</button>
<button mat-menu-item>Another action</button>
<button mat-menu-item>Something else here</button>
</mat-menu>
</div>
<ng-scrollbar style="height: 410px" visibility="hover">
<div class="tableBody">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Project Name</th>
<th>Progress</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
<tr>
<td>Project A</td>
<td>
30%
<mat-progress-bar mode="determinate" value="30" color="warn" class="progress-round">
</mat-progress-bar>
</td>
<td>2 Months</td>
</tr>
<tr>
<td>Project B</td>
<td>
55%
<mat-progress-bar mode="determinate" class="progress-round" value="55">
</mat-progress-bar>
</td>
<td>3 Months</td>
</tr>
<tr>
<td>Project C</td>
<td>
67%
<mat-progress-bar mode="determinate" class="progress-round orange-progress" value="67">
</mat-progress-bar>
</td>
<td>1 Months</td>
</tr>
<tr>
<td>Project D</td>
<td>
70%
<mat-progress-bar mode="determinate" class="progress-round green-progress" value="70">
</mat-progress-bar>
</td>
<td>2 Months</td>
</tr>
<tr>
<td>Project E</td>
<td>
24%
<mat-progress-bar mode="determinate" value="24" class="progress-round l-red-progress">
</mat-progress-bar>
</td>
<td>3 Months</td>
</tr>
<tr>
<td>Project F</td>
<td>
77%
<mat-progress-bar mode="determinate" class="progress-round l-green-progress" value="77">
</mat-progress-bar>
</td>
<td>4 Months</td>
</tr>
<tr>
<td>Project G</td>
<td>
41%
<mat-progress-bar mode="determinate" value="41" class="progress-round l-cyan-progress">
</mat-progress-bar>
</td>
<td>2 Months</td>
</tr>
<tr>
<td>Project H</td>
<td>
41%
<mat-progress-bar mode="determinate" value="41" class="progress-round l-orange-progress">
</mat-progress-bar>
</td>
<td>2 Months</td>
</tr>
</tbody>
</table>
</div>
</div>
</ng-scrollbar>
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-4 col-lg-4">
<div class="card">
<div class="header">
<h2>Earning Source</h2>
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Action</button>
<button mat-menu-item>Another action</button>
<button mat-menu-item>Something else here</button>
</mat-menu>
</div>
<div class="body">
<div class="totalEarning">
<h2>$90,808</h2>
</div>
<div class="tab-pane body" id="skills">
<ul class="list-unstyled">
<li>
<div class="mb-2">
<span class="progress-label">envato.com</span>
<span class="float-end progress-percent label label-info m-b-5">17%</span>
</div>
<div class="progress skill-progress m-b-20 w-100">
<div class="progress-bar l-bg-green width-per-45" role="progressbar" aria-valuenow="45"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</li>
<li>
<div class="mb-2">
<span class="float-start progress-label">google.com</span>
<span class="float-end progress-percent label label-danger m-b-5">27%</span>
</div>
<div class="progress skill-progress m-b-20 w-100">
<div class="progress-bar l-bg-purple width-per-27" role="progressbar" aria-valuenow="27"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</li>
<li>
<div class="mb-2">
<span class="float-start progress-label">yahoo.com</span>
<span class="float-end progress-percent label label-primary m-b-5">25%</span>
</div>
<div class="progress skill-progress m-b-20 w-100">
<div class="progress-bar l-bg-orange width-per-25" role="progressbar" aria-valuenow="25"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</li>
<li>
<div class="mb-2">
<span class="float-start progress-label">store</span>
<span class="float-end progress-percent label label-success m-b-5">18%</span>
</div>
<div class="progress skill-progress m-b-20 w-100">
<div class="progress-bar l-bg-cyan width-per-18" role="progressbar" aria-valuenow="18"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</li>
<li>
<div class="mb-2">
<span class="float-start progress-label">Others</span>
<span class="float-end progress-percent label label-warning m-b-5">13%</span>
</div>
<div class="progress skill-progress m-b-20 w-100">
<div class="progress-bar l-bg-red width-per-13" role="progressbar" aria-valuenow="13"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div class="card">
<div class="header">
<h2>Recent Orders</h2>
<button mat-icon-button [matMenuTriggerFor]="menu" class="header-dropdown">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item>Action</button>
<button mat-menu-item>Another action</button>
<button mat-menu-item>Something else here</button>
</mat-menu>
</div>
<div class="tableBody">
<div class="table-responsive">
<table class="table display product-overview mb-30" id="support_table">
<thead>
<tr>
<th>Order ID</th>
<th>Customer Name</th>
<th>Item</th>
<th>Order Date</th>
<th>Quantity</th>
<th>Payment</th>
<th>Status</th>
<th>Invoice</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID7865</td>
<td>
<img src="assets/images/user/user1.jpg" alt="" class="tbl-user-img-small">Jens
Brincker
</td>
<td>iPhone X</td>
<td>22/05/2021</td>
<td>2</td>
<td>Credit Card</td>
<td>
<div class="badge badge-solid-green">Paid</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID9357</td>
<td>
<img src="assets/images/user/user2.jpg" alt="" class="tbl-user-img-small">Mark Harry
</td>
<td> Pixel 2</td>
<td>12/06/2021</td>
<td>1</td>
<td>COD</td>
<td>
<div class="badge badge-solid-orange">Unpaid</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID3987</td>
<td>
<img src="assets/images/user/user3.jpg" alt="" class="tbl-user-img-small">Anthony
Davie
</td>
<td>OnePlus</td>
<td>02/02/2021</td>
<td>5</td>
<td>Debit Card</td>
<td>
<div class="badge badge-solid-blue">Pending</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID2483</td>
<td>
<img src="assets/images/user/user4.jpg" alt="" class="tbl-user-img-small">David Perry
</td>
<td>Moto Z2</td>
<td>10/01/2021</td>
<td>2</td>
<td>Credit Card</td>
<td>
<div class="badge badge-solid-green">Paid</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID2986</td>
<td>
<img src="assets/images/user/user5.jpg" alt="" class="tbl-user-img-small">John Doe
</td>
<td>Samsung F62</td>
<td>20/05/2021</td>
<td>3</td>
<td>Net Banking</td>
<td>
<div class="badge badge-solid-orange">Unpaid</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID1267</td>
<td>
<img src="assets/images/user/user6.jpg" alt="" class="tbl-user-img-small">Sarah Smith
</td>
<td>iPhone 12</td>
<td>10/07/2021</td>
<td>1</td>
<td>COD</td>
<td>
<div class="badge badge-solid-green">Paid</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID3398</td>
<td>
<img src="assets/images/user/user7.jpg" alt="" class="tbl-user-img-small">Cara Stevens
</td>
<td>Moto Gs5</td>
<td>11/04/2021</td>
<td>2</td>
<td>UPI Payment</td>
<td>
<div class="badge badge-solid-blue">Pending</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
</tr>
<tr>
<td>ID9965</td>
<td>
<img src="assets/images/user/user8.jpg" alt="" class="tbl-user-img-small">Ashton Cox
</td>
<td>Pixel 2</td>
<td>14/05/2021</td>
<td>4</td>
<td>Credit Card</td>
<td>
<div class="badge badge-solid-green">Paid</div>
</td>
<td>
<i class="far fa-file-pdf tbl-pdf"></i>
</td>
<td>
<button mat-stroked-button color="primary">Details</button>
</td>
<td style="text-align: right; font-size: 16px;"><i class="fa fa-circle col-orange msr-2"></i> {{'TOTAL' | translate}}</td>
<td></td>
<td class="col-green" style="text-align: right; font-size: 16px;">{{top10BalanceTotal | number: '1.2'}}</td>
</tr>
</tbody>
</table>
@@ -1,6 +1,9 @@
// angular
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DecimalPipe } from '@angular/common';
// libs
import {
@@ -19,10 +22,12 @@ import {
ApexResponsive,
ApexNonAxisChartSeries,
} from 'ng-apexcharts';
import { ChartComponent } from "ng-apexcharts";
// app
import { ApiRoutes } from '../../api-routes';
import { DashboardModel } from '../../model/dashboard.model';
import { DashboardRestModel } from '../../model/dashboard.model';
export type ChartOptions = {
series: ApexAxisChartSeries;
@@ -49,24 +54,48 @@ export type ChartOptions = {
styleUrls: ['./dashboard2.component.scss'],
})
export class Dashboard2Component implements OnInit {
lineChartOptions!: Partial<ChartOptions>;
pieChartOptions!: Partial<ChartOptions>;
@ViewChild("chart") chart!: ChartComponent;
public pieChartOptions!: Partial<ChartOptions>;
top10Balance?: DashboardRestModel[];
top10BalanceOther?: number;
top10BalanceTotal?: number;
dashboardModel?: DashboardModel;
// color: ["#3FA7DC", "#F6A025", "#9BC311"],
constructor(private httpClient: HttpClient) {
constructor(
private httpClient: HttpClient,
private _decimalPipe: DecimalPipe
) {
}
ngOnInit() {
this.chart1();
this.chart2();
this.httpClient
.get<DashboardModel>(ApiRoutes.Dashboard)
.subscribe(data => {
data.incomeChange = this.calcChanges(data.incomeLast, data.incomePrevious);
data.outcomeChange = this.calcChanges(data.outcomeLast, data.outcomePrevious);
var labels = [];
var series2: number[] = [];
for (var i = 0; i < data.balanceRests.length; i++) {
let item = data.balanceRests[i];
labels.push(item.name);
series2.push(item.balanceAtRate);
}
this.balanceChart(labels, series2);
this.top10Balance = data.balanceRests
.filter(x => x.balanceAtRate > 0)
.sort(x => x.balanceAtRate)
.slice(0, 10);
this.top10BalanceTotal = data.balanceRests.reduce((sum, current) => sum + current.balanceAtRate, 0);
this.top10BalanceOther = this.top10Balance.reduce((sum, current) => sum + current.balanceAtRate, 0);
this.top10BalanceOther = this.top10BalanceTotal - this.top10BalanceOther;
this.dashboardModel = data;
});
}
@@ -82,101 +111,41 @@ export class Dashboard2Component implements OnInit {
return 0;
}
private chart1() {
this.lineChartOptions = {
series: [
{
name: 'Product 1',
data: [70, 200, 80, 180, 170, 105, 210],
},
{
name: 'Product 2',
data: [80, 250, 30, 120, 260, 100, 180],
},
{
name: 'Product 3',
data: [85, 130, 85, 225, 80, 190, 120],
},
],
chart: {
height: 350,
type: 'line',
foreColor: '#9aa0ac',
dropShadow: {
enabled: true,
color: '#000',
top: 18,
left: 7,
blur: 10,
opacity: 0.2,
},
toolbar: {
show: false,
},
},
colors: ['#00E396', '#775DD0', '#FEB019'],
stroke: {
curve: 'smooth',
},
grid: {
show: true,
borderColor: '#9aa0ac',
strokeDashArray: 1,
},
markers: {
size: 3,
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
title: {
text: 'Month',
},
},
yaxis: {
// opposite: true,
title: {
text: 'Sales',
},
},
legend: {
position: 'top',
horizontalAlign: 'right',
floating: true,
offsetY: -25,
offsetX: -5,
},
tooltip: {
theme: 'dark',
marker: {
show: true,
},
x: {
show: true,
},
},
};
}
private balanceChart(labels: string[], series2: number[]) {
var self = this;
private chart2() {
this.pieChartOptions = {
series2: [44, 55, 13, 43, 22],
labels: labels,
series2: series2,
chart: {
type: 'donut',
width: 225,
width: 600,
height: 600,
},
legend: {
show: false,
show: true,
},
dataLabels: {
enabled: false,
},
labels: ['Science', 'Mathes', 'Economics', 'History', 'Music'],
responsive: [
{
breakpoint: 480,
options: {},
responsive: [{
breakpoint: 480,
options: {
legend: {
position: 'bottom'
}
},
],
}],
tooltip: {
x: {
show: false,
},
y: {
formatter: function (value, series) {
return self._decimalPipe.transform(value, '1.2-2') || "";
}
}
}
};
}
}
@@ -67,6 +67,7 @@ export class HeaderComponent extends UnsubscribeOnDestroyAdapter implements OnIn
{ text: 'English', flag: 'assets/images/flags/us.jpg', lang: 'en' },
{ text: 'Spanish', flag: 'assets/images/flags/spain.jpg', lang: 'es' },
{ text: 'German', flag: 'assets/images/flags/germany.jpg', lang: 'de' },
{ text: 'Ukraine', flag: 'assets/images/flags/ukraine.png', lang: 'ua' },
];
notifications: Notifications[] = [
{
@@ -22,20 +22,9 @@ export const ROUTES: RouteInfo[] = [
badge: '',
badgeClass: '',
submenu: [
{
path: 'dashboard/dashboard1',
title: 'MENUITEMS.DASHBOARD.LIST.DASHBOARD1',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: 'dashboard/dashboard2',
title: 'MENUITEMS.DASHBOARD.LIST.DASHBOARD2',
title: 'MENUITEMS.DASHBOARD.LIST.DASHBOARD1',
iconType: '',
icon: '',
class: 'ml-menu',
@@ -48,66 +37,10 @@ export const ROUTES: RouteInfo[] = [
},
// Common Modules
{
path: '',
title: 'Authentication',
iconType: 'feather',
icon: 'user-check',
class: 'menu-toggle',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [
{
path: '/authentication/forgot-password',
title: 'Forgot Password',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: '/authentication/locked',
title: 'Locked',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: '/authentication/page404',
title: '404 - Not Found',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: '/authentication/page500',
title: '500 - Server Error',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
],
},
{
id: 'accounts',
path: '',
title: 'Accounts',
title: 'MENUITEMS.ACCOUNTS.TEXT',
iconType: 'feather',
icon: 'chevrons-down',
class: 'menu-toggle',
@@ -115,79 +48,11 @@ export const ROUTES: RouteInfo[] = [
badge: '',
badgeClass: '',
submenu: [
/*{
path: '/multilevel/first1',
title: 'First',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: '/',
title: 'Second',
iconType: '',
icon: '',
class: 'ml-sub-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [
{
path: '/multilevel/secondlevel/second1',
title: 'Second 1',
iconType: '',
icon: '',
class: 'ml-menu2',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
{
path: '/',
title: 'Second 2',
iconType: '',
icon: '',
class: 'ml-sub-menu2',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [
{
path: '/multilevel/thirdlevel/third1',
title: 'third 1',
iconType: '',
icon: '',
class: 'ml-menu3',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},
],
},
],
},
{
path: '/multilevel/first3',
title: 'Third',
iconType: '',
icon: '',
class: 'ml-menu',
groupTitle: false,
badge: '',
badgeClass: '',
submenu: [],
},*/
],
},
{
path: '',
title: 'Settings',
title: 'MENUITEMS.SETTINGS.TEXT',
iconType: 'feather',
icon: 'settings',
class: 'menu-toggle',
@@ -197,7 +62,7 @@ export const ROUTES: RouteInfo[] = [
submenu: [
{
path: '/settings/currencies',
title: 'Currencies',
title: 'MENUITEMS.SETTINGS.LIST.CURRENCIES',
iconType: '',
icon: '',
class: 'ml-menu',
@@ -208,7 +73,7 @@ export const ROUTES: RouteInfo[] = [
},
{
path: '/settings/account-categories',
title: 'Account categories',
title: 'MENUITEMS.SETTINGS.LIST.ACCOUNTCATEGORIES',
iconType: '',
icon: '',
class: 'ml-menu',
@@ -219,7 +84,7 @@ export const ROUTES: RouteInfo[] = [
},
{
path: '/settings/accounts',
title: 'Accounts',
title: 'MENUITEMS.SETTINGS.LIST.ACCOUNTS',
iconType: '',
icon: '',
class: 'ml-menu',
@@ -230,7 +95,7 @@ export const ROUTES: RouteInfo[] = [
},
{
path: '/settings/item-categories',
title: 'Item categories',
title: '',
iconType: '',
icon: '',
class: 'ml-menu',
@@ -241,7 +106,7 @@ export const ROUTES: RouteInfo[] = [
},
{
path: '/settings/items',
title: 'Items',
title: 'MENUITEMS.SETTINGS.LIST.ITEMS',
iconType: '',
icon: '',
class: 'ml-menu',
@@ -6,6 +6,8 @@ export interface AccountModel {
name?: string,
currencyId?: string,
currencyName?: string,
type?: string,
allowDelete: boolean,
categories?: AccountCategoryModel[],
accessRights?: AccountAccessRightModel[],
}
@@ -5,7 +5,21 @@ export interface DashboardModel {
outcomeLast: number;
outcomePrevious: number;
outcomeChange: number;
balance: number;
balanceDebit: number;
balanceCredit: number;
balanceRests: DashboardRestModel[];
}
export interface DashboardRestModel {
id: string;
name: string;
currencyName: string;
currencyShortName: string;
balance: number;
currencyRate: number;
currencyQuantity: number;
balanceAtRate: number;
}
@@ -30,11 +30,12 @@
<tr *ngFor="let account of accountInCategory(category)">
<td>{{account.name}}</td>
<td>{{account.currencyId}}</td>
<td>{{account.type}}</td>
<td>
<button class="btn-space " (click)="edit(account)" mat-raised-button color="primary">
<button class="btn-space" (click)="edit(account)" mat-raised-button color="primary">
Edit
</button>
<button class="btn-space " (click)="remove(account)" mat-raised-button color="primary">
<button class="btn-space" *ngIf="account.allowDelete" (click)="remove(account)" mat-raised-button color="primary">
Delete
</button>
</td>
@@ -48,11 +49,12 @@
<tr *ngFor="let account of accountInCategory()">
<td>{{account.name}}</td>
<td>{{account.currencyId}}</td>
<td>{{account.type}}</td>
<td>
<button class="btn-space " (click)="edit(account)" mat-raised-button color="primary">
<button class="btn-space" (click)="edit(account)" mat-raised-button color="primary">
Edit
</button>
<button class="btn-space " (click)="remove(account)" mat-raised-button color="primary">
<button class="btn-space" *ngIf="account.allowDelete" (click)="remove(account)" mat-raised-button color="primary">
Delete
</button>
</td>
@@ -67,10 +69,10 @@
<td>{{account.name}}</td>
<td>{{account.currencyId}}</td>
<td>
<button class="btn-space " (click)="edit(account)" mat-raised-button color="primary">
<button class="btn-space" (click)="edit(account)" mat-raised-button color="primary">
Edit
</button>
<button class="btn-space " (click)="remove(account)" mat-raised-button color="primary">
<button class="btn-space" *ngIf="account.allowDelete" (click)="remove(account)" mat-raised-button color="primary">
Delete
</button>
</td>
@@ -81,15 +81,15 @@ export class SettingsAccountComponent {
});
}
public remove(category: AccountModel) {
/*Swal.fire({
title: 'Remove account category ' + category.name,
public remove(account: AccountModel) {
Swal.fire({
title: 'Remove account ' + account.name,
showCancelButton: true,
confirmButtonText: 'Add',
confirmButtonText: 'Delete',
showLoaderOnConfirm: true,
preConfirm: (name) => {
return new Promise((resolve, reject) => {
this.httpClient.delete(ApiRoutes.SettingsAccountCategory.replace(':id', category.id!))
this.httpClient.delete(ApiRoutes.SettingsAccount.replace(':id', account.id!))
.subscribe(data => {
resolve(data);
}, error => {
@@ -103,7 +103,7 @@ export class SettingsAccountComponent {
},
allowOutsideClick: () => !Swal.isLoading(),
}).then((result) => {
this.load();
});*/
this.loadAccounts();
});
}
}
@@ -4,6 +4,22 @@
</h2>
<div mat-dialog-content>
<form [formGroup]="editForm!" (ngSubmit)="onSubmitClick()">
<div class="row">
<div class="col-md-12">
<div class="text-inside">
<mat-form-field class="example-full-width">
<mat-label>Type</mat-label>
<mat-select formControlName="type">
<mat-option *ngFor="let type of types | keyvalue" [value]="type.key">
{{type.key}} ({{type.value}})
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="text-inside">
@@ -19,6 +19,7 @@ import { CurrencyModel } from '../../../model/currency.model';
import { AccountCategoryService } from '../../../services/account.category.service';
import { AccountCategoryModel } from '../../../model/account.category.model';
import { AuthService } from '../../../core/service/auth.service';
import { AccountService } from '../../../services/account.service';
@Component({
templateUrl: './add.account.component.html',
@@ -30,6 +31,7 @@ export class SettingsAccountAddComponent {
public currencies?: CurrencyModel[];
public categories?: AccountCategoryModel[];
public username: string;
public types = new Map<string, string>();
constructor(
private fb: UntypedFormBuilder,
@@ -38,9 +40,11 @@ export class SettingsAccountAddComponent {
private currencyService: CurrencyService,
private accountCategoryService: AccountCategoryService,
private authService: AuthService,
private accountService: AccountService,
@Inject(MAT_DIALOG_DATA) public account: AccountModel
) {
this.username = authService.currentUserValue.userName;
this.types = this.accountService.getTypes();
}
ngOnInit(): void {
@@ -60,6 +64,9 @@ export class SettingsAccountAddComponent {
'',
[Validators.required],
],
type: [
this.account.type
]
});
this.currencyService
@@ -4,6 +4,22 @@
</h2>
<div mat-dialog-content>
<form [formGroup]="editForm!" (ngSubmit)="onSubmitClick()">
<div class="row">
<div class="col-md-12">
<div class="text-inside">
<mat-form-field class="example-full-width">
<mat-label>Type</mat-label>
<mat-select formControlName="type">
<mat-option *ngFor="let type of types | keyvalue" [value]="type.key">
{{type.key}} ({{type.value}})
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="text-inside">
@@ -26,7 +42,9 @@
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<label>Categories</label>
<div class="col-md-12">
@@ -42,20 +60,21 @@
</div>
</div>
<div class="col-md-12">
<table class="table">
<table class="table" style="table-layout: fixed;">
<tbody>
<tr *ngFor="let category of account.categories">
<td>{{category.name}}</td>
<td>
<button class="btn-space" (click)="removeCategory(category)" mat-raised-button color="primary">
Delete
</button>
</td>
</tr>
<tr *ngFor="let category of account.categories">
<td style="width: 100%;">{{category.name}}</td>
<td style="width: 100px;">
<button class="btn-space" (click)="removeCategory(category)" mat-raised-button color="primary">
Delete
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<label>Users</label>
<div class="col-md-12">
@@ -70,20 +89,24 @@
</mat-form-field>
</div>
</div>
<table class="table">
<tbody>
<div class="col-md-12">
<table class="table" style="table-layout: fixed;">
<tbody>
<tr *ngFor="let accessRight of account.accessRights">
<td>{{accessRight.user.email}}</td>
<td>
<td style="width: 100%;">{{accessRight.user.email}}</td>
<td style="width: 100px;">
<button class="btn-space" [disabled]="account.accessRights!.length <= 1" click="removeAccessRight(accessRight)" mat-raised-button color="primary">
Delete
</button>
</td>
</tr>
</tbody>
</table>
</tbody>
</table>
</div>
</div>
</div>
<mat-dialog-actions class="mat-dialog-actions">
<div class="mat-dialog-left">
<div class="alert alert-danger mat-dialog-error" *ngIf="errorMessage">
@@ -19,6 +19,7 @@ import { CurrencyModel } from '../../../model/currency.model';
import { AccountCategoryService } from '../../../services/account.category.service';
import { AccountCategoryModel } from '../../../model/account.category.model';
import { AccountAccessRightModel } from '../../../model/account.accessRight.model';
import { AccountService } from '../../../services/account.service';
@Component({
templateUrl: './edit.account.component.html',
@@ -29,6 +30,7 @@ export class SettingsAccountEditComponent {
public errorMessage?: string;
public currencies?: CurrencyModel[];
public categories?: AccountCategoryModel[];
public types = new Map<string, string>();
constructor(
private fb: UntypedFormBuilder,
@@ -36,8 +38,10 @@ export class SettingsAccountEditComponent {
private dialogRef: MatDialogRef<SettingsAccountEditComponent>,
private currencyService: CurrencyService,
private accountCategoryService: AccountCategoryService,
private accountService: AccountService,
@Inject(MAT_DIALOG_DATA) public account: AccountModel
) {
this.types = this.accountService.getTypes();
}
ngOnInit(): void {
@@ -56,6 +60,9 @@ export class SettingsAccountEditComponent {
categoryId: [
'',
],
type: [
this.account.type,
],
});
this.currencyService
@@ -0,0 +1,28 @@
// angular
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// libs
import { Observable } from 'rxjs';
// app
import { ApiRoutes } from '../api-routes';
import { AccountCategoryModel } from '../model/account.category.model';
@Injectable()
export class AccountService {
private types = new Map<string, string>();
constructor(
private httpClient: HttpClient,
) {
this.types.set('balance', 'Account with balance');
this.types.set('credit', 'Account with loan funds');
this.types.set('external', 'External account');
this.types.set('other', 'Other account');
}
public getTypes(): Map<string, string> {
return this.types;
}
}
@@ -2,7 +2,7 @@
<div class="row">
<div class="col-6">
<div class="breadcrumb-title">
<h4 class="page-title">{{title}}</h4>
<h4 class="page-title">{{title | translate}}</h4>
</div>
</div>
<div class="col-6">
@@ -12,8 +12,8 @@
<app-feather-icons [icon]="'home'" [class]="'breadcrumb-icon'"></app-feather-icons>
</a>
</li>
<li class="breadcrumb-item" *ngFor="let item of items">{{item}}</li>
<li class="breadcrumb-item active">{{active_item}}</li>
<li class="breadcrumb-item" *ngFor="let item of items">{{item | translate}}</li>
<li class="breadcrumb-item active">{{active_item | translate}}</li>
</ul>
</div>
</div>
@@ -1,12 +1,25 @@
// angular
import { NgModule } from '@angular/core';
// libs
import { TranslateModule } from '@ngx-translate/core';
import { FileUploadComponent } from './file-upload/file-upload.component';
import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
import { SharedModule } from '../shared.module';
@NgModule({
declarations: [FileUploadComponent, BreadcrumbComponent],
imports: [SharedModule],
exports: [FileUploadComponent, BreadcrumbComponent],
declarations: [
FileUploadComponent,
BreadcrumbComponent
],
imports: [
SharedModule,
TranslateModule,
],
exports: [
FileUploadComponent,
BreadcrumbComponent
],
})
export class ComponentsModule {
}
+24 -4
View File
@@ -14,11 +14,23 @@
"DASHBOARD": {
"TEXT": "Dashboard",
"LIST": {
"DASHBOARD1": "Dashboard 1",
"DASHBOARD2": "Dashboard 2",
"DASHBOARD3": "Dashboard 3"
"DASHBOARD1": "Dashboard"
}
},
"ACCOUNTS": {
"TEXT": "Accounts"
},
"SETTINGS": {
"TEXT": "Settings",
"LIST": {
"CURRENCIES": "Currencies",
"ACCOUNTCATEGORIES": "Account categories",
"ACCOUNTS": "Accounts",
"ITEMCATEGORIES": "Item categories",
"ITEMS": "Items"
}
},
"ADVANCE-TABLE": {
"TEXT": "Advance Table"
},
@@ -80,5 +92,13 @@
"NGX-DATATABLE": "NGX-Datatable"
}
}
}
},
"BALANCE": "Balance",
"DEBIT.BALANCE": "Debit balance",
"CREDIT.BALANCE": "Credit balance",
"CURRENT.BALANCE": "Current balance",
"HOME": "Home",
"OTHER": "Other",
"TOTAL": "Total"
}
+103
View File
@@ -0,0 +1,103 @@
{
"HEADER": {
"SEARCH": {
"TEXT": "Шукати.."
}
},
"MENUITEMS": {
"USER": {
"POST": "Manager"
},
"MAIN": {
"TEXT": "Головне"
},
"DASHBOARD": {
"TEXT": "Панелі",
"LIST": {
"DASHBOARD1": "Панель"
}
},
"ACCOUNTS": {
"TEXT": "Рахунки"
},
"SETTINGS": {
"TEXT": "Налаштування",
"LIST": {
"CURRENCIES": "Валюти",
"ACCOUNTCATEGORIES": "Категорії рахунків",
"ACCOUNTS": "Рахунки",
"ITEMCATEGORIES": "Категорії статей",
"ITEMS": "Статті"
}
},
"ADVANCE-TABLE": {
"TEXT": "Advance Table"
},
"APPS": {
"TEXT": "Apps"
},
"CALENDAR": {
"TEXT": "Calendar"
},
"TASK": {
"TEXT": "Task"
},
"CONTACTS": {
"TEXT": "Contacts"
},
"EMAIL": {
"TEXT": "Email",
"LIST": {
"INBOX": "Inbox",
"COMPOSE": "Compose",
"READ": "Read Email"
}
},
"MORE-APPS": {
"TEXT": "More Apps",
"LIST": {
"CHAT": "Chat",
"SUPPORT": "Support",
"DRAG-DROP": "Drag & Drop",
"CONTACT-GRID": "Contact Grid"
}
},
"COMPONENTS": {
"TEXT": "Components"
},
"WIDGETS": {
"TEXT": "Widgets",
"LIST": {
"CHART-WIDGET": "Chart-Widget",
"DATA-WIDGET": "Data-Widget"
}
},
"FORMS": {
"TEXT": "Forms",
"LIST": {
"CONTROLS": "Form Controls",
"ADVANCE": "Advance Control",
"EXAMPLE": "Form Examples",
"VALIDATION": "Form Validation",
"WIZARD": "Wizard",
"EDITORS": "Editors"
}
},
"TABLES": {
"TEXT": "Tables",
"LIST": {
"BASIC": "Basic Tables",
"MATERIAL": "Material Tables",
"NGX-DATATABLE": "NGX-Datatable"
}
}
},
"BALANCE": "Баланс",
"DEBIT.BALANCE": "Остаток",
"CREDIT.BALANCE": "Кредиты",
"CURRENT.BALANCE": "Поточний баланс",
"HOME": "Головна",
"OTHER": "Інші",
"TOTAL": "Всього"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

+57 -9
View File
@@ -1,5 +1,6 @@
namespace MyOffice.Services.Account
{
using AutoMapper;
using Core;
using Core.Extensions;
using Core.Helpers;
@@ -16,7 +17,9 @@
public class AccountService
{
private ILogger<AccountService> _logger;
private readonly IMapper _mapper;
private readonly IAccountCategoryRepository _accountCategoryRepository;
private readonly IAccountAccessRepository _accountAccessRepository;
private readonly IAccountRepository _accountRepository;
private readonly ICurrencyRepository _currencyRepository;
private readonly IAccountAccountCategoryRepository _accountAccountCategoryRepository;
@@ -27,7 +30,9 @@
public AccountService(
ILogger<AccountService> logger,
IMapper mapper,
IAccountCategoryRepository accountCategoryRepository,
IAccountAccessRepository accountAccessRepository,
IAccountRepository accountRepository,
ICurrencyRepository currencyRepository,
IAccountAccountCategoryRepository accountAccountCategoryRepository,
@@ -38,7 +43,9 @@
)
{
_logger = logger;
_mapper = mapper;
_accountCategoryRepository = accountCategoryRepository;
_accountAccessRepository = accountAccessRepository;
_accountRepository = accountRepository;
_currencyRepository = currencyRepository;
_accountAccountCategoryRepository = accountAccountCategoryRepository;
@@ -130,24 +137,24 @@
return result.Set(exists);
}
public List<Account> GetAllAccounts(Guid userId)
public List<AccountDto> GetAllAccounts(Guid userId)
{
return _accountRepository.GetAll(userId);
return _mapper.Map<List<AccountDto>>(_accountRepository.GetAll(userId));
}
public List<Account> GetByCategory(Guid userId, Guid categoryId)
public List<AccountDto> GetByCategory(Guid userId, Guid categoryId)
{
return _accountRepository.GetByCategory(userId, categoryId);
return _mapper.Map<List<AccountDto>>(_accountRepository.GetByCategory(userId, categoryId));
}
public List<AccountDetailed> GetByCategoryDetailed(Guid userId, Guid categoryId)
public List<AccountDetailedDto> GetByCategoryDetailed(Guid userId, Guid categoryId)
{
return _accountRepository.GetByCategoryDetailed(userId, categoryId);
return _mapper.Map<List<AccountDetailedDto>>(_accountRepository.GetByCategoryDetailed(userId, categoryId));
}
public Exec<AccountDetailed, GeneralExecStatus> GetByIdDetailed(Guid userId, Guid id)
public Exec<AccountDetailedDto, GeneralExecStatus> GetByIdDetailed(Guid userId, Guid id)
{
var result = new Exec<AccountDetailed, GeneralExecStatus>(GeneralExecStatus.success);
var result = new Exec<AccountDetailedDto, GeneralExecStatus>(GeneralExecStatus.success);
var account = _accountRepository.GetByIdDetailed(userId, id);
if (account == null)
@@ -155,7 +162,7 @@
return result.Set(GeneralExecStatus.not_found);
}
return result.Set(account);
return result.Set(_mapper.Map<AccountDetailedDto>(account));
}
public Exec<Account, AccountAddResult> AccountAdd(Guid userId, AccountAdd input)
@@ -208,9 +215,40 @@
return result.Set(AccountAddResult.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, AccountEditResult> AccountUpdate(Guid userId, Guid accountId, AccountEdit input)
{
if (input == null)
@@ -263,6 +301,16 @@
return result.Set(AccountEditResult.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);
}
@@ -5,5 +5,6 @@
public string Name { get; set; } = null!;
public string CurrencyId { get; set; } = null!;
public Guid CategoryId { get; set; }
public string Type { get; set; } = null!;
}
}
@@ -0,0 +1,9 @@
namespace MyOffice.Services.Account.Domain;
public class AccountDetailedDto
{
public AccountDto Account { get; set; } = null!;
public decimal TotalPlus { get; set; }
public decimal TotalMinus { get; set; }
public decimal Rest => TotalPlus - TotalMinus;
}
@@ -0,0 +1,92 @@
using MyOffice.Data.Models.Accounts;
using MyOffice.Data.Models.Users;
using MyOffice.Services.Account.Domain;
namespace MyOffice.Services.Account.Domain
{
using AutoMapper;
using MyOffice.Data.Models.Accounts;
using MyOffice.Data.Models.Currencies;
using MyOffice.Data.Models.Users;
using System;
public class AccountDto
{
public Guid Id { get; set; }
public string CurrencyGlobalId { get; set; } = null!;
public CurrencyGlobal? CurrencyGlobal { get; set; }
public Guid CurrencyId { get; set; }
public Currency? Currency { get; set; }
public Guid UserId { get; set; }
public UserDto? User { get; set; }
public Guid OwnerId { get; set; }
public UserDto? Owner { get; set; }
public string Name { get; set; } = null!;
public bool HasMotions { get; set; }
public List<AccountAccessDto>? AccessRights { get; set; }
public List<AccountAccountCategoryDto>? Categories { get; set; }
}
public class AccountAccessDto
{
public Guid AccountId { get; set; }
public AccountDto? Account { get; set; }
public Guid UserId { get; set; }
public User? User { get; set; }
public bool IsAllowRead { get; set; }
public bool IsAllowWrite { get; set; }
public bool IsAllowManage { get; set; }
public AccountAccessTypeEnum Type { get; set; }
}
public class AccountAccountCategoryDto
{
public Guid CategoryId { get; set; }
public AccountCategoryDto? Category { get; set; } = null!;
}
public class AccountMappingProfile : Profile
{
public AccountMappingProfile()
{
CreateMap<User, UserDto>();
CreateMap<Account, AccountDto>()
.ForMember(x => x.HasMotions, o => o.MapFrom(x => x.Motions.Any()))
;
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.Id, o => o.MapFrom(x => x.Id))
;
}
}
public class UserDto
{
public Guid Id { get; set; }
public string UserName { get; set; } = null!;
public string Email { get; set; } = null!;
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? FullName { get; set; }
public string? Phone { get; set; }
public string CurrencyId { get; set; }
public CurrencyGlobal? Currency { get; set; }
}
}
public class AccountCategoryDto
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Name { get; set; } = null!;
}
@@ -6,4 +6,5 @@ public class AccountEdit
public string CurrencyId { get; set; } = null!;
public Guid? CategoryId { get; set; }
public Guid? UserId { get; set; }
public string? Type { get; set; }
}
+27 -11
View File
@@ -27,21 +27,37 @@ public class DashboardService
public DashboardData GetDashboardRestData(Guid userId)
{
/*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 result = new DashboardData
{
//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)),
Balance = rests
.Where(x => (x.Type == AccountAccessTypeEnum.balance || x.Type == AccountAccessTypeEnum.credit) && x.CurrencyRate.HasValue)
.Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)),
BalanceDebit = rests
.Where(x => x.Type == AccountAccessTypeEnum.balance && x.CurrencyRate.HasValue)
.Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)),
BalanceCredit = rests
.Where(x => x.Type == AccountAccessTypeEnum.credit && x.CurrencyRate.HasValue)
.Sum(x => x.Balance * (x.CurrencyRate * x.CurrencyQuantity)),
BalanceRests = rests
.Where(x => x.Type == AccountAccessTypeEnum.balance)
.Where(x => x.CurrencyRate.HasValue && x.Balance.HasValue && x.Balance != 0)
.Select(x => new DashboardRestData
{
Id = x.Id,
Name = x.Name,
Balance = x.Balance,
CurrencyName = x.CurrencyName,
CurrencyShortName = x.CurrencyShortName,
CurrencyRate = x.CurrencyRate,
CurrencyQuantity = x.CurrencyQuantity,
})
.OrderByDescending(x => x.BalanceAtRate)
.ToList(),
};
return result;
@@ -13,13 +13,18 @@ public class DashboardData
public decimal? Balance { get; set; }
public decimal? BalanceDebit { get; set; }
public decimal? BalanceCredit { get; set; }
public List<DashboardRestData> BalanceRests { get; set; }
}
public class DashboardRestData
{
public AccountSimple AccountSimple { get; set; }
public decimal? Balance => AccountSimple.Balance;
public Guid Id { get; set; }
public string Name { get; set; } = null!;
public string CurrencyName { get; set; } = null!;
public string CurrencyShortName { get; set; } = null!;
public decimal? Balance { get; set; }
public decimal? CurrencyRate { get; set; }
public decimal? CurrencyQuantity { get; set; }
public decimal? BalanceAtRate =>
AccountSimple.Balance * (AccountSimple.CurrencyRate * AccountSimple.CurrencyQuantity);
public decimal? BalanceAtRate => Balance * (CurrencyRate * CurrencyQuantity);
}
@@ -0,0 +1,10 @@
namespace MyOffice.Services.Identity;
using Data.Models.Users;
public interface IContextProvider
{
User User { get; }
Guid UserId { get; }
}
@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyOffice.Core\MyOffice.Core.csproj" />
<ProjectReference Include="..\MyOffice.Data.Repositories\MyOffice.Data.Repositories.csproj" />
@@ -1,5 +1,6 @@
namespace MyOffice.Web.Controllers;
using AutoMapper;
using Core;
using Core.Extensions;
using Microsoft.AspNetCore.Authorization;
@@ -18,28 +19,29 @@ using Services.Item;
public class AccountController : BaseApiController
{
private readonly ILogger<AccountController> _logger;
private readonly IMapper _mapper;
private readonly AccountService _accountService;
private readonly ItemService _itemService;
public AccountController(
ILogger<AccountController> logger,
IMapper mapper,
AccountService accountService,
ItemService itemService
)
{
_logger = logger;
_mapper = mapper;
_accountService = accountService;
_itemService = itemService;
}
[HttpGet("~/api/accounts")]
public object AccountsGet(string category)
public List<AccountDetailedViewModel> AccountsGet(string category)
{
var list = _accountService.GetByCategoryDetailed(UserId, category!.AsGuid());
return list
.OrderBy(x => x.Account.Name)
.Select(x => x.ToModel());
return _mapper.Map<List<AccountDetailedViewModel>>(list.OrderBy(x => x.Account.Name).ToList());
}
[HttpGet("~/api/accounts/{id}")]
@@ -54,7 +56,8 @@ public class AccountController : BaseApiController
return ProblemBadRequest("Account not found.");
case GeneralExecStatus.success:
return exec.Result!.ToModel();
return _mapper.Map<AccountDetailedViewModel>(exec.Result);
//return exec.Result!.ToModel();
default:
throw new NotSupportedException(exec.Status.ToString());
@@ -1,5 +1,6 @@
namespace MyOffice.Web.Controllers;
using AutoMapper;
using Core;
using Core.Extensions;
using Data.Models.Accounts;
@@ -15,14 +16,17 @@ using Services.Account.Domain;
public class SettingsAccountController : BaseApiController
{
private readonly ILogger<SettingsAccountController> _logger;
private readonly IMapper _mapper;
private readonly AccountService _accountService;
public SettingsAccountController(
ILogger<SettingsAccountController> logger,
IMapper mapper,
AccountService accountService
)
{
_logger = logger;
_mapper = mapper;
_accountService = accountService;
}
@@ -111,15 +115,13 @@ public class SettingsAccountController : BaseApiController
}
[HttpGet("~/api/settings/accounts")]
public object AccountsGet(string? category)
public List<AccountViewModel> AccountsGet(string? category)
{
var list = category.IsPresent()
? _accountService.GetByCategory(UserId, category!.AsGuid())
: _accountService.GetAllAccounts(UserId);
return list
.OrderBy(x => x.Name)
.Select(x => x.ToModel());
return _mapper.Map<List<AccountViewModel>>(list.OrderBy(x => x.Name));
}
[HttpPost("~/api/settings/accounts")]
@@ -130,6 +132,7 @@ public class SettingsAccountController : BaseApiController
Name = request.Name,
CurrencyId = request.CurrencyId,
CategoryId = request.CategoryId.AsGuid(),
Type = request.Type,
});
switch (exec.Status)
@@ -158,6 +161,7 @@ public class SettingsAccountController : BaseApiController
CurrencyId = request.CurrencyId,
CategoryId = request.CategoryId?.AsGuidNull(),
UserId = request.UserId?.AsGuidNull(),
Type = request.Type,
});
switch (exec.Status)
@@ -177,7 +181,23 @@ public class SettingsAccountController : BaseApiController
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpDelete("~/api/settings/accounts/{id}")]
public object AccountsDelete(string id)
{
var exec = _accountService.AccountDelete(UserId, id);
switch (exec.Status)
{
case GeneralExecStatus.not_found:
return ProblemBadRequest("Account not found.");
case GeneralExecStatus.success:
return exec.Result!;
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpDelete("~/api/settings/accounts/{id}/category/{categoryId}")]
public object AccountsCategoryRemove(string id, string categoryId)
{
+3 -1
View File
@@ -13,6 +13,7 @@ using MyOffice.Core.Identity;
using Core;
using Services.Users;
using Services.Users.Domain;
using MyOffice.Data.Models.Currencies;
[ApiController]
[Route("api/[controller]")]
@@ -40,7 +41,8 @@ public class UserController : BaseApiController
{
Id = Guid.NewGuid(),
UserName = request.UserName,
Email = request.UserName
Email = request.UserName,
CurrencyId = CurrencyGlobalIdEnum.USD.ToString(),
};
var result = await _userManager.CreateAsync(user, request.Password);
@@ -20,7 +20,6 @@ public class ConfigureIdentityServerOptions : IConfigureNamedOptions<IdentitySer
public void Configure(string? name, IdentityServerAuthenticationOptions options)
{
//_logger.LogError($"Configure: {_globalSettings.Host}");
if (name == IdentityServerAuthenticationDefaults.AuthenticationScheme)
{
options.RequireHttpsMetadata = false;
+39
View File
@@ -0,0 +1,39 @@
namespace MyOffice.Web.Identity;
using Data.Repositories.Users;
using MyOffice.Data.Models.Users;
using MyOffice.Services.Identity;
public class ContextProvider : IContextProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IUserRepository _userRepository;
public ContextProvider(
IHttpContextAccessor httpContextAccessor,
IUserRepository userRepository
)
{
_httpContextAccessor = httpContextAccessor;
_userRepository = userRepository;
}
private User? _user = null;
public User User
{
get
{
_user ??= _userRepository.GetUser(UserId);
return _user!;
}
}
public Guid UserId
{
get
{
return Guid.Parse(_httpContextAccessor!.HttpContext!.User!.Claims!.FirstOrDefault(x => x.Type == "sub")!.Value);
}
}
}
@@ -1,6 +1,7 @@
namespace MyOffice.Web.Models.Account
{
using Data.Models.Accounts;
using Services.Account.Domain;
using User;
public class AccessRightsViewModel
@@ -13,7 +14,7 @@
public static class AccessRightsViewModelExtensions
{
public static AccessRightsViewModel ToModel(this AccountAccess accountAccess)
public static AccessRightsViewModel ToModel(this AccountAccessDto accountAccess)
{
return new AccessRightsViewModel
{
@@ -1,6 +1,7 @@
namespace MyOffice.Web.Models.Account;
using Data.Models.Accounts;
using Services.Account.Domain;
public class AccountDetailedViewModel
{
@@ -8,9 +9,9 @@ public class AccountDetailedViewModel
public decimal Rest { get; set; }
}
public static class AccountDetailedViewModelExtensions
/*public static class AccountDetailedViewModelExtensions
{
public static AccountDetailedViewModel ToModel(this AccountDetailed input)
public static AccountDetailedViewModel ToModel(this AccountDetailedDto input)
{
return new AccountDetailedViewModel
{
@@ -18,4 +19,4 @@ public static class AccountDetailedViewModelExtensions
Rest = input.Rest,
};
}
}
}*/
@@ -8,6 +8,7 @@ public class AccountEditRequestModel
public string Name { get; set; } = null!;
[Required]
public string CurrencyId { get; set; } = null!;
public string? CategoryId { get; set; } = null!;
public string? UserId { get; set; } = null!;
public string? CategoryId { get; set; }
public string? UserId { get; set; }
public string? Type { get; set; }
}
@@ -1,19 +1,25 @@
namespace MyOffice.Web.Models.Account
{
using System.ComponentModel.DataAnnotations;
using AutoMapper;
using Core.Extensions;
using Data.Models.Accounts;
using Services.Account.Domain;
using Services.Identity;
using User;
public class AccountViewModel
{
public string? Id { get; set; }
[Required]
[Required]
public string Name { get; set; } = null!;
public string Type { get; set; } = null!;
[Required]
[Required]
public string CurrencyId { get; set; } = null!;
public string? CurrencyName { get; set; }
public bool AllowDelete { get; set; }
public List<AccountCategoryViewModel>? Categories { get; set; }
@@ -23,9 +29,48 @@
public List<AccessRightsViewModel>? AccessRights { get; set; }
}
public static class AccountViewModelExtensions
public class ViewModelProfile : Profile
{
public static AccountViewModel ToModel(this Account input)
public ViewModelProfile()
{
CreateMap<Guid, string>().ConvertUsing(x => x.ToShort());
}
}
public class AccountViewModelProfile : Profile
{
public AccountViewModelProfile(
IContextProvider contextProvider
)
{
CreateMap<AccountDetailedDto, AccountDetailedViewModel>()
.ForMember(x => x.Account, o => o.MapFrom(x => x.Account))
;
CreateMap<UserDto, UserViewModel>();
CreateMap<MyOffice.Data.Models.Users.User, UserViewModel>();
CreateMap<AccountDto, AccountViewModel>()
.ForMember(x => x.Type, o => o.MapFrom(x => x.AccessRights!.FirstOrDefault(a => a.UserId == contextProvider.UserId)!.Type.ToString()))
.ForMember(x => x.CurrencyId, o => o.MapFrom(x => x.CurrencyGlobalId))
.ForMember(x => x.CurrencyName, o => o.MapFrom(x => x.Currency!.Name))
.ForMember(x => x.AllowDelete, o => o.MapFrom(x => !x.HasMotions))
;
CreateMap<AccountAccountCategoryDto, AccountCategoryViewModel>()
.ForMember(x => x.Id, o => o.MapFrom(x => x.CategoryId))
.ForMember(x => x.Name, o => o.MapFrom(x => x.Category!.Name))
;
CreateMap<AccountAccessDto, AccessRightsViewModel>();
}
}
/*public static class AccountViewModelExtensions
{
public static AccountViewModel ToModel(this AccountDto input)
{
return new AccountViewModel
{
@@ -41,5 +86,5 @@
.ToList(),
};
}
}
}*/
}
+2
View File
@@ -34,6 +34,8 @@
<PackageReference Include="Auth0.AuthenticationApi" Version="7.17.4" />
<PackageReference Include="Auth0.Core" Version="7.17.4" />
<PackageReference Include="Auth0.ManagementApi" Version="7.17.4" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Google.Apis.Auth" Version="1.58.0-beta01" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
+36 -2
View File
@@ -37,10 +37,14 @@ using Identity.Configure;
using Infrastructure;
using Microsoft.Extensions.Logging.Console;
using IdentityServer4.Services;
using Models.Account;
using MyOffice.Services.Currency;
using Services.Account;
using Services.Item;
using MyOffice.Services.Dashboard;
using Services.Account.Domain;
using MyOffice.Services.Identity;
using AutoMapper;
public class Program
{
@@ -92,6 +96,10 @@ public class Program
AddIdentityServices(builder);
builder.Services.AddScoped<IContextProvider, ContextProvider>();
AddMapping(builder);
AddRepositories(builder);
AddBusinessServices(builder);
@@ -205,6 +213,20 @@ public class Program
}
}
private static void AddMapping(WebApplicationBuilder builder)
{
builder.Services.AddSingleton(provider => new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AccountMappingProfile());
cfg.AddProfile(new ViewModelProfile());
cfg.AddProfile(new AccountViewModelProfile(provider.CreateScope().ServiceProvider.GetService<IContextProvider>()!));
}).CreateMapper());
//builder.Services.AddAutoMapper(typeof(AccountMappingProfile));
//builder.Services.AddAutoMapper(typeof(AccountViewModelProfile));
//builder.Services.AddAutoMapper(typeof(ViewModelProfile));
}
private static void AddRepositories(WebApplicationBuilder builder)
{
builder.Services.AddScoped<IUserRepository, UserRepository>();
@@ -216,6 +238,7 @@ public class Program
builder.Services.AddScoped<IAccountCategoryRepository, AccountCategoryRepository>();
builder.Services.AddScoped<IAccountRepository, AccountRepository>();
builder.Services.AddScoped<IAccountAccessRepository, AccountAccessRepository>();
builder.Services.AddScoped<IAccountAccountCategoryRepository, AccountAccountCategoryRepository>();
builder.Services.AddScoped<IItemCategoryRepository, ItemCategoryRepository>();
@@ -274,9 +297,20 @@ public class Program
{
_firstRequest = false;
// set real frontend host
globalSettings = ctx.RequestServices.GetRequiredService<GlobalSettings>();
globalSettings.Host = GetFrontendHost(ctx, logger);
var configuration = ctx.RequestServices.GetRequiredService<IConfiguration>();
var frontEndHost = configuration.GetValue<string>("FrontEnd:Host");
var isDynamicFrontEndHost = frontEndHost.IsMissing();
if (!isDynamicFrontEndHost)
{
globalSettings.Host = frontEndHost;
}
else
{
// set real frontend host
globalSettings.Host = GetFrontendHost(ctx, logger);
}
// update identity server
var options = ctx.RequestServices.GetRequiredService<IdentityServerOptions>();