Skip to content
This repository has been archived by the owner on Nov 3, 2022. It is now read-only.

Commit

Permalink
Merge pull request #12 from TheAwiteb/auto-VAT
Browse files Browse the repository at this point in the history
- 𝙰𝚞𝚝𝚘𝚖𝚊𝚝𝚒𝚌𝚊𝚕𝚕𝚢 𝚍𝚎𝚍𝚞𝚌𝚝𝚒𝚗𝚐 𝚝𝚑𝚎 𝚝𝚊𝚡 𝚟𝚊𝚕𝚞𝚎 𝚏𝚛𝚘𝚖 𝚝𝚑𝚎 𝚝𝚘𝚝𝚊𝚕 𝚊𝚖𝚘𝚞𝚗𝚝 𝚒𝚏 𝚒𝚝𝚜 𝚟𝚊𝚕𝚞𝚎 𝚒𝚜 `𝙽𝚘𝚗𝚎`.
- 𝚃𝚑𝚎 𝚙𝚘𝚜𝚜𝚒𝚋𝚒𝚕𝚒𝚝𝚢 𝚘𝚏 𝚌𝚑𝚊𝚗𝚐𝚒𝚗𝚐 𝚝𝚑𝚎 𝚝𝚊𝚡 𝚛𝚊𝚝𝚎 𝚟𝚒𝚊 𝚝𝚑𝚎 𝚟𝚊𝚛𝚒𝚊𝚋𝚕𝚎 `𝚟𝚊𝚝_𝚛𝚊𝚝𝚎𝚜`.

## 𝙲𝚘𝚖𝚖𝚒𝚝𝚜
- 7835589: 𝙲𝚑𝚊𝚗𝚐𝚎 𝚝𝚊𝚡 𝚊𝚖𝚘𝚞𝚗𝚝 𝚘𝚏 𝟷𝟷𝟻 𝚏𝚛𝚘𝚖 𝟷𝟻 𝚝𝚘 𝟷𝟽.𝟸𝟻.
- 7ff429a: 𝙰𝚍𝚍 𝚏𝚊𝚝𝚘𝚘𝚛𝚊 𝚕𝚘𝚐𝚘, 𝚞𝚙𝚍𝚊𝚝𝚎 𝚎𝚡𝚊𝚖𝚙𝚕𝚎𝚜 𝚝𝚊𝚡_𝚊𝚖𝚘𝚞𝚗𝚝, 𝚞𝚙𝚍𝚊𝚝𝚎 𝚎𝚡𝚊𝚖𝚙𝚕𝚎𝚜 𝚛𝚎𝚜𝚞𝚕𝚝, 𝚊𝚍𝚍 𝚜𝚘𝚖𝚎 𝚗𝚘𝚝𝚎, 𝚊𝚗𝚍 𝙴𝚡𝚙𝚕𝚊𝚒𝚗 𝚝𝚑𝚎 𝚠𝚘𝚛𝚔 𝚘𝚏 𝚟𝚊𝚛𝚒𝚊𝚋𝚕𝚎𝚜
- bcffb19: 𝙴𝚗𝚊𝚋𝚕𝚎𝚜 𝙰𝚞𝚝𝚘 𝚟𝚊𝚝

## 𝙳𝚒𝚜𝚌𝚞𝚜𝚜𝚒𝚘𝚗𝚜
𝙰𝚞𝚝𝚘 𝚟𝚊𝚝 𝚍𝚒𝚜𝚌𝚞𝚜𝚜𝚒𝚘𝚗 𝚑𝚎𝚛𝚎 #9
  • Loading branch information
TheAwiteb authored Jan 22, 2022
2 parents ea86c41 + 9ff8d0e commit 31eaee5
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 35 deletions.
65 changes: 44 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div align="center">
<img src="https://zatca.gov.sa/ar/E-Invoicing/PublishingImages/header_logo.svg">
<h1>ZATCA (Fatoora) QR-Code Implementation</h1>
<p>An unofficial package help developers to implement ZATCA (Fatoora) QR code easily which required for e-invoicing</p>
<p>An <strong>unofficial package</strong> help developers to implement ZATCA (Fatoora) QR code easily which required for e-invoicing</p>
<a href="https://pypi.org/project/fatoora/">
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/fatoora?color=9cf">
</a>
Expand Down Expand Up @@ -90,16 +91,25 @@ $ python3 setup.py install

**Here the features of the variables of the Fatoora class will be explained**

- Seller’s name.
- Seller’ tax number, which is the VAT registration number.
- Invoice date, which is the timestamp of the electronic invoice.
- Invoice total amount, which is the electronic invoice total **with VAT**.
- Tax amount, which is the VAT total.


| Name | Feature | How will it be in the tag| How will it be when you call|
|--------------|:-----:|:-----:|:-----:|
| seller_name | saved directly without processing | No changes will be made to it | No changes will be made to it |
| tax_number | receives the tax number as a text and a number as well, and save it as string | number as string | number as string|
| invoice_date | receives the date as timestamp or datetime object, or string ISO 8601 Zulu format| string of date as ISO 8601 Zulu format | datetime object |
| total_amount | receives the tax number as a text and a number as well | It is of type str | It is of type float|
| tax_amount | same total_amount | same total_amount | same total_amount|
| total_amount | receives the tax number as a text and a number as well | It is of type str as float | It is of type float|
| tax_amount | same total_amount + **Accept `None` value to get VAT auto from `total_amount`** | same total_amount | same total_amount|


> Note: The `tax_amount` is rounded to the nearest two decimal places, if it is greater than that ( if `tax_amount` == `15` its will be `15.0`)
<!-- | name here | feature here| -->
> Note: If you set `tax_amount` to `None` the `vat_rates` == `0.15` you can change it in `Fatoora` object

<br>
Expand All @@ -113,12 +123,12 @@ fatoora_obj = Fatoora(
seller_name="Awiteb",
tax_number=1234567891, # or "1234567891"
invoice_date=1635872693.3186214, # timestamp or datetime object, or string ISO 8601 Zulu format
total_amount=115, # or 115.0, 115.00, "115.0", "115.00"
total_amount=100, # or 100.0, 100.00, "100.0", "100.00"
tax_amount=15, # or 15.0, 15.00, "15.0", "15.00"
)

print(fatoora_obj.base64)
# AQZBd2l0ZWICCjEyMzQ1Njc4OTEDFDIwMjEtMTEtMDJUMTc6MDQ6NTNaBAUxMTUuMAUEMTUuMA==
# AQZBd2l0ZWICCjEyMzQ1Njc4OTEDFDIwMjEtMTEtMDJUMTc6MDQ6NTNaBAUxMDAuMAUEMTUuMA==
```

### Render A QR Code Image
Expand All @@ -134,16 +144,14 @@ fatoora_obj = Fatoora(
seller_name="Awiteb",
tax_number=1234567891,
invoice_date=1635872693.3186214,
total_amount=115,
total_amount=100,
tax_amount=15,
)

fatoora_obj.qrcode("qr_code.png")
```
<div align="center">

![qr_code.png](https://i.suar.me/gNyBw/t)

<img src="https://i.suar.me/dWOB7/t" alt="qr_code.png">
</div>

#### The content is the invoice url
Expand All @@ -155,14 +163,18 @@ fatoora_obj = Fatoora(
seller_name="Awiteb",
tax_number=1234567891,
invoice_date=1635872693.3186214,
total_amount=115,
total_amount=100,
tax_amount=15,
qrcode_url="https://example.com"
)

fatoora_obj.qrcode("qr_code_with_url.png")
```

<div align="center">
<img src="https://i.suar.me/OLJ3G/t" alt="qr_code_with_url.png">
</div>

### Generate hash (sha256)

```python
Expand All @@ -172,12 +184,12 @@ fatoora_obj = Fatoora(
seller_name="Awiteb",
tax_number=1234567891,
invoice_date=1635872693.3186214,
total_amount=115,
total_amount=100,
tax_amount=15,
)

print(fatoora_obj.hash)
# 7e9b157acb01bb3cc727aecb1210c19a6a2c950589cf9bfa40f0dca57b64b100
# 20b8ddb6ed9cb98be3d8535a1f4f28e35888842c1b0aed0e90c3e7fb51080dd9
```

### Read qr code
Expand All @@ -189,17 +201,17 @@ fatoora_obj = Fatoora(
seller_name="Awiteb",
tax_number=1234567891,
invoice_date=1635872693.3186214,
total_amount=115,
total_amount=100,
tax_amount=15,
)

fatoora_obj.qrcode("qr_code.png")

print(Fatoora.read_qrcode("qr_code.png", dct=True))
# {'seller_name': 'Awiteb', 'tax_number': '1234567891', 'invoice_date': '2021-11-02T17:04:53Z', 'total_amount': '115.0', 'tax_amount': '15.0'}
# {'seller_name': 'Awiteb', 'tax_number': '1234567891', 'invoice_date': '2021-11-02T17:04:53Z', 'total_amount': '100.0', 'tax_amount': '15.0'}

print(Fatoora.read_qrcode("qr_code.png", dct=False))
# AQZBd2l0ZWICCjEyMzQ1Njc4OTEDFDIwMjEtMTEtMDJUMTc6MDQ6NTNaBAUxMTUuMAUEMTUuMA==
# AQZBd2l0ZWICCjEyMzQ1Njc4OTEDFDIwMjEtMTEtMDJUMTc6MDQ6NTNaBAUxMDAuMAUEMTUuMA==

```

Expand All @@ -211,7 +223,7 @@ fatoora_obj = Fatoora(
seller_name="Awiteb",
tax_number=1234567891,
invoice_date=1635872693.3186214,
total_amount=115,
total_amount=100,
tax_amount=15,
)

Expand All @@ -225,15 +237,15 @@ print(fatoora_obj.invoice_date.timestamp())
# 1635861893.0

print(fatoora_obj.json())
# '{"seller_name": "Awiteb", "tax_number": "1234567891", "invoice_date": "2021-11-02T17:04:53Z", "total_amount": "115.0", "tax_amount": "15.0"}'
# '{"seller_name": "Awiteb", "tax_number": "1234567891", "invoice_date": "2021-11-02T17:04:53Z", "total_amount": "100.0", "tax_amount": "15.0"}'

print(fatoora_obj.dict())
# {'seller_name': 'Awiteb', 'tax_number': '1234567891', 'invoice_date': '2021-11-02T17:04:53Z', 'total_amount': '115.0', 'tax_amount': '15.0'}
# {'seller_name': 'Awiteb', 'tax_number': '1234567891', 'invoice_date': '2021-11-02T17:04:53Z', 'total_amount': '100.0', 'tax_amount': '15.0'}

# Use class to get fatoora details by base64

print(Fatoora.base2dict(fatoora_obj.base64))
# {'seller_name': 'Awiteb', 'tax_number': '1234567891', 'invoice_date': '2021-11-02T17:04:53Z', 'total_amount': '115.0', 'tax_amount': '15.0'}
# {'seller_name': 'Awiteb', 'tax_number': '1234567891', 'invoice_date': '2021-11-02T17:04:53Z', 'total_amount': '100.0', 'tax_amount': '15.0'}


```
Expand All @@ -254,12 +266,23 @@ print(fatoora.is_valid_iso8601_zulu_format("2021-11-02T17:04:53Z"))
Question, feature request, discuss about fatoora [here](https://github.com/TheAwiteb/fatoora/discussions)

## Issues
You can report a bug [here](https://github.com/TheAwiteb/fatoora/issues)
You can report a bug from [here](https://github.com/TheAwiteb/fatoora/issues/new?assignees=&labels=bug&template=bug.md)

## Security

If you discover any security related issues.

## Donating
> Note: This address for BEP20 tokens
| Currency | Address |
| ---------------------|------------------------------------------------ |
| Binance **BNB**| ```0x4ab0974c7dfcdcdf24d8323a93b061d41e9cf3f0```|
| Binance USD **BUSD** | ```0x4ab0974c7dfcdcdf24d8323a93b061d41e9cf3f0```|
| Tether **USDT** | ```0x4ab0974c7dfcdcdf24d8323a93b061d41e9cf3f0``` |
| Bitcoin **BTC** | ```0x4ab0974c7dfcdcdf24d8323a93b061d41e9cf3f0```|
| Bitcoin Cash **BCH**| ```0x4ab0974c7dfcdcdf24d8323a93b061d41e9cf3f0```|

## Project use this package
RAQ ERP - [raqss.co](https://raqss.co)

Expand Down
1 change: 0 additions & 1 deletion fatoora/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from .fatoora import *
from .version import *

__all__ = "Fatoora", "version", "version_info"
__author__ = "Awiteb <https://github.com/TheAwiteb>"
__copyright__ = "Copyright (c) 2021 <Awiteb@hotmail.com>"
__license__ = "MIT"
Expand Down
36 changes: 26 additions & 10 deletions fatoora/fatoora.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ def __init__(
tax_number: int,
invoice_date: float,
total_amount: float,
tax_amount: float,
tax_amount: Optional[float] = None,
qrcode_url: Optional[str] = None,
vat_rates: float = 15 / 100, # 15% vat rate
tags: TLV = TLV(),
):
self.tags = tags
self.vat_rates = vat_rates
self.seller_name = seller_name
self.tax_number = tax_number
self.invoice_date = invoice_date
Expand Down Expand Up @@ -83,9 +85,7 @@ def base2dict(cls, base: str) -> dict:
return dict(zip(keys, values))

@classmethod
def read_qrcode(
cls, filename: str, dct: bool = False
) -> Optional[Union[str, dict]]:
def read_qrcode(cls, filename: str, dct: bool = False) -> Union[str, dict]:
"""read content of qr code
Args:
Expand All @@ -94,7 +94,7 @@ def read_qrcode(
False -> return base64 of content or url. Defaults to False.
Returns:
Optional[Union[str, dict]]: content of qr code
Union[str, dict]: content of qr code
"""
data = decode(Image.open(filename))[0].data.decode()

Expand All @@ -103,6 +103,15 @@ def read_qrcode(
else:
return data

@property
def vat_rates(self) -> float:
return self.__vat_rates

@vat_rates.setter
@validate_arguments
def vat_rates(self, rate: float) -> None:
self.__vat_rates = rate

@property
def seller_name(self) -> str:
return self.tags[1]
Expand Down Expand Up @@ -149,7 +158,7 @@ def invoice_date(self, date: Union[datetime, float, str]) -> None:
self.tags[0x03] = date.strftime(iso8601_zulu_format)

@property
def total_amount(self) -> str:
def total_amount(self) -> float:
return float(self.tags[4])

@total_amount.setter
Expand All @@ -158,21 +167,28 @@ def total_amount(self, new_value: float) -> None:
self.tags[0x04] = str(new_value)

@property
def tax_amount(self) -> str:
def tax_amount(self) -> float:
return float(self.tags[5])

@tax_amount.setter
@validate_arguments
def tax_amount(self, new_value: float) -> None:
self.tags[0x05] = str(new_value)
def tax_amount(self, new_value: Optional[float] = None) -> None:
"""Auto set tax if it is `None` from `total_amount`
Args:
new_value (float): Tax amount of `total_amount`
"""
self.tags[0x05] = str(
round((new_value) or (self.total_amount * self.vat_rates), 2)
)

@property
def qrcode_url(self) -> Optional[str]:
return self._qrcode_url

@qrcode_url.setter
@validate_arguments
def qrcode_url(self, new_value: Optional[str]) -> None:
def qrcode_url(self, new_value: Optional[str] = None) -> None:
if not new_value or validators.url(new_value):
self._qrcode_url = new_value
else:
Expand Down
8 changes: 5 additions & 3 deletions tests/fatoora_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
tax_number=1234567891, # or "1234567891"
invoice_date=1635872693.3186214, # timestamp
total_amount=115, # or 115.0, 115.00, "115.0", "115.00"
tax_amount=15, # or 15.0, 15.00, "15.0", "15.00"
tax_amount=17.25, # or 17.0, 17.00, "17.0", "17.00"
)

fatoora_obj_with_url = Fatoora(
seller_name="Awiteb",
tax_number=1234567891, # or "1234567891"
invoice_date=1635872693.3186214, # timestamp
total_amount=115, # or 115.0, 115.00, "115.0", "115.00"
tax_amount=15, # or 15.0, 15.00, "15.0", "15.00"
tax_amount=17.25, # or 17.0, 17.00, "17.0", "17.00"
qrcode_url="https://example.com",
)

Expand All @@ -27,7 +27,7 @@
"tax_number": "1234567891",
"invoice_date": "2021-11-02T17:04:53Z",
"total_amount": "115.0",
"tax_amount": "15.0",
"tax_amount": "17.25",
}


Expand All @@ -54,6 +54,8 @@ def test_total_amount():
def test_tax_amount():
for obj in [fatoora_obj, fatoora_obj_with_url]:
assert obj.tax_amount == float(fatoora_details.get("tax_amount"))
obj.tax_amount = None
assert obj.tax_amount == float(fatoora_details.get("tax_amount"))


def test_dict():
Expand Down

0 comments on commit 31eaee5

Please sign in to comment.