Cyklicznie wystawianie faktur w Infakt za pomocą N8N

Wstęp

Co miesiąc ten sam rytuał. Otwierasz arkusz, sprawdzasz kto ma abonament, wystawiasz faktury. Ręcznie. Jedna po drugiej. Czasem zapomnisz, czasem pomylisz datę. Brzmi znajomo? U nas tak było. Różni klienci, różne usługi, różne daty rozpoczęcia usług. Każdego miesiąca traciliśmy cenny czas na mechaniczne czynności. Postanowiłem to zmienić i stworzyłem prosty workflow w n8n, który całkowicie wyeliminował ten problem.

W tym poradniku przeprowadzę Cię przez proces tworzenia automatyzacji, która sprawdzi, komu należy wystawić fakturę, wygeneruje ją automatycznie, wyśle do klienta profesjonalnego maila z linkiem do płatności online i zaktualizuje arkusz z historią faktur. Brzmi skomplikowanie? Spokojnie, rozbijemy to na proste kroki.

Narzędzia, których użyjemy

  • n8n – platforma do automatyzacji workflow
  • Google Sheets – do przechowywania danych o klientach i usługach
  • Infakt API – do generowania faktur
  • Mailgun – do wysyłania maili

Wymagania wstępne

Zanim przejdziemy do konfiguracji, upewnij się, że masz:

  1. Konto w n8n (możesz użyć wersji cloud lub self-hosted).
  2. Arkusz Google Sheets z danymi o klientach i usługach.
  3. Konto w Infakt z dostępem do API (potrzebny klucz API).
  4. Konto w Mailgun (lub inna usługa do wysyłania maili) (potrzebny klucz API).
  5. Podstawową znajomość n8n – jeśli jesteś początkujący, nie martw się, wyjaśnię wszystko krok po kroku.

Konfiguracja Arkusza Google Sheets

Pierwszym krokiem jest przygotowanie arkusza Google Sheets, który będzie przechowywał dane o Twoich klientach i usługach. Arkusz powinien mieć dwie zakładki:

  1. Usługi – tu przechowujemy informacje o klientach i ich usługach.
  2. Historia_faktur – tu będziemy zapisywać historię wystawionych faktur.

Struktura zakładki „Usługi”

Oto jak powinna wyglądać zakładka „Usługi”:

  • ID – unikalny identyfikator usługi.
  • Nazwa_firmy – nazwa firmy klienta.
  • E_mail – adres email klienta.
  • Nazwa_usługi – nazwa usługi z placeholderami, np. „Usługa testowa – abonament %month% %year%”.
  • Cena_netto – cena netto usługi.
  • VAT – stawka VAT (np. 23).
  • Cykl – cykl rozliczeniowy (miesięczny lub roczny).
  • Data_rozpoczęcia – data rozpoczęcia usługi.
  • Ostatnia_faktura – data ostatniej wystawionej faktury.
  • Status – status usługi (aktywna lub nieaktywna).

Przykład danych w Google Sheets

Zrzut ekranu przedstawia arkusz Google Sheets z zakładką „Usługi”.
Ten arkusz jest podstawą automatyzacji, dostarczając dane do generowania faktur.

Konfiguracja n8n

Teraz przejdziemy do n8n, gdzie stworzymy workflow automatyzujący cały proces. Workflow składa się z 11 node-ów, które wykonują poszczególne zadania: od sprawdzenia daty, przez wystawienie faktury, po wysłanie maila do klienta.

Oto jak wygląda gotowy workflow:

Zrzut ekranu przedstawia workflow „Automatyczne wystawianie faktur w Dogtronic” w n8n.

Krok 1 – utwórz nowy workflow

Zaloguj się do n8n i utwórz nowy workflow. Nazwij go np. „Automatyczne wystawianie faktur”.

Krok 2 – dodaj node Schedule Trigger

Ten node uruchomi workflow codziennie o 8:00 rano.

  • Typ: Schedule Trigger.
  • Konfiguracja: Ustaw Trigger at na 8:00 AM.
  • Cel: Codzienne sprawdzanie, czy należy wystawić nowe faktury.

Krok 3 – dodaj node Google Sheets (get_invoices)

Ten node pobierze dane z arkusza „Usługi”.

  • Typ: Google Sheets.
  • Operacja: Read.
  • Document ID: ID Twojego arkusza Google Sheets.
  • Sheet Name: „Usługi”.
  • Autentykacja: Użyj OAuth2 dla Google Sheets.
  • Cel: Pobranie listy usług i klientów z arkusza.

Krok 4 – dodaj node Code (check_date)

Ta noda zawiera skrypt JavaScript, który sprawdzi, dla których usług minął miesiąc lub rok od ostatniej faktury i przygotuje dane do wystawienia nowych faktur.

Przykładowy kod JavaScript:



const today = new Date();
// Ustawiamy godzinę na koniec dnia dla precyzyjniejszego porównania
today.setHours(23, 59, 59, 999);

const items = $input.all();
const invoicesToCreate = [];

// Pobierz aktualny miesiąc i rok
const currentMonth = today.toLocaleString('pl-PL', { month: 'long' }); // styczeń, luty, etc.
const currentYear = today.getFullYear(); // 2025

// Funkcja do bezpiecznego obliczania następnej daty faktury
function calculateNextInvoiceDate(lastInvoiceDate, cycle) {
  const lastDate = new Date(lastInvoiceDate);
  
  if (cycle === 'miesięczny') {
    // Pobierz dzień miesiąca z ostatniej faktury
    const dayOfMonth = lastDate.getDate();
    
    // Utwórz nową datę o miesiąc później
    const nextDate = new Date(lastDate.getFullYear(), lastDate.getMonth() + 1, 1);
    
    // Sprawdź ile dni ma następny miesiąc
    const daysInNextMonth = new Date(nextDate.getFullYear(), nextDate.getMonth() + 1, 0).getDate();
    
    // Ustaw dzień - jeśli oryginalny dzień jest większy niż liczba dni w następnym miesiącu,
    // ustaw na ostatni dzień miesiąca
    const targetDay = Math.min(dayOfMonth, daysInNextMonth);
    nextDate.setDate(targetDay);
    
    return nextDate;
  } else if (cycle === 'roczny') {
    const nextDate = new Date(lastDate);
    nextDate.setFullYear(nextDate.getFullYear() + 1);
    return nextDate;
  }
  
  return null;
}

// Funkcja do formatowania daty dla logów
function formatDate(date) {
  return date.toISOString().split('T')[0];
}

console.log(`=== SPRAWDZANIE FAKTUR NA DZIEŃ: ${formatDate(today)} ===`);

for (const item of items) {
  const data = item.json;
  
  console.log(`\n--- Sprawdzanie usługi: ${data.Nazwa_usługi} (ID: ${data.ID}) ---`);
  
  if (data.Status !== 'aktywna') {
    console.log(`Status: ${data.Status} - pomijam`);
    continue;
  }
  
  if (!data.Ostatnia_faktura) {
    console.log(`Brak daty ostatniej faktury - pomijam`);
    continue;
  }
  
  const lastInvoice = new Date(data.Ostatnia_faktura);
  const startDate = new Date(data.Data_rozpoczęcia);
  
  console.log(`Ostatnia faktura: ${formatDate(lastInvoice)}`);
  console.log(`Cykl: ${data.Cykl}`);
  
  // Sprawdź czy data ostatniej faktury jest prawidłowa
  if (isNaN(lastInvoice.getTime())) {
    console.log(` Nieprawidłowa data ostatniej faktury: ${data.Ostatnia_faktura} - pomijam`);
    continue;
  }
  
  const nextInvoiceDate = calculateNextInvoiceDate(lastInvoice, data.Cykl);
  
  if (!nextInvoiceDate) {
    console.log(` Nieobsługiwany cykl: ${data.Cykl} - pomijam`);
    continue;
  }
  
  console.log(` Następna faktura powinna być: ${formatDate(nextInvoiceDate)}`);
  console.log(` Dzisiaj: ${formatDate(today)}`);
  console.log(` Czy czas na fakturę? ${nextInvoiceDate <= today ? 'TAK' : 'NIE'}`);
  
  if (nextInvoiceDate <= today) {
    let serviceName;
    
    // Sprawdź czy istnieje numer zamówienia
    if (data.Numer_zamowienia && data.Numer_zamowienia.trim() !== '') {
      // Jeśli jest numer zamówienia, użyj tego formatu
      serviceName = `Usługi zgodnie zamówieniem nr ${data.Numer_zamowienia}`;
    } else {
      // Jeśli nie ma numeru zamówienia, użyj standardowej nazwy z placeholderami
      // Pobierz miesiąc i rok z daty następnej faktury
      const invoiceMonth = nextInvoiceDate.toLocaleString('pl-PL', { month: 'long' });
      const invoiceYear = nextInvoiceDate.getFullYear();

      // Zamień %month% i %year% w nazwie usługi
      serviceName = data.Nazwa_usługi
        .replace('%month%', invoiceMonth)
        .replace('%year%', invoiceYear);
    }
    
    const invoice = {
      serviceId: data.ID,
      clientId: data.Klient_ID,
      client_company_name: data.Nazwa_firmy,
      client_email: data.E_mail,
      serviceName: serviceName,
      price: data.Cena_netto,
      vat: data.VAT,
      cycle: data.Cykl,
      // Dodaj dodatkowe informacje do debugowania
      lastInvoiceDate: formatDate(lastInvoice),
      nextInvoiceDate: formatDate(nextInvoiceDate),
      todayDate: formatDate(today)
    };
    
    invoicesToCreate.push(invoice);
    console.log(`FAKTURA DO UTWORZENIA dla ${data.Nazwa_firmy}`);
  } else {
    console.log(`Jeszcze nie czas na fakturę`);
  }
}



if (invoicesToCreate.length > 0) {
  console.log(`\n Lista faktur do utworzenia:`);
  invoicesToCreate.forEach((invoice, index) => {
    console.log(`${index + 1}. ${invoice.client_company_name} - ${invoice.serviceName}`);
  });
}

return invoicesToCreate.map(invoice => ({ json: invoice }));

Dynamiczne nazwy usług – mały trick, duża oszczędność
Zamiast ręcznie edytować nazwę usługi co miesiąc, używam placeholderów:
W arkuszu wpisuję: Usługa testowa - abonament %month% %year%.
System automatycznie zamienia to na: Usługa testowa – abonament czerwiec 2025.

Cel: Sprawdzenie, które usługi wymagają wystawienia faktury i przygotowanie danych z dynamiczną nazwą usługi (np. "Usługa testowa - abonament czerwiec 2025").

Krok 5 – Dodaj node If

Ten node sprawdzi, czy istnieją FV do wystawienia.

  • Warunek: Sprawdź, czy client_email istnieje i nie jest pusty.
  • Cel: Upewnienie się, że faktura zostanie wysłana tylko do klientów z poprawnym adresem email.

Krok 6 – dodaj node Split in Batches

Ten node podzieli listę faktur do wystawienia na pojedyncze elementy, aby przetwarzać je po kolei.

  • Konfiguracja: Ustaw batch size na 1.
  • Cel: Przetwarzanie faktur jedna po drugiej z opóźnieniem, aby uniknąć problemów z numeracją faktur w Infakt.

Krok 7 – dodaj nodę HTTP Request (create_invoice)

Ten node wyśle żądanie POST do API Infakt w celu utworzenia nowej faktury.

  • URL: https://api.infakt.pl/v3/invoices.json.
  • Metoda: POST.
  • Autentykacja: Header Auth z kluczem API Infakt.
  • Body (JSON):
{
  "invoice": {
    "kind": "vat",
    "number": null,
    "sell_date": "{{$json.nextInvoiceDate}}",
    "invoice_date": "{{$json.nextInvoiceDate}}",
    "payment_to": "{{DateTime.fromISO($json.nextInvoiceDate).plus({days: 7}).toFormat('yyyy-MM-dd')}}",
    "client_company_name": "{{ $json.client_company_name }}",
    "services": [
      {
        "name": "{{$json.serviceName}}",
        "net_price": "{{Math.round($json.price * 100)}}",
        "unit_net_price": "{{Math.round($json.price * 100)}}",
        "tax_symbol": "{{$json.vat}}",
        "quantity": 1
      }
    ]
  }
}
Cel: Utworzenie nowej faktury w Infakt z dynamicznymi danymi.

Krok 8 – Dodaj node Google Sheets (update_sheet)

Po wystawieniu faktury zaktualizuj arkusz „Usługi” z nową datą ostatniej faktury.

  • Operacja: Update.
  • Sheet Name: Usługi.
  • Columns to update: Ostatnia_faktura z wartością {{ $json.nextInvoiceDate }}.
  • Matching column: ID z wartością {{ $json.serviceId }}.
  • Cel: Aktualizacja daty ostatniej faktury w arkuszu.

Krok 9 – Dodaj node Google Sheets (add_to_history)

Dodaj rekord o wystawionej fakturze do zakładki Historia_faktur.

  • Operacja: Append.
  • Sheet Name: „Historia_faktur”.
  • Columns:
    • ID: {{ $('create_invoice').item.json.uuid }}.
    • Data_dodania: {{DateTime.now().toFormat('yyyy-MM-dd H:mm')}}.
    • Klient_ID: {{ $json.client_company_name }}.
    • Numer_faktury: {{ $('create_invoice').item.json.number }}.
    • Data_wystawienia: {{ $('create_invoice').item.json.invoice_date }}.
    • Kwota_netto: {{ $('create_invoice').item.json.net_price / 100 }}.
    • Status: {{ $('create_invoice').item.json.status }}.
    • Url: {{ $('create_invoice').item.json.extensions.payments.link }}.
  • Cel: Zapisanie historii wystawionych faktur.

Krok 10 – Dodaj node HTTP Request (download_invoice)

Pobierz PDF faktury z Infakt.

  • URL: https://api.infakt.pl/v3/invoices/{{ $('create_invoice').item.json.uuid }}/pdf.json?document_type=original.
  • Metoda: GET.
  • Autentykacja: Header Auth z kluczem API Infakt.
  • Cel: Pobranie PDF faktury do załączenia w mailu.

Krok 11 – Dodaj node Mailgun (send_mail)

Wyślij maila do klienta z fakturą i linkiem do płatności.

  • From: Twój adres skrzynki nadawczej.
  • To: {{ $json.client_email }}.
  • CC: Adres np. zarządu.
  • Subject: Nowa faktura do zapłaty ({{ $('create_invoice').item.json.number }}).
  • HTML Body: Profesjonalnie sformatowany mail z danymi faktury i przyciskiem „ZAPŁAĆ TERAZ”.
  • Attachments: PDF faktury.

Przykład maila wysłanego do klienta:

Przykład wiadomość e-mail, którą otrzymuje klient.

Fragment HTML maila:

<p>Dzień dobry,</p>
<p>W załączeniu przesyłamy fakturę:</p>
<ul>
  <li><strong>Numer faktury:</strong> {{ $('create_invoice').item.json.number }}</li>
  <li><strong>Usługa:</strong> {{ $json.serviceName }}</li>
  <li><strong>Data wystawienia:</strong> {{ $('create_invoice').item.json.invoice_date }}</li>
  <li><strong>Termin płatności:</strong> {{ $('create_invoice').item.json.payment_date }}</li>
  <li><strong>Kwota do zapłaty:</strong> {{ $('create_invoice').item.json.gross_price / 100 }} PLN</li>
</ul>
<div class="payment-section">
  <p>Zapłać wygodnie i bezpiecznie online:</p>
  <a href="{{ $('add_to_history').item.json.Url }}" style="display: inline-block; background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; font-weight: bold; margin: 15px 0; text-align: center;">ZAPŁAĆ TERAZ</a>
  <p><small>Bezpieczne płatności obsługiwane przez Infakt.pl</small></p>
</div>
<p>Dziękujemy za terminową płatność i dotychczasową współpracę! 🙏</p>
<p>W razie pytań jesteśmy do Państwa dyspozycji.</p>
<hr>
<p><small>Ta wiadomość została wygenerowana automatycznie. Prosimy nie odpowiadać na ten email. W razie wątpliwości prosimy o kontakt na adres hello@dogtronic.io</small></p>
Cel: Wysłanie maila do klienta z fakturą i linkiem do płatności.

Krok 12 – Dodaj node Wait

Wprowadź opóźnienie 1 sekundy przed przetworzeniem kolejnej faktury.

  • Amount: 1 second.
  • Cel: Zapobieganie problemom z numeracją faktur w Infakt przez zbyt szybkie wysyłanie żądań.

Testowanie Workflow

Po skonfigurowaniu wszystkich nodów, przetestuj workflow z danymi testowymi:

  1. Upewnij się, że w arkuszu „Usługi” masz co najmniej jeden wiersz z datą ostatniej faktury, która wymaga wystawienia nowej faktury.
  2. Uruchom workflow manualnie, aby sprawdzić, czy faktura jest poprawnie tworzona i wysyłana.
  3. Sprawdź logi w n8n, aby upewnić się, że nie ma błędów.
  4. Zweryfikuj, czy arkusz „Historia_faktur” został zaktualizowany.
  5. Sprawdź, czy mail został wysłany poprawnie z załącznikiem PDF.

Typowe problemy i rozwiązania:

  • Błąd autentykacji API: Upewnij się, że klucze API są poprawne.
  • Nieprawidłowe daty: Sprawdź format dat w arkuszu i w kodzie JavaScript.
  • Problemy z numeracją faktur: Upewnij się, że node Wait jest skonfigurowana poprawnie.

Podsumowanie

Gratulacje! Właśnie stworzyłeś automatyzację, która oszczędzi Ci masę czasu i zminimalizuje ryzyko błędów w procesie wystawiania faktur. Workflow działa sam, bez Twojej interwencji, a Ty możesz skupić się na ważniejszych zadaniach.

Korzyści

  • Zero pomyłek w datach i kwotach.
  • Automatyczne wysyłanie maili z linkiem do płatności.
  • Pełna historia faktur w arkuszu.
  • Zarząd na bieżąco z informacjami (dzięki CC w mailu).

Zachęcam do dostosowania tego workflow do swoich specyficznych potrzeb. Możesz dodać więcej logiki, np. obsługę różnych walut czy automatyczne przypomnienia o zaległych płatnościach.

Chcesz więcej automatyzacji? Sprawdź nasz wpis o automatycznym oznaczaniu opłaconych faktur.


Dodatkowe zasoby

Pamiętaj, że ten poradnik jest punktem wyjścia. Automatyzacja to potężne narzędzie, które możesz rozwijać i dostosowywać do swoich potrzeb.

Powodzenia 🐶.

TLDR

Nie masz teraz czasu na pełną lekturę? Poniżej znajdziesz podsumowanie najważniejszych wniosków z całego artykułu.

  • Sprawdza codziennie o 8:00, komu należy wystawić fakturę, generuje ją w Infakt, wysyła maila z PDF i linkiem do płatności, a następnie aktualizuje arkusz Google Sheets z historią faktur.

  • n8n (do automatyzacji), Google Sheets (dane klientów), Infakt API (faktury), Mailgun (wysyłka maili) oraz klucze API do wszystkich usług.

  • Około 2-3 godzin na pierwsze skonfigurowanie wszystkich 11 node-ów w n8n, plus czas na przygotowanie arkusza Google Sheets.

  • JavaScript porównuje datę ostatniej faktury z obecną datą, uwzględniając cykl rozliczeniowy (miesięczny/roczny) i automatycznie oblicza, czy czas na nową fakturę.

  • System automatycznie zamienia placeholdery %month% %year% w nazwach usług, np. „Abonament %month% %year%” staje się „Abonament czerwiec 2025”.

  • Tak, workflow można uruchomić manualnie z danymi testowymi, sprawdzić logi w n8n i zweryfikować czy wszystkie kroki działają poprawnie.

  • Każda wystawiona faktura jest automatycznie zapisywana w arkuszu „Historia_faktur” z numerem, datą, kwotą i linkiem do płatności online.

  • Tak, automatycznie generowane maile zawierają szczegóły faktury, załącznik PDF i kolorowy przycisk „ZAPŁAĆ TERAZ” prowadzący do bramki płatniczej.

  • Workflow ma wbudowane opóźnienie 1 sekundy między fakturami i przetwarza je pojedynczo, co zapobiega konfliktom w API Infakt.

  • Zero ręcznej pracy, brak pomyłek w datach/kwotach, automatyczne maile z płatnościami online, pełna historia w arkuszu i zarząd informowany na bieżąco.

Marek Golan

CEO Dogtronic, Prezes ESTA Cluster, dumny opiekun Borysa. Ma prawie 20 lat doświadczenia w branży IT zarówno jako programista jak i właściciel firm technologicznych.

Przekształć
swoje pomysły
w rzeczywistość

Skontaktuj się z nami i pozwól nam sprawdzić jak możemy Ci pomóc.

Najciekawsze treści na Blogu

Zobacz wszystkie

Automatyczne oznaczanie opłaconych faktur w inFakt

Jeśli prowadzisz firmę i wystawiasz faktury, zapewne znasz ten ból – codzienne sprawdzanie, które faktury zostały opłacone i ręczne oznaczanie ich w systemie. Ja przez lata robiłem to samo, aż w końcu stworzyłem automatyzację, która robi to za mnie. Dziś podzielę się moim rozwiązaniem.

Problem – ręczne oznaczanie faktur

Moja codzienna rutyna wyglądała tak:

  1. NestBank wysyłał mi maila w stylu: „Dzień dobry, dnia 01.07.2025 nastąpił wpływ 19500,00 PLN na konto BIZnest Konto 56187010452078105478980001, od Acme Spółka z ograniczoną odpowiedzialnością, tytułem 12/06/2025”
  2. Czytałem maila
  3. Logowałem się do inFakt
  4. Szukałem faktury 12/06/2025
  5. Weryfikowałem dane
  6. Oznaczałem jako opłaconą

Brzmi prosto? No właśnie. Problem w tym, że trzeba to robić codziennie, a czasem kilka razy dziennie. I jak człowiek się spieszy albo ma gorszy dzień, to się zdarza, że jakąś fakturę pominie. Efekt? Niepotrzebnie ruszamy proces windykacyjny do klienta, który już zapłacił. Niezręcznie.

Rozwiązanie – automatyczny workflow

Stworzyłem prosty workflow, który:

  • Automatycznie czyta maile od banku z powiadomieniami o wpłatach
  • Używa AI do wyciągnięcia kluczowych danych (numer faktury, klient, kwota)
  • Szuka pasującej faktury w bazie inFakt
  • Zmienia jej status na „opłacona” przez API
  • Wysyła mi powiadomienie, że wszystko poszło OK

Czemu nie ma prostszej integracji?

Niestety, inFakt nie oferuje bezpośredniej integracji z NestBank. Gdyby oferował, pewnie bym z niej skorzystał.

Po co AI do parsowania maili?

Tu przyznaję się do lenistwa. Mogłem napisać regex’y do wyciągania danych z maili, ale:

  • AI radzi sobie z tym błyskawicznie
  • Nie muszę się martwić o różne formaty maili
  • Koszt API jest praktycznie niezauważalny przy tej skali
  • Jeśli bank zmieni format maila, wystarczy dostosować prompt

Jak wygląda workflow?

Na załączonym schemacie widać cały przepływ:

  1. Gmail Trigger – nasłuchuje na maile z odpowiednią etykietą
  2. Basic LLM Chain – AI parsuje treść maila i wyciąga dane
  3. Payment – formatuje dane do dalszego przetwarzania
  4. Get_invoices – pobiera listę niezapłaconych faktur z Google Sheets (musisz wcześniej zrobić integrację do pobierania FV z Infakt do Google Sheets)
  5. Check_if_paid – sprawdza czy znaleziono pasującą fakturę
  6. If – decyzja: znaleziono czy nie?
  7. Infakt_mark_as_paid – oznacza fakturę jako opłaconą przez API
  8. Send_notification – wysyła powiadomienie o sukcesie
  9. Mark_message_as_read – oznacza maila jako przeczytany

Czy warto?

Absolutnie tak. Jedna powtarzalna, irytująca czynność mniej. Zero pomyłek. Zero stresu, że coś pominąłem. Wszystko dzieje się automatycznie w tle.

Jak samemu stworzyć taki work-flow w N8N?

Wymagania wstępne

Zanim zaczniemy, upewnij się że masz:

  • Działającą instancję n8n (self-hosted lub cloud)
  • Konto Google z dostępem do Gmail i Google Sheets
  • Konto inFakt z dostępem do API
  • Konto OpenRouter (do AI) lub inny model LLM
  • Konto Mailgun (opcjonalnie – do powiadomień)

Krok 1: Przygotowanie Google Sheets

Najpierw potrzebujemy arkusza z listą faktur:

  1. Stwórz nowy arkusz Google Sheets nazwany np. „Faktury: [Twoja Firma] 2025”
  2. Stwórz arkusz o nazwie „Infakt”
  3. Dodaj kolumny:
    • ID – unikalny identyfikator
    • Numer faktury – np. „12/06/2025”
    • Nazwa klienta – pełna nazwa firmy
    • Kwota brutto – kwota faktury
    • Waluta – np. „PLN”
    • Status – „sent” lub „printed” (dla nieopłaconych)
    • uuid – UUID faktury z inFakt
    • Pozostało do zapłaty – kwota do zapłaty
Musisz stworzyć automatyzację, która będzie pobierała faktury z inFakt do Google Sheet. Jeśli nie wiesz jak to zrobić daj znać w komentarzu - dopiszę instrukcję.

Krok 2: Konfiguracja Gmail

  1. W Gmail (lub w innej poczcie z której korzystasz) stwórz nową etykietę np. „Wpłaty-Bank
  2. Stwórz filtr, który automatycznie oznacza maile od banku tą etykietą
  3. Upewnij się, że maile z powiadomieniami o wpłatach trafiają do tej etykiety

Krok 3: Tworzenie workflow w n8n

3.1 Gmail Trigger

  1. Dodaj node Gmail Trigger
  2. Połącz swoje konto Google (OAuth2)
  3. Skonfiguruj:
    • Poll Times: Every Minute
    • Filters → Label IDs: Wybierz swoją etykietę (np. „Wpłaty-Bank”)
    • Read Status: Unread

3.2 Basic LLM Chain (parsowanie maila)

  1. Dodaj node Basic LLM Chain
  2. Połącz z Gmail Trigger
  3. W polu Prompt wklej:
Z poniższego tekstu wyciągnij mi do formatu JSON:

- invoice_number
- client
- paid_date
- amount

Przykłady odpowiedzi:

- invoice_number: 15/06/2025
- client: ESTA CLUSTER
- paid_date: 01.07.2025
- amount: 1230,00 PLN


#Tekst do analizy: 

{{ $json.snippet }}
  1. Zaznacz Has Output Parser

3.3 OpenRouter Chat Model

  1. Dodaj node OpenRouter Chat Model
  2. Połącz z Basic LLM Chain (połączenie AI)
  3. Skonfiguruj credentials OpenRouter

3.4 Code node – formatowanie danych

  1. Dodaj node Code
  2. Nazwij go „payment”
  3. Wklej kod:
// Pobierz odpowiedź z LLM
const llmResponse = $input.first().json.text

// Usuń markdown formatting i sparsuj JSON
const jsonString = llmResponse.replace(/```json\n?/, '').replace(/\n?```/, '');
const parsedJson = JSON.parse(jsonString);

return [{ json: parsedJson }];

3.5 Pobieranie faktur z Google Sheets

  1. Dodaj 2 node’y Google Sheets:
    • Nazwij pierwszy: „get_invoices_printed”
    • Nazwij drugi: „get_invoices_sent”
  2. W obu skonfiguruj:
    • Document ID: ID twojego arkusza
    • Sheet Name: „Infakt”
    • Credentials: Twoje konto Google
  3. W „get_invoices_printed”:
    • Filters: Status = „printed”
  4. W „get_invoices_sent”:
    • Filters: Status = „sent”

3.6 Merge node – łączenie wyników

  1. Dodaj node Merge
  2. Nazwij go „un_paid_invoices”
  3. Połącz oba node’y Google Sheets do niego

3.7 Code node – sprawdzanie czy opłacona

  1. Dodaj node Code
  2. Nazwij go „check_if_paid”
  3. Wklej kod:
// Pobierz wszystkie items z un_paid_invoices
const unpaidInvoices = $('un_paid_invoices').all();
// Pobierz payment data
const paymentData = $('payment').first().json;

console.log('Liczba nieopłaconych faktur:', unpaidInvoices.length);
console.log('Szukana faktura:', paymentData.invoice_number);

// Sprawdź każdy item z nieopłaconych faktur
for (let i = 0; i < unpaidInvoices.length; i++) {
  const invoice = unpaidInvoices[i].json;
  
  console.log(`Sprawdzam fakturę ${i + 1}:`, invoice["Numer faktury"]);
  
  // Dopasuj numer faktury
  if (invoice["Numer faktury"] === paymentData.invoice_number) {
    console.log('Znaleziono dopasowanie!');
    
    return [{
      json: {
        found: true,
        invoice_id: invoice.ID,
        invoice_number: invoice["Numer faktury"],
        client_name: invoice["Nazwa klienta"],
        amount_due: invoice["Pozostało do zapłaty"],
        payment_amount: paymentData.amount,
        payment_date: paymentData.paid_date,
        matched_invoice: invoice
      }
    }];
  }
}

// Jeśli nie znaleziono
return [{
  json: {
    found: false,
    searched_invoice: paymentData.invoice_number,
    searched_client: paymentData.client,
    available_invoices: unpaidInvoices.map(item => item.json["Numer faktury"]),
    message: "Nie znaleziono dopasowanej faktury"
  }
}];

3.8 IF node – decyzja

  1. Dodaj node IF
  2. Skonfiguruj warunek:
    • Left Value: {{ $json.found }}
    • Operation: Boolean → is true

3.9 HTTP Request – oznaczanie w inFakt

  1. Dodaj node HTTP Request
  2. Nazwij go „infakt_mark_as_paid”
  3. Skonfiguruj:
    • Method: POST
    • URL: https://api.infakt.pl/api/v3/async/invoices/{{ $json.matched_invoice.uuid }}/paid.json
    • Headers:
      • Name: X-inFakt-ApiKey
      • Value: [TWÓJ_KLUCZ_API_INFAKT]
    • Body Type: JSON
    • JSON Body:
    json{ "paid_date": "{{ $json.payment_date.split('.').reverse().join('-') }}" }

3.10 Mailgun – powiadomienie

  1. Dodaj node Mailgun
  2. Nazwij go „send_notification”
  3. Skonfiguruj:
    • From Email: noreply@twojafirma.pl
    • To Email: twoj@email.pl
    • Subject: Faktura {{ $('check_if_paid').item.json.matched_invoice['Numer faktury'] }} została opłacona
    • HTML: (możesz użyć szablonu z przykładu)

3.11 Gmail – oznacz jako przeczytany

  1. Dodaj node Gmail
  2. Nazwij go „mark_message_as_read”
  3. Skonfiguruj:
    • Operation: Mark as Read
    • Message ID: {{ $('Gmail Trigger').first().json.id }}

Krok 4: Połączenia między node’ami

Połącz node’y zgodnie ze schematem:

  • Gmail Trigger → Basic LLM Chain
  • Basic LLM Chain → payment
  • payment → oba Google Sheets
  • oba Google Sheets → un_paid_invoices
  • un_paid_invoices → check_if_paid
  • check_if_paid → If
  • If (true) → infakt_mark_as_paid → send_notification → mark_message_as_read
  • If (false) → No Operation

Krok 5: Testowanie

  1. Wyślij sobie testowego maila z banku
  2. Uruchom workflow ręcznie (Execute Workflow)
  3. Sprawdź każdy krok w podglądzie
  4. Upewnij się, że faktura została oznaczona w inFakt

Krok 6: Aktywacja

  1. Kliknij przełącznik Active w prawym górnym rogu
  2. Workflow będzie teraz działał automatycznie co minutę

Wskazówki i rozwiązywanie problemów

Klucz API inFakt

Znajdziesz go w inFakt: Ustawienia → API → Wygeneruj klucz

Format daty

Zwróć uwagę na konwersję daty z polskiego formatu (DD.MM.YYYY) na format API (YYYY-MM-DD)

Debugowanie

  • Używaj console.log() w Code node’ach
  • Sprawdzaj zakładkę „Output” każdego node’a
  • Testuj na pojedynczych mailach

Różne formaty maili bankowych

Dostosuj prompt w LLM Chain do formatu swojego banku

TLDR

Nie masz teraz czasu na pełną lekturę? Poniżej znajdziesz podsumowanie najważniejszych wniosków z całego artykułu.

  • Automatycznie odczytuje maile z banku, wyciąga z nich numer faktury/klienta/kwotę, znajduje pasującą FV w Google Sheets (zaciągniętą z inFakt), oznacza ją przez API jako „opłacona”, wysyła powiadomienie i oznacza mail jako przeczytany.

  • n8n, Gmail (trigger + label), Google Sheets, inFakt API, model LLM przez OpenRouter (do parsowania maili), opcjonalnie Mailgun do powiadomień.

  • AI lepiej znosi różne formaty maili, mniej kodu do utrzymania, tani w użyciu przy tej skali; gdy bank zmieni szablon – aktualizujesz prompt, a nie zestaw regexów.

  • Z osobnej integracji z inFakt → Sheets (arkusz „Infakt” z m.in. numerem FV, statusem, UUID, „Pozostało do zapłaty”). Workflow łączy statusy „sent/printed” jako „nieopłacone”.

  • Po numerze faktury wyciągniętym z maila (LLM). W nodzie check_if_paid porównuje invoice_number z kolumną „Numer faktury”. Możesz dodać walidację kwoty/klienta jako drugi warunek.

  • HTTP POST do:
    /api/v3/async/invoices/{{ uuid }}/paid.json
    z paid_date w formacie YYYY-MM-DD (konwersja z DD.MM.YYYY w Code node). Klucz API podany w nagłówku.

  • Gałąź If (false) nic nie zmienia w inFakt. Możesz dodać alternatywę: wyślij alert/Slack/email z danymi z maila i listą dostępnych numerów do ręcznego sprawdzenia.

  • Gmail Trigger co minutę na etykiecie (np. „Wpłaty-Bank”). Klucze API trzymaj w Credentials n8n. Ogranicz źródła: filtruj po nadawcy, etykiecie i rozważ whitelistę domen banku.

  • Koszt LLM przez OpenRouter jest minimalny przy krótkich promptach. Reszta to wywołania API i odczyt z Sheets. Działa w sekundach; realne opóźnienie to polling Gmail (do 1 min).

  • Tak — regexami. Ale będziesz częściej aktualizować wzorce przy zmianach formatów maili. Hybryda (AI + proste regexy dla sanity-check, np. numeru FV i kwoty) jest często najlepsza.

Marek Golan

CEO Dogtronic, Prezes ESTA Cluster, dumny opiekun Borysa. Ma prawie 20 lat doświadczenia w branży IT zarówno jako programista jak i właściciel firm technologicznych.

Przekształć
swoje pomysły
w rzeczywistość

Skontaktuj się z nami i pozwól nam sprawdzić jak możemy Ci pomóc.

Najciekawsze treści na Blogu

Zobacz wszystkie