brynq-sdk-nmbrs 2.3.4.dev0__py3-none-any.whl → 2.4.5__py3-none-any.whl
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.
- brynq_sdk_nmbrs/__init__.py +95 -91
- brynq_sdk_nmbrs/absence.py +1 -4
- brynq_sdk_nmbrs/address.py +5 -81
- brynq_sdk_nmbrs/bank.py +1 -2
- brynq_sdk_nmbrs/companies.py +130 -39
- brynq_sdk_nmbrs/contract.py +0 -1
- brynq_sdk_nmbrs/days.py +1 -1
- brynq_sdk_nmbrs/debtors.py +53 -65
- brynq_sdk_nmbrs/department.py +23 -111
- brynq_sdk_nmbrs/document.py +195 -4
- brynq_sdk_nmbrs/employee_wage_tax_settings.py +13 -5
- brynq_sdk_nmbrs/employees.py +55 -21
- brynq_sdk_nmbrs/employment.py +0 -2
- brynq_sdk_nmbrs/extra_fields.py +126 -0
- brynq_sdk_nmbrs/function.py +35 -97
- brynq_sdk_nmbrs/leave.py +53 -58
- brynq_sdk_nmbrs/manager.py +51 -35
- brynq_sdk_nmbrs/salaries.py +48 -65
- brynq_sdk_nmbrs/salary_tables.py +1 -4
- brynq_sdk_nmbrs/schedules.py +5 -78
- brynq_sdk_nmbrs/schemas/absence.py +26 -16
- brynq_sdk_nmbrs/schemas/address.py +2 -29
- brynq_sdk_nmbrs/schemas/children.py +0 -2
- brynq_sdk_nmbrs/schemas/contracts.py +7 -6
- brynq_sdk_nmbrs/schemas/costcenter.py +2 -2
- brynq_sdk_nmbrs/schemas/costunit.py +2 -0
- brynq_sdk_nmbrs/schemas/days.py +13 -11
- brynq_sdk_nmbrs/schemas/debtor.py +6 -28
- brynq_sdk_nmbrs/schemas/department.py +5 -39
- brynq_sdk_nmbrs/schemas/document.py +0 -2
- brynq_sdk_nmbrs/schemas/employee_wage_tax_settings.py +75 -0
- brynq_sdk_nmbrs/schemas/employees.py +2 -0
- brynq_sdk_nmbrs/schemas/extra_fields.py +56 -0
- brynq_sdk_nmbrs/schemas/function.py +5 -32
- brynq_sdk_nmbrs/schemas/hours.py +5 -1
- brynq_sdk_nmbrs/schemas/leave.py +17 -6
- brynq_sdk_nmbrs/schemas/manager.py +11 -20
- brynq_sdk_nmbrs/schemas/salary.py +11 -1
- brynq_sdk_nmbrs/schemas/schedules.py +2 -54
- brynq_sdk_nmbrs/schemas/svw_settings.py +116 -0
- brynq_sdk_nmbrs/schemas/wage_tax.py +53 -21
- brynq_sdk_nmbrs/schemas/wagecomponents.py +6 -9
- brynq_sdk_nmbrs/svw_settings.py +213 -0
- brynq_sdk_nmbrs/wage_tax.py +120 -11
- {brynq_sdk_nmbrs-2.3.4.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/METADATA +1 -1
- brynq_sdk_nmbrs-2.4.5.dist-info/RECORD +58 -0
- brynq_sdk_nmbrs/schemas/social_insurance.py +0 -73
- brynq_sdk_nmbrs/social_insurance.py +0 -130
- brynq_sdk_nmbrs-2.3.4.dev0.dist-info/RECORD +0 -55
- {brynq_sdk_nmbrs-2.3.4.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/WHEEL +0 -0
- {brynq_sdk_nmbrs-2.3.4.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/top_level.txt +0 -0
brynq_sdk_nmbrs/debtors.py
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
|
+
|
|
1
2
|
import pandas as pd
|
|
2
3
|
import requests
|
|
3
|
-
|
|
4
|
-
from zeep.exceptions import Fault
|
|
4
|
+
|
|
5
5
|
from brynq_sdk_functions import Functions
|
|
6
|
+
|
|
6
7
|
from .department import Departments
|
|
7
8
|
from .function import Functions as NmbrsFunctions
|
|
8
|
-
from .schemas.debtor import DebtorsGet
|
|
9
|
+
from .schemas.debtor import DebtorsGet
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class Debtors:
|
|
13
|
+
"""
|
|
14
|
+
Class for handling debtor information.
|
|
15
|
+
|
|
16
|
+
Endpoints:
|
|
17
|
+
- GET /api/debtors
|
|
18
|
+
- GET /api/debtors/info
|
|
19
|
+
- GET /api/debtors/endcontractreasons/{Year}
|
|
20
|
+
"""
|
|
21
|
+
|
|
12
22
|
def __init__(self, nmbrs):
|
|
13
23
|
self.nmbrs = nmbrs
|
|
14
24
|
self.departments = Departments(nmbrs)
|
|
15
25
|
self.functions = NmbrsFunctions(nmbrs)
|
|
16
26
|
|
|
17
|
-
def get(self) ->
|
|
27
|
+
def get(self) -> tuple:
|
|
28
|
+
"""
|
|
29
|
+
Get a list of all debtors for the authenticated user.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Tuple of (valid_data, invalid_data) DataFrames
|
|
33
|
+
"""
|
|
18
34
|
request = requests.Request(method='GET',
|
|
19
35
|
url=f"{self.nmbrs.base_url}debtors")
|
|
20
36
|
data = self.nmbrs.get_paginated_result(request)
|
|
@@ -25,75 +41,47 @@ class Debtors:
|
|
|
25
41
|
|
|
26
42
|
return valid_debtors, invalid_debtors
|
|
27
43
|
|
|
28
|
-
def
|
|
44
|
+
def get_info(self) -> pd.DataFrame:
|
|
29
45
|
"""
|
|
30
|
-
|
|
46
|
+
Get information about the authenticated debtor.
|
|
31
47
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- number: Debtor number
|
|
35
|
-
- name: Debtor name
|
|
48
|
+
Returns info about the debtor that the API keys belong to,
|
|
49
|
+
including current settings and subscription details.
|
|
36
50
|
|
|
37
51
|
Returns:
|
|
38
|
-
|
|
52
|
+
DataFrame containing debtor info
|
|
39
53
|
"""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
return response
|
|
58
|
-
|
|
59
|
-
except Fault as e:
|
|
60
|
-
raise Exception(f"SOAP request failed: {str(e)}")
|
|
61
|
-
except Exception as e:
|
|
62
|
-
raise Exception(f"Failed to create Debtor: {str(e)}")
|
|
63
|
-
|
|
64
|
-
def update(self, data: Dict[str, Any]):
|
|
54
|
+
resp = self.nmbrs.session.get(
|
|
55
|
+
url=f"{self.nmbrs.base_url}debtors/info",
|
|
56
|
+
timeout=self.nmbrs.timeout
|
|
57
|
+
)
|
|
58
|
+
resp.raise_for_status()
|
|
59
|
+
data = resp.json()
|
|
60
|
+
|
|
61
|
+
# Normalize the response if it's a dict
|
|
62
|
+
if isinstance(data, dict):
|
|
63
|
+
df = pd.json_normalize(data)
|
|
64
|
+
else:
|
|
65
|
+
df = pd.DataFrame(data)
|
|
66
|
+
|
|
67
|
+
return df
|
|
68
|
+
|
|
69
|
+
def get_end_contract_reasons(self, year: int) -> pd.DataFrame:
|
|
65
70
|
"""
|
|
66
|
-
|
|
71
|
+
Get the available end contract reasons for a specific year.
|
|
67
72
|
|
|
68
73
|
Args:
|
|
69
|
-
|
|
70
|
-
- debtor_id: Debtor ID to update
|
|
71
|
-
- number: Debtor number
|
|
72
|
-
- name: Debtor name
|
|
74
|
+
year: The year to get end contract reasons for
|
|
73
75
|
|
|
74
76
|
Returns:
|
|
75
|
-
|
|
77
|
+
DataFrame containing end contract reasons
|
|
76
78
|
"""
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Number=debtor_model.number,
|
|
87
|
-
Name=debtor_model.name
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
response = self.nmbrs.soap_client_debtors.service.Debtor_Update(
|
|
91
|
-
Debtor=soap_debtor,
|
|
92
|
-
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
93
|
-
)
|
|
94
|
-
return response
|
|
95
|
-
|
|
96
|
-
except Fault as e:
|
|
97
|
-
raise Exception(f"SOAP request failed: {str(e)}")
|
|
98
|
-
except Exception as e:
|
|
99
|
-
raise Exception(f"Failed to update Debtor: {str(e)}")
|
|
79
|
+
request = requests.Request(
|
|
80
|
+
method='GET',
|
|
81
|
+
url=f"{self.nmbrs.base_url}debtors/endcontractreasons/{year}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
85
|
+
df = pd.DataFrame(data)
|
|
86
|
+
|
|
87
|
+
return df
|
brynq_sdk_nmbrs/department.py
CHANGED
|
@@ -2,19 +2,13 @@ from typing import Any, Dict
|
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import requests
|
|
5
|
-
from zeep.exceptions import Fault
|
|
6
5
|
|
|
7
6
|
from brynq_sdk_functions import Functions
|
|
8
7
|
|
|
9
8
|
from .schemas.department import (
|
|
10
|
-
DepartmentCreate,
|
|
11
9
|
DepartmentGet,
|
|
12
|
-
DepartmentMasterCreate,
|
|
13
|
-
DepartmentMasterDelete,
|
|
14
|
-
DepartmentMasterUpdate,
|
|
15
10
|
EmployeeDepartmentGet,
|
|
16
11
|
EmployeeDepartmentUpdate,
|
|
17
|
-
Period,
|
|
18
12
|
)
|
|
19
13
|
|
|
20
14
|
|
|
@@ -119,125 +113,43 @@ class EmployeeDepartment:
|
|
|
119
113
|
|
|
120
114
|
class Departments:
|
|
121
115
|
"""Master department operations (Debtor level) - uses SOAP for create/update/delete."""
|
|
122
|
-
|
|
116
|
+
|
|
123
117
|
def __init__(self, nmbrs):
|
|
124
118
|
self.nmbrs = nmbrs
|
|
125
119
|
|
|
126
|
-
def get(self,
|
|
127
|
-
debtor_id: str) -> pd.DataFrame:
|
|
128
|
-
request = requests.Request(method='GET',
|
|
129
|
-
url=f"{self.nmbrs.base_url}debtors/{debtor_id}/departments")
|
|
130
|
-
|
|
131
|
-
data = self.nmbrs.get_paginated_result(request)
|
|
132
|
-
|
|
133
|
-
df = pd.DataFrame(data)
|
|
134
|
-
valid_departments, invalid_departments = Functions.validate_data(df=df, schema=DepartmentGet, debug=True)
|
|
135
|
-
|
|
136
|
-
return valid_departments, invalid_departments
|
|
137
|
-
|
|
138
|
-
def create(self, data: Dict[str, Any]) -> int:
|
|
120
|
+
def get(self, debtor_id: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
139
121
|
"""
|
|
140
|
-
|
|
122
|
+
Get all departments for debtor(s).
|
|
141
123
|
|
|
142
124
|
Args:
|
|
143
|
-
|
|
144
|
-
- debtor_id: Debtor ID
|
|
145
|
-
- code: Department code
|
|
146
|
-
- description: Department description
|
|
125
|
+
debtor_id: Optional debtor ID. If not provided, fetches for all debtors.
|
|
147
126
|
|
|
148
127
|
Returns:
|
|
149
|
-
|
|
128
|
+
Tuple of (valid_departments, invalid_departments) DataFrames
|
|
150
129
|
"""
|
|
151
|
-
|
|
130
|
+
if debtor_id:
|
|
131
|
+
df = self._get(debtor_id)
|
|
132
|
+
else:
|
|
133
|
+
df = pd.DataFrame()
|
|
134
|
+
for debtor in self.nmbrs.debtor_ids:
|
|
135
|
+
df = pd.concat([df, self._get(debtor)])
|
|
152
136
|
|
|
153
|
-
if
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
try:
|
|
157
|
-
DepartmentType = self.nmbrs.soap_client_debtors.get_type('ns0:Department')
|
|
158
|
-
soap_department = DepartmentType(
|
|
159
|
-
Id=0, # 0 for new department
|
|
160
|
-
Code=dept_model.code,
|
|
161
|
-
Description=dept_model.description
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
response = self.nmbrs.soap_client_debtors.service.Department_Insert(
|
|
165
|
-
DebtorId=dept_model.debtor_id,
|
|
166
|
-
department=soap_department,
|
|
167
|
-
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
168
|
-
)
|
|
169
|
-
return response
|
|
170
|
-
|
|
171
|
-
except Fault as e:
|
|
172
|
-
raise Exception(f"SOAP request failed: {str(e)}")
|
|
173
|
-
except Exception as e:
|
|
174
|
-
raise Exception(f"Failed to create Department: {str(e)}")
|
|
175
|
-
|
|
176
|
-
def update(self, data: Dict[str, Any]):
|
|
177
|
-
"""
|
|
178
|
-
Update a master department for a debtor using SOAP API.
|
|
137
|
+
if df.empty:
|
|
138
|
+
return df, pd.DataFrame()
|
|
179
139
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
- debtor_id: Debtor ID
|
|
183
|
-
- department_id: Department ID to update
|
|
184
|
-
- code: Department code
|
|
185
|
-
- description: Department description
|
|
140
|
+
valid_departments, invalid_departments = Functions.validate_data(df=df, schema=DepartmentGet, debug=self.nmbrs.debug)
|
|
141
|
+
return valid_departments, invalid_departments
|
|
186
142
|
|
|
187
|
-
|
|
188
|
-
Response from the API
|
|
143
|
+
def _get(self, debtor_id: str) -> pd.DataFrame:
|
|
189
144
|
"""
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if self.nmbrs.mock_mode:
|
|
193
|
-
return dept_model
|
|
194
|
-
|
|
195
|
-
try:
|
|
196
|
-
DepartmentType = self.nmbrs.soap_client_debtors.get_type('ns0:Department')
|
|
197
|
-
soap_department = DepartmentType(
|
|
198
|
-
Id=dept_model.department_id,
|
|
199
|
-
Code=dept_model.code,
|
|
200
|
-
Description=dept_model.description
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
response = self.nmbrs.soap_client_debtors.service.Department_Update(
|
|
204
|
-
DebtorId=dept_model.debtor_id,
|
|
205
|
-
department=soap_department,
|
|
206
|
-
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
207
|
-
)
|
|
208
|
-
return response
|
|
209
|
-
|
|
210
|
-
except Fault as e:
|
|
211
|
-
raise Exception(f"SOAP request failed: {str(e)}")
|
|
212
|
-
except Exception as e:
|
|
213
|
-
raise Exception(f"Failed to update Department: {str(e)}")
|
|
214
|
-
|
|
215
|
-
def delete(self, data: Dict[str, Any]):
|
|
145
|
+
Internal method to get departments for a single debtor.
|
|
216
146
|
"""
|
|
217
|
-
|
|
147
|
+
request = requests.Request(method='GET',
|
|
148
|
+
url=f"{self.nmbrs.base_url}debtors/{debtor_id}/departments")
|
|
218
149
|
|
|
219
|
-
|
|
220
|
-
data: Dictionary containing department data with fields matching DepartmentMasterDelete schema:
|
|
221
|
-
- debtor_id: Debtor ID
|
|
222
|
-
- department_id: Department ID to delete
|
|
150
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
223
151
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"""
|
|
227
|
-
dept_model = DepartmentMasterDelete(**data)
|
|
152
|
+
df = pd.DataFrame(data)
|
|
153
|
+
df['debtorId'] = debtor_id
|
|
228
154
|
|
|
229
|
-
|
|
230
|
-
return True
|
|
231
|
-
|
|
232
|
-
try:
|
|
233
|
-
response = self.nmbrs.soap_client_debtors.service.Department_Delete(
|
|
234
|
-
DebtorId=dept_model.debtor_id,
|
|
235
|
-
id=dept_model.department_id,
|
|
236
|
-
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
237
|
-
)
|
|
238
|
-
return response
|
|
239
|
-
|
|
240
|
-
except Fault as e:
|
|
241
|
-
raise Exception(f"SOAP request failed: {str(e)}")
|
|
242
|
-
except Exception as e:
|
|
243
|
-
raise Exception(f"Failed to delete Department: {str(e)}")
|
|
155
|
+
return df
|
brynq_sdk_nmbrs/document.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from io import BytesIO
|
|
2
1
|
import base64
|
|
3
|
-
from
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
from typing import Any, BinaryIO, Dict, Optional
|
|
4
4
|
|
|
5
5
|
import pandas as pd
|
|
6
6
|
import requests
|
|
@@ -11,7 +11,7 @@ from .schemas.document import DocumentUpload
|
|
|
11
11
|
|
|
12
12
|
class EmployeeDocument:
|
|
13
13
|
"""Handle employee document operations via SOAP API."""
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def __init__(self, nmbrs):
|
|
16
16
|
self.nmbrs = nmbrs
|
|
17
17
|
|
|
@@ -55,13 +55,31 @@ class EmployeeDocument:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
class Payslip:
|
|
58
|
+
"""
|
|
59
|
+
Class for handling employee payslips.
|
|
60
|
+
|
|
61
|
+
Endpoints:
|
|
62
|
+
- GET /api/employees/{employeeId}/payslipperperiod
|
|
63
|
+
"""
|
|
64
|
+
|
|
58
65
|
def __init__(self, nmbrs):
|
|
59
66
|
self.nmbrs = nmbrs
|
|
60
67
|
|
|
61
68
|
def get(self,
|
|
62
69
|
employee_id: str,
|
|
63
70
|
period: int = None,
|
|
64
|
-
year: int = None) ->
|
|
71
|
+
year: int = None) -> BytesIO:
|
|
72
|
+
"""
|
|
73
|
+
Get an employee payslip per period.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
employee_id: The ID of the employee
|
|
77
|
+
period: Optional period number
|
|
78
|
+
year: Optional year
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
BytesIO containing the payslip PDF
|
|
82
|
+
"""
|
|
65
83
|
params = {}
|
|
66
84
|
if period:
|
|
67
85
|
params['period'] = period
|
|
@@ -78,3 +96,176 @@ class Payslip:
|
|
|
78
96
|
return BytesIO(resp.content)
|
|
79
97
|
|
|
80
98
|
|
|
99
|
+
class Document:
|
|
100
|
+
"""
|
|
101
|
+
Class for handling employee documents.
|
|
102
|
+
|
|
103
|
+
Endpoints:
|
|
104
|
+
- GET /api/employees/{employeeId}/annualstatement
|
|
105
|
+
- GET /api/employees/{employeeId}/documents/types
|
|
106
|
+
- GET /api/employees/{employeeId}/documents/folders
|
|
107
|
+
- POST /api/employees/{employeeId}/documents
|
|
108
|
+
- GET /api/documents/{taskId}
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self, nmbrs):
|
|
112
|
+
self.nmbrs = nmbrs
|
|
113
|
+
|
|
114
|
+
def get_annual_statement(self, employee_id: str, year: Optional[int] = None) -> BytesIO:
|
|
115
|
+
"""
|
|
116
|
+
Get an employee annual statement for the given year.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
employee_id: The ID of the employee
|
|
120
|
+
year: Optional year filter
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
BytesIO containing the annual statement PDF
|
|
124
|
+
"""
|
|
125
|
+
params = {}
|
|
126
|
+
if year is not None:
|
|
127
|
+
params['year'] = year
|
|
128
|
+
|
|
129
|
+
resp = self.nmbrs.session.get(
|
|
130
|
+
f"{self.nmbrs.base_url}employees/{employee_id}/annualstatement",
|
|
131
|
+
params=params,
|
|
132
|
+
timeout=self.nmbrs.timeout
|
|
133
|
+
)
|
|
134
|
+
resp.raise_for_status()
|
|
135
|
+
task_id = resp.json()['taskId']
|
|
136
|
+
|
|
137
|
+
# Retrieve the actual document content
|
|
138
|
+
resp = self.nmbrs.session.get(
|
|
139
|
+
f"{self.nmbrs.base_url}documents/{task_id}",
|
|
140
|
+
timeout=self.nmbrs.timeout
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return BytesIO(resp.content)
|
|
144
|
+
|
|
145
|
+
def get_types(self, employee_id: str) -> pd.DataFrame:
|
|
146
|
+
"""
|
|
147
|
+
Get the document types available for a specific employee.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
employee_id: The ID of the employee
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
DataFrame containing document types
|
|
154
|
+
"""
|
|
155
|
+
request = requests.Request(
|
|
156
|
+
method='GET',
|
|
157
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/documents/types"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
161
|
+
return pd.DataFrame(data)
|
|
162
|
+
|
|
163
|
+
def get_folders(self, employee_id: str) -> pd.DataFrame:
|
|
164
|
+
"""
|
|
165
|
+
Get document folders for a specific employee.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
employee_id: The ID of the employee
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
DataFrame containing document folders
|
|
172
|
+
"""
|
|
173
|
+
request = requests.Request(
|
|
174
|
+
method='GET',
|
|
175
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/documents/folders"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
179
|
+
return pd.DataFrame(data)
|
|
180
|
+
|
|
181
|
+
def upload(self,
|
|
182
|
+
employee_id: str,
|
|
183
|
+
file: BinaryIO,
|
|
184
|
+
file_name: str,
|
|
185
|
+
file_extension: str,
|
|
186
|
+
document_type: str,
|
|
187
|
+
description: Optional[str] = None,
|
|
188
|
+
number: Optional[str] = None,
|
|
189
|
+
is_visible_for_employee: Optional[bool] = None,
|
|
190
|
+
folder: Optional[str] = None,
|
|
191
|
+
valid_from: Optional[str] = None,
|
|
192
|
+
valid_to: Optional[str] = None):
|
|
193
|
+
"""
|
|
194
|
+
Upload a document for a specific employee.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
employee_id: The ID of the employee
|
|
198
|
+
file: Binary file content to upload
|
|
199
|
+
file_name: Name of the file
|
|
200
|
+
file_extension: Extension of the file (e.g., "pdf", "docx")
|
|
201
|
+
document_type: Type of the document
|
|
202
|
+
description: Optional description of the document
|
|
203
|
+
number: Optional document number
|
|
204
|
+
is_visible_for_employee: Optional flag if document is visible for employee
|
|
205
|
+
folder: Optional folder UUID to place document in
|
|
206
|
+
valid_from: Optional valid from date (ISO format)
|
|
207
|
+
valid_to: Optional valid to date (ISO format)
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
Response from the API
|
|
211
|
+
"""
|
|
212
|
+
if self.nmbrs.mock_mode:
|
|
213
|
+
return {
|
|
214
|
+
'employee_id': employee_id,
|
|
215
|
+
'file_name': file_name,
|
|
216
|
+
'file_extension': file_extension,
|
|
217
|
+
'document_type': document_type,
|
|
218
|
+
'description': description
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# Prepare multipart form data
|
|
222
|
+
files = {
|
|
223
|
+
'file': (file_name, file)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
data = {
|
|
227
|
+
'fileName': file_name,
|
|
228
|
+
'fileExtension': file_extension,
|
|
229
|
+
'type': document_type
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if description is not None:
|
|
233
|
+
data['description'] = description
|
|
234
|
+
if number is not None:
|
|
235
|
+
data['number'] = number
|
|
236
|
+
if is_visible_for_employee is not None:
|
|
237
|
+
data['isVisibleForEmployee'] = str(is_visible_for_employee).lower()
|
|
238
|
+
if folder is not None:
|
|
239
|
+
data['folder'] = folder
|
|
240
|
+
if valid_from is not None:
|
|
241
|
+
data['validFrom'] = valid_from
|
|
242
|
+
if valid_to is not None:
|
|
243
|
+
data['validTo'] = valid_to
|
|
244
|
+
|
|
245
|
+
resp = self.nmbrs.session.post(
|
|
246
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/documents",
|
|
247
|
+
data=data,
|
|
248
|
+
files=files,
|
|
249
|
+
timeout=self.nmbrs.timeout
|
|
250
|
+
)
|
|
251
|
+
resp.raise_for_status()
|
|
252
|
+
return resp.json()
|
|
253
|
+
|
|
254
|
+
def get_content(self, task_id: str) -> BytesIO:
|
|
255
|
+
"""
|
|
256
|
+
Get document content by task ID.
|
|
257
|
+
|
|
258
|
+
Use this to retrieve documents after getting a task ID from other endpoints.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
task_id: The task ID from a previous document request
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
BytesIO containing the document content
|
|
265
|
+
"""
|
|
266
|
+
resp = self.nmbrs.session.get(
|
|
267
|
+
f"{self.nmbrs.base_url}documents/{task_id}",
|
|
268
|
+
timeout=self.nmbrs.timeout
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return BytesIO(resp.content)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import requests
|
|
@@ -16,7 +16,9 @@ class EmployeeWageTaxSettings:
|
|
|
16
16
|
self.nmbrs = nmbrs
|
|
17
17
|
|
|
18
18
|
def get(self,
|
|
19
|
-
|
|
19
|
+
company_id: Optional[str] = None,
|
|
20
|
+
employee_id: str = None,
|
|
21
|
+
created_from: Optional[str] = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
20
22
|
"""
|
|
21
23
|
Get wage tax settings history for employees in companies.
|
|
22
24
|
|
|
@@ -27,8 +29,11 @@ class EmployeeWageTaxSettings:
|
|
|
27
29
|
Tuple of (valid_data, invalid_data) DataFrames
|
|
28
30
|
"""
|
|
29
31
|
wage_tax_settings = pd.DataFrame()
|
|
30
|
-
|
|
31
|
-
wage_tax_settings =
|
|
32
|
+
if company_id:
|
|
33
|
+
wage_tax_settings = self._get(company_id, employee_id, created_from)
|
|
34
|
+
else:
|
|
35
|
+
for company in self.nmbrs.company_ids:
|
|
36
|
+
wage_tax_settings = pd.concat([wage_tax_settings, self._get(company, employee_id, created_from)])
|
|
32
37
|
|
|
33
38
|
valid_settings, invalid_settings = Functions.validate_data(
|
|
34
39
|
df=wage_tax_settings, schema=EmployeeWageTaxSettingsGet, debug=True
|
|
@@ -38,7 +43,8 @@ class EmployeeWageTaxSettings:
|
|
|
38
43
|
|
|
39
44
|
def _get(self,
|
|
40
45
|
company_id: str,
|
|
41
|
-
employee_id: str = None
|
|
46
|
+
employee_id: str = None,
|
|
47
|
+
created_from: Optional[str] = None) -> pd.DataFrame:
|
|
42
48
|
"""
|
|
43
49
|
Get wage tax settings history for a specific company.
|
|
44
50
|
|
|
@@ -52,6 +58,8 @@ class EmployeeWageTaxSettings:
|
|
|
52
58
|
params = {}
|
|
53
59
|
if employee_id:
|
|
54
60
|
params['employeeId'] = employee_id
|
|
61
|
+
if created_from is not None:
|
|
62
|
+
params['createdFrom'] = created_from
|
|
55
63
|
|
|
56
64
|
request = requests.Request(
|
|
57
65
|
method='GET',
|