Skip to content

Commit e108ee7

Browse files
authored
fix: sync shipping-platform patches (rate sheets, connectors, graph, core) (#1068)
Exported from jtlshipping/shipping-platform main via ./bin/export-karrio-patches --force per PRDs/SUBTREE_SYNC_WORKFLOW.md. 1445 files across modules/connectors (579), modules/core (198), apps/api (160), modules/manager (113), modules/pricing (75), modules/sdk (55), modules/events (46), modules/orders (39), modules/data (36), modules/documents (28), modules/graph (25), modules/admin (24) + packages, plugins, scripts, and PRDs. Major functional additions from shipping-platform: - rate sheet Excel/CSV import/export (apps/, packages/, modules/data/) - Playwright e2e package scaffold (packages/e2e/) - dhl_parcel_de improvements (optional dims, reference fallback) - carrier i18n translation expansions (dhl_parcel_de, asendia) - ruff-format reflow across the tree from SP's linter config 54 three-way conflicts resolved per SUBTREE_SYNC_WORKFLOW.md matrix (see PR body for per-file decisions). Smartkargo, FedEx signature defaults, rate-sheet UX variants, and graph archive/unarchive kept at upstream (ours); carrier i18n dicts merged; e2e fixtures and de locale compiled catalog copied directly from SP. Pre-excluded from apply: 10 phantom-deletions of SP-experimental credential/secret modules that never reached upstream, and 5 binary-new files that needed direct copy (xlsx fixtures, django.mo). No JTL-proprietary modules in diff.
1 parent b699462 commit e108ee7

1,445 files changed

Lines changed: 41610 additions & 20154 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ jobs:
9292
uses: actions/setup-node@v4
9393
with:
9494
node-version: "20"
95+
cache: "npm"
9596

9697
- id: get_tag
9798
run: |
@@ -111,14 +112,14 @@ jobs:
111112
112113
# Build browser JS bundle
113114
cd packages/karriojs
114-
npm install
115+
npm ci
115116
npx gulp build --output "${GITHUB_WORKSPACE}/apps/api/karrio/server/static/karrio/js/karrio.js"
116117
cd -
117118
118119
- name: Build embeddable elements
119120
run: |
120121
cd packages/elements
121-
npm install
122+
npm ci
122123
npm run build
123124
124125
# Copy built elements to Django static directory
@@ -163,15 +164,28 @@ jobs:
163164
steps:
164165
- uses: actions/checkout@v4
165166

167+
- name: Set up Docker Buildx
168+
uses: docker/setup-buildx-action@v3
169+
166170
- id: get_tag
167171
run: |
168172
cat ./apps/api/karrio/server/VERSION
169173
echo "tag=$(cat ./apps/api/karrio/server/VERSION)" >> "$GITHUB_ENV"
170174
171175
- name: Build karrio dashboard image
172-
run: |
173-
echo 'Building karrio dashboard:${{ env.tag }}...'
174-
./bin/build-dashboard-image ${{ env.tag }}
176+
uses: docker/build-push-action@v6
177+
with:
178+
context: .
179+
file: ./docker/dashboard/Dockerfile
180+
push: false
181+
load: true
182+
tags: karrio/dashboard:${{ env.tag }}
183+
build-args: |
184+
VERSION=${{ env.tag }}
185+
NEXT_PUBLIC_DASHBOARD_VERSION=${{ env.tag }}
186+
SOURCE=https://github.com/karrioapi/karrio
187+
cache-from: type=gha,scope=dashboard
188+
cache-to: type=gha,mode=max,scope=dashboard
175189

176190
- name: Push karrio dashboard image
177191
run: |

.github/workflows/insiders-build.yml

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ jobs:
9595
uses: actions/setup-node@v4
9696
with:
9797
node-version: "20"
98+
cache: "npm"
9899

99100
- id: get_tag
100101
run: |
@@ -112,14 +113,14 @@ jobs:
112113
--additional-properties=useSingleRequestParameter=true
113114
114115
cd packages/karriojs
115-
npm install
116+
npm ci
116117
npx gulp build --output "${GITHUB_WORKSPACE}/apps/api/karrio/server/static/karrio/js/karrio.js"
117118
cd -
118119
119120
- name: Build embeddable elements
120121
run: |
121122
cd packages/elements
122-
npm install
123+
npm ci
123124
npm run build
124125
125126
ELEMENTS_STATIC="${GITHUB_WORKSPACE}/apps/api/karrio/server/static/karrio/elements"
@@ -156,14 +157,27 @@ jobs:
156157
submodules: recursive
157158
token: ${{ secrets.GH_PAT }}
158159

160+
- name: Set up Docker Buildx
161+
uses: docker/setup-buildx-action@v3
162+
159163
- id: get_tag
160164
run: |
161165
cat ./apps/api/karrio/server/VERSION
162166
echo "tag=$(cat ./apps/api/karrio/server/VERSION)" >> "$GITHUB_ENV"
163167
168+
- name: Login to GHCR
169+
run: echo ${{ secrets.GH_PAT }} | docker login ghcr.io -u USERNAME --password-stdin
170+
164171
- name: Build insider dashboard image
165-
run: |
166-
echo 'Build and push karrio-insiders dashboard:${{ env.tag }}...'
167-
echo ${{ secrets.GH_PAT }} | docker login ghcr.io -u USERNAME --password-stdin
168-
KARRIO_IMAGE=ghcr.io/karrioapi/dashboard SOURCE=https://github.com/karrioapi/karrio-insiders ./bin/build-dashboard-image ${{ env.tag }} &&
169-
docker push ghcr.io/karrioapi/dashboard:${{ env.tag }} || exit 1
172+
uses: docker/build-push-action@v6
173+
with:
174+
context: .
175+
file: ./docker/dashboard/Dockerfile
176+
push: true
177+
tags: ghcr.io/karrioapi/dashboard:${{ env.tag }}
178+
build-args: |
179+
VERSION=${{ env.tag }}
180+
NEXT_PUBLIC_DASHBOARD_VERSION=${{ env.tag }}
181+
SOURCE=https://github.com/karrioapi/karrio-insiders
182+
cache-from: type=gha,scope=insiders-dashboard
183+
cache-to: type=gha,mode=max,scope=insiders-dashboard

.github/workflows/platform-build.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
uses: actions/setup-node@v4
4242
with:
4343
node-version: "20"
44+
cache: "npm"
4445

4546
- id: get_tag
4647
run: |
@@ -57,14 +58,14 @@ jobs:
5758
--additional-properties=useSingleRequestParameter=true
5859
5960
cd packages/karriojs
60-
npm install
61+
npm ci
6162
npx gulp build --output "${GITHUB_WORKSPACE}/apps/api/karrio/server/static/karrio/js/karrio.js"
6263
cd -
6364
6465
- name: Build embeddable elements
6566
run: |
6667
cd packages/elements
67-
npm install
68+
npm ci
6869
npm run build
6970
7071
ELEMENTS_STATIC="${GITHUB_WORKSPACE}/apps/api/karrio/server/static/karrio/elements"
@@ -126,6 +127,12 @@ jobs:
126127
submodules: recursive
127128
token: ${{ secrets.GH_PAT }}
128129

130+
- name: Set up Node.js
131+
uses: actions/setup-node@v4
132+
with:
133+
node-version: "20"
134+
cache: "npm"
135+
129136
- name: Configure AWS credentials
130137
uses: aws-actions/configure-aws-credentials@v4
131138
with:
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# Rate Sheet Inventory + Manual Verification Report
2+
3+
**Status:** Verification round complete · 2026-04-14
4+
**Stack tested:** local (admin :3104, karrio :5002) with `admin@example.com / demo`
5+
**Source CSV:** `/Users/danielk/Downloads/JTL_Shipping_Master_FinalV4.xlsx`
6+
regenerated via `karrio/bin/regenerate-rate-sheets``~/Downloads/rate-sheets/DHL-DE.csv` (87 rows).
7+
8+
This is the post-implementation inventory. It documents every rate-sheet
9+
feature (frontend + backend), tags each one with verification status, and
10+
lists the regressions found + fixed during the manual Playwright walk.
11+
12+
---
13+
14+
## A. End-to-end verification — what works after this PR
15+
16+
### A.1 Import flow (Create new rate sheet)
17+
18+
| Step | Observed | Status |
19+
|---|---|---|
20+
| Click "Add Rate Sheet" → editor opens | sheet panel `Create Rate Sheet` renders with Edit/Import/Export tabs ||
21+
| Click "Import" tab | drop-zone + file picker visible ||
22+
| Upload `DHL-DE.csv` | dry-run validates: `87 unchanged, 0 added` (when reimporting) or `87 added` (fresh) | ✅ FIXED — was `7 unchanged` before this PR |
23+
| Diff table | one row per (service × zone × weight × options-bundle) variant — 87 distinct rows visible | ✅ FIXED |
24+
| Click "Confirm Import" | sheet closes, returns to list, dhl_parcel_de card shows "3 Services" ||
25+
| Persisted state (GraphQL) | 3 canonical services + 87 rate rows preserved with `meta.options` + `meta.shipping_method` per row | ✅ verified live |
26+
27+
### A.2 Persisted data shape (verified via `/admin/graphql` against the imported sheet)
28+
29+
```
30+
=== dhl_parcel_de (rsht_xxx) — 3 services, 87 rate rows ===
31+
Services:
32+
- dhl_parcel_de_retoure | DHL Retoure Online | metadata.shipping_method='DHL Retoure Online'
33+
- dhl_parcel_de_paket | DHL Paket | metadata.shipping_method='DHL Paket'
34+
- dhl_parcel_de_kleinpaket | DHL Kleinpaket | metadata.shipping_method='DHL Kleinpaket'
35+
36+
Service rates (excerpt — dhl_parcel_de_retoure, 3 variant rows at flat 0.001-31.501 kg):
37+
rate=6.19 options={} shipping_method='DHL Retoure Online'
38+
rate=6.19 options={'qr_code': True} shipping_method='DHL Retoure Online - QR Code'
39+
rate=6.34 options={'gogreen_plus': True, 'qr_code': True} shipping_method='DHL Retoure Online GoGreen Plus - QR Code'
40+
41+
Service rates (excerpt — dhl_parcel_de_kleinpaket, 2 variant rows):
42+
rate=3.39 options={} shipping_method='DHL KleinPaket'
43+
rate=3.49 options={'gogreen_plus': True} shipping_method='DHL Kleinpaket GoGreen Plus'
44+
45+
dhl_parcel_de_paket: 82 variant rate rows (5 weight buckets × ~16 option combos).
46+
```
47+
48+
This is the canonical model: **canonical service_codes, options as the variant discriminator, per-rate `shipping_method` for display.**
49+
50+
---
51+
52+
## B. Regressions found + fixed during this verification
53+
54+
### B.1 `compute_diff` collapsed variant rows (CRITICAL)
55+
**Symptom:** dry-run preview reported `7 unchanged` after importing the regenerated 87-row CSV. Variants were invisible.
56+
**Root cause:** `_rate_key` in `compute_diff` keyed on `(service, zone, st, min, max)` — same tuple for every variant. The `incoming_map` dict comprehension collapsed 87 rows into 7.
57+
**Fix:** `_rate_key` now returns a 6-tuple including a stable options fingerprint (sorted `key=value|...` from `rate.meta.options`). `_diff_row` exposes the fingerprint as `row['options']` and the per-variant `shipping_method` as `row['shipping_method']`.
58+
**Verified:** dry-run now reports `87 unchanged` on re-import. Variant identity surfaced in diff rows. Commit `6bc77ca0`.
59+
60+
### B.2 Service-level `shipping_method` polluted by arbitrary variant
61+
**Symptom:** `ServiceLevel.metadata.shipping_method` for the `dhl_parcel_de_paket` service was `"DHL Paket Visual Age Check 16+"` — an arbitrary variant name, not the canonical "DHL Paket".
62+
**Root cause:** `build_rate_sheet_input_from_flat` did first-row-wins for the service-level lift. With 22 rows mapping to one canonical service_code, the first row's variant name won.
63+
**Fix:** Service-level `shipping_method` now prefers the no-options (canonical) variant. Per-rate `meta.shipping_method` keeps each row's own variant name.
64+
**Verified:** service-level metadata now shows `"DHL Paket"`, `"DHL Kleinpaket"`, `"DHL Retoure Online"` — clean canonical names. Variant names ride per-row.
65+
66+
---
67+
68+
## C. Feature inventory — admin rate-sheet module
69+
70+
Tagged with verification status: ✅ verified working · 🟡 not yet exercised in this verification pass · ⚠️ known limitation/follow-up.
71+
72+
### C.1 Rate-sheet list page (`RateSheetsPage.tsx`)
73+
| Feature | Status | Notes |
74+
|---|---|---|
75+
| Card grid with carrier logo / counts / actions || dhl_parcel_de card shows "3 Services" after import |
76+
| Search by name/carrier (debounced 500ms) | 🟡 | not exercised |
77+
| Empty / loading / error states | 🟡 | not triggered |
78+
| Add Rate Sheet button → opens editor with `id=new` || |
79+
| Three-dot menu Edit / Delete | 🟡 | not exercised |
80+
81+
### C.2 Rate-sheet editor (`rate-sheet-editor.tsx`)
82+
| Feature | Status | Notes |
83+
|---|---|---|
84+
| Edit / Import / Export top tabs || |
85+
| Carrier dropdown, sheet name, currency, origin countries, weight/dimension units | 🟡 | not exercised in this pass |
86+
| Service tabs in left column | 🟡 | tabs render after import (see post-import editor screenshot) |
87+
| Save → CREATE_RATE_SHEET / UPDATE_RATE_SHEET mutation || indirect via import flow |
88+
89+
### C.3 Service editor dialog (`service-editor-dialog.tsx`)
90+
| Feature | Status | Notes |
91+
|---|---|---|
92+
| 6 tabs: General / Transit / Features / Logistics / Limits / Surcharges | 🟡 | not opened in this verification pass |
93+
| 14-flag features object incl. labelless / notification / address_validation || backend GraphQL `ServiceLevelFeaturesInput` accepts all 14 (regression test in PR) |
94+
95+
### C.4 Zones (`zone-editor-dialog.tsx`, `zones-tab.tsx`)
96+
| Feature | Status |
97+
|---|---|
98+
| Create / edit / delete zone | 🟡 not exercised |
99+
| Per-service zone assignment (zone_ids) | ✅ DHL-DE imports a single `DE` zone linked to all 3 services |
100+
101+
### C.5 Surcharges (`surcharges-tab.tsx`, `surcharge-editor-dialog.tsx`)
102+
| Feature | Status |
103+
|---|---|
104+
| Shared surcharge CRUD | 🟡 not exercised |
105+
| AMOUNT vs PERCENTAGE | 🟡 |
106+
107+
### C.6 Rate grid (`weight-rate-grid.tsx`, `service-rate-detail-view.tsx`)
108+
| Feature | Status |
109+
|---|---|
110+
| Editable cells | 🟡 not exercised |
111+
| Weight range CRUD | 🟡 |
112+
| Edit Service Rate dialog: Plans → Custom Margin (`meta.plan_costs[markup_id]`) | ✅ wiring verified end-to-end in #444; plan_cost columns of regenerated CSV emit `meta.plan_costs` correctly |
113+
| Excluded markups / surcharges per cell | 🟡 not exercised |
114+
115+
### C.7 Markups / Brokerage tab (`markups-tab.tsx`, `markup-editor-dialog.tsx`)
116+
| Feature | Status |
117+
|---|---|
118+
| Markup CRUD | 🟡 |
119+
| Scoping (carrier_codes, service_codes, connection_ids, organizations) | ✅ accepted by backend (verified earlier) |
120+
| Sheet-level `excluded_markup_ids` toggle | 🟡 |
121+
| Per-service exclusions | 🟡 |
122+
123+
### C.8 CSV import (`rate-sheet-import-panel.tsx`, `batch_rate_sheets.py`)
124+
| Feature | Status | Notes |
125+
|---|---|---|
126+
| Drag-drop OR file picker | ✅ file-picker path verified |
127+
| Dry-run preview with diff summary | ✅ FIXED — variant rows now visible |
128+
| `create_mode` flag forces unique slug | ✅ wired (this PR) |
129+
| Plan-cost override resolution (`plan_margin_<slug>_eur``rate.meta.plan_costs`) ||
130+
| Per-rate options (dynamic `option_*` columns) → `rate.meta.options` ||
131+
| Per-rate `shipping_method``rate.meta.shipping_method` ||
132+
| Service-level `shipping_method` lift (canonical only) | ✅ FIXED |
133+
| `notes` column kept as legacy alias ||
134+
| Duplicate-row validator includes options fingerprint ||
135+
136+
### C.9 CSV preview grid (`rate-sheet-csv-preview.tsx`)
137+
| Feature | Status |
138+
|---|---|
139+
| One row per variant bundle (rate-level granularity) | ✅ admin PR #58 (paired) |
140+
| Dynamic markup / surcharge columns | 🟡 not exercised in this pass |
141+
| Feature-gated markup toggles | 🟡 |
142+
143+
### C.10 Export (`export_rate_sheet_xlsx`)
144+
| Feature | Status |
145+
|---|---|
146+
| Round-trip safe export | 🟡 not exercised in this pass |
147+
| `shipping_method` column in output | ✅ in code; not verified live |
148+
| ⚠️ Doesn't yet emit `option_*` columns (pre-PR limitation) | ⚠️ deferred — re-import will lose variant identity |
149+
150+
### C.11 Rate resolver runtime
151+
| Feature | Status |
152+
|---|---|
153+
| `rate.meta.plan_costs[markup_id]` honored as override (vs default markup.amount) | ✅ verified in #444 with green tests |
154+
| Rate response includes `meta.shipping_method` per-rate | ✅ wired in #444 (universal SDK proxy + resolver) |
155+
| Options-based filtering (request `options.gogreen_plus=true` → matching variant rate) | ⚠️ not yet implemented in resolver — separate PR; today rates with options just coexist as siblings |
156+
157+
---
158+
159+
## D. APIs — endpoints relevant to rate sheets
160+
161+
### Admin GraphQL (`POST /admin/graphql`)
162+
163+
| Operation | Verified |
164+
|---|---|
165+
| `rate_sheets` query | ✅ used in this verification |
166+
| `rate_sheet` query ||
167+
| `create_rate_sheet` mutation | ✅ via import |
168+
| `update_rate_sheet` mutation | 🟡 not exercised in this pass |
169+
| `delete_rate_sheet` mutation | 🟡 |
170+
| Service / zone / surcharge / weight-range mutations | 🟡 |
171+
| `create_markup` / `update_markup` / `delete_markup` | 🟡 |
172+
173+
### Admin REST
174+
175+
| Endpoint | Verified |
176+
|---|---|
177+
| `POST /admin/batches/data/import` (multipart, dry_run / rate_sheet_id / create_mode) ||
178+
| `GET /v1/batches/data/export/rate_sheet.xlsx?id=…` | 🟡 |
179+
180+
### Token / auth
181+
182+
| Endpoint | Verified |
183+
|---|---|
184+
| `POST /api/token` (admin@example.com / demo) | ✅ used to obtain JWT for raw GraphQL inspection |
185+
186+
---
187+
188+
## E. What's still on the follow-up list (explicitly out of this PR)
189+
190+
1. **Rate resolver options filtering** — when a rate request includes `options.gogreen_plus=true`, the resolver should pick the variant rate row whose `meta.options` matches. Today multiple variant rows coexist but the resolver doesn't filter — picks "first match".
191+
2. **Other carriers in `CARRIER_SERVICE_MAP`** — only `dhl_parcel_de` is authored. Asendia, Chronopost, DPD, ParcelOne, UPS-DE, UPS-NL still fall back to slugified composite codes (and trip the duplicate validator).
192+
3. **Export path emits `option_*` columns** — current export path doesn't (yet) include the option columns, so re-import → variant identity lost.
193+
4. **Admin frontend `create_mode=true` body** — backend accepts it; admin UI still needs to send it from the import panel when `isEditMode=false`.
194+
5. **Session stability** (admin logout / drag-drop re-login) — separate from rate-sheet logic; tracked separately.
195+
196+
---
197+
198+
## F. Test consolidation plan (next step after this verification)
199+
200+
Now that the manual flow works end-to-end, write the following tests to lock it in:
201+
202+
1. `karrio.server.data.tests.test_rate_sheet_import`
203+
- `test_options_fingerprint_keeps_variants_distinct_in_diff` — incoming has 2 rows same key + diff options → diff has 2 rows.
204+
- `test_service_metadata_shipping_method_uses_canonical_no_options_variant`.
205+
- `test_dynamic_option_column_lifts_to_meta_options`.
206+
2. `admin/e2e/_manual-inspect.spec.ts` → split into focused specs:
207+
- `rate-sheet-import-canonical-services.spec.ts` (fresh import → 3 services + 87 rates).
208+
- `rate-sheet-import-dry-run-shows-variants.spec.ts` (re-import → 87 unchanged).
209+
3. End-to-end rate-quote integration test once resolver options filtering lands.
210+
211+
Until those tests exist, the manual `_manual-inspect.spec.ts` script (in the admin repo) is a reproducible verification harness — pointed at staging or local stack via env vars.

0 commit comments

Comments
 (0)