feat(26-manual-tracking-number): reczne dodawanie numeru przesylki do zamowienia
Nowy endpoint POST /orders/{id}/shipment/manual z formularzem inline
w zakladce Przesylki. Reuse tabeli shipment_packages (provider='manual',
status='created'). Activity log, walidacja CSRF, HTML required.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,7 @@ Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów sprzedaży i n
|
||||
- [x] Wyświetlanie nazwy integracji zamiast generycznego "shopPRO" na liście i szczegółach zamówienia — Phase 21
|
||||
- [x] Naprawa zapisu REGON, BDO, KRS i logo w ustawieniach firmy — Phase 22
|
||||
- [x] Presety przesyłek — customowe przyciski szybkiego wypełniania formularza (CRUD + autofill + zarządzanie) — Phase 23-25
|
||||
- [x] Ręczny numer przesyłki — dodawanie tracking number bez API przewoźnika — Phase 26
|
||||
|
||||
### Active (In Progress)
|
||||
|
||||
@@ -139,4 +140,4 @@ Quick Reference:
|
||||
|
||||
---
|
||||
*PROJECT.md — Updated when requirements or context change*
|
||||
*Last updated: 2026-03-22 after Phase 25 (Shipment Presets Management complete)*
|
||||
*Last updated: 2026-03-23 after Phase 26 (Manual Tracking Number complete)*
|
||||
|
||||
@@ -10,6 +10,19 @@ None — ready for next milestone planning.
|
||||
|
||||
## Completed Milestones
|
||||
|
||||
<details>
|
||||
<summary>v1.1 Ręczny numer przesyłki — 2026-03-23 (1 phase, 1 plan)</summary>
|
||||
|
||||
Możliwość ręcznego dodania numeru śledzenia przesyłki do zamówienia (bez tworzenia przesyłki przez API przewoźnika).
|
||||
|
||||
| Phase | Name | Plans | Completed |
|
||||
|-------|------|-------|-----------|
|
||||
| 26 | Manual Tracking Number | 1/1 | 2026-03-23 |
|
||||
|
||||
Archive: `.paul/phases/26-manual-tracking-number/`
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>v1.0 Presety przesyłek — 2026-03-22 (3 phases, 3 plans)</summary>
|
||||
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
See: .paul/PROJECT.md (updated 2026-03-12)
|
||||
|
||||
**Core value:** Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów sprzedaży i nadawać przesyłki bez przełączania się między platformami.
|
||||
**Current focus:** v1.0 Presety przesyłek — MILESTONE COMPLETE ✓
|
||||
**Current focus:** v1.1 Ręczny numer przesyłki — MILESTONE COMPLETE ✓
|
||||
|
||||
## Current Position
|
||||
|
||||
Milestone: v1.0 Presety przesyłek
|
||||
Milestone: v1.0 Presety przesyłek — COMPLETE ✓
|
||||
Phase: [3] of [3] (Shipment Presets Management) — COMPLETE ✓
|
||||
Plan: 25-01 — loop closed
|
||||
Status: Milestone v1.0 complete
|
||||
Last activity: 2026-03-22 — UNIFY complete, milestone v1.0 done
|
||||
Milestone: v1.1 Ręczny numer przesyłki — COMPLETE ✓
|
||||
Phase: [1] of [1] (Manual Tracking Number) — COMPLETE ✓
|
||||
Plan: 26-01 — loop closed
|
||||
Status: Milestone v1.1 complete
|
||||
Last activity: 2026-03-23 — UNIFY complete, milestone v1.1 done
|
||||
|
||||
Progress:
|
||||
- v0.1 Initial Release: [██████████] 100% ✓
|
||||
@@ -25,20 +24,17 @@ Progress:
|
||||
- v0.6 Poprawki UX: [██████████] 100% ✓
|
||||
- v0.7 Zdalne drukowanie etykiet: [██████████] 100% ✓
|
||||
- v0.8 Poprawki źródła zamówień: [██████████] 100% ✓
|
||||
- Phase 21: [██████████] 100% ✓ (1/1 plans)
|
||||
- v0.9 Poprawki ustawień firmy: [██████████] 100% ✓
|
||||
- Phase 22: [██████████] 100% ✓ (1/1 plans)
|
||||
- v1.0 Presety przesyłek: [██████████] 100% ✓
|
||||
- Phase 23: [██████████] 100% ✓ (1/1 plans)
|
||||
- Phase 24: [██████████] 100% ✓ (1/1 plans)
|
||||
- Phase 25: [██████████] 100% ✓ (1/1 plans)
|
||||
- v1.1 Ręczny numer przesyłki: [██████████] 100% ✓
|
||||
- Phase 26: [██████████] 100% ✓ (1/1 plans)
|
||||
|
||||
## Loop Position
|
||||
|
||||
Current loop state:
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ✓ ✓ [Milestone v1.0 complete]
|
||||
✓ ✓ ✓ [Milestone v1.1 complete]
|
||||
```
|
||||
|
||||
## Accumulated Context
|
||||
@@ -72,6 +68,11 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
| 2026-03-17 | Email history jako wpisy w order_activity_log (nie osobna sekcja) | Faza 15 | Spójność z istniejącym UX — jeden timeline zamiast fragmentacji |
|
||||
| 2026-03-17 | VariableResolver wydzielony z EmailTemplateController | Faza 15 | Reuse logiki zmiennych; resolwer niezależny od kontrolera szablonów |
|
||||
|
||||
### Skill Audit (Faza 26, Plan 01)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
| sonar-scanner | ✓ | 0 nowych issues; 8 pre-existing na ShipmentController (S3776, S1192, S1142, S3358) |
|
||||
|
||||
### Skill Audit (Faza 25, Plan 01)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
@@ -225,10 +226,10 @@ Brak.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-03-22
|
||||
Stopped at: Milestone v1.0 complete
|
||||
Last session: 2026-03-23
|
||||
Stopped at: Milestone v1.1 complete
|
||||
Next action: /paul:discuss-milestone lub /paul:milestone
|
||||
Resume file: .paul/phases/25-shipment-presets-management/25-01-SUMMARY.md
|
||||
Resume file: .paul/phases/26-manual-tracking-number/26-01-SUMMARY.md
|
||||
Resume context:
|
||||
- v0.1: COMPLETE ✓ (6 phases, 15 plans)
|
||||
- v0.2: COMPLETE ✓ (1 phase, 5 plans)
|
||||
@@ -240,6 +241,7 @@ Resume context:
|
||||
- v0.8: COMPLETE ✓ (1 phase, 1 plan) — Poprawki źródła zamówień
|
||||
- v0.9: COMPLETE ✓ (1 phase, 1 plan) — Poprawki ustawień firmy
|
||||
- v1.0: COMPLETE ✓ (3 phases, 3 plans) — Presety przesyłek
|
||||
- v1.1: IN PROGRESS (1 phase, 1 plan) — Ręczny numer przesyłki
|
||||
|
||||
---
|
||||
*STATE.md — Updated after every significant action*
|
||||
|
||||
193
.paul/phases/26-manual-tracking-number/26-01-PLAN.md
Normal file
193
.paul/phases/26-manual-tracking-number/26-01-PLAN.md
Normal file
@@ -0,0 +1,193 @@
|
||||
---
|
||||
phase: 26-manual-tracking-number
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/Modules/Shipments/ShipmentController.php
|
||||
- src/Modules/Shipments/ShipmentPackageRepository.php
|
||||
- resources/views/orders/show.php
|
||||
- resources/scss/pages/_orders.scss
|
||||
- routes/web.php
|
||||
- DOCS/ARCHITECTURE.md
|
||||
- DOCS/TECH_CHANGELOG.md
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dodanie możliwości ręcznego wpisania numeru przesyłki (tracking number) do zamówienia — bez tworzenia przesyłki przez API przewoźnika.
|
||||
|
||||
## Purpose
|
||||
Sprzedawca często nadaje paczki poza systemem (np. na poczcie, przez innego kuriera bez integracji API). Potrzebuje zapisać numer śledzenia przy zamówieniu, żeby mieć komplet informacji w jednym miejscu.
|
||||
|
||||
## Output
|
||||
- Nowy endpoint POST `/orders/{id}/shipment/manual` w ShipmentController
|
||||
- Formularz inline w zakładce Przesyłki na stronie zamówienia
|
||||
- Rekord w `shipment_packages` z provider='manual', status='created'
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Shipments/ShipmentController.php
|
||||
@src/Modules/Shipments/ShipmentPackageRepository.php
|
||||
@resources/views/orders/show.php
|
||||
@routes/web.php
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills (from SPECIAL-FLOWS.md)
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] sonar-scanner loaded (run command or confirm)
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Ręczne dodanie numeru przesyłki
|
||||
```gherkin
|
||||
Given zamówienie istnieje i użytkownik jest na stronie szczegółów zamówienia
|
||||
When użytkownik wpisuje numer przesyłki i opcjonalnie nazwę przewoźnika w formularzu inline, i klika "Dodaj"
|
||||
Then w tabeli shipment_packages powstaje rekord z provider='manual', status='created', tracking_number=wpisany numer
|
||||
And numer pojawia się na liście przesyłek zamówienia
|
||||
And wpis pojawia się w activity log zamówienia
|
||||
```
|
||||
|
||||
## AC-2: Walidacja — pusty numer
|
||||
```gherkin
|
||||
Given użytkownik jest na stronie szczegółów zamówienia
|
||||
When użytkownik klika "Dodaj" bez wpisania numeru przesyłki
|
||||
Then formularz nie jest wysyłany (walidacja HTML required)
|
||||
And żaden rekord nie jest tworzony
|
||||
```
|
||||
|
||||
## AC-3: Wyświetlanie ręcznych przesyłek na liście
|
||||
```gherkin
|
||||
Given zamówienie ma ręcznie dodany numer przesyłki
|
||||
When użytkownik otwiera stronę szczegółów zamówienia
|
||||
Then na liście przesyłek widoczny jest rekord z etykietą "Ręczna" w kolumnie Przewoźnik
|
||||
And tracking number jest wyświetlany
|
||||
And kolumna Etykieta pokazuje "—" (brak etykiety dla ręcznych)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Metoda createManual w ShipmentPackageRepository + endpoint w ShipmentController</name>
|
||||
<files>src/Modules/Shipments/ShipmentPackageRepository.php, src/Modules/Shipments/ShipmentController.php, routes/web.php</files>
|
||||
<action>
|
||||
1. W ShipmentPackageRepository dodać metodę `createManual(int $orderId, string $trackingNumber, ?string $carrierName = null): int`:
|
||||
- INSERT do shipment_packages z: order_id, provider='manual', tracking_number, carrier_id=carrierName (lub null), status='created', reference_number=null
|
||||
- Zwraca ID nowego rekordu
|
||||
|
||||
2. W ShipmentController dodać metodę `createManual(Request $request): Response`:
|
||||
- Walidacja CSRF
|
||||
- Pobranie orderId z URL, tracking_number i carrier_name z POST
|
||||
- Walidacja: tracking_number nie może być pusty (trim)
|
||||
- Wywołanie $this->packageRepository->createManual(...)
|
||||
- recordActivity na ordersRepository: typ 'shipment_manual', opis z numerem i przewoźnikiem
|
||||
- Flash success + redirect do /orders/{id}
|
||||
|
||||
3. W routes/web.php dodać route:
|
||||
- POST /orders/{id}/shipment/manual → [$shipmentController, 'createManual']
|
||||
- Z $authMiddleware
|
||||
</action>
|
||||
<verify>
|
||||
POST na /orders/{id}/shipment/manual z tracking_number tworzy rekord w shipment_packages i redirectuje z flash success.
|
||||
</verify>
|
||||
<done>AC-1 satisfied: ręczne dodanie numeru przesyłki działa end-to-end</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Formularz inline + wyświetlanie ręcznych przesyłek w widoku zamówienia</name>
|
||||
<files>resources/views/orders/show.php, resources/scss/pages/_orders.scss</files>
|
||||
<action>
|
||||
1. W resources/views/orders/show.php, w sekcji zakładki Przesyłki (tab "shipments"), dodać kompaktowy formularz inline pod listą przesyłek:
|
||||
- Jeden wiersz: input text "Nr przesyłki" (required), input text "Przewoźnik" (opcjonalny), przycisk "Dodaj"
|
||||
- form method POST action="/orders/{id}/shipment/manual"
|
||||
- Ukryte pole _token z CSRF
|
||||
- Klasa CSS: `manual-tracking-form`
|
||||
|
||||
2. W tabeli przesyłek (packagesList foreach), dla rekordu z provider='manual':
|
||||
- W kolumnie "Przewoźnik": wyświetlić carrier_id jeśli niepuste, inaczej "Ręczna"
|
||||
- W kolumnie "Etykieta": wyświetlić "—"
|
||||
- W kolumnie "Status": wyświetlić badge "Dodana ręcznie" (klasa `badge badge--neutral`)
|
||||
|
||||
3. W resources/scss/pages/_orders.scss dodać style dla `.manual-tracking-form`:
|
||||
- display: flex, gap: 8px, align-items: center
|
||||
- margin-top: 12px
|
||||
- Inputy: standardowe style projektu
|
||||
</action>
|
||||
<verify>
|
||||
Na stronie szczegółów zamówienia widoczny jest formularz dodawania ręcznego numeru. Po dodaniu, numer wyświetla się na liście z oznaczeniem "Ręczna".
|
||||
</verify>
|
||||
<done>AC-2, AC-3 satisfied: walidacja formularza i wyświetlanie ręcznych przesyłek</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Aktualizacja dokumentacji</name>
|
||||
<files>DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md</files>
|
||||
<action>
|
||||
1. W DOCS/ARCHITECTURE.md:
|
||||
- W sekcji ShipmentController dodać metodę createManual
|
||||
- W sekcji ShipmentPackageRepository dodać metodę createManual
|
||||
|
||||
2. W DOCS/TECH_CHANGELOG.md:
|
||||
- Dodać wpis o nowej funkcjonalności ręcznego numeru przesyłki
|
||||
</action>
|
||||
<verify>Dokumentacja zaktualizowana zgodnie z nowymi endpointami.</verify>
|
||||
<done>Dokumentacja odzwierciedla nową funkcjonalność</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- database/migrations/* (nie trzeba nowej migracji — tabela shipment_packages już ma wszystkie potrzebne kolumny)
|
||||
- src/Modules/Shipments/AllegroShipmentService.php
|
||||
- src/Modules/Shipments/ApaczkaShipmentService.php
|
||||
- src/Modules/Shipments/InpostShipmentService.php
|
||||
- src/Modules/Shipments/ShipmentProviderInterface.php
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie dodawać usuwania/edycji ręcznych przesyłek (oddzielna faza jeśli potrzebne)
|
||||
- Nie modyfikować flow tworzenia przesyłek przez API przewoźników
|
||||
- Nie dodawać nowych zależności composer
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] POST /orders/{id}/shipment/manual tworzy rekord w shipment_packages
|
||||
- [ ] Rekord ma provider='manual', status='created', poprawny tracking_number
|
||||
- [ ] Activity log zawiera wpis o ręcznym dodaniu
|
||||
- [ ] Formularz inline widoczny w zakładce Przesyłki
|
||||
- [ ] Walidacja HTML required na polu tracking_number
|
||||
- [ ] Ręczne przesyłki wyświetlają się poprawnie na liście (Przewoźnik, Status, Etykieta)
|
||||
- [ ] CSRF protection działa
|
||||
- [ ] Brak błędów PHP
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie taski zakończone
|
||||
- Wszystkie weryfikacje przechodzą
|
||||
- Brak nowych błędów PHP ani JS
|
||||
- Dokumentacja zaktualizowana
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/26-manual-tracking-number/26-01-SUMMARY.md`
|
||||
</output>
|
||||
109
.paul/phases/26-manual-tracking-number/26-01-SUMMARY.md
Normal file
109
.paul/phases/26-manual-tracking-number/26-01-SUMMARY.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
phase: 26-manual-tracking-number
|
||||
plan: 01
|
||||
subsystem: shipments
|
||||
tags: [tracking-number, manual-entry, shipment-packages]
|
||||
|
||||
requires:
|
||||
- phase: 25-shipment-presets-management
|
||||
provides: shipment UI in order details
|
||||
provides:
|
||||
- manual tracking number entry endpoint
|
||||
- inline form in order detail shipments tab
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [provider='manual' for non-API shipments]
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/Modules/Shipments/ShipmentPackageRepository.php
|
||||
- src/Modules/Shipments/ShipmentController.php
|
||||
- routes/web.php
|
||||
- resources/views/orders/show.php
|
||||
- resources/scss/app.scss
|
||||
|
||||
key-decisions:
|
||||
- "Reuse shipment_packages table with provider='manual' instead of new table"
|
||||
- "Inline form in shipments tab instead of modal"
|
||||
|
||||
patterns-established:
|
||||
- "provider='manual' pattern for non-API tracking entries"
|
||||
|
||||
duration: ~10min
|
||||
completed: 2026-03-23
|
||||
---
|
||||
|
||||
# Phase 26 Plan 01: Manual Tracking Number Summary
|
||||
|
||||
**Endpoint POST /orders/{id}/shipment/manual z formularzem inline — ręczne dodawanie numerów śledzenia przesyłek do zamówień**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~10min |
|
||||
| Completed | 2026-03-23 |
|
||||
| Tasks | 3 completed |
|
||||
| Files modified | 7 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Ręczne dodanie numeru przesyłki | Pass | Endpoint tworzy rekord, activity log, flash success |
|
||||
| AC-2: Walidacja — pusty numer | Pass | HTML required + server-side trim check |
|
||||
| AC-3: Wyświetlanie ręcznych przesyłek | Pass | Status "Dodana recznie", przewoźnik z carrier_id, etykieta "—" |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Nowy endpoint `POST /orders/{id}/shipment/manual` z walidacją CSRF i tracking_number
|
||||
- Metoda `ShipmentPackageRepository::createManual()` — INSERT z provider='manual', status='created'
|
||||
- Formularz inline w zakładce Przesyłki + zmienione renderowanie dla ręcznych przesyłek
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Shipments/ShipmentPackageRepository.php` | Modified | Nowa metoda createManual() |
|
||||
| `src/Modules/Shipments/ShipmentController.php` | Modified | Nowa metoda createManual() — endpoint |
|
||||
| `routes/web.php` | Modified | Nowa route POST /orders/{id}/shipment/manual |
|
||||
| `resources/views/orders/show.php` | Modified | Formularz inline + wyświetlanie ręcznych przesyłek |
|
||||
| `resources/scss/app.scss` | Modified | Klasa .manual-tracking-form |
|
||||
| `public/assets/css/app.css` | Rebuilt | Skompilowany SCSS |
|
||||
| `DOCS/ARCHITECTURE.md` | Modified | Dokumentacja nowego endpointu |
|
||||
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 26 |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Reuse shipment_packages z provider='manual' | Tabela ma już tracking_number, carrier_id, status — nie trzeba nowej migracji | Zero zmian DB |
|
||||
| Formularz inline zamiast modala | Szybszy dostęp, mniej kliknięć, spójny z kompaktowym UI | Prostsze UX |
|
||||
| carrier_id jako nazwa przewoźnika dla manual | Pole VARCHAR już istnieje, semantycznie pasuje | Brak zmian schematu |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Ręczne numery przesyłek w pełni funkcjonalne
|
||||
- Milestone v1.1 gotowy do zamknięcia
|
||||
|
||||
**Concerns:**
|
||||
- Brak możliwości edycji/usunięcia ręcznych przesyłek (celowo poza scope)
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 26-manual-tracking-number, Plan: 01*
|
||||
*Completed: 2026-03-23*
|
||||
@@ -318,6 +318,12 @@
|
||||
- `ShipmentController::label(Request): Response`,
|
||||
- wybiera providera po `shipment_packages.provider` i deleguje `downloadLabel(...)`,
|
||||
- dla Apaczka bledy typu `Label is not available for this order` oznaczaja paczke jako `error`, aby nie ponawiac nieskutecznych prob pobrania.
|
||||
- `POST /orders/{id}/shipment/manual`:
|
||||
- `ShipmentController::createManual(Request): Response`,
|
||||
- tworzy rekord w `shipment_packages` z `provider='manual'`, `status='created'` i podanym `tracking_number`,
|
||||
- opcjonalnie zapisuje nazwe przewoznika w `carrier_id`,
|
||||
- loguje zdarzenie `shipment_manual` w `order_activity_log`,
|
||||
- `ShipmentPackageRepository::createManual(int $orderId, string $trackingNumber, ?string $carrierName): int`.
|
||||
|
||||
## Przeplyw Ustawienia > Integracje > Allegro
|
||||
- `GET /settings/integrations/allegro`:
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Tech Changelog
|
||||
|
||||
## 2026-03-23 (Phase 26 — Manual Tracking Number, Plan 01)
|
||||
- Nowa metoda `ShipmentPackageRepository::createManual(int, string, ?string): int` — INSERT do `shipment_packages` z `provider='manual'`, `status='created'`.
|
||||
- Nowa metoda `ShipmentController::createManual(Request): Response` — endpoint `POST /orders/{id}/shipment/manual`, walidacja CSRF + tracking_number, activity log `shipment_manual`.
|
||||
- Nowa route w `routes/web.php`: `POST /orders/{id}/shipment/manual`.
|
||||
- `resources/views/orders/show.php` — formularz inline do dodawania recznego numeru przesylki w zakladce Przesylki; zmienione wyswietlanie przesylek manualnych (status "Dodana recznie", przewoznik z carrier_id, brak etykiety).
|
||||
- `resources/scss/app.scss` — nowa klasa `.manual-tracking-form` (flex, gap 8px).
|
||||
|
||||
## 2026-03-22 (Phase 21 — Order Source Display, Plan 01)
|
||||
- `OrdersRepository::buildListSql()` — LEFT JOIN `integrations ig ON ig.id = o.integration_id`, nowa kolumna `ig.name AS integration_name`.
|
||||
- `OrdersRepository::findDetails()` — analogiczny LEFT JOIN dla strony szczegolow zamowienia.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1574,6 +1574,16 @@ h4.section-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.manual-tracking-form {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.form-control {
|
||||
max-width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
.order-empty-placeholder {
|
||||
border: 1px dashed #cbd5e1;
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -384,7 +384,8 @@ foreach ($addressesList as $address) {
|
||||
$pkgTracking = trim((string) ($pkg['tracking_number'] ?? ''));
|
||||
$pkgCarrierId = trim((string) ($pkg['carrier_id'] ?? ''));
|
||||
$pkgProvider = trim((string) ($pkg['provider'] ?? ''));
|
||||
$providerLabels = ['apaczka' => 'Apaczka', 'allegro_wza' => 'Allegro', 'inpost' => 'InPost'];
|
||||
$isManual = $pkgProvider === 'manual';
|
||||
$providerLabels = ['apaczka' => 'Apaczka', 'allegro_wza' => 'Allegro', 'inpost' => 'InPost', 'manual' => 'Reczna'];
|
||||
$pkgProviderLabel = $providerLabels[$pkgProvider] ?? $pkgProvider;
|
||||
$pkgCarrier = $pkgCarrierId !== '' ? ($pkgProviderLabel . ' → ' . $pkgCarrierId) : $pkgProviderLabel;
|
||||
$pkgLabelPath = trim((string) ($pkg['label_path'] ?? ''));
|
||||
@@ -396,16 +397,23 @@ foreach ($addressesList as $address) {
|
||||
<tr>
|
||||
<td><?= $e((string) ($pkg['id'] ?? '')) ?></td>
|
||||
<td>
|
||||
<?php if ($isManual): ?>
|
||||
<span class="order-tag is-neutral">Dodana recznie</span>
|
||||
<?php else: ?>
|
||||
<span class="order-tag <?= $pkgStatus === 'label_ready' || $pkgStatus === 'created' ? 'is-success' : ($pkgStatus === 'error' ? 'is-danger' : 'is-warn') ?>">
|
||||
<?= $e($pkgStatus) ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if ($pkgError !== ''): ?>
|
||||
<div class="muted mt-4" style="font-size:0.75rem"><?= $e($pkgError) ?></div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= $e($pkgTracking !== '' ? $pkgTracking : '-') ?></td>
|
||||
<td><?php if ($pkgCarrierId !== ''): ?><?= $e($pkgProviderLabel) ?> → <?= $e($pkgCarrierId) ?><?php elseif ($pkgProviderLabel !== ''): ?><?= $e($pkgProviderLabel) ?><?php else: ?>-<?php endif; ?></td>
|
||||
<td><?php if ($isManual): ?><?= $e($pkgCarrierId !== '' ? $pkgCarrierId : 'Reczna') ?><?php elseif ($pkgCarrierId !== ''): ?><?= $e($pkgProviderLabel) ?> → <?= $e($pkgCarrierId) ?><?php elseif ($pkgProviderLabel !== ''): ?><?= $e($pkgProviderLabel) ?><?php else: ?>-<?php endif; ?></td>
|
||||
<td>
|
||||
<?php if ($isManual): ?>
|
||||
—
|
||||
<?php else: ?>
|
||||
<span style="display:inline-flex;gap:4px;align-items:center">
|
||||
<?php if ($pkgLabelPath !== '' && $pkgStatus !== 'error'): ?>
|
||||
<a href="/orders/<?= $e((string) ($orderId ?? 0)) ?>/shipment/prepare" class="btn btn--sm btn--secondary">Pobierz</a>
|
||||
@@ -424,6 +432,7 @@ foreach ($addressesList as $address) {
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-nowrap"><?= $e((string) ($pkg['created_at'] ?? '')) ?></td>
|
||||
</tr>
|
||||
@@ -466,6 +475,16 @@ foreach ($addressesList as $address) {
|
||||
<p class="muted mt-12">Brak przesylek dla tego zamowienia.</p>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="card mt-16">
|
||||
<h3 class="section-title">Dodaj reczny numer przesylki</h3>
|
||||
<form method="POST" action="/orders/<?= $e((string) ($orderId ?? 0)) ?>/shipment/manual" class="manual-tracking-form mt-12">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="text" name="tracking_number" placeholder="Nr przesylki" required class="form-control">
|
||||
<input type="text" name="carrier_name" placeholder="Przewoznik (opcjonalnie)" class="form-control">
|
||||
<button type="submit" class="btn btn--primary">Dodaj</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="order-tab-panel" data-order-tab-panel="payments">
|
||||
|
||||
@@ -400,6 +400,7 @@ return static function (Application $app): void {
|
||||
$router->post('/orders/{id}/shipment/create', [$shipmentController, 'create'], [$authMiddleware]);
|
||||
$router->get('/orders/{id}/shipment/{packageId}/status', [$shipmentController, 'checkStatus'], [$authMiddleware]);
|
||||
$router->post('/orders/{id}/shipment/{packageId}/label', [$shipmentController, 'label'], [$authMiddleware]);
|
||||
$router->post('/orders/{id}/shipment/manual', [$shipmentController, 'createManual'], [$authMiddleware]);
|
||||
|
||||
// --- Printing module ---
|
||||
$printApiKeyRepository = new PrintApiKeyRepository($app->db());
|
||||
|
||||
@@ -338,6 +338,54 @@ final class ShipmentController
|
||||
return Response::redirect('/orders/' . $orderId . '/shipment/prepare');
|
||||
}
|
||||
|
||||
public function createManual(Request $request): Response
|
||||
{
|
||||
$orderId = max(0, (int) $request->input('id', 0));
|
||||
if ($orderId <= 0) {
|
||||
return Response::html('Not found', 404);
|
||||
}
|
||||
|
||||
$csrfToken = (string) $request->input('_token', '');
|
||||
if (!Csrf::validate($csrfToken)) {
|
||||
Flash::set('shipment.error', $this->translator->get('auth.errors.csrf_expired'));
|
||||
return Response::redirect('/orders/' . $orderId);
|
||||
}
|
||||
|
||||
$trackingNumber = trim((string) $request->input('tracking_number', ''));
|
||||
if ($trackingNumber === '') {
|
||||
Flash::set('order.error', 'Numer przesylki jest wymagany.');
|
||||
return Response::redirect('/orders/' . $orderId);
|
||||
}
|
||||
|
||||
$carrierName = trim((string) $request->input('carrier_name', ''));
|
||||
$user = $this->auth->user();
|
||||
$actorName = is_array($user) ? trim((string) ($user['name'] ?? $user['email'] ?? '')) : null;
|
||||
$actorName = ($actorName !== null && $actorName !== '') ? $actorName : null;
|
||||
|
||||
$packageId = $this->packageRepository->createManual(
|
||||
$orderId,
|
||||
$trackingNumber,
|
||||
$carrierName !== '' ? $carrierName : null
|
||||
);
|
||||
|
||||
$description = 'Dodano reczny numer przesylki: ' . $trackingNumber;
|
||||
if ($carrierName !== '') {
|
||||
$description .= ' (' . $carrierName . ')';
|
||||
}
|
||||
|
||||
$this->ordersRepository->recordActivity(
|
||||
$orderId,
|
||||
'shipment_manual',
|
||||
$description,
|
||||
['package_id' => $packageId, 'tracking_number' => $trackingNumber, 'carrier_name' => $carrierName],
|
||||
'user',
|
||||
$actorName
|
||||
);
|
||||
|
||||
Flash::set('order.success', 'Numer przesylki dodany.');
|
||||
return Response::redirect('/orders/' . $orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|null $deliveryAddr
|
||||
* @param array<string, mixed>|null $customerAddr
|
||||
|
||||
@@ -121,6 +121,24 @@ final class ShipmentPackageRepository
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function createManual(int $orderId, string $trackingNumber, ?string $carrierName = null): int
|
||||
{
|
||||
$statement = $this->pdo->prepare(
|
||||
'INSERT INTO shipment_packages (order_id, provider, tracking_number, carrier_id, status)
|
||||
VALUES (:order_id, :provider, :tracking_number, :carrier_id, :status)'
|
||||
);
|
||||
|
||||
$statement->execute([
|
||||
'order_id' => $orderId,
|
||||
'provider' => 'manual',
|
||||
'tracking_number' => $trackingNumber,
|
||||
'carrier_id' => $carrierName !== null && trim($carrierName) !== '' ? trim($carrierName) : null,
|
||||
'status' => 'created',
|
||||
]);
|
||||
|
||||
return (int) $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
public function findByCommandId(string $commandId): ?array
|
||||
{
|
||||
$statement = $this->pdo->prepare('SELECT * FROM shipment_packages WHERE command_id = :command_id LIMIT 1');
|
||||
|
||||
Reference in New Issue
Block a user