lib-invoice 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: lib-invoice
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Nathan Blum
|
|
6
|
+
Author-email: n.blum@student.tudelft.nl
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Dist: lib-utilys (>=0.1.3,<0.2.0)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# lib-invoice
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# lib-invoice
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lib-invoice"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Nathan Blum",email = "n.blum@student.tudelft.nl"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"lib-utilys (>=0.1.3,<0.2.0)",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[tool.poetry]
|
|
15
|
+
packages = [{include = "lib_invoice", from = "src"}]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
20
|
+
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import chardet
|
|
3
|
+
from bs4 import BeautifulSoup
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from email import message_from_string
|
|
6
|
+
from email.header import decode_header
|
|
7
|
+
from lib_utilys import clean_special_characters, read_json
|
|
8
|
+
|
|
9
|
+
class Invoice:
|
|
10
|
+
def __init__(self, uid: str, adress: str, message: str, business: str, subject: str, text: str, pdf: bytes):
|
|
11
|
+
self.uid = uid
|
|
12
|
+
self.adress = adress
|
|
13
|
+
self.message = message
|
|
14
|
+
self.business = business
|
|
15
|
+
self.subject = subject
|
|
16
|
+
self.text = text
|
|
17
|
+
self.pdf = pdf
|
|
18
|
+
self.pdf_filename = None
|
|
19
|
+
self.kvpairs = None
|
|
20
|
+
self.type = None
|
|
21
|
+
|
|
22
|
+
def configure_kvpairs(self, kv_pairs: dict):
|
|
23
|
+
"""Configures key-value pairs for the invoice."""
|
|
24
|
+
self.kvpairs = kv_pairs
|
|
25
|
+
self.check_type_()
|
|
26
|
+
self.set_line_level_()
|
|
27
|
+
|
|
28
|
+
def check_type_(self):
|
|
29
|
+
"""Checks the type of the invoice."""
|
|
30
|
+
if self.kvpairs['Invoice_value'] == 0:
|
|
31
|
+
self.type = 'NULL'
|
|
32
|
+
self.kvpairs['Type'] = 'NULL'
|
|
33
|
+
elif self.kvpairs['Invoice_value'] < 0:
|
|
34
|
+
self.type = 'CRME'
|
|
35
|
+
self.kvpairs['Type'] = 'CRME'
|
|
36
|
+
elif self.kvpairs['Invoice_value'] > 0:
|
|
37
|
+
self.type = 'INVO'
|
|
38
|
+
self.kvpairs['Type'] = 'INVO'
|
|
39
|
+
|
|
40
|
+
def additional_kv_pairs(self, crit_path: str, debmap_pth: str, ctryabbr_path: str):
|
|
41
|
+
"""Adds additional key-value pairs to the invoice."""
|
|
42
|
+
self.kvpairs['Creation_date'] = datetime.now().strftime('%Y%m%d')
|
|
43
|
+
self.kvpairs['Creation_time'] = datetime.now().strftime('%H%M%S')
|
|
44
|
+
self.kvpairs['Timestamp'] = datetime.now().strftime('%Y%m%d%H%M%S')
|
|
45
|
+
self.kvpairs['Debtor_code'] = (read_json(crit_path)).get(self.kvpairs['Debtor_name'], {}).get('debtor_code')
|
|
46
|
+
if self.kvpairs['Debtor_code'] is None: raise MissingValueError("Debtor code is None")
|
|
47
|
+
self.kvpairs['Debtor_international_location_number'] = (read_json(debmap_pth)).get(self.kvpairs['Debtor_name'], {}).get('illnr')
|
|
48
|
+
self.kvpairs['Partner_country'] = (read_json(ctryabbr_path)).get(self.kvpairs['Partner_country'])
|
|
49
|
+
if self.kvpairs['Partner_country'] is None: raise MissingValueError("Partner country is None")
|
|
50
|
+
if isinstance(self.kvpairs['Creditor_number'], dict): self.kvpairs['Creditor_number'] = self.kvpairs['Creditor_number'].get(f'{self.kvpairs["Partner_country"]}')
|
|
51
|
+
self.kvpairs['Invoice_number'] = clean_special_characters(self.kvpairs['Invoice_number'])
|
|
52
|
+
if 'Debtor_number' in self.kvpairs and self.kvpairs['Debtor_number'] is not None: self.kvpairs['Debtor_number'] = clean_special_characters(self.kvpairs['Debtor_number'])
|
|
53
|
+
self.pdf_filename = f"{self.kvpairs['Creditor_number']}-{self.kvpairs['Debtor_international_location_number']}.{self.kvpairs['Invoice_number']}.pdf"
|
|
54
|
+
self.pdf_filename = clean_special_characters(self.pdf_filename)
|
|
55
|
+
self.configure_tax_()
|
|
56
|
+
|
|
57
|
+
def set_line_level_(self):
|
|
58
|
+
"""Sets certain key-value pairs to line level in Material_list."""
|
|
59
|
+
if self.kvpairs['Material_list']:
|
|
60
|
+
previous_pol = None
|
|
61
|
+
for line in self.kvpairs.get('Material_list'):
|
|
62
|
+
Purchase_order_line = line.get('Purchase_order_line', None)
|
|
63
|
+
if Purchase_order_line is None and self.kvpairs.get('Purchase_order') is not None:
|
|
64
|
+
line['Purchase_order_line'] = self.kvpairs.get('Purchase_order')
|
|
65
|
+
if previous_pol is not None and Purchase_order_line is None and self.kvpairs.get('Purchase_order') is None:
|
|
66
|
+
line['Purchase_order_line'] = previous_pol
|
|
67
|
+
previous_pol = line.get('Purchase_order_line', None)
|
|
68
|
+
if Purchase_order_line is None and self.kvpairs.get('Purchase_order') is None and previous_pol is None:
|
|
69
|
+
raise MissingValueError("Purchase order is None")
|
|
70
|
+
|
|
71
|
+
def configure_crme(self):
|
|
72
|
+
"""Configures the CRME type of invoice."""
|
|
73
|
+
try:
|
|
74
|
+
self.kvpairs['Invoice_value'] = str(self.kvpairs['Invoice_value']).replace('-', '')
|
|
75
|
+
self.kvpairs['Net_value'] = str(self.kvpairs['Net_value']).replace('-', '')
|
|
76
|
+
self.kvpairs['Total_tax'] = str(self.kvpairs['Total_tax']).replace('-', '')
|
|
77
|
+
if self.kvpairs['Material_list']:
|
|
78
|
+
self.kvpairs['Material_list'][0]['Quantity'] = str(self.kvpairs['Material_list'][0]['Quantity']).replace('-', '')
|
|
79
|
+
except Exception as e:
|
|
80
|
+
self.Output.output(f"Error configuring CRME: {e}")
|
|
81
|
+
|
|
82
|
+
def configure_tax_(self, EUabbr_path: str, taxmap_path: str):
|
|
83
|
+
"""Configures the tax for the invoice."""
|
|
84
|
+
eu_countries = (read_json(EUabbr_path)).get('EU_country_abbreviations', [])
|
|
85
|
+
if self.kvpairs['Partner_country'] == 'NL':
|
|
86
|
+
self.kvpairs['Tax_qualifier'] = (read_json(taxmap_path)).get('NL', {}).get(str(int(self.kvpairs['Tax_percent'] if self.kvpairs['Tax_percent'] is not None else 0)))
|
|
87
|
+
if self.kvpairs['Tax_qualifier'] is None: raise MissingValueError("Tax qualifier is None")
|
|
88
|
+
elif self.kvpairs['Partner_country'] in eu_countries:
|
|
89
|
+
self.kvpairs['Tax_qualifier'] = (read_json(taxmap_path)).get('EU', {}).get(str(int(self.kvpairs['Tax_percent'] if self.kvpairs['Tax_percent'] is not None else 0)))
|
|
90
|
+
if self.kvpairs['Tax_qualifier'] is None: raise MissingValueError("Tax qualifier is None")
|
|
91
|
+
else:
|
|
92
|
+
self.kvpairs['Tax_qualifier'] = (read_json(taxmap_path)).get('Non-EU', {}).get(str(int(self.kvpairs['Tax_percent'] if self.kvpairs['Tax_percent'] is not None else 0)))
|
|
93
|
+
if self.kvpairs['Tax_qualifier'] is None: raise MissingValueError("Tax qualifier is None")
|
|
94
|
+
|
|
95
|
+
class MissingValueError(Exception):
|
|
96
|
+
"""Custom exception for missing or None values."""
|
|
97
|
+
def __init__(self, message="Required value is missing or None"):
|
|
98
|
+
self.message = message
|
|
99
|
+
super().__init__(self.message)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|