update
This commit is contained in:
@@ -16,3 +16,8 @@ DB_DATABASE=orderpro
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
DB_CHARSET=utf8mb4
|
||||
|
||||
# SSL/TLS — sciezka do CA bundle dla cURL
|
||||
# Windows XAMPP: C:/xampp/php/extras/ssl/cacert.pem
|
||||
# Linux: /etc/ssl/certs/ca-certificates.crt
|
||||
CURL_CA_BUNDLE_PATH=
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
# PAUL Handoff
|
||||
|
||||
**Date:** 2026-03-13
|
||||
**Status:** paused — session complete, plans ready for execution
|
||||
|
||||
---
|
||||
|
||||
## READ THIS FIRST
|
||||
|
||||
You have no prior context. This document tells you everything.
|
||||
|
||||
**Project:** orderPRO — aplikacja do zarządzania zamówieniami z wielu źródeł sprzedaży (Allegro, Erli, shopPRO) z generowaniem etykiet przewozowych.
|
||||
**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 State
|
||||
|
||||
**Milestone:** v0.2 Pre-Expansion Fixes
|
||||
**Phase:** 7 — pre-expansion-fixes
|
||||
**Plan:** 07-01..07-05 CREATED, żaden nie wykonany
|
||||
|
||||
**Loop Position:**
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ○ ○ [Plan gotowy — czeka na APPLY]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Was Done (ta sesja)
|
||||
|
||||
- UNIFY 06-05: god classes split — ShopproOrdersSyncService 39→9 metod, AllegroIntegrationController 35→25 metod
|
||||
- Faza 06 zamknięta: 6/6 planów, SonarQube quality baseline
|
||||
- /paul:complete-milestone: v0.1 Initial Release zamknięty, git tag v0.1.0
|
||||
- CONCERNS.md skategoryzowany — "przed rozbudową" vs "odroczić"
|
||||
- Faza 07 zaplanowana: 5 planów (07-01..07-05) gotowe do APPLY
|
||||
|
||||
---
|
||||
|
||||
## What's Next
|
||||
|
||||
**Immediate:** `/paul:apply .paul/phases/07-pre-expansion-fixes/07-01-PLAN.md`
|
||||
|
||||
Kolejność:
|
||||
1. 07-01 — autonomiczny (Performance: N+1, static cache, DB indexes)
|
||||
2. 07-02 — autonomiczny (SSL verify, cron→DB, migration 000014b)
|
||||
3. 07-03 — ma checkpoint:human-verify (UX: disable orderpro_to_allegro, UI items 14-17)
|
||||
4. 07-04 — autonomiczny (Tests: AllegroTokenManager + import)
|
||||
5. 07-05 — ma checkpoint:decision (InPost ShipmentProviderInterface)
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `.paul/STATE.md` | Live project state |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-01-PLAN.md` | Performance fixes |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-02-PLAN.md` | SSL + cron + migration |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-03-PLAN.md` | UX fixes (checkpoint) |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-04-PLAN.md` | Unit tests |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-05-PLAN.md` | InPost provider (checkpoint:decision) |
|
||||
|
||||
---
|
||||
|
||||
## Resume Instructions
|
||||
|
||||
1. `/paul:resume` — odczyta STATE.md i pokaże aktualny stan
|
||||
2. Zatwierdź: `/paul:apply .paul/phases/07-pre-expansion-fixes/07-01-PLAN.md`
|
||||
|
||||
---
|
||||
|
||||
*Handoff created: 2026-03-13*
|
||||
@@ -5,26 +5,26 @@
|
||||
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:** Faza 07 — Pre-Expansion Fixes. 5 planów utworzonych, gotowe do APPLY.
|
||||
**Current focus:** Faza 07 — Pre-Expansion Fixes. Plan 07-03 UNIFY complete, 07-04 następny.
|
||||
|
||||
## Current Position
|
||||
|
||||
Milestone: v0.2 Pre-Expansion Fixes
|
||||
Phase: 7 of TBD (07-pre-expansion-fixes) — Planning
|
||||
Plan: 07-01 do 07-05 CREATED, awaiting approval
|
||||
Status: PLANy gotowe — wybrać plan do APPLY
|
||||
Last activity: 2026-03-13 — Faza 07 zaplanowana (5 planów)
|
||||
Phase: 7 of TBD (07-pre-expansion-fixes) — Executing
|
||||
Plan: 07-01 ✓, 07-02 ✓, 07-03 ✓, 07-04..07-05 awaiting
|
||||
Status: Loop 07-03 zamknięty — następny /paul:apply 07-04
|
||||
Last activity: 2026-03-14 — Plan 07-03 loop closed (UX fixes + SSL hotfix)
|
||||
|
||||
Progress:
|
||||
- v0.1 Initial Release: [██████████] 100% ✓
|
||||
- v0.2 Pre-Expansion Fixes: [░░░░░░░░░░] 0% (0/5 planów)
|
||||
- v0.2 Pre-Expansion Fixes: [██████░░░░] 60% (3/5 planów)
|
||||
|
||||
## Loop Position
|
||||
|
||||
Current loop state:
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ○ ○ [07-01 plan gotowy — zacznij od /paul:apply 07-01]
|
||||
✓ ✓ ✓ [07-03 complete — next: /paul:apply 07-04]
|
||||
```
|
||||
|
||||
## Accumulated Context
|
||||
@@ -39,6 +39,21 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
| 2026-03-13 | Flash messages: Flash::set('module.type') / Flash::get('module.type', '') | Faza 05 | OrdersController i ShipmentController zmigrowane; jeden wzorzec w całej aplikacji |
|
||||
| 2026-03-13 | validateXxxInput(): ?string i validateXxxAccess(): ?Response jako wzorce helperów walidacji | Faza 06 | Redukcja return statements do ≤3; wzorzec do użycia w kolejnych planach |
|
||||
|
||||
### Skill Audit (Faza 07, Plan 03)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
| sonar-scanner | ○ | Pominięto — brak instalacji w PATH |
|
||||
|
||||
### Skill Audit (Faza 07, Plan 02)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
| sonar-scanner | ○ | Pominięto — brak instalacji w PATH |
|
||||
|
||||
### Skill Audit (Faza 07, Plan 01)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
| sonar-scanner | ○ | Pominięto — brak instalacji w PATH |
|
||||
|
||||
### Skill Audit (Faza 06, Plan 06)
|
||||
| Oczekiwany | Wywołany | Uwagi |
|
||||
|------------|---------|-------|
|
||||
@@ -80,6 +95,7 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
### Deferred Issues
|
||||
- **CI/CD SonarQube** — dodać GitHub Actions workflow (`.github/workflows/sonarqube.yml`) który odpala `sonar-scanner` automatycznie przy każdym pushu. Token projektu: `sqp_8ef2748d037777cf00cf1b38534f8d435b762d7d` (dodać jako GitHub Secret `SONAR_TOKEN`). Przypisać do fazy związanej z infrastrukturą/DevOps gdy tylko fazy zostaną zdefiniowane.
|
||||
- **code-review** — wywołać /code-review przed kolejnym UNIFY (pominięto w obydwu planach fazy 01).
|
||||
- **Delivery mapping "Szukaj..." layout** — JS `attachSelectFilter()` w allegro.php tworzy input search dla InPost/Apaczka selectów, wizualnie wygląda jakby należał do wiersza powyżej. Pre-existing bug, do naprawy osobno.
|
||||
|
||||
### Git State
|
||||
Last commit: fbb3020 (docs: close loop 06-05 — UNIFY complete)
|
||||
@@ -91,14 +107,14 @@ Brak.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-03-13
|
||||
Stopped at: Faza 07 zaplanowana — 5 planów gotowych do wykonania
|
||||
Next action: /paul:apply .paul/phases/07-pre-expansion-fixes/07-01-PLAN.md
|
||||
Resume file: .paul/phases/07-pre-expansion-fixes/07-01-PLAN.md
|
||||
Last session: 2026-03-14
|
||||
Stopped at: Loop 07-03 zamknięty — SUMMARY utworzony
|
||||
Next action: /paul:apply .paul/phases/07-pre-expansion-fixes/07-04-PLAN.md
|
||||
Resume file: .paul/phases/07-pre-expansion-fixes/07-03-SUMMARY.md
|
||||
Resume context:
|
||||
- 07-01: Performance (N+1 subqueries, information_schema static, DB indexes)
|
||||
- 07-02: SSL verification + cron throttle DB + migration 000014b
|
||||
- 07-03: UX fixes (orderpro_to_allegro disable, items 14-17) — ma checkpoint
|
||||
- 07-01: COMPLETE ✓ (N+1→LEFT JOIN, static cache, migration 000048)
|
||||
- 07-02: COMPLETE ✓ (SSL verification, cron→DB, migration 000014b)
|
||||
- 07-03: COMPLETE ✓ (UX fixes + SSL hotfix CA bundle nullable)
|
||||
- 07-04: Tests (AllegroTokenManager + AllegroOrderImportService)
|
||||
- 07-05: InPost ShipmentProviderInterface — ma checkpoint:decision
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# PAUL Handoff
|
||||
|
||||
**Date:** 2026-03-13
|
||||
**Status:** paused
|
||||
**Status:** paused — session complete, plans ready for execution
|
||||
|
||||
---
|
||||
|
||||
@@ -9,50 +9,45 @@
|
||||
|
||||
You have no prior context. This document tells you everything.
|
||||
|
||||
**Project:** orderPRO — aplikacja do zarządzania zamówieniami z wielu kanałów sprzedaży (Allegro, Erli, własne sklepy). Generowanie etykiet kurierskich.
|
||||
**Core value:** Sprzedawca obsługuje wszystkie kanały i nadaje przesyłki bez przełączania platform.
|
||||
**Project:** orderPRO — aplikacja do zarządzania zamówieniami z wielu źródeł sprzedaży (Allegro, Erli, shopPRO) z generowaniem etykiet przewozowych.
|
||||
**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 State
|
||||
|
||||
**Version:** v0.1.0 (In Progress)
|
||||
**Phase:** 6 of TBD — 06-sonarqube-quality
|
||||
**Plan:** 06-01 ✓ DONE, 06-03 ✓ DONE, 06-02/04/05/06 awaiting
|
||||
**Milestone:** v0.2 Pre-Expansion Fixes
|
||||
**Phase:** 7 — pre-expansion-fixes
|
||||
**Plan:** 07-01..07-05 CREATED, żaden nie wykonany
|
||||
|
||||
**Loop Position:**
|
||||
```
|
||||
PLAN ──▶ APPLY ──▶ UNIFY
|
||||
✓ ✓ ✓ [loop closed — ready for next plan]
|
||||
✓ ○ ○ [Plan gotowy — czeka na APPLY]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Was Done (this session)
|
||||
## What Was Done (ta sesja)
|
||||
|
||||
- **UNIFY 06-01** — zamknięto pętlę dla exception hierarchy (commit 3c27c4e)
|
||||
- **APPLY 06-03** — IntegrationSources + RedirectPaths constants created; 30+ literals replaced (commit d7d3f99)
|
||||
- Auto-fix: naprawiono złamane `use AppCoreExceptions...` z planu 06-01 w AllegroOrderImportService + AllegroIntegrationController
|
||||
- **UNIFY 06-03** — pętla zamknięta, SUMMARY.md utworzony
|
||||
- Phase 6 progress: 2/6 plans complete (33%)
|
||||
|
||||
---
|
||||
|
||||
## What's In Progress
|
||||
|
||||
Nic — obie pętle zamknięte, codebase w stabilnym stanie.
|
||||
- UNIFY 06-05: god classes split — ShopproOrdersSyncService 39→9 metod, AllegroIntegrationController 35→25 metod
|
||||
- Faza 06 zamknięta: 6/6 planów, SonarQube quality baseline
|
||||
- /paul:complete-milestone: v0.1 Initial Release zamknięty, git tag v0.1.0
|
||||
- CONCERNS.md skategoryzowany — "przed rozbudową" vs "odroczić"
|
||||
- Faza 07 zaplanowana: 5 planów (07-01..07-05) gotowe do APPLY
|
||||
|
||||
---
|
||||
|
||||
## What's Next
|
||||
|
||||
**Immediate:** `/paul:apply .paul/phases/06-sonarqube-quality/06-02-PLAN.md`
|
||||
— php:S1142 redukcja return statements (save() z 5→≤3 w AllegroIntegrationController + ShopproIntegrationsController)
|
||||
**Immediate:** `/paul:apply .paul/phases/07-pre-expansion-fixes/07-01-PLAN.md`
|
||||
|
||||
**Kolejność pozostałych planów:** 06-02 → 06-06 → 06-04 → 06-05
|
||||
- 06-05 (god classes) zależy od 06-04 i ma `checkpoint:human-verify` (nie autonomous)
|
||||
|
||||
**Uwaga:** Warto sprawdzić czy broken `use` statements z 06-01 są w innych plikach Modules/Settings/ (wzorzec: `use AppCore...` bez backslashy)
|
||||
Kolejność:
|
||||
1. 07-01 — autonomiczny (Performance: N+1, static cache, DB indexes)
|
||||
2. 07-02 — autonomiczny (SSL verify, cron→DB, migration 000014b)
|
||||
3. 07-03 — ma checkpoint:human-verify (UX: disable orderpro_to_allegro, UI items 14-17)
|
||||
4. 07-04 — autonomiczny (Tests: AllegroTokenManager + import)
|
||||
5. 07-05 — ma checkpoint:decision (InPost ShipmentProviderInterface)
|
||||
|
||||
---
|
||||
|
||||
@@ -61,19 +56,18 @@ Nic — obie pętle zamknięte, codebase w stabilnym stanie.
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `.paul/STATE.md` | Live project state |
|
||||
| `.paul/ROADMAP.md` | Phase overview |
|
||||
| `.paul/phases/06-sonarqube-quality/06-02-PLAN.md` | S1142: Redukcja return statements |
|
||||
| `.paul/phases/06-sonarqube-quality/06-03-SUMMARY.md` | Ostatni UNIFY — context dla 06-02 |
|
||||
| `src/Core/Constants/IntegrationSources.php` | Nowa klasa stałych (ALLEGRO, SHOPPRO, etc.) |
|
||||
| `src/Core/Constants/RedirectPaths.php` | Nowa klasa stałych (redirect paths) |
|
||||
| `.paul/codebase/CONCERNS.md` | Pełna lista concerns |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-01-PLAN.md` | Performance fixes |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-02-PLAN.md` | SSL + cron + migration |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-03-PLAN.md` | UX fixes (checkpoint) |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-04-PLAN.md` | Unit tests |
|
||||
| `.paul/phases/07-pre-expansion-fixes/07-05-PLAN.md` | InPost provider (checkpoint:decision) |
|
||||
|
||||
---
|
||||
|
||||
## Resume Instructions
|
||||
|
||||
1. Przeczytaj `.paul/STATE.md` — potwierdź pozycję w loop
|
||||
2. Uruchom `/paul:apply .paul/phases/06-sonarqube-quality/06-02-PLAN.md`
|
||||
1. `/paul:resume` — odczyta STATE.md i pokaże aktualny stan
|
||||
2. Zatwierdź: `/paul:apply .paul/phases/07-pre-expansion-fixes/07-01-PLAN.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
122
.paul/phases/07-pre-expansion-fixes/07-01-SUMMARY.md
Normal file
122
.paul/phases/07-pre-expansion-fixes/07-01-SUMMARY.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
phase: 07-pre-expansion-fixes
|
||||
plan: 01
|
||||
subsystem: database
|
||||
tags: [performance, n+1, sql-optimization, indexes, static-cache]
|
||||
|
||||
requires:
|
||||
- phase: 06-sonarqube-quality
|
||||
provides: OrdersRepository refactored (god class split)
|
||||
provides:
|
||||
- Aggregating LEFT JOINs replacing N+1 correlated subqueries in orders list
|
||||
- Static cache for information_schema check
|
||||
- Performance indexes on orders table
|
||||
affects: [07-02, 07-03, 07-04]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [aggregating-left-join, static-property-cache]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- database/migrations/20260314_000048_add_orders_performance_indexes.sql
|
||||
modified:
|
||||
- src/Modules/Orders/OrdersRepository.php
|
||||
|
||||
key-decisions:
|
||||
- "allegro_order_status_mappings index skipped — already has UNIQUE KEY on allegro_status_code"
|
||||
- "Migration date 20260314 (execution date, not plan date)"
|
||||
|
||||
patterns-established:
|
||||
- "Aggregating LEFT JOIN zamiast correlated subqueries dla countów/sum w listach"
|
||||
- "Static property cache dla information_schema lookups"
|
||||
|
||||
duration: ~8min
|
||||
started: 2026-03-14
|
||||
completed: 2026-03-14
|
||||
---
|
||||
|
||||
# Phase 7 Plan 01: Performance Fixes Summary
|
||||
|
||||
**Eliminacja N+1 subqueries w liście zamówień, static cache dla information_schema, indeksy wydajnościowe na tabeli orders**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~8min |
|
||||
| Started | 2026-03-14 |
|
||||
| Completed | 2026-03-14 |
|
||||
| Tasks | 3 completed |
|
||||
| Files modified | 2 (1 modified, 1 created) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Brak correlated subqueries w liście zamówień | Pass | 4 subqueries zastąpione aggregating LEFT JOINs; grep potwierdza zero `SELECT COUNT(*) FROM order_items WHERE oi.order_id` |
|
||||
| AC-2: information_schema nie odpytywany per-request | Pass | `private static ?bool $supportsMappedMedia` + `self::$supportsMappedMedia` — max 1× na cykl PHP |
|
||||
| AC-3: Brakujące indeksy dodane migracją | Pass | Migracja 000048: source, external_status_id, ordered_at, composite (source, external_status_id); IF NOT EXISTS |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- 4 correlated subqueries (items_count, items_qty, shipments_count, documents_count) → 3 aggregating LEFT JOINs — eliminacja ~200 dodatkowych zapytań przy 50 wierszach na stronę
|
||||
- `canResolveMappedMedia()` z instance na static property — information_schema odpytywany max 1× na cykl PHP
|
||||
- 4 brakujące indeksy na tabeli orders dla typowych filtrów/sortowań
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Orders/OrdersRepository.php` | Modified | LEFT JOINs zamiast subqueries w buildListSql(); static cache w canResolveMappedMedia() |
|
||||
| `database/migrations/20260314_000048_add_orders_performance_indexes.sql` | Created | Indeksy: source, external_status_id, ordered_at, composite (source, external_status_id) |
|
||||
| `DOCS/DB_SCHEMA.md` | Modified | Wpis o migracji 000048 |
|
||||
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis 2026-03-14 o optymalizacjach |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Pominięto indeks na allegro_order_status_mappings.allegro_status_code | Tabela już ma UNIQUE KEY na tej kolumnie (migracja 000025) | Brak duplikatu indeksu |
|
||||
| Data migracji 20260314 zamiast 20260313 | Wykonanie w dniu 2026-03-14 | Numeracja zgodna z datą faktycznego utworzenia |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 0 | - |
|
||||
| Scope additions | 0 | - |
|
||||
| Deferred | 0 | - |
|
||||
|
||||
**Total impact:** Plan wykonany zgodnie ze specyfikacją. Jedyna różnica: pominięcie zbędnego indeksu na allegro_order_status_mappings (UNIQUE KEY już istniał).
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Verification Results
|
||||
|
||||
```
|
||||
✓ php -l src/Modules/Orders/OrdersRepository.php — No syntax errors
|
||||
✓ grep correlated subqueries — 0 matches (eliminated)
|
||||
✓ grep supportsMappedMedia — static + self:: confirmed
|
||||
✓ Migration 000048 — exists, non-empty, idempotent (IF NOT EXISTS)
|
||||
```
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- OrdersRepository zoptymalizowany — lista zamówień gotowa na wzrost danych
|
||||
- Plan 07-02 (SSL + cron + migration) niezależny, może być wykonany natychmiast
|
||||
|
||||
**Concerns:**
|
||||
- Migracja 000048 wymaga uruchomienia na środowisku docelowym
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 07-pre-expansion-fixes, Plan: 01*
|
||||
*Completed: 2026-03-14*
|
||||
132
.paul/phases/07-pre-expansion-fixes/07-02-SUMMARY.md
Normal file
132
.paul/phases/07-pre-expansion-fixes/07-02-SUMMARY.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
phase: 07-pre-expansion-fixes
|
||||
plan: 02
|
||||
subsystem: security, infra
|
||||
tags: [ssl, curl, cron-throttle, migration-dedup, app-settings]
|
||||
|
||||
requires:
|
||||
- phase: 06-sonarqube-quality
|
||||
provides: ApiClient classes refactored
|
||||
provides:
|
||||
- SSL verification in all 4 ApiClient classes
|
||||
- DB-backed cron web throttle (no more $_SESSION dependency)
|
||||
- Deduplicated migration sequence (000014b)
|
||||
affects: [07-03, 07-04, 07-05]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [getCaBundlePath-per-client, app-settings-as-kv-store]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- database/migrations/20260314_000049_add_cron_last_run_at_setting.sql
|
||||
modified:
|
||||
- src/Modules/Settings/AllegroApiClient.php
|
||||
- src/Modules/Settings/AllegroOAuthClient.php
|
||||
- src/Modules/Settings/ShopproApiClient.php
|
||||
- src/Modules/Settings/ApaczkaApiClient.php
|
||||
- src/Core/Application.php
|
||||
- .env.example
|
||||
- database/migrations/20260301_000014b_add_products_sku_format_setting.sql (renamed)
|
||||
|
||||
key-decisions:
|
||||
- "getCaBundlePath() per class — no shared trait, acceptable duplication for 4 classes"
|
||||
- "ON DUPLICATE KEY UPDATE with named params :ts/:ts2 (PDO limitation — no repeated named params)"
|
||||
|
||||
patterns-established:
|
||||
- "SSL: CURLOPT_SSL_VERIFYPEER=true + CURLOPT_SSL_VERIFYHOST=2 + CURLOPT_CAINFO in every curl call"
|
||||
- "app_settings as key-value store for cross-session state"
|
||||
|
||||
duration: ~10min
|
||||
started: 2026-03-14
|
||||
completed: 2026-03-14
|
||||
---
|
||||
|
||||
# Phase 7 Plan 02: SSL + Cron Throttle + Migration Dedup Summary
|
||||
|
||||
**SSL verification w 4 ApiClient klasach, cron throttle przeniesiony z $_SESSION do app_settings DB, deduplikacja migracji 000014**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~10min |
|
||||
| Started | 2026-03-14 |
|
||||
| Completed | 2026-03-14 |
|
||||
| Tasks | 3 completed |
|
||||
| Files modified | 8 (6 modified, 1 created, 1 renamed) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: SSL weryfikowany w każdym cURL wywołaniu | Pass | 6 curl_setopt_array blocks w 4 plikach mają CURLOPT_SSL_VERIFYPEER=true + VERIFYHOST=2 + CAINFO |
|
||||
| AC-2: Web cron throttle oparty na DB | Pass | $_SESSION['cron_web_last_run_at'] usunięty; getWebCronLastRunAt()/setWebCronLastRunAt() czytają/piszą app_settings |
|
||||
| AC-3: Migracja 000014 zdeduplikowana | Pass | git mv → 000014b; INSERT ON DUPLICATE KEY UPDATE (idempotentna) |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- 4 klasy ApiClient (6 miejsc cURL) zabezpieczone SSL verification z fallback chain: ENV → XAMPP → system
|
||||
- Cron web throttle nie zależy od sesji — działa poprawnie przy wielu aktywnych sesjach
|
||||
- Sekwencja migracji czysta — brak duplikatów numerów
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Settings/AllegroApiClient.php` | Modified | SSL opts w postJson(), postBinary(), requestJson() + getCaBundlePath() |
|
||||
| `src/Modules/Settings/AllegroOAuthClient.php` | Modified | SSL opts w requestToken() + getCaBundlePath() |
|
||||
| `src/Modules/Settings/ShopproApiClient.php` | Modified | SSL opts w requestJson() + getCaBundlePath() |
|
||||
| `src/Modules/Settings/ApaczkaApiClient.php` | Modified | SSL opts w executeRequest() + getCaBundlePath() |
|
||||
| `src/Core/Application.php` | Modified | isWebCronThrottled() → app_settings; +getWebCronLastRunAt(), +setWebCronLastRunAt() |
|
||||
| `.env.example` | Modified | Dodano CURL_CA_BUNDLE_PATH |
|
||||
| `database/migrations/20260314_000049_add_cron_last_run_at_setting.sql` | Created | Seed cron_web_last_run_at w app_settings |
|
||||
| `database/migrations/20260301_000014b_...` | Renamed | git mv z 000014 na 000014b |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| getCaBundlePath() w każdej klasie osobno | Brak wspólnego traita/klasy bazowej; 4 kopie to akceptowalna duplikacja vs. przedwczesna abstrakcja | Przyszła refaktoryzacja może wydzielić trait |
|
||||
| PDO named params :ts/:ts2 w ON DUPLICATE KEY | PDO nie pozwala na powtórzenie tego samego named param; użyto dwóch nazw | Standardowy workaround |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 1 | Minimal |
|
||||
| Scope additions | 0 | - |
|
||||
| Deferred | 0 | - |
|
||||
|
||||
**Total impact:** Minimalna odchyłka — data pliku migracji zmieniona z 20260313 na 20260314.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Data migracji**
|
||||
- **Found during:** Task 2
|
||||
- **Issue:** Plan proponował `20260313_000049`, ale wykonanie było 2026-03-14
|
||||
- **Fix:** Użyto `20260314_000049` zgodnie z datą wykonania
|
||||
- **Verification:** Plik istnieje, spójna konwencja nazewnicza
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Wszystkie ApiClienty bezpieczne pod SSL — nowe integracje mogą kopiować wzorzec
|
||||
- Cron throttle stabilny dla wielu sesji
|
||||
- Plan 07-03 (UX fixes) niezależny, może być wykonany natychmiast
|
||||
|
||||
**Concerns:**
|
||||
- Migracja 000049 wymaga uruchomienia na środowisku docelowym
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 07-pre-expansion-fixes, Plan: 02*
|
||||
*Completed: 2026-03-14*
|
||||
145
.paul/phases/07-pre-expansion-fixes/07-03-SUMMARY.md
Normal file
145
.paul/phases/07-pre-expansion-fixes/07-03-SUMMARY.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
phase: 07-pre-expansion-fixes
|
||||
plan: 03
|
||||
subsystem: ui, security
|
||||
tags: [ux, status-colors, ssl-hotfix, delivery-mapping-bug]
|
||||
|
||||
requires:
|
||||
- phase: 07-pre-expansion-fixes
|
||||
provides: Plan 07-02 SSL verification (hotfixed here)
|
||||
provides:
|
||||
- orderpro_to_allegro sync disabled (ok:false + UI disabled)
|
||||
- Orders list: source before ID, "ID:" prefix, sourceLabel()
|
||||
- Status badges colored by group color_hex from config
|
||||
- Darker form borders (#e2e8f0 → #b0bec5)
|
||||
- SSL getCaBundlePath() hotfix — nullable, CAINFO only when file exists
|
||||
affects: [07-04, 07-05]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [statusColorMap, sourceLabel, conditional-curlopt-cainfo]
|
||||
|
||||
key-files:
|
||||
modified:
|
||||
- src/Modules/Settings/AllegroStatusSyncService.php
|
||||
- resources/views/settings/allegro.php
|
||||
- src/Modules/Orders/OrdersController.php
|
||||
- resources/scss/shared/_ui-components.scss
|
||||
- public/assets/css/app.css
|
||||
- src/Modules/Settings/AllegroApiClient.php
|
||||
- src/Modules/Settings/AllegroOAuthClient.php
|
||||
- src/Modules/Settings/ShopproApiClient.php
|
||||
- src/Modules/Settings/ApaczkaApiClient.php
|
||||
|
||||
key-decisions:
|
||||
- "sourceLabel() z match expression — mapuje shoppro→shopPRO, allegro→Allegro, erli→Erli"
|
||||
- "statusColorMap() z group color_hex — status badge inline style gdy kolor dostępny, fallback na CSS klasy"
|
||||
- "SSL CURLOPT_CAINFO warunkowy — null gdy żaden CA bundle nie znaleziony, cURL używa systemowego"
|
||||
|
||||
patterns-established:
|
||||
- "Status coloring: statusColorMap() + inline style background-color + color:#fff"
|
||||
- "SSL: getCaBundlePath() nullable + withSslOptions() helper (AllegroApiClient)"
|
||||
|
||||
duration: ~20min
|
||||
started: 2026-03-14
|
||||
completed: 2026-03-14
|
||||
---
|
||||
|
||||
# Phase 7 Plan 03: UX Fixes Summary
|
||||
|
||||
**Disable orderpro_to_allegro sync, source/ID swap w liście zamówień, kolorowanie statusów, ciemniejsze bordery + hotfix SSL CA bundle**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~20min |
|
||||
| Started | 2026-03-14 |
|
||||
| Completed | 2026-03-14 |
|
||||
| Tasks | 3 completed (+ checkpoint) |
|
||||
| Files modified | 10 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: orderpro_to_allegro nie daje false-positive | Pass | `ok: false` w early return |
|
||||
| AC-2: Opcja UI disabled | Pass | `disabled` + "(wkrótce)" na option |
|
||||
| AC-3: Source przed ID + prefix "ID:" | Pass | sourceLabel() + swap kolejności spanów |
|
||||
| AC-4: Statusy kolorowane | Pass | statusColorMap() → inline style background-color |
|
||||
| AC-5: Ciemniejsze obramowanie | Pass | --c-border: #e2e8f0 → #b0bec5, CSS rebuilt |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- orderpro_to_allegro sync zwraca ok:false (nie ok:true), opcja UI disabled z "(wkrótce)"
|
||||
- Lista zamówień: source (Allegro/shopPRO/Erli) przed ID z prefixem "ID:"
|
||||
- Statusy kolorowane kolorem grupy z konfiguracji, fallback na CSS klasy dla niezamapowanych
|
||||
- Ciemniejsze obramowanie formularzy w całej aplikacji
|
||||
- **Hotfix SSL:** getCaBundlePath() zwraca null gdy żaden CA bundle nie znaleziony — cURL używa systemowego domyślnego, eliminuje błąd na serwerze
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Settings/AllegroStatusSyncService.php` | Modified | ok:true → ok:false dla orderpro_to_allegro |
|
||||
| `resources/views/settings/allegro.php` | Modified | disabled + "(wkrótce)" na opcji sync direction |
|
||||
| `src/Modules/Orders/OrdersController.php` | Modified | sourceLabel(), statusColorMap(), statusBadge() z kolorem, swap source/ID |
|
||||
| `resources/scss/shared/_ui-components.scss` | Modified | --c-border: #b0bec5 |
|
||||
| `public/assets/css/app.css` | Rebuilt | CSS z nowym border color |
|
||||
| `src/Modules/Settings/AllegroApiClient.php` | Modified | getCaBundlePath() nullable + withSslOptions() |
|
||||
| `src/Modules/Settings/AllegroOAuthClient.php` | Modified | getCaBundlePath() nullable, warunkowy CAINFO |
|
||||
| `src/Modules/Settings/ShopproApiClient.php` | Modified | getCaBundlePath() nullable, warunkowy CAINFO |
|
||||
| `src/Modules/Settings/ApaczkaApiClient.php` | Modified | getCaBundlePath() nullable, warunkowy CAINFO |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| sourceLabel() match expression | Czytelne mapowanie 3 znanych źródeł + ucfirst fallback | Łatwe do rozszerzenia o nowe źródła |
|
||||
| Status color inline style | group color_hex z konfiguracji; brak kolumny color w wierszu zamówienia | Nie wymaga migracji, kolor pochodzi z istniejącej tabeli order_status_groups |
|
||||
| SSL CAINFO warunkowy | Serwer produkcyjny nie ma lokalnych CA bundle paths, cURL ma wbudowany systemowy | Eliminuje błąd "error setting certificate verify locations" |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 1 | Critical — SSL hotfix z planu 07-02 |
|
||||
| Scope additions | 0 | - |
|
||||
| Deferred | 1 | Pre-existing bug |
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. SSL CA bundle path error na serwerze**
|
||||
- **Found during:** Checkpoint verification (user report)
|
||||
- **Issue:** getCaBundlePath() zwracał hardcoded path który nie istnieje na serwerze → cURL error
|
||||
- **Fix:** getCaBundlePath() zwraca null gdy brak pliku; CURLOPT_CAINFO ustawiany warunkowo
|
||||
- **Files:** 4 ApiClient klasy
|
||||
- **Verification:** User potwierdzi po deploy
|
||||
|
||||
### Deferred Items
|
||||
|
||||
- **Pre-existing:** Pole "Szukaj..." w mapowaniu form dostawy Allegro — JS `attachSelectFilter()` tworzy input search dla InPost/Apaczka selectów, layout myląco wygląda jakby należał do wiersza powyżej
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| SSL error na serwerze produkcyjnym | getCaBundlePath() → nullable, CAINFO warunkowy |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- UX fixes wdrożone, lista zamówień czytelniejsza
|
||||
- Plan 07-04 (testy) niezależny
|
||||
|
||||
**Concerns:**
|
||||
- Delivery mapping "Szukaj..." layout — pre-existing, do naprawy osobno
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 07-pre-expansion-fixes, Plan: 03*
|
||||
*Completed: 2026-03-14*
|
||||
258
.vscode/ftp-kr.sync.cache.json
vendored
258
.vscode/ftp-kr.sync.cache.json
vendored
@@ -730,32 +730,32 @@
|
||||
},
|
||||
"20260308_000038_ensure_order_status_mappings_table.sql": {
|
||||
"type": "-",
|
||||
"size": 888,
|
||||
"lmtime": 1772991139910,
|
||||
"size": 1513,
|
||||
"lmtime": 1773391946879,
|
||||
"modified": false
|
||||
},
|
||||
"20260308_000039_ensure_integrations_fetch_columns.sql": {
|
||||
"type": "-",
|
||||
"size": 808,
|
||||
"lmtime": 1772991829022,
|
||||
"size": 1596,
|
||||
"lmtime": 1773391955510,
|
||||
"modified": false
|
||||
},
|
||||
"20260308_000040_ensure_shoppro_orders_import_schedule.sql": {
|
||||
"type": "-",
|
||||
"size": 527,
|
||||
"lmtime": 1772992935478,
|
||||
"size": 1251,
|
||||
"lmtime": 1773391962537,
|
||||
"modified": false
|
||||
},
|
||||
"20260308_000041_ensure_shoppro_status_sync_schedule_and_direction.sql": {
|
||||
"type": "-",
|
||||
"size": 989,
|
||||
"lmtime": 1772993839010,
|
||||
"size": 1780,
|
||||
"lmtime": 1773391969582,
|
||||
"modified": false
|
||||
},
|
||||
"20260308_000042_ensure_shoppro_payment_sync_schedule_and_columns.sql": {
|
||||
"type": "-",
|
||||
"size": 958,
|
||||
"lmtime": 1772997818616,
|
||||
"size": 1660,
|
||||
"lmtime": 1773391978785,
|
||||
"modified": false
|
||||
},
|
||||
"20260308_000043_create_shoppro_delivery_method_mappings_table.sql": {
|
||||
@@ -832,14 +832,14 @@
|
||||
"DOCS": {
|
||||
"ARCHITECTURE.md": {
|
||||
"type": "-",
|
||||
"size": 28217,
|
||||
"size": 28598,
|
||||
"lmtime": 1773009533084,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"DB_SCHEMA.md": {
|
||||
"type": "-",
|
||||
"size": 20060,
|
||||
"lmtime": 1773009570461,
|
||||
"size": 21229,
|
||||
"lmtime": 1773392001064,
|
||||
"modified": false
|
||||
},
|
||||
"ORDERS_SCHEMA_APILO_DRAFT.md": {
|
||||
@@ -862,9 +862,9 @@
|
||||
},
|
||||
"todo.md": {
|
||||
"type": "-",
|
||||
"size": 1677,
|
||||
"size": 3163,
|
||||
"lmtime": 1772997209432,
|
||||
"modified": false
|
||||
"modified": true
|
||||
}
|
||||
},
|
||||
".env": {
|
||||
@@ -2397,8 +2397,8 @@
|
||||
"routes": {
|
||||
"web.php": {
|
||||
"type": "-",
|
||||
"size": 13708,
|
||||
"lmtime": 1773000073865,
|
||||
"size": 14437,
|
||||
"lmtime": 1773418817257,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
@@ -2453,9 +2453,9 @@
|
||||
"Core": {
|
||||
"Application.php": {
|
||||
"type": "-",
|
||||
"size": 13242,
|
||||
"size": 8679,
|
||||
"lmtime": 1772997978216,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"Database": {
|
||||
"ConnectionFactory.php": {
|
||||
@@ -2471,6 +2471,44 @@
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
"Exceptions": {
|
||||
"AllegroApiException.php": {
|
||||
"type": "-",
|
||||
"size": 128,
|
||||
"lmtime": 1773396173463,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroOAuthException.php": {
|
||||
"type": "-",
|
||||
"size": 132,
|
||||
"lmtime": 1773396174308,
|
||||
"modified": false
|
||||
},
|
||||
"ApaczkaApiException.php": {
|
||||
"type": "-",
|
||||
"size": 128,
|
||||
"lmtime": 1773396175168,
|
||||
"modified": false
|
||||
},
|
||||
"OrderProException.php": {
|
||||
"type": "-",
|
||||
"size": 126,
|
||||
"lmtime": 1773396172716,
|
||||
"modified": false
|
||||
},
|
||||
"ShipmentException.php": {
|
||||
"type": "-",
|
||||
"size": 126,
|
||||
"lmtime": 1773396176089,
|
||||
"modified": false
|
||||
},
|
||||
"IntegrationConfigException.php": {
|
||||
"type": "-",
|
||||
"size": 135,
|
||||
"lmtime": 1773396176697,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
"Http": {
|
||||
"Request.php": {
|
||||
"type": "-",
|
||||
@@ -2542,6 +2580,20 @@
|
||||
"lmtime": 1771460443424,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
"Constants": {
|
||||
"IntegrationSources.php": {
|
||||
"type": "-",
|
||||
"size": 263,
|
||||
"lmtime": 1773397435140,
|
||||
"modified": false
|
||||
},
|
||||
"RedirectPaths.php": {
|
||||
"type": "-",
|
||||
"size": 643,
|
||||
"lmtime": 1773397436702,
|
||||
"modified": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"Modules": {
|
||||
@@ -2584,6 +2636,12 @@
|
||||
"lmtime": 1772655083686,
|
||||
"modified": false
|
||||
},
|
||||
"CronHandlerFactory.php": {
|
||||
"type": "-",
|
||||
"size": 4482,
|
||||
"lmtime": 1773418273174,
|
||||
"modified": false
|
||||
},
|
||||
"CronJobProcessor.php": {
|
||||
"type": "-",
|
||||
"size": 6385,
|
||||
@@ -2644,17 +2702,17 @@
|
||||
"lmtime": 1772489139382,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproStatusSyncHandler.php": {
|
||||
"type": "-",
|
||||
"size": 456,
|
||||
"lmtime": 1772993806375,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproPaymentStatusSyncHandler.php": {
|
||||
"type": "-",
|
||||
"size": 577,
|
||||
"lmtime": 1772997967988,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproStatusSyncHandler.php": {
|
||||
"type": "-",
|
||||
"size": 456,
|
||||
"lmtime": 1772993806375,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
"GS1": {
|
||||
@@ -2700,15 +2758,15 @@
|
||||
},
|
||||
"OrdersController.php": {
|
||||
"type": "-",
|
||||
"size": 27279,
|
||||
"lmtime": 1773359707641,
|
||||
"size": 27184,
|
||||
"lmtime": 1773392942464,
|
||||
"modified": false
|
||||
},
|
||||
"OrdersRepository.php": {
|
||||
"type": "-",
|
||||
"size": 30415,
|
||||
"lmtime": 1772996631841,
|
||||
"modified": true
|
||||
"size": 31275,
|
||||
"lmtime": 1773401508499,
|
||||
"modified": false
|
||||
},
|
||||
"OrderStatusSyncService.php": {
|
||||
"type": "-",
|
||||
@@ -2796,8 +2854,14 @@
|
||||
"Settings": {
|
||||
"AllegroApiClient.php": {
|
||||
"type": "-",
|
||||
"size": 11015,
|
||||
"lmtime": 1772746725988,
|
||||
"size": 11092,
|
||||
"lmtime": 1773396192542,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroDeliveryMappingController.php": {
|
||||
"type": "-",
|
||||
"size": 9202,
|
||||
"lmtime": 1773418766854,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroDeliveryMethodMappingRepository.php": {
|
||||
@@ -2808,62 +2872,74 @@
|
||||
},
|
||||
"AllegroIntegrationController.php": {
|
||||
"type": "-",
|
||||
"size": 39516,
|
||||
"lmtime": 1772999795856,
|
||||
"size": 26499,
|
||||
"lmtime": 1773418757646,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroIntegrationRepository.php": {
|
||||
"type": "-",
|
||||
"size": 15433,
|
||||
"lmtime": 1772985465032,
|
||||
"size": 15397,
|
||||
"lmtime": 1773396209969,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroOAuthClient.php": {
|
||||
"type": "-",
|
||||
"size": 6130,
|
||||
"lmtime": 1772746387780,
|
||||
"size": 6185,
|
||||
"lmtime": 1773396209523,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroOrderImportService.php": {
|
||||
"type": "-",
|
||||
"size": 32847,
|
||||
"lmtime": 1772985483883,
|
||||
"size": 29620,
|
||||
"lmtime": 1773397494713,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroOrdersSyncService.php": {
|
||||
"type": "-",
|
||||
"size": 11653,
|
||||
"lmtime": 1772985474211,
|
||||
"size": 8112,
|
||||
"lmtime": 1773396210179,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroOrderSyncStateRepository.php": {
|
||||
"type": "-",
|
||||
"size": 8757,
|
||||
"size": 8672,
|
||||
"lmtime": 1772660685699,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"AllegroStatusDiscoveryService.php": {
|
||||
"type": "-",
|
||||
"size": 5786,
|
||||
"size": 2545,
|
||||
"lmtime": 1772657848652,
|
||||
"modified": true
|
||||
},
|
||||
"AllegroStatusMappingController.php": {
|
||||
"type": "-",
|
||||
"size": 7597,
|
||||
"lmtime": 1773418641524,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroStatusMappingRepository.php": {
|
||||
"type": "-",
|
||||
"size": 4690,
|
||||
"size": 4584,
|
||||
"lmtime": 1772657817169,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"AllegroStatusSyncService.php": {
|
||||
"type": "-",
|
||||
"size": 3266,
|
||||
"lmtime": 1772803803020,
|
||||
"size": 3869,
|
||||
"lmtime": 1773397499705,
|
||||
"modified": false
|
||||
},
|
||||
"AllegroTokenManager.php": {
|
||||
"type": "-",
|
||||
"size": 3075,
|
||||
"lmtime": 1773396209752,
|
||||
"modified": false
|
||||
},
|
||||
"ApaczkaApiClient.php": {
|
||||
"type": "-",
|
||||
"size": 8911,
|
||||
"lmtime": 1773001526890,
|
||||
"size": 8957,
|
||||
"lmtime": 1773396223690,
|
||||
"modified": false
|
||||
},
|
||||
"ApaczkaIntegrationController.php": {
|
||||
@@ -2874,8 +2950,8 @@
|
||||
},
|
||||
"ApaczkaIntegrationRepository.php": {
|
||||
"type": "-",
|
||||
"size": 6203,
|
||||
"lmtime": 1773001299233,
|
||||
"size": 6268,
|
||||
"lmtime": 1773396223901,
|
||||
"modified": false
|
||||
},
|
||||
"AppSettingsRepository.php": {
|
||||
@@ -2886,9 +2962,9 @@
|
||||
},
|
||||
"CarrierDeliveryMethodMappingRepository.php": {
|
||||
"type": "-",
|
||||
"size": 8057,
|
||||
"size": 7901,
|
||||
"lmtime": 1773004976663,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"CompanySettingsController.php": {
|
||||
"type": "-",
|
||||
@@ -2898,9 +2974,9 @@
|
||||
},
|
||||
"CompanySettingsRepository.php": {
|
||||
"type": "-",
|
||||
"size": 7232,
|
||||
"size": 7207,
|
||||
"lmtime": 1773009466748,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"CronSettingsController.php": {
|
||||
"type": "-",
|
||||
@@ -2916,8 +2992,8 @@
|
||||
},
|
||||
"InpostIntegrationRepository.php": {
|
||||
"type": "-",
|
||||
"size": 8562,
|
||||
"lmtime": 1772985383681,
|
||||
"size": 8498,
|
||||
"lmtime": 1773396224300,
|
||||
"modified": false
|
||||
},
|
||||
"IntegrationRepository.php": {
|
||||
@@ -2928,8 +3004,8 @@
|
||||
},
|
||||
"IntegrationSecretCipher.php": {
|
||||
"type": "-",
|
||||
"size": 2085,
|
||||
"lmtime": 1772985270857,
|
||||
"size": 2140,
|
||||
"lmtime": 1773396224100,
|
||||
"modified": false
|
||||
},
|
||||
"IntegrationsHubController.php": {
|
||||
@@ -2940,9 +3016,9 @@
|
||||
},
|
||||
"IntegrationsRepository.php": {
|
||||
"type": "-",
|
||||
"size": 4220,
|
||||
"size": 4106,
|
||||
"lmtime": 1772985295068,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"OrderStatusMappingRepository.php": {
|
||||
"type": "-",
|
||||
@@ -2958,9 +3034,9 @@
|
||||
},
|
||||
"SettingsController.php": {
|
||||
"type": "-",
|
||||
"size": 16872,
|
||||
"size": 16670,
|
||||
"lmtime": 1772493293023,
|
||||
"modified": false
|
||||
"modified": true
|
||||
},
|
||||
"ShopproApiClient.php": {
|
||||
"type": "-",
|
||||
@@ -2982,26 +3058,44 @@
|
||||
},
|
||||
"ShopproIntegrationsController.php": {
|
||||
"type": "-",
|
||||
"size": 36438,
|
||||
"lmtime": 1773003944439,
|
||||
"size": 37809,
|
||||
"lmtime": 1773408010714,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproIntegrationsRepository.php": {
|
||||
"type": "-",
|
||||
"size": 21407,
|
||||
"lmtime": 1772997877315,
|
||||
"size": 21350,
|
||||
"lmtime": 1773396224535,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproOrderMapper.php": {
|
||||
"type": "-",
|
||||
"size": 38431,
|
||||
"lmtime": 1773418140037,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproOrdersSyncService.php": {
|
||||
"type": "-",
|
||||
"size": 52376,
|
||||
"lmtime": 1773005399696,
|
||||
"size": 12433,
|
||||
"lmtime": 1773418261049,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproOrderSyncStateRepository.php": {
|
||||
"type": "-",
|
||||
"size": 8941,
|
||||
"lmtime": 0,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproPaymentStatusSyncService.php": {
|
||||
"type": "-",
|
||||
"size": 14133,
|
||||
"lmtime": 1772997958340,
|
||||
"size": 13746,
|
||||
"lmtime": 1773397561728,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproProductImageResolver.php": {
|
||||
"type": "-",
|
||||
"size": 4673,
|
||||
"lmtime": 1773418240242,
|
||||
"modified": false
|
||||
},
|
||||
"ShopproStatusMappingRepository.php": {
|
||||
@@ -3013,7 +3107,7 @@
|
||||
"ShopproStatusSyncService.php": {
|
||||
"type": "-",
|
||||
"size": 2075,
|
||||
"lmtime": 1772993798910,
|
||||
"lmtime": 1773397552472,
|
||||
"modified": false
|
||||
}
|
||||
},
|
||||
@@ -3034,20 +3128,20 @@
|
||||
"Shipments": {
|
||||
"AllegroShipmentService.php": {
|
||||
"type": "-",
|
||||
"size": 15178,
|
||||
"lmtime": 1772999590482,
|
||||
"modified": true
|
||||
"size": 14926,
|
||||
"lmtime": 1773396238404,
|
||||
"modified": false
|
||||
},
|
||||
"ApaczkaShipmentService.php": {
|
||||
"type": "-",
|
||||
"size": 32921,
|
||||
"lmtime": 1773009754471,
|
||||
"size": 33037,
|
||||
"lmtime": 1773396238099,
|
||||
"modified": false
|
||||
},
|
||||
"ShipmentController.php": {
|
||||
"type": "-",
|
||||
"size": 16795,
|
||||
"lmtime": 1773359710858,
|
||||
"size": 16702,
|
||||
"lmtime": 1773396224755,
|
||||
"modified": false
|
||||
},
|
||||
"ShipmentPackageRepository.php": {
|
||||
|
||||
@@ -85,6 +85,9 @@ Migracje z prefiksem `ensure_` to migracje kompensujące — zostały dodane
|
||||
- rozszerzenie `company_settings` o `sender_contact_person` (osoba kontaktowa nadawcy),
|
||||
- wykorzystywane w payloadzie Apaczka jako `address.sender.contact_person`.
|
||||
- 2026-03-08: Ujednolicono styl naglowkow sekcji UI (`section-title`) - bez zmian schematu bazy.
|
||||
- 2026-03-14: Dodano migracje `20260314_000048_add_orders_performance_indexes.sql` — indeksy wydajnosciowe na tabeli `orders`: `source`, `external_status_id`, `ordered_at`, composite `(source, external_status_id)`.
|
||||
- 2026-03-14: Dodano migracje `20260314_000049_add_cron_last_run_at_setting.sql` — seed klucza `cron_web_last_run_at` w `app_settings` (cron throttle przeniesiony z sesji do DB).
|
||||
- 2026-03-14: Przemianowano migracje `20260301_000014_add_products_sku_format_setting.sql` na `20260301_000014b_add_products_sku_format_setting.sql` — deduplikacja numeru sekwencji (kolizja z `000014_create_product_integration_translations`).
|
||||
|
||||
## Tabele
|
||||
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# Tech Changelog
|
||||
|
||||
## 2026-03-14
|
||||
- Zoptymalizowano zapytanie listy zamowien (`OrdersRepository::buildListSql()`):
|
||||
- 4 correlated subqueries (items_count, items_qty, shipments_count, documents_count) zastapiono aggregating LEFT JOINami — eliminuje N+1 na kazdym wierszu listy.
|
||||
- `OrdersRepository::canResolveMappedMedia()` — zamiana instance property na `static` — `information_schema` odpytywany co najwyzej raz na cykl PHP zamiast raz per instancja.
|
||||
- Dodano migracje `20260314_000048_add_orders_performance_indexes.sql` — indeksy na `orders`: `source`, `external_status_id`, `ordered_at`, composite `(source, external_status_id)`.
|
||||
- Dodano SSL verification (`CURLOPT_SSL_VERIFYPEER => true`, `CURLOPT_SSL_VERIFYHOST => 2`, `CURLOPT_CAINFO`) do 4 klas ApiClient: AllegroApiClient (3 metody), AllegroOAuthClient, ShopproApiClient, ApaczkaApiClient. Fallback: `$_ENV['CURL_CA_BUNDLE_PATH']` → XAMPP cacert.pem → system CA bundle.
|
||||
- Cron web throttle (`isWebCronThrottled()`) przeniesiony z `$_SESSION` do `app_settings` (klucz `cron_web_last_run_at`) — eliminuje wielokrotne uruchamianie crona przy wielu aktywnych sesjach.
|
||||
- Deduplikacja migracji `000014` → `000014b` (kolizja z `create_product_integration_translations`).
|
||||
- `AllegroStatusSyncService::sync()` zwraca `ok:false` dla kierunku `orderpro_to_allegro` (wczesniej false-positive `ok:true`). Opcja UI oznaczona jako `disabled` z `(wkrotce)`.
|
||||
- Lista zamowien: source wyswietlany przed ID z prefixem `ID:`; `sourceLabel()` mapuje shoppro→shopPRO, allegro→Allegro.
|
||||
- Statusy zamowien na liscie kolorowane kolorem grupy z konfiguracji (`statusColorMap()` → inline `background-color`).
|
||||
- Ciemniejsze obramowanie pol formularzy: `--c-border` zmieniony z `#e2e8f0` na `#b0bec5`.
|
||||
- Hotfix SSL: `getCaBundlePath()` zwraca `null` gdy zaden CA bundle nie znaleziony — `CURLOPT_CAINFO` ustawiany warunkowo, cURL uzywa systemowego CA na serwerze.
|
||||
|
||||
## 2026-03-08
|
||||
- Poprawiono date podjazdu kuriera w payloadzie Apaczka:
|
||||
- `pickup.date` dla trybu `COURIER` jest normalizowane tak, aby nie wypadal w niedziele (niedziela -> poniedzialek),
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Performance indexes for orders list view
|
||||
-- Covers: source filter, external_status_id filter/sort, ordered_at sort,
|
||||
-- composite source+external_status_id for combined filtering
|
||||
ALTER TABLE orders
|
||||
ADD INDEX IF NOT EXISTS orders_source_idx (source),
|
||||
ADD INDEX IF NOT EXISTS orders_external_status_idx (external_status_id),
|
||||
ADD INDEX IF NOT EXISTS orders_ordered_at_idx (ordered_at),
|
||||
ADD INDEX IF NOT EXISTS orders_source_status_idx (source, external_status_id);
|
||||
@@ -0,0 +1,3 @@
|
||||
INSERT INTO app_settings (setting_key, setting_value, created_at, updated_at)
|
||||
VALUES ('cron_web_last_run_at', '0', NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE updated_at = updated_at;
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
--c-text: #4e5e6a;
|
||||
--c-text-strong: #2d3748;
|
||||
--c-muted: #718096;
|
||||
--c-border: #e2e8f0;
|
||||
--c-border: #b0bec5;
|
||||
--c-danger: #cc0000;
|
||||
--focus-ring: 0 0 0 3px rgba(102, 144, 244, 0.15);
|
||||
--shadow-card: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||
|
||||
@@ -261,8 +261,8 @@ $orderproStatuses = is_array($orderproStatuses ?? null) ? $orderproStatuses : []
|
||||
<option value="allegro_to_orderpro"<?= $statusSyncDirection === 'allegro_to_orderpro' ? ' selected' : '' ?>>
|
||||
<?= $e($t('settings.allegro.settings.status_sync_direction_allegro_to_orderpro')) ?>
|
||||
</option>
|
||||
<option value="orderpro_to_allegro"<?= $statusSyncDirection === 'orderpro_to_allegro' ? ' selected' : '' ?>>
|
||||
<?= $e($t('settings.allegro.settings.status_sync_direction_orderpro_to_allegro')) ?>
|
||||
<option value="orderpro_to_allegro"<?= $statusSyncDirection === 'orderpro_to_allegro' ? ' selected' : '' ?> disabled>
|
||||
<?= $e($t('settings.allegro.settings.status_sync_direction_orderpro_to_allegro')) ?> (wkrótce)
|
||||
</option>
|
||||
</select>
|
||||
<span class="muted"><?= $e($t('settings.allegro.settings.status_sync_direction_hint')) ?></span>
|
||||
|
||||
@@ -269,16 +269,42 @@ final class Application
|
||||
{
|
||||
$safeInterval = max(1, $minIntervalSeconds);
|
||||
$now = time();
|
||||
$lastRunAt = isset($_SESSION['cron_web_last_run_at']) ? (int) $_SESSION['cron_web_last_run_at'] : 0;
|
||||
$lastRunAt = $this->getWebCronLastRunAt();
|
||||
|
||||
if ($lastRunAt > 0 && ($now - $lastRunAt) < $safeInterval) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$_SESSION['cron_web_last_run_at'] = $now;
|
||||
$this->setWebCronLastRunAt($now);
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getWebCronLastRunAt(): int
|
||||
{
|
||||
try {
|
||||
$stmt = $this->db->prepare(
|
||||
"SELECT setting_value FROM app_settings WHERE setting_key = 'cron_web_last_run_at'"
|
||||
);
|
||||
$stmt->execute();
|
||||
$value = $stmt->fetchColumn();
|
||||
return $value !== false ? (int) $value : 0;
|
||||
} catch (Throwable) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private function setWebCronLastRunAt(int $timestamp): void
|
||||
{
|
||||
try {
|
||||
$this->db->prepare(
|
||||
"INSERT INTO app_settings (setting_key, setting_value, created_at, updated_at)
|
||||
VALUES ('cron_web_last_run_at', :ts, NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE setting_value = :ts2, updated_at = NOW()"
|
||||
)->execute(['ts' => (string) $timestamp, 'ts2' => (string) $timestamp]);
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
private function acquireWebCronLock(): bool
|
||||
{
|
||||
$statement = $this->db->query("SELECT GET_LOCK('orderpro_web_cron_lock', 0)");
|
||||
|
||||
@@ -47,10 +47,11 @@ final class OrdersController
|
||||
$statusCounts = $this->orders->statusCounts();
|
||||
$statusConfig = $this->orders->statusPanelConfig();
|
||||
$statusLabelMap = $this->statusLabelMap($statusConfig);
|
||||
$statusColorMap = $this->statusColorMap($statusConfig);
|
||||
$statusOptions = $this->buildStatusFilterOptions($this->orders->statusOptions(), $statusLabelMap);
|
||||
$statusPanel = $this->buildStatusPanel($statusConfig, $statusCounts, $filters['status'], $filters);
|
||||
|
||||
$tableRows = array_map(fn (array $row): array => $this->toTableRow($row, $statusLabelMap), (array) ($result['items'] ?? []));
|
||||
$tableRows = array_map(fn (array $row): array => $this->toTableRow($row, $statusLabelMap, $statusColorMap), (array) ($result['items'] ?? []));
|
||||
|
||||
$html = $this->template->render('orders/list', [
|
||||
'title' => $this->translator->get('orders.title'),
|
||||
@@ -228,7 +229,7 @@ final class OrdersController
|
||||
* @param array<string, mixed> $row
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function toTableRow(array $row, array $statusLabelMap): array
|
||||
private function toTableRow(array $row, array $statusLabelMap, array $statusColorMap = []): array
|
||||
{
|
||||
$internalOrderNumber = trim((string) ($row['internal_order_number'] ?? ''));
|
||||
$sourceOrderId = trim((string) ($row['source_order_id'] ?? ''));
|
||||
@@ -257,8 +258,8 @@ final class OrdersController
|
||||
. htmlspecialchars($internalOrderNumber !== '' ? $internalOrderNumber : ('#' . (string) ($row['id'] ?? 0)), ENT_QUOTES, 'UTF-8')
|
||||
. '</a></div>'
|
||||
. '<div class="orders-ref__meta">'
|
||||
. '<span>' . htmlspecialchars($sourceOrderId !== '' ? $sourceOrderId : $externalOrderId, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '<span>' . htmlspecialchars($source, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '<span>' . htmlspecialchars($this->sourceLabel($source), ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '<span>ID: ' . htmlspecialchars($sourceOrderId !== '' ? $sourceOrderId : $externalOrderId, ENT_QUOTES, 'UTF-8') . '</span>'
|
||||
. '</div>'
|
||||
. '</div>',
|
||||
'buyer' => '<div class="orders-buyer">'
|
||||
@@ -269,7 +270,7 @@ final class OrdersController
|
||||
. '</div>'
|
||||
. '</div>',
|
||||
'status_badges' => '<div class="orders-status-wrap">'
|
||||
. $this->statusBadge($status, $this->statusLabel($status, $statusLabelMap))
|
||||
. $this->statusBadge($status, $this->statusLabel($status, $statusLabelMap), $statusColorMap[strtolower(trim($status))] ?? '')
|
||||
. '</div>',
|
||||
'products' => $this->productsHtml($itemsPreview, $itemsCount, $itemsQty),
|
||||
'totals' => '<div class="orders-money">'
|
||||
@@ -285,10 +286,16 @@ final class OrdersController
|
||||
];
|
||||
}
|
||||
|
||||
private function statusBadge(string $statusCode, string $statusLabel): string
|
||||
private function statusBadge(string $statusCode, string $statusLabel, string $colorHex = ''): string
|
||||
{
|
||||
$label = $statusLabel !== '' ? $statusLabel : '-';
|
||||
$code = strtolower(trim($statusCode));
|
||||
|
||||
if ($colorHex !== '') {
|
||||
$style = 'background-color:' . htmlspecialchars($colorHex, ENT_QUOTES, 'UTF-8') . ';color:#fff';
|
||||
return '<span class="order-tag" style="' . $style . '">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||
}
|
||||
|
||||
$class = 'is-neutral';
|
||||
if (in_array($code, ['shipped', 'delivered'], true)) {
|
||||
$class = 'is-success';
|
||||
@@ -303,6 +310,16 @@ final class OrdersController
|
||||
return '<span class="order-tag ' . $class . '">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||
}
|
||||
|
||||
private function sourceLabel(string $source): string
|
||||
{
|
||||
return match (strtolower(trim($source))) {
|
||||
'allegro' => 'Allegro',
|
||||
'shoppro' => 'shopPRO',
|
||||
'erli' => 'Erli',
|
||||
default => ucfirst(strtolower(trim($source))),
|
||||
};
|
||||
}
|
||||
|
||||
private function statusLabel(string $statusCode, array $statusLabelMap = []): string
|
||||
{
|
||||
$key = strtolower(trim($statusCode));
|
||||
@@ -475,6 +492,30 @@ final class OrdersController
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{name:string,color_hex:string,items:array<int, array{code:string,name:string}>}> $config
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function statusColorMap(array $config): array
|
||||
{
|
||||
$map = [];
|
||||
foreach ($config as $group) {
|
||||
$groupColor = StringHelper::normalizeColorHex((string) ($group['color_hex'] ?? ''));
|
||||
if ($groupColor === '') {
|
||||
continue;
|
||||
}
|
||||
$items = is_array($group['items'] ?? null) ? $group['items'] : [];
|
||||
foreach ($items as $item) {
|
||||
$code = strtolower(trim((string) ($item['code'] ?? '')));
|
||||
if ($code !== '') {
|
||||
$map[$code] = $groupColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $statusCodes
|
||||
* @param array<string, string> $statusLabelMap
|
||||
|
||||
@@ -9,7 +9,7 @@ use Throwable;
|
||||
|
||||
final class OrdersRepository
|
||||
{
|
||||
private ?bool $supportsMappedMedia = null;
|
||||
private static ?bool $supportsMappedMedia = null;
|
||||
|
||||
public function __construct(private readonly PDO $pdo)
|
||||
{
|
||||
@@ -158,13 +158,25 @@ final class OrdersRepository
|
||||
a.city AS buyer_city,
|
||||
o.external_carrier_id,
|
||||
o.external_payment_type_id,
|
||||
(SELECT COUNT(*) FROM order_items oi WHERE oi.order_id = o.id) AS items_count,
|
||||
(SELECT COALESCE(SUM(oi.quantity), 0) FROM order_items oi WHERE oi.order_id = o.id) AS items_qty,
|
||||
(SELECT COUNT(*) FROM order_shipments sh WHERE sh.order_id = o.id) AS shipments_count,
|
||||
(SELECT COUNT(*) FROM order_documents od WHERE od.order_id = o.id) AS documents_count
|
||||
COALESCE(oi_agg.items_count, 0) AS items_count,
|
||||
COALESCE(oi_agg.items_qty, 0) AS items_qty,
|
||||
COALESCE(sh_agg.shipments_count, 0) AS shipments_count,
|
||||
COALESCE(od_agg.documents_count, 0) AS documents_count
|
||||
FROM orders o
|
||||
LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer"
|
||||
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.external_status_id) = asm.allegro_status_code'
|
||||
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.external_status_id) = asm.allegro_status_code
|
||||
LEFT JOIN (
|
||||
SELECT order_id, COUNT(*) AS items_count, COALESCE(SUM(quantity), 0) AS items_qty
|
||||
FROM order_items GROUP BY order_id
|
||||
) oi_agg ON oi_agg.order_id = o.id
|
||||
LEFT JOIN (
|
||||
SELECT order_id, COUNT(*) AS shipments_count
|
||||
FROM order_shipments GROUP BY order_id
|
||||
) sh_agg ON sh_agg.order_id = o.id
|
||||
LEFT JOIN (
|
||||
SELECT order_id, COUNT(*) AS documents_count
|
||||
FROM order_documents GROUP BY order_id
|
||||
) od_agg ON od_agg.order_id = o.id'
|
||||
. $whereSql
|
||||
. ' ORDER BY ' . $sortColumn . ' ' . $sortDir
|
||||
. ' LIMIT :limit OFFSET :offset';
|
||||
@@ -646,8 +658,8 @@ final class OrdersRepository
|
||||
|
||||
private function canResolveMappedMedia(): bool
|
||||
{
|
||||
if ($this->supportsMappedMedia !== null) {
|
||||
return $this->supportsMappedMedia;
|
||||
if (self::$supportsMappedMedia !== null) {
|
||||
return self::$supportsMappedMedia;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -682,12 +694,12 @@ final class OrdersRepository
|
||||
$stmt->execute($params);
|
||||
$count = (int) $stmt->fetchColumn();
|
||||
|
||||
$this->supportsMappedMedia = ($count === count($requiredColumns));
|
||||
self::$supportsMappedMedia = ($count === count($requiredColumns));
|
||||
} catch (Throwable) {
|
||||
$this->supportsMappedMedia = false;
|
||||
self::$supportsMappedMedia = false;
|
||||
}
|
||||
|
||||
return $this->supportsMappedMedia;
|
||||
return self::$supportsMappedMedia;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -146,6 +146,44 @@ final class AllegroApiClient
|
||||
return $this->postJson($url, $accessToken, $body);
|
||||
}
|
||||
|
||||
private function getCaBundlePath(): ?string
|
||||
{
|
||||
$envPath = (string) ($_ENV['CURL_CA_BUNDLE_PATH'] ?? '');
|
||||
if ($envPath !== '' && is_file($envPath)) {
|
||||
return $envPath;
|
||||
}
|
||||
$iniPath = (string) ini_get('curl.cainfo');
|
||||
if ($iniPath !== '' && is_file($iniPath)) {
|
||||
return $iniPath;
|
||||
}
|
||||
$candidates = [
|
||||
'C:/xampp/apache/bin/curl-ca-bundle.crt',
|
||||
'C:/xampp/php/extras/ssl/cacert.pem',
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
];
|
||||
foreach ($candidates as $path) {
|
||||
if (is_file($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $opts
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
private function withSslOptions(array $opts): array
|
||||
{
|
||||
$opts[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$opts[CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$caPath = $this->getCaBundlePath();
|
||||
if ($caPath !== null) {
|
||||
$opts[CURLOPT_CAINFO] = $caPath;
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
private function apiBaseUrl(string $environment): string
|
||||
{
|
||||
return trim(strtolower($environment)) === 'production'
|
||||
@@ -166,7 +204,7 @@ final class AllegroApiClient
|
||||
throw new AllegroApiException('Nie udalo sie zainicjowac polaczenia z API Allegro.');
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
curl_setopt_array($ch, $this->withSslOptions([
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $jsonBody,
|
||||
@@ -177,7 +215,7 @@ final class AllegroApiClient
|
||||
'Content-Type: application/vnd.allegro.public.v1+json',
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
],
|
||||
]);
|
||||
]));
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
@@ -230,7 +268,7 @@ final class AllegroApiClient
|
||||
throw new AllegroApiException('Nie udalo sie zainicjowac polaczenia z API Allegro.');
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
curl_setopt_array($ch, $this->withSslOptions([
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $jsonBody,
|
||||
@@ -241,7 +279,7 @@ final class AllegroApiClient
|
||||
'Content-Type: application/vnd.allegro.public.v1+json',
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
],
|
||||
]);
|
||||
]));
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
@@ -279,7 +317,7 @@ final class AllegroApiClient
|
||||
throw new AllegroApiException('Nie udalo sie zainicjowac polaczenia z API Allegro.');
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
curl_setopt_array($ch, $this->withSslOptions([
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPGET => true,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
@@ -288,7 +326,7 @@ final class AllegroApiClient
|
||||
'Accept: application/vnd.allegro.public.v1+json',
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
],
|
||||
]);
|
||||
]));
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
@@ -126,6 +126,29 @@ final class AllegroOAuthClient
|
||||
return trim(strtolower($environment)) === 'production' ? 'production' : 'sandbox';
|
||||
}
|
||||
|
||||
private function getCaBundlePath(): ?string
|
||||
{
|
||||
$envPath = (string) ($_ENV['CURL_CA_BUNDLE_PATH'] ?? '');
|
||||
if ($envPath !== '' && is_file($envPath)) {
|
||||
return $envPath;
|
||||
}
|
||||
$iniPath = (string) ini_get('curl.cainfo');
|
||||
if ($iniPath !== '' && is_file($iniPath)) {
|
||||
return $iniPath;
|
||||
}
|
||||
$candidates = [
|
||||
'C:/xampp/apache/bin/curl-ca-bundle.crt',
|
||||
'C:/xampp/php/extras/ssl/cacert.pem',
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
];
|
||||
foreach ($candidates as $path) {
|
||||
if (is_file($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $formData
|
||||
* @return array<string, mixed>
|
||||
@@ -141,18 +164,25 @@ final class AllegroOAuthClient
|
||||
throw new AllegroOAuthException('Nie udalo sie zainicjowac polaczenia OAuth z Allegro.');
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
$sslOpts = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_TIMEOUT => 20,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_SSL_VERIFYHOST => 2,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'Authorization: Basic ' . base64_encode($clientId . ':' . $clientSecret),
|
||||
],
|
||||
CURLOPT_POSTFIELDS => http_build_query($formData),
|
||||
]);
|
||||
];
|
||||
$caPath = $this->getCaBundlePath();
|
||||
if ($caPath !== null) {
|
||||
$sslOpts[CURLOPT_CAINFO] = $caPath;
|
||||
}
|
||||
curl_setopt_array($ch, $sslOpts);
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
@@ -38,7 +38,7 @@ final class AllegroStatusSyncService
|
||||
|
||||
if ($direction === self::DIRECTION_ORDERPRO_TO_ALLEGRO) {
|
||||
return [
|
||||
'ok' => true,
|
||||
'ok' => false,
|
||||
'direction' => $direction,
|
||||
'processed' => 0,
|
||||
'message' => 'Kierunek orderPRO -> Allegro nie jest jeszcze wdrozony.',
|
||||
|
||||
@@ -9,6 +9,29 @@ final class ApaczkaApiClient
|
||||
{
|
||||
private const API_BASE_URL = 'https://www.apaczka.pl/api/v2';
|
||||
|
||||
private function getCaBundlePath(): ?string
|
||||
{
|
||||
$envPath = (string) ($_ENV['CURL_CA_BUNDLE_PATH'] ?? '');
|
||||
if ($envPath !== '' && is_file($envPath)) {
|
||||
return $envPath;
|
||||
}
|
||||
$iniPath = (string) ini_get('curl.cainfo');
|
||||
if ($iniPath !== '' && is_file($iniPath)) {
|
||||
return $iniPath;
|
||||
}
|
||||
$candidates = [
|
||||
'C:/xampp/apache/bin/curl-ca-bundle.crt',
|
||||
'C:/xampp/php/extras/ssl/cacert.pem',
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
];
|
||||
foreach ($candidates as $path) {
|
||||
if (is_file($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
@@ -180,18 +203,25 @@ final class ApaczkaApiClient
|
||||
throw new ApaczkaApiException('Nie udalo sie zainicjowac polaczenia z API Apaczka.');
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
$sslOpts = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($payload),
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_SSL_VERIFYHOST => 2,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'User-Agent: orderPRO/1.0',
|
||||
],
|
||||
]);
|
||||
];
|
||||
$caPath = $this->getCaBundlePath();
|
||||
if ($caPath !== null) {
|
||||
$sslOpts[CURLOPT_CAINFO] = $caPath;
|
||||
}
|
||||
curl_setopt_array($ch, $sslOpts);
|
||||
|
||||
$rawBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
@@ -5,6 +5,29 @@ namespace App\Modules\Settings;
|
||||
|
||||
final class ShopproApiClient
|
||||
{
|
||||
private function getCaBundlePath(): ?string
|
||||
{
|
||||
$envPath = (string) ($_ENV['CURL_CA_BUNDLE_PATH'] ?? '');
|
||||
if ($envPath !== '' && is_file($envPath)) {
|
||||
return $envPath;
|
||||
}
|
||||
$iniPath = (string) ini_get('curl.cainfo');
|
||||
if ($iniPath !== '' && is_file($iniPath)) {
|
||||
return $iniPath;
|
||||
}
|
||||
$candidates = [
|
||||
'C:/xampp/apache/bin/curl-ca-bundle.crt',
|
||||
'C:/xampp/php/extras/ssl/cacert.pem',
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
];
|
||||
foreach ($candidates as $path) {
|
||||
if (is_file($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{ok:bool,http_code:int|null,message:string,items:array<int,array<string,mixed>>,total:int,page:int,per_page:int}
|
||||
*/
|
||||
@@ -193,15 +216,22 @@ final class ShopproApiClient
|
||||
];
|
||||
}
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
$sslOpts = [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => max(1, min(120, $timeoutSeconds)),
|
||||
CURLOPT_CONNECTTIMEOUT => max(1, min(120, $timeoutSeconds)),
|
||||
CURLOPT_SSL_VERIFYPEER => true,
|
||||
CURLOPT_SSL_VERIFYHOST => 2,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'X-Api-Key: ' . $apiKey,
|
||||
],
|
||||
]);
|
||||
];
|
||||
$caPath = $this->getCaBundlePath();
|
||||
if ($caPath !== null) {
|
||||
$sslOpts[CURLOPT_CAINFO] = $caPath;
|
||||
}
|
||||
curl_setopt_array($curl, $sslOpts);
|
||||
|
||||
$body = curl_exec($curl);
|
||||
$httpCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
Reference in New Issue
Block a user