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_weightthresholds for CSV-miss fallback. The highest threshold not exceeding the article weight wins. Thresholds may overlap through the retained, unusedmax_weightfield, but each numericmin_weightmust be unique; multiple rows without amin_weightare 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 defaultS)
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):
- Refresh CSV from active SFTP (if configured)
- 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).