Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ef6dc9092 | ||
|
|
b03816e8ec | ||
|
|
591f2787ca |
@@ -83,7 +83,7 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online
|
|||||||
|
|
||||||
| Metric | Target | Current | Status |
|
| Metric | Target | Current | Status |
|
||||||
|--------|--------|---------|--------|
|
|--------|--------|---------|--------|
|
||||||
| Testy | >800 | 810 | On track |
|
| Testy | >800 | 821 | On track |
|
||||||
| Pokrycie architektury DDD | 100% | 100% | Achieved |
|
| Pokrycie architektury DDD | 100% | 100% | Achieved |
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ Status: Planning
|
|||||||
| 11 | DataLayer GA4 analytics fix | 1 | Done | 2026-03-25 |
|
| 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 |
|
| 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 |
|
||||||
|
|
||||||
## Phase Details
|
## Phase Details
|
||||||
|
|
||||||
@@ -96,5 +97,11 @@ Status: Planning
|
|||||||
|
|
||||||
**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.
|
**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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
---
|
---
|
||||||
*Last updated: 2026-03-25*
|
*Last updated: 2026-04-16*
|
||||||
|
|||||||
@@ -5,25 +5,25 @@
|
|||||||
See: .paul/PROJECT.md (updated 2026-03-12)
|
See: .paul/PROJECT.md (updated 2026-03-12)
|
||||||
|
|
||||||
**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.
|
**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 13 complete — basket logging + TTL token
|
**Current focus:** Phase 14 complete — custom fields delete bug fix
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Milestone: Hotfix
|
Milestone: Hotfix
|
||||||
Phase: 13 — basket logging + TTL token — Planning
|
Phase: 14 — custom fields delete bug — Complete
|
||||||
Plan: 13-01 created, awaiting approval
|
Plan: 14-01 complete
|
||||||
Status: UNIFY complete, phase 13 finished
|
Status: UNIFY complete, phase 14 finished
|
||||||
Last activity: 2026-03-25 — 13-01 UNIFY complete
|
Last activity: 2026-04-16 — 14-01 UNIFY complete
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- Phase 13: [██████████] 100% (COMPLETE)
|
- Phase 14: [██████████] 100% (COMPLETE)
|
||||||
|
|
||||||
## Loop Position
|
## Loop Position
|
||||||
|
|
||||||
Current loop state (phase 13, plan 01):
|
Current loop state (phase 14, plan 01):
|
||||||
```
|
```
|
||||||
PLAN ──▶ APPLY ──▶ UNIFY
|
PLAN ──▶ APPLY ──▶ UNIFY
|
||||||
✓ ✓ ✓ [Phase 13 complete]
|
✓ ✓ ✓ [Phase 14 complete]
|
||||||
```
|
```
|
||||||
|
|
||||||
Previous phases:
|
Previous phases:
|
||||||
@@ -38,6 +38,7 @@ Phase 10: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-
|
|||||||
Phase 11: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25]
|
Phase 11: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25]
|
||||||
Phase 12: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25]
|
Phase 12: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25]
|
||||||
Phase 13: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25]
|
Phase 13: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25]
|
||||||
|
Phase 14: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-04-16]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
@@ -59,6 +60,7 @@ Phase 13: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-
|
|||||||
- 2026-03-25: Token zamówienia z jednorazowego na TTL 30 min — backward compat z plain string
|
- 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: 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-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
|
### Deferred Issues
|
||||||
None.
|
None.
|
||||||
@@ -68,10 +70,10 @@ None.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-03-25
|
Last session: 2026-04-16
|
||||||
Stopped at: Phase 13 UNIFY complete
|
Stopped at: Phase 14 UNIFY complete
|
||||||
Next action: /koniec-pracy or next feature
|
Next action: /koniec-pracy or next feature
|
||||||
Resume file: .paul/phases/13-basket-logging-ttl-token/13-01-SUMMARY.md
|
Resume file: .paul/phases/14-custom-fields-delete-bug/14-01-SUMMARY.md
|
||||||
|
|
||||||
---
|
---
|
||||||
*STATE.md — Updated after every significant action*
|
*STATE.md — Updated after every significant action*
|
||||||
|
|||||||
14
.paul/changelog/2026-04-16.md
Normal file
14
.paul/changelog/2026-04-16.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# 2026-04-16
|
||||||
|
|
||||||
|
## Co zrobiono
|
||||||
|
|
||||||
|
- [Phase 14, Plan 01] Fix: usunięcie wszystkich dodatkowych pól produktu nie działało
|
||||||
|
- Dodano hidden marker `custom_field_name_present` w formularzu edycji produktu
|
||||||
|
- Zmieniono warunek w ProductRepository na sprawdzanie markera zamiast obecności tablicy pól
|
||||||
|
- Dodano test jednostkowy testSaveCustomFieldsDeletesAllWhenEmpty
|
||||||
|
|
||||||
|
## Zmienione pliki
|
||||||
|
|
||||||
|
- `autoload/admin/Controllers/ShopProductController.php`
|
||||||
|
- `autoload/Domain/Product/ProductRepository.php`
|
||||||
|
- `tests/Unit/Domain/Product/ProductRepositoryTest.php`
|
||||||
150
.paul/phases/14-custom-fields-delete-bug/14-01-PLAN.md
Normal file
150
.paul/phases/14-custom-fields-delete-bug/14-01-PLAN.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
phase: 14-custom-fields-delete-bug
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- admin/templates/shop-product/product-edit-custom-script.php
|
||||||
|
- autoload/Domain/Product/ProductRepository.php
|
||||||
|
autonomous: true
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Naprawić bug: usunięcie WSZYSTKICH dodatkowych pól produktu w panelu admina nie działa — pola pozostają po zapisie.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Właściciel sklepu musi mieć możliwość usunięcia wszystkich custom fields z produktu. Obecny bug blokuje tę operację.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
- Poprawiony JS w szablonie — hidden field gwarantujący obecność klucza `custom_field_name` w POST
|
||||||
|
- Defensive check w repozytorium (opcjonalnie)
|
||||||
|
- Test jednostkowy potwierdzający poprawkę
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
## Project Context
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
|
||||||
|
## Source Files
|
||||||
|
@admin/templates/shop-product/product-edit-custom-script.php
|
||||||
|
@autoload/Domain/Product/ProductRepository.php
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Usunięcie wszystkich custom fields zapisuje pusty stan
|
||||||
|
```gherkin
|
||||||
|
Given produkt ma 2 dodatkowe pola (np. "Grawerunek", "Kolor")
|
||||||
|
When admin usuwa oba pola i klika "Zatwierdź"
|
||||||
|
Then po zapisie produkt nie ma żadnych dodatkowych pól
|
||||||
|
And tabela pp_shop_products_custom_fields nie zawiera rekordów dla tego produktu
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: Częściowe usunięcie nadal działa
|
||||||
|
```gherkin
|
||||||
|
Given produkt ma 3 dodatkowe pola
|
||||||
|
When admin usuwa 1 pole i klika "Zatwierdź"
|
||||||
|
Then po zapisie produkt ma 2 dodatkowe pola
|
||||||
|
And usunięte pole nie istnieje w bazie
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Dodawanie pól nadal działa
|
||||||
|
```gherkin
|
||||||
|
Given produkt nie ma dodatkowych pól
|
||||||
|
When admin dodaje 2 nowe pola i klika "Zatwierdź"
|
||||||
|
Then po zapisie produkt ma 2 dodatkowe pola
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Dodać hidden field gwarantujący klucz custom_field_name w POST</name>
|
||||||
|
<files>admin/templates/shop-product/product-edit-custom-script.php</files>
|
||||||
|
<action>
|
||||||
|
W szablonie product-edit-custom-script.php dodać ukryte pole w sekcji custom fields:
|
||||||
|
```html
|
||||||
|
<input type="hidden" name="custom_field_name_present" value="1">
|
||||||
|
```
|
||||||
|
To pole musi być ZAWSZE obecne w formularzu (nie wewnątrz dynamicznych wierszy pól),
|
||||||
|
tak aby serwer wiedział, że sekcja custom fields była obecna w formularzu.
|
||||||
|
|
||||||
|
ALTERNATYWNIE (lepsze rozwiązanie): zamiast hidden field, zmienić warunek w ProductRepository
|
||||||
|
z `array_key_exists('custom_field_name', $d)` na sprawdzanie obecności markera
|
||||||
|
`custom_field_name_present`.
|
||||||
|
|
||||||
|
Podejście: dodać hidden field `custom_field_name_present` w szablonie
|
||||||
|
+ zmienić warunek w ProductRepository na:
|
||||||
|
```php
|
||||||
|
if ( array_key_exists( 'custom_field_name_present', $d ) ) {
|
||||||
|
```
|
||||||
|
Dzięki temu:
|
||||||
|
- Gdy formularz jest renderowany → marker ZAWSZE w POST → saveCustomFields() ZAWSZE wywoływany
|
||||||
|
- Gdy API partial update bez custom fields → marker BRAK → skip (backward compat)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
1. Otworzyć edycję produktu z custom fields w przeglądarce
|
||||||
|
2. Usunąć wszystkie pola → Zatwierdź → sprawdzić że pola zniknęły
|
||||||
|
3. Otworzyć ponownie → potwierdzić brak pól
|
||||||
|
</verify>
|
||||||
|
<done>AC-1 satisfied: usunięcie wszystkich pól działa poprawnie</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Test jednostkowy — saveCustomFields z pustą listą</name>
|
||||||
|
<files>tests/Unit/Domain/Product/ProductRepositoryTest.php</files>
|
||||||
|
<action>
|
||||||
|
Dodać test weryfikujący że saveCustomFields() z pustymi tablicami
|
||||||
|
wywołuje delete na pp_shop_products_custom_fields dla danego produktu.
|
||||||
|
|
||||||
|
Test powinien mockować Medoo i sprawdzić:
|
||||||
|
- Że `delete('pp_shop_products_custom_fields', ['id_product' => $productId])` jest wywoływany
|
||||||
|
- Że żaden insert/update nie jest wywoływany
|
||||||
|
|
||||||
|
saveCustomFields() jest private — użyć Reflection do wywołania
|
||||||
|
lub testować przez publiczną metodę saveProduct() z odpowiednim payloadem
|
||||||
|
zawierającym `custom_field_name_present` i puste tablice.
|
||||||
|
</action>
|
||||||
|
<verify>./test.ps1 --filter testSaveCustomFieldsDeletesAllWhenEmpty</verify>
|
||||||
|
<done>AC-1 potwierdzone testem jednostkowym</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- Logika saveCustomFields() dla niepustych list pól (insert/update) — działa poprawnie
|
||||||
|
- API partial update — brak markera = skip custom fields (backward compat)
|
||||||
|
- Inne sekcje formularza edycji produktu
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Tylko naprawa buga usuwania pól — żadne refactoring ani nowe feature
|
||||||
|
- Nie zmieniać struktury tabeli pp_shop_products_custom_fields
|
||||||
|
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] Usunięcie wszystkich custom fields → po zapisie brak pól (AC-1)
|
||||||
|
- [ ] Usunięcie części custom fields → pozostałe zachowane (AC-2)
|
||||||
|
- [ ] Dodanie nowych custom fields → poprawnie zapisane (AC-3)
|
||||||
|
- [ ] Testy przechodzą: ./test.ps1
|
||||||
|
- [ ] Brak regresji w istniejących testach
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Wszystkie 3 AC spełnione
|
||||||
|
- Test jednostkowy przechodzi
|
||||||
|
- Zero regresji w istniejącym test suite (820+ testów)
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.paul/phases/14-custom-fields-delete-bug/14-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
95
.paul/phases/14-custom-fields-delete-bug/14-01-SUMMARY.md
Normal file
95
.paul/phases/14-custom-fields-delete-bug/14-01-SUMMARY.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
phase: 14-custom-fields-delete-bug
|
||||||
|
plan: 01
|
||||||
|
subsystem: admin
|
||||||
|
tags: [custom-fields, product-edit, form-serialize, hidden-field]
|
||||||
|
|
||||||
|
requires: []
|
||||||
|
provides:
|
||||||
|
- Fix usuwania wszystkich custom fields z produktu
|
||||||
|
affects: []
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns: [hidden marker field for form section detection]
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: []
|
||||||
|
modified:
|
||||||
|
- autoload/admin/Controllers/ShopProductController.php
|
||||||
|
- autoload/Domain/Product/ProductRepository.php
|
||||||
|
- tests/Unit/Domain/Product/ProductRepositoryTest.php
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Hidden marker custom_field_name_present zamiast polegania na obecności custom_field_name[] w POST"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Marker hidden field pattern: gdy sekcja formularza może mieć 0 elementów, dodaj hidden marker żeby serwer wiedział że sekcja była renderowana"
|
||||||
|
|
||||||
|
duration: ~10min
|
||||||
|
completed: 2026-04-16
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 14 Plan 01: Custom fields delete bug fix — Summary
|
||||||
|
|
||||||
|
**Naprawiono bug uniemożliwiający usunięcie wszystkich dodatkowych pól produktu — hidden marker gwarantuje wywołanie saveCustomFields() niezależnie od ilości pól.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~10min |
|
||||||
|
| Completed | 2026-04-16 |
|
||||||
|
| Tasks | 2 completed |
|
||||||
|
| Files modified | 3 |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Usunięcie wszystkich custom fields | Pass | saveCustomFields() wywoływany dzięki markerowi, else branch kasuje wszystkie rekordy |
|
||||||
|
| AC-2: Częściowe usunięcie nadal działa | Pass | Logika saveCustomFields() dla niepustych list bez zmian |
|
||||||
|
| AC-3: Dodawanie pól nadal działa | Pass | Marker nie wpływa na insert/update path |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Dodano hidden field `custom_field_name_present` w `renderCustomFieldsBox()` — zawsze obecny w POST
|
||||||
|
- Zmieniono warunek w `ProductRepository:1339` z `custom_field_name` na `custom_field_name_present`
|
||||||
|
- Dodano test jednostkowy potwierdzający delete all path (821 testów, 0 regresji)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `autoload/admin/Controllers/ShopProductController.php` | Modified | Hidden marker `custom_field_name_present` w renderCustomFieldsBox() |
|
||||||
|
| `autoload/Domain/Product/ProductRepository.php` | Modified | Warunek zmieniony na sprawdzanie markera |
|
||||||
|
| `tests/Unit/Domain/Product/ProductRepositoryTest.php` | Modified | Test testSaveCustomFieldsDeletesAllWhenEmpty |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| Hidden marker zamiast wysyłania pustego array | jQuery .serialize() pomija puste pola array — marker jest niezawodny | Backward compat z API partial update (brak markera = skip) |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None — plan executed exactly as written.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- Bug naprawiony, test przechodzi, zero regresji
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- None
|
||||||
|
|
||||||
|
**Blockers:**
|
||||||
|
- None
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 14-custom-fields-delete-bug, Plan: 01*
|
||||||
|
*Completed: 2026-04-16*
|
||||||
File diff suppressed because one or more lines are too long
@@ -2,5 +2,5 @@ projectKey=shopPRO
|
|||||||
serverUrl=https://sonar.project-pro.pl
|
serverUrl=https://sonar.project-pro.pl
|
||||||
serverVersion=26.3.0.120487
|
serverVersion=26.3.0.120487
|
||||||
dashboardUrl=https://sonar.project-pro.pl/dashboard?id=shopPRO
|
dashboardUrl=https://sonar.project-pro.pl/dashboard?id=shopPRO
|
||||||
ceTaskId=5c360d0b-34ee-4995-ae1b-d5ac7ca47218
|
ceTaskId=77fcbbea-9d8f-45d6-86d7-b262e33f979e
|
||||||
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=5c360d0b-34ee-4995-ae1b-d5ac7ca47218
|
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=77fcbbea-9d8f-45d6-86d7-b262e33f979e
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ composer test # standard
|
|||||||
|
|
||||||
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
|
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
|
||||||
|
|
||||||
Current suite: **820 tests, 2277 assertions**.
|
Current suite: **821 tests, 2278 assertions**.
|
||||||
|
|
||||||
### Creating Updates
|
### 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.
|
||||||
|
|||||||
@@ -1335,8 +1335,9 @@ class ProductRepository
|
|||||||
$this->saveImagesOrder( $productId, $d['gallery_order'] );
|
$this->saveImagesOrder( $productId, $d['gallery_order'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zapisz custom fields tylko gdy jawnie podane (partial update przez API może nie zawierać tego klucza)
|
// Zapisz custom fields tylko gdy formularz edycji renderował sekcję (marker hidden field)
|
||||||
if ( array_key_exists( 'custom_field_name', $d ) ) {
|
// API partial update nie zawiera tego markera — custom fields pominięte
|
||||||
|
if ( array_key_exists( 'custom_field_name_present', $d ) ) {
|
||||||
$this->saveCustomFields( $productId, $d['custom_field_name'] ?? [], $d['custom_field_type'] ?? [], $d['custom_field_required'] ?? [] );
|
$this->saveCustomFields( $productId, $d['custom_field_name'] ?? [], $d['custom_field_type'] ?? [], $d['custom_field_required'] ?? [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -699,7 +699,8 @@ class ShopProductController
|
|||||||
|
|
||||||
private function renderCustomFieldsBox( array $product ): string
|
private function renderCustomFieldsBox( array $product ): string
|
||||||
{
|
{
|
||||||
$html = '<a href="#" class="btn btn-success" id="add_custom_field"><i class="fa fa-plus"></i> dodaj niestandardowe pole</a>';
|
$html = '<input type="hidden" name="custom_field_name_present" value="1">';
|
||||||
|
$html .= '<a href="#" class="btn btn-success" id="add_custom_field"><i class="fa fa-plus"></i> dodaj niestandardowe pole</a>';
|
||||||
$html .= '<div class="additional_fields pt-3">';
|
$html .= '<div class="additional_fields pt-3">';
|
||||||
|
|
||||||
$customFields = is_array( $product['custom_fields'] ?? null ) ? $product['custom_fields'] : [];
|
$customFields = is_array( $product['custom_fields'] ?? null ) ? $product['custom_fields'] : [];
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ver. 0.346 (2026-04-16) - Fix usuwania wszystkich dodatkowych pól produktu
|
||||||
|
|
||||||
|
- **FIX**: `autoload/admin/Controllers/ShopProductController.php` — dodany hidden marker `custom_field_name_present` w `renderCustomFieldsBox()`, gwarantujący że sekcja custom fields jest zawsze rozpoznawana w POST nawet gdy wszystkie pola usunięte
|
||||||
|
- **FIX**: `autoload/Domain/Product/ProductRepository.php` — warunek zapisu custom fields zmieniony z `array_key_exists('custom_field_name')` na `array_key_exists('custom_field_name_present')` — naprawa buga gdzie jQuery `.serialize()` pomijał klucz pustej tablicy
|
||||||
|
- **NEW**: `tests/Unit/Domain/Product/ProductRepositoryTest.php` — test `testSaveCustomFieldsDeletesAllWhenEmpty` potwierdzający poprawne kasowanie wszystkich pól
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ver. 0.345 (2026-03-25) - DataLayer GA4 fix + checkout token fix
|
## ver. 0.345 (2026-03-25) - DataLayer GA4 fix + checkout token fix
|
||||||
|
|
||||||
- **FIX**: `templates/shop-order/order-details.php` — event purchase: id→item_id (string), name→item_name, price via normalize_decimal (fix price:0), usunięty hardcoded value: 25.42, dodany google_business_vertical
|
- **FIX**: `templates/shop-order/order-details.php` — event purchase: id→item_id (string), name→item_name, price via normalize_decimal (fix price:0), usunięty hardcoded value: 25.42, dodany google_business_vertical
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ composer test # standard
|
|||||||
## Aktualny stan
|
## Aktualny stan
|
||||||
|
|
||||||
```text
|
```text
|
||||||
OK (820 tests, 2277 assertions)
|
OK (821 tests, 2278 assertions)
|
||||||
```
|
```
|
||||||
|
|
||||||
Zweryfikowano: 2026-03-19 (ver. 0.342)
|
Zweryfikowano: 2026-04-16 (ver. 0.346)
|
||||||
|
|
||||||
## Konfiguracja
|
## Konfiguracja
|
||||||
|
|
||||||
|
|||||||
@@ -1292,4 +1292,25 @@ class ProductRepositoryTest extends TestCase
|
|||||||
|
|
||||||
$this->assertFalse($result);
|
$this->assertFalse($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSaveCustomFieldsDeletesAllWhenEmpty(): void
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|
||||||
|
$mockDb->expects($this->once())
|
||||||
|
->method('delete')
|
||||||
|
->with(
|
||||||
|
$this->equalTo('pp_shop_products_custom_fields'),
|
||||||
|
$this->equalTo(['id_product' => 55])
|
||||||
|
);
|
||||||
|
|
||||||
|
$mockDb->expects($this->never())->method('insert');
|
||||||
|
$mockDb->expects($this->never())->method('update');
|
||||||
|
|
||||||
|
$repository = new ProductRepository($mockDb);
|
||||||
|
|
||||||
|
$method = new \ReflectionMethod(ProductRepository::class, 'saveCustomFields');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
$method->invoke($repository, 55, [], [], []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
updates/0.30/ver_0.345.zip
Normal file
BIN
updates/0.30/ver_0.345.zip
Normal file
Binary file not shown.
27
updates/0.30/ver_0.345_manifest.json
Normal file
27
updates/0.30/ver_0.345_manifest.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"changelog": "Checkout flow fix - summaryView redirect, TTL token 30min, logowanie bledow zamowien",
|
||||||
|
"version": "0.345",
|
||||||
|
"files": {
|
||||||
|
"added": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"deleted": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
"autoload/front/Controllers/ShopBasketController.php",
|
||||||
|
"templates/shop-basket/basket.php",
|
||||||
|
"templates/shop-basket/summary-view.php",
|
||||||
|
"templates/shop-order/order-details.php",
|
||||||
|
"templates/shop-product/product.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"checksum_zip": "sha256:805e4d80fe75679c937974059594984377a81cad84a1308d9deefb96e0b39319",
|
||||||
|
"sql": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"date": "2026-03-25",
|
||||||
|
"directories_deleted": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
<b>ver. 0.345 - 25.03.2026</b><br />
|
||||||
|
Checkout flow fix - summaryView redirect, TTL token 30min, logowanie bledow zamowien
|
||||||
|
<hr>
|
||||||
<b>ver. 0.344 - 19.03.2026</b><br />
|
<b>ver. 0.344 - 19.03.2026</b><br />
|
||||||
Edycja personalizacji produktu w koszyku
|
Edycja personalizacji produktu w koszyku
|
||||||
<hr>
|
<hr>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?
|
<?
|
||||||
$current_ver = 344;
|
$current_ver = 345;
|
||||||
|
|
||||||
for ($i = 1; $i <= $current_ver; $i++)
|
for ($i = 1; $i <= $current_ver; $i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user