ksef2lms — darmowa instrukcja: jak podłączyć LMS do Krajowego Systemu e-Faktur
To jest ksef2lms — darmowy łącznik LMS z KSeF 2.0 (LMS w KSeF, wysyłka faktur z LMS do Krajowego Systemu e-Faktur). Działa z LMS Chilka, nie jest częścią oficjalnego repozytorium.
Cześć! Na samym wstępie muszę się przyznać — jestem wieloletnim i oddanym fanem systemu LMS :D Poniższy projekt powstawał momentami w bólach, prawdopodobnie bywa pełen chaosu i z pewnością nie wygra konkursu na najlepiej udokumentowany kod w historii. Początkowo tworzyłem to wyłącznie na swoje własne, absolutnie prywatne potrzeby, żeby jakoś okiełznać nadchodzącego potwora zwanego KSeF. Nie ukrywam! W dużej mierze pomogła tutaj sztuczna inteligencja...
Jednak, ulegając presji tłumu i na prośbę wielu z Was, postanowiłem podzielić się tym dziełem zupełnie nieodpłatnie. Musisz jednak wiedzieć o jednym: nie działam w porozumieniu z autorami ani programistami oficjalnego LMS-a. Oznacza to, że wszelkie ich poprawki i aktualizacje mogą znacząco wpłynąć na (nie)działanie niniejszej integracji. Mój projekt nie jest oficjalną częścią systemu. Oczywiście bardzo się starałem, by całość była jak najbardziej spójna i niczego nie zepsuła, ale korzystasz z tego na własną odpowiedzialność! Oczywiście udostępniony kod może być także inspiracją dla innych, śmiało korzystaj! Zachęcam również do dzielenia się :P
lms-npi.sh), potem skrypty ręcznie.--retry-errors — Pobierz A. Gita nie ruszasz.| Składnik | Minimalna wersja | Sprawdź |
|---|---|---|
| LMS | 28-git, migracje ≥ 2026081100 | SELECT keyvalue FROM dbinfo WHERE keytype='dbversion' |
| PHP | 8.1+ | php -v |
| Composer | dowolna | composer --version |
| Biblioteka KSeF | n1ebieski/ksef-php-client | ls vendor/n1ebieski/ |
| Certyfikat KSeF | Plik .p12 tworzony z plików .crt, .key oraz hasła wygenerowanych w portalu KSeF | — |
| ext-zip | wymagane przez PHP | php -m | grep zip |
Jeśli brak biblioteki ksef-php-client (pierwsza instalacja, nie aktualizacja):
cd /twoja/sciezka/lms
composer require n1ebieski/ksef-php-client --no-dev
Po aktualizacji Chilka — konkretna paczka, nie goły composer update:
cd /twoja/sciezka/lms
composer update n1ebieski/ksef-php-client --with-dependencies --no-dev
composer show n1ebieski/ksef-php-client | grep versions
Aktualna wersja biblioteki: 1.9.0. jQuery UI (js/jqueryui/dist/) jest w procedurze B/C przy pobieraniu — bez tego GUI kręci kółko, to nie jest KSeF.
Aby uwierzytelnić się w systemie KSeF, potrzebujesz certyfikatu. W portalu KSeF Ministerstwa Finansów (Aplikacja Podatnika → Certyfikaty) generujesz parę plików o rozszerzeniach .crt oraz .key, a także podajesz dla nich hasło, które musisz zapamiętać i wpisać do pliku lms.ini. Z tych danych generujesz docelowy plik .p12 (PKCS#12) tym poleceniem:
# Połącz .crt i .key w plik .p12 openssl pkcs12 -export \ -in certyfikat.crt \ -inkey certyfikat.key \ -out /etc/lms/ksef_cert.p12 # lub inna wybrana ścieżka \ -passout pass:TwojeHaslo123! # Zabezpiecz plik chmod 640 /etc/lms/ksef_cert.p12 chown root:www-data /etc/lms/ksef_cert.p12
/etc/lms/ksef_cert.p12 z uprawnieniami 640.
Ministerstwo Finansów może ogłosić awaryjny tryb offline KSeF — wtedy faktury wystawia się bez połączenia z systemem MF. Na PDF pojawia się kod QR podpisany kluczem ECDSA. Osobny certyfikat: w portalu KSeF przeznaczenie „Offline". Pliki .crt i .key łączysz w .pem:
# Certyfikat offline musi być ECDSA (EC P-256) — nie RSA cat ksef_offline.key ksef_offline.crt > /etc/lms/ksef_offline.pem chmod 640 /etc/lms/ksef_offline.pem # Weryfikacja że to EC (a nie RSA): openssl pkey -in /etc/lms/ksef_offline.pem -text -noout | grep "EC\|prime256" # Oczekiwany wynik: id-ecPublicKey, prime256v1
W lms.ini w [ksef]:
offline_certificate = /etc/lms/ksef_offline.pem offline_password = # hasło do pliku .pem (zazwyczaj puste) offline_support = 1 # zezwól na wysyłkę e-mail faktur bez numeru KSeF
Do samego wystawienia faktury offline ten certyfikat nie jest wymagany. Typ 2 jest potrzebny, gdy chcesz drugi kod QR (tożsamość wystawcy) przy offline24 / niedostępności / trybie awaryjnym. Bez niego faktura offline nadal przejdzie, tylko bez tego QR.
Nie testowałem tego na prawdziwej awarii KSeF — nie było kiedy. Zrobione wg dokumentacji MF i LMS. W realu może wymagać korekty.
Edytuj swój plik lms.ini (zazwyczaj /etc/lms/lms.ini lub inna ścieżka podana przy instalacji LMS) i dodaj / uzupełnij poniższe sekcje. Pamiętaj, by we wszystkich komendach zamienić /sciezka/do/lms.ini na własną ścieżkę:
[phpui] plugins = KSeFSubmit # dodaj do istniejącej listy pluginów [ksef] environment = prod # prod / test / demo auth_method = certificate # certificate (zalecane) lub token certificate = /etc/lms/ksef_cert.p12 password = "TwojeHaslo123!" boundary_date = 2026/04/01 # data od której wysyłamy faktury encryption_key = Wpisz32ZnakowyLosowyKluczAES256! encryption_iv = 16ZnakowIV1234 debug = 0 # 1 = loguj plugin do PHP error_log, 0 = cisza [invoices] issuer = 'Dział obsługi rozliczeń' ; podpis wystawcy na fakturze PDF show_balance_summary = 0 ; 0 = nie pokazuj salda klienta na fakturze PDF (zalecane dla ISP) show_pricing_method = 0 info_box_text = '<center><b>Informacja</b>\nFaktura wystawiona w KSeF.\nDokument w KSeF stanowi fakturę właściwą.\nPDF ma charakter informacyjny.</center>' ; ↑ ramka na fakturach B2B — obsługuje HTML, \n = nowa linia info_box_text_b2c = '<center>Dziękujemy za korzystanie z naszych usług</center>' ; ↑ ramka na fakturach B2C — jeśli brak: używana info_box_text [sendinvoices] ; faktury B2B — firmy, klienci z NIPem smtp_host = poczta.twojadomena.pl ; serwer SMTP smtp_port = 587 ; 587=STARTTLS, 465=SSL, 25=plain smtp_auth = PLAIN ; PLAIN lub LOGIN smtp_user = nadawca@twojadomena.pl ; login SMTP smtp_pass = "HasloDoSMTP!" ; haslo SMTP smtp_ssl_verify_peer = 0 ; 0 = nie weryfikuj certyfikatu SSL (webstorage itp.) smtp_ssl_verify_peer_name = 0 smtp_ssl_allow_self_signed = 0 lms_url = https://adres.twojego.lms/ ; URL do LMS (do linkow w mailach) lms_user = uzytkownik_lms ; uzytkownik LMS do generowania PDF lms_password = "HasloUzytkownikaLMS!" customergroups = nazwa-grupy ; grupy klientow do wysylki (spacja = separator) sender_name = Nazwa Twojej Firmy sender_email = biuro@twojadomena.pl invoice_filename = Faktura_%number ; nazwa pliku PDF w zalacznik mail_subject = Faktura VAT %invoice mail_body = /etc/lms/mail_body_b2b.txt mail_format = html ; debug_email = twoj@email.pl ; jesli ustawione — mail idzie TYLKO na ten adres (do testow) [sendinvoices-b2c] ; faktury B2C — osoby fizyczne smtp_host = poczta.twojadomena.pl smtp_port = 587 smtp_auth = PLAIN smtp_user = nadawca@twojadomena.pl smtp_pass = "HasloDoSMTP!" smtp_ssl_verify_peer = 0 smtp_ssl_verify_peer_name = 0 smtp_ssl_allow_self_signed = 0 lms_url = https://adres.twojego.lms/ lms_user = uzytkownik_lms lms_password = "HasloUzytkownikaLMS!" customergroups = nazwa-grupy sender_name = Nazwa Twojej Firmy sender_email = biuro@twojadomena.pl invoice_filename = Faktura_%number mail_subject = Faktura %invoice mail_body = /etc/lms/mail_body_b2c.txt mail_format = html
Pliki wskazane w mail_body to szablony HTML. Zmienne dostępne w treści:
| Zmienna | Znaczenie |
|---|---|
%invoice | Numer faktury (np. FV/001/04/2026) |
%ksef-number | Numer KSeF (np. 8441240447-20260401-...) |
%gross | Kwota brutto faktury |
%bankaccount | Numer konta bankowego |
%paytime | Termin płatności |
%customerid | ID klienta w LMS |
/etc/lms/mail_body_b2b.txt — dla firm (B2B):
Szanowni Państwo,<br><br> informujemy, że została wystawiona faktura VAT nr <b>%invoice</b>.<br><br> <b>Numer KSeF: </b> %ksef-number<br> Faktura została wystawiona w KSeF.<br> Dokument dostępny w KSeF stanowi fakturę właściwą.<br><br> Załączony plik PDF ma charakter poglądowy.<br><br> W razie pytań prosimy o kontakt.<br><br> Z poważaniem,<br> <b>Twoja Firma</b>
/etc/lms/mail_body_b2c.txt — dla osób fizycznych (B2C):
Szanowny Kliencie,<br><br> w załączniku przesyłamy fakturę nr <b>%invoice</b>.<br><br> Dziękujemy za korzystanie z naszych usług.<br><br> Pozdrawiamy,<br> <b>Twoja Firma</b>
info_box_text / info_box_text_b2c w [invoices] to moja prywatna dopiska do szablonu — w standardowym LMS-ie jej nie ma. Łącznik KSeF działa bez niej. Jeśli zostawiasz oryginalny PDF Chilka, te opcje po prostu nic nie zrobią.
Ramka pojawia się na fakturach PDF wystawionych po dacie granicznej KSeF (boundary_date). Dla faktur starszych — nie wyświetla się.
Obsługuje HTML: <b>, <center>, <i> oraz \n jako nową linię.
Jeśli ustawiona tylko info_box_text — pojawia się dla wszystkich klientów.
Jeśli ustawione oba — info_box_text dla B2B (firmy), info_box_text_b2c dla B2C (osoby fizyczne).
# encryption_key — 32 znaki losowe openssl rand -base64 24 | tr -d '=+/' | head -c 32 # encryption_iv — 16 znaków losowych openssl rand -base64 12 | tr -d '=+/' | head -c 16
LMS musi już działać. Tu dokładasz tylko łącznik KSeF: plugin, skrypty w bin/ i jedną linijkę w pliku Chilka (setlocale). Który zip pobrać i czego nie wklejać — sekcja Pobierz. Tam są trzy osobne przypadki, nie mieszaj ich.
Instalator lms-npi.sh z paczki 30.08 kopiuje plugin i skrypty, potem nakłada setlocale. Nie rusza lib/KSeF/KSeF.php (zamiast kopiowania — patcher) ani szablonu PDF. Najpierw dry-run:
# 1. Wgraj paczkę na serwer i rozpakuj unzip lms-ksef-netlink-*.zip -d /tmp/ksef-pack # 2. Dry-run — podgląd, nic nie zmienia bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini --dry-run # 3. Właściwa instalacja bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini
W lms.ini w [phpui] plugins musi być KSeFSubmit (krok 2). Bibliotekę KSeF dociągasz composerem z sekcji wymagań, jeśli jej jeszcze nie ma. Po instalacji: cd /sciezka/lms && bash bin/check-netlink-patches.sh — brak ramki info_box_text na PDF Chilka to nie błąd.
bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini --dry-run bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini --no-backup # bez tar i dumpa bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini --no-backup-dir # bez tar katalogu LMS bash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini --no-backup-db # bez dumpa bazy
Jeśli wolisz kopiować pliki samodzielnie — plugin i skrypty tak, KSeF.php i PDF z paczki nie:
PACK=/tmp/ksef-pack # katalog z rozpakowaną paczką
LMS=/twoja/sciezka/lms # katalog instalacji LMS
mkdir -p $LMS/plugins/KSeFSubmit/handlers $LMS/plugins/KSeFSubmit/lib
cp $PACK/plugins/KSeFSubmit/KSeFSubmit.php $LMS/plugins/KSeFSubmit/
cp $PACK/plugins/KSeFSubmit/handlers/KSeFSubmitHandler.php $LMS/plugins/KSeFSubmit/handlers/
cp $PACK/plugins/KSeFSubmit/lib/KSeFApiService.php $LMS/plugins/KSeFSubmit/lib/
cp $PACK/bin/lms-ksef.php $PACK/bin/lms-ksef-download.php \
$PACK/bin/lms-ksef-sync.php $PACK/bin/lms-ksef-bulk-correct.php \
$PACK/bin/lms-ksef-upo-download.php $PACK/bin/gen-xml.php \
$PACK/bin/patch-netlink.py $LMS/bin/
cp $PACK/bin/check-netlink-patches.sh $LMS/bin/ 2>/dev/null || \
cp $PACK/check-netlink-patches.sh $LMS/bin/
chmod +x $LMS/bin/lms-ksef*.php $LMS/bin/patch-netlink.py
python3 $LMS/bin/patch-netlink.py $LMS
To jest w paczce. Cron i GUI wołają te same rzeczy.
| Plik | Po co |
|---|---|
bin/lms-ksef.php | Wysyłka sprzedaży do KSeF. Szuka faktur od boundary_date bez numeru i je wysyła. Można odpalać ile razy — drugi raz nic nie zrobi. --status-only tylko odbiera numery. --retry-errors ponawia status 400 (paczka 30.08: pomija te, które numer już mają). |
bin/lms-ksef-download.php | Faktury zakupowe (ktoś wystawił na Twój NIP). Cron codzienny. Bez parametrów: wczoraj+dziś. Przy 429 czeka ile każe MF. |
bin/lms-ksef-sync.php | To samo co download, ale Export API: jeden zaszyfrowany ZIP zamiast setek zapytań. Jednorazowo, na zaległości. Nie do crona. |
plugins/KSeFSubmit/ | Wysyłka z GUI zaraz po zapisie faktury. Jak MF nie zdąży w 45s — cron --status-only odbierze numer. |
bin/patch-netlink.py | Po git pull Chilek zjada setlocale. Patcher wstawia z powrotem. Nie kopiuj całego KSeF.php z paczki — npi też go nie kopiuje. |
bin/check-netlink-patches.sh | Czy setlocale i skrypty siedzą. Brak ramki info_box_text na PDF Chilka to nie błąd. |
lms-ksef.php?Zasada działania: Skrypt szuka w bazie wszystkich faktur bez numeru KSeF wystawionych od boundary_date (ustawiane w lms.ini). Nie wie nic o miesiącach ani datach uruchomienia — patrzy tylko na tabelę ksefdocuments i pyta: "czy ta faktura ma już numer KSeF?". Jeśli nie — wysyła.
Zawsze najpierw sprawdza czy są otwarte sesje batch (status 0 lub 100) i pobiera dla nich numery KSeF od MF. To jest ważne — jeśli poprzednie uruchomienie wysłało faktury ale nie zdążyło odebrać numerów (np. skrypt się wysypał), ta faza je nadrobi.
Zapytanie SQL szuka faktur gdzie:
d.cdate >= boundary_date (np. 2026-04-01)d.type IN (DOC_INVOICE, DOC_CNOTE, DOC_DNOTE)kd.id IS NULL — brak rekordu w ksefdocuments = jeszcze nie wysłanaPotem filtruje B2B/B2C — firmy zawsze, osoby fizyczne tylko gdy mają zgodę.
Wystawiasz faktury 1. dnia, uruchamiasz skrypt 5. dnia tego miesiąca:
Działa dokładnie tak samo jak gdybyś uruchomił 1. dnia. Skrypt znajdzie wszystkie faktury z kd.id IS NULL — czyli wszystkie które w międzyczasie nie zostały wysłane — i wyśle je w jednej sesji batch. Nic się nie traci.
Nie uruchamiasz 2 miesiące:
Skrypt znajdzie faktury z obu miesięcy naraz i wyśle je wszystkie w jednym przebiegu. Przykład: masz 200 faktur za marzec i 200 za kwiecień → skrypt wyśle 400 faktur w jednej lub kilku paczkach ZIP (max 50MB per paczka). Jedyne ograniczenie: MF ma limit sesji batch — ale przy 400 fakturach ISP to żaden problem.
Co jeśli skrypt uruchomisz dwa razy z rzędu:
Drugie uruchomienie nic nie zrobi. Faktury już mają rekordy w ksefdocuments → warunek kd.id IS NULL zwróci pustą listę → skrypt zakończy się komunikatem "Brak nowych faktur".
Co jeśli MF odrzuci fakturę (status 400):
Faktura trafia do ksefdocuments ze statusem 400. Kolejne normalne uruchomienie jej nie ruszy — bo kd.id IS NULL już nie jest prawdą. Żeby ponownie wysłać — trzeba użyć flagi: --retry-errors:
./lms-ksef.php -C /sciezka/do/lms.ini --retry-errors
Co jeśli skrypt padnie w połowie wysyłki:
FAZA 1 przy następnym uruchomieniu sprawdzi otwarte sesje i odbierze numery KSeF dla tego co zdążyło dotrzeć do MF. Faktury które nie zostały w ogóle wysłane — mają nadal kd.id IS NULL i wejdą w kolejnym uruchomieniu.
--status-only (w cronie co godzinę):
Wykonuje tylko FAZĘ 1 — sprawdza otwarte sesje i odbiera numery KSeF. Nie wysyła nic nowego. Używaj tego w cronie między 1. a ostatnim dniem miesiąca, żeby faktury dostały numery KSeF szybciej niż czekając na kolejne miesięczne uruchomienie.
Podsumowanie jednym zdaniem:
Skrypt jest idempotentny i bezpieczny do wielokrotnego uruchamiania — zawsze wysyła tylko to czego jeszcze nie ma w KSeF, nigdy dwa razy to samo.
lms-ksef-download.phpCo robi: pobiera faktury które dostawcy wystawili NA Twój NIP i wrzucili do KSeF. To są Twoje faktury zakupowe — Shell, Orange, Allegro itp.
Opcje uruchomienia:
--from=2026/04/01 → od konkretnej daty--days=7 → ostatnie N dni--full-sync → od boundary_date do dziś (pierwsze uruchomienie)Jak działa krok po kroku:
POST /invoices/query/metadata) z paginacją po 100 sztuk — powtarza dopóki hasMore = true.ksef_number już jest w bazie — jeśli tak, pomija.ksefinvoices i plik XML na dysk.fillMissingXml() — dla faktur które dostały 429 przy pierwszym pobraniu i nie mają XML, próbuje jeszcze raz.Idempotentny? Tak — sprawdza ksef_number w bazie, nigdy nie zapisze dwa razy tej samej faktury.
Przy 429: czyta Retry-After z odpowiedzi MF, czeka tyle ile MF każe + bufor, zwiększa globalny odstęp do minimum 5s na kolejne zapytania.
lms-ksef-sync.phpCo robi: to samo co download.php ale innym mechanizmem — przez Export API. Zamiast pobierać faktury jedna po drugiej (64 zapytania/h), prosi MF o spakowanie wszystkiego do jednego zaszyfrowanego ZIPa.
Kiedy używać: jednorazowo — do nadrobienia zaległości historycznych. Do crona codziennego używaj lms-ksef-download.php.
Jak działa krok po kroku:
POST /invoices/exports → MF zaczyna pakować faktury asynchronicznie.GET /invoices/exports/{ref}) aż MF powie że gotowe (status 200)..zip.aes.lms.ini (encryption_key, encryption_iv)._metadata.json (pomijany).lms-ksef-download.php.Zaleta: brak throttlingu — zamiast setek pojedynczych zapytań MF pakuje wszystko do jednego zaszyfrowanego ZIPa (AES-256-CBC). Skrypt odszyfrowuje go kluczami z lms.ini (encryption_key + encryption_iv) i przetwarza wszystkie faktury lokalnie.
Aktywacja w lms.ini: plugin wymaga wpisu w sekcji [phpui]:
[phpui] plugins = KSeFSubmit ; dodaj do istniejącej listy pluginów
Aktywacja w GUI LMS: po wpisaniu do lms.ini przejdź do
Administracja → Konfiguracja → KSeF — pojawi się zakładka konfiguracji pluginu.
Poniżej widok przycisku wysyłki na fakturze:

Co robi: wysyła fakturę do KSeF natychmiast po zapisaniu jej w GUI — bez czekania na cron 1. dnia miesiąca. Klikasz "Zapisz fakturę" → w ciągu kilku sekund masz numer KSeF.
Jak jest zbudowany:
KSeFSubmit.php — rejestruje plugin i hookiKSeFSubmitHandler.php — łapie zdarzenia z GUIKSeFApiService.php — komunikacja z APIKtóre zdarzenia łapie: invoicenew_save_after_submit (nowa faktura), invoiceedit_save_after_submit (edycja faktury), invoicenote_save_after_submit (faktura korygująca).
Jak działa krok po kroku:
boundary_date? Czy to nie proforma? Czy klient B2B albo ma zgodę B2C? Czy KSeF jest dostępny (Latarnia)?DB->UnLockTables() (faktura już zapisana, unlock bezpieczny).ksefdocuments).GetInvoiceContent().KSeF::getInvoiceXml().sessions()->batch()->openAndSend().sessions()->batch()->close() — wymagane żeby MF zaczął przetwarzać fakturę.ksefbatchsessions i fakturę do ksefdocuments (status 0 = oczekuje).sessions()->status().Zarówno plugin jak i lms-ksef.php używają Batch API — plugin wysyła jedną fakturę na sesję, CLI może wysłać wiele faktur naraz.
Co jeśli plugin nie zdąży w 45s (MF wolno przetwarza): faktura zostaje w ksefdocuments ze statusem 0. FAZA 1 w lms-ksef.php --status-only odbierze numer i UPO przy kolejnym uruchomieniu crona.
Chilek trzyma lib/KSeF/KSeF.php w swoim repo. Po git pull / rsync ten plik wraca do wersji Chilka i znika setlocale. W zipie łącznika ten plik może leżeć, ale go nie kopiuj na ślepo — npi z 30.08 go nie wgrywa. Setlocale dokłada bin/patch-netlink.py (instalator odpala go sam na końcu).
LMS przestawia separator dziesiętny na przecinek (locale pl_PL). KSeF wymaga kropki w XML — bez tej poprawki pola P_13_*, P_14_*, P_15 (podstawy VAT, kwoty VAT, suma) generowane przez sprintf('%.2f', ...) zawierają przecinki i MF odrzuca fakturę.
smartFormatNumber() z NumberFormatter('en_US'), ale używa go tylko dla ilości, cen jednostkowych i kursu waluty. Pola sum i podatków (P_13_*, P_14_*, P_15) nadal używają sprintf('%.2f', ...) — czyli są nadal wrażliwe na locale. Poprawka P1 (setlocale) jest nadal wymagana.
# Co robi poprawka P1: public function getInvoiceXml(array $invoice) { setlocale(LC_NUMERIC, 'C'); // NETLink P1 ...
<Rozliczenie> przez tabelę ksefconfig i GUI LMS. Saldo klienta pojawia się tylko wtedy, gdy w konfiguracji oddziału w GUI LMS jest zaznaczony odpowiedni checkbox (showbalancesummary). Domyślnie jest wyłączone — co jest poprawnym zachowaniem. Zgodnie ze schematem FA(3) sekcja <Rozliczenie> ma minOccurs="0" — jest opcjonalna i MF jej nie wymaga.
Jeśli chcesz żeby faktury PDF i XML zawierały saldo klienta (sekcja <Rozliczenie>) — skonfiguruj to w GUI LMS:
Administracja → Konfiguracja → KSeF → Ustawienia oddziałów → Pokazuj rozliczenie salda
Sprawdź czy patche zostały poprawnie nałożone:
# Uruchom z katalogu LMS cd /twoja/sciezka/lms && bash bin/check-netlink-patches.sh
[ksef] do lms.ini ze środowiskiem (jeśli jeszcze nie ma): environment = prodksefconfig — jeśli pusta, wstaw rekord (SQL jest w Archiwum zmian → 18.04.2026). Instalator z 30.08 tego nie robi.cd /twoja/sciezka/lms && composer dump-autoloadUPO to dokument XML wystawiony przez MF potwierdzający że faktura przeszła walidację i została przyjęta przez KSeF. MF przechowuje UPO przez 10 lat — możesz je pobrać w każdej chwili, nie ma pośpiechu.
lms-ksef.php automatycznie zapisuje UPO na dysk podczas
pobierania statusów sesji wsadowych. Pliki lądują w:{sys_dir}/storage/ksef/upo/{NIP}/{YYYYMMDD}/{numerKSeF}.xml
upgradedb.php Chilka. Jeśli jest pusty lub go
brakuje — sprawdź i utwórz ręcznie:
ls -la /twoja/sciezka/lms/storage/ksef/
# Powinno byc: drwxr-xr-x apache apache ... upo
# Jesli brak:
mkdir -p /twoja/sciezka/lms/storage/ksef/upo
chown apache:apache /twoja/sciezka/lms/storage/ksef/upo
chmod 750 /twoja/sciezka/lms/storage/ksef/upo
Plugin GUI (KSeFSubmit) od wersji 17.04.2026 fix3 pobiera i zapisuje UPO automatycznie — razem z numerem KSeF, w tym samym żądaniu HTTP co wysyłka faktury. UPO trafia do tego samego katalogu co przy wysyłce przez CLI:
{sys_dir}/storage/ksef/upo/{NIP}/{YYYYMMDD}/{numerKSeF}.xml
Jeśli plugin nie zdążył pobrać UPO (timeout 45s) — lms-ksef.php --status-only pobierze je automatycznie przy kolejnym uruchomieniu crona.
Pierwsze uruchomienie — pobierz wszystkie faktury które wystawcy przesłali do KSeF na Twój NIP. Skrypt używa Export API (jeden zaszyfrowany ZIP zamiast setek osobnych zapytań):
cd /twoja/sciezka/lms/bin
# Najpierw dry-run — sprawdź ile faktur zostanie pobranych
./lms-ksef-sync.php -C /sciezka/do/lms.ini --from=2026/01/01 --dry-run
# Właściwy sync (zakres maksymalnie 3 miesiące, podziel jeśli większy)
./lms-ksef-sync.php -C /sciezka/do/lms.ini --from=2026/01/01 --to=2026/03/31
./lms-ksef-sync.php -C /sciezka/do/lms.ini --from=2026/04/01
# Dry-run — sprawdź ile faktur do wysłania ./lms-ksef.php -C /sciezka/do/lms.ini --dry-run # Właściwa wysyłka ./lms-ksef.php -C /sciezka/do/lms.ini
Zamień /twoja/sciezka/lms i /sciezka/do/lms.ini na własne ścieżki.
lms-ksef.php kiedy chcesz — 1. dnia, 5. dnia, codziennie. To wynika z Twojej konfiguracji taryf w LMS, nie ze skryptów.
Klasyczny model: LMS generuje faktury 1. dnia, skrypty wysyłają je do KSeF i e-mailem tego samego dnia. Ważne: lms-ksef.php startuje 20 minut po generowaniu — żeby faktury były już gotowe w bazie.
# crontab -e # Generowanie faktur i płatności — 1. dnia o 02:00 0 2 1 * * /twoja/sciezka/lms/bin/lms-payments.php -C /sciezka/do/lms.ini # Wysyłka do KSeF — 20 minut po generowaniu 20 2 1 * * php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini >> /var/log/lms-ksef.log 2>&1 # Wysyłka e-mail B2B (tylko faktury z numerem KSeF) 40 2 1 * * /twoja/sciezka/lms/bin/lms-sendinvoices.php -C /sciezka/do/lms.ini --ksef # Wysyłka e-mail B2C (osoby fizyczne — nie wymagają KSeF) 45 2 1 * * /twoja/sciezka/lms/bin/lms-sendinvoices.php -C /sciezka/do/lms.ini --without-ksef --section=sendinvoices-b2c # Sprawdzanie statusów KSeF co godzinę (przez cały miesiąc) 0 * * * * php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini --status-only >> /var/log/lms-ksef.log 2>&1 # Pobieranie faktur zakupowych od dostawców — codziennie o 06:15 15 6 * * * php /twoja/sciezka/lms/bin/lms-ksef-download.php -C /sciezka/do/lms.ini -q >> /var/log/lms-ksef-download.log 2>&1
Jeśli Twoi klienci mają zobowiązania rozłożone na różne dni miesiąca, lms-payments.php generuje faktury codziennie tylko dla tych którzy mają zobowiązanie na dany dzień. Wtedy warto też codziennie wysyłać do KSeF:
# Generowanie faktur — codziennie o 02:00 0 2 * * * /twoja/sciezka/lms/bin/lms-payments.php -C /sciezka/do/lms.ini # Wysyłka do KSeF — 20 min po generowaniu, codziennie 20 2 * * * php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini >> /var/log/lms-ksef.log 2>&1 # Wysyłka e-mail B2B 40 2 * * * /twoja/sciezka/lms/bin/lms-sendinvoices.php -C /sciezka/do/lms.ini --ksef # Wysyłka e-mail B2C 45 2 * * * /twoja/sciezka/lms/bin/lms-sendinvoices.php -C /sciezka/do/lms.ini --without-ksef --section=sendinvoices-b2c # Sprawdzanie statusów KSeF co godzinę 0 * * * * php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini --status-only >> /var/log/lms-ksef.log 2>&1 # Pobieranie faktur zakupowych — codziennie 15 6 * * * php /twoja/sciezka/lms/bin/lms-ksef-download.php -C /sciezka/do/lms.ini -q >> /var/log/lms-ksef-download.log 2>&1
lms-payments.php generuje faktury w LMS.lms-ksef.php wysyła je do KSeF (uruchom minimum 20 minut po generowaniu).lms-sendinvoices.php --ksef wysyła e-maile tylko fakturom z numerem KSeF — musi startować po lms-ksef.php.--status-only (co godzinę) odbierze numery automatycznie. E-mail pójdzie przy następnym uruchomieniu lms-sendinvoices.php.
Możesz uruchamiać wszystkie skrypty ręcznie kiedy chcesz — cron tylko to automatyzuje:
php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini --dry-run # sprawdź ile faktur czeka php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini # wyślij do KSeF php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini --status-only # odbierz numery /twoja/sciezka/lms/bin/lms-sendinvoices.php -C /sciezka/do/lms.ini --ksef # wyślij e-maile
| lms-payments.php | Generuje faktury w LMS na dany dzień (wg taryf) | zależy od taryf |
| lms-ksef.php | Wysyła faktury do KSeF, odbiera numery KSeF | po generowaniu |
| lms-ksef.php --status-only | Sprawdza statusy i odbiera numery KSeF dla oczekujących | co godzinę |
| lms-sendinvoices.php --ksef | Wysyła e-mail z PDF do B2B (tylko z numerem KSeF) | po wysyłce do KSeF |
| lms-sendinvoices.php --without-ksef | Wysyła e-mail z PDF do B2C (osoby fizyczne) | po generowaniu |
| lms-ksef-download.php | Pobiera faktury zakupowe od dostawców | codziennie |
Szanowni Państwo,<br> <br> w załączniku faktura VAT <b>%invoice</b>.<br> Numer KSeF: <b>%ksef-number</b><br> Kwota do zapłaty: <b>%value PLN</b><br> Numer konta: %bankaccount<br> <br> Z poważaniem
Szanowny Kliencie,<br> <br> w załączniku faktura <b>%invoice</b>.<br> Kwota do zapłaty: <b>%value PLN</b><br> Numer konta: %bankaccount<br> <br> Dziękujemy!
| Zmienna | Znaczenie |
|---|---|
%invoice | Numer faktury (np. LMS_001/04/2026) |
%ksef-number | Numer KSeF (po potwierdzeniu przez MF) |
%value | Kwota brutto |
%bankaccount | Numer konta bankowego |
%customerid | ID klienta w LMS |
%pin | PIN klienta |
Przed wysyłką do KSeF warto sprawdzić czy XML faktury wygląda poprawnie. Skrypt gen-xml.php generuje XML bez wysyłki:
cd /twoja/sciezka/lms php bin/gen-xml.php -C /sciezka/do/lms.ini ID_FAKTURY # XML zapisywany jest do /tmp/ID_FAKTURY.xml cat /tmp/ID_FAKTURY.xml
Sprawdź w wygenerowanym XML:
2595.30<Rozliczenie> zawiera kwotę faktury i <DoZaplaty><Podmiot2> z NIPemKSeF bywa niedostępny — czy to przez kilka godzin, czy przez kilka dni. Skrypty są zaprojektowane tak, żeby nic nie ginęło i żebyś nie musiał robić niczego ręcznie w większości przypadków.
Co się dzieje automatycznie: skrypty przed wysyłką sprawdzają dostępność KSeF przez oficjalny endpoint MF (tzw. Latarnia). Jeśli KSeF nie działa — skrypt kończy pracę z komunikatem i faktury czekają w kolejce.
Co Ty robisz: nic. Po powrocie MF cron automatycznie wyśle wszystkie zaległe faktury i odbierze numery KSeF. Możesz też uruchomić ręcznie:
# Sprawdź ile faktur czeka php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini --dry-run # Wyślij zaległe i odbierz numery KSeF php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini
Czasem Ministerstwo Finansów oficjalnie ogłasza tryb offline — oznacza to, że faktury można wystawiać bez wysyłania ich do KSeF, a po powrocie systemu masz kilka dni na uzupełnienie. W tym czasie:
# Wyślij e-mailem faktury które czekają na numer KSeF (tryb offline MF) /twoja/sciezka/lms/bin/lms-sendinvoices.php -C /sciezka/do/lms.ini --ksef-offline
Jeśli masz skonfigurowany certyfikat offline — na fakturach PDF pojawi się specjalny kod QR podpisany Twoim kluczem ECDSA, który można zweryfikować bez internetu. Po powrocie KSeF wystarczy uruchomić lms-ksef.php — skrypt sam wyśle zaległe faktury.
Latarnia (ang. lighthouse) to oficjalny publiczny endpoint Ministerstwa Finansów, który informuje o aktualnym stanie systemu KSeF. Nie wymaga logowania ani certyfikatu — każdy może go odpytać.
Nasze skrypty pytają Latarnię przed każdą próbą wysyłki faktur. Jeśli KSeF jest niedostępny — skrypt nie próbuje wysyłać, tylko loguje informację i kończy pracę. Faktury czekają bezpiecznie w bazie. Przy kolejnym uruchomieniu crona (lub ręcznym) — procedura powtarza się.
Możliwe stany Latarni:
| Status | Znaczenie | Co robi skrypt |
|---|---|---|
Available |
KSeF działa normalnie | Wysyła faktury |
Planned_Maintenance |
Planowana przerwa techniczna | Przerywa — cron nadrobi później |
Unavailable |
Awaria nieplanowana | Przerywa — cron nadrobi później |
Error |
Błąd systemu MF | Przerywa — cron nadrobi później |
| brak odpowiedzi | Latarnia sama niedostępna (problem sieciowy) | Próbuje wysyłać — jeśli KSeF też nie odpowie, obsługuje błąd normalnie |
Jeśli MF odrzuci fakturę (np. brakujący NIP, błędna stawka VAT) — faktura trafia do bazy ze statusem błędu. Nie zniknie i nie zostanie wysłana ponownie automatycznie — musisz to naprawić ręcznie:
# Sprawdź które faktury mają błąd (w bazie danych) SELECT d.fullnumber, kd.status, kd.statusdescription FROM ksefdocuments kd JOIN documents d ON d.id = kd.docid WHERE kd.status >= 400 ORDER BY d.cdate DESC; # Po poprawieniu danych klienta/faktury — ponów wysyłkę php /twoja/sciezka/lms/bin/lms-ksef.php -C /sciezka/do/lms.ini --retry-errors
| Sytuacja | Rozwiązanie |
|---|---|
Po aktualizacji LMS (kwiecień → sierpień) sidebar działa, środek kręci się w nieskończoność; F12: 404 na jquery-ui.min.js |
To normalne przy skoku na master 30.08. Szablon woła już js/jqueryui/dist/jquery-ui.min.js, a stary composer zostawił UI 1.12 obok, bez dist/. Z katalogu LMS, potem Ctrl+F5:
composer update components/jqueryui --with-dependencies --no-dev mkdir -p js/jqueryui/dist if [ -f vendor/components/jqueryui/dist/jquery-ui.min.js ]; then cp -a vendor/components/jqueryui/dist/jquery-ui.min.js js/jqueryui/dist/ else ln -sf ../jquery-ui.min.js js/jqueryui/dist/jquery-ui.min.js fi |
| Faktury wysłane, brak numerów KSeF (status 100) | Poczekaj — cron --status-only odpytuje co godzinę. Albo ręcznie: lms-ksef.php --status-only |
| Faktury z błędem (status 400) | Popraw dane klienta/faktury, potem: lms-ksef.php --retry-errors |
| Błąd 429 Too Many Requests | MF limit zapytań na godzinę. Odczekaj. Do historycznego sync używaj lms-ksef-sync.php |
| Kwoty w XML z przecinkami (KSeF odrzuca) | Brak poprawki P1. Uruchom: python3 bin/patch-netlink.py /twoja/sciezka/lmsUwaga: smartFormatNumber w KSeF.php od 22.04.2026 nie zastępuje P1 — pola P_13_*/P_14_*/P_15 nadal używają sprintf i są wrażliwe na locale. |
| Błędna kwota w KSeF (niedopłata) | Sprawdź ustawienia oddziału w GUI LMS (ksefconfig.showbalancesummary). Korekty: lms-ksef-bulk-correct.php --dry-run |
--retry-errors znowu wysyła faktury, które już mają numer KSeF; w SQL pojawia się nowy wiersz ze status=0 |
Bug w łączniku NETLink (nie w LMS Chilka), zgłoszony z testów. Stary warunek retry patrzył na dowolny historyczny wiersz z błędem, a nie na to czy dokument już dostał numer. Fix: paczka 30.08.2026 — podmień bin/lms-ksef.php. Nie odpalaj --retry-errors na produkcji na starszej wersji. |
| Błąd certyfikatu | openssl pkcs12 -in /etc/lms/ksef_cert.p12 -info — sprawdź ważność i hasło |
| Faktury sprzed boundary_date mają "BRAK NUMERU KSEF" na PDF | Tabela ksefconfig ma boundarydate=0. Napraw SQL:MySQL: UPDATE ksefconfig SET boundarydate=UNIX_TIMESTAMP('2026-04-01') WHERE boundarydate=0 OR boundarydate IS NULL;PostgreSQL: UPDATE ksefconfig SET boundarydate=1775001600 WHERE boundarydate=0 OR boundarydate IS NULL;Albo wstaw rekord ręcznie (SQL w archiwum zmian → 18.04.2026). Instalator z paczki 30.08 tego nie robi. |
Po git pull przestały działać faktury lub KSeF |
Chilek nadpisał lib/KSeF/KSeF.php — znika setlocale. Nie rób git reset --hard. Po fast-forward: python3 bin/patch-netlink.py /sciezka/lms, wyczyść templates_c, odpal checker. Własnego starego PDF-a nie wgrywaj z powrotem, jeśli nie musisz — oryginał Chilka wystarczy do KSeF. |
| Sytuacja | Rozwiązanie |
|---|---|
Plugin wysyła fakturę ale timeout 45s — brak numeru KSeF w GUI, pojawia się dopiero po --status-only |
Prawie na pewno skok biblioteki n1ebieski/ksef-php-client ze starszej wersji na v1.x. W v1.x kod statusu sesji jest w statusData->status->code, a starsze wersje używały statusData->processingCode. Plugin czyta zawsze null, polling nigdy nie widzi kodu 200 i odpada timeoutem. Faktura i tak trafia do KSeF — tylko numer KSeF i UPO nie wróciły do GUI od razu.Fix: zainstaluj paczkę 17.04.2026 fix3 lub nowszą — poprawka jest już w KSeFApiService.php. |
| Instalator wychodzi bez żadnego komunikatu | Odpalasz sam plik .sh bez reszty paczki albo z katalogu bin/. Rozpakuj całość i odpal z katalogu zipa:unzip lms-ksef-netlink-*.zip -d /tmp/ksef-packbash /tmp/ksef-pack/lms-npi.sh -c /etc/lms/lms.ini --dry-run |
| Instalator: configparser.ParsingError lub Brak nazwy bazy | Stara wersja instalatora (przed v2026-04-09). Pobierz aktualną paczkę — nowy instalator czyta lms.ini przez grep, odporny na wieloliniowe wartości takie jak mail_body w sekcji [sendinvoices]. |
| Instalator: Nie znaleziono katalogu LMS | Skrypt nie widzi katalogu LMS. Sprawdź czy w lms.ini jest:[directories]sys_dir = /sciezka/do/lms |
check-netlink-patches.sh: BRAK P2 |
Stary checker z wersji przed 13.04.2026. Wgraj aktualną paczkę — nowy checker sprawdza showbalancesummary |
lib/KSeF/KSeF.php lokalnie zmodyfikowany — instalator pomija, P1 nie jest naniesiona |
To dotyczy starego instalatora (przed 30.08), który pomijał pliki o innym SHA. Npi z paczki 30.08 nadpisuje plugin i skrypty (zostawia .bak), a KSeF.php i PDF i tak nie kopiuje. P1 nakłada patcher:python3 /twoja/sciezka/lms/bin/patch-netlink.py /twoja/sciezka/lmsbash /twoja/sciezka/lms/bin/check-netlink-patches.sh |
| Po aktualizacji zniknęła ramka na PDF / wystawca z ini / alias logo | To była moja lokalna wariacja szablonu, nie część LMS-a. Jak używasz zwykłego PDF Chilka — nic nie rób, info_box_text = 0 jest w porządku. Jak jednak dogrywałeś mój plik: nie wgrywaj go w całości na nowy master (zginie PDF Chilka z sierpnia). Albo zostań przy oryginale, albo dołóż tylko te kilka bloków na aktualnym pliku. |
30.08.2026 — master Chilka + poprawka --retry-errors
Chilek dał master z 30.08.2026, baza 2026081100. W środku m.in. poprawka logowania login#admin i kilka zmian bezpieczeństwa z ostatnich miesięcy.
Samo git pull nie wystarczy. Kolejność jest w sekcji Pobierz.
Po aktualizacji LMS pokazuje menu, ale środek strony się nie ładuje
Jeżeli aktualizujesz ze starszego mastera (kwiecień / czerwiec), LMS wstaje, menu jest, a zawartość strony kręci się w kółko.
To nie jest problem z KSeF ani z bazą danych.
W nowszym masterze jQuery UI leży już w js/jqueryui/dist/. U Ciebie po starym composerze pliki mogą nadal siedzieć bezpośrednio w js/jqueryui/.
W F12 będą 404 na plikach jQuery UI. Komendy są w instrukcji aktualizacji i w tabeli problemów wyżej.
Biblioteka KSeF
n1ebieski/ksef-php-client idzie teraz na 1.9.0. Wpis o 1.5.1 zostawiam niżej jako historię.
Po aktualizacji LMS:
composer update n1ebieski/ksef-php-client --with-dependencies --no-dev
Nie odpalaj gołego composer update bez nazwy paczki — pociągnie też inne zależności.
Szablon PDF faktury
W mojej paczce jest zmodyfikowany przeze mnie szablon PDF — m.in. B2B/B2C, issuer i alias logo. Do KSeF2LMS nie jest potrzebny.
Jak masz standardowy szablon Chilka — zostań przy nim. Wgranie mojego starszego PDF-a na aktualnego mastera nadpisze to, co Chilek dodał ostatnio, m.in. drugi adres odbiorcy.
setlocale nakłada patcher, niezależnie od szablonu PDF.
Poprawka lms-ksef.php --retry-errors
Poprawiłem --retry-errors.
Wcześniej skrypt mógł ponownie wysłać fakturę, która numer KSeF już miała, jeśli w bazie wisiał stary wpis błędu przy tym dokumencie.
Przy problemach z sesją błąd ląduje też na dokumentach, nie tylko na sesji. Stary warunek SQL brał taki wpis jako powód do ponownej wysyłki XML-a do MF.
Teraz --retry-errors pomija dokumenty, które mają już numer KSeF.
Jeżeli łącznik już Ci działa i chcesz tylko tę poprawkę — nie ruszaj całego LMS-a. Wystarczy podmienić bin/lms-ksef.php.
Master LMS i ta poprawka --retry-errors to dwie osobne sprawy.
05.06.2026
recipient_address_id2) — nowy blok <Podmiot3> #2 w XML dla faktur z dwoma odbiorcami<IDNabywcy> (UUID) w Podmiot2/Podmiot3 oraz blok <Podmiot2K> gdy dane nabywcy różnią się między oryginałem a korektąisset() dla taxest zapobiega błędowi "undefined index" gdy stawka VAT występuje tylko po jednej stronie korekty2026050500 (sessions), 2026050600 (voip_cdr), 2026050700 (odsetki), 2026052500 (kolumny recipient_address_id2/ten2/type2 w assignments i documents), 2026060100 (ksefinvoiceitems: net_price, net_value + backfill)1.5.1 (poprzednia: 1.1.3); zaktualizuj przez composer update n1ebieski/ksef-php-client24.04.2026 — 13.05.2026
30.04.2026
<TerminOpis> z opisem kwoty do zapłaty w PLN, kwoty w walucie i kursu przeliczenia28.04.2026
xmladdallvalues w konfiguracji oddziału (ksefconfig) — generuje w XML wartości <P_11> / <P_11A> dla wszystkich pozycjiksefdelays, ksefallconsumers, ksefboundarydates, ksefshowbalancesummaries) zastąpione jedną tabelą ksefconfig24.04.2026
<P_18A> (mechanizm podzielonej płatności) bazuje teraz na fladze DOC_FLAG_SPLIT_PAYMENT zamiast automatycznego progu kwotowego ≥ 15 000 złdemo i test (Latarnia działa tylko na PROD; wcześniej blokować wysyłkę na środowiskach testowych)<Podmiot3> nie jest generowany (wcześniej <BrakID>1</BrakID> powodował odrzucenie faktury przez walidator XSD)lms-npi.sh — zastępuje lms-netlink-install.sh; wykrywa katalog LMS z lms.ini. (Wpis z kwietnia. Aktualny npi z 30.08 kopiuje plugin i skrypty oraz odpala patcher — nie rusza ksefconfig, composera ani upgradedb.)22.04.2026
smartFormatNumber() dla ilości i cen jednostkowych (merge z master 22.04); poprawka P1 (setlocale) nadal wymagana dla pól sum/VATKodKraju) pobierany z bazy danych (countries), wcześniej na stałe PLpart=auto (merge z master)PIN/ID) wyświetlany po lewej stronie (obok danych nabywcy), nie po prawejinvoices.logo_path jako zamiennik invoices.header_imageinvoices.issuer w lms.ini nadpisuje pole wystawcy z bazy danychinvoices.info_box_text (B2B) i invoices.info_box_text_b2c (B2C) — pojawia się tylko na fakturach po ksef.boundary_date21.04.2026
smartFormatNumber() z NumberFormatter (merge z master 21.04)18.04.2026
n1ebieski/ksef-php-client v1.x — po aktualizacji biblioteki plugin nie pobierał numeru KSeF ani UPOlms-ksef.php gotowy na PostgreSQL (wcześniej działał tylko z MySQL)lms-ksef.php) — funkcja pobierająca dane faktury nie zwracała typu dokumentu, przez co XML był generowany błędnie. Teraz typ jest dociągany osobno z bazy.lms-ksef.php — domyślne okno skanowania 60 dni wstecz zamiast od 1.04 na zawsze; opcja --from-date=YYYY-MM-DDlms-ksef.php — stan Latarni TOTAL_FAILURE blokuje wysyłkę (wcześniej był ignorowany)lms-ksef.php — czytelniejszy log po filtracji B2B/B2CWymagane po instalacji — tabela ksefconfig
Jeśli tabela ksefconfig jest pusta, filtry KSeF w GUI LMS nie działają poprawnie. Wstaw rekord dla każdej dywizji (sprawdź ID w Ustawienia → Oddziały).
MySQL:
INSERT INTO ksefconfig (divisionid, delay, allconsumers, boundarydate, showbalancesummary, xmladdallvalues)
VALUES (1, 0, 0, UNIX_TIMESTAMP('2026-04-01'), 0, 0);
PostgreSQL:
INSERT INTO ksefconfig (divisionid, delay, allconsumers, boundarydate, showbalancesummary, xmladdallvalues) VALUES (1, 0, 0, EXTRACT(EPOCH FROM TIMESTAMP '2026-04-01')::bigint, 0, 0);
Zmień divisionid = 1 jeśli masz inny ID dywizji. Jeśli masz więcej dywizji — wstaw wiersz dla każdej.
17.04.2026
StopkaFaktury, format_ten, bugfixy KSeF.phpksefallconsumers → ksefconfigSą dwa zipy. Nie pobieraj obu „na wszelki wypadek” i nie odpalaj obu procedur pod spodem — to dwa różne światy.
git merge ten plik zostaw w spokoju.bin/lms-ksef*.php, patcher. To nie jest cały LMS. Z tej paczki nie wgrywaj lib/KSeF/KSeF.php ani szablonu PDF.templates_c i cache) oraz dump bazy. W komendach php to ten sam binarny, którym Apache odpala LMS — sprawdź php -m | grep mysqli. Jak pusto, u Ciebie pewnie php81 albo php82, nie systemowe php. Zamiast /sciezka/lms i /etc/lms/lms.ini — swoje ścieżki.
A. Łącznik już masz — tylko poprawka wysyłki
Chodzi o lms-ksef.php --retry-errors: stary skrypt mógł ponownie wysłać do MF fakturę, która numer KSeF już miała. Gita, composera, bazy i PDF nie ruszasz.
cd /tmp wget -O lms-ksef-netlink-30082026.zip "https://ksef2lms.idzik.pl/?track=lms-ksef-netlink-30082026.zip" unzip -o lms-ksef-netlink-30082026.zip -d ksef-pack cp /tmp/ksef-pack/bin/lms-ksef.php /sciezka/lms/bin/lms-ksef.php
B. LMS z gita — aktualizacja Chilka (np. kwiecień/czerwiec → 30.08)
git pull samego kodu nie kończy sprawy. Git nie rusza pluginów ani bin/lms-ksef*.php (ich nie ma u Chilka). Nadpisze lib/KSeF/KSeF.php — znika setlocale, patcher to potem wrzuca z powrotem. Szablon PDF też nadpisze, jeśli wcześniej go ręcznie zmieniałeś; mojego starego PDF-a z paczki na sierpniowy plik Chilka nie wgrywaj.
Nie rób git reset --hard. Nie rób git stash pop ze starym zestawem plików z kwietnia — wrócą Ci stare XML/PDF na nowego Chilka.
1. Kod Chilka. Najpierw git status — jeśli wisi cokolwiek poza KSeF.php, nie wklejaj merge na ślepo. Brudny sam KSeF.php możesz oddać Chilkowi (setlocale wróci w punkcie 3):
cd /sciezka/lms git status -sb git fetch origin git checkout -- lib/KSeF/KSeF.php git merge --ff-only origin/master
git merge --ff-only ma przejść. Jak odmawia — stop. Są wtedy lokalne commity albo inne brudne pliki. Nie obchodź tego resetem na twardo.
2. Biblioteki. Git nie aktualizuje vendor/ ani JS. Po skoku z kwietnia LMS pokazuje menu, a środek strony kręci kółko — szablon woła jQuery UI z js/jqueryui/dist/, a u Ciebie plik leży obok, bez dist/. To nie jest awaria KSeF. Nie odpalaj gołego composer update bez nazwy paczki (pociągnie pół projektu):
cd /sciezka/lms composer update n1ebieski/ksef-php-client components/jqueryui --with-dependencies --no-dev mkdir -p js/jqueryui/dist if [ -f vendor/components/jqueryui/dist/jquery-ui.min.js ]; then cp -a vendor/components/jqueryui/dist/jquery-ui.min.js js/jqueryui/dist/ else ln -sf ../jquery-ui.min.js js/jqueryui/dist/jquery-ui.min.js fi composer show n1ebieski/ksef-php-client | grep versions ls -l js/jqueryui/dist/jquery-ui.min.js
3. Setlocale, baza, cache. Patcher musi już leżeć w bin/ (jest w paczce łącznika). php = ten od Apache, jak wyżej:
cd /sciezka/lms python3 bin/patch-netlink.py /sciezka/lms php devel/upgradedb.php -C /etc/lms/lms.ini find templates_c -type f ! -name '.gitkeep' -delete bash bin/check-netlink-patches.sh
Checker ma zgłosić setlocale i skrypty. Komunikat że brak info_box_text w PDF jest w porządku przy zwykłym szablonie Chilka — to moja ramka na fakturze, nie część łącznika.
4. Pluginu i skryptów jeszcze nie ma? Dopiero wtedy paczka łącznika i lms-npi.sh (Krok 3). To nie zastępuje punktów 1–3 i nie wgrywa PDF.
C. Nie masz gita — aktualizacja LMS z zipa Chilka
To jest dla instalacji, które i tak żyją na ręcznym wgrywaniu zipa. Nie rób tego na katalogu, w którym masz gita — rozjedziesz working tree. rsync nadpisze pliki LMS o tych samych nazwach (w tym KSeF.php i szablon PDF). Pluginów nie skasuje. Zip Chilka nie zawiera vendor/.
1. Pobierz zip Chilka (łącznik tylko jeśli jeszcze go nie masz):
cd /tmp wget -O lms-master-30082026.zip "https://ksef2lms.idzik.pl/?track=lms-master-30082026.zip" unzip -q lms-master-30082026.zip -d /tmp/lms-src
2. Wgraj kod Chilka. Zamień ścieżkę. Nie dodawaj --delete:
rsync -a --exclude vendor --exclude templates_c --exclude cache --exclude .git \ /tmp/lms-src/lms-master/ /sciezka/lms/
3. Jeśli łącznika jeszcze nie było — najpierw Krok 3 (lms-npi.sh). Potem w katalogu LMS:
cd /sciezka/lms composer update n1ebieski/ksef-php-client components/jqueryui --with-dependencies --no-dev mkdir -p js/jqueryui/dist if [ -f vendor/components/jqueryui/dist/jquery-ui.min.js ]; then cp -a vendor/components/jqueryui/dist/jquery-ui.min.js js/jqueryui/dist/ else ln -sf ../jquery-ui.min.js js/jqueryui/dist/jquery-ui.min.js fi python3 bin/patch-netlink.py /sciezka/lms php devel/upgradedb.php -C /etc/lms/lms.ini find templates_c -type f ! -name '.gitkeep' -delete bash bin/check-netlink-patches.sh
| Data | LMS-MASTER | NETLink pack | Zmiany |
|---|---|---|---|
| 05.06.2026 | lms-master-05062026.zip | lms-ksef-netlink-05062026.zip | Sync z master Chilka 05.06: drugi adres odbiorcy (Podmiot3 #2, recipient_address_id2), IDNabywcy UUID dla korekt, Podmiot2K, bugfixy korekt taxest. n1ebieski/ksef-php-client 1.5.1. |
| 24.04.2026 | lms-master-24042026.zip | lms-ksef-netlink-24042026.zip | Merge z master 24.04: KSeF.php — P_18A bazuje na DOC_FLAG_SPLIT_PAYMENT (bugfix Chilka). Latarnia pomijana na demo/test. Fix XSD JST: Podmiot3 nie generuje BrakID gdy brak NIP odbiorcy. |
| 22.04.2026 | lms-master-22042026.zip | lms-ksef-netlink-22042026.zip | Merge z master 22.04: KSeF.php — GROUPING_USED=false (fix separatora tysięcy w XML), KodKraju z bazy countries, lms-sendinvoices.php part=auto, bugfixy LMSEventManager/numberplanedit. |
| 21.04.2026 | lms-master-21042026.zip | lms-ksef-netlink-21042026.zip | Merge z master 21.04: smartFormatNumber (KSeF.php) + zachowany setlocale patch P1. lms-master-update-21042026.zip zawiera tylko 12 zmienionych plików (delta, nie pełny master). |
| 18.04.2026 | lms-master-18042026.zip | lms-ksef-netlink-18042026.zip | Faktura korygująca (plugin + bulk). PostgreSQL. lms-ksef.php: --from-date, TOTAL_FAILURE, czystszy log. Instrukcja INSERT ksefconfig. |
| 17.04.2026 | lms-master-17042026.zip | lms-ksef-netlink-17042026.zip | REPO MASTER 17.04. Plugin KSeFSubmit przepisany na Batch API. Migracja ksefallconsumers→ksefconfig. |
| 15.04.2026 | lms-master-15042026.zip | lms-ksef-netlink-15042026.zip | Chilek: bugfix pustego adresu w XML (AdresL1/AdresL2), bugfix INSERT ksefconfig. |
| 14.04.2026 | lms-master-14042026.zip | lms-ksef-netlink-14042026.zip | Chilek: xmladdallvalues (P_11/P_11A), bugfix ksefshowbalancesummary, lms-notify. |
| 13.04.2026 | lms-master-13042026.zip | lms-ksef-netlink-13042026.zip | KSeF.php: nowe Rozliczenie, NazwaBanku. Poprawka fill-xml po 429. P2 wycofana. |
| 11.04.2026 | lms-master-11042026.zip | lms-ksef-netlink-11042026.zip | Pierwsza publiczna wersja. UPO, plugin GUI, saveUpoFile w cron. |
cd /twoja/sciezka/lms && bash bin/check-netlink-patches.sh
Uwagi lub zapytania dotyczące działania integracji można zgłaszać na adres: cocoban78@gmail.com.
Pytania i uwagi do integracji
Odpowiadasz jako cocoban78