brynq-sdk-nmbrs 2.3.1.dev0__py3-none-any.whl → 2.3.3.dev0__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 +86 -80
- brynq_sdk_nmbrs/address.py +82 -2
- brynq_sdk_nmbrs/bank.py +5 -2
- brynq_sdk_nmbrs/children.py +96 -61
- brynq_sdk_nmbrs/companies.py +45 -1
- brynq_sdk_nmbrs/debtors.py +76 -2
- brynq_sdk_nmbrs/department.py +113 -1
- brynq_sdk_nmbrs/document.py +50 -0
- brynq_sdk_nmbrs/employee_wage_tax_settings.py +16 -9
- brynq_sdk_nmbrs/function.py +128 -2
- brynq_sdk_nmbrs/leave.py +105 -8
- brynq_sdk_nmbrs/salaries.py +75 -3
- brynq_sdk_nmbrs/schedules.py +77 -3
- brynq_sdk_nmbrs/schemas/address.py +33 -4
- brynq_sdk_nmbrs/schemas/children.py +67 -0
- brynq_sdk_nmbrs/schemas/company.py +16 -0
- brynq_sdk_nmbrs/schemas/debtor.py +23 -1
- brynq_sdk_nmbrs/schemas/department.py +33 -0
- brynq_sdk_nmbrs/schemas/document.py +13 -0
- brynq_sdk_nmbrs/schemas/employees.py +3 -1
- brynq_sdk_nmbrs/schemas/function.py +28 -0
- brynq_sdk_nmbrs/schemas/leave.py +12 -0
- brynq_sdk_nmbrs/schemas/salary.py +18 -0
- brynq_sdk_nmbrs/schemas/schedules.py +81 -16
- brynq_sdk_nmbrs/schemas/social_insurance.py +39 -6
- brynq_sdk_nmbrs/schemas/wage_tax.py +65 -5
- brynq_sdk_nmbrs/schemas/wage_tax_settings.py +42 -29
- brynq_sdk_nmbrs/schemas/wagecomponents.py +66 -9
- brynq_sdk_nmbrs/social_insurance.py +81 -3
- brynq_sdk_nmbrs/wage_tax.py +104 -3
- brynq_sdk_nmbrs/wagecomponents.py +81 -45
- {brynq_sdk_nmbrs-2.3.1.dev0.dist-info → brynq_sdk_nmbrs-2.3.3.dev0.dist-info}/METADATA +1 -1
- brynq_sdk_nmbrs-2.3.3.dev0.dist-info/RECORD +55 -0
- {brynq_sdk_nmbrs-2.3.1.dev0.dist-info → brynq_sdk_nmbrs-2.3.3.dev0.dist-info}/WHEEL +1 -1
- brynq_sdk_nmbrs-2.3.1.dev0.dist-info/RECORD +0 -52
- {brynq_sdk_nmbrs-2.3.1.dev0.dist-info → brynq_sdk_nmbrs-2.3.3.dev0.dist-info}/top_level.txt +0 -0
brynq_sdk_nmbrs/debtors.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
import requests
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
from zeep.exceptions import Fault
|
|
3
5
|
from brynq_sdk_functions import Functions
|
|
4
6
|
from .department import Departments
|
|
5
7
|
from .function import Functions as NmbrsFunctions
|
|
6
|
-
from .schemas.debtor import DebtorsGet
|
|
8
|
+
from .schemas.debtor import DebtorsGet, DebtorCreate, DebtorUpdate
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class Debtors:
|
|
@@ -12,7 +14,6 @@ class Debtors:
|
|
|
12
14
|
self.departments = Departments(nmbrs)
|
|
13
15
|
self.functions = NmbrsFunctions(nmbrs)
|
|
14
16
|
|
|
15
|
-
|
|
16
17
|
def get(self) -> (pd.DataFrame, pd.DataFrame):
|
|
17
18
|
request = requests.Request(method='GET',
|
|
18
19
|
url=f"{self.nmbrs.base_url}debtors")
|
|
@@ -23,3 +24,76 @@ class Debtors:
|
|
|
23
24
|
valid_debtors, invalid_debtors = Functions.validate_data(df=df, schema=DebtorsGet, debug=True)
|
|
24
25
|
|
|
25
26
|
return valid_debtors, invalid_debtors
|
|
27
|
+
|
|
28
|
+
def create(self, data: Dict[str, Any]) -> int:
|
|
29
|
+
"""
|
|
30
|
+
Create a new debtor using SOAP API.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
data: Dictionary containing debtor data with fields matching DebtorCreate schema:
|
|
34
|
+
- number: Debtor number
|
|
35
|
+
- name: Debtor name
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
The ID of the newly created debtor.
|
|
39
|
+
"""
|
|
40
|
+
debtor_model = DebtorCreate(**data)
|
|
41
|
+
|
|
42
|
+
if self.nmbrs.mock_mode:
|
|
43
|
+
return 12345 # Mock ID
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
DebtorType = self.nmbrs.soap_client_debtors.get_type('ns0:Debtor')
|
|
47
|
+
soap_debtor = DebtorType(
|
|
48
|
+
Id=0, # 0 for new debtor
|
|
49
|
+
Number=debtor_model.number,
|
|
50
|
+
Name=debtor_model.name
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
response = self.nmbrs.soap_client_debtors.service.Debtor_Insert(
|
|
54
|
+
Debtor=soap_debtor,
|
|
55
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
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]):
|
|
65
|
+
"""
|
|
66
|
+
Update a debtor using SOAP API.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
data: Dictionary containing debtor data with fields matching DebtorUpdate schema:
|
|
70
|
+
- debtor_id: Debtor ID to update
|
|
71
|
+
- number: Debtor number
|
|
72
|
+
- name: Debtor name
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Response from the API
|
|
76
|
+
"""
|
|
77
|
+
debtor_model = DebtorUpdate(**data)
|
|
78
|
+
|
|
79
|
+
if self.nmbrs.mock_mode:
|
|
80
|
+
return debtor_model
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
DebtorType = self.nmbrs.soap_client_debtors.get_type('ns0:Debtor')
|
|
84
|
+
soap_debtor = DebtorType(
|
|
85
|
+
Id=debtor_model.debtor_id,
|
|
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)}")
|
brynq_sdk_nmbrs/department.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import math
|
|
2
1
|
from typing import Any, Dict
|
|
3
2
|
|
|
4
3
|
import pandas as pd
|
|
5
4
|
import requests
|
|
5
|
+
from zeep.exceptions import Fault
|
|
6
6
|
|
|
7
7
|
from brynq_sdk_functions import Functions
|
|
8
8
|
|
|
9
9
|
from .schemas.department import (
|
|
10
10
|
DepartmentCreate,
|
|
11
11
|
DepartmentGet,
|
|
12
|
+
DepartmentMasterCreate,
|
|
13
|
+
DepartmentMasterDelete,
|
|
14
|
+
DepartmentMasterUpdate,
|
|
12
15
|
EmployeeDepartmentGet,
|
|
13
16
|
EmployeeDepartmentUpdate,
|
|
14
17
|
Period,
|
|
@@ -115,6 +118,8 @@ class EmployeeDepartment:
|
|
|
115
118
|
|
|
116
119
|
|
|
117
120
|
class Departments:
|
|
121
|
+
"""Master department operations (Debtor level) - uses SOAP for create/update/delete."""
|
|
122
|
+
|
|
118
123
|
def __init__(self, nmbrs):
|
|
119
124
|
self.nmbrs = nmbrs
|
|
120
125
|
|
|
@@ -129,3 +134,110 @@ class Departments:
|
|
|
129
134
|
valid_departments, invalid_departments = Functions.validate_data(df=df, schema=DepartmentGet, debug=True)
|
|
130
135
|
|
|
131
136
|
return valid_departments, invalid_departments
|
|
137
|
+
|
|
138
|
+
def create(self, data: Dict[str, Any]) -> int:
|
|
139
|
+
"""
|
|
140
|
+
Create a new master department for a debtor using SOAP API.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
data: Dictionary containing department data with fields matching DepartmentMasterCreate schema:
|
|
144
|
+
- debtor_id: Debtor ID
|
|
145
|
+
- code: Department code
|
|
146
|
+
- description: Department description
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
The ID of the newly created department.
|
|
150
|
+
"""
|
|
151
|
+
dept_model = DepartmentMasterCreate(**data)
|
|
152
|
+
|
|
153
|
+
if self.nmbrs.mock_mode:
|
|
154
|
+
return 12345 # Mock ID
|
|
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.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
data: Dictionary containing department data with fields matching DepartmentMasterUpdate schema:
|
|
182
|
+
- debtor_id: Debtor ID
|
|
183
|
+
- department_id: Department ID to update
|
|
184
|
+
- code: Department code
|
|
185
|
+
- description: Department description
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Response from the API
|
|
189
|
+
"""
|
|
190
|
+
dept_model = DepartmentMasterUpdate(**data)
|
|
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]):
|
|
216
|
+
"""
|
|
217
|
+
Delete a master department for a debtor using SOAP API.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
data: Dictionary containing department data with fields matching DepartmentMasterDelete schema:
|
|
221
|
+
- debtor_id: Debtor ID
|
|
222
|
+
- department_id: Department ID to delete
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Response from the API
|
|
226
|
+
"""
|
|
227
|
+
dept_model = DepartmentMasterDelete(**data)
|
|
228
|
+
|
|
229
|
+
if self.nmbrs.mock_mode:
|
|
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)}")
|
brynq_sdk_nmbrs/document.py
CHANGED
|
@@ -1,7 +1,57 @@
|
|
|
1
1
|
from io import BytesIO
|
|
2
|
+
import base64
|
|
3
|
+
from typing import Dict, Any, Union
|
|
2
4
|
|
|
3
5
|
import pandas as pd
|
|
4
6
|
import requests
|
|
7
|
+
from zeep.exceptions import Fault
|
|
8
|
+
|
|
9
|
+
from .schemas.document import DocumentUpload
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EmployeeDocument:
|
|
13
|
+
"""Handle employee document operations via SOAP API."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, nmbrs):
|
|
16
|
+
self.nmbrs = nmbrs
|
|
17
|
+
|
|
18
|
+
def upload(self, data: Dict[str, Any], file_content: bytes) -> bool:
|
|
19
|
+
"""
|
|
20
|
+
Upload a document to an employee using SOAP API.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
data: Dictionary containing document data with fields matching DocumentUpload schema:
|
|
24
|
+
- employee_id: Employee ID
|
|
25
|
+
- document_name: Document name (with extension, e.g., "contract.pdf")
|
|
26
|
+
- document_type_guid: Document type GUID
|
|
27
|
+
file_content: Binary content of the file to upload
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
True if upload was successful
|
|
31
|
+
"""
|
|
32
|
+
doc_model = DocumentUpload(**data)
|
|
33
|
+
|
|
34
|
+
if self.nmbrs.mock_mode:
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
# Convert file content to base64
|
|
39
|
+
body_base64 = base64.b64encode(file_content)
|
|
40
|
+
|
|
41
|
+
response = self.nmbrs.soap_client_employees.service.EmployeeDocument_UploadDocument(
|
|
42
|
+
EmployeeId=doc_model.employee_id,
|
|
43
|
+
StrDocumentName=doc_model.document_name,
|
|
44
|
+
Body=body_base64,
|
|
45
|
+
GuidDocumentType=doc_model.document_type_guid,
|
|
46
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return response
|
|
50
|
+
|
|
51
|
+
except Fault as e:
|
|
52
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
raise Exception(f"Failed to upload document: {str(e)}")
|
|
5
55
|
|
|
6
56
|
|
|
7
57
|
class Payslip:
|
|
@@ -28,9 +28,7 @@ class EmployeeWageTaxSettings:
|
|
|
28
28
|
"""
|
|
29
29
|
wage_tax_settings = pd.DataFrame()
|
|
30
30
|
for company in self.nmbrs.company_ids:
|
|
31
|
-
|
|
32
|
-
if not company_settings.empty:
|
|
33
|
-
wage_tax_settings = pd.concat([wage_tax_settings, company_settings])
|
|
31
|
+
wage_tax_settings = pd.concat([wage_tax_settings, self._get(company, employee_id)])
|
|
34
32
|
|
|
35
33
|
valid_settings, invalid_settings = Functions.validate_data(
|
|
36
34
|
df=wage_tax_settings, schema=EmployeeWageTaxSettingsGet, debug=True
|
|
@@ -62,17 +60,26 @@ class EmployeeWageTaxSettings:
|
|
|
62
60
|
)
|
|
63
61
|
|
|
64
62
|
data = self.nmbrs.get_paginated_result(request)
|
|
65
|
-
|
|
66
|
-
if not data:
|
|
67
|
-
return pd.DataFrame()
|
|
68
|
-
|
|
69
|
-
# Normalize nested structure similar to departments/employments
|
|
70
63
|
df = pd.json_normalize(
|
|
71
64
|
data,
|
|
72
|
-
record_path='
|
|
65
|
+
record_path='wageTaxSettings',
|
|
73
66
|
meta=['employeeId']
|
|
74
67
|
)
|
|
75
68
|
|
|
69
|
+
# Flatten nested Calc30PercentRuling object if it exists
|
|
70
|
+
if 'Calc30PercentRuling' in df.columns:
|
|
71
|
+
calc_ruling_dicts = df['Calc30PercentRuling'].dropna()
|
|
72
|
+
if not calc_ruling_dicts.empty and isinstance(calc_ruling_dicts.iloc[0], dict):
|
|
73
|
+
calc_ruling_expanded = pd.json_normalize(calc_ruling_dicts)
|
|
74
|
+
calc_ruling_expanded.columns = [f'Calc30PercentRuling.{col}' for col in calc_ruling_expanded.columns]
|
|
75
|
+
calc_ruling_expanded = calc_ruling_expanded.reindex(df.index)
|
|
76
|
+
df = pd.concat([df.drop(columns=['Calc30PercentRuling']), calc_ruling_expanded], axis=1)
|
|
77
|
+
else:
|
|
78
|
+
# If Calc30PercentRuling is not dict-like or all None, create columns with None
|
|
79
|
+
for col in ['Calc30PercentRuling.Cal30PercentRuling', 'Calc30PercentRuling.endPeriod', 'Calc30PercentRuling.endYear']:
|
|
80
|
+
df[col] = None
|
|
81
|
+
df = df.drop(columns=['Calc30PercentRuling'])
|
|
82
|
+
|
|
76
83
|
return df
|
|
77
84
|
|
|
78
85
|
def create(self, employee_id: str, data: Dict[str, Any]):
|
brynq_sdk_nmbrs/function.py
CHANGED
|
@@ -2,8 +2,13 @@ import pandas as pd
|
|
|
2
2
|
import requests
|
|
3
3
|
from brynq_sdk_functions import Functions as BrynQFunctions
|
|
4
4
|
import math
|
|
5
|
-
from typing import Dict, Any
|
|
6
|
-
from .schemas.function import
|
|
5
|
+
from typing import Dict, Any, Union
|
|
6
|
+
from .schemas.function import (
|
|
7
|
+
EmployeeFunctionGet, FunctionUpdate, FunctionGet,
|
|
8
|
+
FunctionCreate, FunctionDelete, FunctionMasterUpdate
|
|
9
|
+
)
|
|
10
|
+
from zeep.exceptions import Fault
|
|
11
|
+
from zeep.helpers import serialize_object
|
|
7
12
|
|
|
8
13
|
|
|
9
14
|
class EmployeeFunction:
|
|
@@ -68,9 +73,44 @@ class EmployeeFunction:
|
|
|
68
73
|
)
|
|
69
74
|
return resp
|
|
70
75
|
|
|
76
|
+
def get_current(self, employee_id: Union[int, str]) -> pd.DataFrame:
|
|
77
|
+
"""
|
|
78
|
+
Get current function for a specific employee using SOAP API.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
employee_id: The ID of the employee
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
DataFrame with current function
|
|
85
|
+
"""
|
|
86
|
+
if self.nmbrs.mock_mode:
|
|
87
|
+
return pd.DataFrame()
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
response = self.nmbrs.soap_client_employees.service.Function_GetCurrent(
|
|
91
|
+
EmployeeId=int(employee_id),
|
|
92
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if response:
|
|
96
|
+
serialized = serialize_object(response)
|
|
97
|
+
if not isinstance(serialized, list):
|
|
98
|
+
serialized = [serialized]
|
|
99
|
+
df = pd.DataFrame(serialized)
|
|
100
|
+
df['employee_id'] = str(employee_id)
|
|
101
|
+
return df
|
|
102
|
+
else:
|
|
103
|
+
return pd.DataFrame()
|
|
104
|
+
|
|
105
|
+
except Fault as e:
|
|
106
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
71
107
|
|
|
72
108
|
|
|
73
109
|
class Functions:
|
|
110
|
+
"""
|
|
111
|
+
Master Functions (Debtor level) - manages function definitions.
|
|
112
|
+
Uses DebtorService SOAP API for create, update, delete.
|
|
113
|
+
"""
|
|
74
114
|
def __init__(self, nmbrs):
|
|
75
115
|
self.nmbrs = nmbrs
|
|
76
116
|
|
|
@@ -87,3 +127,89 @@ class Functions:
|
|
|
87
127
|
df = pd.DataFrame()
|
|
88
128
|
|
|
89
129
|
return valid_functions, invalid_functions
|
|
130
|
+
|
|
131
|
+
def create(self, debtor_id: Union[int, str], data: Dict[str, Any]):
|
|
132
|
+
"""
|
|
133
|
+
Create a new master function using SOAP API.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
debtor_id: The ID of the debtor
|
|
137
|
+
data: Dictionary containing function data with fields matching FunctionCreate schema
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Response from the API (function ID)
|
|
141
|
+
"""
|
|
142
|
+
function_model = FunctionCreate(**data)
|
|
143
|
+
|
|
144
|
+
if self.nmbrs.mock_mode:
|
|
145
|
+
return function_model
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
response = self.nmbrs.soap_client_debtors.service.Function_Insert(
|
|
149
|
+
DebtorId=int(debtor_id),
|
|
150
|
+
function={
|
|
151
|
+
'Id': 0, # 0 for new function
|
|
152
|
+
'Code': function_model.code,
|
|
153
|
+
'Description': function_model.description
|
|
154
|
+
},
|
|
155
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
156
|
+
)
|
|
157
|
+
return response
|
|
158
|
+
except Fault as e:
|
|
159
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
160
|
+
|
|
161
|
+
def delete(self, debtor_id: Union[int, str], function_id: Union[int, str]):
|
|
162
|
+
"""
|
|
163
|
+
Delete a master function using SOAP API.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
debtor_id: The ID of the debtor
|
|
167
|
+
function_id: The ID of the function to delete
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Response from the API
|
|
171
|
+
"""
|
|
172
|
+
delete_model = FunctionDelete(function_id=int(function_id))
|
|
173
|
+
|
|
174
|
+
if self.nmbrs.mock_mode:
|
|
175
|
+
return delete_model
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
response = self.nmbrs.soap_client_debtors.service.Function_Delete(
|
|
179
|
+
DebtorId=int(debtor_id),
|
|
180
|
+
id=delete_model.function_id,
|
|
181
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
182
|
+
)
|
|
183
|
+
return response
|
|
184
|
+
except Fault as e:
|
|
185
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
186
|
+
|
|
187
|
+
def update(self, debtor_id: Union[int, str], data: Dict[str, Any]):
|
|
188
|
+
"""
|
|
189
|
+
Update a master function using SOAP API.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
debtor_id: The ID of the debtor
|
|
193
|
+
data: Dictionary containing function data with fields matching FunctionMasterUpdate schema
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Response from the API
|
|
197
|
+
"""
|
|
198
|
+
function_model = FunctionMasterUpdate(**data)
|
|
199
|
+
|
|
200
|
+
if self.nmbrs.mock_mode:
|
|
201
|
+
return function_model
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
response = self.nmbrs.soap_client_debtors.service.Function_Update(
|
|
205
|
+
DebtorId=int(debtor_id),
|
|
206
|
+
function={
|
|
207
|
+
'Id': function_model.function_id,
|
|
208
|
+
'Code': function_model.code,
|
|
209
|
+
'Description': function_model.description
|
|
210
|
+
},
|
|
211
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_debtors}
|
|
212
|
+
)
|
|
213
|
+
return response
|
|
214
|
+
except Fault as e:
|
|
215
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
brynq_sdk_nmbrs/leave.py
CHANGED
|
@@ -2,8 +2,9 @@ import math
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
import requests
|
|
4
4
|
from brynq_sdk_functions import Functions
|
|
5
|
-
from typing import Dict, Any
|
|
6
|
-
from .schemas.leave import LeaveBalanceGet, LeaveGet, LeaveCreate
|
|
5
|
+
from typing import Dict, Any, Union
|
|
6
|
+
from .schemas.leave import LeaveBalanceGet, LeaveGet, LeaveCreate, LeaveDelete, LeaveUpdate
|
|
7
|
+
from zeep.exceptions import Fault
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class Leave:
|
|
@@ -95,6 +96,42 @@ class Leave:
|
|
|
95
96
|
)
|
|
96
97
|
return resp
|
|
97
98
|
|
|
99
|
+
def update(self, data: Dict[str, Any]):
|
|
100
|
+
"""
|
|
101
|
+
Update a leave request for an employee using SOAP API.
|
|
102
|
+
(REST API does not support leave update)
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
data: Dictionary containing leave data with fields matching LeaveUpdate schema:
|
|
106
|
+
- employee_id: Employee ID
|
|
107
|
+
- leave_id: Leave ID
|
|
108
|
+
- start_date: Start date
|
|
109
|
+
- end_date: End date
|
|
110
|
+
- description: Optional description
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Response from the API
|
|
114
|
+
"""
|
|
115
|
+
leave_model = LeaveUpdate(**data)
|
|
116
|
+
|
|
117
|
+
if self.nmbrs.mock_mode:
|
|
118
|
+
return leave_model
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
resp = self.nmbrs.soap_client_employees.service.Leave_Update(
|
|
122
|
+
EmployeeId=leave_model.employee_id,
|
|
123
|
+
Leave={
|
|
124
|
+
'Id': leave_model.leave_id,
|
|
125
|
+
'Start': leave_model.start_date,
|
|
126
|
+
'End': leave_model.end_date,
|
|
127
|
+
'Description': leave_model.description or ''
|
|
128
|
+
},
|
|
129
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
130
|
+
)
|
|
131
|
+
return resp
|
|
132
|
+
except Fault as e:
|
|
133
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
134
|
+
|
|
98
135
|
|
|
99
136
|
class LeaveBalance:
|
|
100
137
|
def __init__(self, nmbrs):
|
|
@@ -111,7 +148,12 @@ class LeaveBalance:
|
|
|
111
148
|
return valid_leave, invalid_leave
|
|
112
149
|
|
|
113
150
|
def _get(self,
|
|
114
|
-
company_id: str
|
|
151
|
+
company_id: str,
|
|
152
|
+
changed_from: str = None) -> pd.DataFrame:
|
|
153
|
+
"""
|
|
154
|
+
Note: changed_from parameter is accepted for consistency but not used
|
|
155
|
+
by the leaveBalances endpoint.
|
|
156
|
+
"""
|
|
115
157
|
try:
|
|
116
158
|
request = requests.Request(method='GET',
|
|
117
159
|
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/leaveBalances")
|
|
@@ -129,11 +171,66 @@ class LeaveBalance:
|
|
|
129
171
|
|
|
130
172
|
|
|
131
173
|
class LeaveGroup:
|
|
174
|
+
"""
|
|
175
|
+
LeaveGroup (Leave Type Groups) - uses SOAP CompanyLeaveTypeGroups_Get
|
|
176
|
+
"""
|
|
132
177
|
def __init__(self, nmbrs):
|
|
133
178
|
self.nmbrs = nmbrs
|
|
134
179
|
|
|
135
|
-
def get(self,
|
|
136
|
-
|
|
137
|
-
leave
|
|
138
|
-
|
|
139
|
-
|
|
180
|
+
def get(self, company_id: Union[int, str] = None) -> pd.DataFrame:
|
|
181
|
+
"""
|
|
182
|
+
Get leave type groups using SOAP API.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
company_id: Optional. If provided, get groups for this company only.
|
|
186
|
+
If not provided, get groups for all companies.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
DataFrame with leave type groups
|
|
190
|
+
"""
|
|
191
|
+
if self.nmbrs.mock_mode:
|
|
192
|
+
return pd.DataFrame()
|
|
193
|
+
|
|
194
|
+
from zeep.helpers import serialize_object
|
|
195
|
+
|
|
196
|
+
all_groups = []
|
|
197
|
+
try:
|
|
198
|
+
# If company_id provided, only get for that company
|
|
199
|
+
if company_id is not None:
|
|
200
|
+
company_ids = [int(company_id)]
|
|
201
|
+
else:
|
|
202
|
+
# Get all company IDs from SOAP
|
|
203
|
+
companies_response = self.nmbrs.soap_client_companies.service.List_GetAll(
|
|
204
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Handle response - could be list or object with Company attribute
|
|
208
|
+
companies = []
|
|
209
|
+
if companies_response:
|
|
210
|
+
if hasattr(companies_response, 'Company'):
|
|
211
|
+
companies = companies_response.Company or []
|
|
212
|
+
elif isinstance(companies_response, list):
|
|
213
|
+
companies = companies_response
|
|
214
|
+
|
|
215
|
+
company_ids = [c.ID for c in companies]
|
|
216
|
+
|
|
217
|
+
for cid in company_ids:
|
|
218
|
+
try:
|
|
219
|
+
response = self.nmbrs.soap_client_companies.service.CompanyLeaveTypeGroups_Get(
|
|
220
|
+
CompanyId=cid,
|
|
221
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header}
|
|
222
|
+
)
|
|
223
|
+
if response and response.LeaveTypeGroup:
|
|
224
|
+
groups = serialize_object(response.LeaveTypeGroup)
|
|
225
|
+
if not isinstance(groups, list):
|
|
226
|
+
groups = [groups]
|
|
227
|
+
for group in groups:
|
|
228
|
+
group['company_id'] = str(cid)
|
|
229
|
+
all_groups.extend(groups)
|
|
230
|
+
except Exception:
|
|
231
|
+
continue
|
|
232
|
+
|
|
233
|
+
return pd.DataFrame(all_groups)
|
|
234
|
+
|
|
235
|
+
except Fault as e:
|
|
236
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|