inFakt

Uzgadnianie rozliczeń

Czy inFakt zgadza się, że te zamówienia zostały opłacone: dlaczego jedynym trwałym sygnałem jest paid_date, pięć kodów rozbieżności, przesuwne okno 90 dni i dlaczego nic nie naprawia się samo.

Wystawienie faktury i odnotowanie, że została opłacona, to dwie różne sprawy w dwóch różnych cyklach. Pierwsza ma termin ustawowy, druga to księgowość, którą można poprawić godzinę później. Ten mechanizm jest tą drugą i działa w całości poza potokiem fakturowania - własne zadanie, własne kolumny, własne zdarzenia.

Ten rozdział nie jest kwestią estetyki. Wcześniejsza wersja wcisnęła rozliczanie do potoku wystawiania jako pętlę ponowień i skutek był taki, że wystawione, wysłane do KSeF faktury nie mogły się domknąć przez kwadrans.

Sygnałem jest paid_date i nic poza nim

status w inFakcie to jedno pole typu „ostatni zapis wygrywa" (draft, sent, printed, paid), które nadpisuje każda późniejsza operacja na dokumencie - w tym zwykłe pobranie PDF, bo tak inFakt odnotowuje, że dokument opuścił system.

Produkcyjna faktura 2/09/2026: oznaczona jako zapłacona o 12:40:03. Trzy sekundy później nasz własny załącznik do Allegro pobrał PDF i status pokazywał sent. paid_date pozostało nietknięte.

Czytana przez status ta faktura na zawsze raportuje się jako nieopłacona. Czytana przez paid_date - zapisywane przez endpoint oznaczania i nietykane przez późniejsze operacje - raportuje się jako to, czym jest.

Kwoty nie są lepsze. Faktura 9/08/2026 ma równocześnie status: "paid" i paid_price: 0. Zarówno paid_price, jak i left_to_pay są zapisywane jako materiał dowodowy i żadne z nich nigdy nie rozstrzyga.

Tylko w jedną stronę

Źródłem prawdy o płatności jest Medusa, inFakt jest po stronie odbiorczej. Uzgadnianie czyta oba systemy, zapisuje, co zastało, i nie zapisuje stanu płatności do żadnego z nich. Przeniesienie „ktoś odhaczył zapłatę w panelu inFaktu" z powrotem do zamówienia zrobiłoby z panelu księgowego bramkę płatności.

Nie pobiera też PDF-u. To właśnie ten endpoint psuje status, więc uzgadnianie, które by go używało, niszczyłoby pole, na którego zawodność jest odpowiedzią.

Cztery kolumny

KolumnaZnaczenie
settled_atpaid_date z inFaktu jako znacznik czasu. Puste znaczy „nierozliczone wedle ostatniego odczytu"
settlement_checked_atKiedy ten wiersz był ostatnio czytany z inFaktu. Puste znaczy, że nikt nie patrzył
settlement_driftNa czym polega rozbieżność, albo puste, gdy oba systemy się zgadzają
settlement_paid_minorpaid_price z inFaktu. Wyłącznie dowód, nigdy podstawa decyzji

Wszystkie cztery są nullowalne, bez wartości domyślnej i bez backfillu. Na istniejącym sklepie każdy wiersz startuje z pustymi wartościami, co czyta się jako „jeszcze niesprawdzony" - nic nie zostaje zinterpretowane na nowo, a uzgadnianie wypełnia je własnym tempem, od najdawniej niesprawdzanych.

Pięć kodów rozbieżności

KodCo znaczyDo naprawy maszynowo?
unsettledMedusa ma pobraną całą kwotę, inFakt nie ma paid_dateJedyny kandydat
refunded_but_settledPieniądze wróciły, a inFakt wciąż ma dokument rozliczonyNie - tylko raport
settled_without_captureinFakt ma rozliczone, Medusa nie pobrała nicNie - tylko raport
amount_mismatchinFakt ma rozliczone, Medusa pobrała część kwotyNie - tylko raport
unreadableFaktury albo zamówienia nie dało się odczytać na tyle, żeby porównaćNie

amount_mismatch wynika z pobrań, nigdy z paid_price inFaktu - patrz faktura 9/08/2026 wyżej.

Zwrot na rozliczonej fakturze jest raportowany i nigdy cofany. inFakt nie ma „odznacz", a właściwym instrumentem jest faktura korygująca, której ta wtyczka nie wystawia z własnej inicjatywy.

Nic nie naprawia się samo

Nawet unsettled. Ta wersja czyta, porównuje i raportuje; raport wskazuje wiersze, których naprawa by dotknęła (auto_fixable), żeby dało się ocenić zasięg, zanim cokolwiek zostanie uzbrojone.

Faktury przejęte (adopted) są raportowane i nigdy naprawiane, niezależnie od kodu. Przejęta faktura istniała, zanim powstał jej wiersz w rejestrze, jej księgowanie płatności należy do tego, kto ją wystawił, a wpisanie na niej daty zapłaty to zmiana w cudzym zapisie księgowym. W środowisku, dla którego to projektowano, 25 z 30 wierszy jest przejętych - ta odmowa to większość tabeli, a nie przypadek brzegowy.

Kiedy działa

  • Na zdarzenie. payment.captured, payment.refunded i order.canceled uzgadniają to jedno zamówienie, którego dotyczą, sekundy po zdarzeniu. Zwrot jest jedynym sposobem, w jaki rozliczona faktura staje się nieaktualna po fakcie, a nic innego w tej wtyczce zwrotu nie zauważa.
  • Co godzinę, o 17 * * * * (INFAKT_SETTLEMENT_CRON), jako siatka bezpieczeństwa - dla oznaczenia, które inFakt przyjął i zgubił, dla zapłaty odhaczonej ręcznie w panelu, dla zdarzenia, które nigdy nie dotarło, i dla każdego wiersza sprzed istnienia tego mechanizmu.

Wiersz, który się rozliczył i zgadza, nie jest czytany ponownie: paid_date się nie cofa. Wiersz, który się nie zgadza, jest czytany najwyżej co 6 godzin. To właśnie trzyma koszt w dziesiątkach zapytań dziennie, a nie w setkach.

Zadanie nie bierze blokady fakturowania. Nie dzieli żadnej kolumny z maszyną stanów wystawiania, więc nie jest w stanie zatrzymać faktury, spalić budżetu ponowień ani trzymać blokady, za którą czekałby dokument prawny.

Okno dziewięćdziesięciu dni

Siatka bezpieczeństwa domyślnie patrzy 90 dni wstecz. Faktura, której rozliczenie nigdy się nie uzgodniło, jest problemem operacyjnym do momentu, aż ktoś to zauważy - czyli tygodnie, nie miesiące - a okno bez końca oznacza przemielanie całej historii faktur sklepu, w kółko, po to, żeby nic nie znaleźć.

Pełny przemiał jest dostępny na żądanie i taki właśnie ma być kształt: rzadko, na wyraźną prośbę i pod obserwacją.

Raport

GET  /admin/infakt/settlement?days=90&full=true
POST /admin/infakt/settlement  { days?, full?, order_ids? }

GET czyta rejestr. Nie woła niczego na zewnątrz, kosztuje jedno zapytanie i można go bezpiecznie odpytywać albo trzymać otwartego w karcie.

POST czyta inFakt na nowo dla wierszy w zakresie, aktualizuje te same cztery kolumny i odpowiada odświeżonym raportem. To odświeżenie, nie działanie: nigdy nie oznacza faktury jako zapłaconej i nie dotyka maszyny stanów wystawiania. apply: true kończy się błędem 400, a nie zignorowaniem - automatyczna naprawa nie istnieje, a pytający o nią nie może zostać z przekonaniem, że rozbieżność została poprawiona.

W odróżnieniu od tras fakturowania żadna z tych metod nie patrzy na przełącznik pauzy: odczyt raportu niczego nie wystawia, a sklep porządkujący księgi bardzo prawdopodobnie ma fakturowanie zapauzowane.

Co obserwować

GET /admin/infakt zwraca sekcję settlement obok liczników statusów, bez dodatkowego zapytania:

PoleDlaczego jest ważne
drift.unsettledFaktury wystawione przez tę wtyczkę, których inFakt nie ma za rozliczone
auto_fixablePodzbiór, którego dotknęłaby przyszła naprawa: unsettled, nieprzejęte
adopted_driftRozbieżności na fakturach przejętych - zawsze tylko raport
never_checkedZafakturowane wiersze, których nikt jeszcze nie uzgadniał
oldest_checked_age_secondsTo do alertu. Zatrzymane uzgadnianie wygląda dokładnie jak rozliczony sklep, dopóki nikt nie mierzy wieku sprawdzenia

Spis treści