ui and bug fixers 29/09

This commit is contained in:
2026-09-29 14:49:11 +05:30
parent d24072e20a
commit b2646c6cb3
85 changed files with 30050 additions and 750 deletions
@@ -107,11 +107,14 @@ public sealed class AuthHexClient : IAuthHexClient
// ---- RecoveryManager ------------------------------------------------
public Task<ForgotPasswordResponse> ForgotPasswordAsync(ForgotPasswordRequest request, CancellationToken ct)
=> CallAsync<ForgotPasswordResponse>("recovery", "forgotPassword", request, null, ct);
// RecoveryManagerService returns its own { success, message, data } object, which the
// function handler then wraps again as the envelope's `data` — so the payload sits one
// level deeper than for other AuthHex calls (ERP_Auth_Service RecoveryManager.cs).
public async Task<ForgotPasswordResponse> ForgotPasswordAsync(ForgotPasswordRequest request, CancellationToken ct)
=> Unwrap(await CallAsync<AuthHexEnvelope<ForgotPasswordResponse>>("recovery", "forgotPassword", request, null, ct));
public Task<VerifyRecoveryOtpResponse> VerifyRecoveryOtpAsync(VerifyRecoveryOtpRequest request, CancellationToken ct)
=> CallAsync<VerifyRecoveryOtpResponse>("recovery", "verifyOTP", request, null, ct);
public async Task<VerifyRecoveryOtpResponse> VerifyRecoveryOtpAsync(VerifyRecoveryOtpRequest request, CancellationToken ct)
=> Unwrap(await CallAsync<AuthHexEnvelope<VerifyRecoveryOtpResponse>>("recovery", "verifyOTP", request, null, ct));
public Task ResetPasswordAsync(ResetPasswordRequest request, CancellationToken ct)
=> CallVoidAsync("recovery", "resetPassword", request, null, ct);
@@ -149,6 +152,14 @@ public sealed class AuthHexClient : IAuthHexClient
// ---- Transport --------------------------------------------------------
/// <summary>Unwraps the inner result object that the recovery functions nest inside the envelope's data.</summary>
private static T Unwrap<T>(AuthHexEnvelope<T>? inner)
{
if (inner is null || !inner.Success || inner.Data is null)
throw new DomainException(ErrorCodes.AuthUpstreamError, inner?.Message ?? "Authentication request failed.", 400);
return inner.Data;
}
private async Task CallVoidAsync(string routeGroup, string functionName, object payload, string? bearerToken, CancellationToken ct)
=> await CallAsync<JsonElement?>(routeGroup, functionName, payload, bearerToken, ct);
@@ -21,6 +21,8 @@ public sealed class BranchConfigConfiguration : IEntityTypeConfiguration<BranchC
builder.Property(c => c.ConfigId).ValueGeneratedNever();
builder.Property(c => c.BranchingEnabled).IsRequired().HasDefaultValue(true);
// Defaults on so the existing row keeps today's per-branch purchase orders.
builder.Property(c => c.PurchaseOrderBranchingEnabled).IsRequired().HasDefaultValue(true);
// PostgreSQL xmin system column as the optimistic concurrency token (ETag).
builder.Property(c => c.RowVersion).IsRowVersion();
@@ -18,6 +18,8 @@ public sealed class VendorConfiguration : IEntityTypeConfiguration<Vendor>
builder.Property(v => v.Name).IsRequired().HasMaxLength(200);
builder.Property(v => v.Terms).HasMaxLength(50);
builder.Property(v => v.TaxReg).HasMaxLength(50);
builder.Property(v => v.Address).HasMaxLength(500);
builder.Property(v => v.Phone).HasMaxLength(30);
builder.Property(v => v.Currency).IsRequired().HasMaxLength(3).HasDefaultValue("LKR");
builder.Property(v => v.Status)
@@ -195,7 +195,12 @@ public class ErpDbContext : DbContext
modelBuilder.Entity<Customer>().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId);
modelBuilder.Entity<Requisition>().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId);
modelBuilder.Entity<Rfq>().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId);
modelBuilder.Entity<PurchaseOrder>().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId);
// Purchase orders are only scoped while the "divide purchase orders by branch" toggle is
// on — off turns them into one shared pool every branch can see.
modelBuilder.Entity<PurchaseOrder>().HasQueryFilter(e => !_currentBranch.IsActive
|| e.BranchId == _currentBranch.BranchId
|| !Set<Domain.Entities.BranchConfig>().Any(c => c.ConfigId == Domain.Entities.BranchConfig.SingletonId
&& c.BranchingEnabled && c.PurchaseOrderBranchingEnabled));
modelBuilder.Entity<PurchaseInvoice>().HasQueryFilter(e => !_currentBranch.IsActive || e.BranchId == _currentBranch.BranchId);
// Npgsql requires DateTime values written to `timestamp with time zone` columns