Metadata-Version: 2.4
Name: furs_fiscalization
Version: 1.0.15
Summary: Python library for simplified communication with FURS (Financna uprava Republike Slovenije).
Author-email: "Hermes d.o.o." <info@hermes-solutions.si>
License-Expression: MIT
Project-URL: Homepage, https://github.com/HermesGH/furs-fiscalization
Project-URL: Source, https://github.com/HermesGH/furs-fiscalization
Project-URL: Changelog, https://github.com/HermesGH/furs-fiscalization/blob/master/CHANGELOG.md
Project-URL: Issues, https://github.com/HermesGH/furs-fiscalization/issues
Keywords: FURS,fiscal,fiscal register,davcne blagajne
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: Slovenian
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Accounting
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.20.0
Requires-Dist: PyJWT==2.8.0
Requires-Dist: pyOpenSSL>=17.5.0
Requires-Dist: cryptography>=42.0
Requires-Dist: Pillow
Requires-Dist: qrcode
Dynamic: license-file

# 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):

```python
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.

```python
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.

```python
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


```python
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:

```python
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.

```python
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.

```python
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.**
