commit e4689c6632d227e1426947c96cff0ac62dfeec07 Author: norohind <60548839+norohind@users.noreply.github.com> Date: Thu Aug 29 13:20:32 2024 +0200 init diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e568d75 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "taxgovme" +version = "0.0.1" +description = "A toolset to parse invoices from mapr.tax.gov.me" +requires-python = ">=3.10" +dependencies = ["requests", "pydantic"] +license = {text = "MIT"} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..903705e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pydantic +requests diff --git a/src/taxgovme.py b/src/taxgovme.py new file mode 100644 index 0000000..2fe47d7 --- /dev/null +++ b/src/taxgovme.py @@ -0,0 +1,79 @@ +import datetime +import json +from pydantic import BaseModel, Field, field_validator, ValidationInfo, ValidationError, computed_field, AliasPath +from decimal import Decimal +import requests +from urllib.parse import urlparse, parse_qs + + +class Item(BaseModel): + name: str + quantity: Decimal + unit_price: Decimal = Field(alias='unitPriceAfterVat') + total_price: Decimal = Field(alias='priceAfterVat') + barcode: str | None = Field(default=None, alias='code') + unit: str | None = Field(default=None, alias='unit') + + @field_validator('total_price') + def total_price_validate(cls, total_price: Decimal, info: ValidationInfo) -> Decimal: + total_price = round(total_price, 2) + # calculated_total_price = round(info.data['quantity'] * info.data['unit_price'], 2) + # if calculated_total_price != total_price: + # raise ValueError(f"{calculated_total_price=}. Invoice {total_price=}") + + return total_price + + @computed_field(return_type=Decimal) + def rounding_errors(self): + return self.total_price - round(self.unit_price * self.quantity, 2) + + @field_validator('name') + def name_normalize(cls, name: str, infok) -> str: + return ' '.join(name.split()) + +class Invoice(BaseModel): + total_price: Decimal = Field(alias='totalPrice') + timestamp: datetime.datetime = Field(alias='dateTimeCreated') + seller_name: str | None = Field(validation_alias=AliasPath('seller', 'name')) + items: list[Item] + + @computed_field(return_type=Decimal) + def rounding_errors(self) -> Decimal: + return self.total_price - sum(i.total_price for i in self.items) + + +def query_verify_invoice(iic, dateTimeCreated, tin) -> dict: + r = requests.post( + 'https://mapr.tax.gov.me/ic/api/verifyInvoice', + params={'iic': iic, 'dateTimeCreated': dateTimeCreated, 'tin': tin} + ) + + try: + r.raise_for_status() + doc = r.json() + return doc + + except (requests.HTTPError, json.decoder.JSONDecodeError): + print(r.status_code) + print(r.content) + raise + + except ValidationError: + print(json.dumps(doc, ensure_ascii=False, indent=2)) + raise + +def extract_params_from_url(url: str) -> tuple[str, str, str]: + parsed = urlparse(url, allow_fragments=False) + params = parse_qs(parsed.query) + return params['iic'][0], params['crtd'][0], params['tin'][0] + +def query_verify_invoice_by_url(url) -> dict: + params = extract_params_from_url(url) + return query_verify_invoice(*params) + +def invoice_by_url(url: str) -> Invoice: + invoice_dict = query_verify_invoice_by_url(url) + inv = Invoice.model_validate(invoice_dict) + inv.items.sort(key=lambda item: item.total_price, reverse=True) + + return inv \ No newline at end of file