commit e7e7f74f9856b9392f237dc40cfd071e8ba10e8f Author: Dariusz Aniszewski Date: Wed May 27 23:00:28 2015 +0200 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae65f9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Python +.env +.pyc +dist +*.egg-info/* + +# PyCharm +.idea/* \ No newline at end of file diff --git a/python-ifirma/__init__.py b/python-ifirma/__init__.py new file mode 100644 index 0000000..b26a56d --- /dev/null +++ b/python-ifirma/__init__.py @@ -0,0 +1 @@ +__author__ = 'dariusz' diff --git a/python-ifirma/ifirma.py b/python-ifirma/ifirma.py new file mode 100644 index 0000000..dec17fb --- /dev/null +++ b/python-ifirma/ifirma.py @@ -0,0 +1,346 @@ +# -*- coding: utf-8 -*- +import binascii +from enum import Enum +import hashlib +import hmac +import json +import datetime + +import requests +import six + + +class VAT(Enum): + VAT_0 = 0.00 + VAT_5 = 0.05 + VAT_8 = 0.08 + VAT_23 = 0.23 + PERCENTAGE = "PRC" + EXEMPT = "ZW" + + +class Client: + __name = None + __id = None + __eu_prefix = None + __street = None + __zip_code = None + __country = None + __city = None + __email = None + __phone_number = None + __is_private = True + + def __init__(self, name, street, zip_code, country, city, _id=None, eu_prefix=None, email=None, phone_number=None, + is_private=True): + self.__name = name + self.__street = street + self.__zip_code = zip_code + self.__country = country + self.__city = city + self.__id = _id + self.__eu_prefix = eu_prefix + self.__email = email + self.__phone_number = phone_number + self.__is_private = is_private + + def get_dict(self): + return { + "Nazwa": self.__name, + "Identyfikator": self.__id, + "PrefiksUE": self.__eu_prefix, + "Ulica": self.__street, + "KodPocztowy": self.__zip_code, + "Kraj": self.__country, + "Miejscowosc": self.__city, + "Email": self.__email, + "Telefon": self.__phone_number, + "OsobaFizyczna": self.__is_private, + } + + def get_json(self): + return json.dumps(self.get_dict()) + + +class Position: + __vat = None + __quantity = None + __base_price = None + __full_name = None + __unit = None + __pkwiu = None + __vat_type = None + __discount = None + + def __init__(self, vat, quantity, base_price, full_name, unit, pkwiu="", vat_type=VAT.PERCENTAGE, discount=0): + self.__vat = vat + self.__quantity = quantity + self.__base_price = base_price + self.__full_name = full_name + self.__unit = unit + self.__pkwiu = pkwiu + self.__vat_type = vat_type + self.__discount = discount + + def get_dict(self): + return { + "StawkaVat": self.__vat.value, + "Ilosc": self.__quantity, + "CenaJednostkowa": self.__base_price, + "NazwaPelna": self.__full_name, + "Jednostka": self.__unit, + "PKWiU": self.__pkwiu, + "TypStawkiVat": self.__vat_type.value, + "Rabat": self.__discount + } + + +class Invoice: + __client = None + __positions = [] + + def set_client(self, client): + self.__client = client + + def add_position(self, position): + self.__positions.append(position) + + def clear_positions(self): + self.__positions.clear() + + def get_dict(self): + return { + "LiczOd": "BRT", + "NumerKontaBankowego": None, + "TypFakturyKrajowej": "SPRZ", + "DataWystawienia": today, + "MiejsceWystawienia": "Warszawa", + "TerminPlatnosci": None, + "SposobZaplaty": "PRZ", + "NazwaSeriiNumeracji": "default", + "NazwaSzablonu": "logo", + "RodzajPodpisuOdbiorcy": "BPO", + "PodpisOdbiorcy": "", + "PodpisWystawcy": "", + "Uwagi": "", + "WidocznyNumerGios": True, + "Numer": None, + "Pozycje": [Position(VAT.VAT_23, 1, 1000, "Nazwa pozycji", "szt", discount=10).get_dict()], + "Kontrahent": Client("Imię Nazwisko", "Ulica", "01-234", "Polska", "Warszawa").get_dict() + } + + +class iFirmaAPI(): + username = None + invoice_key_name = "faktura" + invoice_key_value = None + + user_key_name = "abonent" + user_key_value = None + + @classmethod + def __sanitize_key_value(cls, text): + try: + return binascii.unhexlify(text) + except TypeError: + return text + + @classmethod + def __get_hmac_of_text(cls, key, text): + return hmac.new(key, six.b(text), hashlib.sha1).hexdigest() + + def __init__(self, _username, _invoice_key_value, _user_key_value=None): + self.username = _username + self.invoice_key_value = self.__sanitize_key_value(_invoice_key_value) + self.user_key_value = self.__sanitize_key_value(_user_key_value) + + def __create_invoice_and_return_id(self, data, url): + request_content = json.dumps(data, separators=(',', ':')) + request_hash_text = "{}{}{}{}".format( + url, + self.username, + self.invoice_key_name, + request_content, + ) + headers = { + "Accept": "application/json", + "Content-type": "application/json; charset=UTF-8", + "Authentication": "IAPIS user={}, hmac-sha1={}".format( + self.username, + self.__get_hmac_of_text(self.invoice_key_value, request_hash_text) + ) + } + response = requests.post(url, data=request_content, headers=headers) + response_dict = json.loads(response.content.decode("utf-8"), 'utf-8') + if response_dict["response"].get("Identyfikator"): + invoice_id = response_dict["response"]["Identyfikator"] + return invoice_id + else: + code = response_dict["response"]["Kod"] + info = response_dict["response"]["Informacja"] + if code == 201 and info.find(u"musi być zgodna z miesiącem i rokiem księgowym") > 0: + self.change_billing_month_to_next() + return self.__create_invoice_and_return_id(data, url) + return None + + def generate_invoice(self, data): + url = "https://www.ifirma.pl/iapi/fakturakraj.json" + return self.__create_invoice_and_return_id(data, url) + + def generate_proforma_invoice(self, data): + url = "https://www.ifirma.pl/iapi/fakturaproformakraj.json" + + return self.__create_invoice_and_return_id(data, url) + + def __download_pdf(self, file_path, url): + request_hash_text = "{}{}{}".format( + url, + self.username, + self.invoice_key_name, + ) + headers = { + "Accept": "application/pdf", + "Content-type": "application/pdf; charset=UTF-8", + "Authentication": "IAPIS user={}, hmac-sha1={}".format( + self.username, + self.__get_hmac_of_text(self.invoice_key_value, request_hash_text) + ) + } + resp = requests.get(url, headers=headers) + content = resp.content + print(content) + file = open(file_path, "wb") + file.write(content) + file.close() + + def download_invoice_pdf(self, invoice_id, file_path): + url = "https://www.ifirma.pl/iapi/fakturakraj/{}.pdf".format(invoice_id) + self.__download_pdf(file_path, url) + + def download_proforma_invoice_pdf(self, invoice_id, file_path): + url = "https://www.ifirma.pl/iapi/fakturaproformakraj/{}.pdf".format(invoice_id) + self.__download_pdf(file_path, url) + + def create_invoice_from_proforma(self, proforma_id): + url = "https://www.ifirma.pl/iapi/fakturaproformakraj/add/{}.json".format(proforma_id) + request_hash_text = "{}{}{}".format( + url, + self.username, + self.invoice_key_name, + ) + headers = { + "Accept": "application/json", + "Content-type": "application/json; charset=UTF-8", + "Authentication": "IAPIS user={}, hmac-sha1={}".format( + self.username, + self.__get_hmac_of_text(self.invoice_key_value, request_hash_text) + ) + } + resp = requests.get(url, headers=headers) + response_dict = json.loads(resp.content.decode("utf-8")) + invoice_id = response_dict["response"]["Identyfikator"] + return invoice_id + + def change_billing_month_to_next(self): + data = { + "MiesiacKsiegowy": "NAST", + "PrzeniesDaneZPoprzedniegoRoku": False, + } + url = "https://www.ifirma.pl/iapi/abonent/miesiacksiegowy.json" + request_content = json.dumps(data, separators=(',', ':')) + + request_hash_text = "{}{}{}{}".format( + url, + self.username, + self.user_key_name, + request_content + ) + + headers = { + "Accept": "application/json", + "Content-type": "application/json; charset=UTF-8", + "Authentication": "IAPIS user={}, hmac-sha1={}".format( + self.username, + self.__get_hmac_of_text(self.user_key_value, request_hash_text), + ) + } + resp = requests.put(url, data=request_content, headers=headers) + + def get_invoice_details(self, invoice_id): + url = "https://www.ifirma.pl/iapi/fakturakraj/{}.json".format(invoice_id) + request_hash_text = "{}{}{}".format( + url, + self.username, + self.invoice_key_name, + ) + headers = { + "Accept": "application/json", + "Content-type": "application/json; charset=UTF-8", + "Authentication": "IAPIS user={}, hmac-sha1={}".format( + self.username, + self.__get_hmac_of_text(self.invoice_key_value, request_hash_text), + ) + } + resp = requests.get(url, headers=headers) + return json.loads(resp.content) + + def get_pro_forma_details(self, proforma_id): + url = "https://www.ifirma.pl/iapi/fakturaproformakraj/{}.json".format(proforma_id) + request_hash_text = "{}{}{}".format( + url, + self.username, + self.invoice_key_name, + ) + headers = { + "Accept": "application/json", + "Content-type": "application/json; charset=UTF-8", + "Authentication": "IAPIS user={}, hmac-sha1={}".format( + self.username, + self.__get_hmac_of_text(self.invoice_key_value, request_hash_text), + ) + } + resp = requests.get(url, headers=headers) + return json.loads(resp.content) + + +def main(): + api = iFirmaAPI("$DEMO239002", "B4E1FC1D017C5556", _user_key_value="114D8F7A22BC87F8") + + + def change_billing_month(): + api.change_billing_month_to_next() + + def issue_proforma_and_then_invoice_and_download(): + today = "{}".format(datetime.date.today()) + data = { + "LiczOd": "BRT", + "NumerKontaBankowego": None, + "TypFakturyKrajowej": "SPRZ", + "DataWystawienia": today, + "MiejsceWystawienia": "Warszawa", + "TerminPlatnosci": None, + "SposobZaplaty": "PRZ", + "NazwaSeriiNumeracji": "default", + "NazwaSzablonu": "logo", + "RodzajPodpisuOdbiorcy": "BPO", + "PodpisOdbiorcy": "", + "PodpisWystawcy": "", + "Uwagi": "", + "WidocznyNumerGios": True, + "Numer": None, + "Pozycje": [Position(VAT.VAT_23, 1, 1000, "Nazwa pozycji", "szt", discount=10).get_dict()], + "Kontrahent": Client("Imię Nazwisko", "Ulica", "01-234", "Polska", "Warszawa").get_dict() + } + proforma_invoice_id = api.generate_proforma_invoice(data) + api.download_proforma_invoice_pdf(proforma_invoice_id, "inv.pdf") + invoice_id = api.create_invoice_from_proforma(proforma_invoice_id) + api.download_invoice_pdf(invoice_id, "inv2.pdf") + + # change_billing_month() + issue_proforma_and_then_invoice_and_download() + + # api.create_invoice_from_proforma(346723) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python-ifirma/tests.py b/python-ifirma/tests.py new file mode 100644 index 0000000..2b6b516 --- /dev/null +++ b/python-ifirma/tests.py @@ -0,0 +1,35 @@ +from unittest.case import TestCase + +from ifirma import Client + + +class TestClient(TestCase): + def setUp(self): + pass + + def __create_basic_client(self): + self.client = Client( + "name", + "street", + "00-001", + "Polska", + "Warszawa" + ) + + def testClient(self): + self.__create_basic_client() + get_dict = self.client.get_dict() + self.assertIn("Nazwa", get_dict) + self.assertIn("Identyfikator", get_dict) + self.assertIn("PrefiksUE", get_dict) + self.assertIn("Ulica", get_dict) + self.assertIn("KodPocztowy", get_dict) + self.assertIn("Kraj", get_dict) + self.assertIn("Miejscowosc", get_dict) + self.assertIn("Email", get_dict) + self.assertIn("Telefon", get_dict) + self.assertIn("OsobaFizyczna", get_dict) + + def testEmail(self): + self.__create_basic_client() + self.assertIsNone(self.client.get_dict()["Email"]) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b8bb625 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests +six \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9f7397e --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup, find_packages + +setup( + name="python-ifirma", + version="0.0.1", + packages=find_packages(), + install_requires=[ + 'requests', + 'six', + ], + author='Dariusz Aniszewski', + author_email='dariusz@aniszewski.eu', + license='BSD License', + +) \ No newline at end of file