Skip to content

Package Mappings Endpoints

Router prefix: /package_mappings → full base: /api/v1/multi/package_mappings

Implementation: backend/service_multi-repricer/app/api/v1/endpoints/package_mappings.py

Package types

Allowed values: XS, S, M, L1, L2, L3, L5, XL (each with its own cost).

Cost mapping CRUD

Method Route Purpose
GET /types List allowed package types
POST /assign Re-assign packages for all articles (optional ?sku=)
GET / List all mappings
POST / Create (package_type + cost required; optional min_weight)
PUT /{id} Update
DELETE /{id} Delete

The Package mappings UI exposes optional min_weight thresholds for CSV-miss fallback. The highest threshold not exceeding the article weight wins. Thresholds may overlap through the retained, unused max_weight field, but each numeric min_weight must be unique; multiple rows without a min_weight are allowed.

POST /assign returns assignment and change counts. When an article's package type or cost changes, its non-OOS channel details are marked update_needed and a background task force-recalculates min/max prices for every channel. For an all-SKU assignment, the response omits the potentially large changed-SKU list.

1:1 bundles: peers linked via parent child_skus ↔ alias bundle_packages (qty 1) always get the same package. Resolution uses all peer EANs for CSV first, then the max peer weight for fallback (then default S).

CSV (File Manager volume)

Stored as uploaded_files/package_mappings.csv (same Docker volume as Otto XLSX → File Manager multi_repricer_uploads).

Columns: Artikelname, GTIN (EAN), Name, Wert (semicolon / tab / comma; JTL quoted style supported).

Only rows with Name = Box_Past (or a usable package Wert) are used. Extra JTL attribute rows (Small / Large) are skipped so they do not overwrite the package code.

Method Route Purpose
POST /csv/upload Multipart upload
GET /csv/exists Existence + size + row count
GET /csv/preview Status + sample rows
GET /csv/download Download stored CSV

Wert → type

  • BOX-XS-* → XS, BOX-S-* → S, BOX-M-* → M, BOX-XL-* → XL (ignore trailing number)
  • BOX-L-1/2/3/5 → L1 / L2 / L3 / L5 (number matters)
  • NO-BOX → ignored (weight fallback, then default S)

For articles with multiple EANs, every CSV hit is considered: the largest real package type wins (XS < S < M < L1 < L2 < L3 < L5 < XL). NO-BOX does not block weight fallback.

SFTP

Passwords encrypted at rest (Fernet / SECRET_KEY), same pattern as Webshare.

Method Route Purpose
GET /sftp/ List credentials (password masked)
GET /sftp/active Active credential
POST /sftp/ Create
PUT /sftp/{id} Update (empty password keeps existing)
DELETE /sftp/{id} Delete
POST /sftp/{id}/activate Toggle active
POST /sftp/test Ad-hoc test (connection + selectable JTL/CSV)
POST /sftp/{id}/test Test saved credential
POST /sftp/refresh Download selected CSV via active credential
POST /sftp/{id}/download Download via specific credential

Fields: host, port, username, password, path (remote directory). When multiple .csv files exist, the download picks the one whose filename contains JTL (case-insensitive). If there is no JTL match but exactly one .csv, that file is used.

Base repricer

On each base sync (_sync_articles_internal):

  1. Refresh CSV from active SFTP (if configured)
  2. Assign: CSV by EAN → else weight fallback → else default S (1:1 peers share one package)

Otto recalculate also runs assignment when package_type is missing (Patch 1.11.0).