From c5bd7fb4c8780b494009d5fbf8aec1933e8bae99 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 20 Feb 2026 08:54:01 -0500 Subject: [PATCH] feat: add Drawing entity with revision tracking Introduce a Drawing table that tracks unique drawing numbers with their current PDF content hash and revision counter. ExportRecord gains a DrawingId FK. Includes a data migration to seed Drawings from existing export records. Co-Authored-By: Claude Opus 4.6 --- FabWorks.Core/Data/FabWorksDbContext.cs | 15 + ...0260220125334_AddDrawingEntity.Designer.cs | 325 ++++++++++++++++++ .../20260220125334_AddDrawingEntity.cs | 75 ++++ ...eedDrawingsFromExistingExports.Designer.cs | 325 ++++++++++++++++++ ...0130029_SeedDrawingsFromExistingExports.cs | 51 +++ .../FabWorksDbContextModelSnapshot.cs | 52 +++ FabWorks.Core/Models/Drawing.cs | 15 + FabWorks.Core/Models/ExportRecord.cs | 3 + 8 files changed, 861 insertions(+) create mode 100644 FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.Designer.cs create mode 100644 FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.cs create mode 100644 FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.Designer.cs create mode 100644 FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.cs create mode 100644 FabWorks.Core/Models/Drawing.cs diff --git a/FabWorks.Core/Data/FabWorksDbContext.cs b/FabWorks.Core/Data/FabWorksDbContext.cs index f2d6a40..3cca49c 100644 --- a/FabWorks.Core/Data/FabWorksDbContext.cs +++ b/FabWorks.Core/Data/FabWorksDbContext.cs @@ -9,6 +9,7 @@ namespace FabWorks.Core.Data public DbSet BomItems { get; set; } public DbSet CutTemplates { get; set; } public DbSet FormPrograms { get; set; } + public DbSet Drawings { get; set; } public FabWorksDbContext(DbContextOptions options) : base(options) { } @@ -32,6 +33,11 @@ namespace FabWorks.Core.Data .WithOne(b => b.ExportRecord) .HasForeignKey(b => b.ExportRecordId) .OnDelete(DeleteBehavior.Cascade); + + entity.HasOne(e => e.Drawing) + .WithMany(d => d.ExportRecords) + .HasForeignKey(e => e.DrawingId) + .OnDelete(DeleteBehavior.SetNull); }); modelBuilder.Entity(entity => @@ -74,6 +80,15 @@ namespace FabWorks.Core.Data entity.Property(e => e.LowerToolNames).HasMaxLength(500); entity.Property(e => e.SetupNotes).HasMaxLength(2000); }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.Property(e => e.DrawingNumber).HasMaxLength(100); + entity.Property(e => e.Title).HasMaxLength(200); + entity.Property(e => e.PdfContentHash).HasMaxLength(64); + entity.HasIndex(e => e.DrawingNumber).IsUnique(); + }); } } } diff --git a/FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.Designer.cs b/FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.Designer.cs new file mode 100644 index 0000000..b0572e4 --- /dev/null +++ b/FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.Designer.cs @@ -0,0 +1,325 @@ +// +using System; +using FabWorks.Core.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FabWorks.Core.Migrations +{ + [DbContext(typeof(FabWorksDbContext))] + [Migration("20260220125334_AddDrawingEntity")] + partial class AddDrawingEntity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("ConfigurationName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ExportRecordId") + .HasColumnType("int"); + + b.Property("ItemNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Material") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PartName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PartNo") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Qty") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("TotalQty") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("ExportRecordId"); + + b.ToTable("BomItems"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BomItemId") + .HasColumnType("int"); + + b.Property("ContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CutTemplateName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("DefaultBendRadius") + .HasColumnType("float"); + + b.Property("DxfFilePath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("KFactor") + .HasColumnType("float"); + + b.Property("Revision") + .HasColumnType("int"); + + b.Property("Thickness") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("BomItemId") + .IsUnique(); + + b.ToTable("CutTemplates"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.Drawing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DrawingNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PdfContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Revision") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DrawingNumber") + .IsUnique() + .HasFilter("[DrawingNumber] IS NOT NULL"); + + b.ToTable("Drawings"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DrawingId") + .HasColumnType("int"); + + b.Property("DrawingNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DrawingNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EquipmentNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ExportedAt") + .HasColumnType("datetime2"); + + b.Property("ExportedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OutputFolder") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("PdfContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("SourceFilePath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DrawingId"); + + b.ToTable("ExportRecords"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BendCount") + .HasColumnType("int"); + + b.Property("BomItemId") + .HasColumnType("int"); + + b.Property("ContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("KFactor") + .HasColumnType("float"); + + b.Property("LowerToolNames") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("MaterialType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ProgramFilePath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ProgramName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("SetupNotes") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("Thickness") + .HasColumnType("float"); + + b.Property("UpperToolNames") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("BomItemId") + .IsUnique(); + + b.ToTable("FormPrograms"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => + { + b.HasOne("FabWorks.Core.Models.ExportRecord", "ExportRecord") + .WithMany("BomItems") + .HasForeignKey("ExportRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExportRecord"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => + { + b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") + .WithOne("CutTemplate") + .HasForeignKey("FabWorks.Core.Models.CutTemplate", "BomItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BomItem"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.HasOne("FabWorks.Core.Models.Drawing", "Drawing") + .WithMany("ExportRecords") + .HasForeignKey("DrawingId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Drawing"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => + { + b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") + .WithOne("FormProgram") + .HasForeignKey("FabWorks.Core.Models.FormProgram", "BomItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BomItem"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => + { + b.Navigation("CutTemplate"); + + b.Navigation("FormProgram"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.Drawing", b => + { + b.Navigation("ExportRecords"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.Navigation("BomItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.cs b/FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.cs new file mode 100644 index 0000000..8e7ffdd --- /dev/null +++ b/FabWorks.Core/Migrations/20260220125334_AddDrawingEntity.cs @@ -0,0 +1,75 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FabWorks.Core.Migrations +{ + /// + public partial class AddDrawingEntity : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DrawingId", + table: "ExportRecords", + type: "int", + nullable: true); + + migrationBuilder.CreateTable( + name: "Drawings", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + DrawingNumber = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Title = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), + PdfContentHash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + Revision = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Drawings", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_ExportRecords_DrawingId", + table: "ExportRecords", + column: "DrawingId"); + + migrationBuilder.CreateIndex( + name: "IX_Drawings_DrawingNumber", + table: "Drawings", + column: "DrawingNumber", + unique: true, + filter: "[DrawingNumber] IS NOT NULL"); + + migrationBuilder.AddForeignKey( + name: "FK_ExportRecords_Drawings_DrawingId", + table: "ExportRecords", + column: "DrawingId", + principalTable: "Drawings", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ExportRecords_Drawings_DrawingId", + table: "ExportRecords"); + + migrationBuilder.DropTable( + name: "Drawings"); + + migrationBuilder.DropIndex( + name: "IX_ExportRecords_DrawingId", + table: "ExportRecords"); + + migrationBuilder.DropColumn( + name: "DrawingId", + table: "ExportRecords"); + } + } +} diff --git a/FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.Designer.cs b/FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.Designer.cs new file mode 100644 index 0000000..b6888f9 --- /dev/null +++ b/FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.Designer.cs @@ -0,0 +1,325 @@ +// +using System; +using FabWorks.Core.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace FabWorks.Core.Migrations +{ + [DbContext(typeof(FabWorksDbContext))] + [Migration("20260220130029_SeedDrawingsFromExistingExports")] + partial class SeedDrawingsFromExistingExports + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); + + b.Property("ConfigurationName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ExportRecordId") + .HasColumnType("int"); + + b.Property("ItemNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Material") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PartName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PartNo") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Qty") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("TotalQty") + .HasColumnType("int"); + + b.HasKey("ID"); + + b.HasIndex("ExportRecordId"); + + b.ToTable("BomItems"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BomItemId") + .HasColumnType("int"); + + b.Property("ContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CutTemplateName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("DefaultBendRadius") + .HasColumnType("float"); + + b.Property("DxfFilePath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("KFactor") + .HasColumnType("float"); + + b.Property("Revision") + .HasColumnType("int"); + + b.Property("Thickness") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.HasIndex("BomItemId") + .IsUnique(); + + b.ToTable("CutTemplates"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.Drawing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DrawingNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PdfContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Revision") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DrawingNumber") + .IsUnique() + .HasFilter("[DrawingNumber] IS NOT NULL"); + + b.ToTable("Drawings"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DrawingId") + .HasColumnType("int"); + + b.Property("DrawingNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("DrawingNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EquipmentNo") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ExportedAt") + .HasColumnType("datetime2"); + + b.Property("ExportedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OutputFolder") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("PdfContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("SourceFilePath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DrawingId"); + + b.ToTable("ExportRecords"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BendCount") + .HasColumnType("int"); + + b.Property("BomItemId") + .HasColumnType("int"); + + b.Property("ContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("KFactor") + .HasColumnType("float"); + + b.Property("LowerToolNames") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("MaterialType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ProgramFilePath") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ProgramName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("SetupNotes") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("Thickness") + .HasColumnType("float"); + + b.Property("UpperToolNames") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("BomItemId") + .IsUnique(); + + b.ToTable("FormPrograms"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => + { + b.HasOne("FabWorks.Core.Models.ExportRecord", "ExportRecord") + .WithMany("BomItems") + .HasForeignKey("ExportRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ExportRecord"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => + { + b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") + .WithOne("CutTemplate") + .HasForeignKey("FabWorks.Core.Models.CutTemplate", "BomItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BomItem"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.HasOne("FabWorks.Core.Models.Drawing", "Drawing") + .WithMany("ExportRecords") + .HasForeignKey("DrawingId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Drawing"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => + { + b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") + .WithOne("FormProgram") + .HasForeignKey("FabWorks.Core.Models.FormProgram", "BomItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BomItem"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => + { + b.Navigation("CutTemplate"); + + b.Navigation("FormProgram"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.Drawing", b => + { + b.Navigation("ExportRecords"); + }); + + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.Navigation("BomItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.cs b/FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.cs new file mode 100644 index 0000000..adcee9b --- /dev/null +++ b/FabWorks.Core/Migrations/20260220130029_SeedDrawingsFromExistingExports.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace FabWorks.Core.Migrations +{ + /// + public partial class SeedDrawingsFromExistingExports : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // Create Drawing records from existing ExportRecords (latest hash per DrawingNumber) + migrationBuilder.Sql(@" + INSERT INTO Drawings (DrawingNumber, Title, PdfContentHash, Revision) + SELECT + sub.DrawingNumber, + sub.Title, + sub.PdfContentHash, + 1 + FROM ( + SELECT + e.DrawingNumber, + e.Title, + e.PdfContentHash, + ROW_NUMBER() OVER (PARTITION BY e.DrawingNumber ORDER BY e.Id DESC) AS rn + FROM ExportRecords e + WHERE e.DrawingNumber IS NOT NULL + AND e.PdfContentHash IS NOT NULL + ) sub + WHERE sub.rn = 1; + "); + + // Link ExportRecords to their Drawing + migrationBuilder.Sql(@" + UPDATE er + SET er.DrawingId = d.Id + FROM ExportRecords er + INNER JOIN Drawings d ON d.DrawingNumber = er.DrawingNumber + WHERE er.PdfContentHash IS NOT NULL; + "); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("UPDATE ExportRecords SET DrawingId = NULL;"); + migrationBuilder.Sql("DELETE FROM Drawings;"); + } + } +} diff --git a/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs b/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs index c6ebca9..de602de 100644 --- a/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs +++ b/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs @@ -116,6 +116,38 @@ namespace FabWorks.Core.Migrations b.ToTable("CutTemplates"); }); + modelBuilder.Entity("FabWorks.Core.Models.Drawing", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DrawingNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PdfContentHash") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Revision") + .HasColumnType("int"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DrawingNumber") + .IsUnique() + .HasFilter("[DrawingNumber] IS NOT NULL"); + + b.ToTable("Drawings"); + }); + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => { b.Property("Id") @@ -124,6 +156,9 @@ namespace FabWorks.Core.Migrations SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("DrawingId") + .HasColumnType("int"); + b.Property("DrawingNo") .HasMaxLength(50) .HasColumnType("nvarchar(50)"); @@ -161,6 +196,8 @@ namespace FabWorks.Core.Migrations b.HasKey("Id"); + b.HasIndex("DrawingId"); + b.ToTable("ExportRecords"); }); @@ -242,6 +279,16 @@ namespace FabWorks.Core.Migrations b.Navigation("BomItem"); }); + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => + { + b.HasOne("FabWorks.Core.Models.Drawing", "Drawing") + .WithMany("ExportRecords") + .HasForeignKey("DrawingId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Drawing"); + }); + modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => { b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") @@ -260,6 +307,11 @@ namespace FabWorks.Core.Migrations b.Navigation("FormProgram"); }); + modelBuilder.Entity("FabWorks.Core.Models.Drawing", b => + { + b.Navigation("ExportRecords"); + }); + modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => { b.Navigation("BomItems"); diff --git a/FabWorks.Core/Models/Drawing.cs b/FabWorks.Core/Models/Drawing.cs new file mode 100644 index 0000000..e23b0c2 --- /dev/null +++ b/FabWorks.Core/Models/Drawing.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace FabWorks.Core.Models +{ + public class Drawing + { + public int Id { get; set; } + public string DrawingNumber { get; set; } + public string Title { get; set; } + public string PdfContentHash { get; set; } + public int Revision { get; set; } = 1; + + public virtual ICollection ExportRecords { get; set; } = new List(); + } +} diff --git a/FabWorks.Core/Models/ExportRecord.cs b/FabWorks.Core/Models/ExportRecord.cs index 29b5937..0e1491a 100644 --- a/FabWorks.Core/Models/ExportRecord.cs +++ b/FabWorks.Core/Models/ExportRecord.cs @@ -16,6 +16,9 @@ namespace FabWorks.Core.Models public string ExportedBy { get; set; } public string PdfContentHash { get; set; } + public int? DrawingId { get; set; } + public virtual Drawing Drawing { get; set; } + public virtual ICollection BomItems { get; set; } = new List(); } }