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):
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ć ;-)
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.
CAVEAT: ŻADNYCH GWARANCJI. UŻYWASZ NA WŁASNE RYZYKO.
=======================================================
Kontakt
Jeżeli potrzebujesz się ze mną skontaktować, użyj poniższego kodu QR.
Comments
Post a Comment