furs-fiscalization
Python library for simplified communication with FURS (Finančna uprava Republike Slovenije).
Installation
$ pip install furs_fiscalization
Logging
This library uses Python's standard logging module and does not call logging.basicConfig() (so it plays nicely with Odoo's logging configuration).
Odoo integration
By default, logs from this package propagate to the application's root logger. In Odoo, they will appear in the Odoo logs if your Odoo log level includes them.
Dedicated log file (optional)
You can enable a rotating log file either via environment variables:
FURS_FISCALIZATION_LOG_FILE(path to the log file)FURS_FISCALIZATION_LOG_LEVEL(e.g.DEBUG,INFO)
Or from code (call once at startup):
from furs_fiscalization.logging_utils import configure_logging
configure_logging(log_file="/var/log/odoo/furs_fiscalization.log", level="DEBUG")
Trust and verification
The FURS endpoints (blagajne.fu.gov.si, blagajne-test.fu.gov.si) are not signed by a public CA;
the library ships their anchors in furs_fiscalization/certs/furs-ca-bundle.pem (SIGOV-CA and
SI-TRUST Root). TLS verification against that bundle is available but off by default
(verify_tls=False, the behaviour of releases before 1.0.15 - only urllib3's
InsecureRequestWarning is silenced); pass verify_tls=True to enable it. Verification of the signed responses is available but off by default
(verify_response_signature=False), so a FURS certificate rotation can never block
fiscalization. With verify_response_signature=True the certificate FURS puts into the x5c
JWS header must chain to the same bundle, be valid and carry the expected common name
(DavPotRac in production, DavPotRacTEST on the test system); only then is the RS256
signature verified. A failed check raises ResponseVerificationException and the response is
discarded.
api = FURSInvoiceAPI(p12_path='my_cert.p12', p12_password='cert_pass', production=True,
verify_tls=True, verify_response_signature=True)
Both anchors can be replaced with the ca_bundle= and signer_common_name= keyword arguments
of any API class, for example when FURS rotates its certificates before a new release ships.
The protective mark (ZOI) is computed exactly as the FURS technical documentation prescribes:
SHA256withRSA (PKCS#1 v1.5, deterministic) over tax number, dd.MM.yyyy HH:mm:ss, invoice
number, business premise, electronic device and the amount with two decimals, reduced with MD5.
Quick Start
Registering Immovable Business Premise
Registering new Business Premises is simple. But you will need to obtain certain information from your client such as:
- Real Estate Cadastral Number
- Real Estate Building Number
- Real Estate Building Section Number
One thing you should also keep in mind is your premise address - if your premise is located at Tovarniška ulica you will need to pass house number 12 and additional number A as separate parameters. If your street does not have additional house number/letter just pass None.
from furs_fiscalization.api import FURSBusinessPremiseAPI
api = FURSBusinessPremiseAPI(p12_path = 'my_cert.p12',
p12_password = 'cert_pass',
production = True, request_timeout=2.0)
api.register_immovable_business_premise(tax_number = 10128085,
premise_id = 'PP001',
real_estate_cadastral_number = 1322,
real_estate_building_number = 1,
real_estate_building_section_number = 1,
street = 'Tovarniška ulica',
house_number = '12',
house_number_additional = 'A',
community = 'Krško',
city = 'Krško',
postal_code = '8270',
validity_date = datetime.now() - timedelta(days=60),
software_supplier_tax_number = 10128085,
foreign_software_supplier_name = None,
special_notes = '')
NOTE: As of 23.11.2015 FURS does require you to send some kind of Special note to them. Empty string will raise invalid JSON on their side.
Registering Movable Business Premise
In order to register Movable Business Premise you will need to define one of three types:
- TYPE_MOVABLE_PREMISE_A: Movable object such as vehicle, movable stand etc.
- TYPE_MOVABLE_PREMISE_B: Object at permanent location such as news stand, market stand etc.
- TYPE_MOVABLE_PREMISE_C: Individual electronic device in cases when the company does not use other business premises
from furs_fiscalization.api import FURSBusinessPremiseAPI, TYPE_MOVABLE_PREMISE_A
api = FURSBusinessPremiseAPI(p12_path = 'my_cert.p12',
p12_password = 'cert_pass',
production = False,
request_timeout = 2.0)
api.register_movable_business_premise(tax_number = 10128085,
premise_id = 'PP001',
movable_type = TYPE_MOVABLE_PREMISE_A,
validity_date = datetime.now() - timedelta(days=60),
software_supplier_tax_number = 10128085,
foreign_software_supplier_name = None,
special_notes = '')
Calculate Invoice ZOI - Protected ID
At the end of every Invoice your should print ZOI (Protected ID). To obtain it follow the next procedure:
from furs_fiscalization.api import FURSInvoiceAPI
api = FURSInvoiceAPI(p12_path = 'my_cert.p12',
p12_password = 'cert_pass',
production = False,
request_timeout = 1.0)
date_issued = datetime.now()
zoi = api.calculate_zoi(tax_number = 10128085,
issued_date = date_issued,
invoice_number = '11',
business_premise_id = 'PP001',
electronic_device_id = 'BLAG1',
invoice_amount = Decimal('19.15'))
Generate Data for QR/Code128/PDF417
You're supposed to print QR Code/Code128 or PDF 417 on every invoice after the ZOI. To obtain the data for QR/Code128/PDF417 perform the following method call on FURSInvoiceAPI object.
qr_data = api.prepare_printable(tax_number = 10128085,
zoi = zoi,
issued_date = date_issued)
Get EOR From FURS
To obtain FURS EOR code - UniqueID, you'll have to call the following method. It provides several other parameters, for issuing invoice storno and special tax rules. Please read the full documentation.
eor = api.get_invoice_eor(zoi = zoi,
tax_number = 10128085,
issued_date = date_issued,
invoice_number = '11',
business_premise_id = 'PP001',
electronic_device_id = 'BLAG1',
invoice_amount = 66.71,
low_tax_rate_base = 35.14,
low_tax_rate_amount = 3.34,
high_tax_rate_base = 23.14,
high_tax_rate_amount = 5.09,
lowered_tax_rate_base = None,
lowered_tax_rate_amount = None,
operator_tax_number = 12345678)
Contact
Hermes d.o.o.
Metadata
Release files for furs-fiscalization 1.0.15
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| furs_fiscalization-1.0.15.tar.gz | 30.8 kB | Without provenance | Details for furs_fiscalization-1.0.15.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| furs_fiscalization-1.0.15-py3-none-any.whl | Python 3 | none | any | Details for furs_fiscalization-1.0.15-py3-none-any.whl |
Total release size: 53.5 kB
Release files / furs_fiscalization-1.0.15.tar.gz
| Download URL | furs_fiscalization-1.0.15.tar.gz |
|---|---|
| Size | 30.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d62310613a890ddd0f6838268be2a1457896ba666aad209a2ce6b2753ddce508
|
|
BLAKE2b-256 checksum How to use checksums |
5134971fae2530761bd7780182cb0bb718c57afb710611bd1ed43e07859808de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|
Release files / furs_fiscalization-1.0.15-py3-none-any.whl
| Download URL | furs_fiscalization-1.0.15-py3-none-any.whl |
|---|---|
| Size | 22.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b69246b1989eee0692ed139deaa7a4d10eb41d60b91a1447daa89786450409c7
|
|
BLAKE2b-256 checksum How to use checksums |
0c10134fe5562347b89044e98524eafc762d989c32f20a721b855fa44d46ed03
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|
Security for furs-fiscalization 1.0.15
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.