Refactor code structure for improved readability and maintainability
This commit is contained in:
File diff suppressed because one or more lines are too long
107
DATABASE_STRUCTURE.md
Normal file
107
DATABASE_STRUCTURE.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# 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 |
|
||||||
|
| baselinker_product_name | Nazwa produktu w Baselinker |
|
||||||
|
| apilo_product_name | Nazwa produktu w Apilo |
|
||||||
|
|
||||||
|
**Używane w:** `Domain\Product\ProductRepository`, `admin\factory\ShopProduct`
|
||||||
|
|
||||||
|
## 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_categories
|
||||||
|
Przypisanie produktów do kategorii.
|
||||||
|
|
||||||
|
| Kolumna | Opis |
|
||||||
|
|---------|------|
|
||||||
|
| product_id | FK do pp_shop_products |
|
||||||
|
|
||||||
|
**Używane w:** `admin\factory\ShopProduct::product_delete()`
|
||||||
|
|
||||||
|
## 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`
|
||||||
|
|
||||||
|
## 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`
|
||||||
|
|
||||||
|
## pp_articles
|
||||||
|
Artykuły.
|
||||||
|
|
||||||
|
| Kolumna | Opis |
|
||||||
|
|---------|------|
|
||||||
|
| id | PK |
|
||||||
|
| status | -1 = archiwum, 0 = nieaktywny, 1 = aktywny |
|
||||||
|
|
||||||
|
**Używane w:** `admin\controls\ArticlesArchive`
|
||||||
|
|
||||||
|
## pp_articles_pages
|
||||||
|
Strony artykułów.
|
||||||
|
|
||||||
|
## pp_articles_langs
|
||||||
|
Tłumaczenia artykułów.
|
||||||
|
|
||||||
|
## pp_articles_images
|
||||||
|
Zdjęcia artykułów.
|
||||||
|
|
||||||
|
## pp_articles_files
|
||||||
|
Pliki artykułów.
|
||||||
@@ -185,7 +185,7 @@ $product = \shop\Product::getFromCache($product_id, $lang_id, $permutation_hash)
|
|||||||
autoload/
|
autoload/
|
||||||
├── Domain/ # Nowa warstwa biznesowa (namespace \Domain\)
|
├── Domain/ # Nowa warstwa biznesowa (namespace \Domain\)
|
||||||
│ ├── Product/
|
│ ├── Product/
|
||||||
│ │ └── ProductRepository.php # getQuantity, getPrice, getName, find, updateQuantity
|
│ │ └── ProductRepository.php # getQuantity, getPrice, getName, find, updateQuantity, archive, unarchive
|
||||||
│ ├── Banner/
|
│ ├── Banner/
|
||||||
│ │ └── BannerRepository.php # find, delete, save
|
│ │ └── BannerRepository.php # find, delete, save
|
||||||
│ ├── Settings/
|
│ ├── Settings/
|
||||||
@@ -194,8 +194,9 @@ autoload/
|
|||||||
│ └── CacheRepository.php # clearCache (dirs + Redis)
|
│ └── CacheRepository.php # clearCache (dirs + Redis)
|
||||||
├── admin/
|
├── admin/
|
||||||
│ ├── Controllers/ # Nowe kontrolery (namespace \admin\Controllers\)
|
│ ├── Controllers/ # Nowe kontrolery (namespace \admin\Controllers\)
|
||||||
│ │ ├── BannerController.php # DI, instancyjny
|
│ │ ├── BannerController.php # DI, instancyjny
|
||||||
│ │ └── SettingsController.php # DI, instancyjny (clearCache, save, view)
|
│ │ ├── SettingsController.php # DI, instancyjny (clearCache, save, view)
|
||||||
|
│ │ └── ProductArchiveController.php # DI, instancyjny (list, unarchive)
|
||||||
│ ├── class.Site.php # Router: nowy kontroler → fallback stary
|
│ ├── class.Site.php # Router: nowy kontroler → fallback stary
|
||||||
│ ├── controls/ # Stare kontrolery (niezależny fallback)
|
│ ├── controls/ # Stare kontrolery (niezależny fallback)
|
||||||
│ ├── factory/ # Stare helpery (niezależny fallback)
|
│ ├── factory/ # Stare helpery (niezależny fallback)
|
||||||
@@ -240,13 +241,25 @@ tests/
|
|||||||
│ │ ├── Settings/SettingsRepositoryTest.php # 3 testy
|
│ │ ├── Settings/SettingsRepositoryTest.php # 3 testy
|
||||||
│ │ └── Cache/CacheRepositoryTest.php # 4 testy
|
│ │ └── Cache/CacheRepositoryTest.php # 4 testy
|
||||||
│ └── admin/
|
│ └── admin/
|
||||||
│ └── Controllers/SettingsControllerTest.php # 7 testów
|
│ └── Controllers/
|
||||||
|
│ ├── SettingsControllerTest.php # 7 testów
|
||||||
|
│ └── ProductArchiveControllerTest.php # 6 testów
|
||||||
└── Integration/
|
└── Integration/
|
||||||
```
|
```
|
||||||
**Łącznie: 29 testów, 60 asercji**
|
**Łącznie: 39 testów, 73 asercji**
|
||||||
|
|
||||||
## Ostatnie modyfikacje
|
## Ostatnie modyfikacje
|
||||||
|
|
||||||
|
### 2026-02-06: Migracja ProductArchive (ver. 0.241)
|
||||||
|
- **NOWE:** `admin\Controllers\ProductArchiveController` - kontroler archiwum produktów z DI
|
||||||
|
- **NOWE:** `ProductRepository::archive()`, `unarchive()` - operacje archiwizacji w repozytorium
|
||||||
|
- **RENAME:** `admin/templates/archive/` → `admin/templates/product_archive/`
|
||||||
|
- **FIX:** SQL w `ajax_products_list_archive()` - puste wyszukiwanie generowało `name|ean|sku LIKE '%%'` (NULL bitwise OR filtrował wyniki)
|
||||||
|
- **FIX:** Brakujący `archive = 1` w branchu bez wyszukiwania
|
||||||
|
- **CLEANUP:** Usunięto zbędny JS z szablonu archiwum (apilo, baselinker, duplikowanie, edycja cen)
|
||||||
|
- Stary kontroler `admin\controls\Archive` zachowany jako fallback
|
||||||
|
- Testy: 39 testów, 73 asercji (+10 nowych)
|
||||||
|
|
||||||
### 2026-02-05: Migracja Settings + Cache (ver. 0.240)
|
### 2026-02-05: Migracja Settings + Cache (ver. 0.240)
|
||||||
- **NOWE:** `Domain\Settings\SettingsRepository` - repozytorium ustawień (fasada → factory)
|
- **NOWE:** `Domain\Settings\SettingsRepository` - repozytorium ustawień (fasada → factory)
|
||||||
- **NOWE:** `Domain\Cache\CacheRepository` - repozytorium cache (dirs + Redis)
|
- **NOWE:** `Domain\Cache\CacheRepository` - repozytorium cache (dirs + Redis)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Stopniowe przeniesienie logiki biznesowej do architektury warstwowej:
|
|||||||
autoload/
|
autoload/
|
||||||
├── Domain/ # Logika biznesowa (CORE) - namespace \Domain\
|
├── Domain/ # Logika biznesowa (CORE) - namespace \Domain\
|
||||||
│ ├── Product/
|
│ ├── Product/
|
||||||
│ │ ├── ProductRepository.php # ✅ Zmigrowane (getQuantity, getPrice, getName, find, updateQuantity)
|
│ │ ├── ProductRepository.php # ✅ Zmigrowane (getQuantity, getPrice, getName, find, updateQuantity, archive, unarchive)
|
||||||
│ │ ├── ProductService.php # Logika biznesowa (przyszłość)
|
│ │ ├── ProductService.php # Logika biznesowa (przyszłość)
|
||||||
│ │ └── ProductCacheService.php # Cache produktu (przyszłość)
|
│ │ └── ProductCacheService.php # Cache produktu (przyszłość)
|
||||||
│ ├── Banner/
|
│ ├── Banner/
|
||||||
@@ -155,6 +155,13 @@ grep -r "Product::getQuantity" .
|
|||||||
- Testy: ✅ 2 nowe testy (nazwa znaleziona, nie znaleziona)
|
- Testy: ✅ 2 nowe testy (nazwa znaleziona, nie znaleziona)
|
||||||
- Użycie: brak aktywnych wywołań (przygotowane na przyszłość)
|
- Użycie: brak aktywnych wywołań (przygotowane na przyszłość)
|
||||||
- Aktualizacja: ver. 0.239
|
- Aktualizacja: ver. 0.239
|
||||||
|
- ✅ archive() / unarchive() - **ZMIGROWANE** (2026-02-06) 🎉
|
||||||
|
- Nowe metody: `Domain\Product\ProductRepository::archive()`, `unarchive()`
|
||||||
|
- Nowy kontroler: `admin\Controllers\ProductArchiveController` (DI, instancyjny)
|
||||||
|
- Szablony: `admin/templates/product_archive/` (rename z `archive/`)
|
||||||
|
- Testy: ✅ 4 nowe testy repozytorium + 6 testów kontrolera
|
||||||
|
- FIX: SQL bug w `ajax_products_list_archive()` (puste wyszukiwanie + brak `archive = 1`)
|
||||||
|
- Aktualizacja: ver. 0.241
|
||||||
- [ ] is_product_on_promotion() - NASTĘPNA 👉
|
- [ ] is_product_on_promotion() - NASTĘPNA 👉
|
||||||
|
|
||||||
- **Banner** (DEMO pełnej migracji kontrolera)
|
- **Banner** (DEMO pełnej migracji kontrolera)
|
||||||
@@ -200,15 +207,17 @@ composer require --dev phpunit/phpunit
|
|||||||
tests/
|
tests/
|
||||||
├── Unit/
|
├── Unit/
|
||||||
│ ├── Domain/
|
│ ├── Domain/
|
||||||
│ │ ├── Product/ProductRepositoryTest.php # 11 testów
|
│ │ ├── Product/ProductRepositoryTest.php # 15 testów
|
||||||
│ │ ├── Banner/BannerRepositoryTest.php # 4 testy
|
│ │ ├── Banner/BannerRepositoryTest.php # 4 testy
|
||||||
│ │ ├── Settings/SettingsRepositoryTest.php # 3 testy
|
│ │ ├── Settings/SettingsRepositoryTest.php # 3 testy
|
||||||
│ │ └── Cache/CacheRepositoryTest.php # 4 testy
|
│ │ └── Cache/CacheRepositoryTest.php # 4 testy
|
||||||
│ └── admin/
|
│ └── admin/
|
||||||
│ └── Controllers/SettingsControllerTest.php # 7 testów
|
│ └── Controllers/
|
||||||
|
│ ├── SettingsControllerTest.php # 7 testów
|
||||||
|
│ └── ProductArchiveControllerTest.php # 6 testów
|
||||||
└── Integration/
|
└── Integration/
|
||||||
```
|
```
|
||||||
**Łącznie: 29 testów, 60 asercji**
|
**Łącznie: 39 testów, 73 asercji**
|
||||||
|
|
||||||
### Przykład testu
|
### Przykład testu
|
||||||
```php
|
```php
|
||||||
@@ -283,12 +292,14 @@ vendor/bin/phpstan analyse autoload/Domain
|
|||||||
- ✅ getQuantity (ver. 0.238)
|
- ✅ getQuantity (ver. 0.238)
|
||||||
- ✅ getPrice (ver. 0.239)
|
- ✅ getPrice (ver. 0.239)
|
||||||
- ✅ getName (ver. 0.239)
|
- ✅ getName (ver. 0.239)
|
||||||
|
- ✅ archive / unarchive (ver. 0.241)
|
||||||
- [ ] is_product_on_promotion - NASTĘPNA 👉
|
- [ ] is_product_on_promotion - NASTĘPNA 👉
|
||||||
- [ ] getFromCache
|
- [ ] getFromCache
|
||||||
- [ ] getProductImg
|
- [ ] getProductImg
|
||||||
3. **Banner** ✅ (pełna migracja kontrolera, ver. 0.239)
|
3. **Banner** ✅ (pełna migracja kontrolera, ver. 0.239)
|
||||||
4. **Settings** ✅ (migracja kontrolera - krok pośredni, ver. 0.240)
|
4. **Settings** ✅ (migracja kontrolera - krok pośredni, ver. 0.240)
|
||||||
5. **Order**
|
5. **ProductArchive** ✅ (migracja kontrolera + cleanup szablonów, ver. 0.241)
|
||||||
|
6. **Order**
|
||||||
5. **Category**
|
5. **Category**
|
||||||
6. **ShopAttribute**
|
6. **ShopAttribute**
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
|
|||||||
## Procedura tworzenia nowej aktualizacji
|
## Procedura tworzenia nowej aktualizacji
|
||||||
|
|
||||||
### 1. Określ numer wersji
|
### 1. Określ numer wersji
|
||||||
Sprawdź ostatnią wersję w `updates/0.20/` i zwiększ o 1.
|
Sprawdź ostatnią wersję w `temp/` i zwiększ o 1.
|
||||||
|
|
||||||
### 2. Utwórz folder tymczasowy ze strukturą
|
### 2. Utwórz folder tymczasowy ze strukturą w katalogu temp
|
||||||
```bash
|
```bash
|
||||||
mkdir -p updates/0.20/temp_XXX/sciezka/do/pliku
|
mkdir -p temp/temp_XXX/sciezka/do/pliku
|
||||||
```
|
```
|
||||||
|
|
||||||
**WAŻNE:** W archiwum ZIP NIE powinno być folderu z nazwą wersji (np. ver_0.234/).
|
**WAŻNE:** W archiwum ZIP NIE powinno być folderu z nazwą wersji (np. ver_0.234/).
|
||||||
@@ -26,18 +26,18 @@ Struktura ZIP powinna zaczynać się bezpośrednio od katalogów projektu (admin
|
|||||||
|
|
||||||
### 3. Skopiuj zmienione pliki do folderu tymczasowego
|
### 3. Skopiuj zmienione pliki do folderu tymczasowego
|
||||||
```bash
|
```bash
|
||||||
cp sciezka/do/pliku.php updates/0.20/temp_XXX/sciezka/do/pliku.php
|
cp sciezka/do/pliku.php temp/temp_XXX/sciezka/do/pliku.php
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Utwórz plik ZIP z zawartości folderu (nie z samego folderu!)
|
### 4. Utwórz plik ZIP z zawartości folderu (nie z samego folderu!)
|
||||||
```powershell
|
```powershell
|
||||||
cd updates/0.20/temp_XXX
|
cd temp/temp_XXX
|
||||||
powershell -Command "Compress-Archive -Path '*' -DestinationPath '../ver_X.XXX.zip' -Force"
|
powershell -Command "Compress-Archive -Path '*' -DestinationPath '../ver_X.XXX.zip' -Force"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Usuń folder tymczasowy
|
### 5. Usuń folder tymczasowy
|
||||||
```bash
|
```bash
|
||||||
rm -rf updates/0.20/temp_XXX
|
rm -rf temp/temp_XXX
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Zaktualizuj changelog.php
|
### 6. Zaktualizuj changelog.php
|
||||||
@@ -82,15 +82,15 @@ Opis: Dodanie przycisku do zaznaczania zamówienia jako wysłane do trustmate.io
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Utwórz strukturę w folderze tymczasowym
|
# Utwórz strukturę w folderze tymczasowym
|
||||||
mkdir -p updates/0.20/temp_234/autoload/admin/controls
|
mkdir -p temp/temp_234/autoload/admin/controls
|
||||||
mkdir -p updates/0.20/temp_234/admin/templates/shop-order
|
mkdir -p temp/temp_234/admin/templates/shop-order
|
||||||
|
|
||||||
# Skopiuj pliki
|
# Skopiuj pliki
|
||||||
cp autoload/admin/controls/class.ShopOrder.php updates/0.20/temp_234/autoload/admin/controls/
|
cp autoload/admin/controls/class.ShopOrder.php temp/temp_234/autoload/admin/controls/
|
||||||
cp admin/templates/shop-order/order-details.php updates/0.20/temp_234/admin/templates/shop-order/
|
cp admin/templates/shop-order/order-details.php temp/temp_234/admin/templates/shop-order/
|
||||||
|
|
||||||
# Utwórz ZIP z ZAWARTOŚCI folderu (ważne: wejdź do folderu i spakuj '*')
|
# Utwórz ZIP z ZAWARTOŚCI folderu (ważne: wejdź do folderu i spakuj '*')
|
||||||
cd updates/0.20/temp_234
|
cd temp/temp_234
|
||||||
powershell -Command "Compress-Archive -Path '*' -DestinationPath '../ver_0.234.zip' -Force"
|
powershell -Command "Compress-Archive -Path '*' -DestinationPath '../ver_0.234.zip' -Force"
|
||||||
|
|
||||||
# Wróć i usuń folder tymczasowy
|
# Wróć i usuń folder tymczasowy
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
<? $i = ( $this -> current_page - 1 ) * 10 + 1;?>
|
|
||||||
<? foreach ( $this -> products as $product ):?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<?= $i++;?>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="product-image">
|
|
||||||
<? if ( $product['images'][0]['src'] ):?>
|
|
||||||
<img src="<?= $product['images'][0]['src'];?>" alt="<?= $product['images'][0]['alt'];?>" class="img-responsive">
|
|
||||||
<? else:?>
|
|
||||||
<img src="/admin/layout/images/no-image.png" alt="Brak zdjęcia" class="img-responsive">
|
|
||||||
<? endif;?>
|
|
||||||
</div>
|
|
||||||
<div class="product-name">
|
|
||||||
<a href="/admin/shop_product/product_edit/id=<?= $product['id'];?>">
|
|
||||||
<?= $product['languages']['pl']['name'];?>
|
|
||||||
</a>
|
|
||||||
<a href="#" class="text-muted duplicate-product" product-id="<?= $product['id'];?>">duplikuj</a>
|
|
||||||
</div>
|
|
||||||
<small class="text-muted"><?= \admin\factory\ShopProduct::product_categories( $product['id'] );?></small>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<input type="text" class="product-price form-control text-right" product-id="<?= $product['id'];?>" value="<?= $product['price_brutto'];?>" style="max-width: 75px;">
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<input type="text" class="product-price-promo form-control text-right" product-id="<?= $product['id'];?>" value="<?= $product['price_brutto_promo'];?>" style="max-width: 75px;">
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<?= $product['promoted'] ? '<span class="text-success text-bold">tak</span>' : 'nie';?>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<?= $product['status'] ? 'tak' : '<span class="text-danger text-bold">nie</span>';?>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<span class="text-muted"><?= (int)\admin\factory\shopProduct::get_product_quantity_list( $product['id'] );?></span>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<?
|
|
||||||
if ( $product['baselinker_product_name'] != "" ) {
|
|
||||||
echo "<span title='" . htmlspecialchars( $product['baselinker_product_name'] ) . "'>" . mb_substr( $product['baselinker_product_name'], 0, 25, 'UTF-8' ) . "...</span>";
|
|
||||||
echo "<br>";
|
|
||||||
echo "<span class='text-danger baselinker-delete-linking' product-id='" . $product['id'] . "'>";
|
|
||||||
echo "<i class='fa fa-times'></i>usuń powiązanie";
|
|
||||||
echo "</span>";
|
|
||||||
} else {
|
|
||||||
echo "<span class='text-danger baselinker-product-search' product-id='" . $product['id'] . "'>";
|
|
||||||
echo "nie przypisano <i class='fa fa-search'></i>";
|
|
||||||
echo "</span>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<?
|
|
||||||
if ( $product['apilo_product_name'] != "" ) {
|
|
||||||
echo "<span title='" . htmlspecialchars( $product['apilo_product_name'] ) . "'>" . mb_substr( $product['apilo_product_name'], 0, 25, "UTF-8" ) . "...</span>";
|
|
||||||
echo "<br>";
|
|
||||||
echo "<span class='text-danger apilo-delete-linking' product-id='" . $product['id'] . "'>";
|
|
||||||
echo "<i class='fa fa-times'></i>usuń powiązanie";
|
|
||||||
echo "</span>";
|
|
||||||
} else {
|
|
||||||
echo "<span class='text-danger apilo-product-search' product-id='" . $product['id'] . "'>";
|
|
||||||
echo "nie przypisano <i class='fa fa-search'></i>";
|
|
||||||
echo "</span>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href='/admin/shop_product/product_combination/product_id=<?= $product['id'];?>'>kombinacje (<?= \admin\factory\shopProduct::count_product_combinations( $product['id'] );?>)</a>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href='/admin/shop_product/product_edit/id=<?= $product['id'];?>'>edytuj</a>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href='/admin/shop_product/product_unarchive/product_id=<?= $product['id'];?>' class="product-unarchive">przywróć</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<? endforeach;?>
|
|
||||||
@@ -1,488 +0,0 @@
|
|||||||
<div class="panel">
|
|
||||||
<div class="panel-body">
|
|
||||||
<a href="/admin/shop_product/product_edit/" class="btn btn-success select-all">Dodaj produkt</a>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body pn">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-bordered table-hover table-striped mbn" id="table-products">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 10px;">#</th>
|
|
||||||
<th>Nazwa</th>
|
|
||||||
<th class="text-center" style="width: 100px;">Cena</th>
|
|
||||||
<th class="text-center" style="width: 100px;">Cena promocyjna</th>
|
|
||||||
<th class="text-center" style="width: 25px;">Promowany</th>
|
|
||||||
<th class="text-center" style="width: 25px;">Aktywny</th>
|
|
||||||
<th class="text-center" style="width: 75px;">Stan MG</th>
|
|
||||||
<th class="text-center" style="width: 100px;">Baselinker</th>
|
|
||||||
<th class="text-center" style="width: 100px;">Apilo</th>
|
|
||||||
<th class="text-center" style="width: 100px;">Kombinacje</th>
|
|
||||||
<th class="text-center" style="width: 75px;">Edytuj</th>
|
|
||||||
<th class="text-center" style="width: 75px;">Przywróć</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th>
|
|
||||||
<input type="text" class="form-control table-search" field_name="name|ean|sku" placeholder="szukaj..." value="<?= htmlspecialchars( $this -> query_array['name'], ENT_QUOTES, 'UTF-8');?>">
|
|
||||||
</th>
|
|
||||||
<th colspan="10"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan="12">
|
|
||||||
<ul class="pagination" pagination_max="<?= $this -> pagination_max;?>">
|
|
||||||
<li>
|
|
||||||
<a href="#" page="1" title="pierwsza strona">
|
|
||||||
<i class="fa fa-angle-double-left"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" class="previous" page="<?= ( $this -> current_page - 1 > 1 ) ? ( $this -> current_page - 1 ) : 1;?>" title="poprzednia strona">
|
|
||||||
<i class="fa fa-angle-left" title="poprzednia strona"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div>
|
|
||||||
Strona <span id="current-page"><?= $this -> current_page;?></span> z <span id="max_page"><?= $this -> pagination_max;?></span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" class="next" page="<?= ( $this -> current_page + 1 < $this -> pagination_max ) ? ( $this -> current_page + 1 ) : $this -> pagination_max;?>" title="następna strona">
|
|
||||||
<i class="fa fa-angle-right"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" class="last" page="<?= $this -> pagination_max;?>" title="ostatnia strona">
|
|
||||||
<i class="fa fa-angle-double-right"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
$( function() {
|
|
||||||
ajax_load_products( <?= $this -> current_page;?>, null );
|
|
||||||
|
|
||||||
$( 'body' ).on( 'change', '.table-search', function() {
|
|
||||||
ajax_load_products( 1 );
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.pagination a', function() {
|
|
||||||
var current_page = $( this ).attr( 'page' );
|
|
||||||
ajax_load_products( current_page );
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.product-unarchive', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var href = $( this ).attr( 'href' );
|
|
||||||
$.alert({
|
|
||||||
title: 'Pytanie',
|
|
||||||
content: 'Na pewno chcesz przywrócić wybrany produkt z archiwum?',
|
|
||||||
type: 'orange',
|
|
||||||
closeIcon: true,
|
|
||||||
closeIconClass: 'fa fa-times',
|
|
||||||
typeAnimated: true,
|
|
||||||
animation: 'opacity',
|
|
||||||
columnClass: 'col-12 col-lg-10',
|
|
||||||
theme: 'supervan',
|
|
||||||
icon: 'fa fa-question',
|
|
||||||
buttons: {
|
|
||||||
cancel: {
|
|
||||||
text: 'Nie',
|
|
||||||
btnClass: 'btn-success',
|
|
||||||
action: function() {}
|
|
||||||
},
|
|
||||||
confirm: {
|
|
||||||
text: 'Tak',
|
|
||||||
btnClass: 'btn-danger',
|
|
||||||
keys: ['enter'],
|
|
||||||
action: function() {
|
|
||||||
document.location.href = href;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$( 'body' ).on( 'change', '.product-price-promo', function(e)
|
|
||||||
{
|
|
||||||
var price = $( this ).val();
|
|
||||||
price = price.replace( ' ', '' );
|
|
||||||
price = parseFloat( price.replace( ',', '.' ) * 1 );
|
|
||||||
price = number_format( price, 2, '.', '' );
|
|
||||||
|
|
||||||
$( this ).val( price );
|
|
||||||
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/product_change_price_brutto_promo/',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
product_id: product_id,
|
|
||||||
price: price
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status !== 'ok' )
|
|
||||||
create_error( response.msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'change', '.product-price', function(e)
|
|
||||||
{
|
|
||||||
var price = $( this ).val();
|
|
||||||
price = price.replace( ' ', '' );
|
|
||||||
price = parseFloat( price.replace( ',', '.' ) * 1 );
|
|
||||||
price = number_format( price, 2, '.', '' );
|
|
||||||
|
|
||||||
$( this ).val( price );
|
|
||||||
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/product_change_price_brutto/',
|
|
||||||
data:
|
|
||||||
{
|
|
||||||
product_id: product_id,
|
|
||||||
price: price
|
|
||||||
},
|
|
||||||
beforeSend: function()
|
|
||||||
{
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( data )
|
|
||||||
{
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
|
|
||||||
response = jQuery.parseJSON( data );
|
|
||||||
|
|
||||||
if ( response.status !== 'ok' )
|
|
||||||
create_error( response.msg );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.duplicate-product', function(e)
|
|
||||||
{
|
|
||||||
e.preventDefault();
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.alert({
|
|
||||||
title: 'Pytanie',
|
|
||||||
content: 'Na pewno chcesz wykonać duplikat produktu?',
|
|
||||||
type: 'orange',
|
|
||||||
closeIcon: true,
|
|
||||||
closeIconClass: 'fa fa-times',
|
|
||||||
typeAnimated: true,
|
|
||||||
animation: 'opacity',
|
|
||||||
columnClass: 'col-12 col-lg-10',
|
|
||||||
theme: 'modern',
|
|
||||||
icon: 'fa fa-question',
|
|
||||||
buttons: {
|
|
||||||
confirm: {
|
|
||||||
text: 'Tak (produkt bez kombinacji)',
|
|
||||||
btnClass: 'btn-success',
|
|
||||||
keys: ['enter'],
|
|
||||||
action: function() {
|
|
||||||
document.location.href = '/admin/shop_product/duplicate_product/product-id=' + product_id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
confirm2: {
|
|
||||||
text: 'Tak (produkt z KOMBINACJAMI)',
|
|
||||||
btnClass: 'btn-primary',
|
|
||||||
keys: ['enter'],
|
|
||||||
action: function() {
|
|
||||||
document.location.href = '/admin/shop_product/duplicate_product/product-id=' + product_id + '&combination=1';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cancel: {
|
|
||||||
text: 'Nie',
|
|
||||||
btnClass: 'btn-dark',
|
|
||||||
action: function() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
// apilo product search
|
|
||||||
$( 'body' ).on( 'click', '.apilo-product-search', function() {
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/integrations/apilo_product_search/',
|
|
||||||
data: {
|
|
||||||
product_id: product_id
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'SUCCESS' ) {
|
|
||||||
var html = '<div class="apilo-found-products">';
|
|
||||||
html += '<p>Znaleziono ' + data.products.length + ' produktów</p>';
|
|
||||||
html += '<select class="form-control apilo-product-select" product-id="' + product_id + '">';
|
|
||||||
$.each( data.products, function( index, value ) {
|
|
||||||
html += '<option value="' + value.id + '">' + value.name + ' SKU: ' + value.sku + '</option>';
|
|
||||||
});
|
|
||||||
html += '</select>';
|
|
||||||
html += '<button class="btn btn-success apilo-product-select-save" product-id="' + product_id + '">Zapisz</button>';
|
|
||||||
html += '</div>';
|
|
||||||
$( 'span.apilo-product-search[product-id="' + product_id + '"]' ).closest( 'td' ).append( html );
|
|
||||||
} else if ( data.status == 'error' ) {
|
|
||||||
$.alert({
|
|
||||||
title: 'Błąd',
|
|
||||||
content: data.msg,
|
|
||||||
type: 'red',
|
|
||||||
closeIcon: true,
|
|
||||||
closeIconClass: 'fa fa-times',
|
|
||||||
typeAnimated: true,
|
|
||||||
animation: 'opacity',
|
|
||||||
columnClass: 'col-12 col-lg-10',
|
|
||||||
theme: 'modern',
|
|
||||||
icon: 'fa fa-exclamation-triangle',
|
|
||||||
buttons: {
|
|
||||||
confirm: {
|
|
||||||
text: 'OK',
|
|
||||||
btnClass: 'btn-danger',
|
|
||||||
keys: ['enter'],
|
|
||||||
action: function() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete apilo product linking
|
|
||||||
$( 'body' ).on( 'click', '.apilo-delete-linking', function() {
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/apilo_product_select_delete/',
|
|
||||||
data: {
|
|
||||||
product_id: product_id
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'ok' ) {
|
|
||||||
$( 'span.apilo-delete-linking[product-id="' + product_id + '"]' ).closest('td').html( '<span class="text-danger apilo-product-search" product-id="' + product_id + '">nie przypisano <i class="fa fa-search"></i></span>' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete baselinker product linking
|
|
||||||
$( 'body' ).on( 'click', '.baselinker-delete-linking', function() {
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/baselinker_product_select_delete/',
|
|
||||||
data: {
|
|
||||||
product_id: product_id
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'ok' ) {
|
|
||||||
$( 'span.baselinker-delete-linking[product-id="' + product_id + '"]' ).closest('td').html( '<span class="text-danger baselinker-product-search" product-id="' + product_id + '">nie przypisano <i class="fa fa-search"></i></span>' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.baselinker-product-search', function() {
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/baselinker_product_search/',
|
|
||||||
data: {
|
|
||||||
product_id: product_id
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'SUCCESS' ) {
|
|
||||||
var html = '<div class="baselinker-found-products">';
|
|
||||||
html += '<p>Znaleziono ' + data.products.length + ' produktów</p>';
|
|
||||||
html += '<select class="form-control baselinker-product-select" product-id="' + product_id + '">';
|
|
||||||
$.each( data.products, function( index, value ) {
|
|
||||||
html += '<option value="' + value.product_id + '">' + value.name + ' SKU: ' + value.sku + '</option>';
|
|
||||||
});
|
|
||||||
html += '</select>';
|
|
||||||
html += '<button class="btn btn-success baselinker-product-select-save" product-id="' + product_id + '">Zapisz</button>';
|
|
||||||
html += '</div>';
|
|
||||||
$( 'span.baselinker-product-search[product-id="' + product_id + '"]' ).closest( 'td' ).append( html );
|
|
||||||
} else if ( data.status == 'error' ) {
|
|
||||||
$.alert({
|
|
||||||
title: 'Błąd',
|
|
||||||
content: data.msg,
|
|
||||||
type: 'red',
|
|
||||||
closeIcon: true,
|
|
||||||
closeIconClass: 'fa fa-times',
|
|
||||||
typeAnimated: true,
|
|
||||||
animation: 'opacity',
|
|
||||||
columnClass: 'col-12 col-lg-10',
|
|
||||||
theme: 'modern',
|
|
||||||
icon: 'fa fa-exclamation-triangle',
|
|
||||||
buttons: {
|
|
||||||
confirm: {
|
|
||||||
text: 'OK',
|
|
||||||
btnClass: 'btn-danger',
|
|
||||||
keys: ['enter'],
|
|
||||||
action: function() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// apilo product select save
|
|
||||||
$( 'body' ).on( 'click', '.apilo-product-select-save', function(){
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
var apilo_product_id = $( '.apilo-product-select[product-id="' + product_id + '"]' ).val();
|
|
||||||
var apilo_product_name = $( '.apilo-product-select[product-id="' + product_id + '"] option:selected' ).text();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/apilo_product_select_save/',
|
|
||||||
data: {
|
|
||||||
product_id: product_id,
|
|
||||||
apilo_product_id: apilo_product_id,
|
|
||||||
apilo_product_name: apilo_product_name
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'ok' ) {
|
|
||||||
$( '.apilo-product-select[product-id="' + product_id + '"]' ).closest( '.apilo-found-products' ).remove();
|
|
||||||
$( 'span.apilo-product-search[product-id="' + product_id + '"]' ).html( '<span title="' + apilo_product_name + '">' + apilo_product_name.substr( 0, 25 ) + '...</span>' ).removeClass( 'apilo-product-search' ).removeClass( 'text-danger' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$( 'body' ).on( 'click', '.baselinker-product-select-save', function(){
|
|
||||||
var product_id = $( this ).attr( 'product-id' );
|
|
||||||
var baselinker_product_id = $( '.baselinker-product-select[product-id="' + product_id + '"]' ).val();
|
|
||||||
var baselinker_product_name = $( '.baselinker-product-select[product-id="' + product_id + '"] option:selected' ).text();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/baselinker_product_select_save/',
|
|
||||||
data: {
|
|
||||||
product_id: product_id,
|
|
||||||
baselinker_product_id: baselinker_product_id,
|
|
||||||
baselinker_product_name: baselinker_product_name
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'ok' ) {
|
|
||||||
$( '.baselinker-product-select[product-id="' + product_id + '"]' ).closest( '.baselinker-found-products' ).remove();
|
|
||||||
$( 'span.baselinker-product-search[product-id="' + product_id + '"]' ).html( '<span title="' + baselinker_product_name + '">' + baselinker_product_name.substr( 0, 25 ) + '...</span>' ).removeClass( 'baselinker-product-search' ).removeClass( 'text-danger' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function ajax_load_products( current_page ) {
|
|
||||||
var pagination_max = parseInt( $( '.pagination' ).attr( 'pagination_max' ) );
|
|
||||||
|
|
||||||
var query = '';
|
|
||||||
$( '.table-search' ).each(function(){
|
|
||||||
query += $( this ).attr( 'field_name' ) + '=' + $( this ).val() + '&';
|
|
||||||
});
|
|
||||||
|
|
||||||
current_page = parseInt( current_page );
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
cache: false,
|
|
||||||
url: '/admin/shop_product/ajax_load_products_archive/',
|
|
||||||
data: {
|
|
||||||
current_page: current_page,
|
|
||||||
query: query
|
|
||||||
},
|
|
||||||
beforeSend: function() {
|
|
||||||
$( '#overlay' ).show();
|
|
||||||
},
|
|
||||||
success: function( response ) {
|
|
||||||
$( '#overlay' ).hide();
|
|
||||||
|
|
||||||
data = jQuery.parseJSON( response );
|
|
||||||
|
|
||||||
if ( data.status == 'ok' ) {
|
|
||||||
$( '#table-products tbody' ).html( data.html );
|
|
||||||
$( '.pagination .previous' ).attr( 'page', ( current_page - 1 > 1 ) ? ( current_page - 1 ) : 1 );
|
|
||||||
$( '.pagination .next' ).attr( 'page', ( current_page + 1 < pagination_max ) ? ( current_page + 1 ) : pagination_max );
|
|
||||||
$( '.pagination span' ).html( current_page );
|
|
||||||
if ( data.pagination_max ) {
|
|
||||||
$( '.pagination' ).attr( 'pagination_max', data.pagination_max );
|
|
||||||
$( '.pagination #max_page' ).html( data.pagination_max );
|
|
||||||
$( '.pagination .last' ).attr( 'page', data.pagination_max );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
41
admin/templates/product_archive/products-list-table.php
Normal file
41
admin/templates/product_archive/products-list-table.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<? $i = ( $this -> current_page - 1 ) * 10 + 1;?>
|
||||||
|
<? foreach ( $this -> products as $product ):?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?= $i++;?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="product-image">
|
||||||
|
<? if ( $product['images'][0]['src'] ):?>
|
||||||
|
<img src="<?= $product['images'][0]['src'];?>" alt="<?= $product['images'][0]['alt'];?>" class="img-responsive">
|
||||||
|
<? else:?>
|
||||||
|
<img src="/admin/layout/images/no-image.png" alt="Brak zdjęcia" class="img-responsive">
|
||||||
|
<? endif;?>
|
||||||
|
</div>
|
||||||
|
<div class="product-name">
|
||||||
|
<a href="/admin/shop_product/product_edit/id=<?= $product['id'];?>">
|
||||||
|
<?= $product['languages']['pl']['name'];?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted"><?= \admin\factory\ShopProduct::product_categories( $product['id'] );?></small>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<?= $product['price_brutto'];?>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<?= $product['price_brutto_promo'];?>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="text-muted"><?= (int)\admin\factory\shopProduct::get_product_quantity_list( $product['id'] );?></span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href='/admin/shop_product/product_combination/product_id=<?= $product['id'];?>'>kombinacje (<?= \admin\factory\shopProduct::count_product_combinations( $product['id'] );?>)</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href='/admin/shop_product/product_edit/id=<?= $product['id'];?>'>edytuj</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href='/admin/product_archive/unarchive/product_id=<?= $product['id'];?>' class="product-unarchive">przywróć</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<? endforeach;?>
|
||||||
152
admin/templates/product_archive/products-list.php
Normal file
152
admin/templates/product_archive/products-list.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<div class="panel">
|
||||||
|
<div class="panel-body pn">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover table-striped mbn" id="table-products">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 10px;">#</th>
|
||||||
|
<th>Nazwa</th>
|
||||||
|
<th class="text-center" style="width: 100px;">Cena</th>
|
||||||
|
<th class="text-center" style="width: 100px;">Cena promocyjna</th>
|
||||||
|
<th class="text-center" style="width: 75px;">Stan MG</th>
|
||||||
|
<th class="text-center" style="width: 100px;">Kombinacje</th>
|
||||||
|
<th class="text-center" style="width: 75px;">Edytuj</th>
|
||||||
|
<th class="text-center" style="width: 75px;">Przywróć</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>
|
||||||
|
<input type="text" class="form-control table-search" field_name="name|ean|sku" placeholder="szukaj..." value="<?= htmlspecialchars( $this -> query_array['name'] ?? '', ENT_QUOTES, 'UTF-8');?>">
|
||||||
|
</th>
|
||||||
|
<th colspan="6"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="8">
|
||||||
|
<ul class="pagination" pagination_max="<?= $this -> pagination_max;?>">
|
||||||
|
<li>
|
||||||
|
<a href="#" page="1" title="pierwsza strona">
|
||||||
|
<i class="fa fa-angle-double-left"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="previous" page="<?= ( $this -> current_page - 1 > 1 ) ? ( $this -> current_page - 1 ) : 1;?>" title="poprzednia strona">
|
||||||
|
<i class="fa fa-angle-left" title="poprzednia strona"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
Strona <span id="current-page"><?= $this -> current_page;?></span> z <span id="max_page"><?= $this -> pagination_max;?></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="next" page="<?= ( $this -> current_page + 1 < $this -> pagination_max ) ? ( $this -> current_page + 1 ) : $this -> pagination_max;?>" title="następna strona">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" class="last" page="<?= $this -> pagination_max;?>" title="ostatnia strona">
|
||||||
|
<i class="fa fa-angle-double-right"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$( function() {
|
||||||
|
ajax_load_products( <?= $this -> current_page;?> );
|
||||||
|
|
||||||
|
$( 'body' ).on( 'change', '.table-search', function() {
|
||||||
|
ajax_load_products( 1 );
|
||||||
|
});
|
||||||
|
|
||||||
|
$( 'body' ).on( 'click', '.pagination a', function() {
|
||||||
|
var current_page = $( this ).attr( 'page' );
|
||||||
|
ajax_load_products( current_page );
|
||||||
|
});
|
||||||
|
|
||||||
|
$( 'body' ).on( 'click', '.product-unarchive', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var href = $( this ).attr( 'href' );
|
||||||
|
$.alert({
|
||||||
|
title: 'Pytanie',
|
||||||
|
content: 'Na pewno chcesz przywrócić wybrany produkt z archiwum?',
|
||||||
|
type: 'orange',
|
||||||
|
closeIcon: true,
|
||||||
|
closeIconClass: 'fa fa-times',
|
||||||
|
typeAnimated: true,
|
||||||
|
animation: 'opacity',
|
||||||
|
columnClass: 'col-12 col-lg-10',
|
||||||
|
theme: 'supervan',
|
||||||
|
icon: 'fa fa-question',
|
||||||
|
buttons: {
|
||||||
|
cancel: {
|
||||||
|
text: 'Nie',
|
||||||
|
btnClass: 'btn-success',
|
||||||
|
action: function() {}
|
||||||
|
},
|
||||||
|
confirm: {
|
||||||
|
text: 'Tak',
|
||||||
|
btnClass: 'btn-danger',
|
||||||
|
keys: ['enter'],
|
||||||
|
action: function() {
|
||||||
|
document.location.href = href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function ajax_load_products( current_page ) {
|
||||||
|
var pagination_max = parseInt( $( '.pagination' ).attr( 'pagination_max' ) );
|
||||||
|
|
||||||
|
var query = '';
|
||||||
|
$( '.table-search' ).each(function(){
|
||||||
|
query += $( this ).attr( 'field_name' ) + '=' + $( this ).val() + '&';
|
||||||
|
});
|
||||||
|
|
||||||
|
current_page = parseInt( current_page );
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
url: '/admin/shop_product/ajax_load_products_archive/',
|
||||||
|
data: {
|
||||||
|
current_page: current_page,
|
||||||
|
query: query
|
||||||
|
},
|
||||||
|
beforeSend: function() {
|
||||||
|
$( '#overlay' ).show();
|
||||||
|
},
|
||||||
|
success: function( response ) {
|
||||||
|
$( '#overlay' ).hide();
|
||||||
|
|
||||||
|
data = jQuery.parseJSON( response );
|
||||||
|
|
||||||
|
if ( data.status == 'ok' ) {
|
||||||
|
$( '#table-products tbody' ).html( data.html );
|
||||||
|
$( '.pagination .previous' ).attr( 'page', ( current_page - 1 > 1 ) ? ( current_page - 1 ) : 1 );
|
||||||
|
$( '.pagination .next' ).attr( 'page', ( current_page + 1 < pagination_max ) ? ( current_page + 1 ) : pagination_max );
|
||||||
|
$( '.pagination span' ).html( current_page );
|
||||||
|
if ( data.pagination_max ) {
|
||||||
|
$( '.pagination' ).attr( 'pagination_max', data.pagination_max );
|
||||||
|
$( '.pagination #max_page' ).html( data.pagination_max );
|
||||||
|
$( '.pagination .last' ).attr( 'page', data.pagination_max );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/archive/products_list/">
|
<a href="/admin/product_archive/products_list/">
|
||||||
<i class="fa fa-trash" aria-hidden="true"></i>Produkty
|
<i class="fa fa-trash" aria-hidden="true"></i>Produkty
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -102,4 +102,32 @@ class ProductRepository
|
|||||||
|
|
||||||
return $result !== false;
|
return $result !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Przywraca produkt z archiwum (wraz z kombinacjami)
|
||||||
|
*
|
||||||
|
* @param int $productId ID produktu
|
||||||
|
* @return bool Czy operacja się powiodła
|
||||||
|
*/
|
||||||
|
public function unarchive(int $productId): bool
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'id' => $productId ] );
|
||||||
|
$this->db->update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'parent_id' => $productId ] );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Przenosi produkt do archiwum (wraz z kombinacjami)
|
||||||
|
*
|
||||||
|
* @param int $productId ID produktu
|
||||||
|
* @return bool Czy operacja się powiodła
|
||||||
|
*/
|
||||||
|
public function archive(int $productId): bool
|
||||||
|
{
|
||||||
|
$this->db->update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'id' => $productId ] );
|
||||||
|
$this->db->update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'parent_id' => $productId ] );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
autoload/admin/Controllers/ProductArchiveController.php
Normal file
47
autoload/admin/Controllers/ProductArchiveController.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
namespace admin\Controllers;
|
||||||
|
|
||||||
|
use Domain\Product\ProductRepository;
|
||||||
|
|
||||||
|
class ProductArchiveController
|
||||||
|
{
|
||||||
|
private ProductRepository $productRepository;
|
||||||
|
|
||||||
|
public function __construct(ProductRepository $productRepository)
|
||||||
|
{
|
||||||
|
$this->productRepository = $productRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list(): string
|
||||||
|
{
|
||||||
|
$current_page = \S::get_session( 'archive_products_list_current_page' );
|
||||||
|
|
||||||
|
if ( !$current_page ) {
|
||||||
|
$current_page = 1;
|
||||||
|
\S::set_session( 'archive_products_list_current_page', $current_page );
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = \S::get_session( 'archive_products_list_query' );
|
||||||
|
$query_array = [];
|
||||||
|
if ( $query ) {
|
||||||
|
parse_str( $query, $query_array );
|
||||||
|
}
|
||||||
|
|
||||||
|
return \Tpl::view( 'product_archive/products-list', [
|
||||||
|
'current_page' => $current_page,
|
||||||
|
'query_array' => $query_array,
|
||||||
|
'pagination_max' => ceil( \admin\factory\ShopProduct::count_product( [ 'archive' => 1 ] ) / 10 )
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unarchive(): void
|
||||||
|
{
|
||||||
|
if ( $this->productRepository->unarchive( (int) \S::get( 'product_id' ) ) )
|
||||||
|
\S::alert( 'Produkt został przywrócony z archiwum.' );
|
||||||
|
else
|
||||||
|
\S::alert( 'Podczas przywracania produktu z archiwum wystąpił błąd. Proszę spróbować ponownie' );
|
||||||
|
|
||||||
|
header( 'Location: /admin/product_archive/products_list/' );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -211,6 +211,13 @@ class Site
|
|||||||
new \Domain\Settings\SettingsRepository()
|
new \Domain\Settings\SettingsRepository()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
'ProductArchive' => function() {
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
return new \admin\Controllers\ProductArchiveController(
|
||||||
|
new \Domain\Product\ProductRepository( $mdb )
|
||||||
|
);
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return self::$newControllers;
|
return self::$newControllers;
|
||||||
@@ -246,6 +253,7 @@ class Site
|
|||||||
'clear_cache' => 'clearCache',
|
'clear_cache' => 'clearCache',
|
||||||
'clear_cache_ajax' => 'clearCacheAjax',
|
'clear_cache_ajax' => 'clearCacheAjax',
|
||||||
'settings_save' => 'save',
|
'settings_save' => 'save',
|
||||||
|
'products_list' => 'list',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function route()
|
public static function route()
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<?
|
<?
|
||||||
namespace admin\controls;
|
namespace admin\controls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Użyj \admin\Controllers\ProductArchiveController
|
||||||
|
* Klasa zachowana jako fallback dla starego URL /admin/archive/products_list/
|
||||||
|
*/
|
||||||
class Archive {
|
class Archive {
|
||||||
static public function products_list() {
|
static public function products_list() {
|
||||||
|
|
||||||
@@ -16,7 +21,7 @@ class Archive {
|
|||||||
parse_str( $query, $query_array );
|
parse_str( $query, $query_array );
|
||||||
}
|
}
|
||||||
|
|
||||||
return \Tpl::view( 'archive/products-list', [
|
return \Tpl::view( 'product_archive/products-list', [
|
||||||
'current_page' => $current_page,
|
'current_page' => $current_page,
|
||||||
'query_array' => $query_array,
|
'query_array' => $query_array,
|
||||||
'pagination_max' => ceil( \admin\factory\ShopProduct::count_product() / 10 )
|
'pagination_max' => ceil( \admin\factory\ShopProduct::count_product() / 10 )
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ class ShopProduct
|
|||||||
else
|
else
|
||||||
\S::alert( 'Podczas przywracania produktu z archiwum wystąpił błąd. Proszę spróbować ponownie' );
|
\S::alert( 'Podczas przywracania produktu z archiwum wystąpił błąd. Proszę spróbować ponownie' );
|
||||||
|
|
||||||
header( 'Location: /admin/archive/products_list/' );
|
header( 'Location: /admin/product_archive/products_list/' );
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ class ShopProduct
|
|||||||
$response = [
|
$response = [
|
||||||
'status' => 'ok',
|
'status' => 'ok',
|
||||||
'pagination_max' => ceil( $products['products_count'] / 10 ),
|
'pagination_max' => ceil( $products['products_count'] / 10 ),
|
||||||
'html' => \Tpl::view( 'archive/products-list-table', [
|
'html' => \Tpl::view( 'product_archive/products-list-table', [
|
||||||
'products' => $products['products'],
|
'products' => $products['products'],
|
||||||
'current_page' => \S::get( 'current_page' ),
|
'current_page' => \S::get( 'current_page' ),
|
||||||
] )
|
] )
|
||||||
|
|||||||
@@ -482,34 +482,32 @@ class ShopProduct
|
|||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
|
$search = '';
|
||||||
|
|
||||||
if ( $query )
|
if ( $query )
|
||||||
{
|
{
|
||||||
$search = '';
|
|
||||||
$query_array = [];
|
$query_array = [];
|
||||||
|
|
||||||
parse_str( $query, $query_array );
|
parse_str( $query, $query_array );
|
||||||
|
|
||||||
foreach ( $query_array as $key => $val ) {
|
foreach ( $query_array as $key => $val ) {
|
||||||
$search .= ' AND ' . $key . ' LIKE \'%' . $val . '%\'';
|
if ( $val !== '' )
|
||||||
|
$search .= ' AND ' . $key . ' LIKE \'%' . $val . '%\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $mdb -> query( 'SELECT '
|
|
||||||
. 'DISTINCT( psp.id )'
|
|
||||||
. 'FROM '
|
|
||||||
. 'pp_shop_products AS psp '
|
|
||||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
|
|
||||||
. 'WHERE archive = 1 AND parent_id IS NULL ' . $search . ' ORDER BY id DESC LIMIT ' . ( $current_page - 1 ) * 10 . ', 10' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
||||||
$results2 = $mdb -> query( 'SELECT '
|
|
||||||
. 'COUNT( DISTINCT( psp.id ) ) AS products_count '
|
|
||||||
. 'FROM '
|
|
||||||
. 'pp_shop_products AS psp '
|
|
||||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
|
|
||||||
. 'WHERE archive = 1 AND parent_id IS NULL ' . $search ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
||||||
} else {
|
|
||||||
$results = $mdb -> query( 'SELECT id FROM pp_shop_products WHERE parent_id IS NULL ORDER BY id DESC LIMIT ' . ( $current_page - 1 ) * 10 . ', 10' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
||||||
$results2 = $mdb -> query( 'SELECT COUNT( id ) AS products_count FROM pp_shop_products WHERE parent_id IS NULL' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$results = $mdb -> query( 'SELECT '
|
||||||
|
. 'DISTINCT( psp.id )'
|
||||||
|
. 'FROM '
|
||||||
|
. 'pp_shop_products AS psp '
|
||||||
|
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
|
||||||
|
. 'WHERE archive = 1 AND parent_id IS NULL ' . $search . ' ORDER BY id DESC LIMIT ' . ( $current_page - 1 ) * 10 . ', 10' ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||||
|
$results2 = $mdb -> query( 'SELECT '
|
||||||
|
. 'COUNT( DISTINCT( psp.id ) ) AS products_count '
|
||||||
|
. 'FROM '
|
||||||
|
. 'pp_shop_products AS psp '
|
||||||
|
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
|
||||||
|
. 'WHERE archive = 1 AND parent_id IS NULL ' . $search ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||||
|
|
||||||
if ( is_array( $results ) ) foreach ( $results as $row ) {
|
if ( is_array( $results ) ) foreach ( $results as $row ) {
|
||||||
$products[] = \admin\factory\ShopProduct::product_details( $row['id'] );
|
$products[] = \admin\factory\ShopProduct::product_details( $row['id'] );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,4 +259,92 @@ class ProductRepositoryTest extends TestCase
|
|||||||
$this->assertIsInt($quantity); // Sprawdzamy czy konwersja na int zadziałała
|
$this->assertIsInt($quantity); // Sprawdzamy czy konwersja na int zadziałała
|
||||||
$this->assertEquals(25, $quantity);
|
$this->assertEquals(25, $quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test przywracania produktu z archiwum
|
||||||
|
*/
|
||||||
|
public function testUnarchiveUpdatesProductAndChildren()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|
||||||
|
$mockDb->expects($this->exactly(2))
|
||||||
|
->method('update')
|
||||||
|
->withConsecutive(
|
||||||
|
[
|
||||||
|
$this->equalTo('pp_shop_products'),
|
||||||
|
$this->equalTo(['status' => 1, 'archive' => 0]),
|
||||||
|
$this->equalTo(['id' => 123])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$this->equalTo('pp_shop_products'),
|
||||||
|
$this->equalTo(['status' => 1, 'archive' => 0]),
|
||||||
|
$this->equalTo(['parent_id' => 123])
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$repository = new ProductRepository($mockDb);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$result = $repository->unarchive(123);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test przenoszenia produktu do archiwum
|
||||||
|
*/
|
||||||
|
public function testArchiveUpdatesProductAndChildren()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
|
||||||
|
$mockDb->expects($this->exactly(2))
|
||||||
|
->method('update')
|
||||||
|
->withConsecutive(
|
||||||
|
[
|
||||||
|
$this->equalTo('pp_shop_products'),
|
||||||
|
$this->equalTo(['status' => 0, 'archive' => 1]),
|
||||||
|
$this->equalTo(['id' => 456])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$this->equalTo('pp_shop_products'),
|
||||||
|
$this->equalTo(['status' => 0, 'archive' => 1]),
|
||||||
|
$this->equalTo(['parent_id' => 456])
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$repository = new ProductRepository($mockDb);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
$result = $repository->archive(456);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
$this->assertTrue($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test że unarchive zwraca bool
|
||||||
|
*/
|
||||||
|
public function testUnarchiveReturnsBool()
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$repository = new ProductRepository($mockDb);
|
||||||
|
|
||||||
|
$result = $repository->unarchive(1);
|
||||||
|
$this->assertIsBool($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test że archive zwraca bool
|
||||||
|
*/
|
||||||
|
public function testArchiveReturnsBool()
|
||||||
|
{
|
||||||
|
$mockDb = $this->createMock(\medoo::class);
|
||||||
|
$repository = new ProductRepository($mockDb);
|
||||||
|
|
||||||
|
$result = $repository->archive(1);
|
||||||
|
$this->assertIsBool($result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
namespace Tests\Unit\admin\Controllers;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use admin\Controllers\ProductArchiveController;
|
||||||
|
use Domain\Product\ProductRepository;
|
||||||
|
|
||||||
|
class ProductArchiveControllerTest extends TestCase
|
||||||
|
{
|
||||||
|
private $mockRepository;
|
||||||
|
private $controller;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->mockRepository = $this->createMock(ProductRepository::class);
|
||||||
|
$this->controller = new ProductArchiveController($this->mockRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorAcceptsRepository(): void
|
||||||
|
{
|
||||||
|
$controller = new ProductArchiveController($this->mockRepository);
|
||||||
|
$this->assertInstanceOf(ProductArchiveController::class, $controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasListMethod(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(method_exists($this->controller, 'list'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasUnarchiveMethod(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(method_exists($this->controller, 'unarchive'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testListMethodReturnType(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
|
$this->assertEquals('string', (string)$reflection->getMethod('list')->getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnarchiveMethodReturnType(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass($this->controller);
|
||||||
|
$this->assertEquals('void', (string)$reflection->getMethod('unarchive')->getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorRequiresProductRepository(): void
|
||||||
|
{
|
||||||
|
$reflection = new \ReflectionClass(ProductArchiveController::class);
|
||||||
|
$constructor = $reflection->getConstructor();
|
||||||
|
$params = $constructor->getParameters();
|
||||||
|
|
||||||
|
$this->assertCount(1, $params);
|
||||||
|
$this->assertEquals('Domain\Product\ProductRepository', $params[0]->getType()->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
updates/0.20/ver_0.241.zip
Normal file
BIN
updates/0.20/ver_0.241.zip
Normal file
Binary file not shown.
@@ -1,3 +1,10 @@
|
|||||||
|
<b>ver. 0.241</b><br />
|
||||||
|
- NEW - refaktoryzacja: admin\Controllers\ProductArchiveController - archiwum produktów z DI
|
||||||
|
- NEW - ProductRepository::archive(), unarchive() - operacje archiwizacji w repozytorium
|
||||||
|
- FIX - naprawiono SQL w liście archiwum (puste wyszukiwanie filtrowało wszystkie wyniki)
|
||||||
|
- FIX - naprawiono brakujący filtr archive = 1 w zapytaniu bez wyszukiwania
|
||||||
|
- UPDATE - wyczyszczono szablony archiwum (usunięto zbędne funkcje: apilo, baselinker, duplikowanie)
|
||||||
|
<hr>
|
||||||
<b>ver. 0.240</b><br />
|
<b>ver. 0.240</b><br />
|
||||||
- NEW - refaktoryzacja: Domain\Settings\SettingsRepository + admin\Controllers\SettingsController (architektura Domain-Driven)
|
- NEW - refaktoryzacja: Domain\Settings\SettingsRepository + admin\Controllers\SettingsController (architektura Domain-Driven)
|
||||||
- NEW - refaktoryzacja: Domain\Cache\CacheRepository - czyszczenie cache z obsługą Redis
|
- NEW - refaktoryzacja: Domain\Cache\CacheRepository - czyszczenie cache z obsługą Redis
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?
|
<?
|
||||||
$current_ver = 240;
|
$current_ver = 241;
|
||||||
|
|
||||||
for ($i = 1; $i <= $current_ver; $i++)
|
for ($i = 1; $i <= $current_ver; $i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user