Der Server lehnt Rechte ab, die der Anlegende nicht selbst hat. Das UI
bietet jetzt auch nur diese an (z. B. kein shop-sync:write ohne eigenes
Recht) und erklärt, wenn gar keines übrig bleibt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bisher vergab die Oberfläche fest shop-sync:read. Jetzt sind alle
freigegebenen Rechte ankreuzbar (Shop lesen, Shop-Buchungen,
Inventar-Spiegel für ERPNext); vorausgewählt bleibt wie bisher nur
Shop lesen, ohne Haken lässt sich nichts anlegen. Die Liste zeigt, was
ein Schlüssel darf, und der Hinweis nach dem Anlegen nennt das Zielsystem.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
With {ART} in the raw material number format (default {ART}-{YYYY}-{SEQ:4}),
beeswax and propolis each count on their own: WA-2026-0001, PR-2026-0001.
Counters live in the new AutomationSequence table (one row per rule and
scope). Without {ART} the rule keeps its single shared counter, since
separate counters behind one prefix would hand out duplicate numbers.
Resetting a rule clears all of its counters.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- RAW_MATERIAL_LOT_NUMBER_FORMAT / PRODUCTION_BATCH_NUMBER_FORMAT fill in the
lot number when it is left empty, like honey batches and bottlings.
- RAW_MATERIAL_LOW: task when the total of a raw material kind drops below
config.minGrams after consumption, withdrawal or inventory count.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Follow-up to a7609ff for the whole frontend: all 30 API helpers in
src/lib now pass path values through encodeURIComponent, so an id taken
from the URL can never leave its own path segment and make the page call
another endpoint with the viewer's token.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Security review finding (client-side path traversal): ids taken from the
URL were interpolated into API paths unencoded, so a crafted link like
/raw-materials/..%2Fusers made the page call another endpoint with the
viewer's token. Ids are now encodeURIComponent-ed; a regression test pins
"../users" to its own path segment.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Pages for raw material lots (beeswax, propolis): list, create in grams
with optional apiary, detail with stock card (harvested / used in
production / withdrawn / remaining) and "used in" batches.
- Production batch form takes the raw material used (lot + grams) and the
batch page shows what it was made from - traceability both ways.
- Sidebar entry "Rohstoffe".
- Removes backend files that were committed into ApiCore-Frontend/src by
mistake in e52a605 (copies of ApiCore-Backend/src/raw-materials).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- New article page /products/[id]: remaining stock summed over all
production batches (or bottlings for honey), each batch linked to its
own page for withdrawals and counts; "Bestand ansehen" in the list.
- Production batches can be corrected (lot number, date, quantity,
best-before date, note) via PATCH; the article stays fixed.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Bottling detail shows Abgefüllt / Webshop verkauft / Entnommen / Rest
(negative rest flagged "Prüfen") and the full movement list; buttons
"Entnehmen" (reason, note) and "Inventur korrigieren" (counted
quantity, difference booked).
- Rest column in the bottling list.
- New pages for production batches (list, create, detail with the same
stock card), sidebar entry with its own permission.
- Products get "Art" (honey vs. manufactured goods) and the webshop
category; bottlings only offer honey articles.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- DetailHeader: "Abfüllung anlegen" as the main action, PDF export in
"Mehr", state as icon + word, retry and way back on load errors.
- Traceability says what it shows and offers a retry on errors instead
of "Lädt…" forever.
- Removing a container allocation asks first and says what changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Same DetailHeader as colonies/queens: an h1, a 44 px back link, state as
icon + word, the obvious next step as the one primary button (harvest:
"Schleuderung erfassen"); a failed load offers a retry and a way back
instead of a bare red line. Colonies of a harvest are tappable links;
changing equipment condition confirms with a toast.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Drafts were stored in localStorage by form only, so on a shared phone
the next person was offered the previous user's unfinished entry
(colony observations, treatment and supplier details). Drafts are now
keyed by the signed-in user id and every draft is removed on logout,
like the offline cache already was.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
New DetailHeader: heading, back link, status as icon + word, ONE primary
action and a "Mehr" menu for exports, QR and the rest.
- Colony: "Durchsicht erfassen" is the main action; sub-cards show a
load error with retry instead of "Noch keine …".
- Apiary: error states, weather with units.
- Queen: edit card in sections, evaluations as 1-5 tap scales.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The feeding statistics summed every amount regardless of its unit, so
5 l syrup and 2 kg fondant showed as 7. The API now also returns byUnit
(units normalised: "L" = "l"), the old fields stay for compatibility,
and the statistics page shows one list per unit.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Varroa, swarm control, harvest, extraction, bottling, task and queen
forms use the form kit: required fields marked, inline errors with focus
on the first problem, rare fields folded, drafts, save in thumb reach.
Single choices (varroa method, swarm mood/action, priority, marking
colour) are large tap buttons; colonies and harvests are picked as
tiles. Plain hints: per-day mite fall vs. threshold, Brutto/Netto,
water content, MHD and lot number, marking colour by year.
Fixed on the way: the queen form suggested the marking colour from the
first digit typed ("2" -> Gelb for 2026); it now waits for a full year.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Every query shows loading, an error with "Erneut versuchen", an empty
state with a next step, then data; statistics no longer hang on
"Lädt…" after an error, a failed search no longer reads as "no hits".
- Storage: one primary action, stock items as a clearly secondary
section, low stock as icon + word.
- Wintering: "Gassen" explained, outcome as icon + word, touch-size
edit buttons; map: tells how many apiaries have coordinates.
- Statistics: units and plain words instead of "Ø", a select instead
of crowded tabs on phones.
- QR code buttons are touch size.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The Delete key removed a label element for good; it now shows a toast
with "Rückgängig" that puts it back in place. Closing or reloading the
designer with unsaved changes asks first.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Webhooks: one card per connection, events by their German label
instead of keys like "product.stock_changed", state as icon + word,
a one-sentence explanation of what a webhook is for, retry on errors.
- Calendar sync: the group id field says where to find it; the
"not configured" note says who sets it up instead of listing
environment variable names.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Inspection, treatment, feeding, varroa and swarm-control edit dialogs use
the form kit (required markers, inline errors, 1-5 as buttons, yes/no
tiles, number keyboards, save button pinned at the dialog bottom).
Moving, splitting and merging colonies report success; merging - which
cannot be undone - asks first and says what happens.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Lists load through QueryState: a failed load says so and offers
"Erneut versuchen" instead of looking empty; empty lists link to the
create page; exports move into a "Mehr" menu so "Neu …" is the one
primary action.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Same structure as the inspection form: required fields marked, errors at
the field, numbered sections with rarely used fields folded, drafts kept
on the device, save button in thumb reach, plain hints for jargon
(Wirkstoff, Wartezeit, Code, SKU, units). The Bestandsbuch fields of a
treatment stay a visible section of their own. Apiaries can take the
coordinates from the phone's location. Offline queue and idempotency
are unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Users: one card per person on every screen size, status and MFA as
icon plus word, server reasons (e.g. "more rights than you") shown.
- Audit log: names instead of user ids, area as a choice instead of a
free-text type, optional record id, filter reset, cards instead of a
5-column table, empty and error states with a next step.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Phones get a list of the days that have entries (44 px rows with type
word, icon and title) instead of a 7-column grid with 0.7 rem chips.
- Month grid on wider screens: days are buttons, the chosen day lists
all its entries below, so "+N weitere" is no longer a dead end.
- Today is marked with a filled circle and the word "Heute"; a "Heute"
button jumps back. Month navigation buttons are touch size.
- Load errors offer a retry; an empty month says so.
- Entries are grouped by local day instead of UTC, so late-evening
entries no longer land on the next day.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Tabs ordered from everyday (suggestions, recurring tasks, labels) to
technical (calendar sync, API keys, webhooks); the page opens on
"Vorschläge" instead of Webhooks, and says in one sentence what it is.
- Resetting a number counter, deactivating a label template and ending a
recurring task now ask first and say plainly what happens.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Inputs and selects are 44 px on phones (36 px from sm up); default and
small buttons get 40/36 px on phones.
- Dialogs scroll inside the viewport, so long edit forms keep their
save button reachable on a phone.
- Dialog zoom, sheet slide, sidebar icon scale and stat-bar growth only
move at the motion level "Voll" (motion-full), not at "Dezent".
- QueryState / ErrorState / LoadingState: loading, error with "Erneut
versuchen", empty, data - a failed load can no longer look like an
empty list. EmptyState takes an action for the obvious next step.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
AuDHD-oriented rework of the most-used form, plus shared building blocks
for the remaining forms:
- Field: label, "Pflicht"/"optional", hint and the error right at the
field instead of a vanishing toast.
- FormSection: numbered steps; optional parts start folded and say how
many fields they hold.
- RatingScale (1-5 as five 44 px buttons with both ends named),
ToggleChip (yes/no tiles with tick and colour), StickySubmit (save
stays in thumb reach on phones).
- useFormDraft + DraftBanner: an unfinished entry is kept on the device
and offered back explicitly ("Weitermachen" / "Verwerfen"), never
applied silently; drafts expire after 3 days.
Inspection form: 21 flat fields became 5 steps, only colony and date are
required, observations are single taps, a hint appears when neither queen
nor eggs were seen, server messages show inline.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- New passwords (create user, admin reset, own change) need at least 12
characters (ASVS 2.1.1) and may not exceed 72 bytes, which bcrypt would
silently cut off.
- They are checked against Have I Been Pwned with k-anonymity (only the
first 5 SHA-1 characters leave the server, padded responses); an
unreachable service is logged and does not block. PASSWORD_BREACH_CHECK
=false disables it for offline deployments.
- Login accepts existing passwords as before, capped at 256 characters to
bound anonymous bcrypt work.
- Password fields in the UI say the rule up front.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A key can be limited to IP addresses or CIDR networks (e.g. the Wabenhain
server); a stolen key is useless from anywhere else. Requests from other
addresses get 403 "not allowed for this IP" and a log line. The admin UI
asks for the addresses when creating a key, warns when a key works from
anywhere, shows the last source address and can take it over into the
allowlist - changing the list needs no new key (PATCH /api-keys/:id).
Also: a key can only carry scopes its creator holds.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Per-account lockout: five failed attempts (password, MFA code or a
step-up confirmation) lock the account for 15 minutes, on top of the
per-IP throttle - guesses spread over many addresses no longer help.
- A TOTP code is accepted once: the time step is stored and that step or
any earlier one is refused (also between two concurrent requests).
- Login runs bcrypt on every path and answers unknown email, wrong
password, deactivated and locked accounts with the same message; failed
logins for unknown emails no longer write attacker-chosen text into the
audit log.
- Turning MFA off needs password and a current code; turning it on needs
the password. Both end all other sessions.
- New audit action ACCOUNT_LOCKED.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Deliveries connect through a lookup hook that checks the address
actually dialled, so a host that resolves to a private address after
registration (DNS rebinding) or a tampered row cannot reach internal
services. Redirects are still not followed.
- More blocked ranges (CGNAT/100.64/10 incl. cloud metadata, multicast,
reserved, benchmarking, NAT64, 6to4, site-local, IPv4-mapped IPv6);
only ports 80/443, no credentials in the URL, https in production.
- 5 s timeout, response body discarded; dispatch no longer waits for
receivers, so a hanging endpoint cannot stall the user's request.
- Signature covers "timestamp.deliveryId.body" with X-ApiCore-Timestamp
and X-ApiCore-Delivery headers, so receivers can reject replays.
- Signing secrets are stored AES-256-GCM-encrypted; older plain rows still
work.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
backup:configure could point automatic backups at any writable directory
(e.g. a statically served one) and set a cron of every second; an invalid
expression deleted the running job before failing, silently ending
backups.
- A path entered in the UI is resolved under BACKUP_ROOT (default
./backups) and refused if it leaves it; env values stay unrestricted.
- The schedule must be a five-field cron that runs at most hourly.
- reload() validates before replacing the running job.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Backups carried every password hash, encrypted MFA secret and webhook
secret in plain JSON, and an import upserted arbitrary rows - including
users - so anyone holding backup:import, or tricking an admin into
importing a file, could overwrite the admin account with a known password.
- Export (download and scheduled) is AES-256-GCM over the gzipped bundle,
key from BACKUP_ENCRYPTION_KEY or derived from JWT_ACCESS_SECRET via
HKDF; header is authenticated, decompression is capped. Scheduled files
are written 0600 in a 0700 directory; run-now returns only the file name.
- Import requires the current password (and TOTP when MFA is on).
- Only a sealed file restores users, roles, grants, webhooks and audit
entries; an old unsealed file restores business data only and reports
what it skipped.
- Audit entries are append-only on import.
- Imported documents: entityType checked against the enum, MIME type
allowlisted and matched against the file signature, path kept under the
storage root (no more directory traversal via entityType).
- Permission cache is cleared after an import.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- WinterCheck per colony, winter and season (autumn/spring), every field
editable; loss causes limited to the COLOSS monitoring categories.
- Summary per winter and apiary: loss rate lost/wintered, marked
provisional while colonies are not yet checked in spring - an unchecked
colony never counts as survived.
- A loss marks the colony deceased only on explicit request, in the same
transaction and audited.
- Permissions winter-checks:read/create/update granted by migration to
beekeeper and staff roles; winter checks included in backups.
- Page "Überwinterung": next action first, one button per row until the
colony is wintered, German number format.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Treatment records supplier, purchase proof and prescribing vet, and
keeps the apiary the colony stood at on the treatment day.
- Holder name and address in site settings (never in public output;
regression test in public.service.spec).
- treatment-log.pdf: one sheet per apiary, identical treatments grouped,
missing mandatory fields printed as FEHLT, uncertain locations marked.
- Shared PDF table helpers extracted from the record card.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
New report colony-record-card (button "Stockkarte (PDF)" on the colony
page): master data, current queen and queen history, every inspection as
a table row (strength, eggs, open/capped brood, queen seen, queen cells,
gentleness, swarm drive, feed, measures, notes), treatments with batch
number and withdrawal period, feedings, varroa counts and harvests.
Landscape A4, table header repeated on each page. Whole history by
default, one calendar year with ?year=.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Two new rules, "Kontrolle nach Behandlung" and "Kontrolle nach
Fütterung", create a check task when a treatment or feeding is recorded.
The due date counts from the measure itself (end of treatment, else its
date; feeding date) plus the configured number of days, and the task
names the product and date. At most one open check per colony and rule.
Both rules ship switched off and without an interval: the right timing
depends on the product (package leaflet) and season, so the beekeeper
sets it. Every task-creating rule can now carry a "due after N days"
setting in the automation page.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Label sheets printed on plain A4 are cut with a lever paper cutter. Each
template can now print small hairline cut marks in the sheet margin: one
at both ends of every cut line (left/right edge of each column, top/bottom
edge of each row), 1 mm away from the label and never closer than 2 mm to
the paper edge, so they print on normal printers and never touch a label.
Labels without a gap share one mark. Marks are drawn on every sheet for
the full grid, since the cutter cuts across the whole sheet.
On by default (new column cropMarks, default true); switch it off in the
template designer for pre-die-cut label sheets.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Integrations such as the Wabenhain shop sync no longer need a service
account with a password (and without MFA). An admin creates a key under
Verwaltung > Automatisierung > API-Schlüssel.
- Key format ack_<8>_<43>; only its SHA-256 hash is stored, the key is
shown exactly once. Optional expiry, "last used" timestamp, revocation
takes effect immediately; create/revoke are audit-logged without the key.
- A key is not a user: JwtAuthGuard sets request.apiKey, PermissionsGuard
admits it only to routes whose permission is among its scopes and never
to user-only routes. Scopes come from a short allowlist (shop-sync:read).
- Managing keys requires the new permission api-keys:manage.
- Keys are excluded from backups on purpose; a restore means issuing a new
key.
Additive migration (new table).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Start page (AuDHD-oriented, per the agreed "Heute zuerst" layout):
- One highlighted next step: started work first (easy re-entry after an
interruption), then overdue, due today, then urgent undated tasks.
Primary action "Erledigt", secondary "Beginnen", link to the related
colony/apiary/queen.
- "Außerdem heute": at most five further tasks with a large honey-cell
checkbox; undated automatic suggestions are included so a queenless
colony does not sit unseen in the full list.
- Completing is the one authored motion moment: the cell fills, the tick
draws, the row folds away and the next step arrives with a short focus
pull. Every completion can be undone from the toast (10 s), restoring
the previous status.
- Status is shown as icon plus word (never colour alone); tasks created by
automation carry a "Vorschlag" badge, recurring and inspection tasks
their own - a suggestion never looks like work already done.
- Friendly empty, loading and error states; key figures moved into a
compact overview below; overdue tasks are no longer listed twice.
Motion: per-device setting Voll / Dezent / Aus in the profile (default
Voll); the OS reduce-motion setting always lowers Voll to Dezent.
New 44 px "touch" button size for gloves at the apiary; themed text
selection and caret.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
When the server saved an inspection, harvest, treatment, feeding, varroa
measurement or swarm check but the answer was lost (dead zone at the
apiary), the form queued the entry and the sync later created it a second
time - including a second automation task. Each opened form now creates
one Idempotency-Key, sends it with the request and stores it with the
queued entry, so the retry returns the first record. Entries queued before
this change still sync without a key.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The backend accepted every field of these records already; the forms simply
did not offer them, so from the app they were still frozen.
- Inventar: only the condition could be changed. A mistyped inventory number,
a wrong purchase or maintenance date, a renamed item - all stuck for the life
of the record. New edit dialog covering every field; the inline condition
select stays as a shortcut.
- Produkt: the status was shown as a badge but could not be set anywhere, so a
product could never be discontinued or brought back.
- Aufgabe: the status could only ever move to DONE through the "Erledigt"
button. IN_PROGRESS and CANCELLED were unreachable, and an accidentally
completed task could not be reopened.
- Aufgabenvorlage: priority, assignee and the active flag were missing.
Deactivating was one-way, since only a deactivate endpoint was wired up -
the active checkbox now reactivates through PATCH.
- Königinnen-Bewertung: notes and the evaluated colony were missing.
Verified by extracting the field set each form submits and comparing it with
the matching update DTO: every form now covers its DTO. The one gap left is
re-linking a document to a different record, which still has no UI.
Co-Authored-By: Claude Opus 5 <[email protected]>
Both could only be created and deleted. For a webhook that is actively harmful:
recreating it issues a new signing secret, so the receiver that verifies the
X-ApiCore-Signature header against the old one starts rejecting every delivery
without anyone changing it on purpose.
Backend:
- New PATCH /webhooks/:id behind a new webhooks:update permission, covering the
target URL, the event selection and the active flag - the last of which had
no way to be set after creation at all, so a misbehaving receiver could only
be deleted. The signing secret is deliberately left alone and stays out of
the audit log.
- A changed webhook URL runs through the same assertPublicWebhookUrl SSRF check
as a new one; without that the edit would have been a way around the guard
create() applies. Covered by a test.
- New PATCH /documents/:id behind a new documents:update permission, for
re-linking a document filed against the wrong record and correcting its
display name. Re-linking validates the new target with the existing
assertEntityExists; the stored file is untouched.
Frontend:
- New webhook edit dialog. The event labels and the toggle helper moved out of
the create dialog into lib/webhooks/labels.ts so both use one copy.
- Documents can be renamed from the attachment card.
Re-linking a document to a different record has no UI yet: it needs a two-step
picker (colony, then inspection) that is not worth building unprompted.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Both records could be created but never changed. A water-content reading
decides whether a batch may be sold as honey at all, and queen ratings drive
breeding decisions - a slipped digit in either is exactly the kind of wrong
value that must not be left standing.
- New PATCH /honey-batches/measurements/:measurementId behind
honey-batches:measure, and PATCH /queens/evaluations/:evaluationId behind
queens:evaluate. Both are addressed by the child record's own id, so the
routes take two path segments and do not collide with PATCH /:id.
- Both log before and after data, like every other correction.
- The measurement list on the honey batch page and the rating list on the queen
page gained an edit dialog; the rating dialog reuses the existing criteria
list rather than restating it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Four fields were still frozen because changing them would have bypassed the
record that makes the change traceable, and a user's login data could not be
corrected at all. Locking a field is the wrong trade: a wrong value stays in
the system forever. The fix is to let the edit write the same history entry the
dedicated endpoint writes.
- A colony's apiary is editable through PATCH and writes the same
ColonyApiaryHistory entry POST /colonies/:id/move writes, so the location
history never skips a stand the colony was recorded on.
- A honey batch's storage container is editable through PATCH and writes the
same HoneyBatchRelocation entry POST /honey-batches/:id/relocate writes.
- A stock item's quantity is editable and books the difference as an ADJUSTMENT
movement, so the stored level stays the sum of its movements.
- A role's key is editable (system roles were already rejected outright by
RolesService.update, which keeps the seed's ADMIN lookup intact).
- A user's e-mail - the login identity - is editable. A typo there previously
locked the account out for good.
- New POST /users/:id/reset-password lets an administrator set someone else's
password: there is no self-service reset flow, so a forgotten password was
the one thing no edit could repair. It revokes every refresh token and clears
the permission cache exactly as a self-service change does, refuses to act on
your own account (that goes through /profile/password, which proves ownership
by asking for the current one), and never writes the password to the log.
Frontend:
- Users had no edit UI at all, although PATCH /users/:id existed. New
EditUserDialog covers name, e-mail, role and an optional new password.
- Colony, honey batch and stock item forms gained the newly editable fields,
each with a note saying the correction is recorded as a move, a relocation
or a stock adjustment.
- The role dialog gained the key field.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
An inspection, treatment, feeding, varroa measurement or swarm control check
filed against the wrong colony could not be repaired: the colony was frozen at
creation time, and with no delete endpoint either, the only remedy was leaving
a wrong record in the colony's history. The same held for a harvest booked
against the wrong apiary or with a colony forgotten, for an extraction linked
to the wrong harvests, and for a honey batch whose number or source extraction
was mistyped.
Backend:
- The five colony-scoped update DTOs now cover the full create DTO, colonyId
included.
- Harvest update accepts apiaryId and colonyIds, extraction update accepts
harvestIds. Both validate the ids first and replace the whole join-row set
inside the existing transaction; the join rows carry no data of their own,
so replacing beats diffing.
- Honey batch update accepts batchNumber, extractionId and status. Status was
previously only ever set to BOTTLED by the bottling module, so SOLD and
CONSUMED were unreachable through the API.
- Colony and storage container codes, and a task's relatedType/relatedId, are
editable.
- apiaryId on a colony and storageContainerId on a honey batch stay out of
PATCH on purpose: both have dedicated endpoints that also write the history
entry (ColonyApiaryHistory, HoneyBatchRelocation) the traceability chain
depends on.
- AllExceptionsFilter now also translates Prisma P2003 to a 400 and P2025 to a
404. Services write straight against the foreign keys with no application
level existence check, so before this a stale or mistyped relation id came
back as a bare 500 and filled the log with fake server errors.
Frontend:
- New shared fields: ColonySelectField (used by all five colony-scoped edit
dialogs), HarvestSelectField (both extraction dialogs) and TaskRelatedField.
- Re-filing a record invalidates the lists of both the colony it left and the
one it joined, otherwise one of the two stays stale.
- The harvest dialog gained an apiary select and a colony checklist that keeps
already-linked colonies visible even when they moved or were dissolved; the
honey batch dialog gained batch number, source extraction and status.
- Extraction and harvest forms refuse to submit an empty relation set instead
of letting the backend reject it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
A bottling batch could only be corrected in four of its seven fields after
creation: honey batch, jar size and jar count were frozen at creation time,
an assigned product could not be cleared again, and there was no way to
remove a record entered by mistake at all. A mistyped jar count is a
data-entry error, not a fact of production, so it has to stay correctable.
Backend:
- UpdateBottlingBatchDto now covers every field. `productId: null`
explicitly clears the product assignment; omitting it leaves it alone.
- BottlingService.update rebalances the affected honey batch amounts:
the previously bottled amount is given back before the corrected amount
is taken again, so a correction never double-charges the source batch.
Moving a batch to a different honey batch credits the old one and debits
the new one in the same transaction.
- New DELETE /bottling-batches/:id gives the bottled amount back to the
honey batch and keeps the full record in the audit log (action DELETE,
beforeData). The honey batch status is deliberately left untouched - the
status it held before being bottled is not recoverable here.
- New permission bottling:delete, checked server-side. The catalog sync
grants it to system roles on the next boot.
Frontend:
- The edit dialog exposes all fields, offers "Kein Produkt" to clear the
assignment, keeps a discontinued product selectable so opening the dialog
cannot silently drop it, and shows backend errors inline.
- The form is mounted only while the dialog is open, so a cancelled edit
never leaks its values into the next one.
- New delete button behind a confirmation that names the amount credited
back and warns that the public origin page for the lot number stops
resolving.
- Edit and delete are additionally hidden without the matching permission.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>