mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
fix: align vendor trades with confirmed taxonomy
This commit is contained in:
parent
6eda0cd0f8
commit
0b246724d9
3 changed files with 112 additions and 22 deletions
|
|
@ -0,0 +1,84 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces SH-249's provisional prototype vocabulary with the 14-item
|
||||
/// client-confirmed taxonomy recorded in SH-247.
|
||||
/// </summary>
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260820150000_SH247_CanonicalTradeTaxonomy")]
|
||||
public partial class SH247_CanonicalTradeTaxonomy : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
UpdateTrade(migrationBuilder, 1, "Plumbing & Water Systems", 1);
|
||||
UpdateTrade(migrationBuilder, 2, "Electrical", 2);
|
||||
UpdateTrade(migrationBuilder, 3, "Doors & Docks", 3);
|
||||
UpdateTrade(migrationBuilder, 4, "Fire & Life Safety", 4);
|
||||
UpdateTrade(migrationBuilder, 5, "Security & Locksmith", 5);
|
||||
UpdateTrade(migrationBuilder, 6, "HVAC", 6);
|
||||
UpdateTrade(migrationBuilder, 7, "Exterior & Grounds", 7);
|
||||
UpdateTrade(migrationBuilder, 8, "Building Envelope", 8);
|
||||
UpdateTrade(migrationBuilder, 9, "Interior Finishes", 9);
|
||||
UpdateTrade(migrationBuilder, 10, "Signage", 10);
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "Trades",
|
||||
columns: new[] { "Id", "Name", "NormalizedName", "SortOrder", "IsActive" },
|
||||
columnTypes: new[] { "int", "nvarchar(128)", "nvarchar(128)", "int", "bit" },
|
||||
values: new object[,]
|
||||
{
|
||||
{ 11, "Conveyance & MHE", "conveyance & mhe", 11, true },
|
||||
{ 12, "Janitorial", "janitorial", 12, true },
|
||||
{ 13, "Specialty", "specialty", 13, true },
|
||||
{ 14, "General Building & Handyman", "general building & handyman", 14, true }
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
DeleteTrade(migrationBuilder, 11);
|
||||
DeleteTrade(migrationBuilder, 12);
|
||||
DeleteTrade(migrationBuilder, 13);
|
||||
DeleteTrade(migrationBuilder, 14);
|
||||
|
||||
// Free the HVAC normalized key before restoring it to row 3.
|
||||
UpdateTrade(migrationBuilder, 6, "Roofing", 6);
|
||||
UpdateTrade(migrationBuilder, 1, "Plumbing", 1);
|
||||
UpdateTrade(migrationBuilder, 2, "Electrical", 2);
|
||||
UpdateTrade(migrationBuilder, 3, "HVAC", 3);
|
||||
UpdateTrade(migrationBuilder, 4, "Fire/Life Safety", 4);
|
||||
UpdateTrade(migrationBuilder, 5, "General", 5);
|
||||
UpdateTrade(migrationBuilder, 7, "Janitorial", 7);
|
||||
UpdateTrade(migrationBuilder, 8, "Painting", 8);
|
||||
UpdateTrade(migrationBuilder, 9, "Locksmith", 9);
|
||||
UpdateTrade(migrationBuilder, 10, "Carpentry", 10);
|
||||
}
|
||||
|
||||
private static void UpdateTrade(MigrationBuilder migrationBuilder, int id, string name, int sortOrder)
|
||||
{
|
||||
migrationBuilder.UpdateData(
|
||||
table: "Trades",
|
||||
keyColumns: new[] { "Id" },
|
||||
keyColumnTypes: new[] { "int" },
|
||||
keyValues: new object[] { id },
|
||||
columns: new[] { "Name", "NormalizedName", "SortOrder", "IsActive" },
|
||||
columnTypes: new[] { "nvarchar(128)", "nvarchar(128)", "int", "bit" },
|
||||
values: new object[] { name, TradeCatalog.Normalize(name), sortOrder, true });
|
||||
}
|
||||
|
||||
private static void DeleteTrade(MigrationBuilder migrationBuilder, int id)
|
||||
{
|
||||
migrationBuilder.DeleteData(
|
||||
table: "Trades",
|
||||
keyColumns: new[] { "Id" },
|
||||
keyColumnTypes: new[] { "int" },
|
||||
keyValues: new object[] { id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,27 @@
|
|||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
// SH-249 PROVISIONAL canonical trade vocabulary. Sourced from the
|
||||
// in-repo prototype seed (Api.SeaHavenIndustries/Program.cs, disabled
|
||||
// development seeding) because no authoritative Product list exists in
|
||||
// the repo. Product must confirm the list before release; after the
|
||||
// SH249_TradesVocabulary migration ships, extend the vocabulary only
|
||||
// through new migrations (and keep this catalog in sync — tests assert
|
||||
// the seeded table matches this catalog).
|
||||
// SH-247 client-confirmed canonical trade taxonomy. Adam confirmed these
|
||||
// 14 values after the 2026-07-29 validation meeting; they supersede both
|
||||
// SH-24's service-leaf list and the provisional prototype seed originally
|
||||
// shipped by SH-249. Keep migrations and this catalog in sync.
|
||||
public static class TradeCatalog
|
||||
{
|
||||
public static readonly IReadOnlyList<string> CanonicalTrades = new[]
|
||||
{
|
||||
"Plumbing",
|
||||
"Plumbing & Water Systems",
|
||||
"Electrical",
|
||||
"Doors & Docks",
|
||||
"Fire & Life Safety",
|
||||
"Security & Locksmith",
|
||||
"HVAC",
|
||||
"Fire/Life Safety",
|
||||
"General",
|
||||
"Roofing",
|
||||
"Exterior & Grounds",
|
||||
"Building Envelope",
|
||||
"Interior Finishes",
|
||||
"Signage",
|
||||
"Conveyance & MHE",
|
||||
"Janitorial",
|
||||
"Painting",
|
||||
"Locksmith",
|
||||
"Carpentry"
|
||||
"Specialty",
|
||||
"General Building & Handyman"
|
||||
};
|
||||
|
||||
public static string Normalize(string name) => name.Trim().ToLowerInvariant();
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||
|
||||
namespace SeaHavenIndustries.Tests;
|
||||
|
||||
// SH-249: provider-backed migration evidence for the Trades reference table.
|
||||
// Applies the real migration operations (Up, then Down) against SQLite and
|
||||
// asserts the seeded canonical vocabulary matches TradeCatalog.
|
||||
// SH-249/SH-247: provider-backed evidence for the Trades reference table and
|
||||
// the client-confirmed replacement taxonomy.
|
||||
public class SH249TradesVocabularyMigrationTests
|
||||
{
|
||||
[Fact]
|
||||
|
|
@ -24,7 +23,8 @@ public class SH249TradesVocabularyMigrationTests
|
|||
.Options;
|
||||
|
||||
await using var context = new ApplicationDbContext(options);
|
||||
await ApplyMigrationAsync(context, "Up");
|
||||
await ApplyMigrationAsync(context, new SH249_TradesVocabulary(), "Up");
|
||||
await ApplyMigrationAsync(context, new SH247_CanonicalTradeTaxonomy(), "Up");
|
||||
|
||||
var seeded = await context.Trades
|
||||
.OrderBy(t => t.SortOrder)
|
||||
|
|
@ -50,8 +50,10 @@ public class SH249TradesVocabularyMigrationTests
|
|||
.Options;
|
||||
|
||||
await using var context = new ApplicationDbContext(options);
|
||||
await ApplyMigrationAsync(context, "Up");
|
||||
await ApplyMigrationAsync(context, "Down");
|
||||
await ApplyMigrationAsync(context, new SH249_TradesVocabulary(), "Up");
|
||||
await ApplyMigrationAsync(context, new SH247_CanonicalTradeTaxonomy(), "Up");
|
||||
await ApplyMigrationAsync(context, new SH247_CanonicalTradeTaxonomy(), "Down");
|
||||
await ApplyMigrationAsync(context, new SH249_TradesVocabulary(), "Down");
|
||||
|
||||
await using var command = connection.CreateCommand();
|
||||
command.CommandText =
|
||||
|
|
@ -64,12 +66,15 @@ public class SH249TradesVocabularyMigrationTests
|
|||
// Mirrors Phase4SearchIndexesSqlServerTests: invoke the protected Up/Down
|
||||
// body, translate through the configured provider's SQL generator, and
|
||||
// execute the produced commands against the live database.
|
||||
private static async Task ApplyMigrationAsync(ApplicationDbContext context, string direction)
|
||||
private static async Task ApplyMigrationAsync(
|
||||
ApplicationDbContext context,
|
||||
Migration migration,
|
||||
string direction)
|
||||
{
|
||||
var builder = new MigrationBuilder(context.Database.ProviderName!);
|
||||
var operation = typeof(Migration).GetMethod(direction, BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException($"Migration.{direction} not found.");
|
||||
operation.Invoke(new SH249_TradesVocabulary(), [builder]);
|
||||
operation.Invoke(migration, [builder]);
|
||||
|
||||
var sqlGenerator = context.GetService<IMigrationsSqlGenerator>();
|
||||
// InsertData operations need model metadata for column type mapping.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue