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.
- encorsa_e_factura-0.4.0/LICENCE +19 -0
- encorsa_e_factura-0.4.0/PKG-INFO +51 -0
- encorsa_e_factura-0.4.0/README.md +36 -0
- encorsa_e_factura-0.4.0/pyproject.toml +3 -0
- encorsa_e_factura-0.4.0/setup.cfg +32 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/AnafUtils.py +242 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/WebConRequestUtils.py +326 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/XMLFromTemplateBuilder.py +237 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/XMLUtils.py +481 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/__init__.py +2 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/runLocaly.py +10 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/sendInvoiceANAF.py +222 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura/sincronizare.py +262 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura.egg-info/PKG-INFO +51 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura.egg-info/SOURCES.txt +18 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura.egg-info/dependency_links.txt +1 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura.egg-info/entry_points.txt +2 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura.egg-info/requires.txt +1 -0
- encorsa_e_factura-0.4.0/src/Encorsa_e_Factura.egg-info/top_level.txt +1 -0
|
@@ -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,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.")
|