Zastosowanie
Skrypt RPA umożliwia automatyzację procesu pobierania faktur z platformy Krajowego Systemu e-Faktur KSeF bezpośrednio do systemu Comarch BPM. Rozwiązanie to eliminuje konieczność ręcznego importu, zapewniając ciągłość zasilania punktów ACD nowymi dokumentami oraz odpowiednią reakcję na limity wydajnościowe serwerów ministerialnych.
Zawartość przykładu
- Skrypt C#: Implementacja przygotowana w języku C# z wykorzystaniem obiektów globalnych systemu (Globals.ACD, Globals.Common).
Zasada działania
Głównym zadaniem skryptu jest nawiązanie połączenia z serwerami ministerialnymi i przekazanie dokumentów do zdefiniowanego punktu ACD oraz określonego typu obiegu.
- Konfiguracja Punktu ACD: Możliwość wskazania konkretnej skrzynki odbiorczej (np. Dokumenty z KSeF – (A)FZ.
- Automatyczne przypisanie do obiegu: Zaimportowane faktury trafiają do konkretnego procesu, np. „Faktura kosztowa”.
- Mechanizm Retry (Ponawianie): Odporność na błędy komunikacji poprzez automatyczne podejmowanie do 3 prób importu w przypadku problemów.
- Obsługa limitów (Błąd 429): W przypadku przeciążenia serwerów KSeF, skrypt wstrzymuje pracę na 5 minut przed kolejną próbą.
Skrypt działa w oparciu o pętlę sterowaną odpowiedziami z API KSeF:
- Inicjalizacja: Resetuje poprzednie ustawienia filtrów metodą ResetKSeFFilters(), aby uniknąć konfliktów w sesji Globals.ACD.
- Konfiguracja celu: Ustawia Punkt ACD oraz Obieg jako parametry kontekstu importu.
- Import i weryfikacja: Wywołuje metodę ImportKSeFDocuments() i analizuje wynik.
- Jeśli wynik wynosi 0, import zakończył się sukcesem.
- Jeśli wynik wynosi -2, skrypt rozpoznaje błąd 429 (przekroczenie limitu) i uruchamia pauzę przed ponowieniem.
Parametry konfiguracyjne
| Zmienna | Opis |
|---|---|
| maxRetries | Maksymalna liczba prób w przypadku wystąpienia błędów tymczasowych (domyślnie: 3). |
| baseWaitTimeSeconds | Podstawowy czas oczekiwania (90s) używany do wyliczania pauzy po otrzymaniu limitu żądań. |
| punktACD | Nazwa punktu wejścia dla dokumentów w systemie. |
| nazwaObiegu | Nazwa procesu, w którym pojawią się zaimportowane faktury. |
Techniczne aspekty kodu
- Logowanie Startu: Skrypt rejestruje postępy w Globals.Common.Trace, co jest kluczowe dla monitorowania pracy automatu w logach systemowych.
- Exponential Backoff: Skrypt stosuje wykładniczy wzrost czasu oczekiwania. Przy kolejnych próbach po błędzie 429 czas pauzy wynosi odpowiednio: 90s, 180s i 360s. Dzięki temu automat „cierpliwie” czeka na odblokowanie dostępu do API.
- Bezpieczeństwo sesji: W kodzie przewidziano opcjonalne resetowanie filtrów (ResetKSeFFilters) oraz możliwość wymuszenia konkretnego zakresu dat pobierania faktur (SetKSeFFilters), co zapobiega pobieraniu duplikatów.
- Ochrona przed zapętleniem: Jeśli skrypt napotka nieznany błąd (inny niż limit żądań), przerywa pętlę (break), umożliwiając administratorowi analizę kodu błędu w logach.
Konfiguracja przykładu
Aby skrypt mógł poprawnie realizować automatyczny import, niezbędna jest odpowiednia konfiguracja:
- Utworzenie punktu ACD – Import dokumentów zakupu z KSeF – gotowe przykłady (Punkt ACD, Typ obiegu), z których można skorzystać: Przykłady procesów biznesowych
- Wskazanie w pliku BMP.exe.config w kluczu RPAFolderPath, w sekcji <appSettings> ścieżki do folderu z Comarch BPM Desktop, w którym system przechowuje skompilowane skrypty RPA.
- W oknie Konfiguracji automatycznego trybu pracy:
- Dodanie skryptu: Skrypt należy wkleić w zakładce „Edytor skryptów”.
- Kompilacja: Niezbędne jest użycie przycisku
[Kompiluj i zapisz], aby system zweryfikował poprawność kodu i przygotował go do wykonania przez RPA. - Zapis: Po poprawnej kompilacji wymagane jest zapisanie ustawień punktu za pomocą przycisku
[Zapisz].

Gotowy Skrypt C#
using System;
using System.Threading;
using System.Net;
using System.Net.Mail;
// --- KONFIGURACJA AUTOMATU ---
int lastErrorId = 0;
ACDError[] errorsArray = null;
string nazwaPunktuKSeF = "Dokumenty z KseF - (A)FZ";
string nazwaObiegu = "Faktura kosztowa (Elementy)_KSEF"; // <-- WPISZ TUTAJ NAZWĘ OBIEGU
Globals.ACD.Show();
Globals.Common.Trace("=== START: Automatyczny nadzór KSeF (Cykl 5 min) ===");
try
{
while (true)
{
// 1. Monitorowanie błędów na punkcie ACD
Globals.ACD.GetErrorList(ref errorsArray);
if (errorsArray != null && errorsArray.Length > 0)
{
if (lastErrorId != errorsArray[0].Id)
{
lastErrorId = errorsArray[0].Id;
string errorMsg = "Błąd techniczny ACD: " + errorsArray[0].Message;
Globals.Common.Trace(errorMsg);
SendMail("Alert ACD", errorMsg);
}
}
// 2. Ustawienie punktu ACD
Globals.ACD.SetPoint(nazwaPunktuKSeF, 1);
// --- NOWA METODA: Ustawienie typu obiegu ---
int flowResult = Globals.ACD.SetDocumentFlow(nazwaObiegu);
if (flowResult == 0)
{
Globals.Common.Trace("Pomyślnie ustawiono obieg: " + nazwaObiegu);
}
else if (flowResult == -2)
{
Globals.Common.Trace("BŁĄD: Obieg '" + nazwaObiegu + "' nie istnieje!");
}
else
{
Globals.Common.Trace("BŁĄD: Nieoczekiwany błąd przy ustawianiu obiegu (kod: " + flowResult + ")");
}
// 3. Proces importu z KSeF z obsługą limitów
try
{
Globals.Common.Trace("Próba pobrania dokumentów z KSeF...");
int maxRetries = 3;
int retryCount = 0;
int waitTimeSeconds = 300; // 5 minut
bool importSuccess = false;
while (!importSuccess && retryCount < maxRetries)
{
int importResult = Globals.ACD.ImportKSeFDocuments();
if (importResult == 0)
{
Globals.Common.Trace("Import faktur z KSeF zakończony sukcesem");
importSuccess = true;
Globals.ACD.RefreshList();
// Sprawdzenie czy są jakiekolwiek dokumenty na liście
if (Globals.ACD.SelectDocument(0) >= 0)
{
Globals.Common.Trace("Wykryto dokumenty. Generowanie do DMS...");
Globals.ACD.GenerateDMSDocuments();
Globals.Common.Trace("Generowanie zakończone pomyślnie.");
}
else
{
Globals.Common.Trace("Brak nowych dokumentów w KSeF.");
}
}
else if (importResult == -2)
{
retryCount++;
string retryMsg = $"Przekroczono limit żądań KSeF (429 Too Many Requests). " +
$"Próba {retryCount}/{maxRetries}. " +
$"Oczekiwanie {waitTimeSeconds} sekund (5 minut) przed ponowną próbą...";
Globals.Common.Trace(retryMsg);
if (retryCount < maxRetries)
{
Thread.Sleep(waitTimeSeconds * 1000);
}
else
{
string failMsg = "Osiągnięto maksymalną liczbę prób. " +
"Import faktur z KSeF nie powiódł się. " +
"Spróbuj ponownie później.";
Globals.Common.Trace(failMsg);
SendMail("KSeF Rate Limit", failMsg);
}
}
else
{
string statusMsg = $"Błąd importu faktur z KSeF. Kod błędu: {importResult}";
Globals.Common.Trace(statusMsg);
SendMail("Błąd połączenia KSeF", statusMsg);
break;
}
}
}
catch (Exception exIn)
{
Globals.Common.Trace("Błąd podczas pętli importu: " + exIn.Message);
}
// --- USPRAWNIONE OCZEKIWANIE ---
Globals.Common.Trace("Cykl zakończony. Następne sprawdzenie za 5 minut...");
for (int i = 0; i < 300; i++)
{
Thread.Sleep(1000); // Czekaj 1 sekundę (umożliwia responsywność interfejsu)
}
}
}
catch (Exception ex)
{
Globals.Common.Trace("KRYTYCZNY BŁĄD SKRYPTU: " + ex.Message);
SendMail("KSeF Fatal Error", ex.Message);
}
// --- FUNKCJA WYSYŁAJĄCA POWIADOMIENIA E-MAIL ---
string SendMail(string temat, string tresc)
{
try
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress ("XXX@firma-klienta.pl"); // Adres e-mail nadawcy (automatu)
mail.To.Add ("YYY@firma-klienta.pl"); // Adres e-mail do powiadomień o błędach
mail.Subject = "DMS KSeF Alert: " + temat;
mail.Body = "Zdarzenie zarejestrowane przez automat KSeF:\n\n" + tresc;
mail.BodyEncoding = System.Text.Encoding.UTF8;
SmtpClient smtp = new SmtpClient ("smtp.serwer-klienta.pl"); // Serwer SMTP klienta
smtp.Port = 587; // Port serwera (standardowo 587 lub 465)
smtp.Credentials = new NetworkCredential ("użytkownik", "hasło"); //Podać login i hasło
smtp.EnableSsl = true;
smtp.Send(mail);
return "";
}
catch (Exception ex)
{
Globals.Common.Trace("Błąd wysyłki powiadomienia e-mail: " + ex.Message);
return ex.Message;
}
}




