mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 03:43:11 +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
|
namespace Data.SeaHavenIndustries
|
||||||
{
|
{
|
||||||
// SH-249 PROVISIONAL canonical trade vocabulary. Sourced from the
|
// SH-247 client-confirmed canonical trade taxonomy. Adam confirmed these
|
||||||
// in-repo prototype seed (Api.SeaHavenIndustries/Program.cs, disabled
|
// 14 values after the 2026-07-29 validation meeting; they supersede both
|
||||||
// development seeding) because no authoritative Product list exists in
|
// SH-24's service-leaf list and the provisional prototype seed originally
|
||||||
// the repo. Product must confirm the list before release; after the
|
// shipped by SH-249. Keep migrations and this catalog in sync.
|
||||||
// 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).
|
|
||||||
public static class TradeCatalog
|
public static class TradeCatalog
|
||||||
{
|
{
|
||||||
public static readonly IReadOnlyList<string> CanonicalTrades = new[]
|
public static readonly IReadOnlyList<string> CanonicalTrades = new[]
|
||||||
{
|
{
|
||||||
"Plumbing",
|
"Plumbing & Water Systems",
|
||||||
"Electrical",
|
"Electrical",
|
||||||
|
"Doors & Docks",
|
||||||
|
"Fire & Life Safety",
|
||||||
|
"Security & Locksmith",
|
||||||
"HVAC",
|
"HVAC",
|
||||||
"Fire/Life Safety",
|
"Exterior & Grounds",
|
||||||
"General",
|
"Building Envelope",
|
||||||
"Roofing",
|
"Interior Finishes",
|
||||||
|
"Signage",
|
||||||
|
"Conveyance & MHE",
|
||||||
"Janitorial",
|
"Janitorial",
|
||||||
"Painting",
|
"Specialty",
|
||||||
"Locksmith",
|
"General Building & Handyman"
|
||||||
"Carpentry"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string Normalize(string name) => name.Trim().ToLowerInvariant();
|
public static string Normalize(string name) => name.Trim().ToLowerInvariant();
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace SeaHavenIndustries.Tests;
|
namespace SeaHavenIndustries.Tests;
|
||||||
|
|
||||||
// SH-249: provider-backed migration evidence for the Trades reference table.
|
// SH-249/SH-247: provider-backed evidence for the Trades reference table and
|
||||||
// Applies the real migration operations (Up, then Down) against SQLite and
|
// the client-confirmed replacement taxonomy.
|
||||||
// asserts the seeded canonical vocabulary matches TradeCatalog.
|
|
||||||
public class SH249TradesVocabularyMigrationTests
|
public class SH249TradesVocabularyMigrationTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -24,7 +23,8 @@ public class SH249TradesVocabularyMigrationTests
|
||||||
.Options;
|
.Options;
|
||||||
|
|
||||||
await using var context = new ApplicationDbContext(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
|
var seeded = await context.Trades
|
||||||
.OrderBy(t => t.SortOrder)
|
.OrderBy(t => t.SortOrder)
|
||||||
|
|
@ -50,8 +50,10 @@ public class SH249TradesVocabularyMigrationTests
|
||||||
.Options;
|
.Options;
|
||||||
|
|
||||||
await using var context = new ApplicationDbContext(options);
|
await using var context = new ApplicationDbContext(options);
|
||||||
await ApplyMigrationAsync(context, "Up");
|
await ApplyMigrationAsync(context, new SH249_TradesVocabulary(), "Up");
|
||||||
await ApplyMigrationAsync(context, "Down");
|
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();
|
await using var command = connection.CreateCommand();
|
||||||
command.CommandText =
|
command.CommandText =
|
||||||
|
|
@ -64,12 +66,15 @@ public class SH249TradesVocabularyMigrationTests
|
||||||
// Mirrors Phase4SearchIndexesSqlServerTests: invoke the protected Up/Down
|
// Mirrors Phase4SearchIndexesSqlServerTests: invoke the protected Up/Down
|
||||||
// body, translate through the configured provider's SQL generator, and
|
// body, translate through the configured provider's SQL generator, and
|
||||||
// execute the produced commands against the live database.
|
// 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 builder = new MigrationBuilder(context.Database.ProviderName!);
|
||||||
var operation = typeof(Migration).GetMethod(direction, BindingFlags.Instance | BindingFlags.NonPublic)
|
var operation = typeof(Migration).GetMethod(direction, BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
?? throw new InvalidOperationException($"Migration.{direction} not found.");
|
?? throw new InvalidOperationException($"Migration.{direction} not found.");
|
||||||
operation.Invoke(new SH249_TradesVocabulary(), [builder]);
|
operation.Invoke(migration, [builder]);
|
||||||
|
|
||||||
var sqlGenerator = context.GetService<IMigrationsSqlGenerator>();
|
var sqlGenerator = context.GetService<IMigrationsSqlGenerator>();
|
||||||
// InsertData operations need model metadata for column type mapping.
|
// InsertData operations need model metadata for column type mapping.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue