fix(db): reconcile stage schema — drop TrackingType, add IsMarked
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 39s
CI / Web (React + Vite) (push) Successful in 22s
Docker Images / API image (push) Successful in 36s
Docker Images / Web image (push) Successful in 4s
Docker Images / Deploy stage (push) Successful in 18s
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 39s
CI / Web (React + Vite) (push) Successful in 22s
Docker Images / API image (push) Successful in 36s
Docker Images / Web image (push) Successful in 4s
Docker Images / Deploy stage (push) Successful in 18s
Phase2c2_MoySkladAlignment и Phase2c3_MsStrict остались в
__EFMigrationsHistory на стейдже, но .cs-файлы были удалены при откате
кода (8fc9ef1). В результате:
- снапшот не соответствовал актуальной БД
- колонка TrackingType висела в БД, а код ждал IsMarked
- /api/admin/moysklad/import-products валился с 42703
Эта миграция:
1. Добавляет IsMarked bool NOT NULL DEFAULT false
2. Если TrackingType есть — бэкфиллит IsMarked = (TrackingType <> 0)
и удаляет колонку (idempotent через information_schema check)
3. Auto-scaffold также синхронизировал snapshot (был устаревшим —
содержал VatRate/IsAlcohol/Kind/Symbol и пр., которых в коде давно нет).
Локально применилось без ошибок.
This commit is contained in:
parent
8346c9a72e
commit
5f0692587a
1855
src/food-market.infrastructure/Persistence/Migrations/20260423161923_Phase2c4_ReconcileStage.Designer.cs
generated
Normal file
1855
src/food-market.infrastructure/Persistence/Migrations/20260423161923_Phase2c4_ReconcileStage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,70 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace foodmarket.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <summary>Reconciliation migration.
|
||||
///
|
||||
/// Предыдущие миграции Phase2c2_MoySkladAlignment и Phase2c3_MsStrict были применены
|
||||
/// на стейдже, но их исходные .cs файлы были удалены при откате кода (commit 8fc9ef1
|
||||
/// стёр их, но __EFMigrationsHistory уже содержал записи). В результате:
|
||||
/// - snapshot был неактуальным (ссылался на VatRate, IsAlcohol, Kind, и т.п.)
|
||||
/// - БД в состоянии пост-2c3 (поля Vat, VatEnabled, TrackingType; без VatRate,
|
||||
/// без Kind, без IsAlcohol, без Symbol/DecimalPlaces/IsBase)
|
||||
/// - код ожидает IsMarked вместо TrackingType
|
||||
///
|
||||
/// Задача этой миграции — добить различие в одной колонке: заменить TrackingType
|
||||
/// (добавленный в Phase2c2) на IsMarked. Всё остальное уже совпадает.
|
||||
/// EF-scaffold предложил много мусорной работы из-за рассинхрона snapshot'а — это
|
||||
/// тело переписано вручную.</summary>
|
||||
public partial class Phase2c4_ReconcileStage : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// 1. Добавляем IsMarked с дефолтом false.
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsMarked",
|
||||
schema: "public",
|
||||
table: "products",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
// 2. Если TrackingType есть в БД (стейдж) — бэкфиллим и удаляем.
|
||||
// На свежей БД (dev, где migrations 2c2/2c3 не применялись отдельно)
|
||||
// колонки не будет — IF EXISTS защищает от ошибки.
|
||||
migrationBuilder.Sql("""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'products'
|
||||
AND column_name = 'TrackingType') THEN
|
||||
UPDATE public.products SET "IsMarked" = ("TrackingType" <> 0);
|
||||
ALTER TABLE public.products DROP COLUMN "TrackingType";
|
||||
END IF;
|
||||
END $$;
|
||||
""");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "TrackingType",
|
||||
schema: "public",
|
||||
table: "products",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.Sql("""UPDATE public.products SET "TrackingType" = CASE WHEN "IsMarked" THEN 99 ELSE 0 END;""");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsMarked",
|
||||
schema: "public",
|
||||
table: "products");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -380,9 +380,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("LegalName")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)");
|
||||
|
|
@ -418,8 +415,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
|
||||
b.HasIndex("OrganizationId", "Bin");
|
||||
|
||||
b.HasIndex("OrganizationId", "Kind");
|
||||
|
||||
b.HasIndex("OrganizationId", "Name");
|
||||
|
||||
b.ToTable("counterparties", "public");
|
||||
|
|
@ -568,9 +563,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsAlcohol")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsMarked")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
|
|
@ -612,8 +604,11 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("VatRateId")
|
||||
.HasColumnType("uuid");
|
||||
b.Property<int>("Vat")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("VatEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
|
@ -627,8 +622,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
|
||||
b.HasIndex("UnitOfMeasureId");
|
||||
|
||||
b.HasIndex("VatRateId");
|
||||
|
||||
b.HasIndex("OrganizationId", "Article");
|
||||
|
||||
b.HasIndex("OrganizationId", "IsActive");
|
||||
|
|
@ -876,9 +869,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Property<bool>("IsMain")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Kind")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ManagerName")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
|
@ -919,15 +909,13 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("DecimalPlaces")
|
||||
.HasColumnType("integer");
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("character varying(500)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsBase")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
|
|
@ -936,11 +924,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
|
|
@ -952,47 +935,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.ToTable("units_of_measure", "public");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("foodmarket.Domain.Catalog.VatRate", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsIncludedInPrice")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal>("Percent")
|
||||
.HasPrecision(5, 2)
|
||||
.HasColumnType("numeric(5,2)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrganizationId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("vat_rates", "public");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("foodmarket.Domain.Inventory.Stock", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -1652,12 +1594,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("foodmarket.Domain.Catalog.VatRate", "VatRate")
|
||||
.WithMany()
|
||||
.HasForeignKey("VatRateId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CountryOfOrigin");
|
||||
|
||||
b.Navigation("DefaultSupplier");
|
||||
|
|
@ -1667,8 +1603,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
b.Navigation("PurchaseCurrency");
|
||||
|
||||
b.Navigation("UnitOfMeasure");
|
||||
|
||||
b.Navigation("VatRate");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("foodmarket.Domain.Catalog.ProductBarcode", b =>
|
||||
|
|
|
|||
Loading…
Reference in a new issue