fix(apicore-sync): keep internal notes out of the shop and protect edited drafts

- Harvest descriptions no longer include ApiCore's internal harvest notes.
  The description becomes public on "Veröffentlichen", so a draft published
  without a careful read could have carried operational notes into the shop.
- A repeated sync overwrote unpublished harvest drafts, undoing the admin's
  corrections (season instead of honey type, location, sales text). Drafts
  with an admin audit entry are now left alone and reported as protected.
- Every ApiCore request has a 20 s timeout; before, an ApiCore that accepted
  the connection but never answered left the admin action hanging.
- The sync panel shows the time of the last complete run.
- Client tests: timeout, failed fetch is never treated as an empty source,
  malformed responses are rejected as a whole.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-27 05:39:09 +02:00
1 parent e0b47faf45
commit 0482fd490e
9 files changed
+194 -17

No files matched your search

+15 -3
View File
@@ -64,19 +64,31 @@ describe("slugForApiCoreSource", () => {
});
describe("buildHarvestDescription", () => {
test("fasst die vorhandenen Mengenangaben und Notizen zusammen", () => {
test("fasst die vorhandenen Mengenangaben zusammen", () => {
// Arrange
const source = makeHarvest({ honeySuperCount: 12, grossWeight: 48.5, netWeight: 41, notes: " Sehr hell. " });
const source = makeHarvest({ honeySuperCount: 12, grossWeight: 48.5, netWeight: 41 });
// Act
const description = buildHarvestDescription(source);
// Assert
expect(description).toBe(
"Automatisch aus ApiCore importiert. 12 Honigräume · Bruttogewicht 48.5 kg · Nettogewicht 41 kg · Sehr hell."
"Automatisch aus ApiCore importiert. 12 Honigräume · Bruttogewicht 48.5 kg · Nettogewicht 41 kg"
);
});
test("uebernimmt interne ApiCore-Notizen nie in den Shoptext", () => {
// Arrange: Betriebsnotiz, die nicht oeffentlich werden darf.
const source = makeHarvest({ netWeight: 41, notes: "Volk 7 nach Varroa-Behandlung schwach" });
// Act
const description = buildHarvestDescription(source);
// Assert
expect(description).not.toContain("Varroa");
expect(description).not.toContain("Volk 7");
});
test("fordert eine Beschreibung an, wenn ApiCore gar nichts liefert", () => {
// Arrange
const source = makeHarvest();