83 lines
5.7 KiB
Markdown
83 lines
5.7 KiB
Markdown
---
|
|
phase: 05-finances-fakturownia-import
|
|
plan: 04
|
|
status: complete
|
|
completed_at: 2026-04-14
|
|
---
|
|
|
|
# Plan 05-04 Summary — Bugfix importu faktur kosztowych z Fakturowni
|
|
|
|
## Goal (recap)
|
|
Naprawic import faktur kosztowych, ktore nie byly pobierane (zgloszony przyklad: faktura 486639934 = FV 1417/04/2026 z KSeF).
|
|
|
|
## Root cause (2 bugi)
|
|
|
|
**Bug A — `FakturowniaApiClient::fetchCostDocuments`:**
|
|
Kod probowal kolejno `/costs.json`, `/expenses.json`, `/invoices.json?income=no` i zwracal wynik pierwszej sciezki, ktora odpowiedziala HTTP 200. Problem: `/costs.json` na tym koncie Fakturowni zwracal HTTP 200 z PUSTA lista, wiec logika uznawala "done, zwroc puste" i nigdy nie trafiala do `/invoices.json?income=no`, gdzie lezala faktura 486639934.
|
|
|
|
**Bug B — quirk Fakturowni dla wydatkow z KSeF:**
|
|
Dla faktur pobranych z KSeF (`gov_kind='ksef'`, `approval_status='received'`) Fakturownia zapisuje dane wlasnej firmy w polach `seller_*`, a prawdziwego dostawce w `buyer_*` — odwrotnie niz dla klasycznych faktur kosztowych z innych zrodel. Importer dla `documentType=cost` bral `seller_tax_no` jako klucz klienta → trafialo na wlasny NIP → faktura szla do `fakturownia_unmapped_queue` lub byla mapowana na wlasna firme.
|
|
|
|
**Bonus (drugi raunde): hang crona.**
|
|
Po naprawie Bug A, ale przed dodaniem filtra dat po stronie API, usuniecie wczesnego przerywania paginacji powodowalo pobieranie calej historii Fakturowni (100 stron x 100 dok x 3 endpointy = potencjalnie 30k requestow). Cron wisial. Rozwiazanie: `period=more&date_from=startDate&date_to=today` — filtr po stronie API, paginacja konczy sie naturalnie po wyczerpaniu zakresu dat.
|
|
|
|
## Changes
|
|
|
|
### autoload/Domain/Finances/FakturowniaApiClient.php
|
|
- `fetchCostDocuments` przebudowane: odpytuje wszystkie 3 endpointy i merge'uje wyniki z dedup po `id`. Pusta odpowiedz z jednego endpointu NIE skraca wyszukiwania.
|
|
- `fetchSalesDocuments` + `fetchCostDocuments` uzywaja nowej metody `buildDateRangeQuery` → `period=more&date_from=X&date_to=today`.
|
|
- Usunieta dead-code metoda `canUseCurrentMonthPeriod`.
|
|
- `request()` zmieniona z `private` na `protected` — umozliwia subklasowanie w testach.
|
|
|
|
### autoload/Domain/Finances/FakturowniaInvoiceImporter.php
|
|
- `processDocumentType`: usuniete wczesne przerywanie paginacji na podstawie dat w liscie (API moze sortowac po updated_at, nie issue_date). Zostawiony twardy cap 100 stron jako safety.
|
|
- Usunieta dead-code metoda `isDateRelevantForImport`.
|
|
- `normalizeDocument` dla `documentType='cost'`: gdy `seller_tax_no == FAKTUROWNIA_OWN_TAX_NO` (z `.env`), kontrahent jest pobierany z `buyer_*` zamiast `seller_*` (obsluga odwroconych rol KSeF).
|
|
- Nowa metoda `taxNoEqualsOwn($taxNo)` porownujaca NIP po normalizacji.
|
|
- `resolveConfig` czyta nowy klucz `FAKTUROWNIA_OWN_TAX_NO`.
|
|
- Nowe pole `$ownTaxNo` ustawiane z configa.
|
|
|
|
### .env
|
|
- Dodane `FAKTUROWNIA_OWN_TAX_NO=5170167517`.
|
|
|
|
### tests/Domain/Finances/FakturowniaApiClientTest.php (nowy)
|
|
- 7 asercji pokrywajacych: merge endpointow, brak short-circuit na pustym /costs.json, dedup po id, obecnosc `period=more+date_from+date_to` w query, throw gdy wszystkie 3 sciezki zwracaja HTTP 4xx/5xx, pusta liste gdy wszystkie zwracaja 200+[].
|
|
- `FakeFakturowniaApiClient` — subklasa nadpisujaca `request()` do testow bez prawdziwego API.
|
|
|
|
### tests/run.php
|
|
- Zarejestrowany nowy test suite.
|
|
|
|
## Production cleanup (destrukcyjny, wymagal zgody uzytkownika)
|
|
|
|
Wczesniejsze buggowate importy wyprodukowaly brudne dane. Po zgodzie uzytkownika:
|
|
|
|
- `finance_operations`: usunietych 80 rekordow z Fakturowni (sum 7761.43 zl). Wszystkie referencjonowane przez `finance_operation_ids` w `fakturownia_imported_documents`. Reczne wpisy (6 szt.) zostaly nietkniete.
|
|
- `fakturownia_imported_documents`: wyczyszczone (60 rekordow).
|
|
- `fakturownia_client_mappings`: wyczyszczone (36 rekordow) — uzytkownik poprosil o reset wszystkich mapowan.
|
|
- `fakturownia_item_mappings`: wyczyszczone (60 rekordow).
|
|
- `fakturownia_unmapped_queue`: wyczyszczone (112 rekordow).
|
|
- `fakturownia_import_state.last_import_summary`: usuniete.
|
|
|
|
## Verification
|
|
|
|
- `php -l` na wszystkich zmienionych plikach: OK.
|
|
- `php tests/run.php`: 8 test suites, wszystkie zielone (w tym nowy `run_fakturownia_api_client_tests`).
|
|
- Diagnoza na zywym API Fakturowni potwierdzila ze faktura 486639934 jest zwracana przez `fetchCostDocuments` i ma `gov_kind='ksef'` + `approval_status='received'` + odwrocone role seller/buyer.
|
|
|
|
## Deviations vs plan
|
|
|
|
- Task 3 (test regresyjny) zostal zwezony: zamiast testu integracyjnego importera (ktory wymagalby mockowania DB), napisany zostal test jednostkowy dla `FakturowniaApiClient` — pokrywa faktyczny root cause bugu (merge endpointow + filtr dat po stronie API). Test fix KSeF w `normalizeDocument` nie zostal dodany, bo importer nie ma injectable dependency. Przeniesione do 05-05 jako wymagany refactor (dependency injection) przy okazji skip-list.
|
|
|
|
- Pomiedzy T1-T2 a checkpoint pojawil sie hang crona. Drugi raund napraw: dodanie `buildDateRangeQuery` z `period=more&date_from&date_to`. Zaktualizowane testy ACto odpowiednio.
|
|
|
|
- Cleanup bazy poza planem — wymagany poniewaz faktura 486639934 byla juz w `fakturownia_imported_documents` ze zlym mapowaniem klienta, a bez wyczyszczenia `isDocumentImported` zwracalo true i fix KSeF nie mial szansy zadzialac.
|
|
|
|
## Follow-ups
|
|
|
|
- **05-05 (zaplanowany):** skip-list pozycji faktury — mozliwosc oznaczenia wybranej pozycji jako pomijanej w imporcie.
|
|
- **Debt:** `FakturowniaInvoiceImporter` powinien pozwalac na wstrzykniecie `FakturowniaImportRepository` i `FakturowniaApiClient` przez konstruktor — umozliwi pelne testy integracyjne bez DB. Rozwazyc w ramach 05-05.
|
|
|
|
## Next action
|
|
|
|
Wdrozenie na serwer (FTP sync) i reczny reimport. Uzytkownik wykonuje samodzielnie — cleanup bazy juz zrobiony.
|