Add structured quantities + units to recipe ingredients
Phase 2 of structured quantities + UoM. Replaces the free-form Quantity string on RecipeIngredient with a structured (Quantity decimal, UnitOfMeasureId or FamilyUnitOfMeasureId) pair, plus an IsApproximate + QuantityNote escape hatch for "to taste" style entries. The unit FK pair mirrors the existing Product / FamilyProduct pattern, with the same at-most-one and tenant-scoping validation. Existing string Quantity values are dropped per the agreed wipe-to-null migration plan. Frontend ships a QuantityInput component (numeric field + unit dropdown fed by a runes-cached effective catalog from /api/units) and a shared formatter for read-only display. Recipe -> shopping list copy folds the structured quantity into the item Name for now; Phase 3 will move the fields onto ShoppingListItem directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -404,6 +404,12 @@ namespace YesChef.Api.Migrations
|
||||
b.Property<int?>("FamilyProductId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("FamilyUnitOfMeasureId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("IsApproximate")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
@@ -412,9 +418,13 @@ namespace YesChef.Api.Migrations
|
||||
b.Property<int?>("ProductId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Quantity")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
b.Property<decimal?>("Quantity")
|
||||
.HasPrecision(12, 4)
|
||||
.HasColumnType("numeric(12,4)");
|
||||
|
||||
b.Property<string>("QuantityNote")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<int>("RecipeId")
|
||||
.HasColumnType("integer");
|
||||
@@ -422,16 +432,23 @@ namespace YesChef.Api.Migrations
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("UnitOfMeasureId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FamilyId");
|
||||
|
||||
b.HasIndex("FamilyProductId");
|
||||
|
||||
b.HasIndex("FamilyUnitOfMeasureId");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.HasIndex("UnitOfMeasureId");
|
||||
|
||||
b.ToTable("RecipeIngredients");
|
||||
});
|
||||
|
||||
@@ -869,6 +886,11 @@ namespace YesChef.Api.Migrations
|
||||
.HasForeignKey("FamilyProductId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("YesChef.Api.Entities.FamilyUnitOfMeasure", "FamilyUnitOfMeasure")
|
||||
.WithMany()
|
||||
.HasForeignKey("FamilyUnitOfMeasureId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("YesChef.Api.Entities.Product", "Product")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductId")
|
||||
@@ -880,13 +902,22 @@ namespace YesChef.Api.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("YesChef.Api.Entities.UnitOfMeasure", "UnitOfMeasure")
|
||||
.WithMany()
|
||||
.HasForeignKey("UnitOfMeasureId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.Navigation("Family");
|
||||
|
||||
b.Navigation("FamilyProduct");
|
||||
|
||||
b.Navigation("FamilyUnitOfMeasure");
|
||||
|
||||
b.Navigation("Product");
|
||||
|
||||
b.Navigation("Recipe");
|
||||
|
||||
b.Navigation("UnitOfMeasure");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("YesChef.Api.Entities.ShoppingList", b =>
|
||||
|
||||
Reference in New Issue
Block a user