feat(11-12-accounting): phases 11-12 complete — milestone v0.3 done
Phase 11: Receipt preview, print & PDF via dompdf. Phase 12: Accounting section with receipt list, filters, pagination, selectable checkboxes and XLSX export via PhpSpreadsheet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
300
.paul/phases/12-accounting-list/12-01-PLAN.md
Normal file
300
.paul/phases/12-accounting-list/12-01-PLAN.md
Normal file
@@ -0,0 +1,300 @@
|
||||
---
|
||||
phase: 12-accounting-list
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["11-01"]
|
||||
files_modified:
|
||||
- src/Modules/Accounting/AccountingController.php
|
||||
- src/Modules/Accounting/ReceiptRepository.php
|
||||
- resources/views/accounting/index.php
|
||||
- resources/views/layouts/app.php
|
||||
- routes/web.php
|
||||
- resources/lang/pl.php
|
||||
- resources/scss/app.scss
|
||||
- public/assets/css/app.css
|
||||
- composer.json
|
||||
- DOCS/ARCHITECTURE.md
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Sekcja Ksiegowosc w nawigacji glownej z lista wszystkich paragonow, filtrami, paginacja i eksportem do XLSX.
|
||||
|
||||
## Purpose
|
||||
Dotychczas paragony widoczne sa tylko w kontekscie pojedynczego zamowienia. Uzytkownik potrzebuje przegladac wszystkie paragony w jednym miejscu — filtrowac po dacie, konfiguracji, numerze — i eksportowac do XLSX dla celów ksiegowych.
|
||||
|
||||
## Output
|
||||
- Nowy AccountingController z metoda index() i export()
|
||||
- Widok listy paragonow z filtrami (reuse komponentu table-list)
|
||||
- Eksport XLSX przez PhpSpreadsheet
|
||||
- Nowy link "Ksiegowosc" w nawigacji glownej (sidebar)
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/11-receipt-print/11-01-SUMMARY.md — podglad, druk, PDF paragonu
|
||||
@.paul/phases/10-receipt-issue/10-01-SUMMARY.md — wystawianie paragonow, ReceiptRepository
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Accounting/ReceiptRepository.php
|
||||
@src/Modules/Accounting/ReceiptController.php
|
||||
@resources/views/components/table-list.php
|
||||
@resources/views/orders/list.php — wzorzec listy z filtrami
|
||||
@src/Modules/Orders/OrdersController.php — wzorzec metody index() z tableList
|
||||
@resources/views/layouts/app.php — sidebar navigation
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills (from SPECIAL-FLOWS.md)
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] sonar-scanner uruchomiony po APPLY
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Link Ksiegowosc w nawigacji glownej
|
||||
```gherkin
|
||||
Given uzytkownik jest zalogowany
|
||||
When widzi sidebar nawigacji
|
||||
Then widoczny jest nowy link "Ksiegowosc" prowadzacy do /accounting
|
||||
And link jest aktywny gdy uzytkownik jest na stronie /accounting
|
||||
```
|
||||
|
||||
## AC-2: Lista paragonow z paginacja
|
||||
```gherkin
|
||||
Given istnieja paragony w bazie danych
|
||||
When uzytkownik otwiera /accounting
|
||||
Then wyswietlana jest tabela paragonow z kolumnami:
|
||||
- Numer paragonu, Data wystawienia, Data sprzedazy, Kwota brutto, Konfiguracja, Zamowienie
|
||||
And tabela jest paginowana (domyslnie 20 na strone)
|
||||
And mozna sortowac po: numer, data wystawienia, kwota brutto
|
||||
```
|
||||
|
||||
## AC-3: Filtry listy
|
||||
```gherkin
|
||||
Given uzytkownik jest na stronie /accounting
|
||||
When uzywa filtrow
|
||||
Then moze filtrowac po:
|
||||
- Szukaj (numer paragonu, numer zamowienia)
|
||||
- Konfiguracja (select z aktywnych konfiguracji)
|
||||
- Data wystawienia od / do
|
||||
And filtry sa zachowane w URL (query string)
|
||||
```
|
||||
|
||||
## AC-4: Eksport XLSX
|
||||
```gherkin
|
||||
Given uzytkownik jest na stronie /accounting z zastosowanymi filtrami
|
||||
When klika przycisk "Eksportuj XLSX"
|
||||
Then przeglądarka pobiera plik .xlsx z lista paragonow
|
||||
And plik zawiera te same kolumny co tabela + dodatkowe: data sprzedazy, nr referencyjny
|
||||
And eksport uwzglednia aktywne filtry (nie eksportuje wszystkiego)
|
||||
And plik ma nazwe: paragony_YYYY-MM-DD.xlsx
|
||||
```
|
||||
|
||||
## AC-5: Pusta lista
|
||||
```gherkin
|
||||
Given brak paragonow spelniajacych kryteria (pusta baza lub filtr bez wynikow)
|
||||
When uzytkownik otwiera /accounting
|
||||
Then wyswietlany jest komunikat "Brak paragonow"
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: ReceiptRepository — metoda paginate() i exportData()</name>
|
||||
<files>
|
||||
src/Modules/Accounting/ReceiptRepository.php
|
||||
</files>
|
||||
<action>
|
||||
1. **paginate(array $filters): array** — analogicznie do OrdersRepository::paginate()
|
||||
- Przyjmuje filtry: search, config_id, date_from, date_to, sort, sort_dir, page, per_page
|
||||
- SELECT r.*, rc.name AS config_name, o.internal_order_number, o.external_order_id
|
||||
FROM receipts r
|
||||
LEFT JOIN receipt_configs rc ON rc.id = r.config_id
|
||||
LEFT JOIN orders o ON o.id = r.order_id
|
||||
- WHERE dynamiczne na podstawie filtrow:
|
||||
- search: LIKE na receipt_number i o.internal_order_number i o.external_order_id
|
||||
- config_id: = config_id
|
||||
- date_from: issue_date >= date_from
|
||||
- date_to: issue_date <= date_to
|
||||
- ORDER BY dynamiczne (whitelist: receipt_number, issue_date, total_gross) + sort_dir (ASC/DESC)
|
||||
- LIMIT/OFFSET na podstawie page i per_page
|
||||
- Zwraca: ['items' => [...], 'total' => int, 'page' => int, 'per_page' => int]
|
||||
- Uzyj COUNT(*) osobnym zapytaniem dla total
|
||||
|
||||
2. **exportData(array $filters): array** — ta sama logika WHERE co paginate, ale BEZ LIMIT/OFFSET
|
||||
- Zwraca flat array wierszy z tymi samymi kolumnami
|
||||
- Uzywany przez export XLSX
|
||||
|
||||
Wzorzec: prepared statements, whitelist dla sort, max per_page = 100.
|
||||
</action>
|
||||
<verify>
|
||||
- `php -l src/Modules/Accounting/ReceiptRepository.php`
|
||||
</verify>
|
||||
<done>AC-2 backend, AC-3 backend, AC-4 backend</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: AccountingController + trasy + nawigacja + widok</name>
|
||||
<files>
|
||||
src/Modules/Accounting/AccountingController.php,
|
||||
routes/web.php,
|
||||
resources/views/accounting/index.php,
|
||||
resources/views/layouts/app.php,
|
||||
resources/lang/pl.php,
|
||||
composer.json
|
||||
</files>
|
||||
<action>
|
||||
1. **Instalacja PhpSpreadsheet:**
|
||||
- `php composer.phar require phpoffice/phpspreadsheet --ignore-platform-reqs`
|
||||
|
||||
2. **AccountingController** (nowy plik w src/Modules/Accounting/):
|
||||
- Konstruktor: Template, Translator, AuthService, ReceiptRepository, ReceiptConfigRepository
|
||||
- **index(Request): Response** — GET /accounting
|
||||
- Parsuj filtry z request (search, config_id, date_from, date_to, sort, sort_dir, page, per_page)
|
||||
- Wywolaj ReceiptRepository::paginate($filters)
|
||||
- Pobierz liste aktywnych konfiguracji (ReceiptConfigRepository::listAll() filtruj is_active)
|
||||
- Mapuj wiersze do formatu tableList (analogicznie do OrdersController)
|
||||
- Renderuj widok accounting/index z tableList data
|
||||
- **export(Request): Response** — GET /accounting/export
|
||||
- Te same filtry co index
|
||||
- Wywolaj ReceiptRepository::exportData($filters)
|
||||
- Uzyj PhpSpreadsheet: nowy Spreadsheet, ustaw naglowki, wypelnij wiersze
|
||||
- Kolumny: Numer, Data wystawienia, Data sprzedazy, Kwota brutto, Konfiguracja, Nr zamowienia, Nr referencyjny
|
||||
- Zwroc Response z Content-Type xlsx i Content-Disposition attachment
|
||||
- Nazwa pliku: paragony_YYYY-MM-DD.xlsx
|
||||
- UWAGA: PhpSpreadsheet zapisuje do php://output — uzyj ob_start/ob_get_clean
|
||||
|
||||
3. **routes/web.php:**
|
||||
- Dodaj instancje AccountingController (reuse receiptRepository, receiptConfigRepository)
|
||||
- `GET /accounting` → [$accountingController, 'index'], [$authMiddleware]
|
||||
- `GET /accounting/export` → [$accountingController, 'export'], [$authMiddleware]
|
||||
- Dodaj PRZED trasami receipt (logicznie: sekcja accounting)
|
||||
|
||||
4. **resources/views/accounting/index.php:**
|
||||
- Uzyj komponentu table-list (include components/table-list.php z $tableList)
|
||||
- Dodaj przycisk "Eksportuj XLSX" w naglowku (link do /accounting/export z aktualnymi filtrami w query string)
|
||||
- Wzorzec: analogicznie do orders/list.php
|
||||
|
||||
5. **resources/views/layouts/app.php:**
|
||||
- Dodaj nowa sekcje w sidebar PRZED "Ustawienia":
|
||||
```php
|
||||
<a class="sidebar__link<?= $currentMenu === 'accounting' ? ' is-active' : '' ?>" href="/accounting">
|
||||
<svg>...</svg> <?= $e($t('navigation.accounting_section')) ?>
|
||||
</a>
|
||||
```
|
||||
- Uzyj ikony dokumentu/receipt (inline SVG)
|
||||
|
||||
6. **resources/lang/pl.php:**
|
||||
- Dodaj klucze: `navigation.accounting_section` => 'Ksiegowosc'
|
||||
- `accounting.title`, `accounting.export`, `accounting.empty`
|
||||
- `accounting.filters.*`: search, config, date_from, date_to, any
|
||||
- `accounting.columns.*`: number, issue_date, sale_date, total_gross, config, order
|
||||
|
||||
Wzorzec: Reuse komponentu table-list.php, analogia do OrdersController::index().
|
||||
</action>
|
||||
<verify>
|
||||
- `php -l src/Modules/Accounting/AccountingController.php`
|
||||
- `php -l routes/web.php`
|
||||
- `php -l resources/views/accounting/index.php`
|
||||
- `php -l resources/views/layouts/app.php`
|
||||
- `composer show phpoffice/phpspreadsheet` — zainstalowany
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3, AC-4, AC-5 spelnione</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: SCSS build + aktualizacja dokumentacji</name>
|
||||
<files>
|
||||
resources/scss/app.scss,
|
||||
public/assets/css/app.css,
|
||||
DOCS/ARCHITECTURE.md
|
||||
</files>
|
||||
<action>
|
||||
1. Build SCSS do CSS (jesli nowe klasy dodane)
|
||||
2. Zaktualizuj DOCS/ARCHITECTURE.md:
|
||||
- Dodaj AccountingController (index, export) do sekcji Modules/Accounting
|
||||
- Dodaj ReceiptRepository::paginate(), exportData()
|
||||
- Dodaj trasy /accounting i /accounting/export
|
||||
</action>
|
||||
<verify>
|
||||
- `npx sass --style=compressed --no-source-map resources/scss/app.scss public/assets/css/app.css`
|
||||
- Brak bledow
|
||||
</verify>
|
||||
<done>Dokumentacja zaktualizowana, CSS zbudowany</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Sekcja Ksiegowosc: lista paragonow z filtrami, paginacja, eksport XLSX, link w nawigacji</what-built>
|
||||
<how-to-verify>
|
||||
1. Otworz aplikacje → sidebar → kliknij "Ksiegowosc"
|
||||
2. Sprawdz: tabela paragonow wyswietla sie z kolumnami
|
||||
3. Uzyj filtrow: szukaj po numerze, wybierz konfiguracje, ustaw daty
|
||||
4. Sprawdz paginacje: zmien strone, zmien ilosc na strone
|
||||
5. Kliknij "Eksportuj XLSX" — plik pobiera sie
|
||||
6. Otworz XLSX — dane poprawne, filtry uwzglednione
|
||||
7. Sprawdz czy link do zamowienia w tabeli dziala
|
||||
8. Sprawdz pusta liste (filtr bez wynikow) — komunikat "Brak paragonow"
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- database/migrations/* (schemat zablokowany)
|
||||
- src/Modules/Settings/ReceiptConfigRepository.php (gotowe z fazy 09)
|
||||
- src/Modules/Accounting/ReceiptController.php (gotowe z fazy 11 — tylko odczyt)
|
||||
- resources/views/receipts/* (gotowe z fazy 11)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Brak edycji/anulowania paragonu — poza zakresem v0.3
|
||||
- Brak zaawansowanych raportow (sumy per konfiguracja, wykresy) — future
|
||||
- Brak eksportu CSV — tylko XLSX
|
||||
- Brak filtrowania po nabywcy — uproszczenie v0.3
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `composer show phpoffice/phpspreadsheet` — zainstalowany
|
||||
- [ ] `php -l src/Modules/Accounting/AccountingController.php`
|
||||
- [ ] `php -l src/Modules/Accounting/ReceiptRepository.php`
|
||||
- [ ] `php -l resources/views/accounting/index.php`
|
||||
- [ ] `php -l routes/web.php`
|
||||
- [ ] Link "Ksiegowosc" widoczny w sidebar nawigacji
|
||||
- [ ] Lista paragonow wyswietla sie z paginacja
|
||||
- [ ] Filtry dzialaja (search, config, date_from, date_to)
|
||||
- [ ] Sortowanie dziala (numer, data, kwota)
|
||||
- [ ] Eksport XLSX pobiera sie i zawiera poprawne dane
|
||||
- [ ] Pusta lista wyswietla komunikat
|
||||
- [ ] All acceptance criteria met
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 3 taski auto + 1 checkpoint ukonczone
|
||||
- Wszystkie AC-1 do AC-5 spelnione
|
||||
- PhpSpreadsheet zainstalowany i dzialajacy
|
||||
- Brak bledow PHP
|
||||
- Weryfikacja manualna przez uzytkownika (checkpoint)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/12-accounting-list/12-01-SUMMARY.md`
|
||||
</output>
|
||||
143
.paul/phases/12-accounting-list/12-01-SUMMARY.md
Normal file
143
.paul/phases/12-accounting-list/12-01-SUMMARY.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
phase: 12-accounting-list
|
||||
plan: 01
|
||||
subsystem: accounting
|
||||
tags: [phpspreadsheet, xlsx, export, list, pagination, filters, selectable]
|
||||
|
||||
requires:
|
||||
- phase: 11-receipt-print
|
||||
provides: ReceiptController show/print/pdf, receipt views
|
||||
- phase: 10-receipt-issue
|
||||
provides: ReceiptRepository, receipts table, snapshots
|
||||
provides:
|
||||
- Accounting section in main navigation
|
||||
- Receipt list with filters, pagination, sorting
|
||||
- XLSX export (selected or all from filter)
|
||||
- Selectable rows with checkboxes
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: [phpoffice/phpspreadsheet ^5.5]
|
||||
patterns: [selectable table-list with POST export, findByIds batch query]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- src/Modules/Accounting/AccountingController.php
|
||||
- resources/views/accounting/index.php
|
||||
modified:
|
||||
- src/Modules/Accounting/ReceiptRepository.php
|
||||
- routes/web.php
|
||||
- resources/views/layouts/app.php
|
||||
- resources/lang/pl.php
|
||||
- composer.json
|
||||
- DOCS/ARCHITECTURE.md
|
||||
|
||||
key-decisions:
|
||||
- "PhpSpreadsheet v5.5 for XLSX export (not CSV)"
|
||||
- "POST export with CSRF instead of GET (selected IDs via form)"
|
||||
- "Selectable table-list reuse — built-in component feature enabled"
|
||||
- "Two export modes: selected IDs or all matching current filter"
|
||||
|
||||
patterns-established:
|
||||
- "Selectable table-list pattern: selectable: true + JS form submit"
|
||||
- "findByIds() batch query pattern for selected records"
|
||||
|
||||
duration: ~40min
|
||||
completed: 2026-03-15
|
||||
---
|
||||
|
||||
# Phase 12 Plan 01: Accounting Section — Receipt List & XLSX Export Summary
|
||||
|
||||
**Sekcja Księgowość w nawigacji głównej z listą paragonów, filtrami, paginacją, zaznaczaniem i eksportem XLSX.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~40min |
|
||||
| Completed | 2026-03-15 |
|
||||
| Tasks | 4 completed (3 auto + 1 checkpoint) |
|
||||
| Files modified | 10 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Link Księgowość w nawigacji głównej | Pass | Osobny link w sidebar z ikoną dokumentu, przed Ustawienia |
|
||||
| AC-2: Lista paragonów z paginacją | Pass | Reuse table-list, sortowanie po numerze/dacie/kwocie |
|
||||
| AC-3: Filtry listy | Pass | Szukaj, konfiguracja (select), data od/do — zachowane w URL |
|
||||
| AC-4: Eksport XLSX | Pass | Dwa tryby: zaznaczone (POST + selected_ids) i wszystkie z filtra |
|
||||
| AC-5: Pusta lista | Pass | Komunikat "Brak paragonów" |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- AccountingController z index() i export() — pełna sekcja księgowości
|
||||
- ReceiptRepository: paginate(), exportData(), findByIds() — 3 nowe metody
|
||||
- Zaznaczanie paragonów checkboxami (reuse wbudowanego selectable w table-list)
|
||||
- Eksport XLSX z PhpSpreadsheet: zaznaczone lub wszystkie z filtra
|
||||
- Link "Księgowość" w sidebar nawigacji
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Accounting/AccountingController.php` | Created | index() lista + export() XLSX |
|
||||
| `src/Modules/Accounting/ReceiptRepository.php` | Modified | +paginate(), +exportData(), +findByIds() |
|
||||
| `routes/web.php` | Modified | +AccountingController, GET /accounting, POST /accounting/export |
|
||||
| `resources/views/accounting/index.php` | Created | Lista z przyciskami eksportu + JS selection |
|
||||
| `resources/views/layouts/app.php` | Modified | +link Księgowość w sidebar |
|
||||
| `resources/lang/pl.php` | Modified | +accounting.*, +navigation.accounting_section |
|
||||
| `composer.json` | Modified | +phpoffice/phpspreadsheet ^5.5 |
|
||||
| `public/assets/css/app.css` | Modified | Rebuild |
|
||||
| `DOCS/ARCHITECTURE.md` | Modified | +AccountingController, +trasy, +metody repo |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| PhpSpreadsheet (nie CSV) | XLSX lepszy dla księgowości — formatowanie, polskie znaki | Nowa zależność composer |
|
||||
| POST eksport z CSRF | Wysyłanie selected_ids[] wymaga POST; bezpieczeństwo | Formularz ukryty + JS submit |
|
||||
| Dwa tryby eksportu | User request: zaznaczanie + eksport wszystkich z filtra | findByIds() + exportData() |
|
||||
| Selectable table-list reuse | Komponent już ma wbudowane checkboxy | Zero nowego kodu w komponencie |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 1 | Zaznaczanie paragonów (user request during checkpoint) |
|
||||
| Auto-fixed | 0 | — |
|
||||
|
||||
**Total impact:** Pozytywne rozszerzenie — zaznaczanie paragonów do eksportu na prośbę użytkownika.
|
||||
|
||||
### Scope Additions
|
||||
|
||||
**1. Zaznaczanie paragonów do eksportu**
|
||||
- **Requested during:** Checkpoint (Task 4)
|
||||
- **What:** Checkboxy, "Eksportuj zaznaczone", "Eksportuj wszystkie (filtr)"
|
||||
- **Implementation:** selectable: true w table-list, POST export, findByIds(), JS form
|
||||
- **Impact:** Lepsza kontrola użytkownika nad eksportem
|
||||
|
||||
### Skill Audit
|
||||
|
||||
| Expected | Invoked | Notes |
|
||||
|----------|---------|-------|
|
||||
| sonar-scanner | ○ | Required — do uruchomienia przed kolejnym UNIFY |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Milestone v0.3 Moduł Paragonów COMPLETE
|
||||
- Pełny cykl: konfiguracja → wystawienie → podgląd → druk → PDF → lista → eksport
|
||||
|
||||
**Concerns:**
|
||||
- sonar-scanner nie uruchomiony (gap z wielu faz)
|
||||
- vendor/ musi być uploadowany ręcznie na serwer po każdej zmianie zależności
|
||||
|
||||
**Blockers:**
|
||||
- Brak
|
||||
|
||||
---
|
||||
*Phase: 12-accounting-list, Plan: 01*
|
||||
*Completed: 2026-03-15*
|
||||
Reference in New Issue
Block a user