Skip to content

1.7.4

⚙️ Patch 1.7.4 — Repricer fixes, Tickets AI workflow, and CI overhaul

🚀 Summary

  • Deleting a base repricer article now cascades to all attached channel details and Otto pricing history — no orphaned rows left behind.
  • Channel detail status is now reconciled with base-article stock across repricer channels — fixes stale OOS on Otto (and others) when inventory is available again.
  • Tickets service: n8n generates AI drafts; humans review in the UI; Send to Plenty posts directly via Plenty Messenger API (not n8n).
  • n8n workflow settings moved from env vars to database + Credentials UI.
  • Added 37 new repricer tests (detail CRUD, UVP, delete cascade, stock/status reconciliation); multi-repricer suite now 76 tests.
  • GitHub CI rebuilt: faster parallel jobs, no full Docker stack for unit tests, fixed test discovery under app/tests.
  • Invoice service: fixed confirmation endpoint using wrong Invoice field; stale API tests updated.

✨ Improvements

Repricer — cascade delete & tests

  • Repricer cascade delete (repricer_article.remove):
  • New helper remove_all_channel_details runs before the base article is deleted.
  • Removes Shopify, Kaufland, Otto, FBA, and all FBM rows for the article (including multiple FBM entries per SKU).
  • Clears item_pricing_history rows for the SKU.
  • Existing DELETE /{sku} endpoint uses this path — no API change required.
  • Repricer test coverage (service_multi-repricer/app/tests/):
  • test_delete_article.py — cascade delete (all channels + pricing history + extra FBM row); unknown SKU no-op.
  • test_details_crud.py — create / update / delete for Shopify, Kaufland, Otto, FBM, FBA; FBM multi-row behaviour; single-channel delete leaves base article and other channels intact.
  • test_uvp.pyset_uvp full/min-only/max-only updates, selective channel flags, propagation to bundle children, child_skus references, and variant SKU patterns; remove_uvp clears UVP fields, re-enables use_min_max_rules, selective remove flags, prices unchanged on remove.
  • test_article_status.py — stock-based status reconciliation (oosactive), multi-channel reconcile, monitoring skip rules.

Repricer — stock / status reconciliation

  • New services/article_status.py centralises detail status from base stock:
  • stock == 0oos (except paused).
  • stock > 0 and status oos / unknownactive.
  • Preserves paused, error, and update_needed when stock is available.
  • reconcile_article_channel_details updates Shopify, Kaufland, Otto, FBM (all rows), and FBA in one pass.
  • Applied during:
  • sync_base — after Plenty stock sync (replaces OOS-only marking).
  • Otto sync — refreshes Otto quantity for existing SKUs and reconciles status.
  • Otto price monitor — reconciles before skip checks.
  • FBM / FBA recalculate — reconciles before OOS skip.

Tickets — n8n generate, human review, Plenty send

  • Workflow split: | Step | Handler | | -------------- | ---------------------------------------------------------- | | Generate draft | n8n webhook (POST /workflow/generate) | | Save draft | n8n callback (action: generate_complete) | | Human review | Tickets UI (edit content, recipient, reference, whisper) | | Send reply | Backend → POST /rest/messages via Plenty credentials |
  • AI draft storage on tickets table (one draft per ticket — no separate ai_responses table):
  • ai_response_content, ai_response_message_uuid, ai_response_recipient, ai_response_reference, ai_response_whisper, ai_response_status.
  • API (ticket-scoped):
  • GET /tickets/{uuid}/ai-responses — 0 or 1 draft from ticket row.
  • PUT /tickets/{uuid}/ai-response — update draft.
  • POST /tickets/{uuid}/ai-response/send — send to Plenty.
  • DELETE /tickets/{uuid}/ai-response — clear draft.
  • Frontend: Generate via n8n + Send to Plenty button; Credentials tab documents the split flow.
  • Removed in-process OpenAI integration (credentials tab, endpoints, openai dependency).

Tickets — n8n settings in UI

  • New table n8n_workflow_settings (singleton row) stores webhook URL, token, callback URL, and enabled flag.
  • Credentials → n8n Workflow tab: editable form with save (token never returned on GET; leave blank to keep existing).
  • GET/PUT /tickets/workflow/settings — load and persist configuration.
  • Env vars N8N_TICKETS_* remain as fallback when DB fields are empty.

CI pipeline

  • Split into three parallel jobs: backend tests, frontend lint, Docker Compose config validation.
  • Concurrency — outdated runs cancelled on new push.
  • Shared test env: PYTHONPATH, in-memory SQLite DATABASE_URL, test secrets.
  • Pip/npm caching across backend and service requirements.txt files.
  • Webhooks only fire after all jobs succeed on main / master.
  • Removed slow docker compose up --build from the test path (Compose is validated with docker compose config only).
  • run_tests.sh: finds tests under app/tests and tests/; runs backend/app; sets PYTHONPATH; LF line endings via .gitattributes.
  • Backend deps: pytest-asyncio added to backend/requirements.txt.

🐛 Bug Fixes

  • Stale OOS on repricer channels (especially Otto): articles with stock > 0 could keep oos on channel details and were skipped by price monitoring — status is now reconciled on sync and before monitor/recalc.
  • Otto monitor skip logic: no longer excludes items solely because detail status was stale oos; reconciles from stock first.
  • Otto monitor quantity: uses scraped Otto quantity or base-article stock when deciding OOS vs active.
  • Orphaned repricer details after delete: removing a base article left Shopify/Kaufland/Otto/FBM/FBA rows and pricing history in the DB — now deleted in the same transaction path.
  • Tickets send path: previously triggered n8n or falsely marked sent without posting to Plenty — now sends via Plenty_Manager.create_reply_message().
  • Tickets AI storage: code assumed a separate ai_responses table that does not exist in production — draft data lives on tickets only.
  • CI false green: run_tests.sh only looked for a top-level tests/ folder, so service_multi-repricer/app/tests (and similar layouts) were never executed in CI.
  • Invoice confirmation update: update_invoice_confirmation passed row= instead of sheet_row= to the Invoice model — caused 500 errors; HTTP 400 responses from the handler are no longer swallowed as 500.
  • Invoice tests: mocks updated for current OAuth callback response shape, reauthorize signature, and removed /download/test-progress endpoint (2 tests skipped).

📁 Files changed

Backend (service_multi-repricer)

  • app/crud/repricer_article.pyremove_all_channel_details, cascade in remove().
  • app/services/article_status.py — stock/status reconciliation helpers.
  • app/api/v1/endpoints/repricer_articles.pysync_base reconcile hook.
  • app/api/v1/endpoints/otto_articles.py — sync quantity refresh, monitor reconcile, effective quantity.
  • app/api/v1/endpoints/fbm_articles.py, fba_articles.py — reconcile before OOS skip.

Backend (service_tickets)

  • app/services/ticket_ai_draft.py — ticket-row draft mapping.
  • app/services/plenty/plenty_manager.pycreate_reply_message().
  • app/services/n8n_workflow.py — DB-backed config with env fallback.
  • app/schemas/n8n_workflow.py, app/crud/n8n_workflow.py — n8n settings persistence.
  • app/api/v1/endpoints/tickets.py — generate/send/update AI draft on ticket.
  • app/api/v1/endpoints/n8n_webhooks.py — generate callback writes to ticket.
  • app/schemas/tickets.py — AI draft columns on Ticket model.

Frontend

  • src/pages/Tickets/Tabs/TicketsTab.jsx — review UI, Send to Plenty.
  • src/pages/Tickets/Credentials/WorkflowInfo.jsx — n8n settings form.
  • src/pages/Tickets/Tabs/OverviewTab.jsx — updated workflow docs.
  • src/services/api/tickets/tickets.js — ticket-scoped AI + workflow settings API.

Tests

  • service_multi-repricer/app/tests/test_delete_article.py
  • service_multi-repricer/app/tests/test_details_crud.py
  • service_multi-repricer/app/tests/test_uvp.py
  • service_multi-repricer/app/tests/test_article_status.py
  • service_multi-repricer/app/tests/test_recalculate_channels.py — FBM OOS test uses stock=0.

Backend (service_invoice)

  • app/api/v1/endpoints/sheets.pysheet_row fix, re-raise HTTPException on confirmation failure.
  • app/tests/api/v1/test_sheets.py, test_google.py, test_download.py — mock and assertion updates.

CI / tooling

  • .github/workflows/ci.yml
  • run_tests.sh
  • .gitattributes
  • backend/requirements.txt
  • docker/postgres/migrations/add_ai_response_meta.sql
  • docker/postgres/migrations/add_n8n_workflow_settings.sql
  • docker/postgres/tables/n8n_workflow_settings.sql
  • docker/postgres/tables/tickets.sql — AI draft columns.

🔧 Database & SQL

Repricer

  • No schema migration — cascade delete is at the application layer.

Tickets — run on server before deploy

-- AI draft fields on tickets (one draft per ticket)
ALTER TABLE public.tickets
    ADD COLUMN IF NOT EXISTS ai_response_message_uuid text,
    ADD COLUMN IF NOT EXISTS ai_response_recipient text,
    ADD COLUMN IF NOT EXISTS ai_response_reference text,
    ADD COLUMN IF NOT EXISTS ai_response_whisper boolean DEFAULT false,
    ADD COLUMN IF NOT EXISTS ai_response_status text;

-- n8n workflow settings (singleton)
CREATE TABLE IF NOT EXISTS public.n8n_workflow_settings (
    id integer PRIMARY KEY DEFAULT 1,
    webhook_url text,
    webhook_token text,
    callback_url text,
    is_active boolean DEFAULT true,
    created_at timestamp without time zone DEFAULT now(),
    updated_at timestamp without time zone DEFAULT now(),
    CONSTRAINT n8n_workflow_settings_singleton CHECK (id = 1)
);
INSERT INTO public.n8n_workflow_settings (id) VALUES (1) ON CONFLICT (id) DO NOTHING;

See docker/postgres/migrations/add_ai_response_meta.sql and add_n8n_workflow_settings.sql.

Usage notes

Repricer

  • Run repricer tests:
    cd backend/service_multi-repricer
    python -m pytest app/tests
    
  • After deploy, run Plenty sync or Otto sync to fix existing articles with stale OOS status; the next monitor cycle will include them.
  • Deleting a repricer article via the API now removes all channel details for that SKU.

Tickets

  1. Credentials → Plenty — active Plenty login with valid token/user_id.
  2. Credentials → n8n Workflow — webhook URL, token, callback URL (/api/v1/tickets/webhooks/n8n).
  3. Expand a ticket → Generate via n8n → edit draft → Send to Plenty.
  4. n8n callback example: { "action": "generate_complete", "ticket_uuid": "...", "message_uuid": "...", "content": "..." }.

CI

  • Full backend suite from repo root:
    chmod +x run_tests.sh
    ./run_tests.sh
    
  • Frontend lint: cd frontend && npm run lint.