Skrypty w Pythonie

Ułatwiające wypełnianie JPK_VAT7M od jpklibre


(Oj dawno nic nie pisałem na blogu!)

Do niedawna robiłem JPK za pomocą arkusza w Excelu, ale na nowym komputerze Excel 64-bitowy, więc arkusz nie działa. Dlaczego – nie będę się rozpisywać. Pora na rozwiązanie uniwersalne, które będzie działać niezależnie od systemu operacyjnego – skoroszyt jpklibre, bo komputerów z Windowsem mam 1, a z systemem Linux – kilka.

Można oczywiście wypełnić arkusz ręcznie, ale to jest mozolne. Można też poddać w wątpliwość sens arkuszy, gdy pliki JPK powinno generować oprogramowanie księgowe. Owszem, moje generuje, ale uparło się przy tym robić korekty, które nic nie mają wspólnego z rzeczywistością, więc robię sam - półautomatycznie.

Żeby nie wypełniać jpklibre całkowicie ręcznie, można skorzystać z rozwiązania Scriptforge, które dostępne jest "fabrycznie" dla LibreOffice Calc od wersji 7.3 LibreOffice. Trzeba tylko doinstalować rozszerzenie APSO, które ułatwi uruchamianie makr napisanych w Pythonie.

Makra w Pythonie tworzone przez użytkownika LibreOffice Calc znajdują się pod tą ścieżką:
%APPDATA%\LibreOffice\4\user\Scripts\python, gdzie %APPDATA% tak naprawdę oznacza C:\Users\[twoja_nazwa_uzytkownika]\Appdata\Roaming, a jeśli pod Scripts nie ma folderu 'python', to można/trzeba go ręcznie utworzyć. Tam zapisz swój plik z rozszerzeniem *.py, który będzie modułem zawierającym Twoje makra w Pythonie.

W systemie Linux ich miejsce jest w katalogu home użytkownika:

$HOME/.config/libreoffice/4/user/Scripts/python

Jeżeli chodzi strukturę tego pliku, to trzeba pamiętać o pewnych obowiązkowych elementach:

from scriptforge import CreateScriptService
import csv
doc = CreateScriptService("Calc")

Czyli informujemy LibreOffice, że chcemy korzystać ze Scriptforge, tworzymy odwołanie do aktualnie otwartego arkusza jpklibre, a ponadto importujemy obsługę plików CSV, które będziemy wczytywać.

Każde makro w naszym module jest pythonową funkcją, więc należy je utworzyć tak:

def Sprzedaz(args=None):
    # Kod Twojego makra, pamiętaj o czterech spacjach wcięcia 
    # do końca funkcji i coraz głębszych wcięciach dla kolejnych 
    # funkcji.

Na końcu modułu musi się znaleźć podsumowanie, jakie zawiera makra, czyli:

g_exportedScripts = (Sprzedaz, Zakupy,)

Mój moduł zawiera 2 makra. Bez powyższej linijki kodu Calc nie będzie ich widzieć.

Moje Makro dla arkusza Sprzedaż wczytuje plik csv, gdzie wartości są rozdzielone przecinkami, i wszystkie są ujęte w cudzysłowy proste, czyli są tekstowe. Plik CSV (zapisany z kodowaniem utf-8 i z wszystkimi wartościami tekstowymi ujętymi w cudzysłowy proste), przygotowany rzecz jasna też w Calcu, o kolumnach, zaczynając od B, mających następujące nazwy (których w samym pliku nie ma, ale podaję je dla orientacji):

Data VAT Data księgowania Nr archiwalny Nazwa kontrahenta NIP kontrah. Rodzaj dok Numer dokumentu Wartość Netto Wartość VAT Wartość Brutto

Z kolumn D i G makro w ogóle nie korzysta, ale żeby oszczędzić czas, nie wycinam ich. W pierwszej kolumnie przygotowywanego pliku CSV umieszczam oznaczenia, dzięki którym makro orientuje się, do których kolumn wstawić wartości liczbowe. Ponieważ świadczę usługi w kraju i za granicą (Unia i poza nią), a także niektóre usługi importuję, używam następujących wewnętrznych oznaczeń na potrzeby makra: 

s23, sn, siuz

s23 nie wymaga wyjaśnień, sn to eksport usług do Unii i poza nią, dlatego kod makra sprawdza, czy w polu NIP jest 'BRAK', bo jeśli jest NIP, to wartość sprzedaży trzeba oprócz K_11 powielić również do K_12. Jeżeli coś nie spełnia kryteriów s23 i sn to jest siuz, czyli import usług zagranicznych, które, tak na marginesie, trzeba zaksięgować również po stronie zakupów, jeżeli są wykorzystywane do działalności i wtedy VAT się znosi. :-) Logika tego makra i jego struktura odpowiada mojemu zakresowi działalności (sprzedaż i import usług niematerialnych), a jeśli chcąc z niego korzystać, masz inny profil działalności i musisz wstawiać wartości do innych kolumn, to musisz je dostosować.

Co robi makro Sprzedaż:


MIędzy innymi przełącza skoroszyt na arkusz "sprzedaz", aby nie zacząć wypisywać danych na nieprawidłowym arkuszu.

doc.Activate("sprzedaz")

Plik z naszymi transakcjami sprzedaży (przygotowany z zapisów w rejestrach VAT) otwieramy standardowym poleceniem:

f = open('D:\\Documents\\JPK\\s23_04_2022.csv', 'r', encoding='utf-8')

W systemie Windows w ścieżce muszą być podwójne lewe ukośniki.


Makro wykorzystuje zmienną licznik, której wartość początkową ustawiamy na 6, ponieważ arkusz zaczynamy wypełniać od 6 wiersza. Licznik jest też wykorzystywany do tworzenia współrzędnych poszczególnych komórek, gdzie wstawiamy dane.

Blok with z pętlą for i warunkami if - elif - else odczytuje cały plik csv korzystając z zaimportowanego na początku modułu csv, przetwarza i wpisuje dane do komórek w odpowiednich kolumnach.

Aby utworzyć współrzędną komórki, tworzę listę coords, gdzie pierwszy element to kolumna, (tutaj "C"), a drugi to wartość licznika zamieniona na ciąg tekstowy (funkcja str). Python nie może ot tak połączyć tekstu z liczbą, taka próba spowoduje wyrzucenie błędu.

coords = ["C", str(licznik)]

Mając przygotowaną współrzędną, możemy wstawiać wartość:

doc.SetValue(''.join(coords), row[5])

SetValue ma dwa argumenty oddzielone przecinkami, współrzędna jako ciąg tekstowy i po przecinku, wstawiana wartość, w tym przykładzie row[5], bo wartość NIP kontrahenta musimy wstawić jako pierwszą w kolumnie C, ale w naszym wejściowym pliku CSV jest ona w kolumnie F, czyli 5, bo – uwaga – Python liczy indeksy zaczynając od zera. Ale powiesz, zaraz, nie ma współrzędnej, tylko znów jakaś funkcja join i to jeszcze dość ciekawie wyglądająca. Dwa proste apostrofy (bez spacji czy czegokolwiek innego między nimi) i kropka informują funkcję, że do scalania nie ma używać żadnych znaków rozdzielających, a w nawiasie podajemy jej listę, której elementy ma scalić (coords), aby powstała współrzędna komórki wymagana przez SetValue.

I tak dalej, aż zaczyna się robić jeszcze ciekawiej, gdy dochodzimy do komórki, gdzie trzeba wstawić wartość liczbową I wtedy mamy na przykład:

doc.SetValue(''.join(coords), float(row[9].replace(",",".")))

Po kolei, bo zacznie głowa boleć. :-) W pliku CSV mamy wartości tekstowe, więc biorąc wartość z bieżącego wiersza, a kolumny o indeksie np. 9, zamieniamy (replace) w niej przecinek na kropkę, bo Python w liczbach zmiennoprzecinkowych stosuje dziesiętną kropkę, a nie przecinek. Ale to nadal jest ciąg tekstowy, więc Python musi zmienić typ danych z tekstu na liczbę zmiennoprzecinkową (funkcja float). A wszystkie te karkołomne operacje tylko po to, by – jeśli mamy polskie ustawienia w Calcu - kropka znów stała się przecinkiem w momencie wstawienia danych do komórki arkusza. Prościej się nie da, bo Python na swoje wewnętrze potrzeby wymaga kropki i koniec.

Makro Zakupy:

Plik CSV zakupów ma taką samą kolejność kolumn, jak plik CSV sprzedaży (zaczynając od kolumny B, zachowałem konwencję z pliku sprzedaży, gdzie pierwsza kolumna zawiera specjalne kody rejestrów VAT). Dopisuję do niego też wykorzystywane w działalności importowane usługi, które (jak wspomniałem  wcześniej) dzięki niezbadanym ścieżkom, jakimi chadzają zasady księgowości/rachunkowości, trzeba księgować również po stronie sprzedaży. Tu (w moim przypadku) logika makra jest znacznie prostsza, wpisujemy wartość netto zakupów i wartość VAT. I to wszystko, a działanie poszczególnych funkcji opisałem w części na temat makra Sprzedaz

Podsumowanie

Jak się okazuje,  kod programu można znacznie szybciej ułożyć w głowie i przelać na ekran i do pliku, niż napisać tekst o nim. :-)  Nawet jeśli Twój przypadek działalności różni się od mojego, to mam nadzieję, że znajdziesz tu inspirację do własnego rozwiązania, albo nauczysz się trochę Pythona.
Kod makr znajdziesz tutaj: http://englishintopolish.eu/MP.py
Niektóre przeglądarki mogą uznać treść za niebezpieczną, więc można też za pomocą:
wget -c http://englishintopolish.eu/MP.py
wget jest też dostępny dla Windowsa, wystarczy doinstalować ;-)

Aktualizacja - VAT UE (17.02.2024)

Właśnie udostępniłem dodatkowe makro Vatue, które na podstawie wypełnionego arkusza sprzedaży JPK generuje prawidłowy plik XML do wysłania przez formularz elektroniczny na stronie MF.

Ponieważ utworzenie, debugowanie / testowanie skryptu kosztowało mnie jednak trochę czasu i wysiłku, możesz to docenić, jeśli używasz Revoluta, zostawiając napiwek: @piotrx97o
Dzięki. :-)



=======================================================
 CAVEAT: ŻADNYCH GWARANCJI. UŻYWASZ NA WŁASNE RYZYKO.
=======================================================


Kontakt


Jeżeli potrzebujesz się ze mną skontaktować, użyj poniższego kodu QR.

















Comments

Popular posts from this blog