brynq-sdk-nmbrs 2.0.0__tar.gz → 2.1.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.
Files changed (45) hide show
  1. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/PKG-INFO +1 -1
  2. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/__init__.py +6 -5
  3. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/companies.py +4 -2
  4. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/debtors.py +8 -8
  5. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/department.py +3 -2
  6. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/employees.py +2 -2
  7. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/function.py +6 -4
  8. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/hours.py +1 -1
  9. brynq_sdk_nmbrs-2.1.0/brynq_sdk_nmbrs/manager.py +294 -0
  10. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/salary_tables.py +20 -15
  11. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schedules.py +3 -3
  12. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/__init__.py +5 -2
  13. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/address.py +9 -9
  14. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/bank.py +1 -1
  15. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/contracts.py +6 -6
  16. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/costcenter.py +10 -10
  17. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/costunit.py +1 -1
  18. brynq_sdk_nmbrs-2.1.0/brynq_sdk_nmbrs/schemas/debtor.py +16 -0
  19. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/department.py +13 -3
  20. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/employees.py +45 -45
  21. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/function.py +9 -3
  22. brynq_sdk_nmbrs-2.1.0/brynq_sdk_nmbrs/schemas/manager.py +123 -0
  23. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/salary.py +24 -2
  24. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/schedules.py +25 -16
  25. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs.egg-info/PKG-INFO +1 -1
  26. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs.egg-info/SOURCES.txt +3 -0
  27. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/setup.py +1 -1
  28. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/address.py +0 -0
  29. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/bank.py +0 -0
  30. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/children.py +0 -0
  31. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/contract.py +0 -0
  32. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/costcenter.py +0 -0
  33. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/costunit.py +0 -0
  34. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/document.py +0 -0
  35. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/employment.py +0 -0
  36. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/salaries.py +0 -0
  37. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/employment.py +0 -0
  38. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/hours.py +0 -0
  39. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/schemas/wagecomponents.py +0 -0
  40. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs/wagecomponents.py +0 -0
  41. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs.egg-info/dependency_links.txt +0 -0
  42. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs.egg-info/not-zip-safe +0 -0
  43. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs.egg-info/requires.txt +0 -0
  44. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/brynq_sdk_nmbrs.egg-info/top_level.txt +0 -0
  45. {brynq_sdk_nmbrs-2.0.0 → brynq_sdk_nmbrs-2.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq_sdk_nmbrs
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Summary: Nmbrs wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -20,6 +20,7 @@ from .salary_tables import SalaryTables, SalaryScales, SalarySteps
20
20
  from .employment import Employment
21
21
  from .function import EmployeeFunction
22
22
  from .hours import VariableHours, FixedHours
23
+ from .manager import EmployeeManager, Manager
23
24
  from .salaries import Salaries
24
25
  from .schedules import Schedule
25
26
  from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
@@ -73,6 +74,8 @@ class Nmbrs(BrynQ):
73
74
  self.function = EmployeeFunction(self)
74
75
  self.fixed_hours = FixedHours(self)
75
76
  self.variable_hours = VariableHours(self)
77
+ self.manager = Manager(self)
78
+ self.employee_manager = EmployeeManager(self)
76
79
  self.salaries = Salaries(self)
77
80
  self.schedule = Schedule(self)
78
81
  self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
@@ -96,23 +99,22 @@ class Nmbrs(BrynQ):
96
99
  def _get_soap_auth_header(self):
97
100
  """
98
101
  Creates the SOAP authentication header using credentials from initial_credentials.
99
-
100
102
  Returns:
101
103
  AuthHeaderWithDomainType: The authentication header for SOAP requests
102
104
  """
103
105
  initial_credentials = self.get_system_credential(system='nmbrs', label='bob')
104
106
  config = initial_credentials.get("config", {})
105
-
107
+
106
108
  # Get the AuthHeaderWithDomain type from the WSDL
107
109
  AuthHeaderWithDomainType = self.soap_client.get_element('ns0:AuthHeaderWithDomain')
108
-
110
+
109
111
  # Create the auth header using credentials from config
110
112
  auth_header = AuthHeaderWithDomainType(
111
113
  Username=config.get("soap_api_username"),
112
114
  Token=config.get("soap_api_token"),
113
115
  Domain=config.get("soap_api_domain")
114
116
  )
115
-
117
+
116
118
  return auth_header
117
119
 
118
120
  def get_paginated_result(self, request: requests.Request) -> List:
@@ -158,4 +160,3 @@ class Nmbrs(BrynQ):
158
160
  df.columns = map(camel_to_snake_case, df.columns)
159
161
 
160
162
  return df
161
-
@@ -49,10 +49,12 @@ class Companies:
49
49
  if response:
50
50
  # Convert Zeep objects to Python dictionaries
51
51
  serialized_response = serialize_object(response)
52
-
52
+
53
+ # TODO: add validation here
53
54
  # Convert to DataFrame
54
55
  df = pd.DataFrame(serialized_response)
55
-
56
+ df = self.nmbrs._rename_camel_columns_to_snake_case(df)
57
+
56
58
  return df
57
59
  else:
58
60
  return pd.DataFrame()
@@ -1,25 +1,25 @@
1
1
  import pandas as pd
2
2
  import requests
3
- from .costcenter import Costcenter
4
- from .costunit import Costunit
3
+ from brynq_sdk_functions import Functions
5
4
  from .department import Departments
6
- from .hours import Hours
7
- from .bank import Bank
8
- from .function import Functions
5
+ from .function import Functions as NmbrsFunctions
6
+ from .schemas.debtor import DebtorsGet
9
7
 
10
8
 
11
9
  class Debtors:
12
10
  def __init__(self, nmbrs):
13
11
  self.nmbrs = nmbrs
14
12
  self.departments = Departments(nmbrs)
15
- self.functions = Functions(nmbrs)
13
+ self.functions = NmbrsFunctions(nmbrs)
16
14
 
17
15
 
18
- def get(self) -> pd.DataFrame:
16
+ def get(self) -> (pd.DataFrame, pd.DataFrame):
19
17
  request = requests.Request(method='GET',
20
18
  url=f"{self.nmbrs.base_url}debtors")
21
19
  data = self.nmbrs.get_paginated_result(request)
22
20
 
23
21
  df = pd.DataFrame(data)
24
22
 
25
- return df
23
+ valid_debtors, invalid_debtors = Functions.validate_data(df=df, schema=DebtorsGet, debug=True)
24
+
25
+ return valid_debtors, invalid_debtors
@@ -2,7 +2,7 @@ import math
2
2
  import pandas as pd
3
3
  import requests
4
4
  from typing import Dict, Any
5
- from .schemas.department import DepartmentCreate, DepartmentUpdate, DepartmentDelete, Period, DepartmentGet
5
+ from .schemas.department import DepartmentCreate, DepartmentUpdate, DepartmentDelete, Period, EmployeeDepartmentGet, DepartmentGet
6
6
  from brynq_sdk_functions import Functions
7
7
 
8
8
 
@@ -17,7 +17,7 @@ class EmployeeDepartment:
17
17
  for company in self.nmbrs.company_ids:
18
18
  departments = pd.concat([departments, self._get(company, created_from, employee_id)])
19
19
 
20
- valid_departments, invalid_departments = Functions.validate_data(df=departments, schema=DepartmentGet, debug=True)
20
+ valid_departments, invalid_departments = Functions.validate_data(df=departments, schema=EmployeeDepartmentGet, debug=True)
21
21
 
22
22
  return valid_departments, invalid_departments
23
23
 
@@ -114,5 +114,6 @@ class Departments:
114
114
 
115
115
  data = self.nmbrs.get_paginated_result(request)
116
116
  df = pd.DataFrame(data)
117
+ valid_departments, invalid_departments = Functions.validate_data(df=df, schema=DepartmentGet, debug=True)
117
118
 
118
119
  return df
@@ -67,7 +67,7 @@ class Employees:
67
67
  record_path='info',
68
68
  meta=['employeeId']
69
69
  )
70
- df['company_id'] = company_id
70
+ df['companyId'] = company_id
71
71
 
72
72
  df['createdAt'] = pd.to_datetime(df['createdAt'])
73
73
  df = df.loc[df.groupby('employeeId')['createdAt'].idxmax()]
@@ -108,7 +108,7 @@ class Employees:
108
108
  return employee_model
109
109
 
110
110
  # Convert validated model to dict for API payload
111
- payload = employee_model.dict(exclude_none=True)
111
+ payload = employee_model.model_dump(exclude_none=True, by_alias=True)
112
112
 
113
113
  # Send request
114
114
  resp = self.nmbrs.session.post(
@@ -3,7 +3,7 @@ import requests
3
3
  from brynq_sdk_functions import Functions as BrynQFunctions
4
4
  import math
5
5
  from typing import Dict, Any
6
- from .schemas.function import FunctionGet, FunctionCreate, FunctionUpdate, FunctionDelete
6
+ from .schemas.function import EmployeeFunctionGet, FunctionCreate, FunctionUpdate, FunctionDelete, FunctionGet
7
7
 
8
8
 
9
9
  class EmployeeFunction:
@@ -16,7 +16,7 @@ class EmployeeFunction:
16
16
  for company in self.nmbrs.company_ids:
17
17
  functions = pd.concat([functions, self._get(company, created_from)])
18
18
 
19
- valid_functions, invalid_functions = BrynQFunctions.validate_data(df=functions, schema=FunctionGet, debug=True)
19
+ valid_functions, invalid_functions = BrynQFunctions.validate_data(df=functions, schema=EmployeeFunctionGet, debug=True)
20
20
 
21
21
  return valid_functions, invalid_functions
22
22
 
@@ -102,14 +102,16 @@ class Functions:
102
102
  def __init__(self, nmbrs):
103
103
  self.nmbrs = nmbrs
104
104
 
105
- def get(self, debtor_id: str) -> pd.DataFrame:
105
+ def get(self, debtor_id: str) -> (pd.DataFrame, pd.DataFrame):
106
106
  try:
107
107
  request = requests.Request(method='GET',
108
108
  url=f"{self.nmbrs.base_url}debtors/{debtor_id}/functions")
109
109
 
110
110
  data = self.nmbrs.get_paginated_result(request)
111
111
  df = pd.DataFrame(data)
112
+ valid_functions, invalid_functions = BrynQFunctions.validate_data(df=df, schema=FunctionGet, debug=True)
113
+
112
114
  except requests.HTTPError as e:
113
115
  df = pd.DataFrame()
114
116
 
115
- return df
117
+ return valid_functions, invalid_functions
@@ -46,7 +46,7 @@ class VariableHours:
46
46
 
47
47
  df = pd.DataFrame(data)
48
48
 
49
- df['employee_id'] = employee_id # Add employee_id for tracking
49
+ df['employeeId'] = employee_id # Add employee_id for tracking
50
50
 
51
51
  # Validate data using the schema
52
52
  valid_hours, invalid_hours = Functions.validate_data(df=df, schema=VariableHoursGet, debug=True)
@@ -0,0 +1,294 @@
1
+ import pandas as pd
2
+ import requests
3
+ from brynq_sdk_functions import Functions as BrynQFunctions
4
+ import math
5
+ from typing import Dict, Any
6
+ from .schemas.manager import (
7
+ ManagerGet, ManagerBasicGet, EmployeeManagerGet, ManagerHistoricBasicGet,
8
+ ManagerCreate, ManagerUpdate, ManagerDelete, UpdateEmployeeManager
9
+ )
10
+
11
+
12
+ class EmployeeManager:
13
+ def __init__(self, nmbrs):
14
+ self.nmbrs = nmbrs
15
+
16
+ def get(self, created_from: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
17
+ """
18
+ Get employee manager history for all companies.
19
+
20
+ Args:
21
+ created_from: Optional filter to get managers created from a specific date
22
+
23
+ Returns:
24
+ Tuple of (valid_managers, invalid_managers) DataFrames
25
+ """
26
+ managers = pd.DataFrame()
27
+ for company in self.nmbrs.company_ids:
28
+ managers = pd.concat([managers, self._get(company, created_from)])
29
+
30
+ valid_managers, invalid_managers = BrynQFunctions.validate_data(df=managers, schema=EmployeeManagerGet, debug=True)
31
+
32
+ return valid_managers, invalid_managers
33
+
34
+ def _get(self, company_id: str, created_from: str = None) -> pd.DataFrame:
35
+ """
36
+ Get employee manager history for a specific company.
37
+
38
+ Args:
39
+ company_id: The ID of the company
40
+ created_from: Optional filter to get managers created from a specific date
41
+
42
+ Returns:
43
+ DataFrame containing employee manager data
44
+ """
45
+ params = {}
46
+ if created_from:
47
+ params['createdFrom'] = created_from
48
+
49
+ try:
50
+ request = requests.Request(
51
+ method='GET',
52
+ url=f"{self.nmbrs.base_url}companies/{company_id}/employees/managers",
53
+ params=params
54
+ )
55
+ data = self.nmbrs.get_paginated_result(request)
56
+ df = pd.json_normalize(
57
+ data,
58
+ record_path='managers',
59
+ meta=['employeeId']
60
+ )
61
+ df['companyId'] = company_id
62
+ except requests.HTTPError as e:
63
+ df = pd.DataFrame()
64
+
65
+ return df
66
+
67
+ def get_historic_basic(self, created_from: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
68
+ """
69
+ Get historic basic manager information for employees.
70
+
71
+ Args:
72
+ created_from: Optional filter to get managers created from a specific date
73
+
74
+ Returns:
75
+ Tuple of (valid_managers, invalid_managers) DataFrames
76
+ """
77
+ managers = pd.DataFrame()
78
+ for company in self.nmbrs.company_ids:
79
+ managers = pd.concat([managers, self._get_historic_basic(company, created_from)])
80
+
81
+ valid_managers, invalid_managers = BrynQFunctions.validate_data(df=managers, schema=ManagerHistoricBasicGet, debug=True)
82
+
83
+ return valid_managers, invalid_managers
84
+
85
+ def _get_historic_basic(self, company_id: str, created_from: str = None) -> pd.DataFrame:
86
+ """
87
+ Get historic basic manager information for a specific company.
88
+
89
+ Args:
90
+ company_id: The ID of the company
91
+ created_from: Optional filter to get managers created from a specific date
92
+
93
+ Returns:
94
+ DataFrame containing historic basic manager data
95
+ """
96
+ params = {}
97
+ if created_from:
98
+ params['createdFrom'] = created_from
99
+
100
+ try:
101
+ request = requests.Request(
102
+ method='GET',
103
+ url=f"{self.nmbrs.base_url}companies/{company_id}/employees/managers",
104
+ params=params
105
+ )
106
+ data = self.nmbrs.get_paginated_result(request)
107
+ df = pd.json_normalize(
108
+ data,
109
+ record_path='managers',
110
+ meta=['employeeId']
111
+ )
112
+ df['companyId'] = company_id
113
+ except requests.HTTPError as e:
114
+ df = pd.DataFrame()
115
+
116
+ return df
117
+
118
+ def update(self, employee_id: str, data: Dict[str, Any]):
119
+ """
120
+ Update the manager of a specific employee using Pydantic validation.
121
+
122
+ Args:
123
+ employee_id: The ID of the employee
124
+ data: Dictionary containing manager data with fields matching
125
+ the UpdateEmployeeManager schema (using camelCase field names)
126
+
127
+ Returns:
128
+ Response from the API
129
+ """
130
+ # Validate with Pydantic model
131
+ manager_model = UpdateEmployeeManager(**data)
132
+
133
+ if self.nmbrs.mock_mode:
134
+ return manager_model
135
+
136
+ # Convert validated model to dict for API payload
137
+ payload = manager_model.dict(exclude_none=True)
138
+
139
+ # Send request
140
+ resp = self.nmbrs.session.put(
141
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/manager",
142
+ json=payload,
143
+ timeout=self.nmbrs.timeout
144
+ )
145
+ return resp
146
+
147
+
148
+ class Manager:
149
+ def __init__(self, nmbrs):
150
+ self.nmbrs = nmbrs
151
+
152
+ def get(self, debtor_id: str) -> tuple[pd.DataFrame, pd.DataFrame]:
153
+ """
154
+ Get all managers for a specific debtor.
155
+
156
+ Args:
157
+ debtor_id: The ID of the debtor
158
+
159
+ Returns:
160
+ Tuple of (valid_managers, invalid_managers) DataFrames
161
+ """
162
+ try:
163
+ request = requests.Request(
164
+ method='GET',
165
+ url=f"{self.nmbrs.base_url}debtors/{debtor_id}/managers"
166
+ )
167
+
168
+ data = self.nmbrs.get_paginated_result(request)
169
+ df = pd.DataFrame(data)
170
+ df['debtorId'] = debtor_id
171
+
172
+ valid_managers, invalid_managers = BrynQFunctions.validate_data(df=df, schema=ManagerGet, debug=True)
173
+
174
+ except requests.HTTPError as e:
175
+ df = pd.DataFrame()
176
+ valid_managers = df
177
+ invalid_managers = df
178
+
179
+ return valid_managers, invalid_managers
180
+
181
+ def get_basic(self, debtor_id: str) -> tuple[pd.DataFrame, pd.DataFrame]:
182
+ """
183
+ Get basic manager information for a specific debtor.
184
+
185
+ Args:
186
+ debtor_id: The ID of the debtor
187
+
188
+ Returns:
189
+ Tuple of (valid_managers, invalid_managers) DataFrames
190
+ """
191
+ try:
192
+ request = requests.Request(
193
+ method='GET',
194
+ url=f"{self.nmbrs.base_url}debtors/{debtor_id}/managers"
195
+ )
196
+
197
+ data = self.nmbrs.get_paginated_result(request)
198
+ df = pd.DataFrame(data)
199
+ df['debtorId'] = debtor_id
200
+
201
+ valid_managers, invalid_managers = BrynQFunctions.validate_data(df=df, schema=ManagerBasicGet, debug=True)
202
+
203
+ except requests.HTTPError as e:
204
+ df = pd.DataFrame()
205
+ valid_managers = df
206
+ invalid_managers = df
207
+
208
+ return valid_managers, invalid_managers
209
+
210
+ def create(self, debtor_id: str, data: Dict[str, Any]):
211
+ """
212
+ Create a new manager using Pydantic validation.
213
+
214
+ Args:
215
+ debtor_id: The ID of the debtor
216
+ data: Dictionary containing manager data with fields matching
217
+ the ManagerCreate schema (using camelCase field names)
218
+
219
+ Returns:
220
+ Response from the API
221
+ """
222
+ # Validate with Pydantic model
223
+ manager_model = ManagerCreate(**data)
224
+
225
+ if self.nmbrs.mock_mode:
226
+ return manager_model
227
+
228
+ # Convert validated model to dict for API payload
229
+ payload = manager_model.dict(exclude_none=True)
230
+
231
+ # Send request
232
+ resp = self.nmbrs.session.post(
233
+ url=f"{self.nmbrs.base_url}debtors/{debtor_id}/managers",
234
+ json=payload,
235
+ timeout=self.nmbrs.timeout
236
+ )
237
+ return resp
238
+
239
+ def update(self, debtor_id: str, data: Dict[str, Any]):
240
+ """
241
+ Update a manager using Pydantic validation.
242
+
243
+ Args:
244
+ debtor_id: The ID of the debtor
245
+ data: Dictionary containing manager data with fields matching
246
+ the ManagerUpdate schema (using camelCase field names)
247
+
248
+ Returns:
249
+ Response from the API
250
+ """
251
+ # Validate with Pydantic model
252
+ manager_model = ManagerUpdate(**data)
253
+
254
+ if self.nmbrs.mock_mode:
255
+ return manager_model
256
+
257
+ # Convert validated model to dict for API payload
258
+ payload = manager_model.dict(exclude_none=True)
259
+
260
+ # Send request
261
+ resp = self.nmbrs.session.put(
262
+ url=f"{self.nmbrs.base_url}debtors/{debtor_id}/managers/{payload['managerId']}",
263
+ json=payload,
264
+ timeout=self.nmbrs.timeout
265
+ )
266
+ return resp
267
+
268
+ def delete(self, debtor_id: str, data: Dict[str, Any]):
269
+ """
270
+ Delete a manager using Pydantic validation.
271
+
272
+ Args:
273
+ debtor_id: The ID of the debtor
274
+ data: Dictionary containing manager data with fields matching
275
+ the ManagerDelete schema (using camelCase field names)
276
+
277
+ Returns:
278
+ Response from the API
279
+ """
280
+ # Validate with Pydantic model
281
+ manager_model = ManagerDelete(**data)
282
+
283
+ if self.nmbrs.mock_mode:
284
+ return manager_model
285
+
286
+ # Convert validated model to dict for API payload
287
+ payload = manager_model.dict(exclude_none=True)
288
+
289
+ # Send request
290
+ resp = self.nmbrs.session.delete(
291
+ url=f"{self.nmbrs.base_url}debtors/{debtor_id}/managers/{payload['managerId']}",
292
+ timeout=self.nmbrs.timeout
293
+ )
294
+ return resp
@@ -5,7 +5,8 @@ from zeep.ns import WSDL, SOAP_ENV_11
5
5
  from zeep.xsd import ComplexType, Element, String
6
6
  from zeep.helpers import serialize_object
7
7
  # import logging
8
-
8
+ from brynq_sdk_functions import Functions
9
+ from .schemas.salary import SalaryTableGet, SalaryScalesGet, SalaryStepsGet
9
10
 
10
11
 
11
12
  class SalaryTables:
@@ -13,7 +14,7 @@ class SalaryTables:
13
14
  self.nmbrs = nmbrs
14
15
  self.soap_client = nmbrs.soap_client
15
16
 
16
- def get(self, period: int, year: int) -> pd.DataFrame:
17
+ def get(self, period: int, year: int) -> (pd.DataFrame, pd.DataFrame):
17
18
  """
18
19
  Get salary tables for all companies for a specific period and year.
19
20
 
@@ -28,11 +29,13 @@ class SalaryTables:
28
29
  for company in self.nmbrs.soap_company_ids.to_dict(orient='records'):
29
30
  salary_table_temp = self._get(company['i_d'], period, year)
30
31
  if not salary_table_temp.empty:
31
- salary_table_temp['company_id'] = company['number']
32
+ salary_table_temp['companyId'] = company['number']
32
33
  salary_tables = pd.concat([salary_tables, salary_table_temp])
33
-
34
+
35
+ valid_salary_tables, invalid_salary_tables = Functions.validate_data(df=salary_tables, schema=SalaryTableGet, debug=True)
36
+
34
37
  # No validation schema for now, but could be added later
35
- return salary_tables
38
+ return valid_salary_tables, invalid_salary_tables
36
39
 
37
40
  def _get(self, company_id: int, period: int, year: int) -> pd.DataFrame:
38
41
  """
@@ -85,7 +88,7 @@ class SalaryScales:
85
88
  self.nmbrs = nmbrs
86
89
  self.soap_client = nmbrs.soap_client
87
90
 
88
- def get(self, period: int, year: int) -> pd.DataFrame:
91
+ def get(self, period: int, year: int) -> (pd.DataFrame, pd.DataFrame):
89
92
  """
90
93
  Get salary scales for all companies for a specific period and year.
91
94
 
@@ -100,11 +103,12 @@ class SalaryScales:
100
103
  for company in self.nmbrs.soap_company_ids.to_dict(orient='records'):
101
104
  salary_scale_temp = self._get(company['i_d'], period, year)
102
105
  if not salary_scale_temp.empty:
103
- salary_scale_temp['company_id'] = company['number']
106
+ salary_scale_temp['companyId'] = company['number']
104
107
  salary_scales = pd.concat([salary_scales, salary_scale_temp])
105
-
106
- # No validation schema for now, but could be added later
107
- return salary_scales
108
+
109
+ valid_salary_scales, invalid_salary_scales = Functions.validate_data(df=salary_scales, schema=SalaryScalesGet, debug=True)
110
+
111
+ return valid_salary_scales, invalid_salary_scales
108
112
 
109
113
  def _get(self, company_id: int, period: int, year: int) -> pd.DataFrame:
110
114
  """
@@ -157,7 +161,7 @@ class SalarySteps:
157
161
  self.nmbrs = nmbrs
158
162
  self.soap_client = nmbrs.soap_client
159
163
 
160
- def get(self, period: int, year: int, scale: dict) -> pd.DataFrame:
164
+ def get(self, period: int, year: int, scale: dict) -> (pd.DataFrame, pd.DataFrame):
161
165
  """
162
166
  Get salary steps for all companies for a specific period, year and scale.
163
167
 
@@ -178,11 +182,12 @@ class SalarySteps:
178
182
  for company in self.nmbrs.soap_company_ids.to_dict(orient='records'):
179
183
  salary_steps_temp = self._get(company['i_d'], period, year, scale)
180
184
  if not salary_steps_temp.empty:
181
- salary_steps_temp['company_id'] = company['number']
185
+ salary_steps_temp['companyId'] = company['number']
182
186
  salary_steps = pd.concat([salary_steps, salary_steps_temp])
183
-
184
- # No validation schema for now, but could be added later
185
- return salary_steps
187
+
188
+ valid_salary_steps, invalid_salary_steps = Functions.validate_data(df=salary_steps, schema=SalaryStepsGet, debug=True)
189
+
190
+ return valid_salary_steps, invalid_salary_steps
186
191
 
187
192
  def _get(self, company_id: int, period: int, year: int, scale: dict) -> pd.DataFrame:
188
193
  """
@@ -68,10 +68,10 @@ class Schedule:
68
68
  schedule_data["startDate"] = data["start_date_schedule"]
69
69
 
70
70
  # Optional field
71
- if "weekly_hours" in data:
71
+ if "hours_per_week" in data:
72
72
  # Handle NaN values
73
- if not isinstance(data["weekly_hours"], float) or not math.isnan(data["weekly_hours"]):
74
- schedule_data["hoursPerWeek"] = data["weekly_hours"]
73
+ if not isinstance(data["hours_per_week"], float) or not math.isnan(data["hours_per_week"]):
74
+ schedule_data["hoursPerWeek"] = data["hours_per_week"]
75
75
 
76
76
  # Create week1 and week2 data
77
77
  week1_data = {}
@@ -5,11 +5,12 @@ DATEFORMAT = '%Y%m%d'
5
5
  from .address import AddressGet, AddressCreate, AddressUpdate, AddressDelete
6
6
  from .bank import BankGet, BankCreate, BankUpdate, BankDelete
7
7
  from .contracts import ContractGet, ContractCreate, ContractUpdate, ContractDelete
8
- from .department import DepartmentGet, DepartmentCreate, DepartmentUpdate, DepartmentDelete
8
+ from .department import EmployeeDepartmentGet, DepartmentCreate, DepartmentUpdate, DepartmentDelete, DepartmentGet
9
9
  from .employees import EmployeeGet, EmployeeCreate, EmployeeUpdate, EmployeeDelete
10
10
  from .employment import EmploymentGet, EmploymentCreate, EmploymentUpdate, EmploymentDelete
11
11
  from .function import FunctionGet, FunctionCreate, FunctionUpdate, FunctionDelete
12
12
  from .hours import FixedHoursGet, FixedHoursCreate, FixedHoursUpdate, HoursDelete, VariableHoursGet, VariableHoursCreate, VariableHoursUpdate
13
+ from .manager import ManagerGet, ManagerBasicGet, EmployeeManagerGet, ManagerHistoricBasicGet, ManagerCreate, ManagerUpdate, ManagerDelete, UpdateEmployeeManager
13
14
  from .salary import SalaryGet, SalaryCreate, SalaryUpdate, SalaryDelete
14
15
  from .wagecomponents import FixedWageComponentGet, FixedWageComponentCreate, FixedWageComponentUpdate, WageComponentDelete, VariableWageComponentGet, VariableWageComponentCreate, VariableWageComponentUpdate
15
16
 
@@ -19,12 +20,14 @@ __all__ = [
19
20
  'AddressGet', 'AddressCreate', 'AddressUpdate', 'AddressDelete',
20
21
  'BankGet', 'BankCreate', 'BankUpdate', 'BankDelete',
21
22
  'ContractGet', 'ContractCreate', 'ContractUpdate', 'ContractDelete',
22
- 'DepartmentGet', 'DepartmentCreate', 'DepartmentUpdate', 'DepartmentDelete',
23
+ 'EmployeeDepartmentGet', 'DepartmentCreate', 'DepartmentUpdate', 'DepartmentDelete', 'DepartmentGet',
23
24
  'EmployeeGet', 'EmployeeCreate', 'EmployeeUpdate', 'EmployeeDelete',
24
25
  'EmploymentGet', 'EmploymentCreate', 'EmploymentUpdate', 'EmploymentDelete',
25
26
  'FunctionGet', 'FunctionCreate', 'FunctionUpdate', 'FunctionDelete',
26
27
  'FixedHoursGet', 'FixedHoursCreate', 'FixedHoursUpdate', 'HoursDelete',
27
28
  'VariableHoursGet', 'VariableHoursCreate', 'VariableHoursUpdate',
29
+ 'ManagerGet', 'ManagerBasicGet', 'EmployeeManagerGet', 'ManagerHistoricBasicGet',
30
+ 'ManagerCreate', 'ManagerUpdate', 'ManagerDelete', 'UpdateEmployeeManager',
28
31
  'SalaryGet', 'SalaryCreate', 'SalaryUpdate', 'SalaryDelete',
29
32
  'FixedWageComponentGet', 'FixedWageComponentCreate', 'FixedWageComponentUpdate', 'WageComponentDelete',
30
33
  'VariableWageComponentGet', 'VariableWageComponentCreate', 'VariableWageComponentUpdate'
@@ -27,8 +27,8 @@ class AddressGet(BrynQPanderaDataFrameModel):
27
27
  city: Series[String] = pa.Field(coerce=True, description="City", alias="city")
28
28
  state_province: Series[String] = pa.Field(coerce=True, nullable=True, description="State or Province", alias="stateProvince")
29
29
  country_i_s_o_code: Series[String] = pa.Field(coerce=True, nullable=True, description="Country ISO code", alias="countryISOCode")
30
- period_year: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Year", alias="periodYear")
31
- period_period: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Period", alias="periodPeriod")
30
+ period_year: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Year", alias="period.year")
31
+ period_period: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Period", alias="period.period")
32
32
 
33
33
  class _Annotation:
34
34
  primary_key = "address_id"
@@ -64,11 +64,11 @@ class AddressCreate(BaseModel):
64
64
  city: str = Field(..., min_length=1, max_length=100, example="Amsterdam", description="City", alias="city")
65
65
  state_province: Optional[str] = Field(None, max_length=100, example="Noord-Holland", description="State or Province", alias="stateProvince")
66
66
  country_iso_code: Annotated[
67
- str,
67
+ str,
68
68
  StringConstraints(
69
- pattern=r'^[A-Za-z]+$',
69
+ pattern=r'^[A-Za-z]+$',
70
70
  strip_whitespace=True,
71
- min_length=2,
71
+ min_length=2,
72
72
  max_length=3
73
73
  )
74
74
  ] = Field(..., example="NL", description="Country ISO Code", alias="countryISOCode")
@@ -91,15 +91,15 @@ class AddressUpdate(BaseModel):
91
91
  city: str = Field(..., min_length=1, max_length=100, example="Amsterdam", description="City", alias="city")
92
92
  state_province: Optional[str] = Field(None, max_length=100, example="Noord-Holland", description="State or Province", alias="stateProvince")
93
93
  country_iso_code: Annotated[
94
- str,
94
+ str,
95
95
  StringConstraints(
96
- pattern=r'^[A-Za-z]+$',
96
+ pattern=r'^[A-Za-z]+$',
97
97
  strip_whitespace=True,
98
- min_length=2,
98
+ min_length=2,
99
99
  max_length=3
100
100
  )
101
101
  ] = Field(..., example="NL")
102
102
  period_details: Period
103
103
 
104
104
  class AddressDelete(BaseModel):
105
- address_id: str = Field(..., description="Address ID", example="cc3449f5-9941-4209-be36-187a16267394", alias="addressId")
105
+ address_id: str = Field(..., description="Address ID", example="cc3449f5-9941-4209-be36-187a16267394", alias="addressId")