brynq-sdk-zenegy 1.3.1.dev0__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.
Files changed (38) hide show
  1. brynq_sdk_zenegy-1.3.1.dev0/PKG-INFO +10 -0
  2. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/__init__.py +23 -0
  3. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/absence.py +100 -0
  4. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/companies.py +43 -0
  5. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/cost_center.py +80 -0
  6. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/departments.py +77 -0
  7. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/employee_documents.py +40 -0
  8. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/employees.py +240 -0
  9. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/global_value_sets.py +260 -0
  10. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/global_values.py +265 -0
  11. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/paychecks.py +119 -0
  12. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/payroll.py +117 -0
  13. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/payslips.py +43 -0
  14. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/pensions.py +118 -0
  15. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/__init__.py +30 -0
  16. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/absences.py +393 -0
  17. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/companies.py +42 -0
  18. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/company_cost_centers.py +48 -0
  19. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/company_departments.py +147 -0
  20. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/employee_documents.py +30 -0
  21. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/employee_pay_checks.py +169 -0
  22. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/employee_pensions.py +140 -0
  23. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/employees.py +2110 -0
  24. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/global_value_sets.py +185 -0
  25. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/global_values.py +433 -0
  26. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/payrolls.py +134 -0
  27. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/payslips.py +32 -0
  28. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/schemas/supplements_and_deductions_rates.py +189 -0
  29. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/supplements_and_deductions_rates.py +71 -0
  30. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy/zenegy.py +221 -0
  31. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy.egg-info/PKG-INFO +10 -0
  32. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy.egg-info/SOURCES.txt +36 -0
  33. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy.egg-info/dependency_links.txt +1 -0
  34. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy.egg-info/not-zip-safe +1 -0
  35. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy.egg-info/requires.txt +1 -0
  36. brynq_sdk_zenegy-1.3.1.dev0/brynq_sdk_zenegy.egg-info/top_level.txt +1 -0
  37. brynq_sdk_zenegy-1.3.1.dev0/setup.cfg +4 -0
  38. brynq_sdk_zenegy-1.3.1.dev0/setup.py +16 -0
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 1.0
2
+ Name: brynq_sdk_zenegy
3
+ Version: 1.3.1.dev0
4
+ Summary: Zenegy wrapper from BrynQ
5
+ Home-page: UNKNOWN
6
+ Author: BrynQ
7
+ Author-email: support@brynq.com
8
+ License: BrynQ License
9
+ Description: Zenegy wrapper from BrynQ
10
+ Platform: UNKNOWN
@@ -0,0 +1,23 @@
1
+ """
2
+ Brynq SDK for Zenegy API integration
3
+ """
4
+
5
+ from .absence import Absences
6
+ from .companies import Companies
7
+ from .cost_center import CostCenter
8
+ from .departments import CompanyDepartments
9
+ from .employees import Employees
10
+ from .employee_documents import EmployeeDocuments
11
+ from .global_values import GlobalValues
12
+ from .global_value_sets import GlobalValueSets
13
+ from .paychecks import PayChecks
14
+ from .payroll import Payrolls
15
+ from .payslips import Payslips
16
+ from .pensions import Pensions
17
+ from .supplements_and_deductions_rates import SupplementsAndDeductionsRates
18
+
19
+ __all__ = [
20
+ 'Absences', 'Companies', 'CostCenter', 'CompanyDepartments', 'Employees',
21
+ 'EmployeeDocuments', 'GlobalValues', 'GlobalValueSets', 'PayChecks', 'Payrolls',
22
+ 'Payslips', 'Pensions', 'SupplementsAndDeductionsRates'
23
+ ]
@@ -0,0 +1,100 @@
1
+ from .schemas.absences import (CreateAbsenceRequest,
2
+ UpdateAbsenceRequest,
3
+ AbsenceGet)
4
+ import requests
5
+ from uuid import UUID
6
+ from brynq_sdk_functions import Functions
7
+ from typing import Dict, Any, List, Tuple
8
+ import pandas as pd
9
+
10
+ class Absences:
11
+ """
12
+ Handles all absence-related operations in Zenegy API
13
+ """
14
+
15
+ def __init__(self, zenegy):
16
+ """
17
+ Initialize the Absences class.
18
+
19
+ Args:
20
+ zenegy: The Zenegy instance to use for API calls
21
+ """
22
+ self.zenegy = zenegy
23
+ self.endpoint = f"api/companies/{self.zenegy.company_uid}/absence"
24
+
25
+ def get(self) -> Tuple[pd.DataFrame, pd.DataFrame]:
26
+ """
27
+ GetAbsenceDaysPerCompany
28
+ Returns:
29
+ DataFrame with absence information
30
+ """
31
+ endpoint = f"api/companies/{self.zenegy.company_uid}/absence"
32
+ try:
33
+ # Make the API request and get raw response
34
+ content = self.zenegy.get(endpoint=endpoint)
35
+
36
+ # Get data from response
37
+ data = content.get("data", [])
38
+ if data:
39
+ # Normalize the data
40
+ df = pd.json_normalize(
41
+ data,
42
+ sep='__'
43
+ )
44
+ if df.empty:
45
+ return pd.DataFrame(), pd.DataFrame()
46
+ # Validate data using schema
47
+ valid_data, invalid_data = Functions.validate_data(df, AbsenceGet)
48
+ return valid_data, invalid_data
49
+ return pd.DataFrame(), pd.DataFrame()
50
+ except Exception as e:
51
+ raise Exception(f"Failed to retrieve absences: {str(e)}") from e
52
+
53
+ def create(self, data: Dict[str, Any]) -> requests.Response:
54
+ """
55
+ Create
56
+ Args:
57
+ data (Dict[str, Any]): The data
58
+ Returns:
59
+ requests.Response: The API response
60
+ """
61
+ # Validate the data using Pydantic
62
+ try:
63
+ valid_data = CreateAbsenceRequest(**data)
64
+ req_body = valid_data.model_dump(by_alias=True, mode='json',exclude_none=True)
65
+ response = self.zenegy.post(endpoint=self.endpoint, json=req_body)
66
+ self.zenegy.raise_for_status_with_details(response)
67
+ return response
68
+ except Exception as e:
69
+ raise Exception(f"Failed to create absence: {str(e)}")
70
+
71
+ def delete(self, absence_uid: UUID) -> requests.Response:
72
+ endpoint = f"{self.endpoint}/{absence_uid}"
73
+ try:
74
+ response = self.zenegy.delete(endpoint=endpoint)
75
+ self.zenegy.raise_for_status_with_details(response)
76
+ return response
77
+ except Exception as e:
78
+ raise Exception(f"Failed to delete absence: {str(e)}")
79
+
80
+ def update(self, absence_uid: UUID, data: Dict[str, Any]) -> requests.Response:
81
+ """
82
+ Update an existing absence record.
83
+
84
+ Args:
85
+ absence_uid (UUID): The absence uid to update
86
+ data (Dict[str, Any]): The updated absence data
87
+
88
+ Returns:
89
+ requests.Response: The response from the API
90
+ """
91
+ # Validate the data using Pydantic
92
+ try:
93
+ valida_data = UpdateAbsenceRequest(**data)
94
+ req_body = valida_data.model_dump(by_alias=True, mode='json', exclude_none=True)
95
+ endpoint = f"{self.endpoint}/{absence_uid}"
96
+ response = self.zenegy.put(endpoint=endpoint, json=req_body)
97
+ self.zenegy.raise_for_status_with_details(response)
98
+ return response
99
+ except Exception as e:
100
+ raise Exception(f"Failed to update absences: {str(e)}")
@@ -0,0 +1,43 @@
1
+ from .schemas.companies import CompaniesGet
2
+ from typing import Tuple
3
+ import pandas as pd
4
+ from brynq_sdk_functions import Functions
5
+
6
+ class Companies:
7
+ """
8
+ Handles all company-related operations in Zenegy API
9
+ """
10
+
11
+ def __init__(self, zenegy):
12
+ """
13
+ Initialize the Companies class.
14
+
15
+ Args:
16
+ zenegy: The Zenegy instance to use for API calls
17
+ """
18
+ self.zenegy = zenegy
19
+ self.endpoint = "api/companies"
20
+
21
+ def get(self) -> Tuple[pd.DataFrame, pd.DataFrame]:
22
+ """
23
+ GetCurrentUserCompanies
24
+
25
+ Returns:
26
+ Tuple of (valid_data, invalid_data) DataFrames with company information
27
+ """
28
+ try:
29
+ # Make the API request and get raw response
30
+ content = self.zenegy.get(endpoint=self.endpoint)
31
+
32
+ # Normalize the data (content is already an array)
33
+ df = pd.DataFrame(content)
34
+
35
+ if df.empty:
36
+ return pd.DataFrame(), pd.DataFrame()
37
+
38
+ # Validate data using schema
39
+ valid_data, invalid_data = Functions.validate_data(df, CompaniesGet)
40
+ return valid_data, invalid_data
41
+
42
+ except Exception as e:
43
+ raise Exception(f"Failed to retrieve companies: {str(e)}") from e
@@ -0,0 +1,80 @@
1
+ import requests
2
+ from uuid import UUID
3
+ from .schemas.company_cost_centers import (CostCenterCreate,
4
+ CostCentersGet)
5
+ from brynq_sdk_functions import Functions
6
+ from typing import Dict, Any, List, Tuple
7
+ import pandas as pd
8
+
9
+
10
+ class CostCenter:
11
+ """
12
+ Handles all company cost center related operations in Zenegy API
13
+ """
14
+
15
+ def __init__(self, zenegy):
16
+ """
17
+ Initialize the Companycostcenters class.
18
+
19
+ Args:
20
+ zenegy: The Zenegy instance to use for API calls
21
+ """
22
+ self.zenegy = zenegy
23
+ self.endpoint = f"api/companies/{self.zenegy.company_uid}/cost-center"
24
+
25
+ def get(self) -> Tuple[pd.DataFrame, pd.DataFrame]:
26
+ """
27
+ GetCompanyCostCentersAsync
28
+ Returns:
29
+ Tuple of (valid_data, invalid_data) DataFrames with cost center information
30
+ """
31
+ try:
32
+ # Make the API request and get raw response
33
+ content = self.zenegy.get(endpoint=self.endpoint)
34
+
35
+ # Normalize the data (content is already an array)
36
+ df = pd.DataFrame(content)
37
+
38
+ if df.empty:
39
+ return pd.DataFrame(), pd.DataFrame()
40
+
41
+ # Validate data using schema
42
+ valid_data, invalid_data = Functions.validate_data(df, CostCentersGet)
43
+ return valid_data, invalid_data
44
+
45
+ except Exception as e:
46
+ raise Exception(f"Failed to retrieve cost centers: {str(e)}") from e
47
+
48
+ def create(self, data: Dict[str, Any]) -> requests.Response:
49
+ """
50
+ CreateCostCenterAsync
51
+ Args:
52
+ data (Dict[str, Any]): The data
53
+ Returns:
54
+ requests.Response: The API response
55
+ """
56
+ # Validate the data using Pydantic
57
+ try:
58
+ req_data = CostCenterCreate(**data)
59
+ req_body = req_data.model_dump(by_alias=True, mode='json',exclude_none=True)
60
+ response = self.zenegy.post(endpoint=self.endpoint, json=req_body)
61
+ self.zenegy.raise_for_status_with_details(response)
62
+ return response
63
+ except Exception as e:
64
+ raise Exception(f"Failed to create cost center: {str(e)}")
65
+
66
+ def delete(self, cost_center_uid: UUID) -> requests.Response:
67
+ """
68
+ DeleteCostCenterAsync
69
+ Args:
70
+ cost_center_uid (UUID): The cost center uid
71
+ Returns:
72
+ requests.Response: The API response
73
+ """
74
+ endpoint = f"{self.endpoint}/{cost_center_uid}"
75
+ try:
76
+ response = self.zenegy.delete(endpoint=endpoint)
77
+ self.zenegy.raise_for_status_with_details(response)
78
+ return response
79
+ except Exception as e:
80
+ raise Exception(f"Failed to delete cost center: {str(e)}")
@@ -0,0 +1,77 @@
1
+ from uuid import UUID
2
+ from .schemas.company_departments import DepartmentsGet
3
+ from typing import Tuple
4
+ import pandas as pd
5
+ from brynq_sdk_functions import Functions
6
+
7
+
8
+ class CompanyDepartments:
9
+ """
10
+ Handles all companydepartment-related operations in Zenegy API
11
+ """
12
+
13
+ def __init__(self, zenegy):
14
+ """
15
+ Initialize the CompanyDepartments class.
16
+
17
+ Args:
18
+ zenegy: The Zenegy instance to use for API calls
19
+ """
20
+ self.zenegy = zenegy
21
+ self.endpoint = f"api/companies/{self.zenegy.company_uid}/departments"
22
+
23
+ def get(self) -> Tuple[pd.DataFrame, pd.DataFrame]:
24
+ """
25
+ GetCompanyDepartmentsAsync
26
+ Returns:
27
+ Tuple of (valid_data, invalid_data) DataFrames with department information
28
+ """
29
+ try:
30
+ # Make the API request and get raw response
31
+ content = self.zenegy.get(endpoint=self.endpoint)
32
+
33
+ # Get data from response
34
+ data = content.get("data", [])
35
+
36
+ df = pd.json_normalize(
37
+ data,
38
+ sep='__'
39
+ )
40
+
41
+ if df.empty:
42
+ return pd.DataFrame(), pd.DataFrame()
43
+
44
+ # Validate data using schema
45
+ valid_data, invalid_data = Functions.validate_data(df, DepartmentsGet)
46
+ return valid_data, invalid_data
47
+
48
+ except Exception as e:
49
+ raise Exception(f"Failed to retrieve departments: {str(e)}") from e
50
+
51
+ def get_by_id(self, company_department_uid: UUID) -> Tuple[pd.DataFrame, pd.DataFrame]:
52
+ """
53
+ GetCompanyDepartment
54
+ Args:
55
+ company_department_uid (UUID): The company department uid
56
+ Returns:
57
+ Tuple of (valid_data, invalid_data) DataFrames with department information
58
+ """
59
+ try:
60
+ endpoint = f"{self.endpoint}/{company_department_uid}"
61
+ # Make the API request and get raw response
62
+ content = self.zenegy.get(endpoint=endpoint)
63
+
64
+ df = pd.json_normalize(
65
+ content,
66
+ sep='__'
67
+ )
68
+
69
+ if df.empty:
70
+ return pd.DataFrame(), pd.DataFrame()
71
+
72
+ # Validate data using schema
73
+ valid_data, invalid_data = Functions.validate_data(df, DepartmentsGet)
74
+ return valid_data, invalid_data
75
+
76
+ except Exception as e:
77
+ raise Exception(f"Failed to retrieve department by ID: {str(e)}") from e
@@ -0,0 +1,40 @@
1
+ from uuid import UUID
2
+ from typing import Tuple
3
+ import pandas as pd
4
+
5
+ from brynq_sdk_functions import Functions
6
+ from .schemas.employee_documents import EmployeeDocumentsGet
7
+
8
+
9
+ class EmployeeDocuments:
10
+ """Handles all employee document related operations in the Zenegy API"""
11
+
12
+ def __init__(self, zenegy):
13
+ """Initialize the EmployeeDocuments class with a Zenegy client instance."""
14
+ self.zenegy = zenegy
15
+
16
+ def get(self, employee_uid: UUID) -> Tuple[pd.DataFrame, pd.DataFrame]:
17
+ """
18
+ Retrieve all documents for a given employee.
19
+
20
+ Args:
21
+ employee_uid: The employee UID.
22
+
23
+ Returns:
24
+ Tuple of (valid_data, invalid_data) Pandas DataFrames.
25
+ """
26
+ try:
27
+ endpoint = (
28
+ f"api/employees/{employee_uid}/companies/{self.zenegy.company_uid}/documents"
29
+ )
30
+ content = self.zenegy.get(endpoint=endpoint)
31
+
32
+ records = content if isinstance(content, list) else content.get("data", [])
33
+ df = pd.json_normalize(records, sep="__")
34
+ if df.empty:
35
+ return pd.DataFrame(), pd.DataFrame()
36
+
37
+ valid_data, invalid_data = Functions.validate_data(df, EmployeeDocumentsGet)
38
+ return valid_data, invalid_data
39
+ except Exception as e:
40
+ raise Exception(f"Failed to retrieve employee documents: {str(e)}") from e
@@ -0,0 +1,240 @@
1
+ # Generated endpoint class for tag: Employees
2
+ import pandas as pd
3
+ import requests
4
+ from uuid import UUID
5
+ from brynq_sdk_functions import Functions
6
+
7
+ from .schemas.employees import (EmployeeCreate,
8
+ EmployeeUpdate,
9
+ EmployeesGet,
10
+ EmployeesGetById,
11
+ EmployeeEmploymentDataUpdate,
12
+ EmployeeEmploymentUpdate,
13
+ EmployeeAdditionalUpdate,
14
+ StartSaldo)
15
+ from .paychecks import PayChecks
16
+ from .pensions import Pensions
17
+ from typing import Dict, Any, Tuple
18
+
19
+
20
+ class Employees:
21
+ """
22
+ Handles all employees-related operations in Zenegy API
23
+ """
24
+
25
+ def __init__(self, zenegy):
26
+ """
27
+ Initialize the Employees class.
28
+
29
+ Args:
30
+ zenegy: The Zenegy instance to use for API calls
31
+ """
32
+ self.zenegy = zenegy
33
+ self.endpoint = f"api/companies/{self.zenegy.company_uid}/employees"
34
+
35
+ # Initialize paychecks and pensions
36
+ self.paychecks = PayChecks(zenegy)
37
+ self.pensions = Pensions(zenegy)
38
+
39
+ def get(self) -> Tuple[pd.DataFrame, pd.DataFrame]:
40
+ """
41
+ GetEmployeeBasesAsync
42
+ Returns:
43
+ Tuple of (valid_data, invalid_data) DataFrames with employee information
44
+ """
45
+ try:
46
+ # Make the API request and get raw response
47
+ content = self.zenegy.get(endpoint=self.endpoint)
48
+
49
+ # Get data from response
50
+ data = content.get("data", [])
51
+
52
+ # Normalize the data
53
+ df = pd.json_normalize(
54
+ data,
55
+ sep='__'
56
+ )
57
+
58
+ if df.empty:
59
+ return pd.DataFrame(), pd.DataFrame()
60
+
61
+ # Validate data using schema
62
+ valid_data, invalid_data = Functions.validate_data(df, EmployeesGet)
63
+ return valid_data, invalid_data
64
+
65
+ except Exception as e:
66
+ raise Exception(f"Failed to retrieve employees: {str(e)}") from e
67
+
68
+ def get_by_id(self, employee_uid: UUID) -> Tuple[pd.DataFrame, pd.DataFrame]:
69
+ """
70
+ GetEmployeeAsync
71
+
72
+ Args:
73
+ employee_uid (UUID): The employee uid
74
+ Returns:
75
+ Tuple of (valid_data, invalid_data) DataFrames with employee information
76
+ """
77
+ endpoint = f"{self.endpoint}/{employee_uid}"
78
+ try:
79
+ # Make the API request and get raw response
80
+ content = self.zenegy.get(endpoint=endpoint)
81
+
82
+ # Normalize the data (content is already a dict)
83
+ df = pd.json_normalize(
84
+ [content], # Wrap single object in list for normalization
85
+ sep='__'
86
+ )
87
+
88
+ if df.empty:
89
+ return pd.DataFrame(), pd.DataFrame()
90
+
91
+ # Validate data using detailed by-id schema
92
+ valid_data, invalid_data = Functions.validate_data(df, EmployeesGetById)
93
+ return valid_data, invalid_data
94
+
95
+ except Exception as e:
96
+ raise Exception(f"Failed to retrieve employee by ID: {str(e)}") from e
97
+
98
+ def create(self, data: Dict[str, Any]) -> requests.Response:
99
+ """
100
+ PostEmployeeAsync
101
+ Args:
102
+ data (Dict[str, Any]): The data
103
+ Returns:
104
+ requests.Response: The API response
105
+ """
106
+ try:
107
+ req_data = EmployeeCreate(**data)
108
+ req_body = req_data.model_dump(by_alias=True, mode='json', exclude_none=True)
109
+ response = self.zenegy.post(endpoint=self.endpoint.lstrip('/'), json=req_body)
110
+ patch_body = self.create_update_body(data)
111
+
112
+ # if the patch body is bigger than the request body, that means that there are fields left for the patch:
113
+ # Count actual fields at the lowest level for proper comparison
114
+ patch_field_count = self._count_nested_fields(patch_body)
115
+ if patch_field_count > len(req_body):
116
+ response_data = response.json()
117
+ if isinstance(response_data, str):
118
+ uid = response_data
119
+ elif isinstance(response_data, dict):
120
+ uid = response_data['data']['uid']
121
+ patch_endpoint = f"{self.endpoint}/{uid}"
122
+
123
+ response = self.zenegy.put(
124
+ endpoint=patch_endpoint.lstrip('/'),
125
+ json=patch_body
126
+ )
127
+ self.zenegy.raise_for_status_with_details(response)
128
+ return response
129
+
130
+ self.zenegy.raise_for_status_with_details(response)
131
+ return response
132
+ except Exception as e:
133
+ raise Exception(f"Failed to create employee: {str(e)}")
134
+
135
+ def update(self, data: Dict[str, Any]) -> requests.Response:
136
+ """
137
+ UpsertEmployeeAsync
138
+
139
+ Args:
140
+ data (Dict[str, Any]): The data to update
141
+
142
+ Returns:
143
+ requests.Response: The API response
144
+ """
145
+ endpoint = f"api/companies/{self.zenegy.company_uid}/employees"
146
+ try:
147
+ req_body = self.create_update_body(data)
148
+ uid = data.get('employee_uid')
149
+ endpoint = f"{self.endpoint}/{uid}".lstrip('/')
150
+ response = self.zenegy.put(endpoint=endpoint, json=req_body)
151
+ self.zenegy.raise_for_status_with_details(response)
152
+ return response
153
+ except Exception as e:
154
+ raise Exception(f"Failed to update employee: {str(e)}")
155
+
156
+ def delete(self, employee_uid: UUID) -> requests.Response:
157
+ """
158
+ DeleteEmployee
159
+
160
+ Args:
161
+ employee_uid (UUID): The employee uid
162
+ Returns:
163
+ requests.Response: The API response
164
+ """
165
+ endpoint = f"{self.endpoint}/{employee_uid}"
166
+ try:
167
+ response = self.zenegy.delete(endpoint=endpoint)
168
+ self.zenegy.raise_for_status_with_details(response)
169
+ return response
170
+ except Exception as e:
171
+ raise Exception(f"Failed to delete employee: {str(e)}")
172
+
173
+ def create_update_body(self, data: Dict[str, Any]) -> Dict[str, Any]:
174
+ """
175
+ Create the update body for the employee.
176
+ """
177
+ # There is strange logic in Zenegy that you can create an employee with only a limited set of fields. Afterward, you have to patch the employee with the rest of the fields.
178
+ update_data = EmployeeUpdate(**data)
179
+ body = update_data.model_dump(by_alias=True, mode='json', exclude_none=True)
180
+
181
+
182
+ #-- START TODO: This is a temporary solution to handle the additional fields for the contract related fields in mft.
183
+ #additional fields for contract related fields
184
+ schema_map = {
185
+ "startSaldo": StartSaldo,
186
+ "employmentData": EmployeeEmploymentDataUpdate,
187
+ "employeeEmployment": EmployeeEmploymentUpdate,
188
+ "employeeAditional": EmployeeAdditionalUpdate
189
+ }
190
+
191
+ result_body = {"updateEmployeeBase": body}
192
+
193
+ # Initialize required top-level fields with empty objects (API requires these to always be present)
194
+ result_body["startSaldo"] = {} # can be passed empty
195
+ result_body["employeeEmployment"] = {}
196
+ result_body["employeeAditional"] = {"monthlySalaryFixedBase": 0}
197
+
198
+ # Check if any fields from data match schema fields, and serialize accordingly
199
+ for schema_name, schema_class in schema_map.items():
200
+ schema_fields = schema_class.model_fields.keys()
201
+ # Find matching keys between data and schema fields
202
+ matching_data = {k: v for k, v in data.items() if k in schema_fields}
203
+
204
+ # Skip if employee_number is the only field present
205
+ if matching_data: # and not (len(matching_data) == 1 and 'employee_number' in matching_data):
206
+ # Serialize the matching data using the schema
207
+ schema_instance = schema_class(**matching_data)
208
+ serialized_data = schema_instance.model_dump(by_alias=True, mode='json', exclude_none=True)
209
+
210
+ # startSaldo appears both inside updateEmployeeBase and at top level
211
+ if schema_name == "startSaldo":
212
+ result_body["updateEmployeeBase"][schema_name] = serialized_data
213
+ result_body[schema_name] = serialized_data
214
+ # employmentData appears inside updateEmployeeBase
215
+ elif schema_name == "employmentData":
216
+ result_body["updateEmployeeBase"][schema_name] = serialized_data
217
+ # employeeEmployment and employeeAditional appear at top level
218
+ else:
219
+ result_body[schema_name] = serialized_data
220
+ #--END TODO
221
+ return result_body
222
+
223
+ def _count_nested_fields(self, data: Dict[str, Any]) -> int:
224
+ """
225
+ Count the actual fields at the lowest level of a nested dictionary.
226
+ https://www.google.com/search?client=firefox-b-d&sca_esv=4acce884baa46368&sxsrf=AE3TifPbAKp8zW8Gczemzqd3WYBgKrcwlg:1761129017606&q=recursion&spell=1&sa=X&ved=2ahUKEwja3v3rzLeQAxUwwAIHHZJVKZUQBSgAegQIFhAB
227
+
228
+ Args:
229
+ data: Dictionary that may contain nested dictionaries
230
+
231
+ Returns:
232
+ int: Total count of fields at the lowest level
233
+ """
234
+ count = 0
235
+ for value in data.values():
236
+ if isinstance(value, dict):
237
+ count += self._count_nested_fields(value)
238
+ else:
239
+ count += 1
240
+ return count