[CLAUDE] Infra: gotcha #57 EXT Master filtered-unique Department/Supplier/Project (Mig 47)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m15s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m15s
Extend gotcha #57 filtered-unique fix to 3 Master catalogs (4th/5th/6th cumulative after Holiday Mig 43 S45 + HRM x3 Mig 45 S51). Root cause: app-level dup-check db.X.AnyAsync(Code==req) runs through HasQueryFilter(!IsDeleted) so it ignores soft-deleted rows, but the bare .IsUnique() DB index counted them -> admin delete+re-add same Code = reachable 500. Fix aligns index with query filter via .HasFilter("[IsDeleted] = 0"). - Department/Project/Supplier Configuration: unique Code index + .HasFilter (Supplier Type index untouched) - Mig 47 FilterMasterCatalogUniqueIndexesByIsDeleted (Up: 3x DropIndex+CreateIndex filtered; Down reversible) - test-before MasterCatalogFilteredUniqueTests (3 RED->GREEN, delete+re-add same Code) - Tests 200 -> 203 (58 Domain + 145 Infra) Pipeline: test-specialist -> implementer-backend -> reviewer (PASS, 0 issues). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -15,7 +15,7 @@ public class DepartmentConfiguration : IEntityTypeConfiguration<Department>
|
||||
b.Property(x => x.Name).HasMaxLength(200).IsRequired();
|
||||
b.Property(x => x.Note).HasMaxLength(1000);
|
||||
|
||||
b.HasIndex(x => x.Code).IsUnique();
|
||||
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0"); // Mig 47 (gotcha #57 EXT) — soft-deleted slot reusable, khớp HasQueryFilter !IsDeleted app-check
|
||||
|
||||
b.HasQueryFilter(x => !x.IsDeleted);
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ public class ProjectConfiguration : IEntityTypeConfiguration<Project>
|
||||
b.Property(x => x.BudgetTotal).HasPrecision(18, 2);
|
||||
b.Property(x => x.Note).HasMaxLength(1000);
|
||||
|
||||
b.HasIndex(x => x.Code).IsUnique();
|
||||
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0"); // Mig 47 (gotcha #57 EXT) — soft-deleted slot reusable, khớp HasQueryFilter !IsDeleted app-check
|
||||
|
||||
b.HasQueryFilter(x => !x.IsDeleted);
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ public class SupplierConfiguration : IEntityTypeConfiguration<Supplier>
|
||||
b.Property(x => x.ContactPerson).HasMaxLength(200);
|
||||
b.Property(x => x.Note).HasMaxLength(1000);
|
||||
|
||||
b.HasIndex(x => x.Code).IsUnique();
|
||||
b.HasIndex(x => x.Code).IsUnique().HasFilter("[IsDeleted] = 0"); // Mig 47 (gotcha #57 EXT) — soft-deleted slot reusable, khớp HasQueryFilter !IsDeleted app-check
|
||||
b.HasIndex(x => x.Type);
|
||||
|
||||
b.HasQueryFilter(x => !x.IsDeleted);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,81 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FilterMasterCatalogUniqueIndexesByIsDeleted : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Suppliers_Code",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Projects_Code",
|
||||
table: "Projects");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Departments_Code",
|
||||
table: "Departments");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Suppliers_Code",
|
||||
table: "Suppliers",
|
||||
column: "Code",
|
||||
unique: true,
|
||||
filter: "[IsDeleted] = 0");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Projects_Code",
|
||||
table: "Projects",
|
||||
column: "Code",
|
||||
unique: true,
|
||||
filter: "[IsDeleted] = 0");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Departments_Code",
|
||||
table: "Departments",
|
||||
column: "Code",
|
||||
unique: true,
|
||||
filter: "[IsDeleted] = 0");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Suppliers_Code",
|
||||
table: "Suppliers");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Projects_Code",
|
||||
table: "Projects");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Departments_Code",
|
||||
table: "Departments");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Suppliers_Code",
|
||||
table: "Suppliers",
|
||||
column: "Code",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Projects_Code",
|
||||
table: "Projects",
|
||||
column: "Code",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Departments_Code",
|
||||
table: "Departments",
|
||||
column: "Code",
|
||||
unique: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3448,7 +3448,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
.IsUnique()
|
||||
.HasFilter("[IsDeleted] = 0");
|
||||
|
||||
b.ToTable("Departments", (string)null);
|
||||
});
|
||||
@ -3510,7 +3511,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
.IsUnique()
|
||||
.HasFilter("[IsDeleted] = 0");
|
||||
|
||||
b.ToTable("Projects", (string)null);
|
||||
});
|
||||
@ -3582,7 +3584,8 @@ namespace SolutionErp.Infrastructure.Persistence.Migrations
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
.IsUnique()
|
||||
.HasFilter("[IsDeleted] = 0");
|
||||
|
||||
b.HasIndex("Type");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user