This commit is contained in:
2023-07-28 21:18:18 +03:00
parent 5ecefe5756
commit 90f0386bfe
53 changed files with 3067 additions and 267 deletions
@@ -17,9 +17,12 @@ public class AccountAccess
public Account? Account { get; set; } public Account? Account { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
public User? User { get; set; } public User? User { get; set; }
public Guid OwnerId { get; set; }
public User? Owner { get; set; }
public bool IsAllowRead { get; set; } public bool IsAllowRead { get; set; }
public bool IsAllowWrite { get; set; } public bool IsAllowWrite { get; set; }
public bool IsAllowManage { get; set; } public bool IsAllowManage { get; set; }
public AccountAccessTypeEnum Type { get; set; } public AccountAccessTypeEnum Type { get; set; }
public string? Name { get; set; }
} }
@@ -0,0 +1,15 @@
namespace MyOffice.Data.Models.Accounts;
using Users;
public class AccountAccessInvite
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? AcceptedOn { get; set; }
public DateTime? RejectedOn { get; set; }
public string Email { get; set; } = null!;
public bool IsAllowWrite { get; set; }
}
+2
View File
@@ -30,4 +30,6 @@ public class User
public IEnumerable<AccountCategory>? AccountCategories { get; set; } public IEnumerable<AccountCategory>? AccountCategories { get; set; }
public IEnumerable<ItemCategory>? ItemCategories { get; set; } public IEnumerable<ItemCategory>? ItemCategories { get; set; }
public IEnumerable<Account>? Accounts { get; set; } public IEnumerable<Account>? Accounts { get; set; }
public IEnumerable<AccountAccess>? AccountAccessOwners { get; set; }
public IEnumerable<AccountAccessInvite>? AccountAccessInvites { get; set; }
} }
@@ -0,0 +1,27 @@
namespace MyOffice.Data.Repositories.Account;
using Models.Accounts;
using MyOffice.Data.Repositories;
public class AccountAccessInviteRepository : AppRepository<AccountAccessInvite>, IAccountAccessInviteRepository
{
public AccountAccessInvite? Get(Guid userId, string email)
{
return _context.AccountAccessInvites
.FirstOrDefault(x => x.UserId == userId
&& x.Email == email
&& !x.AcceptedOn.HasValue
&& !x.RejectedOn.HasValue
);
}
public bool Add(AccountAccessInvite invite)
{
return AddBase(invite) > 0;
}
public bool Update(AccountAccessInvite invite)
{
return UpdateBase(invite) > 0;
}
}
@@ -10,4 +10,9 @@ public class AccountAccessRepository : AppRepository<AccountAccess>, IAccountAcc
{ {
return UpdateBase(accountAccess) > 0; return UpdateBase(accountAccess) > 0;
} }
public bool Delete(AccountAccess accountAccess)
{
return RemoveBase(accountAccess) > 0;
}
} }
@@ -1,16 +1,25 @@
namespace MyOffice.Data.Repositories.Account; namespace MyOffice.Data.Repositories.Account;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Models.Accounts; using Models.Accounts;
using MyOffice.Data.Repositories; using MyOffice.Data.Repositories;
public class AccountRepository : AppRepository<Account>, IAccountRepository public class AccountRepository : AppRepository<Account>, IAccountRepository
{ {
private readonly ILogger<AccountRepository> _logger;
public AccountRepository(
ILogger<AccountRepository> logger
)
{
_logger = logger;
}
public List<Account> GetAll(Guid userId) public List<Account> GetAll(Guid userId)
{ {
return _context.Accounts! return _context.Accounts!
.Include(x => x.CurrencyGlobal) .Include(x => x.CurrencyGlobal)
.Include(x => x.Categories)! .Include(x => x.Categories!.Where(x => x.Category.UserId == userId))!
.ThenInclude(x => x.Category) .ThenInclude(x => x.Category)
.Include(x => x.AccessRights)! .Include(x => x.AccessRights)!
.ThenInclude(x => x.User) .ThenInclude(x => x.User)
@@ -0,0 +1,10 @@
namespace MyOffice.Data.Repositories.Account;
using Models.Accounts;
public interface IAccountAccessInviteRepository
{
AccountAccessInvite? Get(Guid userId, string email);
bool Add(AccountAccessInvite invite);
bool Update(AccountAccessInvite invite);
}
@@ -5,4 +5,5 @@ using Models.Accounts;
public interface IAccountAccessRepository public interface IAccountAccessRepository
{ {
bool Update(AccountAccess accountAccess); bool Update(AccountAccess accountAccess);
bool Delete(AccountAccess accountAccess);
} }
+14 -8
View File
@@ -39,6 +39,7 @@ public class AppDbContext : DbContext
public DbSet<Account> Accounts { get; set; } = null!; public DbSet<Account> Accounts { get; set; } = null!;
public DbSet<AccountAccountCategory> AccountAccountCategories { get; set; } = null!; public DbSet<AccountAccountCategory> AccountAccountCategories { get; set; } = null!;
public DbSet<AccountAccess> AccountAccesses { get; set; } = null!; public DbSet<AccountAccess> AccountAccesses { get; set; } = null!;
public DbSet<AccountAccessInvite> AccountAccessInvites { get; set; } = null!;
public DbSet<ItemCategory> ItemCategories { get; set; } = null!; public DbSet<ItemCategory> ItemCategories { get; set; } = null!;
public DbSet<ItemGlobal> ItemGlobals { get; set; } = null!; public DbSet<ItemGlobal> ItemGlobals { get; set; } = null!;
@@ -71,14 +72,6 @@ public class AppDbContext : DbContext
.Property(x => x.Email) .Property(x => x.Email)
.UseCollation(noCaseCollation); .UseCollation(noCaseCollation);
/*modelBuilder.Entity<Account>()
.Property(x => x.Name)
.UseCollation(noCaseCollation);*/
/*modelBuilder.Entity<ItemGlobal>()
.Property(x => x.Name)
.UseCollation(noCaseCollation);*/
modelBuilder.Entity<UserExternal>() modelBuilder.Entity<UserExternal>()
.HasOne(x => x.User) .HasOne(x => x.User)
@@ -141,6 +134,9 @@ public class AppDbContext : DbContext
modelBuilder.Entity<AccountAccess>() modelBuilder.Entity<AccountAccess>()
.HasKey(x => x.Id); .HasKey(x => x.Id);
modelBuilder.Entity<AccountAccessInvite>()
.HasKey(x => x.Id);
modelBuilder.Entity<Motion>() modelBuilder.Entity<Motion>()
.HasKey(x => x.Id); .HasKey(x => x.Id);
@@ -170,6 +166,16 @@ public class AppDbContext : DbContext
.WithMany(x => x.AccountAccess) .WithMany(x => x.AccountAccess)
.HasForeignKey(x => x.UserId); .HasForeignKey(x => x.UserId);
modelBuilder.Entity<AccountAccess>()
.HasOne(x => x.Owner)
.WithMany(x => x.AccountAccessOwners)
.HasForeignKey(x => x.OwnerId);
modelBuilder.Entity<AccountAccessInvite>()
.HasOne(x => x.User)
.WithMany(x => x.AccountAccessInvites)
.HasForeignKey(x => x.UserId);
modelBuilder.Entity<Motion>() modelBuilder.Entity<Motion>()
.HasOne(x => x.Account) .HasOne(x => x.Account)
.WithMany(x => x.Motions) .WithMany(x => x.Motions)
@@ -0,0 +1,663 @@
// <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("20230726160638_AccessOwner")]
partial class AccessOwner
{
/// <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<Guid?>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("OwnerId");
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", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId");
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.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("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,49 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccessOwner : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "OwnerId",
table: "AccountAccesses",
type: "uuid",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_AccountAccesses_OwnerId",
table: "AccountAccesses",
column: "OwnerId");
migrationBuilder.AddForeignKey(
name: "FK_AccountAccesses_Users_OwnerId",
table: "AccountAccesses",
column: "OwnerId",
principalTable: "Users",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AccountAccesses_Users_OwnerId",
table: "AccountAccesses");
migrationBuilder.DropIndex(
name: "IX_AccountAccesses_OwnerId",
table: "AccountAccesses");
migrationBuilder.DropColumn(
name: "OwnerId",
table: "AccountAccesses");
}
}
}
@@ -0,0 +1,665 @@
// <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("20230726161226_AccessOwner2")]
partial class AccessOwner2
{
/// <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<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("OwnerId");
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", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.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("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,60 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccessOwner2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AccountAccesses_Users_OwnerId",
table: "AccountAccesses");
migrationBuilder.AlterColumn<Guid>(
name: "OwnerId",
table: "AccountAccesses",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uuid",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_AccountAccesses_Users_OwnerId",
table: "AccountAccesses",
column: "OwnerId",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_AccountAccesses_Users_OwnerId",
table: "AccountAccesses");
migrationBuilder.AlterColumn<Guid>(
name: "OwnerId",
table: "AccountAccesses",
type: "uuid",
nullable: true,
oldClrType: typeof(Guid),
oldType: "uuid");
migrationBuilder.AddForeignKey(
name: "FK_AccountAccesses_Users_OwnerId",
table: "AccountAccesses",
column: "OwnerId",
principalTable: "Users",
principalColumn: "Id");
}
}
}
@@ -0,0 +1,695 @@
// <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("20230728172249_AccountAccessInvites")]
partial class AccountAccessInvites
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:CollationDefinition:my_ci_collation", "en-u-ks-primary,en-u-ks-primary,icu,False")
.HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid?>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("OwnerId");
b.ToTable("Accounts");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<bool>("IsAllowManage")
.HasColumnType("boolean");
b.Property<bool>("IsAllowRead")
.HasColumnType("boolean");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("OwnerId");
b.HasIndex("UserId");
b.ToTable("AccountAccesses");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("CategoryId");
b.ToTable("AccountAccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AccountCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Motion", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("AccountId")
.HasColumnType("uuid");
b.Property<decimal>("AmountMinus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<decimal>("AmountPlus")
.HasPrecision(18, 6)
.HasColumnType("numeric(18,6)");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<int>("ItemId")
.HasColumnType("integer");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("AccountId");
b.HasIndex("ItemId");
b.HasIndex("UserId");
b.ToTable("Motions");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.Currency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyGlobalId")
.IsRequired()
.HasColumnType("text");
b.Property<int?>("CurrentRateId")
.HasColumnType("integer");
b.Property<bool>("IsPrimary")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ShortName")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CurrencyGlobalId");
b.HasIndex("CurrentRateId");
b.HasIndex("UserId");
b.ToTable("Currencies");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyGlobal", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("DefaultQuantity")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Symbol")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("CurrencyGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Currencies.CurrencyRate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CurrencyId")
.HasColumnType("uuid");
b.Property<DateTime>("DateTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Quantity")
.HasColumnType("integer");
b.Property<decimal>("Rate")
.HasColumnType("numeric");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("CurrencyRates");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.Item", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("CategoryId")
.HasColumnType("uuid");
b.Property<Guid>("ItemGlobalId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.HasIndex("ItemGlobalId");
b.ToTable("Items");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemCategory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("IsInternal")
.HasColumnType("boolean");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ItemCategories");
});
modelBuilder.Entity("MyOffice.Data.Models.Items.ItemGlobal", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("ItemGlobals");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("CurrencyId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("IsEmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Phone")
.HasColumnType("text");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.HasKey("Id");
b.HasIndex("CurrencyId");
b.ToTable("Users");
});
modelBuilder.Entity("MyOffice.Data.Models.Users.UserExternal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Provider")
.IsRequired()
.HasColumnType("text")
.UseCollation("my_ci_collation");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserClaims");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.Account", b =>
{
b.HasOne("MyOffice.Data.Models.Currencies.CurrencyGlobal", "CurrencyGlobal")
.WithMany("Accounts")
.HasForeignKey("CurrencyGlobalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("Accounts")
.HasForeignKey("OwnerId");
b.Navigation("CurrencyGlobal");
b.Navigation("Owner");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccess", b =>
{
b.HasOne("MyOffice.Data.Models.Accounts.Account", "Account")
.WithMany("AccessRights")
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.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("AccountAccessOwners");
b.Navigation("AccountCategories");
b.Navigation("AccountMotions");
b.Navigation("Accounts");
b.Navigation("Currencies");
b.Navigation("ItemCategories");
b.Navigation("UserClaims");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,48 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MyOffice.Migrations.Postgres.Migrations
{
/// <inheritdoc />
public partial class AccountAccessInvites : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Name",
table: "AccountAccesses",
type: "text",
nullable: true);
migrationBuilder.CreateTable(
name: "AccountAccessInvites",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
CreatedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
AcceptedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
RejectedOn = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Email = table.Column<string>(type: "text", nullable: false),
IsAllowWrite = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AccountAccessInvites", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AccountAccessInvites");
migrationBuilder.DropColumn(
name: "Name",
table: "AccountAccesses");
}
}
}
@@ -69,6 +69,12 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Property<bool>("IsAllowWrite") b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean"); .HasColumnType("boolean");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.Property<string>("Type") b.Property<string>("Type")
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
@@ -80,11 +86,40 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.HasIndex("AccountId"); b.HasIndex("AccountId");
b.HasIndex("OwnerId");
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("AccountAccesses"); b.ToTable("AccountAccesses");
}); });
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccessInvite", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime?>("AcceptedOn")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreatedOn")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsAllowWrite")
.HasColumnType("boolean");
b.Property<DateTime?>("RejectedOn")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("AccountAccessInvites");
});
modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b => modelBuilder.Entity("MyOffice.Data.Models.Accounts.AccountAccountCategory", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@@ -427,6 +462,12 @@ namespace MyOffice.Migrations.Postgres.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "Owner")
.WithMany("AccountAccessOwners")
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MyOffice.Data.Models.Users.User", "User") b.HasOne("MyOffice.Data.Models.Users.User", "User")
.WithMany("AccountAccess") .WithMany("AccountAccess")
.HasForeignKey("UserId") .HasForeignKey("UserId")
@@ -435,6 +476,8 @@ namespace MyOffice.Migrations.Postgres.Migrations
b.Navigation("Account"); b.Navigation("Account");
b.Navigation("Owner");
b.Navigation("User"); b.Navigation("User");
}); });
@@ -629,6 +672,8 @@ namespace MyOffice.Migrations.Postgres.Migrations
{ {
b.Navigation("AccountAccess"); b.Navigation("AccountAccess");
b.Navigation("AccountAccessOwners");
b.Navigation("AccountCategories"); b.Navigation("AccountCategories");
b.Navigation("AccountMotions"); b.Navigation("AccountMotions");
+2
View File
@@ -55,6 +55,7 @@
"chart.js": "^4.2.1", "chart.js": "^4.2.1",
"core-js": "^3.28.0", "core-js": "^3.28.0",
"echarts": "^5.4.1", "echarts": "^5.4.1",
"lodash": "^4.17.21",
"moment": "^2.29.4", "moment": "^2.29.4",
"ng-apexcharts": "^1.7.4", "ng-apexcharts": "^1.7.4",
"ng-image-fullscreen-view": "^3.0.3", "ng-image-fullscreen-view": "^3.0.3",
@@ -84,6 +85,7 @@
"@types/ckeditor__ckeditor5-build-classic": "^29.0.1", "@types/ckeditor__ckeditor5-build-classic": "^29.0.1",
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/jasmine": "~4.3.0", "@types/jasmine": "~4.3.0",
"@types/lodash": "^4.14.196",
"@typescript-eslint/eslint-plugin": "5.48.2", "@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2", "@typescript-eslint/parser": "5.48.2",
"eslint": "^8.33.0", "eslint": "^8.33.0",
+2
View File
@@ -15,6 +15,8 @@ export class ApiRoutes {
static SettingsAccounts = '/api/settings/accounts'; static SettingsAccounts = '/api/settings/accounts';
static SettingsAccount = '/api/settings/accounts/:id'; static SettingsAccount = '/api/settings/accounts/:id';
static SettingsAccountAccesses = '/api/settings/accounts/:id/access';
static SettingsAccountAccess = '/api/settings/accounts/:id/access/:access';
static SettingsAccountAccountCategory = '/api/settings/accounts/:id/category/:categoryId'; static SettingsAccountAccountCategory = '/api/settings/accounts/:id/category/:categoryId';
static SettingsItemCategories = '/api/settings/item-categories'; static SettingsItemCategories = '/api/settings/item-categories';
@@ -5,4 +5,6 @@ export interface AccountAccessRightModel {
isAllowRead: boolean; isAllowRead: boolean;
isAllowWrite: boolean; isAllowWrite: boolean;
isAllowManage: boolean; isAllowManage: boolean;
isAllowDelete: boolean;
isOwner: boolean;
} }
@@ -8,6 +8,7 @@ export interface AccountModel {
currencyName?: string, currencyName?: string,
type?: string, type?: string,
allowDelete: boolean, allowDelete: boolean,
allowManage: boolean,
categories?: AccountCategoryModel[], categories?: AccountCategoryModel[],
accessRights?: AccountAccessRightModel[], accessRights?: AccountAccessRightModel[],
} }
@@ -0,0 +1,7 @@
account-motion:nth-child(even) {
filter: brightness(1)
}
account-motion:nth-child(odd) {
filter: brightness(1.75)
}
@@ -33,6 +33,7 @@ import { SettingsAccountCategoryComponent } from './settings/account/account.cat
import { SettingsAccountComponent } from './settings/account/account.component'; import { SettingsAccountComponent } from './settings/account/account.component';
import { SettingsAccountAddComponent } from './settings/account/add.account.component'; import { SettingsAccountAddComponent } from './settings/account/add.account.component';
import { SettingsAccountEditComponent } from './settings/account/edit.account.component'; import { SettingsAccountEditComponent } from './settings/account/edit.account.component';
import { SettingsAccountAccessComponent } from './settings/account/access.account.component';
import { CurrencyService } from '../services/currency.service'; import { CurrencyService } from '../services/currency.service';
import { ItemService } from '../services/item.service'; import { ItemService } from '../services/item.service';
import { SettingsItemCategoryComponent } from './settings/item/item.category.component'; import { SettingsItemCategoryComponent } from './settings/item/item.category.component';
@@ -55,6 +56,7 @@ import { SettingsItemCategoryEditComponent } from './settings/item/edit.item.cat
SettingsAccountComponent, SettingsAccountComponent,
SettingsAccountAddComponent, SettingsAccountAddComponent,
SettingsAccountEditComponent, SettingsAccountEditComponent,
SettingsAccountAccessComponent,
SettingsItemCategoryComponent, SettingsItemCategoryComponent,
SettingsItemComponent, SettingsItemComponent,
@@ -0,0 +1,61 @@
<div>
<h2 mat-dialog-title>
Account access rights
</h2>
<div mat-dialog-content>
<form [formGroup]="editForm!" (ngSubmit)="onSubmitClick()">
<div class="row">
<div class="col-md-6">
<label>Invite users</label>
<div class="text-inside">
<mat-form-field class="example-full-width">
<mat-label>Email</mat-label>
<input matInput formControlName="email">
</mat-form-field>
</div>
</div>
<div class="col-md-6">
<h6>User rights</h6>
<section>
<mat-checkbox formControlName="allowWrite">
Allow write
</mat-checkbox>
</section>
</div>
</div>
<div formArrayName="accesses">
<div class="row" *ngFor="let access of accessRightsSorted; let i = index" [formGroupName]="i">
<div class="col-md-6">
<input type="hidden" formControlName="userId" />
{{access.user.email}}
<span *ngIf="access.isOwner" class="color-gray">(owner)</span>
</div>
<div class="col-md-3">
<mat-checkbox formControlName="allowWrite">
Allow write
</mat-checkbox>
</div>
<div class="col-md-3">
<button type="button" [disabled]="!access.isAllowDelete" class="btn-space" mat-raised-button color="primary" (click)="removeAccessRight(access)">Delete</button>
</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">
{{errorMessage}}
</div>
</div>
<div class="mat-dialog-right">
<div class="button-bottom">
<button type="button" mat-button (click)="closeDialog()">Cancel</button>
<button class="btn-space" mat-raised-button color="primary">Save</button>
</div>
</div>
</mat-dialog-actions>
</form>
</div>
</div>
@@ -0,0 +1,131 @@
// angular
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Inject } from '@angular/core';
import { UntypedFormBuilder } from '@angular/forms';
import { UntypedFormGroup } from '@angular/forms';
import { FormArray } from '@angular/forms';
import { FormGroup } from '@angular/forms';
import { Validators } from '@angular/forms';
// libs
import Swal from 'sweetalert2';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import * as ld from 'lodash';
// app
import { ApiRoutes } from '../../../api-routes';
import { AccountModel } from '../../../model/account.model';
import { MatDialogRef } from '@angular/material/dialog';
import { CurrencyService } from '../../../services/currency.service';
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: './access.account.component.html',
styleUrls: ['./access.account.component.scss'],
})
export class SettingsAccountAccessComponent {
public editForm!: UntypedFormGroup;
public errorMessage?: string;
public currencies?: CurrencyModel[];
public categories?: AccountCategoryModel[];
public types = new Map<string, string>();
public accessRightsSorted: AccountAccessRightModel[];
constructor(
private fb: UntypedFormBuilder,
private httpClient: HttpClient,
private dialogRef: MatDialogRef<SettingsAccountAccessComponent>,
private accountService: AccountService,
@Inject(MAT_DIALOG_DATA) public account: AccountModel
) {
this.types = this.accountService.getTypes();
this.accessRightsSorted = ld.orderBy(account.accessRights!, ['isOwner', 'user.email'], ['desc', 'asc']);
}
ngOnInit(): void {
var accesses = this.fb.array([]);
this.editForm = this.fb.group({
email: [
'',
],
allowWrite: [
false,
],
accesses: accesses,
});
this.accessRightsSorted.forEach((x, i) => {
var g = this.fb.group({
userId: [
x.user.id
],
allowWrite: [{
value: x.isAllowWrite,
disabled: !x.isAllowDelete,
}],
});
accesses.push(g);
});
}
removeAccessRight(accessRight: AccountAccessRightModel) {
Swal.fire({
title: 'Delete access ' + accessRight.user.email,
showCancelButton: true,
confirmButtonText: 'Delete',
showLoaderOnConfirm: true,
preConfirm: (name) => {
return new Promise((resolve, reject) => {
var url = ApiRoutes.SettingsAccountAccess
.replace(':id', this.account.id!)
.replace(':access', accessRight.user.id);
this.errorMessage = undefined;
this.httpClient
.delete(url, this.editForm.value)
.subscribe(response => {
resolve(response);
}, error => {
Swal.showValidationMessage(error.detail);
reject();
});
}).catch(x => {
return false;
});
},
allowOutsideClick: () => !Swal.isLoading(),
}).then((result) => {
if (result.isConfirmed) {
this.dialogRef.close({ refresh: true });
}
});
}
closeDialog(): void {
this.dialogRef.close();
}
onSubmitClick() {
if (this.editForm.valid) {
this.errorMessage = undefined;
this.httpClient
.post<AccountModel>(ApiRoutes.SettingsAccountAccesses.replace(':id', this.account.id!), this.editForm.value)
.subscribe(response => {
this.dialogRef.close({ refresh: true });
}, error => {
this.errorMessage = error.detail;
});
}
}
}
@@ -35,7 +35,10 @@
<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 Edit
</button> </button>
<button class="btn-space" *ngIf="account.allowDelete" (click)="remove(account)" mat-raised-button color="primary"> <button class="btn-space" *ngIf="account.allowManage" (click)="access(account)" mat-raised-button color="primary">
Access
</button>
<button class="btn-space" *ngIf="account.allowDelete" (click)="remove(account)" mat-raised-button color="primary">
Delete Delete
</button> </button>
</td> </td>
@@ -12,6 +12,7 @@ import { AccountModel } from '../../../model/account.model';
import { AccountCategoryModel } from '../../../model/account.category.model'; import { AccountCategoryModel } from '../../../model/account.category.model';
import { SettingsAccountAddComponent } from './add.account.component'; import { SettingsAccountAddComponent } from './add.account.component';
import { SettingsAccountEditComponent } from './edit.account.component'; import { SettingsAccountEditComponent } from './edit.account.component';
import { SettingsAccountAccessComponent } from './access.account.component';
import { AccountCategoryService } from '../../../services/account.category.service'; import { AccountCategoryService } from '../../../services/account.category.service';
@Component({ @Component({
@@ -106,4 +107,17 @@ export class SettingsAccountComponent {
this.loadAccounts(); this.loadAccounts();
}); });
} }
public access(account: AccountModel) {
this.dialogModel.open(SettingsAccountAccessComponent, {
width: '640px',
disableClose: true,
data: account,
}).afterClosed().subscribe(x => {
if (x && x.refresh) {
this.loadAccounts();
}
});
}
} }
@@ -43,7 +43,7 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-12">
<div class="text-inside"> <div class="text-inside">
<mat-form-field class="example-full-width"> <mat-form-field class="example-full-width">
<mat-label>Category</mat-label> <mat-label>Category</mat-label>
@@ -55,14 +55,6 @@
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<div class="col-md-6">
<div class="text-inside">
<mat-form-field class="example-full-width">
<mat-label>User</mat-label>
<input matInput value={{username}} readonly>
</mat-form-field>
</div>
</div>
</div> </div>
<mat-dialog-actions class="mat-dialog-actions"> <mat-dialog-actions class="mat-dialog-actions">
<div class="mat-dialog-left"> <div class="mat-dialog-left">
@@ -45,7 +45,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-12">
<label>Categories</label> <label>Categories</label>
<div class="col-md-12"> <div class="col-md-12">
<div class="text-inside"> <div class="text-inside">
@@ -62,7 +62,7 @@
<div class="col-md-12"> <div class="col-md-12">
<table class="table" style="table-layout: fixed;"> <table class="table" style="table-layout: fixed;">
<tbody> <tbody>
<tr *ngFor="let category of account.categories"> <tr *ngFor="let category of categoriesSorted">
<td style="width: 100%;">{{category.name}}</td> <td style="width: 100%;">{{category.name}}</td>
<td style="width: 100px;"> <td style="width: 100px;">
<button class="btn-space" (click)="removeCategory(category)" mat-raised-button color="primary"> <button class="btn-space" (click)="removeCategory(category)" mat-raised-button color="primary">
@@ -74,37 +74,6 @@
</table> </table>
</div> </div>
</div> </div>
<div class="col-md-6">
<label>Users</label>
<div class="col-md-12">
<div class="text-inside">
<mat-form-field class="example-full-width">
<mat-label>Category</mat-label>
<mat-select formControlName="categoryId">
<mat-option *ngFor="let category of categories" [value]="category.id">
{{category.name}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="col-md-12">
<table class="table" style="table-layout: fixed;">
<tbody>
<tr *ngFor="let accessRight of account.accessRights">
<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>
</div>
</div>
</div> </div>
<mat-dialog-actions class="mat-dialog-actions"> <mat-dialog-actions class="mat-dialog-actions">
@@ -9,6 +9,7 @@ import { Validators } from '@angular/forms';
// libs // libs
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import * as ld from 'lodash';
// app // app
import { ApiRoutes } from '../../../api-routes'; import { ApiRoutes } from '../../../api-routes';
@@ -32,6 +33,9 @@ export class SettingsAccountEditComponent {
public categories?: AccountCategoryModel[]; public categories?: AccountCategoryModel[];
public types = new Map<string, string>(); public types = new Map<string, string>();
public categoriesSorted: AccountCategoryModel[];
public accessRightsSorted: AccountAccessRightModel[];
constructor( constructor(
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private httpClient: HttpClient, private httpClient: HttpClient,
@@ -42,6 +46,9 @@ export class SettingsAccountEditComponent {
@Inject(MAT_DIALOG_DATA) public account: AccountModel @Inject(MAT_DIALOG_DATA) public account: AccountModel
) { ) {
this.types = this.accountService.getTypes(); this.types = this.accountService.getTypes();
this.categoriesSorted = ld.orderBy(account.categories!, ['name'], ['asc']);
this.accessRightsSorted = ld.orderBy(account.accessRights!, ['isOwner', 'user.email'], ['desc', 'asc']);
} }
ngOnInit(): void { ngOnInit(): void {
@@ -63,6 +70,9 @@ export class SettingsAccountEditComponent {
type: [ type: [
this.account.type, this.account.type,
], ],
userEmail: [
'',
],
}); });
this.currencyService this.currencyService
+144 -40
View File
@@ -13,6 +13,7 @@
using Item.Domain; using Item.Domain;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MyOffice.Services.Account.Domain; using MyOffice.Services.Account.Domain;
using System.Collections.Generic;
public class AccountService public class AccountService
{ {
@@ -20,6 +21,7 @@
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly IAccountCategoryRepository _accountCategoryRepository; private readonly IAccountCategoryRepository _accountCategoryRepository;
private readonly IAccountAccessRepository _accountAccessRepository; private readonly IAccountAccessRepository _accountAccessRepository;
private readonly IAccountAccessInviteRepository _accountAccessInviteRepository;
private readonly IAccountRepository _accountRepository; private readonly IAccountRepository _accountRepository;
private readonly ICurrencyRepository _currencyRepository; private readonly ICurrencyRepository _currencyRepository;
private readonly IAccountAccountCategoryRepository _accountAccountCategoryRepository; private readonly IAccountAccountCategoryRepository _accountAccountCategoryRepository;
@@ -33,6 +35,7 @@
IMapper mapper, IMapper mapper,
IAccountCategoryRepository accountCategoryRepository, IAccountCategoryRepository accountCategoryRepository,
IAccountAccessRepository accountAccessRepository, IAccountAccessRepository accountAccessRepository,
IAccountAccessInviteRepository accountAccessInviteRepository,
IAccountRepository accountRepository, IAccountRepository accountRepository,
ICurrencyRepository currencyRepository, ICurrencyRepository currencyRepository,
IAccountAccountCategoryRepository accountAccountCategoryRepository, IAccountAccountCategoryRepository accountAccountCategoryRepository,
@@ -46,6 +49,7 @@
_mapper = mapper; _mapper = mapper;
_accountCategoryRepository = accountCategoryRepository; _accountCategoryRepository = accountCategoryRepository;
_accountAccessRepository = accountAccessRepository; _accountAccessRepository = accountAccessRepository;
_accountAccessInviteRepository = accountAccessInviteRepository;
_accountRepository = accountRepository; _accountRepository = accountRepository;
_currencyRepository = currencyRepository; _currencyRepository = currencyRepository;
_accountAccountCategoryRepository = accountAccountCategoryRepository; _accountAccountCategoryRepository = accountAccountCategoryRepository;
@@ -55,14 +59,16 @@
_itemService = itemService; _itemService = itemService;
} }
public List<AccountCategory> GetAllCategories(Guid userId) public List<AccountCategoryDto> GetAllCategories(Guid userId)
{ {
return _accountCategoryRepository.GetAll(userId); var categories = _accountCategoryRepository.GetAll(userId);
return _mapper.Map<List<AccountCategoryDto>>(categories);
} }
public Exec<AccountCategory, GeneralExecStatus> GetCategory(Guid userId, Guid id) public Exec<AccountCategoryDto, GeneralExecStatus> GetCategory(Guid userId, Guid id)
{ {
var result = new Exec<AccountCategory, GeneralExecStatus>(GeneralExecStatus.success); var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);
var category = _accountCategoryRepository.Get(userId, id); var category = _accountCategoryRepository.Get(userId, id);
@@ -71,15 +77,15 @@
return result.Set(GeneralExecStatus.not_found); return result.Set(GeneralExecStatus.not_found);
} }
return result.Set(category); return result.Set(_mapper.Map<AccountCategoryDto>(category));
} }
public Exec<AccountCategory, GeneralExecStatus> CategoryAdd(Guid userId, AccountCategory category) public Exec<AccountCategoryDto, GeneralExecStatus> CategoryAdd(Guid userId, AccountCategory category)
{ {
if (category == null) if (category == null)
throw new ArgumentNullException(nameof(category)); throw new ArgumentNullException(nameof(category));
var result = new Exec<AccountCategory, GeneralExecStatus>(GeneralExecStatus.success); var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);
category.Id = Guid.NewGuid(); category.Id = Guid.NewGuid();
category.UserId = userId; category.UserId = userId;
@@ -89,15 +95,15 @@
return result.Set(GeneralExecStatus.failure); return result.Set(GeneralExecStatus.failure);
} }
return result.Set(category); return result.Set(_mapper.Map<AccountCategoryDto>(category));
} }
public Exec<AccountCategory, GeneralExecStatus> CategoryUpdate(Guid userId, Guid id, AccountCategory category) public Exec<AccountCategoryDto, GeneralExecStatus> CategoryUpdate(Guid userId, Guid id, AccountCategory category)
{ {
if (category == null) if (category == null)
throw new ArgumentNullException(nameof(category)); throw new ArgumentNullException(nameof(category));
var result = new Exec<AccountCategory, GeneralExecStatus>(GeneralExecStatus.success); var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);
var exists = _accountCategoryRepository.Get(userId, id); var exists = _accountCategoryRepository.Get(userId, id);
if (exists == null) if (exists == null)
@@ -112,12 +118,12 @@
return result.Set(GeneralExecStatus.failure); return result.Set(GeneralExecStatus.failure);
} }
return result.Set(exists); return result.Set(_mapper.Map<AccountCategoryDto>(exists));
} }
public Exec<AccountCategory, AccountCategoryRemoveResult> CategoryRemove(Guid userId, Guid id) public Exec<AccountCategoryDto, AccountCategoryRemoveResult> CategoryRemove(Guid userId, Guid id)
{ {
var result = new Exec<AccountCategory, AccountCategoryRemoveResult>(AccountCategoryRemoveResult.success); var result = new Exec<AccountCategoryDto, AccountCategoryRemoveResult>(AccountCategoryRemoveResult.success);
var exists = _accountCategoryRepository.Get(userId, id); var exists = _accountCategoryRepository.Get(userId, id);
if (exists == null) if (exists == null)
@@ -134,12 +140,14 @@
return result.Set(AccountCategoryRemoveResult.failure); return result.Set(AccountCategoryRemoveResult.failure);
} }
return result.Set(exists); return result.Set(_mapper.Map<AccountCategoryDto>(exists));
} }
public List<AccountDto> GetAllAccounts(Guid userId) public List<AccountDto> GetAllAccounts(Guid userId)
{ {
return _mapper.Map<List<AccountDto>>(_accountRepository.GetAll(userId)); var accounts = _accountRepository.GetAll(userId);
return _mapper.Map<List<AccountDto>>(accounts, o => o.Items.Add("UserId", userId));
} }
public List<AccountDto> GetByCategory(Guid userId, Guid categoryId) public List<AccountDto> GetByCategory(Guid userId, Guid categoryId)
@@ -165,22 +173,22 @@
return result.Set(_mapper.Map<AccountDetailedDto>(account)); return result.Set(_mapper.Map<AccountDetailedDto>(account));
} }
public Exec<Account, AccountAddResult> AccountAdd(Guid userId, AccountAdd input) public Exec<Account, AccountAddStatus> AccountAdd(Guid userId, AccountAdd input)
{ {
if (input == null) if (input == null)
throw new ArgumentNullException(nameof(input)); throw new ArgumentNullException(nameof(input));
var result = new Exec<Account, AccountAddResult>(AccountAddResult.success); var result = new Exec<Account, AccountAddStatus>(AccountAddStatus.success);
var category = _accountCategoryRepository.Get(userId, input.CategoryId); var category = _accountCategoryRepository.Get(userId, input.CategoryId);
if (category == null) if (category == null)
{ {
return result.Set(AccountAddResult.category_not_found); return result.Set(AccountAddStatus.category_not_found);
} }
var currency = _currencyRepository.GetByGlobalCurrency(userId, input.CurrencyId); var currency = _currencyRepository.GetByGlobalCurrency(userId, input.CurrencyId);
if (currency == null) if (currency == null)
{ {
return result.Set(AccountAddResult.currency_not_found); return result.Set(AccountAddStatus.currency_not_found);
} }
var account = new Account var account = new Account
@@ -188,6 +196,7 @@
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Name = input.Name, Name = input.Name,
CurrencyGlobalId = currency.CurrencyGlobalId, CurrencyGlobalId = currency.CurrencyGlobalId,
OwnerId = userId,
}; };
account.Categories = new List<AccountAccountCategory> account.Categories = new List<AccountAccountCategory>
@@ -204,6 +213,7 @@
{ {
AccountId = account.Id, AccountId = account.Id,
UserId = userId, UserId = userId,
OwnerId = userId,
IsAllowManage = true, IsAllowManage = true,
IsAllowRead = true, IsAllowRead = true,
IsAllowWrite = true, IsAllowWrite = true,
@@ -212,7 +222,7 @@
if (!_accountRepository.Add(account)) if (!_accountRepository.Add(account))
{ {
return result.Set(AccountAddResult.failure); return result.Set(AccountAddStatus.failure);
} }
var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId); var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId);
@@ -249,12 +259,12 @@
return result; return result;
} }
public Exec<Account, AccountEditResult> AccountUpdate(Guid userId, Guid accountId, AccountEdit input) public Exec<Account, AccountEditStatus> AccountUpdate(Guid userId, Guid accountId, AccountEdit input)
{ {
if (input == null) if (input == null)
throw new ArgumentNullException(nameof(input)); throw new ArgumentNullException(nameof(input));
var result = new Exec<Account, AccountEditResult>(AccountEditResult.success); var result = new Exec<Account, AccountEditStatus>(AccountEditStatus.success);
AccountCategory? category = null; AccountCategory? category = null;
if (input.CategoryId.HasValue) if (input.CategoryId.HasValue)
@@ -262,20 +272,20 @@
category = _accountCategoryRepository.Get(userId, input.CategoryId.Value); category = _accountCategoryRepository.Get(userId, input.CategoryId.Value);
if (category == null) if (category == null)
{ {
return result.Set(AccountEditResult.category_not_found); return result.Set(AccountEditStatus.category_not_found);
} }
} }
var currency = _currencyRepository.GetByGlobalCurrency(userId, input.CurrencyId); var currency = _currencyRepository.GetByGlobalCurrency(userId, input.CurrencyId);
if (currency == null) if (currency == null)
{ {
return result.Set(AccountEditResult.currency_not_found); return result.Set(AccountEditStatus.currency_not_found);
} }
var account = _accountRepository.Get(userId, accountId); var account = _accountRepository.Get(userId, accountId);
if (account == null) if (account == null)
{ {
return result.Set(AccountEditResult.not_found); return result.Set(AccountEditStatus.not_found);
} }
account.Name = input.Name; account.Name = input.Name;
@@ -298,7 +308,7 @@
if (!_accountRepository.Update(account)) if (!_accountRepository.Update(account))
{ {
return result.Set(AccountEditResult.failure); return result.Set(AccountEditStatus.failure);
} }
var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId); var access = account.AccessRights!.FirstOrDefault(x => x.UserId == userId);
@@ -332,7 +342,7 @@
return result.Set(categories); return result.Set(categories);
} }
public Exec<List<Motion>, MotionAddResult> MotionAdd( public Exec<List<Motion>, MotionAddStatus> MotionAdd(
Guid userId, Guid userId,
Guid accountId, Guid accountId,
MotionAddUpdate motion MotionAddUpdate motion
@@ -343,18 +353,18 @@
if (motion.Item == null) if (motion.Item == null)
throw new ArgumentNullException(nameof(motion.Item)); throw new ArgumentNullException(nameof(motion.Item));
var result = new Exec<List<Motion>, MotionAddResult>(MotionAddResult.success); var result = new Exec<List<Motion>, MotionAddStatus>(MotionAddStatus.success);
var account = _accountRepository.Get(userId, accountId); var account = _accountRepository.Get(userId, accountId);
if (account == null) if (account == null)
{ {
return result.Set(MotionAddResult.account_not_found); return result.Set(MotionAddStatus.account_not_found);
} }
var itemExec = _itemService.GetOrCreate(userId, motion.Item); var itemExec = _itemService.GetOrCreate(userId, motion.Item);
if (itemExec.Status != ItemGetOrAddResult.success) if (itemExec.Status != ItemGetOrAddResult.success)
{ {
return result.Set(MotionAddResult.failure); return result.Set(MotionAddStatus.failure);
} }
var motionDb = new Motion var motionDb = new Motion
@@ -371,7 +381,7 @@
if (!_motionRepository.Add(motionDb)) if (!_motionRepository.Add(motionDb))
{ {
return result.Set(MotionAddResult.failure); return result.Set(MotionAddStatus.failure);
} }
result.Set(new List<Motion>()); result.Set(new List<Motion>());
@@ -415,7 +425,7 @@
return result; return result;
} }
public Exec<Motion, MotionUpdateResult> MotionUpdate( public Exec<Motion, MotionUpdateStatus> MotionUpdate(
Guid userId, Guid userId,
Guid accountId, Guid accountId,
Guid motionId, Guid motionId,
@@ -427,18 +437,18 @@
if (motion.Item == null) if (motion.Item == null)
throw new ArgumentNullException(nameof(motion.Item)); throw new ArgumentNullException(nameof(motion.Item));
var result = new Exec<Motion, MotionUpdateResult>(MotionUpdateResult.success); var result = new Exec<Motion, MotionUpdateStatus>(MotionUpdateStatus.success);
var exists = _motionRepository.Get(userId, motionId); var exists = _motionRepository.Get(userId, motionId);
if (exists == null || exists.AccountId != accountId) if (exists == null || exists.AccountId != accountId)
{ {
return result.Set(MotionUpdateResult.not_found); return result.Set(MotionUpdateStatus.not_found);
} }
var itemExec = _itemService.GetOrCreate(userId, motion.Item); var itemExec = _itemService.GetOrCreate(userId, motion.Item);
if (itemExec.Status != ItemGetOrAddResult.success) if (itemExec.Status != ItemGetOrAddResult.success)
{ {
return result.Set(MotionUpdateResult.failure); return result.Set(MotionUpdateStatus.failure);
} }
exists.Item.Id = itemExec.Result!.Id; exists.Item.Id = itemExec.Result!.Id;
@@ -449,7 +459,7 @@
if (!_motionRepository.Update(exists)) if (!_motionRepository.Update(exists))
{ {
return result.Set(MotionUpdateResult.failure); return result.Set(MotionUpdateStatus.failure);
} }
@@ -476,23 +486,23 @@
return result.Set(motions); return result.Set(motions);
} }
public Exec<Motion, MotionDeleteResult> MotionRemove( public Exec<Motion, MotionDeleteStatus> MotionRemove(
Guid userId, Guid userId,
Guid accountId, Guid accountId,
Guid motionId Guid motionId
) )
{ {
var result = new Exec<Motion, MotionDeleteResult>(MotionDeleteResult.success); var result = new Exec<Motion, MotionDeleteStatus>(MotionDeleteStatus.success);
var exists = _motionRepository.Get(userId, motionId); var exists = _motionRepository.Get(userId, motionId);
if (exists == null || exists.AccountId != accountId) if (exists == null || exists.AccountId != accountId)
{ {
return result.Set(MotionDeleteResult.not_found); return result.Set(MotionDeleteStatus.not_found);
} }
if (!_motionRepository.Remove(exists)) if (!_motionRepository.Remove(exists))
{ {
return result.Set(MotionDeleteResult.failure); return result.Set(MotionDeleteStatus.failure);
} }
return result.Set(exists); return result.Set(exists);
@@ -502,5 +512,99 @@
{ {
return _accountRepository.FindAccounts(userId, term); return _accountRepository.FindAccounts(userId, term);
} }
public Exec<AccountAccessInvite, AccessInviteStatus> AccessInvite(Guid userId, Guid accountId, string email, bool isAllowWrite)
{
if (email == null)
throw new ArgumentNullException(nameof(email));
var result = new Exec<AccountAccessInvite, AccessInviteStatus>(AccessInviteStatus.success);
var account = _accountRepository.Get(userId, accountId);
if (account == null)
{
return result.Set(AccessInviteStatus.account_not_found);
}
if (account.AccessRights!.Any(x => x.User!.Email.EqualsIgnoreCase(email)))
{
return result.Set(AccessInviteStatus.account_not_found);
}
var access = _accountAccessInviteRepository.Get(userId, email);
if (access != null)
{
return result.Set(AccessInviteStatus.access_exists);
}
var invite = new AccountAccessInvite
{
Id = Guid.NewGuid(),
CreatedOn = DateTime.UtcNow,
UserId = userId,
Email = email,
IsAllowWrite = isAllowWrite,
};
_accountAccessInviteRepository.Add(invite);
return result.Set(invite);
}
public Exec<AccountDto, GeneralExecStatus> AccessUpdate(Guid userId, Guid accountId, List<AccountAccessDto> accesses)
{
if (accesses == null)
throw new ArgumentNullException(nameof(accesses));
var result = new Exec<AccountDto, GeneralExecStatus>(GeneralExecStatus.success);
var account = _accountRepository.Get(userId, accountId);
if (account == null)
{
return result.Set(GeneralExecStatus.not_found);
}
foreach (var access in account.AccessRights!)
{
var newAccess = accesses.FirstOrDefault(x => x.UserId == access.UserId);
if (newAccess != null && newAccess.IsAllowWrite != access.IsAllowWrite)
{
access.IsAllowWrite = newAccess.IsAllowWrite;
_accountAccessRepository.Update(access);
}
}
account = _accountRepository.Get(userId, accountId);
return result.Set(_mapper.Map<AccountDto>(account));
}
public Exec<AccountDto, GeneralExecStatus> AccessDelete(Guid userId, Guid accountId, Guid accessUserId)
{
var result = new Exec<AccountDto, GeneralExecStatus>(GeneralExecStatus.success);
var account = _accountRepository.Get(userId, accountId);
if (account == null || !account.AccessRights!.Any() || account.AccessRights!.Count() == 1)
{
return result.Set(GeneralExecStatus.not_found);
}
var access = account.AccessRights!.FirstOrDefault(x => x.UserId == accessUserId);
if (access == null)
{
return result.Set(GeneralExecStatus.not_found);
}
if (access.OwnerId == access.UserId)
{
return result.Set(GeneralExecStatus.not_found);
}
_accountAccessRepository.Delete(access);
account = _accountRepository.Get(userId, accountId);
return result.Set(_mapper.Map<AccountDto>(account));
}
} }
} }
@@ -1,30 +0,0 @@
using MyOffice.Services.Account.Domain;
namespace MyOffice.Services.Account;
using AutoMapper;
using Data.Models.Accounts;
using Data.Models.Users;
public class AccountServiceProfile : Profile
{
public AccountServiceProfile()
{
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))
;
}
}
@@ -0,0 +1,9 @@
namespace MyOffice.Services.Account.Domain;
public enum AccessInviteStatus
{
success,
account_not_found,
access_exists,
invite_exists,
}
@@ -5,10 +5,24 @@ using Data.Models.Users;
public class AccountAccessDto public class AccountAccessDto
{ {
/// <summary>
/// Current user
/// </summary>
public Guid CurrenUserId { get; set; }
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
public AccountDto? Account { get; set; } public AccountDto? Account { get; set; }
/// <summary>
/// User can access to account
/// </summary>
public Guid UserId { get; set; } public Guid UserId { get; set; }
public User? User { get; set; } public User? User { get; set; }
/// <summary>
/// Who add access
/// </summary>
public Guid OwnerId { get; set; }
public User? Owner { get; set; }
public bool IsAllowRead { get; set; } public bool IsAllowRead { get; set; }
public bool IsAllowWrite { get; set; } public bool IsAllowWrite { get; set; }
@@ -1,6 +1,6 @@
namespace MyOffice.Services.Account.Domain namespace MyOffice.Services.Account.Domain
{ {
public enum AccountAddResult public enum AccountAddStatus
{ {
success, success,
failure, failure,
@@ -1,6 +1,9 @@
public class AccountCategoryDto namespace MyOffice.Services.Account.Domain;
public class AccountCategoryDto
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public bool AllowDelete { get; set; }
} }
+22 -21
View File
@@ -1,22 +1,23 @@
namespace MyOffice.Services.Account.Domain namespace MyOffice.Services.Account.Domain;
{
using MyOffice.Data.Models.Currencies;
using System;
public class AccountDto using MyOffice.Data.Models.Currencies;
{ using System;
public Guid Id { get; set; }
public string CurrencyGlobalId { get; set; } = null!; public class AccountDto
public CurrencyGlobal? CurrencyGlobal { get; set; } {
public Guid CurrencyId { get; set; } public Guid Id { get; set; }
public Currency? Currency { get; set; } public Guid CurrentUserId { get; set; }
public Guid UserId { get; set; } public string CurrencyGlobalId { get; set; } = null!;
public UserDto? User { get; set; } public CurrencyGlobal? CurrencyGlobal { get; set; }
public Guid OwnerId { get; set; } public Guid CurrencyId { get; set; }
public UserDto? Owner { get; set; } public Currency? Currency { get; set; }
public string Name { get; set; } = null!; public Guid UserId { get; set; }
public bool HasMotions { get; set; } public UserDto? User { get; set; }
public List<AccountAccessDto>? AccessRights { get; set; } public Guid OwnerId { get; set; }
public List<AccountAccountCategoryDto>? Categories { get; set; } public UserDto? Owner { get; set; }
} public string Name { get; set; } = null!;
} public bool HasMotions { get; set; }
public string Type { get; set; } = null!;
public List<AccountAccessDto>? AccessRights { get; set; }
public List<AccountAccountCategoryDto>? Categories { get; set; }
}
@@ -1,6 +1,6 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
public enum AccountEditResult public enum AccountEditStatus
{ {
success, success,
failure, failure,
@@ -1,6 +1,6 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
public enum MotionAddResult public enum MotionAddStatus
{ {
success, success,
failure, failure,
@@ -1,6 +1,6 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
public enum MotionDeleteResult public enum MotionDeleteStatus
{ {
success, success,
failure, failure,
@@ -1,6 +1,6 @@
namespace MyOffice.Services.Account.Domain; namespace MyOffice.Services.Account.Domain;
public enum MotionUpdateResult public enum MotionUpdateStatus
{ {
success, success,
failure, failure,
+1 -1
View File
@@ -12,6 +12,6 @@ public class UserDto
public string? FullName { get; set; } public string? FullName { get; set; }
public string? Phone { get; set; } public string? Phone { get; set; }
public string CurrencyId { get; set; } public string CurrencyId { get; set; } = null!;
public CurrencyGlobal? Currency { get; set; } public CurrencyGlobal? Currency { get; set; }
} }
@@ -13,7 +13,7 @@ public class DashboardData
public decimal? Balance { get; set; } public decimal? Balance { get; set; }
public decimal? BalanceDebit { get; set; } public decimal? BalanceDebit { get; set; }
public decimal? BalanceCredit { get; set; } public decimal? BalanceCredit { get; set; }
public List<DashboardRestData> BalanceRests { get; set; } public List<DashboardRestData> BalanceRests { get; set; } = null!;
} }
public class DashboardRestData public class DashboardRestData
@@ -0,0 +1,31 @@
namespace MyOffice.Services.Mapper;
using Account.Domain;
using AutoMapper;
using Data.Models.Accounts;
using Data.Models.Users;
public class AccountServiceProfile : Profile
{
public AccountServiceProfile()
{
CreateMap<User, UserDto>();
CreateMap<Account, AccountDto>()
.ForMember(x => x.HasMotions, o => o.MapFrom(x => x.Motions!.Any()))
.ForMember(x => x.CurrentUserId, o => o.MapFrom<UserIdResolver>())
.ForMember(x => x.Type, o => o.MapFrom((src, dst) => src.AccessRights!.FirstOrDefault(x => x.UserId == dst.CurrentUserId)!.Type.ToString()))
;
CreateMap<AccountDetailed, AccountDetailedDto>();
CreateMap<AccountAccess, AccountAccessDto>();
CreateMap<AccountAccountCategory, AccountAccountCategoryDto>();
CreateMap<AccountCategory, AccountCategoryDto>()
.ForMember(x => x.Id, o => o.MapFrom(x => x.Id))
.ForMember(x => x.AllowDelete, o => o.MapFrom(x => !x.Accounts!.Any()))
;
}
}
@@ -0,0 +1,21 @@
namespace MyOffice.Services.Mapper;
using AutoMapper;
using Identity;
public class UserIdResolver : IValueResolver<object, object, Guid>
{
private readonly IContextProvider _contextProvider;
public UserIdResolver(
IContextProvider contextProvider
)
{
_contextProvider = contextProvider;
}
public Guid Resolve(object source, object destination, Guid member, ResolutionContext context)
{
return _contextProvider.UserId;
}
}
+9 -10
View File
@@ -58,7 +58,6 @@ public class AccountController : BaseApiController
case GeneralExecStatus.success: case GeneralExecStatus.success:
return _mapper.Map<AccountDetailedViewModel>(exec.Result); return _mapper.Map<AccountDetailedViewModel>(exec.Result);
//return exec.Result!.ToModel();
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
@@ -92,11 +91,11 @@ public class AccountController : BaseApiController
switch (exec.Status) switch (exec.Status)
{ {
case MotionAddResult.account_not_found: case MotionAddStatus.account_not_found:
return ProblemBadRequest("Account not found."); return ProblemBadRequest("Account not found.");
case MotionAddResult.failure: case MotionAddStatus.failure:
return ProblemBadRequest("Adding motion failed."); return ProblemBadRequest("Adding motion failed.");
case MotionAddResult.success: case MotionAddStatus.success:
return _mapper.Map<MotionViewModel[]>(exec.Result!); return _mapper.Map<MotionViewModel[]>(exec.Result!);
default: default:
@@ -111,11 +110,11 @@ public class AccountController : BaseApiController
switch (exec.Status) switch (exec.Status)
{ {
case MotionUpdateResult.not_found: case MotionUpdateStatus.not_found:
return ProblemBadRequest("Motion not found."); return ProblemBadRequest("Motion not found.");
case MotionUpdateResult.failure: case MotionUpdateStatus.failure:
return ProblemBadRequest("Updating motion failed."); return ProblemBadRequest("Updating motion failed.");
case MotionUpdateResult.success: case MotionUpdateStatus.success:
return exec.Result!.ToModel(); return exec.Result!.ToModel();
default: default:
@@ -130,11 +129,11 @@ public class AccountController : BaseApiController
switch (exec.Status) switch (exec.Status)
{ {
case MotionDeleteResult.not_found: case MotionDeleteStatus.not_found:
return ProblemBadRequest("Motion not found."); return ProblemBadRequest("Motion not found.");
case MotionDeleteResult.failure: case MotionDeleteStatus.failure:
return ProblemBadRequest("Deliting motion failed."); return ProblemBadRequest("Deliting motion failed.");
case MotionDeleteResult.success: case MotionDeleteStatus.success:
return exec.Result!.ToModel(); return exec.Result!.ToModel();
default: default:
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
using Models.Account; using Models.Account;
using Services.Account; using Services.Account;
using Services.Account.Domain; using Services.Account.Domain;
using MyOffice.Web.Infrastructure.Attributes;
[Authorize] [Authorize]
[ApiController] [ApiController]
@@ -35,11 +36,11 @@ public class SettingsAccountController : BaseApiController
{ {
var list = _accountService.GetAllCategories(UserId); var list = _accountService.GetAllCategories(UserId);
return list.OrderBy(x => x.Name).Select(x => x.ToModel()); return _mapper.Map<AccountCategoryViewModel[]>(list).OrderBy(x => x.Name);
} }
[HttpGet("~/api/settings/account-categories/{id}")] [HttpGet("~/api/settings/account-categories/{id}")]
public object AccountCategory(string id) public object AccountCategory([AsGuid] string id)
{ {
var exec = _accountService.GetCategory(UserId, id.AsGuid()); var exec = _accountService.GetCategory(UserId, id.AsGuid());
@@ -50,7 +51,7 @@ public class SettingsAccountController : BaseApiController
return ProblemBadRequest("Account category not found."); return ProblemBadRequest("Account category not found.");
case GeneralExecStatus.success: case GeneralExecStatus.success:
return exec.Result!.ToModel(); return _mapper.Map<AccountCategoryViewModel>(exec.Result!);
default: default:
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
@@ -76,7 +77,7 @@ public class SettingsAccountController : BaseApiController
} }
[HttpPut("~/api/settings/account-categories/{id}")] [HttpPut("~/api/settings/account-categories/{id}")]
public object AccountCategoriesEdit(string id, AccountCategoryViewModel request) public object AccountCategoriesEdit([AsGuid] string id, AccountCategoryViewModel request)
{ {
var exec = _accountService.CategoryUpdate(UserId, id.AsGuid(), new AccountCategory { Name = request.Name! }); var exec = _accountService.CategoryUpdate(UserId, id.AsGuid(), new AccountCategory { Name = request.Name! });
switch (exec.Status) switch (exec.Status)
@@ -94,7 +95,7 @@ public class SettingsAccountController : BaseApiController
} }
[HttpDelete("~/api/settings/account-categories/{id}")] [HttpDelete("~/api/settings/account-categories/{id}")]
public object AccountCategoriesDelete(string id) public object AccountCategoriesDelete([AsGuid] string id)
{ {
var exec = _accountService.CategoryRemove(UserId, id.AsGuid()); var exec = _accountService.CategoryRemove(UserId, id.AsGuid());
switch (exec.Status) switch (exec.Status)
@@ -115,7 +116,7 @@ public class SettingsAccountController : BaseApiController
} }
[HttpGet("~/api/settings/accounts")] [HttpGet("~/api/settings/accounts")]
public List<AccountViewModel> AccountsGet(string? category) public List<AccountViewModel> AccountsGet([AsGuid(true)] string? category)
{ {
var list = category.IsPresent() var list = category.IsPresent()
? _accountService.GetByCategory(UserId, category!.AsGuid()) ? _accountService.GetByCategory(UserId, category!.AsGuid())
@@ -137,13 +138,13 @@ public class SettingsAccountController : BaseApiController
switch (exec.Status) switch (exec.Status)
{ {
case AccountAddResult.category_not_found: case AccountAddStatus.category_not_found:
return ProblemBadRequest("Category not found."); return ProblemBadRequest("Category not found.");
case AccountAddResult.currency_not_found: case AccountAddStatus.currency_not_found:
return ProblemBadRequest("Currency not found."); return ProblemBadRequest("Currency not found.");
case AccountAddResult.failure: case AccountAddStatus.failure:
return ProblemBadRequest("Adding account failed."); return ProblemBadRequest("Adding account failed.");
case AccountAddResult.success: case AccountAddStatus.success:
return exec.Result!; return exec.Result!;
default: default:
@@ -153,7 +154,7 @@ public class SettingsAccountController : BaseApiController
[HttpPut("~/api/settings/accounts/{id}")] [HttpPut("~/api/settings/accounts/{id}")]
public object AccountsAdd(string id, AccountEditRequestModel request) public object AccountsAdd([AsGuid] string id, AccountEditRequestModel request)
{ {
var exec = _accountService.AccountUpdate(UserId, id.AsGuid(), new AccountEdit var exec = _accountService.AccountUpdate(UserId, id.AsGuid(), new AccountEdit
{ {
@@ -166,15 +167,15 @@ public class SettingsAccountController : BaseApiController
switch (exec.Status) switch (exec.Status)
{ {
case AccountEditResult.not_found: case AccountEditStatus.not_found:
return ProblemBadRequest("Account not found."); return ProblemBadRequest("Account not found.");
case AccountEditResult.category_not_found: case AccountEditStatus.category_not_found:
return ProblemBadRequest("Category not found."); return ProblemBadRequest("Category not found.");
case AccountEditResult.currency_not_found: case AccountEditStatus.currency_not_found:
return ProblemBadRequest("Currency not found."); return ProblemBadRequest("Currency not found.");
case AccountEditResult.failure: case AccountEditStatus.failure:
return ProblemBadRequest("Adding account failed."); return ProblemBadRequest("Adding account failed.");
case AccountEditResult.success: case AccountEditStatus.success:
return exec.Result!; return exec.Result!;
default: default:
@@ -183,7 +184,7 @@ public class SettingsAccountController : BaseApiController
} }
[HttpDelete("~/api/settings/accounts/{id}")] [HttpDelete("~/api/settings/accounts/{id}")]
public object AccountsDelete(string id) public object AccountsDelete([AsGuid] string id)
{ {
var exec = _accountService.AccountDelete(UserId, id); var exec = _accountService.AccountDelete(UserId, id);
switch (exec.Status) switch (exec.Status)
@@ -199,7 +200,7 @@ public class SettingsAccountController : BaseApiController
} }
[HttpDelete("~/api/settings/accounts/{id}/category/{categoryId}")] [HttpDelete("~/api/settings/accounts/{id}/category/{categoryId}")]
public object AccountsCategoryRemove(string id, string categoryId) public object AccountsCategoryRemove([AsGuid] string id, [AsGuid] string categoryId)
{ {
var exec = _accountService.AccountCategoryRemove(UserId, id.AsGuid(), categoryId.AsGuid()); var exec = _accountService.AccountCategoryRemove(UserId, id.AsGuid(), categoryId.AsGuid());
@@ -216,4 +217,68 @@ public class SettingsAccountController : BaseApiController
throw new NotSupportedException(exec.Status.ToString()); throw new NotSupportedException(exec.Status.ToString());
} }
} }
[HttpPost("~/api/settings/accounts/{id}/access")]
public object AccountsAdd([AsGuid] string id, AccountAccessViewModel request)
{
var model = _mapper.Map<List<AccountAccessDto>>(request.Accesses);
var exec = _accountService.AccessUpdate(UserId, id.AsGuid(), model);
switch (exec.Status)
{
case GeneralExecStatus.not_found:
case GeneralExecStatus.failure:
return ProblemBadRequest("Account not found.");
case GeneralExecStatus.success:
break;
default:
throw new NotSupportedException(exec.Status.ToString());
}
if (!request.Email.IsPresent())
{
return exec.Result!;
}
var inviteExec = _accountService.AccessInvite(UserId, id.AsGuid(), request.Email!, request.AllowWrite);
switch (inviteExec.Status)
{
case AccessInviteStatus.account_not_found:
return ProblemBadRequest("Account not found.");
case AccessInviteStatus.access_exists:
return ProblemBadRequest("Access allowed.");
case AccessInviteStatus.invite_exists:
case AccessInviteStatus.success:
return exec.Result!;
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
[HttpDelete("~/api/settings/accounts/{id}/access/{userId}")]
public object AccountsAdd([AsGuid] string id, [AsGuid] string userId)
{
var exec = _accountService.AccessDelete(UserId, id.AsGuid(), userId.AsGuid());
switch (exec.Status)
{
case GeneralExecStatus.not_found:
case GeneralExecStatus.failure:
return ProblemBadRequest("Account not found.");
case GeneralExecStatus.success:
return exec.Result!;
default:
throw new NotSupportedException(exec.Status.ToString());
}
}
} }
@@ -0,0 +1,35 @@
namespace MyOffice.Web.Infrastructure.Attributes
{
using System.ComponentModel.DataAnnotations;
using Core.Extensions;
public class AsGuidAttribute: ValidationAttribute
{
private readonly bool _nullable;
public AsGuidAttribute(bool nullable = false)
{
_nullable = nullable;
}
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value == null && _nullable)
{
return ValidationResult.Success;
}
if (value == null || value.ToString().IsMissing())
{
return new ValidationResult("Id parameter is not valid");
}
if (!Guid.TryParse(value.ToString(), out _))
{
return new ValidationResult("Id parameter is not valid");
}
return ValidationResult.Success;
}
}
}
@@ -1,7 +1,6 @@
namespace MyOffice.Web.Models.Account namespace MyOffice.Web.Models.Account
{ {
using Data.Models.Accounts; using Newtonsoft.Json;
using Services.Account.Domain;
using User; using User;
public class AccessRightsViewModel public class AccessRightsViewModel
@@ -10,19 +9,16 @@
public bool IsAllowRead { get; set; } public bool IsAllowRead { get; set; }
public bool IsAllowWrite { get; set; } public bool IsAllowWrite { get; set; }
public bool IsAllowManage { get; set; } public bool IsAllowManage { get; set; }
} public bool IsAllowDelete { get; set; }
public bool IsOwner { get; set; }
public static class AccessRightsViewModelExtensions #region DEBUG
{
public static AccessRightsViewModel ToModel(this AccountAccessDto accountAccess) [JsonIgnore]
{ public Guid UserId { get; set; }
return new AccessRightsViewModel [JsonIgnore]
{ public Guid OwnerId { get; set; }
User = accountAccess.User!.ToModel(),
IsAllowManage = accountAccess.IsAllowManage, #endregion DEBUG
IsAllowRead = accountAccess.IsAllowRead,
IsAllowWrite = accountAccess.IsAllowWrite,
};
}
} }
} }
+12 -60
View File
@@ -1,12 +1,7 @@
namespace MyOffice.Web.Models.Account namespace MyOffice.Web.Models.Account
{ {
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations; 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 class AccountViewModel
{ {
@@ -20,71 +15,28 @@
public string CurrencyId { get; set; } = null!; public string CurrencyId { get; set; } = null!;
public string? CurrencyName { get; set; } public string? CurrencyName { get; set; }
public bool AllowDelete { get; set; } public bool AllowDelete { get; set; }
public bool AllowManage { get; set; }
public List<AccountCategoryViewModel>? Categories { get; set; } public List<AccountCategoryViewModel>? Categories { get; set; }
[Required] [Required]
public string CategoryId { get; set; } = null!; public string CategoryId { get; set; } = null!;
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<AccessRightsViewModel>? AccessRights { get; set; } public List<AccessRightsViewModel>? AccessRights { get; set; }
} }
public class ViewModelProfile : Profile
public class AccountAccessViewModel
{ {
public ViewModelProfile() public class AccountAccessItemViewModel
{ {
CreateMap<Guid, string>().ConvertUsing(x => x.ToShort()); public string UserId { get; set; } = null!;
public bool AllowWrite { get; set; }
} }
public string? Email { get; set; }
public bool AllowWrite { get; set; }
public List<AccountAccessItemViewModel> Accesses { get; set; } = null!;
} }
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
{
Id = input.Id.ToShort(),
Name = input.Name,
CurrencyId = input.CurrencyGlobalId,
CurrencyName = input.CurrencyGlobal!.Name,
Categories = input.Categories!
.Select(x => x.Category!.ToModel())
.ToList(),
AccessRights = input.AccessRights!
.Select(x => x.ToModel())
.ToList(),
};
}
}*/
} }
@@ -0,0 +1,71 @@
namespace MyOffice.Web.Models.Account;
using AutoMapper;
using MyOffice.Services.Identity;
using Services.Account.Domain;
using Services.Mapper;
using System.Security.Cryptography;
using Core.Extensions;
using User;
using static MyOffice.Web.Models.Account.AccountAccessViewModel;
public class CustomResolver : IValueResolver<AccountAccessDto, AccessRightsViewModel, bool>
{
private readonly ILogger<CustomResolver> _logger;
private readonly IContextProvider _contextProvider;
public CustomResolver(
ILogger<CustomResolver> logger,
IContextProvider contextProvider
)
{
_logger = logger;
_contextProvider = contextProvider;
}
public bool Resolve(AccountAccessDto source, AccessRightsViewModel destination, bool member, ResolutionContext context)
{
return source.OwnerId == _contextProvider.UserId;
}
}
public class AccountViewModelProfile : Profile
{
public AccountViewModelProfile()
{
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.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))
.ForMember(x => x.AllowManage, o => o.MapFrom(x => x.AccessRights!.Any(a => a.OwnerId == x.CurrentUserId)))
.ForMember(x => x.AccessRights, o => o.MapFrom((s, d) => s.OwnerId != s.CurrentUserId ? null : s.AccessRights))
;
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>()
.ForMember(x => x.IsAllowDelete, o => o.MapFrom(
(src, dst) => src.Account!.OwnerId == src.Account.CurrentUserId && src.UserId != src.Account.CurrentUserId))
.ForMember(x => x.IsOwner, o => o.MapFrom(
(src, dst) => src.OwnerId == src.UserId))
;
CreateMap<AccountAccessItemViewModel, AccountAccessDto>()
.ForMember(x => x.IsAllowWrite, o => o.MapFrom(x => x.AllowWrite))
;
CreateMap<AccountCategoryDto, AccountCategoryViewModel>()
;
}
}
@@ -0,0 +1,12 @@
namespace MyOffice.Web.Models.Account;
using AutoMapper;
using Core.Extensions;
public class ViewModelProfile : Profile
{
public ViewModelProfile()
{
CreateMap<Guid, string>().ConvertUsing(x => x.ToShort());
}
}
+30 -12
View File
@@ -42,16 +42,28 @@ using MyOffice.Services.Currency;
using Services.Account; using Services.Account;
using Services.Item; using Services.Item;
using MyOffice.Services.Dashboard; using MyOffice.Services.Dashboard;
using Services.Account.Domain;
using MyOffice.Services.Identity; using MyOffice.Services.Identity;
using AutoMapper; using AutoMapper;
using Models.Motion; using Models.Motion;
using MyOffice.Services.Account.Domain;
using MyOffice.Services.Mapper;
//TODO: Data.Model only Repository and Service, response <-> mapper <-> web <-> mapper <-> service <-> repository //TODO: Data.Model only Repository and Service, response <-> mapper <-> web <-> mapper <-> service <-> repository
//TODO: Project management //TODO: Project management
//TODO: Model names. Controller = xxxRequest/xxxResponse. Service xxxInput/xxxOutput. //TODO: Model names. Controller = xxxRequest/xxxResponse. Service xxxInput/xxxOutput.
//TODO: Validate string as Guid when id //TODO: Validate string as Guid when id
//TODO: Logging //TODO: Test back
//TODO: Test front
//TODO: Test DB ???
//TODO: Email confirmation
//TODO: Password recovery
//TODO: Account sharing
//TODO: /dashboard/dashboard -> /dashboard/main or /dashboard
//TODO: SPA load categories twice menu + setting (cache)
//TODO: SPA update account category -> update menu
//TODO: Response model from base type
//TODO: XXXResult -> XXXStatus
//TODO: SPA isAllowDelete -> allowDelete (remove is)
public class Program public class Program
{ {
@@ -79,6 +91,12 @@ public class Program
//File Logger //File Logger
builder.Logging.AddFile(builder.Configuration.GetSection("Logging")); builder.Logging.AddFile(builder.Configuration.GetSection("Logging"));
builder.Services.Configure<LoggerFilterOptions>(options =>
{
//options.AddFilter("IdentityServer4", LogLevel.Warning); // or LogLevel.None to completely disable
options.AddFilter("IdentityServer4", LogLevel.None); // or LogLevel.None to completely disable
});
// shared configuration // shared configuration
builder.Configuration.AddConfiguration(SharedConfiguration.CreateConfigurationContainer()); builder.Configuration.AddConfiguration(SharedConfiguration.CreateConfigurationContainer());
@@ -180,9 +198,8 @@ public class Program
o.UserInteraction = new UserInteractionOptions() o.UserInteraction = new UserInteractionOptions()
{ {
LoginUrl = "/authentication/signin", LoginUrl = "/authentication/signin",
LoginReturnUrlParameter = "returnUrl" LoginReturnUrlParameter = "returnUrl",
}; };
//o.IssuerUri = "http://localhost:9300";
}) })
.AddSigningCredential(CreateSigningCredential(builder)) .AddSigningCredential(CreateSigningCredential(builder))
.AddPersistedGrantStore<PersistedGrantStore>() .AddPersistedGrantStore<PersistedGrantStore>()
@@ -222,14 +239,15 @@ public class Program
private static void AddMapping(WebApplicationBuilder builder) private static void AddMapping(WebApplicationBuilder builder)
{ {
builder.Services.AddSingleton(provider => new MapperConfiguration(cfg => builder.Services.AddAutoMapper(
{ c =>
cfg.AddProfile(new AccountServiceProfile()); {
cfg.AddProfile(new ViewModelProfile()); c.AllowNullCollections = true;
cfg.AddProfile(new AccountViewModelProfile(provider.CreateScope().ServiceProvider.GetService<IContextProvider>()!)); c.AllowNullDestinationValues = true;
},
cfg.AddProfile(new AccountControllerProfile()); typeof(AccountViewModelProfile),
}).CreateMapper()); typeof(AccountServiceProfile)
);
} }
private static void AddRepositories(WebApplicationBuilder builder) private static void AddRepositories(WebApplicationBuilder builder)