diff --git a/backend/API/Controllers/PrintJobsController.cs b/backend/API/Controllers/PrintJobsController.cs index 226430e..5cb802e 100644 --- a/backend/API/Controllers/PrintJobsController.cs +++ b/backend/API/Controllers/PrintJobsController.cs @@ -413,6 +413,92 @@ public class PrintJobsController : ControllerBase return NoContent(); } + // ── GET /api/printjobs/{id}/cost-summary ────────────────────────── + + /// + /// Gets the material cost summary for a specific print job. + /// Calculates total material cost from filament usage (grams derived) + /// and the spool's purchase price. Returns warnings instead of errors + /// when cost data is unavailable. + /// + /// The unique identifier of the print job. + /// A cost summary with breakdown and any warnings about missing data. + /// Returns the cost summary. Warnings field lists any missing data. + /// If the print job with the given ID is not found. + [HttpGet("{id:guid}/cost-summary")] + [ProducesResponseType(typeof(CostSummaryResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> GetCostSummary(Guid id) + { + _logger.LogDebug("Getting cost summary for print job {Id}", id); + + var job = await _dbContext.PrintJobs + .Include(j => j.Spool) + .ThenInclude(s => s!.MaterialBase) + .FirstOrDefaultAsync(j => j.Id == id); + + if (job is null) + { + _logger.LogWarning("Print job {Id} not found for cost summary", id); + return NotFound(new { error = $"Print job with ID '{id}' not found." }); + } + + var warnings = new List(); + var spool = job.Spool; + + // Build response with what we have + var response = new CostSummaryResponse + { + PrintJobId = job.Id, + PrintName = job.PrintName, + SpoolId = job.SpoolId, + SpoolSerial = spool?.SpoolSerial ?? string.Empty, + SpoolBrand = spool?.Brand ?? string.Empty, + SpoolColorName = spool?.ColorName ?? string.Empty, + MmExtruded = job.MmExtruded, + GramsDerived = job.GramsDerived, + SpoolPurchasePrice = spool?.PurchasePrice, + SpoolWeightTotalGrams = spool?.WeightTotalGrams, + StoredCostPerPrint = job.CostPerPrint + }; + + // Validate spool data availability + if (spool is null) + { + warnings.Add("Spool data is not available for this print job. Cost cannot be calculated."); + response.Warnings = warnings; + return Ok(response); + } + + // Check if we can calculate cost + if (!spool.PurchasePrice.HasValue) + { + warnings.Add("Spool purchase price is not set. Cost per gram and total material cost cannot be calculated."); + } + + if (spool.WeightTotalGrams <= 0) + { + warnings.Add("Spool total weight is zero or invalid. Cost per gram and total material cost cannot be calculated."); + } + + // If we have enough data, calculate the cost + if (spool.PurchasePrice.HasValue && spool.WeightTotalGrams > 0) + { + var pricePerGram = spool.PurchasePrice.Value / spool.WeightTotalGrams; + response.PricePerGram = Math.Round(pricePerGram, 4); + response.TotalMaterialCost = Math.Round(job.GramsDerived * pricePerGram, 4); + } + + // Warn if grams derived is zero but mm extruded is non-zero + if (job.GramsDerived == 0 && job.MmExtruded > 0) + { + warnings.Add("GramsDerived is zero despite MmExtruded being non-zero. Cost may be inaccurate. Consider re-deriving grams from filament parameters."); + } + + response.Warnings = warnings; + return Ok(response); + } + // ── Gram Derivation Formula ──────────────────────────────────── /// diff --git a/backend/API/DTOs/PrintJobs/CostSummaryResponse.cs b/backend/API/DTOs/PrintJobs/CostSummaryResponse.cs new file mode 100644 index 0000000..0c2f9ed --- /dev/null +++ b/backend/API/DTOs/PrintJobs/CostSummaryResponse.cs @@ -0,0 +1,55 @@ +namespace Extrudex.API.DTOs.PrintJobs; + +/// +/// Response DTO for the cost summary of a print job. +/// Provides a breakdown of material cost based on filament usage +/// and spool pricing data. If cost data is incomplete, warnings +/// are returned instead of throwing an error. +/// +public class CostSummaryResponse +{ + /// Unique identifier of the print job. + public Guid PrintJobId { get; set; } + + /// Human-readable name of the print job. + public string PrintName { get; set; } = string.Empty; + + /// Foreign key to the spool used for this print job. + public Guid SpoolId { get; set; } + + /// Serial number of the spool. + public string SpoolSerial { get; set; } = string.Empty; + + /// Brand of the spool. + public string SpoolBrand { get; set; } = string.Empty; + + /// Color name of the spool. + public string SpoolColorName { get; set; } = string.Empty; + + /// Total millimeters of filament extruded during this print. + public decimal MmExtruded { get; set; } + + /// Derived grams consumed for this print job. + public decimal GramsDerived { get; set; } + + /// Purchase price of the full spool, if available. + public decimal? SpoolPurchasePrice { get; set; } + + /// Total weight of the spool in grams when full. + public decimal? SpoolWeightTotalGrams { get; set; } + + /// Calculated price per gram (purchase price / total weight), if available. + public decimal? PricePerGram { get; set; } + + /// Calculated total material cost for this print job, if available. + public decimal? TotalMaterialCost { get; set; } + + /// The CostPerPrint stored on the print job entity, if set. + public decimal? StoredCostPerPrint { get; set; } + + /// + /// Warnings about missing data that prevent cost calculation. + /// Empty if all data is available and cost was calculated successfully. + /// + public List Warnings { get; set; } = new(); +} \ No newline at end of file diff --git a/backend/Domain/Entities/FilamentUsage.cs b/backend/Domain/Entities/FilamentUsage.cs new file mode 100644 index 0000000..3237bc9 --- /dev/null +++ b/backend/Domain/Entities/FilamentUsage.cs @@ -0,0 +1,73 @@ +using Extrudex.Domain.Base; + +namespace Extrudex.Domain.Entities; + +/// +/// Tracks filament consumption for a specific print job on a specific spool. +/// Each record captures the grams used, which printer consumed it, and when the +/// usage was recorded. This enables granular per-job usage analytics, COGS +/// reconciliation, and spool weight depletion tracking. +/// +/// A single PrintJob may have multiple FilamentUsage records if multiple spools +/// were consumed (e.g., multi-material prints via AMS). +/// +public class FilamentUsage : AuditableEntity +{ + /// + /// Foreign key to the print job that consumed this filament. + /// A usage record is always tied to a print job. + /// + public Guid PrintJobId { get; set; } + + /// + /// Navigation to the print job that consumed this filament. + /// + public PrintJob PrintJob { get; set; } = null!; + + /// + /// Foreign key to the spool (filament) that provided the material. + /// Links usage back to the specific physical spool for inventory tracking. + /// + public Guid SpoolId { get; set; } + + /// + /// Navigation to the spool that provided the material. + /// + public Spool Spool { get; set; } = null!; + + /// + /// Foreign key to the printer that executed the print job. + /// Denormalized from PrintJob for direct querying of per-printer usage. + /// + public Guid PrinterId { get; set; } + + /// + /// Navigation to the printer that executed the print job. + /// + public Printer Printer { get; set; } = null!; + + /// + /// Grams of filament consumed during this print job. + /// Derived from mm_extruded × cross_section_area × material_density, + /// or measured directly from AMS weight delta. + /// + public decimal GramsUsed { get; set; } + + /// + /// Millimeters of filament extruded for this usage record. + /// The primary physical measurement; grams_used is derived from this. + /// + public decimal MmExtruded { get; set; } + + /// + /// Timestamp when this usage record was created (UTC). + /// Represents when the usage was first logged, which may differ from + /// the print job's started_at or completed_at timestamps. + /// + public DateTime RecordedAt { get; set; } = DateTime.UtcNow; + + /// + /// Optional notes about this usage record (e.g., "AMS tray 3", "manual weight check"). + /// + public string? Notes { get; set; } +} \ No newline at end of file diff --git a/backend/Domain/Entities/PrintJob.cs b/backend/Domain/Entities/PrintJob.cs index 6483f72..caeb2d1 100644 --- a/backend/Domain/Entities/PrintJob.cs +++ b/backend/Domain/Entities/PrintJob.cs @@ -97,4 +97,10 @@ public class PrintJob : AuditableEntity /// Optional notes about the print job (e.g., "First layer adhesion issues"). /// public string? Notes { get; set; } + + /// + /// Navigation collection of filament usage records for this print job. + /// Enables tracking granular per-spool consumption within a print. + /// + public ICollection FilamentUsages { get; set; } = new List(); } \ No newline at end of file diff --git a/backend/Domain/Entities/Printer.cs b/backend/Domain/Entities/Printer.cs index b28419e..4e48b19 100644 --- a/backend/Domain/Entities/Printer.cs +++ b/backend/Domain/Entities/Printer.cs @@ -94,4 +94,10 @@ public class Printer : AuditableEntity /// Navigation collection of print jobs executed on this printer. /// public ICollection PrintJobs { get; set; } = new List(); + + /// + /// Navigation collection of filament usage records tracking consumption on this printer. + /// Enables querying per-printer filament usage and COGS. + /// + public ICollection FilamentUsages { get; set; } = new List(); } \ No newline at end of file diff --git a/backend/Domain/Entities/Spool.cs b/backend/Domain/Entities/Spool.cs index 0356002..5084a7b 100644 --- a/backend/Domain/Entities/Spool.cs +++ b/backend/Domain/Entities/Spool.cs @@ -102,4 +102,10 @@ public class Spool : AuditableEntity /// Navigation collection of print jobs that consumed filament from this spool. /// public ICollection PrintJobs { get; set; } = new List(); + + /// + /// Navigation collection of filament usage records tracking consumption from this spool. + /// Enables querying how much filament was consumed per print job. + /// + public ICollection FilamentUsages { get; set; } = new List(); } \ No newline at end of file diff --git a/backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs b/backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs new file mode 100644 index 0000000..8672735 --- /dev/null +++ b/backend/Infrastructure/Data/Configurations/FilamentUsageConfiguration.cs @@ -0,0 +1,83 @@ +using Extrudex.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Extrudex.Infrastructure.Data.Configurations; + +public class FilamentUsageConfiguration : BaseEntityConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(e => e.PrintJobId) + .HasColumnName("print_job_id") + .IsRequired(); + + builder.Property(e => e.SpoolId) + .HasColumnName("spool_id") + .IsRequired(); + + builder.Property(e => e.PrinterId) + .HasColumnName("printer_id") + .IsRequired(); + + builder.Property(e => e.GramsUsed) + .HasColumnName("grams_used") + .HasPrecision(10, 2) + .IsRequired(); + + builder.Property(e => e.MmExtruded) + .HasColumnName("mm_extruded") + .HasPrecision(12, 2) + .IsRequired(); + + builder.Property(e => e.RecordedAt) + .HasColumnName("recorded_at") + .HasDefaultValueSql("now() at time zone 'utc'") + .IsRequired(); + + builder.Property(e => e.Notes) + .HasColumnName("notes") + .HasMaxLength(2000); + + // Index on print_job_id for querying usage by print job + builder.HasIndex(e => e.PrintJobId) + .HasDatabaseName("ix_filament_usages_print_job_id"); + + // Index on spool_id for querying usage by spool (filament) + builder.HasIndex(e => e.SpoolId) + .HasDatabaseName("ix_filament_usages_spool_id"); + + // Index on printer_id for querying usage by printer + builder.HasIndex(e => e.PrinterId) + .HasDatabaseName("ix_filament_usages_printer_id"); + + // Index on recorded_at for time-range queries + builder.HasIndex(e => e.RecordedAt) + .HasDatabaseName("ix_filament_usages_recorded_at"); + + // Composite index for querying usage by spool within a date range + builder.HasIndex(e => new { e.SpoolId, e.RecordedAt }) + .HasDatabaseName("ix_filament_usages_spool_id_recorded_at"); + + // Relationships + builder.HasOne(e => e.PrintJob) + .WithMany(e => e.FilamentUsages) + .HasForeignKey(e => e.PrintJobId) + .HasConstraintName("fk_filament_usages_print_job") + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(e => e.Spool) + .WithMany(e => e.FilamentUsages) + .HasForeignKey(e => e.SpoolId) + .HasConstraintName("fk_filament_usages_spool") + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(e => e.Printer) + .WithMany(e => e.FilamentUsages) + .HasForeignKey(e => e.PrinterId) + .HasConstraintName("fk_filament_usages_printer") + .OnDelete(DeleteBehavior.Restrict); + } +} \ No newline at end of file diff --git a/backend/Infrastructure/Data/ExtrudexDbContext.cs b/backend/Infrastructure/Data/ExtrudexDbContext.cs index f6ec61a..270718c 100644 --- a/backend/Infrastructure/Data/ExtrudexDbContext.cs +++ b/backend/Infrastructure/Data/ExtrudexDbContext.cs @@ -23,6 +23,7 @@ public class ExtrudexDbContext : DbContext public DbSet AmsUnits => Set(); public DbSet AmsSlots => Set(); public DbSet PrintJobs => Set(); + public DbSet FilamentUsages => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs b/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs new file mode 100644 index 0000000..393abb9 --- /dev/null +++ b/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.Designer.cs @@ -0,0 +1,1068 @@ +// +using System; +using Extrudex.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Extrudex.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ExtrudexDbContext))] + [Migration("20260426183433_AddFilamentUsageTrackingModel")] + partial class AddFilamentUsageTrackingModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AmsUnitId") + .HasColumnType("uuid") + .HasColumnName("ams_unit_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("RemainingWeightG") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("remaining_weight_g"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("TrayIndex") + .HasColumnType("integer") + .HasColumnName("tray_index"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_ams_slots_spool_id"); + + b.HasIndex("AmsUnitId", "TrayIndex") + .IsUnique() + .HasDatabaseName("ix_ams_slots_ams_unit_id_tray_index"); + + b.ToTable("ams_slots", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("UnitIndex") + .HasColumnType("integer") + .HasColumnName("unit_index"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("PrinterId", "UnitIndex") + .IsUnique() + .HasDatabaseName("ix_ams_units_printer_id_unit_index"); + + b.ToTable("ams_units", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("GramsUsed") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("grams_used"); + + b.Property("MmExtruded") + .HasPrecision(12, 2) + .HasColumnType("numeric(12,2)") + .HasColumnName("mm_extruded"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("notes"); + + b.Property("PrintJobId") + .HasColumnType("uuid") + .HasColumnName("print_job_id"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("RecordedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("recorded_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("PrintJobId") + .HasDatabaseName("ix_filament_usages_print_job_id"); + + b.HasIndex("PrinterId") + .HasDatabaseName("ix_filament_usages_printer_id"); + + b.HasIndex("RecordedAt") + .HasDatabaseName("ix_filament_usages_recorded_at"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_filament_usages_spool_id"); + + b.HasIndex("SpoolId", "RecordedAt") + .HasDatabaseName("ix_filament_usages_spool_id_recorded_at"); + + b.ToTable("filament_usages", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("DensityGperCm3") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("density_g_per_cm3"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_material_bases_name"); + + b.ToTable("material_bases", (string)null); + + b.HasData( + new + { + Id = new Guid("10000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388), + DensityGperCm3 = 1.24m, + Name = "PLA", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871), + DensityGperCm3 = 1.27m, + Name = "PETG", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881), + DensityGperCm3 = 1.04m, + Name = "ABS", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888), + DensityGperCm3 = 1.07m, + Name = "ASA", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895), + DensityGperCm3 = 1.21m, + Name = "TPU", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9901), + DensityGperCm3 = 1.14m, + Name = "Nylon", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9902) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("MaterialBaseId", "Name") + .IsUnique() + .HasDatabaseName("ix_material_finishes_material_base_id_name"); + + b.ToTable("material_finishes", (string)null); + + b.HasData( + new + { + Id = new Guid("20000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Silk", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glitter", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Marble", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Sparkle", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(297), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Silk", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(298) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000012"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(316), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(317) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000005"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("MaterialBaseId", "Name") + .IsUnique() + .HasDatabaseName("ix_material_modifiers_material_base_id_name"); + + b.ToTable("material_modifiers", (string)null); + + b.HasData( + new + { + Id = new Guid("30000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(805), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(806) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Wood Fill", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glow-in-the-Dark", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(859), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(860) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("completed_at"); + + b.Property("CostPerPrint") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("cost_per_print"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("DataSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("data_source"); + + b.Property("FilamentDiameterAtPrintMm") + .HasPrecision(6, 3) + .HasColumnType("numeric(6,3)") + .HasColumnName("filament_diameter_at_print_mm"); + + b.Property("GcodeFilePath") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("gcode_file_path"); + + b.Property("GramsDerived") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("grams_derived"); + + b.Property("MaterialDensityAtPrint") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("material_density_at_print"); + + b.Property("MmExtruded") + .HasPrecision(12, 2) + .HasColumnType("numeric(12,2)") + .HasColumnName("mm_extruded"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("notes"); + + b.Property("PrintName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("print_name"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_at"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("Queued") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DataSource") + .HasDatabaseName("ix_print_jobs_data_source"); + + b.HasIndex("PrinterId") + .HasDatabaseName("ix_print_jobs_printer_id"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_print_jobs_spool_id"); + + b.HasIndex("Status") + .HasDatabaseName("ix_print_jobs_status"); + + b.ToTable("print_jobs", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("api_key"); + + b.Property("ConnectionType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("connection_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("HostnameOrIp") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("hostname_or_ip"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("LastSeenAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_at"); + + b.Property("Manufacturer") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("manufacturer"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("model"); + + b.Property("MqttPassword") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("mqtt_password"); + + b.Property("MqttUseTls") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("mqtt_use_tls"); + + b.Property("MqttUsername") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mqtt_username"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("Port") + .HasColumnType("integer") + .HasColumnName("port"); + + b.Property("PrinterType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("printer_type"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("Offline") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("ConnectionType") + .HasDatabaseName("ix_printers_connection_type"); + + b.HasIndex("IsActive") + .HasDatabaseName("ix_printers_is_active"); + + b.HasIndex("PrinterType") + .HasDatabaseName("ix_printers_printer_type"); + + b.HasIndex("Status") + .HasDatabaseName("ix_printers_status"); + + b.ToTable("printers", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Brand") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("brand"); + + b.Property("ColorHex") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character varying(7)") + .HasColumnName("color_hex"); + + b.Property("ColorName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("color_name"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("FilamentDiameterMm") + .HasPrecision(6, 3) + .HasColumnType("numeric(6,3)") + .HasColumnName("filament_diameter_mm"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("MaterialFinishId") + .HasColumnType("uuid") + .HasColumnName("material_finish_id"); + + b.Property("MaterialModifierId") + .HasColumnType("uuid") + .HasColumnName("material_modifier_id"); + + b.Property("PurchaseDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("purchase_date"); + + b.Property("PurchasePrice") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("purchase_price"); + + b.Property("SpoolSerial") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("spool_serial"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("WeightRemainingGrams") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("weight_remaining_grams"); + + b.Property("WeightTotalGrams") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("weight_total_grams"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("ix_spools_is_active"); + + b.HasIndex("MaterialBaseId") + .HasDatabaseName("ix_spools_material_base_id"); + + b.HasIndex("MaterialFinishId") + .HasDatabaseName("ix_spools_material_finish_id"); + + b.HasIndex("MaterialModifierId") + .HasDatabaseName("ix_spools_material_modifier_id"); + + b.HasIndex("SpoolSerial") + .IsUnique() + .HasDatabaseName("ix_spools_spool_serial"); + + b.ToTable("spools", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => + { + b.HasOne("Extrudex.Domain.Entities.AmsUnit", "AmsUnit") + .WithMany("Slots") + .HasForeignKey("AmsUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ams_slots_ams_unit"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("AmsSlots") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_ams_slots_spool"); + + b.Navigation("AmsUnit"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("AmsUnits") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ams_units_printer"); + + b.Navigation("Printer"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => + { + b.HasOne("Extrudex.Domain.Entities.PrintJob", "PrintJob") + .WithMany("FilamentUsages") + .HasForeignKey("PrintJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_filament_usages_print_job"); + + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("FilamentUsages") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_filament_usages_printer"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("FilamentUsages") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_filament_usages_spool"); + + b.Navigation("PrintJob"); + + b.Navigation("Printer"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Finishes") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_material_finishes_material_base"); + + b.Navigation("MaterialBase"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Modifiers") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_material_modifiers_material_base"); + + b.Navigation("MaterialBase"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("PrintJobs") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_print_jobs_printer"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("PrintJobs") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_print_jobs_spool"); + + b.Navigation("Printer"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Spools") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_spools_material_base"); + + b.HasOne("Extrudex.Domain.Entities.MaterialFinish", "MaterialFinish") + .WithMany("Spools") + .HasForeignKey("MaterialFinishId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_spools_material_finish"); + + b.HasOne("Extrudex.Domain.Entities.MaterialModifier", "MaterialModifier") + .WithMany("Spools") + .HasForeignKey("MaterialModifierId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_spools_material_modifier"); + + b.Navigation("MaterialBase"); + + b.Navigation("MaterialFinish"); + + b.Navigation("MaterialModifier"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.Navigation("Slots"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => + { + b.Navigation("Finishes"); + + b.Navigation("Modifiers"); + + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.Navigation("FilamentUsages"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => + { + b.Navigation("AmsUnits"); + + b.Navigation("FilamentUsages"); + + b.Navigation("PrintJobs"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.Navigation("AmsSlots"); + + b.Navigation("FilamentUsages"); + + b.Navigation("PrintJobs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs b/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs new file mode 100644 index 0000000..0adfc7b --- /dev/null +++ b/backend/Infrastructure/Data/Migrations/20260426183433_AddFilamentUsageTrackingModel.cs @@ -0,0 +1,533 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Extrudex.Infrastructure.Data.Migrations +{ + /// + public partial class AddFilamentUsageTrackingModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "filament_usages", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + print_job_id = table.Column(type: "uuid", nullable: false), + spool_id = table.Column(type: "uuid", nullable: false), + printer_id = table.Column(type: "uuid", nullable: false), + grams_used = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false), + mm_extruded = table.Column(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false), + recorded_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + notes = table.Column(type: "character varying(2000)", maxLength: 2000, nullable: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_filament_usages", x => x.id); + table.ForeignKey( + name: "fk_filament_usages_print_job", + column: x => x.print_job_id, + principalTable: "print_jobs", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_filament_usages_printer", + column: x => x.printer_id, + principalTable: "printers", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_filament_usages_spool", + column: x => x.spool_id, + principalTable: "spools", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000001"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000002"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000003"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000004"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000005"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000006"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9901), new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9902) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000001"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000002"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000003"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000004"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000005"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000006"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000007"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000008"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000009"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(297), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(298) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000010"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000011"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000012"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(316), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(317) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000013"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000014"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000015"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000001"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000002"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(805), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(806) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000003"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000004"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000005"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000006"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000007"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000008"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000009"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000010"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(859), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(860) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000011"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866), new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866) }); + + migrationBuilder.CreateIndex( + name: "ix_filament_usages_print_job_id", + table: "filament_usages", + column: "print_job_id"); + + migrationBuilder.CreateIndex( + name: "ix_filament_usages_printer_id", + table: "filament_usages", + column: "printer_id"); + + migrationBuilder.CreateIndex( + name: "ix_filament_usages_recorded_at", + table: "filament_usages", + column: "recorded_at"); + + migrationBuilder.CreateIndex( + name: "ix_filament_usages_spool_id", + table: "filament_usages", + column: "spool_id"); + + migrationBuilder.CreateIndex( + name: "ix_filament_usages_spool_id_recorded_at", + table: "filament_usages", + columns: new[] { "spool_id", "recorded_at" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "filament_usages"); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000001"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000002"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000003"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000004"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000005"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) }); + + migrationBuilder.UpdateData( + table: "material_bases", + keyColumn: "id", + keyValue: new Guid("10000000-0000-0000-0000-000000000006"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000001"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000002"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000003"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000004"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000005"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000006"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000007"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000008"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000009"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000010"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000011"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000012"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000013"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000014"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) }); + + migrationBuilder.UpdateData( + table: "material_finishes", + keyColumn: "id", + keyValue: new Guid("20000000-0000-0000-0000-000000000015"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000001"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000002"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000003"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000004"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000005"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000006"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000007"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000008"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000009"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000010"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) }); + + migrationBuilder.UpdateData( + table: "material_modifiers", + keyColumn: "id", + keyValue: new Guid("30000000-0000-0000-0000-000000000011"), + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) }); + } + } +} diff --git a/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs b/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs index 64647c5..f214506 100644 --- a/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs +++ b/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs @@ -104,6 +104,77 @@ namespace Extrudex.Infrastructure.Data.Migrations b.ToTable("ams_units", (string)null); }); + modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("GramsUsed") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("grams_used"); + + b.Property("MmExtruded") + .HasPrecision(12, 2) + .HasColumnType("numeric(12,2)") + .HasColumnName("mm_extruded"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("notes"); + + b.Property("PrintJobId") + .HasColumnType("uuid") + .HasColumnName("print_job_id"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("RecordedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("recorded_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("PrintJobId") + .HasDatabaseName("ix_filament_usages_print_job_id"); + + b.HasIndex("PrinterId") + .HasDatabaseName("ix_filament_usages_printer_id"); + + b.HasIndex("RecordedAt") + .HasDatabaseName("ix_filament_usages_recorded_at"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_filament_usages_spool_id"); + + b.HasIndex("SpoolId", "RecordedAt") + .HasDatabaseName("ix_filament_usages_spool_id_recorded_at"); + + b.ToTable("filament_usages", (string)null); + }); + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => { b.Property("Id") @@ -145,50 +216,50 @@ namespace Extrudex.Infrastructure.Data.Migrations new { Id = new Guid("10000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388), DensityGperCm3 = 1.24m, Name = "PLA", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9388) }, new { Id = new Guid("10000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871), DensityGperCm3 = 1.27m, Name = "PETG", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9871) }, new { Id = new Guid("10000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881), DensityGperCm3 = 1.04m, Name = "ABS", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9881) }, new { Id = new Guid("10000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888), DensityGperCm3 = 1.07m, Name = "ASA", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9888) }, new { Id = new Guid("10000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895), DensityGperCm3 = 1.21m, Name = "TPU", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9895) }, new { Id = new Guid("10000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9901), DensityGperCm3 = 1.14m, Name = "Nylon", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 291, DateTimeKind.Utc).AddTicks(9902) }); }); @@ -232,122 +303,122 @@ namespace Extrudex.Infrastructure.Data.Migrations new { Id = new Guid("20000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(90) }, new { Id = new Guid("20000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(251) }, new { Id = new Guid("20000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Silk", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(259) }, new { Id = new Guid("20000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Glitter", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(266) }, new { Id = new Guid("20000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Marble", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(272) }, new { Id = new Guid("20000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Sparkle", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(278) }, new { Id = new Guid("20000000-0000-0000-0000-000000000007"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(285) }, new { Id = new Guid("20000000-0000-0000-0000-000000000008"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(291) }, new { Id = new Guid("20000000-0000-0000-0000-000000000009"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(297), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Silk", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(298) }, new { Id = new Guid("20000000-0000-0000-0000-000000000010"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(304) }, new { Id = new Guid("20000000-0000-0000-0000-000000000011"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(310) }, new { Id = new Guid("20000000-0000-0000-0000-000000000012"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(316), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(317) }, new { Id = new Guid("20000000-0000-0000-0000-000000000013"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), Name = "Matte", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(323) }, new { Id = new Guid("20000000-0000-0000-0000-000000000014"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000005"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(329) }, new { Id = new Guid("20000000-0000-0000-0000-000000000015"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), Name = "Basic", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(336) }); }); @@ -391,90 +462,90 @@ namespace Extrudex.Infrastructure.Data.Migrations new { Id = new Guid("30000000-0000-0000-0000-000000000001"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(482) }, new { Id = new Guid("30000000-0000-0000-0000-000000000002"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(805), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(806) }, new { Id = new Guid("30000000-0000-0000-0000-000000000003"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Wood Fill", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(815) }, new { Id = new Guid("30000000-0000-0000-0000-000000000004"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), Name = "Glow-in-the-Dark", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(821) }, new { Id = new Guid("30000000-0000-0000-0000-000000000005"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(828) }, new { Id = new Guid("30000000-0000-0000-0000-000000000006"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(834) }, new { Id = new Guid("30000000-0000-0000-0000-000000000007"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(840) }, new { Id = new Guid("30000000-0000-0000-0000-000000000008"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(847) }, new { Id = new Guid("30000000-0000-0000-0000-000000000009"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(853) }, new { Id = new Guid("30000000-0000-0000-0000-000000000010"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(859), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), Name = "Carbon Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(860) }, new { Id = new Guid("30000000-0000-0000-0000-000000000011"), - CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), + CreatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866), MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), Name = "Glass Fiber", - UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) + UpdatedAt = new DateTime(2026, 4, 26, 18, 34, 33, 292, DateTimeKind.Utc).AddTicks(866) }); }); @@ -838,6 +909,36 @@ namespace Extrudex.Infrastructure.Data.Migrations b.Navigation("Printer"); }); + modelBuilder.Entity("Extrudex.Domain.Entities.FilamentUsage", b => + { + b.HasOne("Extrudex.Domain.Entities.PrintJob", "PrintJob") + .WithMany("FilamentUsages") + .HasForeignKey("PrintJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_filament_usages_print_job"); + + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("FilamentUsages") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_filament_usages_printer"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("FilamentUsages") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_filament_usages_spool"); + + b.Navigation("PrintJob"); + + b.Navigation("Printer"); + + b.Navigation("Spool"); + }); + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => { b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") @@ -936,10 +1037,17 @@ namespace Extrudex.Infrastructure.Data.Migrations b.Navigation("Spools"); }); + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.Navigation("FilamentUsages"); + }); + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => { b.Navigation("AmsUnits"); + b.Navigation("FilamentUsages"); + b.Navigation("PrintJobs"); }); @@ -947,6 +1055,8 @@ namespace Extrudex.Infrastructure.Data.Migrations { b.Navigation("AmsSlots"); + b.Navigation("FilamentUsages"); + b.Navigation("PrintJobs"); }); #pragma warning restore 612, 618 diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..6da98e8 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,11 @@ +node_modules +dist +.git +.gitignore +.angular +.vscode +*.md +.editorconfig +.prettierrc +src/test.ts +**/*.spec.ts \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..4aa493c --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,28 @@ +# Stage 1: Build the Angular application +FROM node:22-alpine AS build + +WORKDIR /app + +# Copy package files first for better layer caching +COPY package.json package-lock.json ./ +RUN npm ci + +# Copy source and build +COPY . . +RUN npx ng build --configuration production + +# Stage 2: Serve static files with nginx +FROM nginx:alpine + +# Remove default nginx config +RUN rm /etc/nginx/conf.d/default.conf + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built Angular artifacts from build stage +COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..64b7fa0 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,42 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; + gzip_min_length 256; + + # Angular SPA — fallback to index.html for client-side routing + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets aggressively + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Proxy API requests to backend + # Uses resolver so nginx doesn't crash if backend isn't available at startup + resolver 127.0.0.11 valid=30s ipv6=off; + set $backend "extrudex-api:8080"; + + location /api/ { + proxy_pass http://$backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "ok"; + add_header Content-Type text/plain; + } +} \ No newline at end of file