[Add] MasterData Services.
This commit is contained in:
148
AMREZ.EOP.Infrastructures/Repositories/AllergenRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/AllergenRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Allergen;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class AllergenRepository : IAllergenRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public AllergenRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Allergen?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Allergens.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Allergen>> SearchEffectiveAsync(Guid tenantId, AllergenListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Allergens.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Allergens
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.AllergenBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Allergens.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Allergen>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Allergens.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Allergen entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Allergens.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Allergen entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Allergens.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Allergens.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.AllergenBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new AllergenBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.AllergenBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/BrandRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/BrandRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class BrandRepository : IBrandRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public BrandRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Brand?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Brands.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Brand>> SearchEffectiveAsync(Guid tenantId, BrandListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Brands.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Brands
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.BrandBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Brands.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Brand>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Brands.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Brand entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Brands.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Brand entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Brands.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Brands.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.BrandBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new BrandBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.BrandBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/CategoryRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/CategoryRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Category;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class CategoryRepository : ICategoryRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public CategoryRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Category?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Categories.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Category>> SearchEffectiveAsync(Guid tenantId, CategoryListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Categories.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Categories
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.CategoryBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Categories.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Category>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Categories.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Category entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Categories.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Category entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Categories.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Categories.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.CategoryBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new CategoryBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.CategoryBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.ComplianceStatus;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class ComplianceStatusRepository : IComplianceStatusRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public ComplianceStatusRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<ComplianceStatus?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.ComplianceStatuses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<ComplianceStatus>> SearchEffectiveAsync(Guid tenantId, ComplianceStatusListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.ComplianceStatuses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.ComplianceStatuses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.ComplianceStatusBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.ComplianceStatuses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<ComplianceStatus>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.ComplianceStatuses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(ComplianceStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.ComplianceStatuses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(ComplianceStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.ComplianceStatuses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.ComplianceStatuses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.ComplianceStatusBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new ComplianceStatusBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.ComplianceStatusBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/CountryRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/CountryRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Country;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class CountryRepository : ICountryRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public CountryRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Country?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Countries.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Country>> SearchEffectiveAsync(Guid tenantId, CountryListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Countries.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Countries
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.CountryBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Countries.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Country>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Countries.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Country entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Countries.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Country entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Countries.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Countries.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.CountryBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new CountryBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.CountryBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
173
AMREZ.EOP.Infrastructures/Repositories/DistrictRepository.cs
Normal file
173
AMREZ.EOP.Infrastructures/Repositories/DistrictRepository.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.District;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class DistrictRepository : IDistrictRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public DistrictRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<District?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
return await db.Districts.AsNoTracking()
|
||||
.Include(x => x.Province)
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<District>> SearchEffectiveAsync(
|
||||
Guid tenantId,
|
||||
DistrictListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Districts.AsNoTracking()
|
||||
.Include(d => d.Province)
|
||||
.Where(d => d.Scope == "tenant" && d.TenantId == tid && !d.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Districts
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.DistrictBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Districts.AsNoTracking()
|
||||
.Include(d => d.Province)
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive)
|
||||
q = q.Where(x => x.IsActive);
|
||||
|
||||
// Filter by Province
|
||||
if (req.ProvinceId.HasValue && req.ProvinceId.Value != Guid.Empty)
|
||||
q = q.Where(x => x.ProvinceId == req.ProvinceId.Value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.ProvinceCode))
|
||||
{
|
||||
var pd = req.ProvinceCode.Trim();
|
||||
q = q.Where(x => x.Province.Code == pd);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x =>
|
||||
x.Code.Contains(s) ||
|
||||
x.Name.Contains(s) ||
|
||||
x.Code.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
|
||||
var items = await q
|
||||
.OrderBy(x => x.Province.Code)
|
||||
.ThenBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<District>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
|
||||
return await db.Districts.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(District entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Districts.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(District entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Districts.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var d = await db.Districts.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (d is null) return 0;
|
||||
|
||||
if (d.Scope == "global")
|
||||
{
|
||||
var exists = await db.DistrictBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new DistrictBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.DistrictBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (d.TenantId != tid) return 0;
|
||||
|
||||
d.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Brand;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.DocControlStatus;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class DocControlStatusRepository : IDocControlStatusRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public DocControlStatusRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<DocControlStatus?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.DocControlStatuses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<DocControlStatus>> SearchEffectiveAsync(Guid tenantId, DocControlStatusListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.DocControlStatuses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.DocControlStatuses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.DocControlStatusBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.DocControlStatuses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<DocControlStatus>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.DocControlStatuses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(DocControlStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.DocControlStatuses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(DocControlStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.DocControlStatuses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.DocControlStatuses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.DocControlStatusBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new DocControlStatusBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.DocControlStatusBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/FuncTestRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/FuncTestRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.FuncTest;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class FuncTestRepository : IFuncTestRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public FuncTestRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<FuncTest?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.FuncTests.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<FuncTest>> SearchEffectiveAsync(Guid tenantId, FuncTestListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.FuncTests.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.FuncTests
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.FuncTestBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.FuncTests.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<FuncTest>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.FuncTests.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(FuncTest entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.FuncTests.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(FuncTest entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.FuncTests.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.FuncTests.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.FuncTestBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new FuncTestBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.FuncTestBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/HazardClassRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/HazardClassRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.HazardClass;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class HazardClassRepository : IHazardClassRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public HazardClassRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<HazardClass?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.HazardClasses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<HazardClass>> SearchEffectiveAsync(Guid tenantId, HazardClassListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.HazardClasses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.HazardClasses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.HazardClassBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.HazardClasses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<HazardClass>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.HazardClasses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(HazardClass entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.HazardClasses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(HazardClass entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.HazardClasses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.HazardClasses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.HazardClassBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new HazardClassBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.HazardClassBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/LanguageRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/LanguageRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Language;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class LanguageRepository : ILanguageRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public LanguageRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Language?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Languages.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Language>> SearchEffectiveAsync(Guid tenantId, LanguageListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Languages.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Languages
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.LanguageBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Languages.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Language>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Languages.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Language entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Languages.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Language entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Languages.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Languages.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.LanguageBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new LanguageBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.LanguageBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/ManufacturerRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/ManufacturerRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Language;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class ManufacturerRepository : IManufacturerRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public ManufacturerRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Manufacturer?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Manufacturers.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Manufacturer>> SearchEffectiveAsync(Guid tenantId, ManufacturerListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Manufacturers.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Manufacturers
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.ManufacturerBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Manufacturers.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Manufacturer>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Manufacturers.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Manufacturer entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Manufacturers.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Manufacturer entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Manufacturers.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Manufacturers.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.ManufacturerBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new ManufacturerBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.ManufacturerBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/MarketRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/MarketRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Manufacturer;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Market;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class MarketRepository : IMarketRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public MarketRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Market?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Markets.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Market>> SearchEffectiveAsync(Guid tenantId, MarketListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Markets.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Markets
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.MarketBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Markets.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Market>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Markets.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Market entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Markets.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Market entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Markets.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Markets.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.MarketBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new MarketBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.MarketBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/PackingGroupRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/PackingGroupRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Market;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class PackingGroupRepository : IPackingGroupRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public PackingGroupRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<PackingGroup?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.PackingGroups.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<PackingGroup>> SearchEffectiveAsync(Guid tenantId, PackingGroupListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.PackingGroups.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.PackingGroups
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.PackingGroupBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.PackingGroups.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<PackingGroup>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.PackingGroups.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(PackingGroup entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.PackingGroups.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(PackingGroup entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.PackingGroups.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.PackingGroups.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.PackingGroupBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new PackingGroupBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.PackingGroupBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
164
AMREZ.EOP.Infrastructures/Repositories/ProvinceRepository.cs
Normal file
164
AMREZ.EOP.Infrastructures/Repositories/ProvinceRepository.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Province;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class ProvinceRepository : IProvinceRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public ProvinceRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Province?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
return await db.Provinces.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Province>> SearchEffectiveAsync(
|
||||
Guid tenantId,
|
||||
ProvinceListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
// tenant scoped
|
||||
var tenantQ = db.Provinces.AsNoTracking()
|
||||
.Where(p => p.Scope == "tenant" && p.TenantId == tid && !p.IsDeleted);
|
||||
|
||||
// overridden globals
|
||||
var overriddenIds = db.Provinces
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
// blocked globals
|
||||
var blockedIds = db.ProvinceBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
// global scoped (effective)
|
||||
var globalsQ = db.Provinces.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive)
|
||||
q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x =>
|
||||
x.Code.Contains(s) ||
|
||||
x.Name.Contains(s) ||
|
||||
x.Code.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Province>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
|
||||
return await db.Provinces.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Province entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Provinces.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Province entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Provinces.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var p = await db.Provinces.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (p is null) return 0;
|
||||
|
||||
if (p.Scope == "global")
|
||||
{
|
||||
var exists = await db.ProvinceBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new ProvinceBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
|
||||
await db.ProvinceBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (p.TenantId != tid) return 0;
|
||||
|
||||
p.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/QaStageRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/QaStageRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.PackingGroup;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.QaStage;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class QaStageRepository : IQaStageRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public QaStageRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<QaStage?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.QaStages.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<QaStage>> SearchEffectiveAsync(Guid tenantId, QaStageListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.QaStages.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.QaStages
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.QaStageBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.QaStages.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<QaStage>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.QaStages.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(QaStage entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.QaStages.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(QaStage entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.QaStages.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.QaStages.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.QaStageBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new QaStageBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.QaStageBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/QcStatusRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/QcStatusRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class QcStatusRepository : IQcStatusRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public QcStatusRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<QcStatus?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.QcStatuses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<QcStatus>> SearchEffectiveAsync(Guid tenantId, QcStatusListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.QcStatuses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.QcStatuses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.QcStatusBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.QcStatuses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<QcStatus>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.QcStatuses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(QcStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.QcStatuses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(QcStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.QcStatuses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.QcStatuses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.QcStatusBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new QcStatusBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.QcStatusBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/RecallClassRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/RecallClassRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.QcStatus;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.RecallClass;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class RecallClassRepository : IRecallClassRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public RecallClassRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<RecallClass?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.RecallClasses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<RecallClass>> SearchEffectiveAsync(Guid tenantId, RecallClassListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.RecallClasses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.RecallClasses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.RecallClassBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.RecallClasses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<RecallClass>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.RecallClasses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(RecallClass entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.RecallClasses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(RecallClass entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.RecallClasses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.RecallClasses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.RecallClassBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new RecallClassBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.RecallClassBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/RiskClassRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/RiskClassRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.RecallClass;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.RiskClass;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class RiskClassRepository : IRiskClassRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public RiskClassRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<RiskClass?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.RiskClasses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<RiskClass>> SearchEffectiveAsync(Guid tenantId, RiskClassListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.RiskClasses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.RiskClasses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.RiskClassBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.RiskClasses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<RiskClass>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.RiskClasses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(RiskClass entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.RiskClasses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(RiskClass entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.RiskClasses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.RiskClasses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.RiskClassBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new RiskClassBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.RiskClassBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/RouteRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/RouteRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.RiskClass;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Route;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class RouteRepository : IRouteRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public RouteRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Route?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Routes.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Route>> SearchEffectiveAsync(Guid tenantId, RouteListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Routes.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Routes
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.RouteBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Routes.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Route>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Routes.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Route entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Routes.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Route entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Routes.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Routes.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.RouteBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new RouteBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.RouteBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
148
AMREZ.EOP.Infrastructures/Repositories/RxScheduleRepository.cs
Normal file
148
AMREZ.EOP.Infrastructures/Repositories/RxScheduleRepository.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Route;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.RxSchedule;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class RxScheduleRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public RxScheduleRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<RxSchedule?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.RxSchedules.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<RxSchedule>> SearchEffectiveAsync(Guid tenantId, RxScheduleListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.RxSchedules.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.RxSchedules
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.RxScheduleBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.RxSchedules.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<RxSchedule>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.RxSchedules.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(RxSchedule entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.RxSchedules.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(RxSchedule entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.RxSchedules.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.RxSchedules.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.RxScheduleBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new RxScheduleBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.RxScheduleBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/SpeciesRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/SpeciesRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.RxSchedule;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Species;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class SpeciesRepository : ISpeciesRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public SpeciesRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Species?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Species.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Species>> SearchEffectiveAsync(Guid tenantId, SpeciesListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Species.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Species
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.SpeciesBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Species.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Species>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Species.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Species entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Species.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Species entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Species.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Species.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.SpeciesBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new SpeciesBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.SpeciesBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Species;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.StabilityStatus;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class StabilityStatusRepository : IStabilityStatusRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public StabilityStatusRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<StabilityStatus?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.StabilityStatuses.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<StabilityStatus>> SearchEffectiveAsync(Guid tenantId, StabilityStatusListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.StabilityStatuses.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.StabilityStatuses
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.StabilityStatusBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.StabilityStatuses.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<StabilityStatus>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.StabilityStatuses.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(StabilityStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.StabilityStatuses.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(StabilityStatus entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.StabilityStatuses.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.StabilityStatuses.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.StabilityStatusBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new StabilityStatusBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.StabilityStatusBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.StabilityStatus;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.SterilizationMethod;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public class SterilizationMethodRepository : ISterilizationMethodRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public SterilizationMethodRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<SterilizationMethod?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.SterilizationMethods.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<SterilizationMethod>> SearchEffectiveAsync(Guid tenantId, SterilizationMethodListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.SterilizationMethods.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.SterilizationMethods
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.SterilizationMethodBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.SterilizationMethods.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<SterilizationMethod>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.SterilizationMethods.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(SterilizationMethod entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.SterilizationMethods.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(SterilizationMethod entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.SterilizationMethods.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.SterilizationMethods.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.SterilizationMethodBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new SterilizationMethodBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.SterilizationMethodBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
187
AMREZ.EOP.Infrastructures/Repositories/SubdistrictRepository.cs
Normal file
187
AMREZ.EOP.Infrastructures/Repositories/SubdistrictRepository.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Subdistrict;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class SubdistrictRepository : ISubdistrictRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public SubdistrictRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Subdistrict?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
return await db.Subdistricts.AsNoTracking()
|
||||
.Include(x => x.District)
|
||||
.ThenInclude(d => d.Province)
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Subdistrict>> SearchEffectiveAsync(
|
||||
Guid tenantId,
|
||||
SubdistrictListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Subdistricts.AsNoTracking()
|
||||
.Include(s => s.District)
|
||||
.ThenInclude(d => d.Province)
|
||||
.Where(s => s.Scope == "tenant" && s.TenantId == tid && !s.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Subdistricts
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.SubdistrictBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Subdistricts.AsNoTracking()
|
||||
.Include(s => s.District)
|
||||
.ThenInclude(d => d.Province)
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive)
|
||||
q = q.Where(x => x.IsActive);
|
||||
|
||||
// Filter by District
|
||||
if (req.DistrictId.HasValue && req.DistrictId.Value != Guid.Empty)
|
||||
q = q.Where(x => x.DistrictId == req.DistrictId.Value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.DistrictCode))
|
||||
{
|
||||
var dd = req.DistrictCode.Trim();
|
||||
q = q.Where(x => x.District.Code == dd);
|
||||
}
|
||||
|
||||
// Filter by Province
|
||||
if (req.ProvinceId.HasValue && req.ProvinceId.Value != Guid.Empty)
|
||||
q = q.Where(x => x.District.ProvinceId == req.ProvinceId.Value);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.ProvinceCode))
|
||||
{
|
||||
var pd = req.ProvinceCode.Trim();
|
||||
q = q.Where(x => x.District.Province.Code == pd);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x =>
|
||||
x.Code.Contains(s) ||
|
||||
x.Name.Contains(s) ||
|
||||
x.Code.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
|
||||
var items = await q
|
||||
.OrderBy(x => x.District.Province.Code)
|
||||
.ThenBy(x => x.District.Code)
|
||||
.ThenBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Subdistrict>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
|
||||
return await db.Subdistricts.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Subdistrict entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Subdistricts.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Subdistrict entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Subdistricts.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var s = await db.Subdistricts.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (s is null) return 0;
|
||||
|
||||
if (s.Scope == "global")
|
||||
{
|
||||
var exists = await db.SubdistrictBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new SubdistrictBlock
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.SubdistrictBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (s.TenantId != tid) return 0;
|
||||
|
||||
s.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/UomRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/UomRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.SterilizationMethod;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Uom;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public sealed class UomRepository : IUomRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public UomRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Uom?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Uoms.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Uom>> SearchEffectiveAsync(Guid tenantId, UomListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Uoms.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Uoms
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.UomBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Uoms.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Uom>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Uoms.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Uom entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Uoms.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Uom entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Uoms.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Uoms.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.UomBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new UomBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.UomBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
149
AMREZ.EOP.Infrastructures/Repositories/VvmRepository.cs
Normal file
149
AMREZ.EOP.Infrastructures/Repositories/VvmRepository.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Contracts.DTOs.Common;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Uom;
|
||||
using AMREZ.EOP.Contracts.DTOs.MasterData.Vvm;
|
||||
using AMREZ.EOP.Domain.Entities.MasterData;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMREZ.EOP.Infrastructures.Repositories;
|
||||
|
||||
public class VvmRepository : IVvmRepository
|
||||
{
|
||||
private readonly IDbScope _scope;
|
||||
private readonly ITenantResolver _tenantResolver;
|
||||
private readonly IHttpContextAccessor _http;
|
||||
|
||||
public VvmRepository(IDbScope scope, ITenantResolver tenantResolver, IHttpContextAccessor http)
|
||||
{
|
||||
_scope = scope;
|
||||
_tenantResolver = tenantResolver;
|
||||
_http = http;
|
||||
}
|
||||
|
||||
private Guid EnsureTenant(out AppDbContext db)
|
||||
{
|
||||
var http = _http.HttpContext ?? throw new InvalidOperationException("No HttpContext");
|
||||
var tc = _tenantResolver.Resolve(http) ?? throw new InvalidOperationException("No tenant");
|
||||
_scope.EnsureForTenant(tc);
|
||||
db = _scope.Get<AppDbContext>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tc.Id) && Guid.TryParse(tc.Id, out var g) && g != Guid.Empty)
|
||||
return g;
|
||||
|
||||
var key = (http.Items.TryGetValue("TargetTenantKey", out var v) ? v as string : null) ?? tc.Id;
|
||||
if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("Tenant key missing");
|
||||
|
||||
var cfg = db.Set<AMREZ.EOP.Domain.Entities.Tenancy.TenantConfig>()
|
||||
.AsNoTracking()
|
||||
.FirstOrDefault(x => x.TenantKey == key);
|
||||
if (cfg is null) throw new InvalidOperationException($"Tenant '{key}' not found");
|
||||
return cfg.TenantId;
|
||||
}
|
||||
|
||||
public async Task<Vvm?> GetAsync(Guid id, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
return await db.Vvms.AsNoTracking()
|
||||
.Where(x => x.Id == id && !x.IsDeleted)
|
||||
.Where(x => x.Scope == "tenant" ? x.TenantId == tid : true)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<PagedResponse<Vvm>> SearchEffectiveAsync(Guid tenantId, VvmListRequest req,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var tenantQ = db.Vvms.AsNoTracking()
|
||||
.Where(b => b.Scope == "tenant" && b.TenantId == tid && !b.IsDeleted);
|
||||
|
||||
var overriddenIds = db.Vvms
|
||||
.Where(t => t.Scope == "tenant" && t.TenantId == tid && !t.IsDeleted && t.OverridesGlobalId != null)
|
||||
.Select(t => t.OverridesGlobalId!.Value);
|
||||
|
||||
var blockedIds = db.VvmBlocks
|
||||
.Where(b => b.TenantId == tid)
|
||||
.Select(b => b.GlobalId);
|
||||
|
||||
var globalsQ = db.Vvms.AsNoTracking()
|
||||
.Where(g => g.Scope == "global" && !g.IsDeleted)
|
||||
.Where(g => !overriddenIds.Contains(g.Id) && !blockedIds.Contains(g.Id));
|
||||
|
||||
var q = tenantQ.Union(globalsQ);
|
||||
|
||||
if (!req.IncludeInactive) q = q.Where(x => x.IsActive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
var s = req.Search.Trim();
|
||||
q = q.Where(x => x.Code.Contains(s) || x.Name.Contains(s));
|
||||
}
|
||||
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(x => x.Code)
|
||||
.ThenBy(x => x.Name)
|
||||
.Skip((req.Page - 1) * req.PageSize)
|
||||
.Take(req.PageSize)
|
||||
.ToListAsync(ct);
|
||||
|
||||
return new PagedResponse<Vvm>(req.Page, req.PageSize, total, items);
|
||||
}
|
||||
|
||||
public async Task<bool> CodeExistsAsync(Guid tenantId, string code, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
var norm = code.Trim();
|
||||
return await db.Vvms.AnyAsync(b =>
|
||||
!b.IsDeleted &&
|
||||
b.Code == norm &&
|
||||
(b.Scope == "tenant" && b.TenantId == tid), ct);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Vvm entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
await db.Vvms.AddAsync(entity, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Vvm entity, CancellationToken ct = default)
|
||||
{
|
||||
EnsureTenant(out var db);
|
||||
db.Vvms.Update(entity);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<int> SoftDeleteAsync(Guid id, Guid tenantId, CancellationToken ct = default)
|
||||
{
|
||||
var tid = EnsureTenant(out var db);
|
||||
|
||||
var b = await db.Vvms.FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
|
||||
if (b is null) return 0;
|
||||
|
||||
if (b.Scope == "global")
|
||||
{
|
||||
var exists = await db.VvmBlocks
|
||||
.AnyAsync(x => x.TenantId == tid && x.GlobalId == id, ct);
|
||||
if (exists) return 0;
|
||||
|
||||
var block = new VvmBlock()
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
TenantId = tid,
|
||||
GlobalId = id
|
||||
};
|
||||
await db.VvmBlocks.AddAsync(block, ct);
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
if (b.TenantId != tid) return 0;
|
||||
|
||||
b.IsDeleted = true;
|
||||
return await db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user