This commit is contained in:
Jacek
2026-04-18 23:52:11 +02:00
parent e195ffc841
commit 41e491c6b7
26 changed files with 4571 additions and 9456 deletions

View File

@@ -1,6 +1,6 @@
# shopPRO Koniec Pracy (release workflow)
# shopPRO - Koniec Pracy (release workflow)
Execute the full release workflow for shopPRO. This is a sequential pipeline each step depends on the previous one succeeding. Stop and report if any step fails.
Execute the full release workflow for shopPRO. This is a sequential pipeline - each step depends on the previous one succeeding. Stop and report if any step fails.
## Step 1: Run tests
@@ -8,7 +8,7 @@ Run the full PHPUnit test suite:
```bash
php phpunit.phar
```
All tests must pass. If any test fails, stop here do not proceed to commit. Report the failures and wait for instructions.
All tests must pass. If any test fails, stop here - do not proceed to commit. Report the failures and wait for instructions.
## Step 1b: SonarQube scan
@@ -19,20 +19,20 @@ sonar-scanner
After the scan completes, query the SonarQube issues via MCP tool `mcp__sonarqube__issues` with `project_key: "shopPRO"` and `resolved: false`. Fetch all open issues (bugs, vulnerabilities, code smells).
Then open `docs/TODO.md` and append the found issues at the bottom under a new section:
Then open `.paul/docs/TODO.md` and append the found issues at the bottom under a new section:
```markdown
## SonarQube {VERSION} ({DATE})
## SonarQube - {VERSION} ({DATE})
- [ ] [SEVERITY] FILENAME:LINE description (rule)
- [ ] [SEVERITY] FILENAME:LINE - description (rule)
- [ ] ...
```
Rules:
- Only add issues that are NOT already present in `docs/TODO.md`
- Only add issues that are NOT already present in `.paul/docs/TODO.md`
- Group by type: first Bugs/Vulnerabilities, then Code Smells
- Skip INFO severity Code Smells only include MINOR and above
- If there are no new issues, write: `## SonarQube {VERSION} brak nowych issues`
- Skip INFO severity Code Smells - only include MINOR and above
- If there are no new issues, write: `## SonarQube - {VERSION} - brak nowych issues`
## Step 2: Determine version
@@ -40,24 +40,24 @@ Read the latest git tag to determine the current version number:
```bash
git tag --sort=-v:refname | head -1
```
The new version is the previous version incremented by 1 (e.g., v0.333 v0.334). Use this version number throughout the remaining steps.
The new version is the previous version incremented by 1 (e.g., v0.333 -> v0.334). Use this version number throughout the remaining steps.
## Step 3: Update documentation
Update these docs files **only if** changes in this session affect them:
Update these docs files **only if** changes in this session affect them.
Do not update files in root `docs/` directory.
| File | When to update |
|------|---------------|
| `docs/CHANGELOG.md` | Always add a new version entry at the top describing what changed |
| `docs/TESTING.md` | If tests were added/removed update test count and structure |
| `CLAUDE.md` | If test count changed — update the "Current suite" line |
| `docs/DATABASE_STRUCTURE.md` | If database schema changed |
| `docs/PROJECT_STRUCTURE.md` | If architecture/files changed significantly |
| `docs/FORM_EDIT_SYSTEM.md` | If form system was modified |
| `.paul/docs/CHANGELOG.md` | Always - add a new version entry at the top describing what changed |
| `.paul/docs/TESTING.md` | If tests were added/removed - update test count and structure |
| `.paul/docs/DB_SCHEMA.md` | If database schema changed |
| `.paul/docs/ARCHITECTURE.md` | If architecture/files changed significantly |
| `.paul/docs/FORMS.md` | If form system was modified |
## Step 4: SQL migrations
If database schema changes were made, create a migration file at `migrations/{version}.sql` (e.g., `migrations/0.334.sql`). Do NOT put SQL files in `updates/` the build script reads from `migrations/` automatically.
If database schema changes were made, create a migration file at `migrations/{version}.sql` (e.g., `migrations/0.334.sql`). Do NOT put SQL files in `updates/` - the build script reads from `migrations/` automatically.
If no DB changes were made, skip this step.

View File

@@ -2,11 +2,11 @@
## What This Is
Autorski silnik sklepu internetowego pisany od podstaw — odpowiednik WooCommerce lub PrestaShop, ale bez zaleĹĽnoĹci od zewnÄ™trznych platform. SkĹ‚ada siÄ™ z panelu administratora (zarzÄ…dzanie zamĂłwieniami, produktami, klientami) oraz części frontowej dla klienta koĹ„cowego.
Autorski silnik sklepu internetowego pisany od podstaw - odpowiednik WooCommerce lub PrestaShop, ale bez zależności od zewnętrznych platform. Składa się z panelu administratora (zarządzanie zamówieniami, produktami, klientami) oraz części frontowej dla klienta końcowego.
## Core Value
WĹciciel sklepu internetowego ma peĹnÄ… kontrolÄ™ nad sprzedaĹĽÄ… online — produktami, zamĂłwieniami i klientami — w jednym spĂłjnym systemie pisanym od podstaw, bez narzutĂłw zewnÄ™trznych platform.
Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online - produktami, zamówieniami i klientami - w jednym spójnym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
## Current State
@@ -20,16 +20,16 @@ WĹciciel sklepu internetowego ma peĹnÄ… kontrolÄ™ nad sprzed
### Validated (Shipped)
- [x] Panel administratora — zarządzanie produktami, kategoriami, atrybutami
- [x] Panel administratora — zarządzanie zamówieniami
- [x] Panel administratora — zarządzanie klientami
- [x] Część frontowa — przeglądanie i kupowanie produktów
- [x] Koszyk i składanie zamówień
- [x] Integracje płatności i dostaw
- [x] Panel administratora - zarządzanie produktami, kategoriami, atrybutami
- [x] Panel administratora - zarządzanie zamówieniami
- [x] Panel administratora - zarządzanie klientami
- [x] Część frontowa - przeglądanie i kupowanie produktów
- [x] Koszyk i składanie zamówień
- [x] Integracje płatności i dostaw
- [x] REST API (ordersPRO + Ekomi)
- [x] Redis caching
- [x] Ochrona przed podwójnym składaniem zamówienia
- [x] Domain-Driven Architecture (migracja z legacy zakończona)
- [x] Ochrona przed podwójnym składaniem zamówienia
- [x] Domain-Driven Architecture (migracja z legacy zakończona)
### Active (In Progress)
@@ -41,16 +41,16 @@ WĹciciel sklepu internetowego ma peĹnÄ… kontrolÄ™ nad sprzed
### Out of Scope
- Multitenancy (wiele sklepów w jednej instancji) — nie planowane
- Multitenancy (wiele sklepów w jednej instancji) - nie planowane
## Target Users
**Primary:** WĹciciel/administrator sklepu internetowego
- ZarzÄ…dza produktami, zamĂłwieniami, klientami przez panel admina
- Potrzebuje niezawodnego, szybkiego narzÄ™dzia bez zbÄ™dnych zaleĹĽnoĹci
**Primary:** Właściciel/administrator sklepu internetowego
- Zarządza produktami, zamówieniami, klientami przez panel admina
- Potrzebuje niezawodnego, szybkiego narzędzia bez zbędnych zależności
**Secondary:** Klient końcowy sklepu
- Przegląda produkty, dodaje do koszyka, składa zamówienia
**Secondary:** Klient końcowy sklepu
- Przegląda produkty, dodaje do koszyka, składa zamówienia
## Context
@@ -58,26 +58,26 @@ WĹciciel sklepu internetowego ma peĹnÄ… kontrolÄ™ nad sprzed
- PHP 7.4+ (produkcja: PHP < 8.0)
- Medoo ORM (`$mdb`), Redis caching
- Domain-Driven Design z Dependency Injection
- PHPUnit 9.6, 810+ testĂłw
- PHPUnit 9.6, 810+ testów
- Namespace: `\Domain\`, `\admin\`, `\front\`, `\api\`, `\Shared\`
## Constraints
### Technical Constraints
- PHP < 8.0 na produkcji (brak `match`, named arguments, union types)
- Medoo ORM — prepared statements bez wyjątków
- Medoo ORM - prepared statements bez wyjątków
- Redis wymagany dla cache
### Business Constraints
- System wdraĹĽany u klientĂłw jako update package (ZIP)
- System wdrażany u klientów jako update package (ZIP)
## Key Decisions
| Decision | Rationale | Date | Status |
|----------|-----------|------|--------|
| DDD + DI zamiast legacy architektury | Testowalność, separacja odpowiedzialności | 2025 | Active |
| PHP < 8.0 kompatybilność | Klienci na starszych serwerach | 2025 | Active |
| Własny silnik zamiast frameworka | Pełna kontrola, brak narzutów | - | Active |
| DDD + DI zamiast legacy architektury | Testowalność, separacja odpowiedzialności | 2025 | Active |
| PHP < 8.0 kompatybilność | Klienci na starszych serwerach | 2025 | Active |
| Własny silnik zamiast frameworka | Pełna kontrola, brak narzutów | - | Active |
| `id` w tabbed FormEdit przez `hiddenFields` | Zapobiega insert zamiast update przy edycji encji | 2026-04-18 | Active |
## Success Metrics
@@ -94,7 +94,7 @@ WĹciciel sklepu internetowego ma peĹnÄ… kontrolÄ™ nad sprzed
| Backend | PHP 7.4+ | < 8.0 na produkcji |
| ORM | Medoo | `$mdb` global |
| Cache | Redis | CacheHandler singleton |
| Frontend | HTML/CSS/JS | Własny silnik szablonów (Tpl) |
| Frontend | HTML/CSS/JS | Własny silnik szablonów (Tpl) |
| Auth | Sesje PHP | CSRF, XSS protection |
| Testy | PHPUnit 9.6 | phpunit.phar |
@@ -103,15 +103,14 @@ WĹciciel sklepu internetowego ma peĹnÄ… kontrolÄ™ nad sprzed
See: .paul/SPECIAL-FLOWS.md
Quick Reference:
- /feature-dev → Nowe funkcje, większe zmiany (required)
- /koniec-pracy → Release, update package (required)
- /frontend-design → Komponenty UI, szablony widoków
- /code-review → Przegląd kodu przed release
- /simplify → Upraszczanie po implementacji
- /claude-md-improver → Utrzymanie CLAUDE.md
- /zapisz + /wznow → Zapis i wznowienie sesji
- /feature-dev -> Nowe funkcje, większe zmiany (required)
- /koniec-pracy -> Release, update package (required)
- /frontend-design -> Komponenty UI, szablony widoków
- /code-review -> Przegląd kodu przed release
- /simplify -> Upraszczanie po implementacji
- /claude-md-improver -> Utrzymanie CLAUDE.md
- /zapisz + /wznow -> Zapis i wznowienie sesji
---
*PROJECT.md — Updated when requirements or context change*
*PROJECT.md - Updated when requirements or context change*
*Last updated: 2026-04-18 after Phase 15*

View File

@@ -2,7 +2,7 @@
## Overview
shopPRO to autorski silnik sklepu internetowego rozwijany iteracyjnie. Projekt jest już na produkcji (v0.333) — roadmap obejmuje planowane funkcje i usprawnienia kolejnych wersji.
shopPRO to autorski silnik sklepu internetowego rozwijany iteracyjnie. Projekt jest już na produkcji (v0.333) roadmap obejmuje planowane funkcje i usprawnienia kolejnych wersji.
## Current Milestone
@@ -16,25 +16,25 @@ Phases: 4 of 4 complete
|-------|------|-------|--------|-----------|
| 1 | Sensitive data logging fix | 1 | Done | 2026-03 |
| 2 | Path traversal + XSS escaping | 1 | Done | 2026-03 (v0.335) |
| 3 | Error handling w krytycznych ścieżkach | 1 | Done | 2026-03 (v0.336) |
| 4 | CSRF protection — admin panel forms | 1 | Applied | 2026-03 (v0.337) |
| 5 | Order bugs fix — duplicate + COD status | 1 | Applied | 2026-03 (v0.338) |
| 3 | Error handling w krytycznych ścieżkach | 1 | Done | 2026-03 (v0.336) |
| 4 | CSRF protection admin panel forms | 1 | Applied | 2026-03 (v0.337) |
| 5 | Order bugs fix duplicate + COD status | 1 | Applied | 2026-03 (v0.338) |
## Next Milestone
**Tech debt — Integrations refactoring**
**Tech debt Integrations refactoring**
Status: Planning
| Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------|
| 6 | IntegrationsRepository split → ApiloRepository | 2 | Done | 2026-03 |
| 6 | IntegrationsRepository split ApiloRepository | 2 | Done | 2026-03 |
## Hotfix
| Phase | Name | Plans | Status | Completed |
|-------|------|-------|--------|-----------|
| 7 | Coupon Fatal Error — order placement crash | 1 | Done | 2026-03-15 |
| 8 | Apilo orders not sending — diagnoza i naprawa | 1 | Done | 2026-03-16 |
| 7 | Coupon Fatal Error order placement crash | 1 | Done | 2026-03-15 |
| 8 | Apilo orders not sending diagnoza i naprawa | 1 | Done | 2026-03-16 |
| 9 | Apilo email notification + infinite retry | 1 | Done | 2026-03-19 |
| 15 | Scontainers edit saves as new record | 1 | Done | 2026-04-18 |
@@ -44,72 +44,72 @@ Status: Planning
|-------|------|-------|--------|-----------|
| 10 | Edycja personalizacji produktu w koszyku | 1 | Done | 2026-03-19 |
| 11 | DataLayer GA4 analytics fix | 1 | Done | 2026-03-25 |
| 12 | summaryView redirect fix — double order block | 1 | Done | 2026-03-25 |
| 12 | summaryView redirect fix double order block | 1 | Done | 2026-03-25 |
| 13 | Basket logging + TTL token fix | 1 | Done | 2026-03-25 |
| 14 | Custom fields delete bug — usunięcie wszystkich pól | 1 | Done | 2026-04-16 |
| 14 | Custom fields delete bug usunięcie wszystkich pól | 1 | Done | 2026-04-16 |
## Phase Details
### Phase 4 — CSRF protection
### Phase 4 CSRF protection
**Problem:** Brak tokenĂłw CSRF na formularzach panelu admina. State-changing POST endpointy (create/update/delete) sÄ… potencjalnie podatne na ataki CSRF.
**Problem:** Brak tokenów CSRF na formularzach panelu admina. State-changing POST endpointy (create/update/delete) są potencjalnie podatne na ataki CSRF.
**Scope:** Dodanie CSRF tokenĂłw do formularzy i walidacji w panelu administracyjnym.
**Scope:** Dodanie CSRF tokenów do formularzy i walidacji w panelu administracyjnym.
**Reference:** `.paul/codebase/concerns.md` — MEDIUM — Missing CSRF tokens
**Reference:** `.paul/codebase/concerns.md` MEDIUM Missing CSRF tokens
### Phase 6 — IntegrationsRepository split
### Phase 6 IntegrationsRepository split
**Problem:** `IntegrationsRepository` ma 875 linii — miesza logikę generyczną (settings, logi, product linking) z logiką specyficzną dla Apilo (~650 linii). Narusza zasadę jednej odpowiedzialności.
**Problem:** `IntegrationsRepository` ma 875 linii miesza logikę generyczną (settings, logi, product linking) z logiką specyficzną dla Apilo (~650 linii). Narusza zasadę jednej odpowiedzialności.
**Scope:**
- Plan 06-01: UtwĂłrz `ApiloRepository` z metodami apilo* (non-breaking)
- Plan 06-02: Zmigruj konsumentów (IntegrationsController, ShopProductController, OrderAdminService, cron.php), usuń apilo* z IntegrationsRepository
- Plan 06-01: Utwórz `ApiloRepository` z metodami apilo* (non-breaking)
- Plan 06-02: Zmigruj konsumentów (IntegrationsController, ShopProductController, OrderAdminService, cron.php), usuń apilo* z IntegrationsRepository
---
### Phase 5 — Order bugs fix
### Phase 5 Order bugs fix
**Problem 1:** Zduplikowane zamĂłwienia — klient widzi błąd i klika złóż zamĂłwienie ponownie. Pierwsze zamĂłwienie trafiĹ‚o do bazy mimo błędu. PowrĂłt do `/podsumowanie` regeneruje token i pozwala zĹoĹĽyć drugie zamĂłwienie.
**Problem 1:** Zduplikowane zamówienia klient widzi błąd i klika złóż zamówienie ponownie. Pierwsze zamówienie trafiło do bazy mimo błędu. Powrót do `/podsumowanie` regeneruje token i pozwala złożyć drugie zamówienie.
**Problem 2:** ZamĂłwienia COD (pĹ‚atność przy odbiorze) dostajÄ… status "ZamĂłwienie zĹoĹĽone" zamiast "PrzyjÄ™te do realizacji". Kod sprawdza hardkodowane `payment_id == 3`, ktĂłre jest inne w tej instancji sklepu.
**Problem 2:** Zamówienia COD (płatność przy odbiorze) dostają status "Zamówienie złożone" zamiast "Przyjęte do realizacji". Kod sprawdza hardkodowane `payment_id == 3`, które jest inne w tej instancji sklepu.
**Scope:** Guard w `summaryView()`, try-catch w `basketSave()`, kolumna `is_cod` w `pp_shop_payment_methods`, uĹĽycie flagi zamiast hardkodowanego ID.
**Scope:** Guard w `summaryView()`, try-catch w `basketSave()`, kolumna `is_cod` w `pp_shop_payment_methods`, użycie flagi zamiast hardkodowanego ID.
---
*Roadmap created: 2026-03-12*
### Phase 11 — DataLayer GA4 analytics fix
**Problem:** Eventy dataLayer ecommerce (purchase, begin_checkout, view_item, add_to_cart) uĹĽywajÄ… starego formatu UA (id/name zamiast item_id/item_name), brak currency w view_item, price:0 w purchase, brak eventu view_cart. Remarketing dynamiczny i konwersje GA4 nie dziaĹajÄ… poprawnie.
### Phase 11 — DataLayer GA4 analytics fix
**Scope:** Poprawka 4 istniejÄ…cych eventĂłw do formatu GA4 + dodanie nowego eventu view_cart na stronie koszyka.
**Problem:** Eventy dataLayer ecommerce (purchase, begin_checkout, view_item, add_to_cart) używają starego formatu UA (id/name zamiast item_id/item_name), brak currency w view_item, price:0 w purchase, brak eventu view_cart. Remarketing dynamiczny i konwersje GA4 nie działają poprawnie.
**Reference:** `poprawki_datalayer_projectpro.md` — audyt analityki z pomysloweprezenty.pl
**Scope:** Poprawka 4 istniejących eventów do formatu GA4 + dodanie nowego eventu view_cart na stronie koszyka.
### Phase 12 — summaryView redirect fix
**Reference:** `poprawki_datalayer_projectpro.md` — audyt analityki z pomysloweprezenty.pl
**Problem:** Po zĹoĹĽeniu pierwszego zamĂłwienia, guard w `summaryView()` sprawdzaĹ sesyjny `order-submit-last-order-id` i redirectowaĹ na stronÄ™ starego zamĂłwienia. BlokowaĹ dostÄ™p do `/koszyk-podsumowanie` dla kolejnych zamĂłwieĹ„. Poprawka z instancji klienta (change.md) do wdroĹĽenia globalnie.
### Phase 12 — summaryView redirect fix
**Scope:** Usunięcie bloku redirect z `summaryView()` w `ShopBasketController.php`. Double-submit protection w `basketSave()` pozostaje bez zmian.
**Problem:** Po złożeniu pierwszego zamówienia, guard w `summaryView()` sprawdzał sesyjny `order-submit-last-order-id` i redirectował na stronę starego zamówienia. Blokował dostęp do `/koszyk-podsumowanie` dla kolejnych zamówień. Poprawka z instancji klienta (change.md) do wdrożenia globalnie.
### Phase 13 — Basket logging + TTL token fix
**Scope:** Usunięcie bloku redirect z `summaryView()` w `ShopBasketController.php`. Double-submit protection w `basketSave()` pozostaje bez zmian.
**Problem:** Brak logowania w basketSave() uniemoĹĽliwia diagnozÄ™ bĹÄ™dĂłw zamĂłwieĹ„. Token zamĂłwienia jednorazowy — nadpisywany przy kaĹĽdym wejĹciu na podsumowanie, co powoduje ĹĽe druga karta, "wstecz" lub odĹwieĹĽenie uniewaĹĽnia formularz.
### Phase 13 — Basket logging + TTL token fix
**Scope:** Dodanie metody logOrder() z 4 punktami logowania, zmiana tokena z jednorazowego na TTL 30 min, redirect przy bĹÄ™dzie tokena na /koszyk-podsumowanie zamiast /koszyk, nowy double-submit guard.
**Problem:** Brak logowania w basketSave() uniemożliwia diagnozę błędów zamówień. Token zamówienia jednorazowy — nadpisywany przy każdym wejściu na podsumowanie, co powoduje że druga karta, "wstecz" lub odświeżenie unieważnia formularz.
### Phase 14 — Custom fields delete bug
**Scope:** Dodanie metody logOrder() z 4 punktami logowania, zmiana tokena z jednorazowego na TTL 30 min, redirect przy błędzie tokena na /koszyk-podsumowanie zamiast /koszyk, nowy double-submit guard.
**Problem:** UsuniÄ™cie WSZYSTKICH dodatkowych pĂłl z produktu nie dziaĹa. jQuery `.serialize()` nie wysyĹa klucza `custom_field_name[]` gdy nie ma ĹĽadnych pĂłl → `array_key_exists('custom_field_name', $d)` w ProductRepository zwraca false → `saveCustomFields()` nigdy nie jest wywoĹywany → pola pozostajÄ… w bazie.
### Phase 14 — Custom fields delete bug
**Problem:** Usunięcie WSZYSTKICH dodatkowych pól z produktu nie działa. jQuery `.serialize()` nie wysyła klucza `custom_field_name[]` gdy nie ma żadnych pól → `array_key_exists('custom_field_name', $d)` w ProductRepository zwraca false → `saveCustomFields()` nigdy nie jest wywoływany → pola pozostają w bazie.
**Scope:** Dodanie hidden markera `custom_field_name_present` w szablonie JS + zmiana warunku w ProductRepository na sprawdzanie tego markera. Test jednostkowy.
### Phase 15 - Scontainers edit saves as new record
**Problem:** Edycja kontenera statycznego (`/admin/scontainers/edit/id={id}`) zapisuje rekord jako nowy wpis zamiast aktualizacji. W praktyce podczas zapisu gubi sie `id` i repository wykonuje insert.
**Problem:** Edycja kontenera statycznego (`/admin/scontainers/edit/id={id}`) zapisuje rekord jako nowy wpis zamiast aktualizacji. W praktyce podczas zapisu gubi się `id` i repository wykonuje insert.
**Scope:** Poprawic przekazywanie `id` w nowym flow formularza ScontainersController + dodac test regresyjny dla edycji, bez zmian globalnych w innych kontrolerach.
**Scope:** Poprawić przekazywanie `id` w nowym flow formularza ScontainersController + dodać test regresyjny dla edycji, bez zmian globalnych w innych kontrolerach.
---
*Last updated: 2026-04-18*

View File

@@ -1,37 +1,37 @@
# Specialized Flows: shopPRO
# Specialized Flows: shopPRO
## Project-Level Dependencies
| Work Type | Skill/Command | Priority | Kiedy używać |
| Work Type | Skill/Command | Priority | Kiedy uzywac |
|-----------|---------------|----------|--------------|
| Komponenty UI, szablony widoków | /frontend-design | optional | Przy tworzeniu HTML/CSS |
| Nowe funkcje, większe zmiany | /feature-dev | required | Przed implementacją fazy |
| Przegląd kodu | /code-review | optional | Przed release / KONIEC PRACY |
| Upraszczanie po zmianach | /simplify | optional | Po zakończeniu implementacji |
| Utrzymanie CLAUDE.md | /claude-md-improver | optional | Co kilka faz / po dużych zmianach |
| Release, budowanie update package | /koniec-pracy | required | Na koniec każdej sesji roboczej |
| Zapis i wznowienie sesji | /zapisz + /wznow | optional | Na przerwę / powrót do pracy |
| Komponenty UI, szablony widokow | /frontend-design | optional | Przy tworzeniu HTML/CSS |
| Nowe funkcje, wieksze zmiany | /feature-dev | required | Przed implementacja fazy |
| Przeglad kodu | /code-review | optional | Przed release / KONIEC PRACY |
| Upraszczanie po zmianach | /simplify | optional | Po zakonczeniu implementacji |
| Utrzymanie CLAUDE.md | /claude-md-improver | optional | Co kilka faz / po duzych zmianach |
| Release, budowanie update package | /koniec-pracy | required | Na koniec kazdej sesji roboczej |
| Zapis i wznowienie sesji | /zapisz + /wznow | optional | Na przerwe / powrot do pracy |
## Phase Overrides
Brak domyślna konfiguracja obowiązuje dla wszystkich faz.
Brak - domyslna konfiguracja obowiazuje dla wszystkich faz.
## Templates & Assets
| Asset Type | Location | When Used |
|------------|----------|-----------|
| CLAUDE.md | CLAUDE.md | Konwencje kodu, architektura, stack techniczny |
| Struktura bazy | docs/DATABASE_STRUCTURE.md | Przy zmianach schematu DB |
| Dokumentacja API | api-docs/api-reference.json | Przy zmianach API |
| TODO | docs/TODO.md | Planowanie nowych funkcji |
| Struktura bazy | .paul/docs/DB_SCHEMA.md | Przy zmianach schematu DB |
| Dokumentacja API | .paul/docs/API.md | Przy zmianach API |
| TODO | .paul/docs/TODO.md | Planowanie nowych funkcji |
## Verification (UNIFY)
Podczas UNIFY sprawdź:
- `/feature-dev` czy był użyty przed implementacją fazy?
- `/koniec-pracy` czy release został wykonany?
Podczas UNIFY sprawdz:
- `/feature-dev` - czy byl uzyty przed implementacja fazy?
- `/koniec-pracy` - czy release zostal wykonany?
Braki dokumentuj w STATE.md (Deferred Issues), nie blokują UNIFY.
Braki dokumentuj w STATE.md (Deferred Issues), nie blokuja UNIFY.
---
*SPECIAL-FLOWS.md Created: 2026-03-12*
*SPECIAL-FLOWS.md - Created: 2026-03-12*

View File

@@ -4,7 +4,7 @@
See: .paul/PROJECT.md (updated 2026-04-18)
**Core value:** WĹciciel sklepu ma peĹnÄ… kontrolÄ™ nad sprzedaĹĽÄ… online w jednym systemie pisanym od podstaw, bez narzutĂłw zewnÄ™trznych platform.
**Core value:** Właściciel sklepu ma pełną kontrolę nad sprzedażą online w jednym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
**Current focus:** Phase 15 complete - loop closed (scontainers edit save fix)
## Current Position
@@ -51,22 +51,22 @@ Phase 15: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-18]
- 2026-04-18: /koniec-pracy requirement mapped to .claude/commands/koniec-pracy.md guidance for end-of-session release flow
- 2026-04-18: Scontainers edit fix - ID from tabbed form can be omitted when hidden field is defined as FormField and not as hiddenFields
- Use existing `CouponRepository::markAsUsed()` instead of adding methods to stdClass
- 2026-03-16: Przyczyna braku wysyłki = brakujące $apiloRepository w use() closures cron.php (regresja z fazy 6)
- 2026-03-16: Przyczyna braku wysyłki = brakujące $apiloRepository w use() closures cron.php (regresja z fazy 6)
- 2026-03-16: Retry -1 orders co 1h zamiast permanent failure
- 2026-03-16: Email notification o trwale failed Apilo jobach
- 2026-03-19: Order-related Apilo joby — infinite retry co 30 min (nigdy permanent failure)
- 2026-03-19: Email z danymi zamówienia + rozróżnienie PONAWIANY vs TRWAŁY BŁĄD
- 2026-03-19: Cleanup stuck sync_payment/sync_status jobów po udanym wysłaniu
- 2026-03-19: Edycja custom fields w koszyku — product_code przeliczany po zmianie, merge duplikatów przy identycznym hashu
- 2026-03-19: Order-related Apilo joby infinite retry co 30 min (nigdy permanent failure)
- 2026-03-19: Email z danymi zamówienia + rozróżnienie PONAWIANY vs TRWAŁY BŁĄD
- 2026-03-19: Cleanup stuck sync_payment/sync_status jobów po udanym wysłaniu
- 2026-03-19: Edycja custom fields w koszyku product_code przeliczany po zmianie, merge duplikatów przy identycznym hashu
- 2026-03-19: JS handlery koszyka w basket.php (nie basket-details.php) bo basket-details jest AJAX-replaceable
- 2026-03-25: view_cart event w basket.php (nie basket-details.php) — ten sam powód
- 2026-03-25: view_cart event w basket.php (nie basket-details.php) ten sam powód
- 2026-03-25: GA4 item format standard: item_id (string), item_name, price (number), quantity (int), google_business_vertical: "retail"
- 2026-03-25: Brak user_data w purchase — wymaga analizy RODO
- 2026-03-25: summaryView() redirect guard usunięty — blokował kolejne zamówienia po pierwszym (z change.md instancji klienta)
- 2026-03-25: Token zamówienia z jednorazowego na TTL 30 min — backward compat z plain string
- 2026-03-25: logOrder() — logowanie bĹÄ™dĂłw zamĂłwieĹ„ do logs/logs-order-YYYY-MM-DD.log
- 2026-03-25: Redirect przy złym tokenie: /koszyk-podsumowanie zamiast /koszyk
- 2026-04-16: Custom fields delete fix — hidden marker `custom_field_name_present` zamiast `array_key_exists('custom_field_name')`
- 2026-03-25: Brak user_data w purchase wymaga analizy RODO
- 2026-03-25: summaryView() redirect guard usunięty blokował kolejne zamówienia po pierwszym (z change.md instancji klienta)
- 2026-03-25: Token zamówienia z jednorazowego na TTL 30 min backward compat z plain string
- 2026-03-25: logOrder() logowanie błędów zamówień do logs/logs-order-YYYY-MM-DD.log
- 2026-03-25: Redirect przy złym tokenie: /koszyk-podsumowanie zamiast /koszyk
- 2026-04-16: Custom fields delete fix hidden marker `custom_field_name_present` zamiast `array_key_exists('custom_field_name')`
### Deferred Issues
None.
@@ -87,5 +87,4 @@ Stopped at: Phase 15 complete, loop closed
Next action: Start next work with $paul-plan (or run /koniec-pracy for release flow)
Resume file: .paul/phases/15-scontainers-edit-save-fix/15-01-SUMMARY.md
---
*STATE.md — Updated after every significant action*
*STATE.md Updated after every significant action*

View File

@@ -1,3 +1,255 @@
# API
> Endpointy, kontrakty request/response, autentykacja.
## Scope
Dokument opisuje aktualne REST API dostepne przez `api.php` (ordersPRO + slowniki + produkty + kategorie).
## Base URL
- Endpoint techniczny: `/api.php`
- Routing odbywa sie przez query params:
- `endpoint` (np. `orders`, `products`)
- `action` (np. `list`, `get`)
Przyklad:
```text
GET /api.php?endpoint=orders&action=list
```
## Authentication
- Wymagany naglowek: `X-Api-Key: <api_key>`
- Klucz jest porownywany z wartoscia `pp_settings.api_key`
- Brak lub zly klucz:
- HTTP `401`
- payload:
```json
{
"status": "error",
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
```
## Response format
Sukces:
```json
{
"status": "ok",
"data": {}
}
```
Blad:
```json
{
"status": "error",
"code": "BAD_REQUEST",
"message": "..."
}
```
## Common HTTP/logic errors
- `400 BAD_REQUEST` - brak wymaganych parametrow/body
- `401 UNAUTHORIZED` - brak/zly API key
- `404 NOT_FOUND` - nieznany endpoint/action lub brak rekordu
- `405 METHOD_NOT_ALLOWED` - zla metoda HTTP
- `500 INTERNAL_ERROR` - blad serwera
## Endpoints
### Orders (`endpoint=orders`)
- `GET action=list`
- filtry (opcjonalne): `status`, `paid`, `date_from`, `date_to`, `updated_since`, `number`, `client`
- paginacja: `page` (default 1), `per_page` (default 50, max 100)
- `GET action=get&id={id}`
- `PUT action=change_status&id={id}`
- body JSON:
- `status_id` (required, int)
- `send_email` (optional, bool)
- `PUT action=set_paid&id={id}`
- body JSON optional: `send_email` (bool)
- `PUT action=set_unpaid&id={id}`
### Products (`endpoint=products`)
- `GET action=list`
- filtry:
- `search`, `status`, `promoted`
- `attribute_{attributeId}={valueId}` (np. `attribute_12=37`)
- sortowanie: `sort` (default `id`), `sort_dir` (default `DESC`)
- paginacja: `page`, `per_page` (max 100)
- `GET action=get&id={id}`
- `POST action=create`
- wymagane minimum:
- `languages` (array, co najmniej jeden jezyk z `name`)
- `price_brutto` (number >= 0)
- `PUT action=update&id={id}`
- partial update przez JSON body
- `GET action=variants&id={parentProductId}`
- dla produktu glownego (nie wariantu)
- `POST action=create_variant&id={parentProductId}`
- body:
- `attributes` (required array)
- opcjonalnie pola wariantu (np. `price_brutto`, `quantity`, `sku`, ...)
- `PUT action=update_variant&id={variantId}`
- partial update wariantu
- `DELETE action=delete_variant&id={variantId}`
- `POST action=upload_image`
- body:
- `id` (product id, required)
- `file_name` (required)
- `content_base64` (required)
- `alt` (optional)
- `o` (optional position)
### Dictionaries (`endpoint=dictionaries`)
- `GET action=statuses`
- `GET action=transports`
- `GET action=payment_methods`
- `GET action=attributes`
- `POST action=ensure_attribute`
- body: `name` (required), `type` (optional int), `lang` (optional, default `pl`)
- `POST action=ensure_attribute_value`
- body: `attribute_id` (required), `name` (required), `lang` (optional, default `pl`)
- `POST action=ensure_producer`
- body: `name` (required)
### Categories (`endpoint=categories`)
- `GET action=list`
- zwraca aktywne kategorie w formie flat list:
- `id`
- `parent_id`
- `title`
- tytuly pobierane najpierw w jezyku domyslnym (`pp_langs.start=1`), potem fallback.
## Source of truth (mapa API w kodzie)
### 1) Wejscie i dispatch
- `api.php`
- wykrywa request API przez `$_GET['endpoint']`
- ustawia JSON content-type
- tworzy `medoo` + `SettingsRepository`
- przekazuje sterowanie do `\api\ApiRouter::handle()`
- `autoload/api/ApiRouter.php`
- autentykacja (`X-Api-Key` vs `pp_settings.api_key`)
- walidacja `endpoint` i `action`
- mapowanie endpoint -> kontroler (`getControllerFactories()`)
- helpery odpowiedzi: `sendSuccess()`, `sendError()`
- helpery requestu: `getJsonBody()`, `requireMethod()`
### 2) Endpointy i kontrolery (runtime source)
#### `endpoint=orders`
- plik: `autoload/api/Controllers/OrdersApiController.php`
- akcje:
- `list` (GET)
- `get` (GET)
- `change_status` (PUT)
- `set_paid` (PUT)
- `set_unpaid` (PUT)
#### `endpoint=products`
- plik: `autoload/api/Controllers/ProductsApiController.php`
- akcje:
- `list` (GET)
- `get` (GET)
- `create` (POST)
- `update` (PUT)
- `variants` (GET)
- `create_variant` (POST)
- `update_variant` (PUT)
- `delete_variant` (DELETE)
- `upload_image` (POST)
#### `endpoint=dictionaries`
- plik: `autoload/api/Controllers/DictionariesApiController.php`
- akcje:
- `statuses` (GET)
- `transports` (GET)
- `payment_methods` (GET)
- `attributes` (GET)
- `ensure_attribute` (POST)
- `ensure_attribute_value` (POST)
- `ensure_producer` (POST)
#### `endpoint=categories`
- plik: `autoload/api/Controllers/CategoriesApiController.php`
- akcje:
- `list` (GET)
### 3) Warstwa domenowa pod API (shape danych)
- Orders:
- `Domain\Order\OrderRepository::listForApi()`
- `Domain\Order\OrderRepository::findForApi()`
- `Domain\Order\OrderAdminService` (zmiana statusu/platnosci)
- Products:
- `Domain\Product\ProductRepository::listForApi()`
- `Domain\Product\ProductRepository::findForApi()`
- `Domain\Product\ProductRepository::saveProduct()`
- metody wariantow `*VariantForApi()`
- Dictionaries:
- `Domain\ShopStatus\ShopStatusRepository`
- `Domain\Transport\TransportRepository`
- `Domain\PaymentMethod\PaymentMethodRepository`
- `Domain\Attribute\AttributeRepository`
- `Domain\Producer\ProducerRepository`
- Categories:
- bezposrednio query przez `$GLOBALS['mdb']` w `CategoriesApiController`
### 4) Testy API (behavior source)
- `tests/Unit/api/ApiRouterTest.php`
- `tests/Unit/api/Controllers/OrdersApiControllerTest.php`
- `tests/Unit/api/Controllers/ProductsApiControllerTest.php`
- `tests/Unit/api/Controllers/DictionariesApiControllerTest.php`
### 5) Dokumentacja kontraktu (human source)
- `api-docs/api-reference.json`
- `api-docs/index.html`
Uwaga: dokumentacja z `api-docs/*` moze byc starsza od runtime.
Zrodlem prawdy dla dzialania endpointow jest zawsze:
`api.php` + `autoload/api/ApiRouter.php` + aktualne kontrolery `autoload/api/Controllers/*`.
## Curl examples
Pobranie listy zamowien:
```bash
curl -X GET "https://example.com/api.php?endpoint=orders&action=list&page=1&per_page=20" \
-H "X-Api-Key: YOUR_API_KEY"
```
Zmiana statusu zamowienia:
```bash
curl -X PUT "https://example.com/api.php?endpoint=orders&action=change_status&id=123" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"status_id\": 6, \"send_email\": true}"
```
Dodanie wariantu produktu:
```bash
curl -X POST "https://example.com/api.php?endpoint=products&action=create_variant&id=50" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"attributes\":[{\"attribute_id\":12,\"attribute_value_id\":37}],\"price_brutto\":99.99,\"quantity\":10}"
```

View File

@@ -1,3 +1,192 @@
# ARCHITECTURE
> Struktura klas, modulow, przeplywow i zaleznosci w projekcie.
## Scope
Dokument opisuje aktualna architekture runtime projektu `shopPRO`:
- warstwy aplikacji i ich odpowiedzialnosci,
- przeplywy requestow (admin/front/api),
- Dependency Injection i miejsca wiringu,
- konwencje autoloadera i namespace,
- granice miedzy nowa architektura a pozostalosciami legacy.
## High-level layout
Kod aplikacji jest podzielony na 4 glowne warstwy:
1. `autoload/Domain/` - logika biznesowa i dostep do danych (28 modulow)
2. `autoload/admin/` - panel administracyjny (router + kontrolery + form system)
3. `autoload/front/` - frontend sklepu (router + layout engine + kontrolery/widoki)
4. `autoload/api/` - REST API (`api.php`, `ApiRouter`, kontrolery endpointow)
Komponenty wspoldzielone sa trzymane w `autoload/Shared/`.
## Directory map (runtime)
```text
autoload/
Domain/ # 28 modulow domenowych
Shared/ # Cache, Helpers, Tpl, Html, Email, Image
admin/
App.php # routing + DI factories
Controllers/ # 28 kontrolerow admin
Support/ # formularze/tabele
Validation/ # walidacja formularzy
ViewModels/ # modele widokow
front/
App.php # routing + DI factories + fallback legacy
LayoutEngine.php # silnik layoutu frontend
Controllers/ # 8 kontrolerow frontend
Views/ # 11 statycznych klas widokow
api/
ApiRouter.php # auth + endpoint dispatch
Controllers/ # 4 kontrolery API
```
## Entry points i przeplyw
### Admin (`admin/index.php`)
1. Bootstrap (sesja, DB, autoload)
2. `admin\App::update()` - uruchamia pending migracje
3. `admin\App::special_actions()` - logowanie/wylogowanie/2FA
4. `admin\App::render()`:
- auth gate (lub formularz logowania),
- `route()` -> kontroler + akcja,
- render przez `Shared\Tpl\Tpl`.
### Front (`index.php`)
1. Bootstrap (sesja, DB, autoload, jezyk)
2. Mapowanie URL (redirecty + routes)
3. `front\App::checkUrlParams()`
4. `front\App::route()`:
- artykul/produkt/kategoria,
- nowe kontrolery DI,
- fallback do `front\controls\*` (legacy, jesli istnieje)
5. `front\LayoutEngine::show()` sklada finalny HTML.
### API (`api.php`)
1. Bootstrap (bez sesji biznesowej)
2. Tworzenie `\api\ApiRouter`
3. `ApiRouter::handle()`:
- auth przez `X-Api-Key`,
- walidacja `endpoint` i `action`,
- dispatch do kontrolera API,
- JSON response przez `sendSuccess()` / `sendError()`.
Szczegolowa specyfikacja endpointow: `.paul/docs/API.md`.
## Dependency Injection (manual factories)
DI jest realizowane recznie w mapach factory:
- admin: `autoload/admin/App.php` -> `getControllerFactories()`
- front: `autoload/front/App.php` -> `getControllerFactories()`
- api: `autoload/api/ApiRouter.php` -> `getControllerFactories()`
Wzorzec:
- router tworzy repozytoria domenowe,
- repozytoria sa wstrzykiwane do kontrolerow przez konstruktor,
- kontroler wywoluje metody domenowe i zwraca HTML/JSON.
## Autoloader i namespace rules
Kazdy entry point korzysta z custom autoloadera:
1. `autoload/{namespace}/class.{ClassName}.php` (legacy)
2. `autoload/{namespace}/{ClassName}.php` (nowy format)
Mapowanie namespace -> katalog (case-sensitive na Linux):
- `\Domain\` -> `autoload/Domain/`
- `\Shared\` -> `autoload/Shared/`
- `\admin\` -> `autoload/admin/` (male `a`)
- `\front\` -> `autoload/front/`
- `\api\` -> `autoload/api/`
Nie uzywac `\Admin\` (duze `A`), bo katalog runtime to `admin/`.
## Domain layer (28 modulow)
Aktualne moduly:
`Article`, `Attribute`, `Banner`, `Basket`, `Cache`, `Category`, `Client`, `Coupon`, `CronJob`, `Dashboard`, `Dictionaries`, `Integrations`, `Languages`, `Layouts`, `Newsletter`, `Order`, `Pages`, `PaymentMethod`, `Producer`, `Product`, `ProductSet`, `Promotion`, `Scontainers`, `Settings`, `ShopStatus`, `Transport`, `Update`, `User`.
Zasada: logika biznesowa i dostep do danych sa w Domain, bez duplikowania osobnych warstw "admin service" i "front service" dla tych samych przypadkow (wyjatki tylko tam, gdzie historycznie juz istnieja, np. `OrderAdminService`).
## Admin architecture
- Router: `admin\App`
- Kontrolery: `autoload/admin/Controllers/*.php` (28 klas)
- Form system:
- `admin\ViewModels\Forms\*`
- `admin\Support\Forms\FormRequestHandler`
- `admin\Support\Forms\FormFieldRenderer`
- `admin\Validation\FormValidator`
- template: `admin/templates/components/form-edit.php`
Admin ma pelne DI i nie korzysta z fallbacku na legacy kontrolery.
## Front architecture
- Router: `front\App`
- Layout engine: `front\LayoutEngine`
- Kontrolery DI: `autoload/front/Controllers/*.php` (8 klas)
- Widoki statyczne: `autoload/front/Views/*.php` (11 klas)
Wazne: frontend nadal ma fallback do `\front\controls\*`, wiec architektura jest hybrydowa (new DI + remaining legacy paths).
## API architecture
- Router: `api\ApiRouter`
- Endpointy: `orders`, `products`, `dictionaries`, `categories`
- Kontrolery: `autoload/api/Controllers/*ApiController.php` (4 klasy)
- API jest stateless, autoryzowane naglowkiem `X-Api-Key`
Source-of-truth API to runtime:
- `api.php`
- `autoload/api/ApiRouter.php`
- `autoload/api/Controllers/*`
## Shared components
Najwazniejsze klasy wspoldzielone:
- `Shared\Cache\CacheHandler`, `Shared\Cache\RedisConnection`
- `Shared\Helpers\Helpers`
- `Shared\Tpl\Tpl`
- `Shared\Html\Html`
- `Shared\Email\Email`
- `Shared\Image\ImageManipulator`
## Data and cache conventions
- ORM: Medoo (`$mdb`)
- Prefix tabel: `pp_`
- Cache: Redis, domyslnie TTL `86400`
- Dane cache czesto serializowane (`serialize`/`unserialize`)
- Czyszczenie cache produktu: pattern `shop\\product:{id}:*`
## Security boundaries
- Admin:
- sesja uzytkownika admina,
- CSRF token w akcjach POST,
- 2FA email flow (pending session + verify).
- API:
- `X-Api-Key` porownywany przez `hash_equals()`,
- brak logiki sesyjnej.
- Front:
- sesja klienta + walidacja przeplywow frontendowych.
## Source files
Najwazniejsze pliki do szybkiej orientacji:
- `autoload/admin/App.php`
- `autoload/front/App.php`
- `autoload/front/LayoutEngine.php`
- `autoload/api/ApiRouter.php`
- `.paul/docs/API.md`
- `docs/PROJECT_STRUCTURE.md`

View File

@@ -1,3 +1,228 @@
# DB_SCHEMA
# DB_SCHEMA
> Schemat bazy danych — tabele, kolumny, FK, indeksy.
## Scope
Dokument opisuje praktyczny schema map dla `shopPRO`:
- najwazniejsze tabele i relacje,
- grupowanie po domenach biznesowych,
- kluczowe kolumny i indeksy, ktore maja znaczenie runtime,
- mapowanie tabela -> warstwa Domain.
Pelna lista tabel i historyczne notki migracyjne:
`docs/DATABASE_STRUCTURE.md` (source of truth dla detali kolumnowych).
## Konwencje globalne
- ORM: Medoo (`$mdb`)
- Prefix tabel: `pp_`
- Primary key: najczesciej `id` (INT AUTO_INCREMENT)
- Jezyki/translations: zwykle tabele `*_langs` z kluczem `lang_id`
- Wiele-do-wielu: tabele lacznikowe `*_products`, `*_payment_methods`, itp.
## Core commerce
### Produkty
- `pp_shop_products`
- core produktu i wariantu (`parent_id` dla kombinacji)
- ceny (`price_brutto`, `price_brutto_promo`), stany (`quantity`)
- flagi (`status`, `archive`, `promoted`)
- `pp_shop_products_langs`
- nazwy/opisy per jezyk
- `pp_shop_products_images`
- obrazy produktu
- `pp_shop_products_categories`
- przypisania produkt-kategoria
- `pp_shop_products_attributes`
- przypisania wariantu do wartosci cech
- `pp_shop_products_custom_fields`
- dodatkowe pola produktu
Warstwa: `Domain\Product\ProductRepository`, `Domain\Attribute\AttributeRepository`.
### Kategorie
- `pp_shop_categories`
- drzewo kategorii (`parent_id`), status, kolejnosc
- `pp_shop_categories_langs`
- tresci SEO i opisy kategorii
Warstwa: `Domain\Category\CategoryRepository`.
### Zamowienia
- `pp_shop_orders`
- dane klienta "w momencie zakupu", summary, status/platnosc, daty
- kluczowe pole integracyjne: `updated_at` (polling API)
Warstwa: `Domain\Order\OrderRepository`, `Domain\Order\OrderAdminService`.
### Klienci
- `pp_shop_clients`
- konto klienta i dane adresowe/logowania (uzywane przez ClientRepository)
Warstwa: `Domain\Client\ClientRepository`.
## Slowniki i checkout
### Platnosci
- `pp_shop_payment_methods`
- status, opis, mapowanie Apilo
- limity kwotowe: `min_order_amount`, `max_order_amount`
- COD flag: `is_cod`
Warstwa: `Domain\PaymentMethod\PaymentMethodRepository`.
### Transport
- `pp_shop_transports`
- koszt, status, limity, mapowanie Apilo
- `pp_shop_transport_payment_methods`
- relacja transport <-> platnosc (N:M)
Warstwa: `Domain\Transport\TransportRepository`.
### Statusy zamowien
- `pp_shop_statuses`
- statusy predefiniowane, kolor, mapowanie Apilo
Warstwa: `Domain\ShopStatus\ShopStatusRepository`.
## Marketing i merch
### Promocje i kupony
- `pp_shop_promotion`
- reguly promocji, daty aktywnosci, warunki i zakresy (JSON categories)
- `pp_shop_coupon`
- kupony, licznik uzyc, ograniczenia
Warstwa: `Domain\Promotion\PromotionRepository`, `Domain\Coupon\CouponRepository`.
### Producenci
- `pp_shop_producer`
- `pp_shop_producer_lang`
Warstwa: `Domain\Producer\ProducerRepository`.
### Zestawy produktow
- `pp_shop_product_sets`
- `pp_shop_product_sets_products`
Warstwa: `Domain\ProductSet\ProductSetRepository`.
### Cechy i wartosci
- `pp_shop_attributes`
- `pp_shop_attributes_langs`
- `pp_shop_attributes_values`
- `pp_shop_attributes_values_langs`
Warstwa: `Domain\Attribute\AttributeRepository`.
## CMS i frontend content
### Artykuly
- `pp_articles`
- `pp_articles_langs`
- `pp_articles_pages`
- `pp_articles_images`
- `pp_articles_files`
Warstwa: `Domain\Article\ArticleRepository`.
### Strony i layouty
- `pp_pages`
- `pp_layouts`
- `pp_layouts_pages`
- `pp_layouts_categories`
Warstwa: `Domain\Pages\PagesRepository`, `Domain\Layouts\LayoutsRepository`.
### Banery i kontenery statyczne
- `pp_banners`
- `pp_banners_langs`
- `pp_scontainers`
- `pp_scontainers_langs`
Warstwa: `Domain\Banner\BannerRepository`, `Domain\Scontainers\ScontainersRepository`.
## Ustawienia i system
### Ustawienia aplikacji
- `pp_settings`
- klucze globalne (w tym `api_key` dla REST API)
- `pp_shop_apilo_settings`
- `pp_shop_shoppro_settings`
Warstwa: `Domain\Settings\SettingsRepository`, `Domain\Integrations\IntegrationsRepository`.
### Jezyki i tlumaczenia
- `pp_langs`
- `pp_langs_translations`
Warstwa: `Domain\Languages\LanguagesRepository`.
### Uzytkownicy admina
- `pp_users`
- login, hash hasla, status
- pola 2FA (`twofa_*`)
Warstwa: `Domain\User\UserRepository`.
## Routing i URL mapping
- `pp_routes`
- regex `pattern` -> `destination` query string
- obsluguje trasy encji oraz trasy systemowe
- cache Redis: `pp_routes:all`
Runtime wykorzystanie:
- `index.php`
- `Shared\Helpers\Helpers::htacces()`
- repozytoria encji generujace/odswiezajace trasy.
## Kolejka cron
- `pp_cron_jobs`
- status processing pipeline (`pending`, `processing`, `completed`, `failed`, `cancelled`)
- retry/backoff: `attempts`, `max_attempts`, `scheduled_at`
- indeksy:
- `(status, priority, scheduled_at)`
- `(job_type)`
- `(status)`
- `pp_cron_schedules`
- harmonogramy okresowe (`interval_seconds`, `next_run_at`)
- indeks `(enabled, next_run_at)`
Warstwa: `Domain\CronJob\CronJobRepository`, `Domain\CronJob\CronJobProcessor`.
## Najwazniejsze relacje (FK logiczne)
- Produkt glowny -> wariant: `pp_shop_products.parent_id -> pp_shop_products.id`
- Produkt -> tlumaczenia: `pp_shop_products_langs.product_id -> pp_shop_products.id`
- Produkt -> kategoria: `pp_shop_products_categories.product_id -> pp_shop_products.id`
- Kategoria -> tlumaczenia: `pp_shop_categories_langs.category_id -> pp_shop_categories.id`
- Zamowienie -> klient: `pp_shop_orders.client_id -> pp_shop_clients.id` (opcjonalne)
- Transport <-> platnosc: `pp_shop_transport_payment_methods`
- Cecha -> wartosci -> warianty: `attributes -> values -> shop_products_attributes`
- Producent -> tlumaczenia: `pp_shop_producer_lang.producer_id -> pp_shop_producer.id`
- Kontener -> tlumaczenia: `pp_scontainers_langs.container_id -> pp_scontainers.id`
## Uwaga operacyjna
Ten dokument jest skrotem architektonicznym.
Przy zmianach SQL/migracji zawsze aktualizuj rownolegle:
1. `docs/DATABASE_STRUCTURE.md` (detal techniczny)
2. `.paul/docs/DB_SCHEMA.md` (mapa domenowa i impact runtime)

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -2,5 +2,5 @@ projectKey=shopPRO
serverUrl=https://sonar.project-pro.pl
serverVersion=26.3.0.120487
dashboardUrl=https://sonar.project-pro.pl/dashboard?id=shopPRO
ceTaskId=77fcbbea-9d8f-45d6-86d7-b262e33f979e
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=77fcbbea-9d8f-45d6-86d7-b262e33f979e
ceTaskId=3051d3c7-50d0-4263-bdc3-12917447a707
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=3051d3c7-50d0-4263-bdc3-12917447a707

165
AGENTS.md
View File

@@ -1,4 +1,4 @@
# CLAUDE.md
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
@@ -7,19 +7,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
shopPRO is a PHP e-commerce platform with an admin panel and customer-facing storefront. It uses Medoo ORM (`$mdb`), Redis caching, and a Domain-Driven Design architecture with Dependency Injection (migration from legacy architecture complete).
## Zasady pisania kodu
- Kod ma być czytelny dla obcego: jasne nazwy, mało magii
- Brak „skrótów na szybko typu logika w widokach, copy-paste, losowe helpery bez spójności
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 3050 linii (jeśli dłuższe dzielić)
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
- KaĹĽda funkcja/klasa ma mieć jednÄ… odpowiedzialność, zwykle do 30–50 linii (jeĹ›li dĹuĹĽsze – dzielić)
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
- Nazewnictwo:
- klasy: PascalCase
- metody/zmienne: camelCase
- stałe: UPPER_SNAKE_CASE
- Zero skrótologii w nazwach (np. $d, $tmp, $x1) poza pętlami 23 linijki
- medoo + prepared statements bez wyjątków (żadnego sklejania SQL stringiem)
- stałe: UPPER_SNAKE_CASE
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
- medoo + prepared statements bez wyjÄ…tkĂłw (ĹĽadnego sklejania SQL stringiem)
- XSS: escape w widokach (np. helper e())
- CSRF dla formularzy, sensowna obsługa sesji
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego, nie „co”
- CSRF dla formularzy, sensowna obsługa sesji
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
## PHP Version Constraint
@@ -36,7 +36,7 @@ shopPRO is a PHP e-commerce platform with an admin panel and customer-facing sto
### Running Tests
```bash
# Full suite (recommended PowerShell, auto-finds php)
# Full suite (recommended — PowerShell, auto-finds php)
./test.ps1
# Specific file
@@ -61,50 +61,50 @@ See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packag
### Directory Structure
```
shopPRO/
├── autoload/ # Autoloaded classes (core codebase)
│ ├── Domain/ # Business logic repositories (\Domain\)
│ ├── Shared/ # Shared utilities (\Shared\)
│ │ ├── Cache/ # CacheHandler, RedisConnection
│ │ ├── Email/ # Email (PHPMailer wrapper)
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
│ │ ├── Html/ # Html utility
│ │ ├── Image/ # ImageManipulator
│ │ └── Tpl/ # Template engine
│ ├── api/ # REST API layer (\api\)
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
│ │ └── Controllers/ # API controllers (\api\Controllers\)
│ ├── admin/ # Admin panel layer
│ │ ├── App.php # Admin router (\admin\App)
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) 28 controllers
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
│ │ ├── Validation/ # FormValidator
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
│ └── front/ # Frontend layer
├── App.php # Frontend router (\front\App)
├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
├── Controllers/ # DI controllers (\front\Controllers\) 8 controllers
└── Views/ # Static views (\front\Views\) 11 view classes
├── admin/ # Admin panel
│ ├── templates/ # Admin view templates
│ └── layout/ # Admin CSS/JS/icons
├── templates/ # Frontend view templates
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
├── tests/ # PHPUnit tests
│ ├── bootstrap.php
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
│ └── Unit/
├── Domain/ # Repository tests
├── admin/Controllers/ # Controller tests
└── api/ # API tests
├── updates/ # Update packages for clients
├── docs/ # Technical documentation
├── config.php # Database/Redis config (not in repo)
├── index.php # Frontend entry point
├── ajax.php # Frontend AJAX handler
├── admin/index.php # Admin entry point
├── admin/ajax.php # Admin AJAX handler
├── cron.php # CRON jobs (Apilo sync)
└── api.php # REST API (ordersPRO + Ekomi)
├── autoload/ # Autoloaded classes (core codebase)
│ ├── Domain/ # Business logic repositories (\Domain\)
│ ├── Shared/ # Shared utilities (\Shared\)
│ │ ├── Cache/ # CacheHandler, RedisConnection
│ │ ├── Email/ # Email (PHPMailer wrapper)
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
│ │ ├── Html/ # Html utility
│ │ ├── Image/ # ImageManipulator
│ │ └── Tpl/ # Template engine
│ ├── api/ # REST API layer (\api\)
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
│ │ └── Controllers/ # API controllers (\api\Controllers\)
│ ├── admin/ # Admin panel layer
│ │ ├── App.php # Admin router (\admin\App)
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) — 28 controllers
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
│ │ ├── Validation/ # FormValidator
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
│ └── front/ # Frontend layer
│ ├── App.php # Frontend router (\front\App)
│ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
│ ├── Controllers/ # DI controllers (\front\Controllers\) — 8 controllers
│ └── Views/ # Static views (\front\Views\) — 11 view classes
├── admin/ # Admin panel
│ ├── templates/ # Admin view templates
│ └── layout/ # Admin CSS/JS/icons
├── templates/ # Frontend view templates
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
├── tests/ # PHPUnit tests
│ ├── bootstrap.php
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
│ └── Unit/
│ ├── Domain/ # Repository tests
│ ├── admin/Controllers/ # Controller tests
│ └── api/ # API tests
├── updates/ # Update packages for clients
├── docs/ # Technical documentation
├── config.php # Database/Redis config (not in repo)
├── index.php # Frontend entry point
├── ajax.php # Frontend AJAX handler
├── admin/index.php # Admin entry point
├── admin/ajax.php # Admin AJAX handler
├── cron.php # CRON jobs (Apilo sync)
└── api.php # REST API (ordersPRO + Ekomi)
```
### Autoloader
@@ -114,19 +114,19 @@ Custom autoloader in each entry point (not Composer autoload at runtime). Tries
2. `autoload/{namespace}/{ClassName}.php` (PSR-4 style, fallback)
### Namespace Conventions (case-sensitive on Linux!)
- `\Domain\` `autoload/Domain/` (uppercase D)
- `\admin\Controllers\` `autoload/admin/Controllers/` (lowercase a)
- `\Shared\` `autoload/Shared/`
- `\api\` `autoload/api/`
- Do NOT use `\Admin\` (uppercase A) the server directory is `admin/` (lowercase)
- `\shop\` namespace is **deleted** all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
- `\Domain\` → `autoload/Domain/` (uppercase D)
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
- `\Shared\` → `autoload/Shared/`
- `\api\` → `autoload/api/`
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
### Domain-Driven Architecture (migration complete)
All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `front/controls/`, `front/view/`, `front/factory/`, `shop/`) have been deleted. All modules now use this pattern:
**Domain Layer** (`autoload/Domain/{Module}/`):
- `{Module}Repository.php` data access, business logic, Redis caching
- `{Module}Repository.php` — data access, business logic, Redis caching
- Constructor DI with `$db` (Medoo instance)
- Methods serve both admin and frontend (shared Domain, no separate services)
@@ -141,7 +141,7 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
- Wired in `front\App::getControllerFactories()`
**Frontend Views** (`autoload/front/Views/`):
- Static classes, no state, no DI pure rendering
- Static classes, no state, no DI — pure rendering
**API Controllers** (`autoload/api/Controllers/`):
- DI via constructor, stateless (no session)
@@ -151,13 +151,13 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
### Key Classes
| Class | Purpose |
|-------|---------|
| `\admin\App` | Admin router maps URL segments to controllers |
| `\front\App` | Frontend router `route()`, `checkUrlParams()` |
| `\front\LayoutEngine` | Frontend layout engine `show()`, tag replacement |
| `\admin\App` | Admin router — maps URL segments to controllers |
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
| `\Shared\Helpers\Helpers` | Utility methods (SEO, email, cache clearing) |
| `\Shared\Tpl\Tpl` | Template engine `render()`, `set()` |
| `\Shared\Cache\CacheHandler` | Redis cache `get()`, `set()`, `delete()`, `deletePattern()` |
| `\api\ApiRouter` | REST API router auth, routing, response helpers |
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |
### Database
- ORM: Medoo (`$mdb` global variable, injected via DI in new code)
@@ -179,7 +179,7 @@ Universal form system for admin edit views. Docs: `docs/FORM_EDIT_SYSTEM.md`.
- Clear product cache: `\Shared\Helpers\Helpers::clear_product_cache($id)`
- Pattern delete: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
- Default TTL: 86400 (24h)
- Data is serialized requires `unserialize()` after `get()`
- Data is serialized — requires `unserialize()` after `get()`
- Config: `config.php` (`$config['redis']`)
## Code Patterns
@@ -203,7 +203,7 @@ $controller = new \admin\Controllers\ExampleController($repo);
```
### Medoo ORM pitfalls
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 has caused bugs
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
- `$mdb->get()` returns `null` when no record, NOT `false`
- After `$mdb->insert()`, check `$mdb->id()` to confirm success
@@ -222,18 +222,19 @@ $controller = new \admin\Controllers\ExampleController($repo);
When user says **"KONIEC PRACY"**, run `/koniec-pracy` (see `.claude/commands/koniec-pracy.md`).
Before starting implementation, review current state of docs.
For documentation updates, modify only .paul/docs/* unless the user explicitly asks to update root docs/*.
## Key Documentation
- `docs/MEMORY.md` project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
- `docs/PROJECT_STRUCTURE.md` current architecture, layers, cache, entry points, integrations
- `docs/DATABASE_STRUCTURE.md` full database schema
- `docs/TESTING.md` test suite guide and structure
- `docs/FORM_EDIT_SYSTEM.md` form system architecture
- `docs/CHANGELOG.md` version history
- `api-docs/api-reference.json` REST API documentation (ordersPRO)
- `api-docs/index.html` REST API documentation (ordersPRO)
- `docs/UPDATE_INSTRUCTIONS.md` how to build client update packages
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
- `docs/DATABASE_STRUCTURE.md` — full database schema
- `docs/TESTING.md` — test suite guide and structure
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
- `docs/CHANGELOG.md` — version history
- `api-docs/api-reference.json` — REST API documentation (ordersPRO)
- `api-docs/index.html` — REST API documentation (ordersPRO)
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
## Wszystkie pliki które tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twórz w folderze temp
## Wszystkie pliki ktĂłre tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twĂłrz w folderze temp

174
CLAUDE.md
View File

@@ -1,4 +1,4 @@
# CLAUDE.md
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
@@ -7,19 +7,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
shopPRO is a PHP e-commerce platform with an admin panel and customer-facing storefront. It uses Medoo ORM (`$mdb`), Redis caching, and a Domain-Driven Design architecture with Dependency Injection (migration from legacy architecture complete).
## Zasady pisania kodu
- Kod ma być czytelny dla obcego: jasne nazwy, mało magii
- Brak „skrótów na szybko typu logika w widokach, copy-paste, losowe helpery bez spójności
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 3050 linii (jeśli dłuższe dzielić)
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
- KaĹĽda funkcja/klasa ma mieć jednÄ… odpowiedzialność, zwykle do 30–50 linii (jeĹ›li dĹuĹĽsze – dzielić)
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
- Nazewnictwo:
- klasy: PascalCase
- metody/zmienne: camelCase
- stałe: UPPER_SNAKE_CASE
- Zero skrótologii w nazwach (np. $d, $tmp, $x1) poza pętlami 23 linijki
- medoo + prepared statements bez wyjątków (żadnego sklejania SQL stringiem)
- stałe: UPPER_SNAKE_CASE
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
- medoo + prepared statements bez wyjÄ…tkĂłw (ĹĽadnego sklejania SQL stringiem)
- XSS: escape w widokach (np. helper e())
- CSRF dla formularzy, sensowna obsługa sesji
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego, nie „co”
- CSRF dla formularzy, sensowna obsługa sesji
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
## PHP Version Constraint
@@ -36,7 +36,7 @@ shopPRO is a PHP e-commerce platform with an admin panel and customer-facing sto
### Running Tests
```bash
# Full suite (recommended PowerShell, auto-finds php)
# Full suite (recommended — PowerShell, auto-finds php)
./test.ps1
# Specific file
@@ -58,57 +58,57 @@ PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpun
Current suite: **823 tests, 2284 assertions**.
### Creating Updates
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs. ZIP structure must start directly from project directories no version subfolder inside the archive.
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs. ZIP structure must start directly from project directories — no version subfolder inside the archive.
## Architecture
### Directory Structure
```
shopPRO/
├── autoload/ # Autoloaded classes (core codebase)
│ ├── Domain/ # Business logic repositories (\Domain\)
│ ├── Shared/ # Shared utilities (\Shared\)
│ │ ├── Cache/ # CacheHandler, RedisConnection
│ │ ├── Email/ # Email (PHPMailer wrapper)
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
│ │ ├── Html/ # Html utility
│ │ ├── Image/ # ImageManipulator
│ │ └── Tpl/ # Template engine
│ ├── api/ # REST API layer (\api\)
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
│ │ └── Controllers/ # API controllers (\api\Controllers\)
│ ├── admin/ # Admin panel layer
│ │ ├── App.php # Admin router (\admin\App)
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) 28 controllers
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
│ │ ├── Validation/ # FormValidator
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
│ └── front/ # Frontend layer
├── App.php # Frontend router (\front\App)
├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
├── Controllers/ # DI controllers (\front\Controllers\) 8 controllers
└── Views/ # Static views (\front\Views\) 11 view classes
├── admin/ # Admin panel
│ ├── templates/ # Admin view templates
│ └── layout/ # Admin CSS/JS/icons
├── templates/ # Frontend view templates
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
├── tests/ # PHPUnit tests
│ ├── bootstrap.php
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
│ └── Unit/
├── Domain/ # Repository tests
├── admin/Controllers/ # Controller tests
└── api/ # API tests
├── updates/ # Update packages for clients
├── docs/ # Technical documentation
├── config.php # Database/Redis config (not in repo)
├── index.php # Frontend entry point
├── ajax.php # Frontend AJAX handler
├── admin/index.php # Admin entry point
├── admin/ajax.php # Admin AJAX handler
├── cron.php # CRON jobs (Apilo sync)
└── api.php # REST API (ordersPRO + Ekomi)
├── autoload/ # Autoloaded classes (core codebase)
│ ├── Domain/ # Business logic repositories (\Domain\)
│ ├── Shared/ # Shared utilities (\Shared\)
│ │ ├── Cache/ # CacheHandler, RedisConnection
│ │ ├── Email/ # Email (PHPMailer wrapper)
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
│ │ ├── Html/ # Html utility
│ │ ├── Image/ # ImageManipulator
│ │ └── Tpl/ # Template engine
│ ├── api/ # REST API layer (\api\)
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
│ │ └── Controllers/ # API controllers (\api\Controllers\)
│ ├── admin/ # Admin panel layer
│ │ ├── App.php # Admin router (\admin\App)
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) — 28 controllers
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
│ │ ├── Validation/ # FormValidator
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
│ └── front/ # Frontend layer
│ ├── App.php # Frontend router (\front\App)
│ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
│ ├── Controllers/ # DI controllers (\front\Controllers\) — 8 controllers
│ └── Views/ # Static views (\front\Views\) — 11 view classes
├── admin/ # Admin panel
│ ├── templates/ # Admin view templates
│ └── layout/ # Admin CSS/JS/icons
├── templates/ # Frontend view templates
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
├── tests/ # PHPUnit tests
│ ├── bootstrap.php
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
│ └── Unit/
│ ├── Domain/ # Repository tests
│ ├── admin/Controllers/ # Controller tests
│ └── api/ # API tests
├── updates/ # Update packages for clients
├── docs/ # Technical documentation
├── config.php # Database/Redis config (not in repo)
├── index.php # Frontend entry point
├── ajax.php # Frontend AJAX handler
├── admin/index.php # Admin entry point
├── admin/ajax.php # Admin AJAX handler
├── cron.php # CRON jobs (Apilo sync)
└── api.php # REST API (ordersPRO + Ekomi)
```
### Autoloader
@@ -118,19 +118,19 @@ Custom autoloader in each entry point (not Composer autoload at runtime). Tries
2. `autoload/{namespace}/{ClassName}.php` (PSR-4 style, fallback)
### Namespace Conventions (case-sensitive on Linux!)
- `\Domain\` `autoload/Domain/` (uppercase D)
- `\admin\Controllers\` `autoload/admin/Controllers/` (lowercase a)
- `\Shared\` `autoload/Shared/`
- `\api\` `autoload/api/`
- Do NOT use `\Admin\` (uppercase A) the server directory is `admin/` (lowercase)
- `\shop\` namespace is **deleted** all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
- `\Domain\` → `autoload/Domain/` (uppercase D)
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
- `\Shared\` → `autoload/Shared/`
- `\api\` → `autoload/api/`
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
### Domain-Driven Architecture (migration complete)
All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `front/controls/`, `front/view/`, `front/factory/`, `shop/`) have been deleted. All modules now use this pattern:
**Domain Layer** (`autoload/Domain/{Module}/`):
- `{Module}Repository.php` data access, business logic, Redis caching
- `{Module}Repository.php` — data access, business logic, Redis caching
- Constructor DI with `$db` (Medoo instance)
- Methods serve both admin and frontend (shared Domain, no separate services)
@@ -145,7 +145,7 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
- Wired in `front\App::getControllerFactories()`
**Frontend Views** (`autoload/front/Views/`):
- Static classes, no state, no DI pure rendering
- Static classes, no state, no DI — pure rendering
**API Controllers** (`autoload/api/Controllers/`):
- DI via constructor, stateless (no session)
@@ -155,13 +155,13 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
### Key Classes
| Class | Purpose |
|-------|---------|
| `\admin\App` | Admin router maps URL segments to controllers |
| `\front\App` | Frontend router `route()`, `checkUrlParams()` |
| `\front\LayoutEngine` | Frontend layout engine `show()`, tag replacement |
| `\admin\App` | Admin router — maps URL segments to controllers |
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
| `\Shared\Helpers\Helpers` | Utility methods (SEO, email, cache clearing) |
| `\Shared\Tpl\Tpl` | Template engine `render()`, `set()` |
| `\Shared\Cache\CacheHandler` | Redis cache `get()`, `set()`, `delete()`, `deletePattern()` |
| `\api\ApiRouter` | REST API router auth, routing, response helpers |
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |
### Database
- ORM: Medoo (`$mdb` global variable, injected via DI in new code)
@@ -183,7 +183,7 @@ Universal form system for admin edit views. Docs: `docs/FORM_EDIT_SYSTEM.md`.
- Clear product cache: `\Shared\Helpers\Helpers::clear_product_cache($id)`
- Pattern delete: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
- Default TTL: 86400 (24h)
- Data is serialized requires `unserialize()` after `get()`
- Data is serialized — requires `unserialize()` after `get()`
- Config: `config.php` (`$config['redis']`)
## Code Patterns
@@ -207,7 +207,7 @@ $controller = new \admin\Controllers\ExampleController($repo);
```
### Medoo ORM pitfalls
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 has caused bugs
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
- `$mdb->get()` returns `null` when no record, NOT `false`
- After `$mdb->insert()`, check `$mdb->id()` to confirm success
@@ -226,21 +226,23 @@ $controller = new \admin\Controllers\ExampleController($repo);
When user says **"KONIEC PRACY"**, run `/koniec-pracy` (see `.claude/commands/koniec-pracy.md`).
Before starting implementation, review current state of docs.
For documentation updates, modify only .paul/docs/* unless the user explicitly asks to update root docs/*.
## Key Documentation
- `docs/MEMORY.md` project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
- `docs/PROJECT_STRUCTURE.md` current architecture, layers, cache, entry points, integrations
- `docs/DATABASE_STRUCTURE.md` full database schema
- `docs/TESTING.md` test suite guide and structure
- `docs/FORM_EDIT_SYSTEM.md` form system architecture
- `docs/CLASS_CATALOG.md` full catalog of all classes with descriptions
- `docs/TODO.md` outstanding tasks and planned features
- `docs/CRON_QUEUE_PLAN.md` planned cron/queue architecture
- `docs/CHANGELOG.md` version history
- `api-docs/api-reference.json` REST API documentation (ordersPRO)
- `api-docs/index.html` REST API documentation (ordersPRO)
- `docs/UPDATE_INSTRUCTIONS.md` how to build client update packages
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
- `docs/DATABASE_STRUCTURE.md` — full database schema
- `docs/TESTING.md` — test suite guide and structure
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
- `docs/CLASS_CATALOG.md` — full catalog of all classes with descriptions
- `docs/TODO.md` — outstanding tasks and planned features
- `docs/CRON_QUEUE_PLAN.md` — planned cron/queue architecture
- `docs/CHANGELOG.md` — version history
- `api-docs/api-reference.json` — REST API documentation (ordersPRO)
- `api-docs/index.html` — REST API documentation (ordersPRO)
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
## Wszystkie pliki ktĂłre tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twĂłrz w folderze temp
## Wszystkie pliki które tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twórz w folderze temp

View File

@@ -1,79 +0,0 @@
# Refaktoryzacja autoloadera — centralizacja
## Problem
Funkcja `__autoload_my_classes()` jest zduplikowana w każdym entry poincie:
- `index.php`
- `admin/index.php`
- `admin/ajax.php`
- `ajax.php`
- `api.php`
- `cron.php`
- `download.php`
- `cron-turstmate.php`
- `cron/cron-xml.php`
Każdy plik zawiera identyczną (lub prawie identyczną) kopię autoloadera. Warianty różnią się ścieżką bazową (`autoload/` vs `../autoload/`) i drobnymi detalami (np. Savant3 special case tylko w `admin/ajax.php`).
## Rozwiązanie
Utworzyć centralny plik `autoload/autoloader.php` z jedną definicją autoloadera, używając `__DIR__` zamiast ścieżek relatywnych.
### Plik: `autoload/autoloader.php`
```php
<?php
function __autoload_my_classes( $classname )
{
$base = __DIR__ . '/';
$q = explode( '\\', $classname );
$c = array_pop( $q );
// Savant3 — special case
if ( $c == 'Savant3' )
{
$f = $base . 'Savant3.php';
if ( file_exists( $f ) ) { require_once( $f ); return; }
}
$path = implode( '/', $q );
// 1. Legacy: class.ClassName.php
$f = $base . $path . '/class.' . $c . '.php';
if ( file_exists( $f ) ) { require_once( $f ); return; }
// 2. PSR-4: ClassName.php
$f = $base . $path . '/' . $c . '.php';
if ( file_exists( $f ) ) require_once( $f );
}
spl_autoload_register( '__autoload_my_classes' );
```
### Zmiana w entry pointach
Zamienić definicję funkcji + `spl_autoload_register` na jedną linię:
```php
// Root entry points (index.php, ajax.php, api.php, cron.php, download.php):
require_once __DIR__ . '/autoload/autoloader.php';
// Admin entry points (admin/index.php, admin/ajax.php):
require_once __DIR__ . '/../autoload/autoloader.php';
// Cron subdirectory (cron/cron-xml.php):
require_once __DIR__ . '/../autoload/autoloader.php';
```
## Korzyści
- **DRY** — jedna definicja zamiast 9+ kopii
- **Bezpieczeństwo** — poprawka w jednym miejscu działa wszędzie
- **`__DIR__`** — ścieżki absolutne, niezależne od cwd
- **Savant3** — obsłużony centralnie, nie tylko w admin/ajax.php
## Uwagi
- Reszta kodu w entry pointach (config.php, medoo, session) pozostaje bez zmian
- Rozwiązanie wdrożone i przetestowane w cmsPRO

View File

@@ -1,147 +0,0 @@
# CARL — Dynamiczne reguły dla Claude Code
[CARL](https://github.com/ChristopherKahler/carl) (Context Augmentation & Reinforcement Layer) to system wstrzykiwania reguł do Claude Code — reguły ładują się **tylko gdy są potrzebne**, zamiast zapychać kontekst sesji regułami, których akurat nie używasz.
Zainstalowany globalnie w `~/.carl/`.
---
## Jak to działa
Claude Code ma ograniczony kontekst sesji. CARL rozwiązuje problem "zbyt wielu reguł na raz":
- **Domeny** — zestawy reguł uruchamiane automatycznie przez słowa kluczowe w prompcie
- **Star-commands** (`*dev`, `*review`, itp.) — tryby uruchamiane ręcznie przez wpisanie `*nazwatrybe`
- **Global** — reguły zawsze aktywne (minimalne, uniwersalne)
Efekt: zamiast 50 reguł na każdą sesję, Claude dostaje 510 tych, które są relevantne teraz.
---
## Struktura plików
```
~/.carl/ # Globalna konfiguracja (wszystkie projekty)
├── manifest # Rejestr domen (stany + słowa kluczowe)
├── global # Reguły zawsze aktywne
├── commands # Definicje star-commands
├── context # Reguły kontekstowe (rozmiar okna kontekstu)
└── {nazwa-domeny} # Twoja domena (bez rozszerzenia!)
.carl/ # Konfiguracja lokalna (tylko ten projekt)
└── {nazwa-domeny} # Reguły specyficzne dla shopPRO
```
**Ważne:** Nazwy plików domen — **małe litery, bez rozszerzenia** (`phpdev`, nie `phpdev.carl`).
---
## Dostępne star-commands
Wpisz `*nazwa` na początku wiadomości lub w środku promptu, żeby przełączyć tryb:
| Komenda | Tryb | Kiedy używać |
|---------|------|-------------|
| `*dev` | Development | Implementacja, szybkie zmiany bez tłumaczeń |
| `*review` | Code review | Przegląd kodu, bezpieczeństwo, edge cases |
| `*brief` | Zwięzłe odpowiedzi | Tylko bullet points, bez elaboracji |
| `*plan` | Planowanie | Eksploracja przed implementacją, opcje + tradeoffs |
| `*discuss` | Dyskusja | Burza mózgów, wiele podejść, bez skakania do kodu |
| `*debug` | Debugowanie | Systematyczna diagnoza, root cause analysis |
| `*explain` | Wyjaśnianie | Nauka, koncepty, stopniowe budowanie wiedzy |
| `*carl` | Pomoc CARL | Zarządzanie domenami, konfiguracja, pytania o CARL |
### Przykłady
```
*dev Napraw błąd w summaryView() gdzie duplikuje zamówienia
*review Przejrzyj OrderRepository::createFromBasket pod kątem bezpieczeństwa
*brief Co robi CacheHandler::deletePattern()
*plan Chcę dodać system rabatów do koszyka
*discuss Czy lepiej rozdzielić ApiloRepository na sync i admin?
```
---
## Tworzenie własnej domeny (projekt)
Kiedy masz zestaw reguł specyficznych dla shopPRO, utwórz lokalną domenę w `.carl/`.
### Krok 1 — Plik domeny
Utwórz `.carl/shoppro` (bez rozszerzenia):
```
# shopPRO Domain Rules
SHOPPRO_RULE_0=PHP < 8.0 — nie używaj match, named args, union types, str_contains
SHOPPRO_RULE_1=ORM: Medoo ($mdb), zawsze prepared statements, nigdy string concatenation
SHOPPRO_RULE_2=Namespace \Domain\ mapuje do autoload/Domain/ (D uppercase, a lowercase)
SHOPPRO_RULE_3=Testy: PHPUnit 9.6, pattern AAA, mock Medoo przez createMock(\medoo::class)
SHOPPRO_RULE_4=Cache: CacheHandler::deletePattern() do kasowania, TTL 86400, dane serialized
```
### Krok 2 — Wpis w manifeście
Dodaj do `.carl/manifest` (lub `~/.carl/manifest` jeśli globalna):
```
SHOPPRO_STATE=active
SHOPPRO_RECALL=shopPRO, medoo, zamówienie, koszyk, OrderRepository, Domain
SHOPPRO_EXCLUDE=
SHOPPRO_ALWAYS_ON=false
```
- `RECALL` — słowa kluczowe które triggerują domenę (przecinek = OR)
- `ALWAYS_ON=true` — ładuj przy każdym prompcie (tylko dla naprawdę universalnych reguł)
- `EXCLUDE` — słowa które blokują domenę mimo dopasowania RECALL
### Krok 3 — Weryfikacja
Wpisz `*carl` w czacie i zapytaj: _"Pokaż mi aktywne domeny"_.
---
## Zarządzanie przez Claude
Zamiast ręcznie edytować pliki, możesz zarządzać CARL przez Claude:
```
*carl Dodaj domenę dla testów PHPUnit w shopPRO
*carl Pokaż moją aktualną konfigurację
*carl Wyłącz domenę SHOPPRO tymczasowo
*carl Dodaj regułę do domeny dev: zawsze uruchamiaj ./test.ps1 po zmianach
```
Claude użyje skills `carl:manager` / `carl:tasks:*` do operacji na plikach.
---
## Integracja z PAUL
CARL i [PAUL](./PAUL_WORKFLOW.md) działają uzupełniająco:
- **PAUL** strukturyzuje *proces* (plan → apply → unify)
- **CARL** dostarcza *reguły domenowe* wtedy gdy są potrzebne
Praktycznie: podczas `/paul:apply` możesz prefixować `*dev` żeby Claude skupił się na kodzie bez elaboracji. Podczas `/paul:discuss``*discuss` żeby dostać pełną analizę opcji.
---
## Dobre praktyki
- **RECALL słowa** — używaj konkretnych, rzadkich słów żeby unikać false triggers. `medoo` lepsze niż `php`.
- **Mało reguł per domena** — 58 reguł to optymalnie. Więcej = wolniejsze matching, więcej tokenów.
- **ALWAYS_ON=false** domyślnie — ALWAYS_ON=true tylko dla reguł naprawdę universalnych (jak GLOBAL).
- **Star-commands przy dużych taskach** — na początku sesji wpisz `*dev` lub `*plan` żeby ustawić tryb.
- **Nie duplikuj CLAUDE.md** — CARL nie zastępuje CLAUDE.md. CLAUDE.md to architektura projektu. CARL to reguły zachowania Claude.
---
*Docs: 2026-03-12*

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,156 +0,0 @@
# Plan: System kolejki zadań cron oparty o bazę danych
## Kontekst
Obecny system cron ma dwa problemy:
1. **Kolejka plikowa (JSON)** — sync płatności/statusów Apilo trzymany w `/temp/apilo-sync-queue.json` — kruchy, brak transakcji, ryzyko utraty danych
2. **Monolityczny cron.php** (~550 linii) — brak priorytetów, brak retry z backoff, brak centralnego zarządzania
Cel: Zastąpienie całego systemu cron tabelą `pp_cron_jobs` z priorytetami, retry/backoff i harmonogramem `pp_cron_schedules`.
## Nowe pliki
| Plik | Opis |
|------|------|
| `autoload/Domain/CronJob/CronJobType.php` | Stałe typów zadań i priorytetów |
| `autoload/Domain/CronJob/CronJobRepository.php` | CRUD na `pp_cron_jobs` + `pp_cron_schedules` |
| `autoload/Domain/CronJob/CronJobProcessor.php` | Orkiestracja: pobierz zadanie → wywołaj handler → obsłuż wynik |
| `tests/Unit/Domain/CronJob/CronJobTypeTest.php` | Testy stałych |
| `tests/Unit/Domain/CronJob/CronJobRepositoryTest.php` | Testy repozytorium |
| `tests/Unit/Domain/CronJob/CronJobProcessorTest.php` | Testy procesora |
| `migrations/0.315.sql` | CREATE TABLE + INSERT harmonogramów |
## Modyfikowane pliki
| Plik | Zmiana |
|------|--------|
| `cron.php` | Zastąpienie ~550 linii orchestratorem (~100 linii) z rejestracją handlerów |
| `cron/cron-xml.php` | Usunięcie — logika przeniesiona do handlera `google_xml_feed` |
| `cron-turstmate.php` | Usunięcie — logika przeniesiona do handlera `trustmate_invitation` |
| `autoload/Domain/Order/OrderAdminService.php` | `queueApiloSync()` → enqueue do DB; usunięcie metod plikowych; `syncApiloPayment()`/`syncApiloStatus()` → public |
| `tests/Unit/Domain/Order/OrderAdminServiceTest.php` | Refaktor testów kolejki: mock `CronJobRepository` zamiast pliku JSON |
| `docs/DATABASE_STRUCTURE.md` | Dodanie tabel `pp_cron_jobs`, `pp_cron_schedules` |
| `docs/CHANGELOG.md` | Wpis o nowym systemie |
## Schemat DB (`migrations/0.315.sql`)
### `pp_cron_jobs`
```sql
CREATE TABLE pp_cron_jobs (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
job_type VARCHAR(50) NOT NULL,
status ENUM('pending','processing','completed','failed','cancelled') NOT NULL DEFAULT 'pending',
priority TINYINT UNSIGNED NOT NULL DEFAULT 100, -- niższy = ważniejszy
payload TEXT NULL, -- JSON z danymi zadania
result TEXT NULL, -- JSON z wynikiem
attempts SMALLINT UNSIGNED NOT NULL DEFAULT 0,
max_attempts SMALLINT UNSIGNED NOT NULL DEFAULT 10,
last_error VARCHAR(500) NULL,
scheduled_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
started_at DATETIME NULL,
completed_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_status_priority_scheduled (status, priority, scheduled_at),
INDEX idx_job_type (job_type),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
### `pp_cron_schedules`
```sql
CREATE TABLE pp_cron_schedules (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
job_type VARCHAR(50) NOT NULL UNIQUE,
interval_seconds INT UNSIGNED NOT NULL,
priority TINYINT UNSIGNED NOT NULL DEFAULT 100,
max_attempts SMALLINT UNSIGNED NOT NULL DEFAULT 3,
payload TEXT NULL,
enabled TINYINT(1) NOT NULL DEFAULT 1,
last_run_at DATETIME NULL,
next_run_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_enabled_next_run (enabled, next_run_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
## Typy zadań i priorytety
| Typ | Priorytet | Harmonogram |
|-----|-----------|-------------|
| `apilo_token_keepalive` | 10 (krytyczny) | co 4 min |
| `apilo_send_order` | 50 (wysoki) | co 1 min |
| `apilo_sync_payment` | 50 (wysoki) | event-driven (enqueue przy zmianie) |
| `apilo_sync_status` | 50 (wysoki) | event-driven |
| `apilo_product_sync` | 100 (normalny) | co 10 min |
| `apilo_pricelist_sync` | 100 (normalny) | co 1h |
| `apilo_status_poll` | 100 (normalny) | co 10 min |
| `price_history` | 100 (normalny) | co 24h |
| `order_analysis` | 100 (normalny) | co 10 min |
| `trustmate_invitation` | 200 (niski) | co 10 min |
| `google_xml_feed` | 200 (niski) | co 1h |
## Architektura klas
### CronJobRepository — metody kluczowe
- `enqueue($jobType, $payload, $priority, $maxAttempts, $scheduledAt)` — dodaj do kolejki
- `fetchNext($limit)` — atomowe pobranie pending jobs (UPDATE WHERE status='pending')
- `markCompleted($jobId, $result)` / `markFailed($jobId, $error, $backoffSeconds)`
- `hasPendingJob($jobType, $payloadMatch)` — zapobiega duplikatom
- `cleanup($olderThanDays)` — GC starych wpisów
- `recoverStuck($olderThanMinutes)` — reset stuck "processing" jobs
- `getDueSchedules()` / `touchSchedule($id)` — harmonogram
### CronJobProcessor — orkiestracja
- `registerHandler($jobType, callable)` — rejestracja handlera
- `createScheduledJobs()` — tworzy jobs z harmonogramów których `next_run_at <= NOW`
- `processQueue($limit)` — pobierz + wywołaj handler + markCompleted/markFailed
- `run($limit)` — główna metoda: schedules + process
### Exponential backoff
```
Próba 1: 60s, Próba 2: 120s, Próba 3: 240s, ... max 3600s (1h)
```
### Zależność "order not yet in Apilo"
Handler `apilo_sync_payment`/`apilo_sync_status` sprawdza `apilo_order_id`. Jeśli brak → zwraca false → `markFailed()` z backoffem → zadanie wraca do kolejki. Max 50 prób.
## Nowy cron.php (schemat)
```php
$cronRepo = new \Domain\CronJob\CronJobRepository($mdb);
$processor = new \Domain\CronJob\CronJobProcessor($mdb, $cronRepo);
// Rejestracja handlerów (każdy to callable)
$processor->registerHandler('apilo_token_keepalive', function($payload) use ($integrationsRepo) { ... });
$processor->registerHandler('apilo_send_order', function($payload) use ($orderService, ...) { ... });
// ... inne handlery
$result = $processor->run(20);
```
## Zmiany w OrderAdminService
1. `queueApiloSync()``CronJobRepository::enqueue()` zamiast zapisu do pliku JSON
2. Usunięcie: `loadApiloSyncQueue()`, `saveApiloSyncQueue()`, `apiloSyncQueuePath()`, stała `APILO_SYNC_QUEUE_FILE`
3. `syncApiloPayment()`, `syncApiloStatus()` → zmiana z `private` na `public`
4. Jednorazowa migracja: odczyt JSON → insert do DB → usunięcie pliku
## Kolejność implementacji
1. Migracja SQL
2. `CronJobType.php`
3. `CronJobRepository.php` + testy
4. `CronJobProcessor.php` + testy
5. Modyfikacja `OrderAdminService` (queue → DB, public methods)
6. Jednorazowa migracja pliku JSON → DB
7. Nowy `cron.php` z handlerami (ekstrakcja logiki z bloków proceduralnych)
8. Aktualizacja testów OrderAdminService
9. Dokumentacja (DATABASE_STRUCTURE.md, CHANGELOG.md)
## Weryfikacja
1. Uruchomienie pełnego zestawu testów: `./test.ps1`
2. Sprawdzenie czy nowe testy CronJob* przechodzą
3. Sprawdzenie czy istniejące testy OrderAdminService przechodzą po refaktorze
4. Weryfikacja migracji SQL na pustej bazie

View File

@@ -1,730 +0,0 @@
# Struktura bazy danych shopPRO
Plik aktualizowany na bieżąco przy zmianach w kodzie.
ORM: Medoo (`$mdb`), prefix tabel: `pp_`
## pp_shop_products
Główna tabela produktów.
| Kolumna | Opis |
|---------|------|
| id | PK |
| parent_id | FK do produktu nadrzędnego (kombinacje) - NULL dla produktów głównych |
| price_brutto | Cena brutto |
| price_brutto_promo | Cena promocyjna brutto |
| quantity | Stan magazynowy |
| status | Status: 1 = aktywny, 0 = nieaktywny |
| archive | Archiwum: 1 = zarchiwizowany, 0 = aktywny |
| promoted | Czy promowany |
| vat | Stawka VAT |
| ean | Kod EAN |
| sku | Kod SKU |
| apilo_product_id | ID produktu w Apilo |
| apilo_product_name | Nazwa produktu w Apilo |
**Używane w:** `Domain\Product\ProductRepository`, `admin\factory\ShopProduct`, `admin\Controllers\ShopProductController`
## pp_shop_products_langs
Tłumaczenia produktów (per język).
| Kolumna | Opis |
|---------|------|
| id | PK |
| product_id | FK do pp_shop_products |
| lang_id | ID języka (np. 'pl') |
| name | Nazwa produktu |
**Używane w:** `Domain\Product\ProductRepository::getName()`
## pp_shop_products_images
Zdjęcia produktów.
| Kolumna | Opis |
|---------|------|
| id | PK |
| product_id | FK do pp_shop_products |
| src | Ścieżka do pliku |
| alt | Tekst alternatywny |
## pp_shop_products_custom_fields
Dodatkowe pola produktów (custom fields).
| Kolumna | Opis |
|---------|------|
| id_additional_field | PK |
| id_product | FK do pp_shop_products |
| name | Nazwa pola |
| type | Typ pola (VARCHAR 30) |
| is_required | Czy wymagane (0/1) |
## pp_shop_products_categories
Przypisanie produktów do kategorii.
| Kolumna | Opis |
|---------|------|
| product_id | FK do pp_shop_products |
**Używane w:** `admin\factory\ShopProduct::product_delete()`, `Domain\Product\ProductRepository::getProductsByCategory()`
**Aktualizacja 2026-02-15 (ver. 0.274):** akcje `/admin/shop_product/mass_edit/*` korzystają z `Domain\Product\ProductRepository` przez `admin\Controllers\ShopProductController`.
## pp_shop_categories
Kategorie sklepu.
| Kolumna | Opis |
|---------|------|
| id | PK |
| parent_id | FK do kategorii nadrzednej (NULL dla root) |
| status | 1 = aktywna, 0 = nieaktywna |
| o | Kolejnosc wyswietlania |
| sort_type | Typ sortowania produktow w kategorii |
| view_subcategories | Czy wyswietlac podkategorie |
**Uzywane w:** `Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`.
## pp_shop_categories_langs
Tlumaczenia kategorii (per jezyk).
| Kolumna | Opis |
|---------|------|
| category_id | FK do pp_shop_categories |
| lang_id | ID jezyka (np. pl, en) |
| title | Nazwa kategorii |
| text | Opis kategorii |
| text_hidden | Rozwiniecie opisu kategorii |
| seo_link | Link SEO kategorii |
| meta_title | Meta title |
| meta_description | Meta description |
| meta_keywords | Meta keywords |
| noindex | Flaga noindex |
| category_title | Naglowek H1 kategorii |
| additional_text | Dodatkowy tekst nad lista produktow |
**Uzywane w:** `Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`.
**Aktualizacja 2026-02-15 (ver. 0.275):** modul `/admin/shop_category/*` korzysta z `Domain\Category\CategoryRepository` przez `admin\Controllers\ShopCategoryController`; usunieto legacy `admin\controls/factory/view\ShopCategory`.
## pp_shop_orders
Zamówienia sklepu (źródło danych dla list i szczegółów klientów w panelu admin).
| Kolumna | Opis |
|---------|------|
| id | PK |
| client_id | FK do `pp_shop_clients` (NULL dla gościa) |
| client_name | Imię klienta z zamówienia |
| client_surname | Nazwisko klienta z zamówienia |
| client_email | E-mail klienta z zamówienia |
| client_phone | Telefon klienta |
| client_city | Miasto klienta |
| summary | Wartość zamówienia |
| date_order | Data złożenia zamówienia |
| payment_method | Nazwa metody płatności |
| transport | Nazwa transportu |
| message | Wiadomość klienta |
| updated_at | Data ostatniej modyfikacji (polling API) |
**Używane w:** `Domain\Client\ClientRepository::listForAdmin()`, `Domain\Client\ClientRepository::ordersForClient()`, `Domain\Client\ClientRepository::totalsForClient()`, `Domain\Order\OrderRepository::listForApi()`, `Domain\Order\OrderRepository::findForApi()`.
**Aktualizacja 2026-02-15 (ver. 0.274):** moduł `/admin/shop_clients/*` korzysta z `Domain\Client\ClientRepository` przez `admin\Controllers\ShopClientsController`.
**Aktualizacja 2026-02-15 (ver. 0.276):** moduł `/admin/shop_order/*` korzysta z `Domain\Order\OrderRepository` przez `admin\Controllers\ShopOrderController`; usunięto legacy `admin\controls\ShopOrder` i `admin\factory\ShopOrder`.
**Aktualizacja 2026-02-17 (ver. 0.290):** frontend `/shop_order/*` korzysta z `Domain\Order\OrderRepository` przez `front\Controllers\ShopOrderController`; usunięto legacy `front\controls\ShopOrder`, `front\factory\ShopOrder`, `front\view\ShopOrder`. Callery (ShopBasketController, ClientRepository, shop\Order, cron-turstmate) przepięte na OrderRepository.
## pp_banners
Banery.
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa banera |
| status | 0/1 |
| date_start | Data rozpoczęcia |
| date_end | Data zakończenia |
| home_page | Czy na stronie głównej 0/1 |
**Używane w:** `Domain\Banner\BannerRepository`, `front\Views\Banners`
**Aktualizacja 2026-02-16 (ver. 0.281):** metody frontendowe `banners()`, `mainBanner()` dodane do `Domain\Banner\BannerRepository`. Fasady `front\factory\Banners` i `front\view\Banners` deleguja do repo/Views.
## pp_banners_langs
Tłumaczenia banerów.
| Kolumna | Opis |
|---------|------|
| id | PK |
| id_banner | FK do pp_banners |
| id_lang | ID języka |
| src | Ścieżka do grafiki |
| url | URL docelowy |
| html | Kod HTML |
| text | Tekst |
**Używane w:** `Domain\Banner\BannerRepository`, `front\Views\Banners`
## pp_articles
Artykuły.
| Kolumna | Opis |
|---------|------|
| id | PK |
| status | -1 = archiwum, 0 = nieaktywny, 1 = aktywny |
**Używane w:** `admin\Controllers\ArticlesArchiveController`, `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::listArchivedForAdmin()`
## pp_articles_pages
Strony artykułów.
| Kolumna | Opis |
|---------|------|
| article_id | FK do pp_articles |
| page_id | FK do strony (pp_pages) |
| o | Kolejność |
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedImages()`
## pp_articles_langs
Tłumaczenia artykułów.
| Kolumna | Opis |
|---------|------|
| article_id | FK do pp_articles |
| lang_id | ID języka (np. 'pl') |
| title | Tytuł artykułu |
| seo_link | Link SEO artykułu |
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedFiles()`
## pp_articles_images
Zdjęcia artykułów.
| Kolumna | Opis |
|---------|------|
| article_id | FK do pp_articles |
| src | Ścieżka do pliku |
| o | Kolejność |
| id | PK (używane też do sortowania DESC) |
**Używane w:** `Domain\Article\ArticleRepository::find()`
## pp_articles_files
Pliki artykułów.
| Kolumna | Opis |
|---------|------|
| id | PK |
| article_id | FK do pp_articles |
| src | Ścieżka do pliku |
| name | Nazwa wyświetlana załącznika (opcjonalna) |
| to_delete | Flaga miękkiego usuwania (0/1) |
| o | Kolejność załączników (używana przez sortowanie drag&drop w adminie) |
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::saveFilesOrder()`
## pp_units
Jednostki/slowniki (np. jednostki produktu).
| Kolumna | Opis |
|---------|------|
| id | PK |
**Używane w:** `Domain\Dictionaries\DictionariesRepository`, `admin\controls\ShopProduct`
## pp_units_langs
Tlumaczenia jednostek (per jezyk).
| Kolumna | Opis |
|---------|------|
| id | PK |
| unit_id | FK do pp_units |
| lang_id | ID jezyka (np. 'pl') |
| text | Nazwa jednostki |
**Używane w:** `Domain\Dictionaries\DictionariesRepository`
## pp_users
Uzytkownicy panelu administratora.
| Kolumna | Opis |
|---------|------|
| id | PK |
| login | Login / e-mail uzytkownika |
| password | Hash hasla (legacy: md5) |
| status | Status konta: 1 = aktywny, 0 = zablokowany |
| admin | Flaga dostepu do panelu admin |
| error_logged_count | Licznik nieudanych logowan |
| last_logged | Data ostatniego poprawnego logowania |
| last_error_logged | Data ostatniej nieudanej proby logowania |
| twofa_enabled | Czy wlaczone 2FA (0/1) |
| twofa_email | E-mail do wysylki kodu 2FA |
| twofa_code_hash | Hash aktualnego kodu 2FA |
| twofa_expires_at | Data waznosci kodu 2FA |
| twofa_sent_at | Data ostatniej wysylki kodu 2FA |
| twofa_failed_attempts | Liczba nieudanych prob 2FA |
**Uzywane w:** `Domain\User\UserRepository`, `admin\Controllers\UsersController`, `admin\factory\Users`
**Aktualizacja 2026-02-12:** uzycia `pp_users` sa prowadzone przez `Domain\\User\\UserRepository` (legacy `admin\\factory\\Users` usunieto).
## pp_langs
Jezyki panelu i frontendu.
| Kolumna | Opis |
|---------|------|
| id | PK (2-literowe ID jezyka, np. pl, en) |
| name | Nazwa jezyka |
| status | 1 = aktywny, 0 = nieaktywny |
| start | 1 = domyslny jezyk |
| o | Kolejnosc |
**Uzywane w:** `Domain\\Languages\\LanguagesRepository`, `admin\\Controllers\\LanguagesController`, `front\\factory\\Languages`
## pp_langs_translations
Slownik tlumaczen panelu/frontendu.
| Kolumna | Opis |
|---------|------|
| id | PK |
| text | Klucz/tekst bazowy |
| <lang_id> | Kolumny dynamiczne per jezyk (np. pl, en) |
**Uzywane w:** `Domain\\Languages\\LanguagesRepository`, `admin\\Controllers\\LanguagesController`, `front\\factory\\Languages`
**Aktualizacja 2026-02-12:** modul jezykow i tlumaczen (`pp_langs`, `pp_langs_translations`) obslugiwany przez `Domain\\Languages\\LanguagesRepository`.
## pp_layouts
Szablony layoutow (HTML/CSS/JS + flagi domyslne).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa szablonu |
| html | Kod HTML |
| css | Kod CSS |
| js | Kod JS |
| m_html | Kod HTML mobilny |
| m_css | Kod CSS mobilny |
| m_js | Kod JS mobilny |
| status | Domyslny layout stron (0/1) |
| categories_default | Domyslny layout kategorii (0/1) |
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `admin\\Controllers\\LayoutsController`, `front\\factory\\Layouts`
## pp_layouts_pages
Przypisanie layoutow do stron CMS.
| Kolumna | Opis |
|---------|------|
| layout_id | FK do pp_layouts |
| page_id | FK do pp_pages |
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `front\\factory\\Layouts`
## pp_layouts_categories
Przypisanie layoutow do kategorii sklepu.
| Kolumna | Opis |
|---------|------|
| layout_id | FK do pp_layouts |
| category_id | FK do pp_shop_categories |
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `front\\factory\\Layouts`
**Aktualizacja 2026-02-12 (ver. 0.256):** modul `/admin/layouts` korzysta z `Domain\\Layouts\\LayoutsRepository` (DI kontroler + fasada legacy).
## pp_newsletter
Adresy e-mail zapisane do newslettera.
| Kolumna | Opis |
|---------|------|
| id | PK |
| email | Adres e-mail subskrybenta |
| hash | Hash potwierdzenia/wypisu |
| status | 1 = potwierdzony, 0 = oczekujacy |
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\Controllers\\NewsletterController`
## pp_newsletter_send
Kolejka wysylki newslettera.
| Kolumna | Opis |
|---------|------|
| id | PK |
| email | Adres docelowy |
| dates | Zakres dat artykulow (tekst) |
| id_template | FK do `pp_newsletter_templates` (NULL gdy brak szablonu) |
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`
## pp_newsletter_templates
Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa/klucz szablonu |
| text | Tresc HTML szablonu |
| is_admin | 1 = szablon administracyjny/systemowy, 0 = szablon uzytkownika |
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `admin\\Controllers\\NewsletterController`
**Aktualizacja 2026-02-12 (ver. 0.257):** modul `/admin/newsletter` korzysta z `Domain\\Newsletter\\NewsletterRepository` (DI kontroler + fasada legacy).
**Aktualizacja 2026-02-16 (ver. 0.279):** `front\\factory\\Newsletter` usunięta — logika przeniesiona do `NewsletterRepository`. Frontend korzysta z `front\\Controllers\\NewsletterController` (DI).
## pp_scontainers
Kontenery statyczne (modul /admin/scontainers).
| Kolumna | Opis |
|---------|------|
| id | PK |
| status | 1 = aktywny, 0 = nieaktywny |
| show_title | 1 = pokaz tytul, 0 = ukryj tytul |
**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `admin\Controllers\ScontainersController`, `front\factory\Scontainers`
## pp_scontainers_langs
Tlumaczenia kontenerow statycznych (per jezyk).
| Kolumna | Opis |
|---------|------|
| id | PK |
| container_id | FK do pp_scontainers |
| lang_id | ID jezyka (np. pl, en) |
| title | Tytul kontenera |
| text | Tresc HTML kontenera |
**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `front\factory\Scontainers`
**Aktualizacja 2026-02-12 (ver. 0.259):** modul `/admin/scontainers` korzysta z `Domain\Scontainers\ScontainersRepository` (DI kontroler + fasada legacy).
**Aktualizacja 2026-02-12 (ver. 0.260):** modul `/admin/articles_archive` korzysta z `Domain\Article\ArticleRepository` (`listArchivedForAdmin`, `restore`, `deletePermanently`) przez `admin\Controllers\ArticlesArchiveController`.
## pp_shop_attributes
Cechy produktu (modul `/admin/shop_attribute`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| status | Status: 1 = aktywny, 0 = nieaktywny |
| type | Typ cechy: 0 = tekst, 1 = kolor, 2 = wzor |
| o | Kolejnosc wyswietlania |
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`, `admin\controls\ShopProduct`, `admin\factory\ShopProduct`
## pp_shop_attributes_langs
Tlumaczenia cech produktu (per jezyk).
| Kolumna | Opis |
|---------|------|
| id | PK |
| attribute_id | FK do pp_shop_attributes |
| lang_id | ID jezyka (np. pl, en) |
| name | Nazwa cechy |
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `shop\ProductAttribute`
## pp_shop_attributes_values
Wartosci cech produktu.
| Kolumna | Opis |
|---------|------|
| id | PK |
| attribute_id | FK do pp_shop_attributes |
| is_default | Czy wartosc domyslna dla cechy (0/1) |
| impact_on_the_price | Wplyw na cene wariantu (NULL = brak) |
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`, `admin\factory\ShopProduct`
## pp_shop_attributes_values_langs
Tlumaczenia wartosci cech (per jezyk).
| Kolumna | Opis |
|---------|------|
| id | PK |
| value_id | FK do pp_shop_attributes_values |
| lang_id | ID jezyka (np. pl, en) |
| name | Nazwa wyswietlana |
| value | Wewnetrzna wartosc techniczna (opcjonalna) |
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `shop\ProductAttribute`
## pp_shop_products_attributes
Powiazanie kombinacji produktow z wartosciami cech.
| Kolumna | Opis |
|---------|------|
| product_id | FK do pp_shop_products (kombinacja) |
| value_id | FK do pp_shop_attributes_values |
**Uzywane w:** `Domain\Attribute\AttributeRepository::refreshCombinationPricesForValue()`, `admin\controls\ShopProduct`, `admin\factory\ShopProduct`
**Aktualizacja 2026-02-14 (ver. 0.271):** modul `/admin/shop_attribute` korzysta z `Domain\Attribute\AttributeRepository` przez `admin\Controllers\ShopAttributeController`. Usunieto legacy klasy `admin\controls\ShopAttribute`, `admin\factory\ShopAttribute`, `admin\view\ShopAttribute`.
## pp_shop_coupon
Kody rabatowe sklepu (modul `/admin/shop_coupon`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Kod kuponu (UNIQUE) |
| status | Status: 1 = aktywny, 0 = nieaktywny |
| send | Czy kupon zostal wyslany (0/1) |
| used | Czy kupon zostal wykorzystany (0/1) |
| date_used | Data wykorzystania kuponu (NULL gdy brak) |
| used_count | Licznik uzyc kuponu |
| type | Typ kuponu (obecnie: 1 = rabat procentowy na koszyk) |
| amount | Wartosc kuponu (np. procent) |
| one_time | Czy kupon jednorazowy (0/1) |
| include_discounted_product | Czy obejmuje rowniez produkty przecenione (0/1) |
| categories | JSON z ID kategorii objetych kuponem (NULL = bez ograniczenia) |
**Uzywane w:** `Domain\Coupon\CouponRepository`, `admin\Controllers\ShopCouponController`, `front\Controllers\ShopCouponController`, `shop\Coupon`, `Domain\Order\OrderRepository`
**Aktualizacja 2026-02-13 (ver. 0.266):** modul `/admin/shop_coupon` korzysta z `Domain\Coupon\CouponRepository` przez `admin\Controllers\ShopCouponController`. Usunieto legacy klasy `admin\controls\ShopCoupon` i `admin\factory\ShopCoupon`.
## pp_shop_promotion
Promocje sklepu (modul `/admin/shop_promotion`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa promocji |
| status | Status: 1 = aktywna, 0 = nieaktywna |
| condition_type | Typ warunku promocji (slownik w `shop\Promotion::$condition_type`) |
| discount_type | Typ rabatu (slownik w `shop\Promotion::$discount_type`) |
| amount | Wartosc rabatu (np. procent) |
| date_from | Data startu promocji (NULL = aktywna od razu) |
| date_to | Data konca promocji (NULL = bez daty konca) |
| categories | JSON z ID kategorii grupy I |
| condition_categories | JSON z ID kategorii grupy II |
| include_coupon | Czy laczyc z kuponami rabatowymi (0/1) |
| include_product_promo | Czy uwzgledniac produkty przecenione (0/1) |
| min_product_count | Minimalna liczba produktow (dla wybranych warunkow) |
| price_cheapest_product | Cena najtanszego produktu (dla wybranych warunkow) |
**Uzywane w:** `Domain\Promotion\PromotionRepository`, `admin\Controllers\ShopPromotionController`, `shop\Promotion`, `front\factory\ShopPromotion`
**Aktualizacja 2026-02-13:** modul `/admin/shop_promotion` korzysta z `Domain\Promotion\PromotionRepository` przez `admin\Controllers\ShopPromotionController`. Usunieto legacy klasy `admin\controls\ShopPromotion` i `admin\factory\ShopPromotion`.
**Aktualizacja 2026-02-13 (ver. 0.265):** dodano obsluge `date_from` (repozytorium, formularz admin, lista admin, filtr aktywnych promocji na froncie) oraz poprawke zapisu edycji promocji po `id`.
## pp_shop_payment_methods
Metody platnosci sklepu (modul `/admin/shop_payment_method`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa metody platnosci |
| description | Opis metody platnosci (wyswietlany m.in. w checkout) |
| status | Status: 1 = aktywna, 0 = nieaktywna |
| apilo_payment_type_id | ID typu platnosci Apilo (NULL gdy brak mapowania) |
| min_order_amount | Minimalna kwota zamowienia (DECIMAL(10,2), NULL = brak limitu) |
| max_order_amount | Maksymalna kwota zamowienia (DECIMAL(10,2), NULL = brak limitu) |
| is_cod | Platnosc przy odbiorze: 1 = tak, 0 = nie (TINYINT DEFAULT 0) |
| sellasist_payment_type_id | DEPRECATED (integracja Sellasist usunieta w ver. 0.263) |
**Uzywane w:** `Domain\PaymentMethod\PaymentMethodRepository`, `admin\Controllers\ShopPaymentMethodController`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod`, `admin\controls\ShopTransport`, `cron.php`
**Aktualizacja 2026-03-12 (ver. 0.338):** dodano kolumne `is_cod` — flaga platnosci przy odbiorze, zastepuje hardkodowane `payment_id == 3` w `OrderRepository::createFromBasket()`.
**Aktualizacja 2026-02-14 (ver. 0.268):** modul `/admin/shop_payment_method` korzysta z `Domain\PaymentMethod\PaymentMethodRepository` przez `admin\Controllers\ShopPaymentMethodController`. Usunieto legacy klasy `admin\controls\ShopPaymentMethod`, `admin\factory\ShopPaymentMethod`, `admin\view\ShopPaymentMethod` oraz widok `admin/templates/shop-payment-method/view-list.php`.
**Aktualizacja 2026-02-22 (ver. 0.304):** dodano kolumny `min_order_amount` i `max_order_amount` — konfigurowalne limity kwotowe metod platnosci. Zastapiono hardcoded warunek PayPo (id=6, 40-1000 PLN) generycznym filtrowaniem na froncie.
## pp_shop_transports
Rodzaje transportu sklepu (modul `/admin/shop_transport`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa (systemowa, readonly) |
| name_visible | Nazwa widoczna dla klienta |
| description | Opis metody transportu |
| status | Status: 1 = aktywny, 0 = nieaktywny |
| cost | Koszt dostawy (PLN) |
| max_wp | Maksymalna waga paczki (NULL = bez limitu) |
| default | Domyslna forma dostawy (0/1) |
| delivery_free | Czy obsluguje darmowa dostawe (0/1) |
| apilo_carrier_account_id | ID konta przewoznika w Apilo (NULL gdy brak mapowania) |
| o | Kolejnosc wyswietlania |
**Uzywane w:** `Domain\Transport\TransportRepository`, `admin\Controllers\ShopTransportController`, `front\factory\ShopTransport`
## pp_shop_transport_payment_methods
Powiazanie metod transportu z metodami platnosci (tabela lacznikowa).
| Kolumna | Opis |
|---------|------|
| id_transport | FK do pp_shop_transports |
| id_payment_method | FK do pp_shop_payment_methods |
**Uzywane w:** `Domain\Transport\TransportRepository`, `Domain\PaymentMethod\PaymentMethodRepository::forTransport()`
**Aktualizacja 2026-02-14 (ver. 0.269):** modul `/admin/shop_transport` korzysta z `Domain\Transport\TransportRepository` przez `admin\Controllers\ShopTransportController`. Usunieto legacy klasy `admin\controls\ShopTransport`, `admin\view\ShopTransport` oraz widok `admin/templates/shop-transport/view-list.php`.
## pp_shop_apilo_settings
Ustawienia integracji Apilo (key-value).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Klucz ustawienia (np. client-id, access-token) |
| value | Wartosc ustawienia |
**Uzywane w:** `Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`, `admin\factory\Integrations`
## pp_shop_shoppro_settings
Ustawienia integracji ShopPRO (key-value).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Klucz ustawienia (np. domain, db_name) |
| value | Wartosc ustawienia |
**Uzywane w:** `Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`, `admin\factory\Integrations`
**Aktualizacja 2026-02-13:** modul `/admin/integrations/` korzysta z `Domain\Integrations\IntegrationsRepository` (DI kontroler + fasada legacy). Usunieto integracje Sellasist i Baselinker.
## pp_shop_statuses
Statusy zamowien sklepu (modul `/admin/shop_statuses`). Statusy sa predefiniowane - brak dodawania/usuwania, mozliwa edycja koloru i mapowania Apilo.
| Kolumna | Opis |
|---------|------|
| id | PK (zaczyna sie od 0!) |
| status | Nazwa statusu (read-only) |
| color | Kolor statusu (hex, np. #ff0000) |
| o | Kolejnosc wyswietlania |
| apilo_status_id | ID statusu w Apilo (NULL gdy brak mapowania) |
| baselinker_status_id | DEPRECATED (usuniety w ver. 0.263) |
| sellasist_status_id | DEPRECATED (usuniety w ver. 0.263) |
**Uzywane w:** `Domain\ShopStatus\ShopStatusRepository`, `admin\Controllers\ShopStatusesController`, `front\factory\ShopStatuses`, `shop\Order`, `cron.php`
**Aktualizacja 2026-02-14 (ver. 0.267):** modul `/admin/shop_statuses` korzysta z `Domain\ShopStatus\ShopStatusRepository` przez `admin\Controllers\ShopStatusesController`. Usunieto legacy klasy `admin\controls\ShopStatuses` i `admin\factory\ShopStatuses`. `front\factory\ShopStatuses` dziala jako fasada do repozytorium.
## pp_shop_product_sets
Komplety produktow (modul `/admin/shop_product_sets`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa kompletu |
| status | Status: 1 = aktywny, 0 = nieaktywny |
**Uzywane w:** `Domain\ProductSet\ProductSetRepository`, `admin\Controllers\ShopProductSetsController`, `shop\ProductSet`, `shop\Product`
## pp_shop_product_sets_products
Powiazanie kompletow z produktami (tabela lacznikowa).
| Kolumna | Opis |
|---------|------|
| id | PK |
| set_id | FK do pp_shop_product_sets |
| product_id | FK do pp_shop_products |
**Uzywane w:** `Domain\ProductSet\ProductSetRepository`, `shop\Product`, `front\factory\ShopProduct`, `admin\factory\ShopProduct`
**Aktualizacja 2026-02-15 (ver. 0.272):** modul `/admin/shop_product_sets` korzysta z `Domain\ProductSet\ProductSetRepository` przez `admin\Controllers\ShopProductSetsController`. Usunieto legacy klasy `admin\controls\ShopProductSets` i `admin\factory\ShopProductSet`. `shop\ProductSet` dziala jako fasada do repozytorium.
## pp_shop_producer
Producenci produktow (modul `/admin/shop_producer`).
| Kolumna | Opis |
|---------|------|
| id | PK |
| name | Nazwa producenta |
| status | Status: 1 = aktywny, 0 = nieaktywny |
| img | Sciezka do logo producenta (NULL gdy brak) |
**Uzywane w:** `Domain\Producer\ProducerRepository`, `admin\Controllers\ShopProducerController`, `front\Controllers\ShopProducerController`, `shop\Product`
## pp_shop_producer_lang
Tlumaczenia producentow (per jezyk). FK kaskadowe ON DELETE CASCADE.
| Kolumna | Opis |
|---------|------|
| id | PK |
| producer_id | FK do pp_shop_producer |
| lang_id | ID jezyka (np. pl, en) |
| description | Opis producenta (TEXT) |
| data | Dane producenta (TEXT, HTML) |
| meta_title | Meta title SEO (VARCHAR 255) |
**Uzywane w:** `Domain\Producer\ProducerRepository`, `shop\Product`
**Aktualizacja 2026-02-15 (ver. 0.273):** modul `/admin/shop_producer` korzysta z `Domain\Producer\ProducerRepository` przez `admin\Controllers\ShopProducerController`. Usunieto legacy `admin\controls\ShopProducer` i `admin\factory\ShopProducer`. `shop\Producer` dziala jako fasada do repozytorium.
**Aktualizacja 2026-02-17 (ver. 0.291):** frontend `/shop_producer/*` korzysta z `Domain\Producer\ProducerRepository` przez `front\Controllers\ShopProducerController`; usunięto legacy `front\controls\ShopProducer` i `shop\Producer`.
## pp_cron_jobs
Kolejka zadań cron z priorytetami i retry/backoff.
| Kolumna | Opis |
|---------|------|
| id | PK auto increment |
| job_type | Typ zadania (VARCHAR 50) — np. apilo_send_order, price_history |
| status | ENUM: pending, processing, completed, failed, cancelled |
| priority | TINYINT — niższy = ważniejszy (10=krytyczny, 50=wysoki, 100=normalny, 200=niski) |
| payload | JSON z danymi zadania (TEXT NULL) |
| result | JSON z wynikiem (TEXT NULL) |
| attempts | Liczba prób (SMALLINT) |
| max_attempts | Maksymalna liczba prób (SMALLINT, domyślnie 10) |
| last_error | Ostatni błąd (VARCHAR 500) |
| scheduled_at | Kiedy zadanie ma być uruchomione (DATETIME) |
| started_at | Kiedy rozpoczęto przetwarzanie (DATETIME NULL) |
| completed_at | Kiedy zakończono (DATETIME NULL) |
| created_at | Data utworzenia (DATETIME) |
| updated_at | Data ostatniej modyfikacji (DATETIME, ON UPDATE) |
**Indeksy:** idx_status_priority_scheduled (status, priority, scheduled_at), idx_job_type, idx_status
**Używane w:** `Domain\CronJob\CronJobRepository`, `Domain\CronJob\CronJobProcessor`
## pp_cron_schedules
Harmonogram cyklicznych zadań cron.
| Kolumna | Opis |
|---------|------|
| id | PK auto increment |
| job_type | Typ zadania (VARCHAR 50, UNIQUE) |
| interval_seconds | Interwał uruchomienia w sekundach |
| priority | Priorytet tworzonych zadań (TINYINT) |
| max_attempts | Maks. prób dla tworzonych zadań (SMALLINT) |
| payload | Opcjonalny payload JSON (TEXT NULL) |
| enabled | Czy harmonogram aktywny (TINYINT 1) |
| last_run_at | Ostatnie uruchomienie (DATETIME NULL) |
| next_run_at | Następne planowane uruchomienie (DATETIME NULL) |
| created_at | Data utworzenia (DATETIME) |
**Indeksy:** idx_enabled_next_run (enabled, next_run_at)
**Używane w:** `Domain\CronJob\CronJobRepository`, `Domain\CronJob\CronJobProcessor`
**Dodano w wersji 0.324.**
## pp_routes
Tabela tras URL — mapowanie wzorców URL (regex) na parametry GET. Zastępuje reguły `RewriteRule` w `.htaccess` dla wszystkich URL-i aplikacji: produktów, kategorii, stron, artykułów oraz systemowych (koszyk, logowanie, newsletter, itp.).
| Kolumna | Opis |
|---------|------|
| id | Klucz główny (AUTO_INCREMENT) |
| product_id | ID produktu (INT NULL) — wypełnione dla tras produktów |
| category_id | ID kategorii (INT NULL) — wypełnione dla tras kategorii |
| page_id | ID strony (INT NULL) — wypełnione dla tras stron |
| article_id | ID artykułu (INT NULL) — wypełnione dla tras artykułów |
| type | Typ trasy: NULL = encja (produkt/kategoria/strona/artykuł), `'system'` = trasa systemowa (koszyk, logowanie, newsletter, AJAX moduły, itp.) |
| lang_id | ID języka (0 dla tras systemowych niezwiązanych z językiem) |
| pattern | Wyrażenie regularne dopasowywane do REQUEST_URI |
| destination | Docelowy query string, np. `index.php?category=5&lang=1` |
**Mechanizm:** `index.php` ładuje wszystkie trasy (z cache Redis `pp_routes:all`) przed `checkUrlParams()`, dopasowuje `pattern` do ścieżki żądania i ustawia `$_GET` z `destination`. Obsługuje grupy przechwytujące (np. `$1` dla paginacji).
**Trasy systemowe:** Przy każdym wywołaniu `Helpers::htacces()` wszystkie rekordy z `type='system'` są usuwane i wstawiane na nowo (32 statycznych + dynamiczne trasy językowe i producentów). Zarządzane automatycznie — nie edytować ręcznie.
**Cache:** Redis klucz `pp_routes:all`, TTL 86400s. Invalidowany automatycznie przy każdym wywołaniu `Helpers::htacces()`.
**Używane w:** `index.php`, `Shared\Helpers\Helpers::htacces()`, `Domain\Product\ProductRepository`, `Domain\Category\CategoryRepository`, `Domain\Pages\PagesRepository`, `Domain\Article\ArticleRepository`
**Dodano w wersji 0.329. Kolumna `type` i trasy systemowe dodane w wersji 0.330.**

View File

@@ -1,178 +0,0 @@
# Form Edit System - Dokumentacja użycia
## Architektura
```
┌─────────────────────────────────────────────────────────────┐
│ Controller │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ edit() │ │ save() │ │
│ │ - buduje VM │ │ - walidacja │ │
│ │ - renderuje │ │ - zapis │ │
│ └────────┬────────┘ └─────────────────┘ │
└───────────┼─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ FormEditViewModel │
│ - title, formId, data, fields, tabs, actions │
│ - validationErrors, persist, languages │
└───────────┬─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ components/form-edit.php (szablon) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ FormFieldRenderer - renderuje każde pole │ │
│ │ ├─ input, select, textarea, switch │ │
│ │ ├─ date, datetime, editor, image │ │
│ │ └─ lang_section (zagnieżdżone pola) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Pliki systemu
| Plik | Opis |
|------|------|
| `autoload/admin/ViewModels/Forms/FormFieldType.php` | Stale typow pol |
| `autoload/admin/ViewModels/Forms/FormField.php` | Factory methods per typ |
| `autoload/admin/ViewModels/Forms/FormTab.php` | Zakladki |
| `autoload/admin/ViewModels/Forms/FormAction.php` | Akcje (zapisz, anuluj) |
| `autoload/admin/ViewModels/Forms/FormEditViewModel.php` | ViewModel formularza |
| `autoload/admin/Support/Forms/FormValidator.php` | Walidacja pol |
| `autoload/admin/Support/Forms/FormRequestHandler.php` | Obsluga POST + persist |
| `autoload/admin/Support/Forms/FormFieldRenderer.php` | Renderowanie HTML |
| `admin/templates/components/form-edit.php` | Uniwersalny szablon |
## Przykład użycia w kontrolerze
```php
use admin\ViewModels\Forms\FormEditViewModel;
use admin\ViewModels\Forms\FormField;
use admin\ViewModels\Forms\FormTab;
use admin\ViewModels\Forms\FormAction;
use admin\Support\Forms\FormRequestHandler;
class BannerController
{
public function edit(): string
{
$banner = $this->repository->find($id);
$languages = \admin\factory\Languages::languages_list();
$viewModel = new FormEditViewModel(
formId: 'banner-edit',
title: 'Edycja banera',
data: $banner,
tabs: [
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
new FormTab('content', 'Zawartość', 'fa-file'),
],
fields: [
// Zakładka Ustawienia
FormField::text('name', [
'label' => 'Nazwa',
'tab' => 'settings',
'required' => true,
]),
FormField::switch('status', [
'label' => 'Aktywny',
'tab' => 'settings',
]),
FormField::date('date_start', [
'label' => 'Data rozpoczęcia',
'tab' => 'settings',
]),
// Sekcja językowa w zakładce Zawartość
FormField::langSection('translations', 'content', [
FormField::image('src', ['label' => 'Obraz']),
FormField::text('url', ['label' => 'Url']),
FormField::editor('text', ['label' => 'Treść']),
]),
],
actions: [
FormAction::save('/admin/banners/save', '/admin/banners'),
FormAction::cancel('/admin/banners'),
],
languages: $languages,
persist: true,
);
return \Tpl::view('components/form-edit', ['form' => $viewModel]);
}
public function save(): void
{
$formHandler = new FormRequestHandler();
$viewModel = $this->buildFormViewModel(); // jak w edit()
$result = $formHandler->handleSubmit($viewModel, $_POST);
if (!$result['success']) {
// Błędy walidacji - zapisane automatycznie do sesji
echo json_encode(['success' => false, 'errors' => $result['errors']]);
exit;
}
// Sukces - persist wyczyszczony automatycznie
$this->repository->save($result['data']);
echo json_encode(['success' => true]);
exit;
}
}
```
## Dostępne typy pól
| Typ | Metoda | Opcje |
|-----|--------|-------|
| `text` | `FormField::text(name, ['label' => '...', 'required' => true])` | placeholder, help |
| `number` | `FormField::number(name, [...])` | - |
| `email` | `FormField::email(name, [...])` | walidacja formatu |
| `password` | `FormField::password(name, [...])` | - |
| `date` | `FormField::date(name, [...])` | datetimepicker |
| `datetime` | `FormField::datetime(name, [...])` | datetimepicker z czasem |
| `switch` | `FormField::switch(name, [...])` | checked (bool) |
| `select` | `FormField::select(name, ['options' => [...]])` | options: [key => label] |
| `textarea` | `FormField::textarea(name, ['rows' => 4])` | rows |
| `editor` | `FormField::editor(name, ['toolbar' => 'MyTool'])` | CKEditor |
| `image` | `FormField::image(name, ['filemanager' => true])` | filemanager URL |
| `file` | `FormField::file(name, [...])` | filemanager |
| `hidden` | `FormField::hidden(name, value)` | - |
| `color` | `FormField::color(name, ['label' => '...'])` | HTML5 color picker + text input |
| `lang_section` | `FormField::langSection(name, 'tab', [fields])` | pola per język |
## Walidacja
Walidacja jest automatyczna na podstawie właściwości pól:
- `required` - pole wymagane
- `type` = `email` - walidacja formatu e-mail
- `type` = `number` - walidacja liczby
- `type` = `date` - walidacja formatu YYYY-MM-DD
Dla sekcji językowych walidacja jest powtarzana dla każdego aktywnego języka.
## Persist (zapamiętywanie danych)
Gdy `persist = true`:
1. Przy błędzie walidacji dane są zapisywane w `$_SESSION['form_persist'][$formId]`
2. Formularz automatycznie przywraca dane z sesji przy ponownym wyświetleniu
3. Po udanym zapisie sesja jest czyszczona automatycznie przez `FormRequestHandler`
## Przerabianie istniejących formularzy
1. **Kontroler** - zamień `view\Xxx::edit()` na `FormEditViewModel`
2. **Repository** - dostosuj `save()` do formatu z `FormRequestHandler` (lub dodaj wsparcie dla obu formatów)
3. **Szablon** - usuń stary szablon lub zostaw jako fallback
4. **Testy** - zaktualizuj testy jeśli zmienił się format danych
## Aktualizacja 2026-02-15 (ver. 0.275)
- Modul `ShopCategory` zostal zmigrowany do warstwy Domain + DI, ale formularz kategorii nadal korzysta z legacy `gridEdit`.
- W ramach migracji wydzielono skrypty UI do osobnych partiali `*-custom-script.php` (lista, browse, edycja, produkty), co upraszcza dalsze przepiecie formularza na `components/form-edit`.
- Po migracji `ShopCategory` kolejnym kandydatem do pelnej migracji formularza na Form Edit System pozostaje modul `Order` (zgodnie z `REFACTORING_PLAN.md`).
---
*Dokument aktualizowany: 2026-02-15*

View File

@@ -1,42 +0,0 @@
# Pamięć projektu shopPRO
Notatki i wnioski zebrane podczas pracy z kodem. Aktualizowane na bieżąco.
---
## Serwer produkcyjny
- PHP < 8.0 — unikać `match`, named arguments, union types, `str_contains()` itp.
- Zamiast `match` używać operatorów trójargumentowych (ternary) lub `if/else`
## Znane problemy / TODO
- `\Shared\Helpers\Helpers::send_email()` i `Shared\Email\Email::send()` — zduplikowana logika PHPMailer. Docelowo zunifikować w `Shared\Email\Email`
## Wzorce potwierdzone w projekcie
- Metody frontendowe (z cache Redis) dodawane do istniejących repozytoriów Domain — NIE tworzymy osobnych FrontendService/AdminService
- Klasy View (`front\Views\*`) są statyczne i bezstanowe — nie wymagają DI
- Kontrolery (`Controllers\*`) są instancyjne z DI przez konstruktor
- Autoloader obsługuje dwa formaty plików: `class.X.php` (legacy) i `X.php` (nowy) — oba działają bez zmian w autoloaderze
- Nowe katalogi z dużej litery: `Views/`, `Controllers/` (legacy: `view/`, `controls/`, `factory/`)
## Medoo ORM — pułapki
- `$mdb->delete()` przyjmuje 2 argumenty (tabela, warunek), NIE 3 — wielokrotnie powodowało bugi (np. `newsletter_unsubscribe`)
- `$mdb->get()` zwraca `null` gdy brak rekordu, NIE `false`
- Przy `$mdb->insert()` sprawdzać `$mdb->id()` aby potwierdzić sukces
## Redis cache — konwencje
- TTL domyślnie 86400 (24h)
- Klucze produktów: `shop\product:{id}:{lang}:{permutation_hash}`
- Wzorzec czyszczenia: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
- Dane w cache są serializowane — wymagają `unserialize()` po `get()`
## Aktualizacje klienckie
- Pliki `*.md` NIGDY nie trafiają do ZIP aktualizacji
- `updates/changelog.php` to plik serwisowy repozytorium, nie runtime klienta
- Główny `.htaccess` wdrażany osobno, poza ZIP aktualizacji
- W archiwum ZIP NIE powinno być folderu z nazwą wersji — struktura zaczyna się od katalogów projektu

View File

@@ -1,150 +0,0 @@
# PAUL — Workflow dla shopPRO
[PAUL](https://github.com/ChristopherKahler/paul) to zestaw komend dla Claude Code, który strukturyzuje pracę nad projektem: planowanie → implementacja → weryfikacja. Działa przez slash-komendy w czacie z Claude.
---
## Jednorazowa inicjalizacja
```
/paul:init
```
Uruchom raz — tworzy plik `.paul/` z konfiguracją projektu (milestones, fazy). Jeśli już zainicjalizowany, pomijasz ten krok.
---
## Nowa funkcja (feature)
### 1. Omów wizję
```
/paul:discuss
```
Claude zadaje pytania, żeby doprecyzować, co dokładnie chcesz zbudować — wynik to jasna definicja przed planowaniem.
### 2. Zbadaj opcje techniczne (opcjonalnie)
```
/paul:discover
```
Przydatne gdy nie jesteś pewny jak zaimplementować coś technicznie — Claude przegląda kod i proponuje podejścia.
### 3. Zaplanuj implementację
```
/paul:plan
```
Tworzy szczegółowy plan z krokami, plikami do zmiany, kolejnością. Zatwierdź plan zanim ruszysz dalej.
### 4. Wykonaj plan
```
/paul:apply
```
Claude implementuje plan krok po kroku, z commitami na każdą fazę.
### 5. Zweryfikuj (UAT)
```
/paul:verify
```
Claude generuje checklistę testów manualnych — punkt po punkcie sprawdzasz czy funkcja działa.
---
## Poprawka błędu (bug fix)
```
/paul:plan-fix
```
Dedykowany flow dla bugów: opisujesz problem → Claude analizuje kod → tworzy plan naprawy → `/paul:apply` wykonuje.
Krótszy wariant dla prostych bugów — możesz też po prostu opisać błąd i użyć `/paul:plan``/paul:apply`.
---
## Zarządzanie sesjami
| Komenda | Kiedy |
|---------|-------|
| `/paul:progress` | Sprawdź gdzie jesteś, co dalej |
| `/paul:pause` | Kończysz sesję — tworzy plik handoff |
| `/paul:resume` | Zaczynasz nową sesję — wczytuje kontekst |
| `/paul:handoff` | Generuje dokument przekazania pracy |
---
## Milestones (większe wdrożenia)
Gdy planujesz większą wersję (np. nowy moduł, refactor):
```
/paul:milestone # Tworzysz nowy milestone
/paul:add-phase # Dodajesz fazę do milestone
/paul:complete-milestone # Zamykasz po wdrożeniu
```
---
## Czy PAUL może przejrzeć projekt pod kątem błędów?
Tak, częściowo. Najlepsze podejście:
### Mapowanie kodu
```
/paul:map-codebase
```
Generuje analizę architektury — wyłapuje niespójności, martwy kod, problematyczne zależności.
### Research konkretnego obszaru
```
/paul:research
```
Np. _"Sprawdź czy walidacja zamówień w OrderRepository jest kompletna"_ — Claude przegląda kod i raportuje problemy.
### Ograniczenia
PAUL nie jest narzędziem do statycznej analizy kodu (jak PHPStan czy psalm). Do systematycznego przeglądu błędów lepiej połączyć:
- **PAUL** (`/paul:map-codebase` + `/paul:research`) — problemy architektoniczne, logika biznesowa
- **Testy** (`./test.ps1`) — regresje, złe zachowanie metod
- **PHPStan** (jeśli dodany do projektu) — typy, niezdefiniowane zmienne
---
## Typowy dzień pracy w shopPRO
```
# Rano — wznów kontekst
/paul:resume
# W trakcie — sprawdź co dalej
/paul:progress
# Nowa funkcja lub bug
/paul:discuss → /paul:plan → /paul:apply → /paul:verify
# Na koniec dnia
/paul:pause
```
---
## Dobre praktyki
- Zawsze zatwierdzaj plan (`/paul:plan`) zanim uruchomisz `/paul:apply` — łatwiej zmienić plan niż cofnąć kod
- Po każdym `/paul:apply` odpal testy: `./test.ps1`
- Używaj `/paul:verify` przed mergem — generuje checklistę zamiast zgadywać co przetestować
- Przy bugach najpierw opisz symptom dokładnie, Claude lepiej planuje naprawę mając konkretny przypadek

View File

@@ -1,190 +0,0 @@
# Struktura Projektu shopPRO
Aktualna architektura po zakonczonej migracji na Domain-Driven Design + Dependency Injection.
## Warstwa domenowa (`autoload/Domain/`)
Kazdy modul zawiera Repository (i opcjonalnie dodatkowe klasy). Konstruktor DI z `$db` (Medoo). Metody sluza zarowno adminowi, jak i frontendowi (wspolna warstwa).
| Modul | Klasy | Uwagi |
|-------|-------|-------|
| Article | ArticleRepository | blog, aktualnosci, galerie, pliki |
| Attribute | AttributeRepository | cechy produktow + wartosci |
| Banner | BannerRepository | banery glowne + boczne, Redis cache |
| Basket | BasketCalculator | summary, count, walidacja stanow |
| Cache | CacheRepository | czyszczenie cache z poziomu admin |
| Category | CategoryRepository | drzewa kategorii, produkty w kategorii, Redis cache |
| Client | ClientRepository | CRUD, auth, adresy, zamowienia |
| Coupon | CouponRepository | kupony rabatowe, walidacja, uzycie |
| CronJob | CronJobType, CronJobRepository, CronJobProcessor | kolejka zadan cron (DB), priorytety, retry/backoff, harmonogram |
| Dashboard | DashboardRepository | statystyki admin, Redis cache |
| Dictionaries | DictionariesRepository | slowniki admin |
| Integrations | IntegrationsRepository | Apilo sync, ustawienia |
| Languages | LanguagesRepository | jezyki, tlumaczenia |
| Layouts | LayoutsRepository | layouty stron, 3-level fallback |
| Newsletter | NewsletterRepository, NewsletterPreviewRenderer | subskrypcje, szablony, kolejka wysylki |
| Order | OrderRepository, OrderAdminService | CRUD, Apilo sync, webhooki platnosci, kolejka retry |
| Pages | PagesRepository | strony, menu, drzewa stron |
| PaymentMethod | PaymentMethodRepository | metody platnosci, mapowanie Apilo |
| Producer | ProducerRepository | producenci |
| Product | ProductRepository | CRUD, cache, kombinacje, zdjecia, Google Feed XML |
| ProductSet | ProductSetRepository | zestawy produktow |
| Promotion | PromotionRepository | promocje, 5 typow applyType*, silnik dopasowania |
| Scontainers | ScontainersRepository | kontenery sidebaru |
| Settings | SettingsRepository | ustawienia sklepu |
| ShopStatus | ShopStatusRepository | statusy zamowien, mapowanie Apilo |
| Transport | TransportRepository | transport, koszty, powiazanie z platnosci |
| Update | UpdateRepository | aktualizacje, migracje SQL |
| User | UserRepository | uzytkownicy admin, 2FA, logowanie |
## Warstwa admin (`autoload/admin/`)
### Router: `admin\App`
- `getControllerFactories()` — mapa kontrolerow z DI wiring
- Brak fallbacku na legacy — wszystkie moduly na nowych kontrolerach
### Kontrolery (`admin\Controllers\`) — 28 kontrolerow
ArticlesArchive, Articles, Banner, Dashboard, Dictionaries, Filemanager, Integrations, Languages, Layouts, Newsletter, Pages, ProductArchive, Scontainers, Settings, ShopAttribute, ShopCategory, ShopClients, ShopCoupon, ShopOrder, ShopPaymentMethod, ShopProducer, ShopProduct, ShopProductSets, ShopPromotion, ShopStatuses, ShopTransport, Update, Users
### Support
- `admin\Support\TableListRequestFactory` — paginacja/sortowanie tabel
- `admin\Support\Forms\FormRequestHandler` — obsluga formularzy (persist przy bledach)
- `admin\Support\Forms\FormFieldRenderer` — renderowanie pol formularzy
### ViewModels
- `admin\ViewModels\Forms\` — FormEditViewModel, FormField, FormTab, FormAction, FormFieldType
- `admin\ViewModels\Common\PaginatedTableViewModel`
### Walidacja
- `admin\Validation\FormValidator` — reguly per pole, sekcje jezykowe
## Warstwa frontend (`autoload/front/`)
### Router: `front\App`
- `route()`, `checkUrlParams()`, `getControllerFactories()`
### Layout Engine: `front\LayoutEngine`
- `show()` — zamiana tagow szablonowych (kategorie, produkty, menu, banery, artykuly, kontenery, meta)
- `contact()`, `cookieInformation()`
### Kontrolery (`front\Controllers\`) — 8 kontrolerow
Newsletter, Search, ShopBasket, ShopClient, ShopCoupon, ShopOrder, ShopProducer, ShopProduct
### Widoki (`front\Views\`) — 11 klas statycznych
Articles, Banners, Languages, Menu, Newsletter, Scontainers, ShopCategory, ShopClient, ShopPaymentMethod, ShopProduct, ShopSearch
## Warstwa API (`autoload/api/`)
REST API dla ordersPRO. Entry point: `api.php`. Stateless (bez sesji), autentykacja przez `X-Api-Key` header.
### Router: `api\ApiRouter`
- `handle()` — autentykacja → routing → dispatch
- Helpery statyczne: `sendSuccess()`, `sendError()`, `getJsonBody()`, `requireMethod()`
### Kontrolery (`api\Controllers\`)
- `OrdersApiController` — lista, szczegoly, zmiana statusu, platnosc (5 akcji)
- `ProductsApiController` — lista, szczegoly, tworzenie, aktualizacja produktow (4 akcje)
- `DictionariesApiController` — statusy, transporty, metody platnosci (3 akcje)
- `CategoriesApiController` — lista aktywnych kategorii (1 akcja)
Dokumentacja: `docs/API.md`
## Warstwa wspoldzielona (`autoload/Shared/`)
| Klasa | Opis |
|-------|------|
| `Shared\Cache\CacheHandler` | Redis cache: get/set/delete/deletePattern |
| `Shared\Cache\RedisConnection` | Singleton polaczenia Redis |
| `Shared\Email\Email` | Wrapper PHPMailer |
| `Shared\Helpers\Helpers` | SEO, email, cache clearing, shortPrice, utility |
| `Shared\Html\Html` | Helpery HTML |
| `Shared\Image\ImageManipulator` | Obrobka obrazow GD |
| `Shared\Tpl\Tpl` | Silnik szablonow: render(), set() |
## Cache Redis
### Klucze
```
shop\product:{id}:{lang}:{permutation_hash} — dane produktu (TTL 24h)
ProductRepository::getProductPermutationQuantityOptions:v2:{id}:{perm} — ilosc + komunikaty
ProductRepository::productSetsWhenAddToBasket:{id} — zestawy "kupowane razem"
```
### Konwencje
- TTL domyslnie 86400 (24h)
- Dane serializowane — `unserialize()` po `get()`
- Czyszczenie: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
- Czyszczenie z poziomu admin: `Shared\Helpers\Helpers::clear_product_cache($id)`
- Przycisk "Wyczysc cache" w admin: `SettingsController::clearCacheAjax()``flushAll()` Redis + `temp/` + `thumbs/`
## Entry pointy
| Plik | Rola |
|------|------|
| `index.php` | Frontend — autoload, sesja, DB, routing (`front\App`), layout (`front\LayoutEngine`), DOM post-processing |
| `ajax.php` | Frontend AJAX — koszyk, transport, kontakt |
| `api.php` | REST API (ordersPRO + Ekomi CSV) — router: `\api\ApiRouter`, kontrolery: `\api\Controllers\` |
| `admin/index.php` | Admin — autoload, sesja, DB, routing (`admin\App`) |
| `admin/ajax.php` | Admin AJAX |
| `cron.php` | CRON: Apilo sync (ceny/stany co 10min, cennik co 1h, retry queue) |
| `cron-turstmate.php` | TrustMate integracja |
| `cron/cron-xml.php` | Google Feed XML |
| `download.php` | Pobieranie plikow |
### Autoloader
Kazdy entry point rejestruje `__autoload_my_classes()`:
1. Probuje `autoload/{namespace}/class.{ClassName}.php` (legacy format)
2. Probuje `autoload/{namespace}/{ClassName}.php` (PSR-4 format)
### Routing frontend (index.php)
Przed `front\App::route()`:
1. Sprawdza tabele `pp_redirects` → 301 redirect
2. Sprawdza tabele `pp_routes` → regex pattern → destination
### Newsletter queue
`index.php` wywoluje `$newsletterRepo->sendQueued()` na koncu kazdego requestu frontendowego (limit 1 mail/request).
## Integracje zewnetrzne
### Apilo (cron.php)
- Synchronizacja cen/stanow produktow (co 10 min)
- Synchronizacja cennika (co 1h)
- Kolejka retry: `temp/apilo-sync-queue.json``OrderAdminService::processApiloSyncQueue()`
- Mapowanie statusow i platnosci przez tabele `pp_shop_statuses` i `pp_shop_payment_methods`
### Webhooki platnosci (front\Controllers\ShopOrderController)
- tPay, Przelewy24, Hotpay — ujednolicone: `set_as_paid` + `update_status`
## Biblioteki (`libraries/`)
- `medoo/medoo.php` — Medoo ORM (`$mdb`)
- `rb.php` — RedBeanPHP ORM (`\R::`, `$pdo`)
- `phpmailer/` — PHPMailer
## Wzorce architektoniczne
### DI zamiast global
```php
// Kontroler wiring (w admin\App lub front\App)
$repo = new \Domain\Example\ExampleRepository($mdb);
$controller = new \admin\Controllers\ExampleController($repo);
```
### Wspolna warstwa Domain
Metody frontendowe (z Redis cache) dodawane do istniejacych repozytoriow — NIE tworzymy osobnych FrontendService/AdminService.
### Klasy View — statyczne, bezstanowe
`front\Views\*` — nie wymagaja DI. Czyste funkcje: dane wchodza, HTML wychodzi.
### Kontrolery — instancyjne z DI
`Controllers\*` — repozytoria wstrzykiwane przez konstruktor.
### Nazewnictwo plikow
- Nowe: `ClassName.php`
- Legacy (pozostalosci): `class.ClassName.php`
- Autoloader obsluguje oba formaty
### Nazewnictwo katalogow
- Nowe: z duzej litery (`Views/`, `Controllers/`)
- Namespace `\admin\` z malej (bo katalog `admin/` jest z malej na serwerze Linux)
- NIE uzywac `\Admin\` (duze A)

View File

@@ -1,131 +0,0 @@
# Testowanie shopPRO
## Szybki start
```bash
# Pelny suite (PowerShell — rekomendowane)
./test.ps1
# Konkretny plik
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php
# Konkretny test
./test.ps1 --filter testGetQuantityReturnsCorrectValue
# Alternatywne
composer test # standard
./test.bat # testdox (czytelna lista)
./test-simple.bat # kropki
./test-debug.bat # debug
./test.sh # Git Bash
```
## Aktualny stan
```text
OK (823 tests, 2284 assertions)
```
Zweryfikowano: 2026-04-18 (ver. 0.347)
## Konfiguracja
- **PHPUnit 9.6** via `phpunit.phar`
- **Bootstrap:** `tests/bootstrap.php`
- **Config:** `phpunit.xml`
## Struktura testow
```
tests/
|-- bootstrap.php
|-- stubs/
| |-- CacheHandler.php (inline w bootstrap)
| |-- Helpers.php (Shared\Helpers\Helpers stub)
| `-- ShopProduct.php (shop\Product stub)
|-- Unit/
| |-- Domain/
| | |-- Article/ArticleRepositoryTest.php
| | |-- Attribute/AttributeRepositoryTest.php
| | |-- Banner/BannerRepositoryTest.php
| | |-- Basket/BasketCalculatorTest.php
| | |-- Cache/CacheRepositoryTest.php
| | |-- Category/CategoryRepositoryTest.php
| | |-- Coupon/CouponRepositoryTest.php
| | |-- CronJob/CronJobTypeTest.php
| | |-- CronJob/CronJobRepositoryTest.php
| | |-- CronJob/CronJobProcessorTest.php
| | |-- Dictionaries/DictionariesRepositoryTest.php
| | |-- Integrations/IntegrationsRepositoryTest.php
| | |-- Languages/LanguagesRepositoryTest.php
| | |-- Layouts/LayoutsRepositoryTest.php
| | |-- Newsletter/NewsletterRepositoryTest.php
| | |-- Pages/PagesRepositoryTest.php
| | |-- PaymentMethod/PaymentMethodRepositoryTest.php
| | |-- Producer/ProducerRepositoryTest.php
| | |-- Product/ProductRepositoryTest.php
| | |-- ProductSet/ProductSetRepositoryTest.php
| | |-- Promotion/PromotionRepositoryTest.php
| | |-- Settings/SettingsRepositoryTest.php
| | |-- ShopStatus/ShopStatusRepositoryTest.php
| | |-- Transport/TransportRepositoryTest.php
| | |-- Update/UpdateRepositoryTest.php
| | `-- User/UserRepositoryTest.php
| |-- Shared/
| | `-- Security/
| | `-- CsrfTokenTest.php
| `-- admin/
| `-- Controllers/
| |-- ArticlesControllerTest.php
| |-- DictionariesControllerTest.php
| |-- IntegrationsControllerTest.php
| |-- ProductArchiveControllerTest.php
| |-- SettingsControllerTest.php
| |-- ShopAttributeControllerTest.php
| |-- ShopCategoryControllerTest.php
| |-- ShopCouponControllerTest.php
| |-- ShopPaymentMethodControllerTest.php
| |-- ShopProducerControllerTest.php
| |-- ShopProductControllerTest.php
| |-- ShopProductSetsControllerTest.php
| |-- ShopPromotionControllerTest.php
| |-- ShopStatusesControllerTest.php
| |-- ShopTransportControllerTest.php
| `-- UsersControllerTest.php
| |-- front/Controllers/
| | `-- ShopBasketControllerTest.php
| `-- api/
| |-- ApiRouterTest.php
| `-- Controllers/
| |-- OrdersApiControllerTest.php
| |-- ProductsApiControllerTest.php
| `-- DictionariesApiControllerTest.php
`-- Integration/ (puste — zarezerwowane)
```
## Dodawanie nowych testow
1. Plik w `tests/Unit/Domain/<Module>/<Class>Test.php`, `tests/Unit/admin/Controllers/<Class>Test.php` lub `tests/Unit/api/Controllers/<Class>Test.php`.
2. Rozszerz `PHPUnit\Framework\TestCase`.
3. Nazwy metod zaczynaj od `test`.
4. Wzorzec AAA: Arrange, Act, Assert.
## Mockowanie Medoo
```php
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')->willReturn(42);
$repo = new ProductRepository($mockDb);
$value = $repo->getQuantity(123);
$this->assertEquals(42, $value);
```
## Bootstrap — stuby
`tests/bootstrap.php` rejestruje autoloader i definiuje stuby:
- `Redis`, `RedisConnection` — klasy Redis (aby nie wymagac rozszerzenia)
- `Shared\Cache\CacheHandler` — inline stub z `get()`/`set()`/`exists()`/`delete()`/`deletePattern()`
- `Shared\Helpers\Helpers` — z `tests/stubs/Helpers.php`
- `shop\Product` — z `tests/stubs/ShopProduct.php`

File diff suppressed because it is too large Load Diff

View File

@@ -1,73 +0,0 @@
# Instrukcja tworzenia aktualizacji shopPRO
## Nowy sposób (od v0.301) — automatyczny build script
### Wymagania
- Git z tagami wersji (np. `v0.299`, `v0.300`)
- PowerShell
### Workflow
```
1. Pracuj normalnie: commit, push, commit, push...
2. Gdy wersja gotowa:
→ git tag v0.XXX
→ ./build-update.ps1 -ToTag v0.XXX -ChangelogEntry "NEW - opis"
3. Upload plików z updates/0.XX/ na serwer aktualizacji
```
### Użycie build-update.ps1
```powershell
# Podgląd zmian (bez tworzenia plików)
./build-update.ps1 -ToTag v0.301 -DryRun
# Budowanie paczki (auto-detect poprzedniego tagu)
./build-update.ps1 -ToTag v0.301 -ChangelogEntry "NEW - opis zmiany"
# Z jawnym tagiem źródłowym
./build-update.ps1 -FromTag v0.300 -ToTag v0.301 -ChangelogEntry "NEW - opis"
```
### Co robi skrypt automatycznie
1. `git diff --name-status` między tagami → listy dodanych/zmodyfikowanych/usuniętych plików
2. Filtrowanie przez `.updateignore` (pliki deweloperskie, konfiguracyjne itp.)
3. Kopiowanie plików do temp, tworzenie ZIP
4. SHA256 checksum ZIP-a
5. Generowanie `ver_X.XXX_manifest.json`
6. Generowanie legacy `_sql.txt` i `_files.txt` (okres przejściowy)
7. Aktualizacja `versions.php` i `changelog.php`
8. Cleanup
### Pliki wynikowe
- `updates/0.XX/ver_X.XXX.zip` — paczka z plikami
- `updates/0.XX/ver_X.XXX_manifest.json` — manifest z checksumem, listą zmian, SQL
- `updates/0.XX/ver_X.XXX_sql.txt` — legacy SQL (okres przejściowy)
- `updates/0.XX/ver_X.XXX_files.txt` — legacy lista plików do usunięcia (okres przejściowy)
### Migracje SQL
Pliki SQL umieszczaj w `migrations/{version}.sql` (np. `migrations/0.301.sql`).
Build script automatycznie je wczyta i umieści w manifeście + legacy `_sql.txt`.
### Format manifestu
```json
{
"version": "0.301",
"date": "2026-02-22",
"checksum_zip": "sha256:abc123...",
"files": {
"modified": ["autoload/Domain/Order/OrderRepository.php"],
"added": ["autoload/Domain/Order/NewHelper.php"],
"deleted": ["autoload/shop/OldClass.php"]
},
"directories_deleted": [],
"sql": ["ALTER TABLE pp_x ADD COLUMN y INT DEFAULT 0"],
"changelog": "NEW - opis zmiany"
}
```
### .updateignore
Plik w katalogu głównym projektu, wzorce plików wykluczonych z paczek (jak `.gitignore`).
### INFO
pamiętaj że push czasem zwraca błąd autoryzacji, wtedy spróbuj ponownie