fix(harvests): allow several harvests of the same type per year
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m15s
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m15s
The ApiCore sync failed with "Unique constraint failed on the constraint:
harvests_year_type_key" as soon as ApiCore had two harvests of the same
honey type in one year, which is normal (several extractions per season).
Every bottling of that harvest was then skipped too.
- Drop the (year, type) unique index. The slug is now the only unique,
public identifier: /ernten/<slug>, e.g. bluetenhonig-2026, then
bluetenhonig-2026-07-14 for a further harvest of the same type.
- New Harvest.apicoreHarvestId as the sync key, backfilled from the old
"apicore-<id>" slugs; the sync gives those rows a readable slug.
- Slugs are generated by one shared slugify() that normalizes Unicode
(decomposed umlauts) and writes out umlauts. The migration repairs
manual slugs created by the old umlaut bug ("fr-htracht-2026").
- Slug collisions are resolved by the database unique index, so parallel
writers cannot produce duplicates; known slugs are skipped up front to
keep the logs free of handled P2002 errors.
- Old URLs /ernten/<year>/<type> answer with 308 to the new address.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
f1c0da4673
commit
641afa1ae4
15 files changed
+407
-83
No files matched your search
@@ -0,0 +1,56 @@
|
||||
-- Mehrere Ernten derselben Sorte im selben Jahr zulassen.
|
||||
--
|
||||
-- Bisher erzwang "harvests_year_type_key" genau eine Ernte je (Jahr, Sorte).
|
||||
-- Eine Imkerei schleudert dieselbe Sorte aber oft mehrmals im Jahr; der
|
||||
-- ApiCore-Sync scheiterte daran mit P2002. Eindeutig ist ab jetzt nur noch
|
||||
-- der Slug (oeffentliche Adresse /ernten/<slug>).
|
||||
--
|
||||
-- Der Sync erkannte importierte Ernten bisher am Slug "apicore-<ID>". Diese
|
||||
-- ID wandert in eine eigene Spalte, damit der Slug lesbar werden darf.
|
||||
-- Keine Zeile wird geloescht; die Aenderung ist rein additiv bis auf den
|
||||
-- entfernten Unique-Index.
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "harvests" ADD COLUMN "apicoreHarvestId" TEXT;
|
||||
|
||||
-- Backfill: bisherige Sync-Ernten behalten ihre ApiCore-Zuordnung.
|
||||
UPDATE "harvests"
|
||||
SET "apicoreHarvestId" = substring("slug" from 9)
|
||||
WHERE "slug" LIKE 'apicore-%';
|
||||
|
||||
-- Von Hand angelegte Ernten bekamen ihren Slug bisher ohne Umlaut-Behandlung
|
||||
-- ("Frühtracht" 2026 -> "fr-htracht-2026"). Bis jetzt war das unsichtbar;
|
||||
-- ab jetzt ist der Slug die oeffentliche Adresse. Genau diese Form wird auf
|
||||
-- die Schreibweise von lib/slugify.ts gebracht ("fruehtracht-2026") — nur,
|
||||
-- wenn sie sich unterscheidet und die neue Adresse noch frei ist. Sonst
|
||||
-- bleibt der alte Slug: er funktioniert, ist nur weniger schoen.
|
||||
WITH renamed AS (
|
||||
SELECT
|
||||
"id",
|
||||
trim(both '-' from regexp_replace(
|
||||
replace(replace(replace(replace(lower("type"), 'ä', 'ae'), 'ö', 'oe'), 'ü', 'ue'), 'ß', 'ss'),
|
||||
'[^a-z0-9]+', '-', 'g'
|
||||
)) || '-' || "year" AS "newSlug"
|
||||
FROM "harvests"
|
||||
WHERE "apicoreHarvestId" IS NULL
|
||||
AND "slug" = regexp_replace(lower("type"), '[^a-z0-9]+', '-', 'g') || '-' || "year"
|
||||
)
|
||||
UPDATE "harvests" h
|
||||
SET "slug" = r."newSlug"
|
||||
FROM renamed r
|
||||
WHERE h."id" = r."id"
|
||||
AND r."newSlug" <> h."slug"
|
||||
AND r."newSlug" <> ('-' || h."year")
|
||||
AND NOT EXISTS (SELECT 1 FROM "harvests" o WHERE o."slug" = r."newSlug")
|
||||
-- z.B. "Frühtracht" und "Fruehtracht" im selben Jahr: beide ergaeben
|
||||
-- denselben Slug, der Unique-Index liesse die Migration scheitern.
|
||||
AND (SELECT count(*) FROM renamed r2 WHERE r2."newSlug" = r."newSlug") = 1;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "harvests_apicoreHarvestId_key" ON "harvests"("apicoreHarvestId");
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "harvests_year_type_key";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "harvests_year_type_idx" ON "harvests"("year", "type");
|
||||
Reference in new issue
Block a user