Encorsa-e-Factura 0.4.0__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,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.4
2
+ Name: Encorsa_e_Factura
3
+ Version: 0.4.0
4
+ Summary: A small example package
5
+ Home-page: https://encorsa.ro
6
+ Author: Dan Popescu, Razvan Ogrezeanu
7
+ Author-email: dan.popescu@encorsa.com, razvan.ogrezeanu@encorsa.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ License-File: LICENCE
13
+ Requires-Dist: requests
14
+ Dynamic: license-file
15
+
16
+ # Encorsa-e-Factura
17
+
18
+ ## Description
19
+
20
+ * Project Description:
21
+ *
22
+ * This project aims to download invoices from the ANAF portal for clients and input them into WEBCON through an API.
23
+ * The download and upload parameters are received as arguments in the library.
24
+
25
+
26
+
27
+ ## Features
28
+
29
+ - [List the main features of your project.]
30
+
31
+ ## Installation
32
+
33
+ [Provide instructions on how to install and set up your project.]
34
+
35
+ For instructions on how to install and set up WEBCON, please refer to the [WEBCON Installation documentation](file:///.../e-Factura-PythonLibrary/DEV/WEBCON%20Installation%20documentation).
36
+
37
+ ## Usage
38
+
39
+ [Explain how to use your project, including any necessary commands or configurations.]
40
+
41
+ ## Contributing
42
+
43
+ [Provide guidelines for contributing to your project, if applicable.]
44
+
45
+ ## License
46
+
47
+ [Specify the license under which your project is distributed.]
48
+
49
+ ## Contact
50
+
51
+ encorsa.ro
@@ -0,0 +1,36 @@
1
+ # Encorsa-e-Factura
2
+
3
+ ## Description
4
+
5
+ * Project Description:
6
+ *
7
+ * This project aims to download invoices from the ANAF portal for clients and input them into WEBCON through an API.
8
+ * The download and upload parameters are received as arguments in the library.
9
+
10
+
11
+
12
+ ## Features
13
+
14
+ - [List the main features of your project.]
15
+
16
+ ## Installation
17
+
18
+ [Provide instructions on how to install and set up your project.]
19
+
20
+ For instructions on how to install and set up WEBCON, please refer to the [WEBCON Installation documentation](file:///.../e-Factura-PythonLibrary/DEV/WEBCON%20Installation%20documentation).
21
+
22
+ ## Usage
23
+
24
+ [Explain how to use your project, including any necessary commands or configurations.]
25
+
26
+ ## Contributing
27
+
28
+ [Provide guidelines for contributing to your project, if applicable.]
29
+
30
+ ## License
31
+
32
+ [Specify the license under which your project is distributed.]
33
+
34
+ ## Contact
35
+
36
+ encorsa.ro
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,32 @@
1
+ [metadata]
2
+ name = Encorsa_e_Factura
3
+ version = 0.4.0
4
+ author = Dan Popescu, Razvan Ogrezeanu
5
+ author_email = dan.popescu@encorsa.com, razvan.ogrezeanu@encorsa.com
6
+ description = A small example package
7
+ long_description = file: README.md
8
+ url = https://encorsa.ro
9
+ classifiers =
10
+ Programming Language :: Python :: 3
11
+ License :: OSI Approved :: MIT License
12
+ Operating System :: OS Independent
13
+
14
+ [options]
15
+ packages = find:
16
+ package_dir =
17
+ =src
18
+ install_requires =
19
+ requests
20
+ python_requires = >=3.8
21
+
22
+ [options.packages.find]
23
+ where = src
24
+
25
+ [options.entry_points]
26
+ console_scripts =
27
+ sincronizare-command = Encorsa_e_Factura.sincronizare:startRunningCLI
28
+
29
+ [egg_info]
30
+ tag_build =
31
+ tag_date = 0
32
+
@@ -0,0 +1,242 @@
1
+ from requests.auth import HTTPBasicAuth
2
+ import requests
3
+ import traceback
4
+ import zipfile
5
+ from io import BytesIO
6
+ import time
7
+ import base64
8
+
9
+
10
+ def get_token_with_refresh(refresh_token, clientID, clientSecret, parameters):
11
+ url = "https://logincert.anaf.ro/anaf-oauth2/v1/token"
12
+ auth = HTTPBasicAuth(clientID, clientSecret)
13
+ data = {
14
+ 'refresh_token': refresh_token,
15
+ 'grant_type': 'refresh_token'
16
+ }
17
+ try:
18
+ proxies = {
19
+ 'http': None,
20
+ 'https': None
21
+ }
22
+
23
+ if "proxi_pt_anaf_https" in parameters and parameters["proxi_pt_anaf_https"] != None and parameters["proxi_pt_anaf_https"] != "":
24
+ proxies["https"] = parameters["proxi_pt_anaf_https"]
25
+
26
+ if "proxi_pt_anaf_http" in parameters and parameters["proxi_pt_anaf_http"] != None and parameters["proxi_pt_anaf_http"] != "":
27
+ proxies["http"] = parameters["proxi_pt_anaf_http"]
28
+
29
+ response = requests.post(url, auth=auth, data=data, proxies=proxies)
30
+ # This checks for HTTP errors and raises an exception if any
31
+ response.raise_for_status()
32
+
33
+ json_response = response.json() # Attempt to parse JSON response
34
+
35
+ if 'access_token' in json_response:
36
+ return json_response['access_token']
37
+ else:
38
+ # Handle cases where 'access_token' is not in response
39
+ error_message = "Error at getting ANAF aceess token. Access token not found in the response."
40
+ print(error_message)
41
+ raise Exception(error_message)
42
+ except Exception as e:
43
+ # Catch all other errors
44
+ error_message = f"Error at getting ANAF access token. Error message: {str(e)}"
45
+ detailed_error = traceback.format_exc()
46
+ print(error_message)
47
+ print(detailed_error)
48
+ raise Exception(f"{error_message}\n{detailed_error}")
49
+
50
+
51
+ def get_lista_paginata_mesaje(token, start_time, end_time, cif, pagina, filter=None, parameters={}):
52
+ url = f"https://api.anaf.ro/prod/FCTEL/rest/listaMesajePaginatieFactura?startTime={start_time}&endTime={end_time}&cif={cif}&pagina={pagina}"
53
+ if filter is not None:
54
+ if filter != "":
55
+ url += "&filtru=" + filter
56
+
57
+ headers = {'Authorization': f'Bearer {token}'}
58
+ try:
59
+ proxies = {
60
+ 'http': None,
61
+ 'https': None
62
+ }
63
+
64
+ if "proxi_pt_anaf_https" in parameters and parameters["proxi_pt_anaf_https"] != None and parameters["proxi_pt_anaf_https"] != "":
65
+ proxies["https"] = parameters["proxi_pt_anaf_https"]
66
+
67
+ if "proxi_pt_anaf_http" in parameters and parameters["proxi_pt_anaf_http"] != None and parameters["proxi_pt_anaf_http"] != "":
68
+ proxies["http"] = parameters["proxi_pt_anaf_http"]
69
+
70
+ response = requests.get(url, headers=headers, proxies=proxies)
71
+ response.raise_for_status() # Ridică o excepție pentru coduri de răspuns HTTP eronate
72
+ return response.json()
73
+ except Exception as e:
74
+ error_message = f"Error at getting messages for page {pagina}. Error message: {str(e)}"
75
+ detalied_error = traceback.format_exc()
76
+ print(error_message)
77
+ print(detalied_error)
78
+ return {'eroare': error_message + '\n' + detalied_error}
79
+
80
+
81
+ def get_all_messages(token, start_time, end_time, cif, filter=None, parameters={}):
82
+ all_messages = [] # Lista pentru a stoca toate mesajele
83
+ current_page = 1 # Indexul paginii curente începe de la 0
84
+
85
+ # Încercăm să obținem mesajele de pe prima pagină pentru a verifica dacă există date
86
+ first_page_response = get_lista_paginata_mesaje(
87
+ token, start_time, end_time, cif, current_page, filter=filter, parameters=parameters)
88
+
89
+ # Verificăm dacă răspunsul conține o eroare
90
+ if "eroare" in first_page_response:
91
+ if 'Nu exista mesaje' in first_page_response["eroare"]:
92
+ print(first_page_response["eroare"])
93
+ exit(0)
94
+ error_message = "Error at getting all messages from ANAF: " + \
95
+ first_page_response["eroare"]
96
+ print(error_message)
97
+ raise Exception(error_message)
98
+
99
+ # Dacă există mesaje, continuăm să le adunăm din toate paginile
100
+ total_pages = first_page_response['numar_total_pagini']
101
+ all_messages.extend(first_page_response['mesaje'])
102
+
103
+ # Continuăm cu următoarele pagini, dacă există
104
+ for current_page in range(2, total_pages + 1):
105
+ response = get_lista_paginata_mesaje(
106
+ token, start_time, end_time, cif, current_page, filter=filter, parameters=parameters)
107
+ if "eroare" in response:
108
+ if 'Nu exista mesaje' in response["eroare"]:
109
+ print(response["eroare"])
110
+ exit(0)
111
+ else:
112
+ error_message = "Error at getting all messages from ANAF: " + \
113
+ response["eroare"]
114
+ print(error_message)
115
+ raise Exception(error_message)
116
+ all_messages.extend(response['mesaje'])
117
+
118
+ return all_messages
119
+
120
+
121
+ """
122
+ Această funcție descarcă o arhivă ZIP de la ANAF folosind un ID de factură
123
+ și extrage un fișier specificat prin nume_fisier din aceasta.
124
+ """
125
+ def descarca_factura_si_extrage_fisier(token, id, nume_fisier, parameters):
126
+ try:
127
+ url = f"https://api.anaf.ro/prod/FCTEL/rest/descarcare?id={id}"
128
+ headers = {'Authorization': f'Bearer {token}'}
129
+
130
+ proxies = {
131
+ 'http': None,
132
+ 'https': None
133
+ }
134
+
135
+ if "proxi_pt_anaf_https" in parameters and parameters["proxi_pt_anaf_https"] != None and parameters["proxi_pt_anaf_https"] != "":
136
+ proxies["https"] = parameters["proxi_pt_anaf_https"]
137
+
138
+ if "proxi_pt_anaf_http" in parameters and parameters["proxi_pt_anaf_http"] != None and parameters["proxi_pt_anaf_http"] != "":
139
+ proxies["http"] = parameters["proxi_pt_anaf_http"]
140
+
141
+ response = requests.get(url, headers=headers, proxies=proxies)
142
+
143
+ # Verify the response status code
144
+ if response.status_code != 200:
145
+ # Raise an exception for non-200 status codes with the HTTP status code
146
+ error_message = f"Error while downloading ZIP archive with the XML. Code: {response.status_code}"
147
+ print(error_message)
148
+ raise Exception(error_message)
149
+
150
+ # Create a BytesIO object from the response content
151
+ zip_in_memory = BytesIO(response.content)
152
+
153
+ try:
154
+ # Open the ZIP archive
155
+ with zipfile.ZipFile(zip_in_memory, 'r') as zip_ref:
156
+ # Check if the file exists in the archive
157
+ if nume_fisier in zip_ref.namelist():
158
+ # Extract the specified file content
159
+ with zip_ref.open(nume_fisier) as fisier:
160
+ content_bytes = fisier.read()
161
+ # Decode bytes into a string using UTF-8
162
+ content_string = content_bytes.decode('utf-8')
163
+ return content_string
164
+ else:
165
+ # File not found in the archive, handle according to your preference
166
+ error_message = f"File '{nume_fisier}' not found in the ZIP archive"
167
+ print(error_message)
168
+ raise Exception(error_message)
169
+ except zipfile.BadZipFile as zp_err:
170
+ # Handle a bad ZIP file error
171
+ error_message = f"Error while extracting ZIP archive with the XML. Message: {zp_err}"
172
+ detalied_error = traceback.format_exc()
173
+ print(error_message)
174
+ print(detalied_error)
175
+ raise Exception(error_message + '\n' + detalied_error)
176
+ except Exception as e:
177
+ # Handle other errors
178
+ error_message = f"Error while downloading ZIP archive with the XML. Message: {e}"
179
+ detalied_error = traceback.format_exc()
180
+ print(error_message)
181
+ print(detalied_error)
182
+ raise Exception(error_message + '\n' + detalied_error)
183
+
184
+
185
+ def xml_to_pdf_to_base64(xml_data, parameters, document_type):
186
+ """
187
+ Funcția trimite date XML către un serviciu web al ANAF pentru a fi convertite
188
+ într-un document PDF, apoi encodează conținutul binar al PDF-ului obținut în format Base64
189
+ """
190
+
191
+ if document_type == "Invoice":
192
+ url = "https://webservicesp.anaf.ro/prod/FCTEL/rest/transformare/FACT1/DA"
193
+ elif document_type == "CreditNote":
194
+ url = "https://webservicesp.anaf.ro/prod/FCTEL/rest/transformare/FCN/DA"
195
+ else:
196
+ raise ValueError("Invalid document type for XML to PDF conversion." + f" Document type: {document_type}")
197
+
198
+ headers = {'Content-Type': 'text/plain'}
199
+ proxies = {'http': None, 'https': None}
200
+
201
+ # Set proxies if provided in parameters
202
+ if "proxi_pt_anaf_https" in parameters and parameters["proxi_pt_anaf_https"]:
203
+ proxies["https"] = parameters["proxi_pt_anaf_https"]
204
+ if "proxi_pt_anaf_http" in parameters and parameters["proxi_pt_anaf_http"]:
205
+ proxies["http"] = parameters["proxi_pt_anaf_http"]
206
+
207
+ # Retry logic
208
+ max_retries = 5
209
+ attempts = 0
210
+
211
+ # Encode the XML data to UTF-8 bytes to ensure compatibility
212
+ xml_data = xml_data.encode('utf-8')
213
+
214
+ while attempts < max_retries:
215
+ try:
216
+ response = requests.post(
217
+ url, headers=headers, data=xml_data, proxies=proxies)
218
+ if response.status_code != 200:
219
+ response.raise_for_status()
220
+
221
+ # Directly check if the response is JSON
222
+ if "application/json" in response.headers.get('Content-Type', ''):
223
+ print("JSON response received, retrying..." + str(response.content))
224
+ # Print the whole response for debugging
225
+ attempts += 1
226
+ # Sleep for 1 second before retrying
227
+ time.sleep(1)
228
+ continue
229
+ else:
230
+ # Assume the response is the binary content of the PDF
231
+ pdf_content = response.content
232
+ # Encode the PDF content to Base64
233
+ base64_encoded_pdf = base64.b64encode(pdf_content)
234
+ return base64_encoded_pdf.decode('utf-8')
235
+ except Exception as e:
236
+ error_message = f"Error when converting the XML to PDF: {str(e)}"
237
+ detalied_error = traceback.format_exc()
238
+ print(error_message + '\n' + detalied_error)
239
+ attempts += 1
240
+
241
+ print("Maximum retries reached, PDF content could not be retrieved.")
242
+ raise Exception("Maximum retries reached, PDF content could not be retrieved.")