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.
Files changed (51) hide show
  1. brynq_sdk_nmbrs/__init__.py +95 -91
  2. brynq_sdk_nmbrs/absence.py +1 -4
  3. brynq_sdk_nmbrs/address.py +5 -81
  4. brynq_sdk_nmbrs/bank.py +1 -2
  5. brynq_sdk_nmbrs/companies.py +130 -39
  6. brynq_sdk_nmbrs/contract.py +0 -1
  7. brynq_sdk_nmbrs/days.py +1 -1
  8. brynq_sdk_nmbrs/debtors.py +53 -65
  9. brynq_sdk_nmbrs/department.py +23 -111
  10. brynq_sdk_nmbrs/document.py +195 -4
  11. brynq_sdk_nmbrs/employee_wage_tax_settings.py +13 -5
  12. brynq_sdk_nmbrs/employees.py +55 -21
  13. brynq_sdk_nmbrs/employment.py +0 -2
  14. brynq_sdk_nmbrs/extra_fields.py +126 -0
  15. brynq_sdk_nmbrs/function.py +35 -97
  16. brynq_sdk_nmbrs/leave.py +53 -58
  17. brynq_sdk_nmbrs/manager.py +51 -35
  18. brynq_sdk_nmbrs/salaries.py +48 -65
  19. brynq_sdk_nmbrs/salary_tables.py +1 -4
  20. brynq_sdk_nmbrs/schedules.py +5 -78
  21. brynq_sdk_nmbrs/schemas/absence.py +26 -16
  22. brynq_sdk_nmbrs/schemas/address.py +2 -29
  23. brynq_sdk_nmbrs/schemas/children.py +0 -2
  24. brynq_sdk_nmbrs/schemas/contracts.py +7 -6
  25. brynq_sdk_nmbrs/schemas/costcenter.py +2 -2
  26. brynq_sdk_nmbrs/schemas/costunit.py +2 -0
  27. brynq_sdk_nmbrs/schemas/days.py +13 -11
  28. brynq_sdk_nmbrs/schemas/debtor.py +6 -28
  29. brynq_sdk_nmbrs/schemas/department.py +5 -39
  30. brynq_sdk_nmbrs/schemas/document.py +0 -2
  31. brynq_sdk_nmbrs/schemas/employee_wage_tax_settings.py +75 -0
  32. brynq_sdk_nmbrs/schemas/employees.py +2 -0
  33. brynq_sdk_nmbrs/schemas/extra_fields.py +56 -0
  34. brynq_sdk_nmbrs/schemas/function.py +5 -32
  35. brynq_sdk_nmbrs/schemas/hours.py +5 -1
  36. brynq_sdk_nmbrs/schemas/leave.py +17 -6
  37. brynq_sdk_nmbrs/schemas/manager.py +11 -20
  38. brynq_sdk_nmbrs/schemas/salary.py +11 -1
  39. brynq_sdk_nmbrs/schemas/schedules.py +2 -54
  40. brynq_sdk_nmbrs/schemas/svw_settings.py +116 -0
  41. brynq_sdk_nmbrs/schemas/wage_tax.py +53 -21
  42. brynq_sdk_nmbrs/schemas/wagecomponents.py +6 -9
  43. brynq_sdk_nmbrs/svw_settings.py +213 -0
  44. brynq_sdk_nmbrs/wage_tax.py +120 -11
  45. {brynq_sdk_nmbrs-2.3.4.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/METADATA +1 -1
  46. brynq_sdk_nmbrs-2.4.5.dist-info/RECORD +58 -0
  47. brynq_sdk_nmbrs/schemas/social_insurance.py +0 -73
  48. brynq_sdk_nmbrs/social_insurance.py +0 -130
  49. brynq_sdk_nmbrs-2.3.4.dev0.dist-info/RECORD +0 -55
  50. {brynq_sdk_nmbrs-2.3.4.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/WHEEL +0 -0
  51. {brynq_sdk_nmbrs-2.3.4.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,8 @@
1
1
  import logging
2
- from typing import Any, Dict, Optional
2
+ from typing import Any, Dict, Tuple
3
3
 
4
4
  import pandas as pd
5
5
  import requests
6
- from pydantic import BaseModel
7
6
  from zeep.exceptions import Fault
8
7
  from zeep.helpers import serialize_object
9
8
 
@@ -23,19 +22,11 @@ from .leave import Leave, LeaveBalance
23
22
  from .salaries import Salaries
24
23
  from .schedules import Schedule
25
24
  from .schemas.employees import (
26
- AdditionalEmployeeInfo,
27
- BasicInfo,
28
- BirthInfo,
29
25
  BsnGet,
30
- ContactInfo,
31
- CreateEmployeePersonalInfo,
32
26
  DefaultEmployeeTemplates,
33
27
  EmployeeCreate,
34
- EmployeeDelete,
35
28
  EmployeeGet,
36
29
  EmployeeUpdate,
37
- PartnerInfo,
38
- Period,
39
30
  )
40
31
  from .wage_tax import WageTax
41
32
  from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
@@ -66,7 +57,7 @@ class Employees:
66
57
 
67
58
  def get(self,
68
59
  employee_type: str = None
69
- ) -> (pd.DataFrame, pd.DataFrame):
60
+ ) -> Tuple[pd.DataFrame, pd.DataFrame]:
70
61
  employees = pd.DataFrame()
71
62
  for company in self.nmbrs.company_ids:
72
63
  company_employees = self._get(company, employee_type)
@@ -172,6 +163,11 @@ class Employees:
172
163
  timeout=self.nmbrs.timeout
173
164
  )
174
165
 
166
+ # Handle social security number update if present
167
+ if 'social_security_number' in data and resp.ok:
168
+ employee_id = resp.json().get('employeeId')
169
+ if employee_id:
170
+ self._update_social_security_number(employee_id, data['social_security_number'])
175
171
 
176
172
  return resp
177
173
 
@@ -206,17 +202,55 @@ class Employees:
206
202
  )
207
203
 
208
204
  # Handle social security number update if present
209
- #TODO niuet overtschirjven
210
- if 'social_security_number' in data:
211
- social_security_payload = {
212
- "socialSecurityNumber": data['social_security_number']
213
- }
214
- resp = self.nmbrs.session.put(
215
- url=f"{self.nmbrs.base_url}employees/{employee_id}/socialsecuritynumber",
216
- json=social_security_payload,
217
- timeout=self.nmbrs.timeout
218
- )
205
+ if 'socialSecurityNumber' in data:
206
+ self._update_social_security_number(employee_id, data['socialSecurityNumber'])
207
+
208
+ return resp
209
+
210
+ def get_by_id(self, employee_id: str) -> pd.DataFrame:
211
+ """
212
+ Get detailed information about a specific employee.
213
+
214
+ The data returned is detailed and refers to the latest entry on the
215
+ current period of the company.
216
+
217
+ Args:
218
+ employee_id: The ID of the employee
219
+
220
+ Returns:
221
+ DataFrame containing employee details
222
+ """
223
+ request = requests.Request(
224
+ method='GET',
225
+ url=f"{self.nmbrs.base_url}employees/{employee_id}"
226
+ )
227
+
228
+ data = self.nmbrs.get_paginated_result(request)
229
+ df = pd.json_normalize(data)
230
+
231
+ return df
232
+
233
+ def _update_social_security_number(self, employee_id: str, social_security_number: str):
234
+ """
235
+ Internal method to update an employee's social security number (BSN).
219
236
 
237
+ Args:
238
+ employee_id: The ID of the employee
239
+ social_security_number: The new social security number
240
+
241
+ Returns:
242
+ Response from the API
243
+ """
244
+ payload = {
245
+ "socialSecurityNumber": social_security_number
246
+ }
247
+
248
+ resp = self.nmbrs.session.put(
249
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/socialsecuritynumber",
250
+ json=payload,
251
+ timeout=self.nmbrs.timeout
252
+ )
253
+ resp.raise_for_status()
220
254
  return resp
221
255
 
222
256
  def get_soap_ids(self) -> pd.DataFrame:
@@ -1,4 +1,3 @@
1
- import math
2
1
  from typing import Any, Dict
3
2
 
4
3
  import pandas as pd
@@ -8,7 +7,6 @@ from brynq_sdk_functions import Functions
8
7
 
9
8
  from .schemas.employment import (
10
9
  EmploymentCreate,
11
- EmploymentDelete,
12
10
  EmploymentGet,
13
11
  EmploymentUpdate,
14
12
  )
@@ -0,0 +1,126 @@
1
+ import pandas as pd
2
+ import requests
3
+ from typing import Optional
4
+ from .schemas.extra_fields import ExtraFieldSettingsGet, EmployeeExtraFieldsGet
5
+ from brynq_sdk_functions import Functions
6
+
7
+
8
+ class ExtraFieldSettings:
9
+ """
10
+ Class for handling company-level extra field settings.
11
+
12
+ Endpoints:
13
+ - GET /api/companies/{companyId}/extrafieldSettings
14
+ """
15
+
16
+ def __init__(self, nmbrs):
17
+ self.nmbrs = nmbrs
18
+
19
+ def get(self, company_id: Optional[str] = None) -> tuple:
20
+ """
21
+ Get extra field settings for a company.
22
+
23
+ Args:
24
+ company_id: Optional company ID. If not provided, fetches for all companies.
25
+
26
+ Returns:
27
+ Tuple of (valid_data, invalid_data) DataFrames
28
+ """
29
+ if company_id:
30
+ df = self._get(company_id)
31
+ else:
32
+ df = pd.DataFrame()
33
+ for company in self.nmbrs.company_ids:
34
+ df = pd.concat([df, self._get(company)])
35
+
36
+ if df.empty:
37
+ return df, pd.DataFrame()
38
+
39
+ valid_data, invalid_data = Functions.validate_data(df=df, schema=ExtraFieldSettingsGet, debug=self.nmbrs.debug)
40
+ return valid_data, invalid_data
41
+
42
+ def _get(self, company_id: str) -> pd.DataFrame:
43
+ """
44
+ Internal method to get extra field settings for a single company.
45
+ """
46
+ request = requests.Request(
47
+ method='GET',
48
+ url=f"{self.nmbrs.base_url}companies/{company_id}/extrafieldSettings"
49
+ )
50
+
51
+ data = self.nmbrs.get_paginated_result(request)
52
+ df = pd.DataFrame(data)
53
+
54
+ if not df.empty:
55
+ df['companyId'] = company_id
56
+
57
+ return df
58
+
59
+
60
+ class EmployeeExtraFields:
61
+ """
62
+ Class for handling employee extra fields.
63
+
64
+ Endpoints:
65
+ - GET /api/companies/{companyId}/employees/extraFields
66
+ """
67
+
68
+ def __init__(self, nmbrs):
69
+ self.nmbrs = nmbrs
70
+
71
+ def get(self,
72
+ company_id: Optional[str] = None,
73
+ extra_field_type: Optional[str] = None) -> tuple:
74
+ """
75
+ Get extra fields for employees of a company.
76
+
77
+ Args:
78
+ company_id: Optional company ID. If not provided, fetches for all companies.
79
+ extra_field_type: Optional filter by extra field type
80
+ (text, decimal, number, date, yesNo, textDate)
81
+
82
+ Returns:
83
+ Tuple of (valid_data, invalid_data) DataFrames
84
+ """
85
+ if company_id:
86
+ df = self._get(company_id, extra_field_type)
87
+ else:
88
+ df = pd.DataFrame()
89
+ for company in self.nmbrs.company_ids:
90
+ df = pd.concat([df, self._get(company, extra_field_type)])
91
+
92
+ if df.empty:
93
+ return df, pd.DataFrame()
94
+
95
+ valid_data, invalid_data = Functions.validate_data(df=df, schema=EmployeeExtraFieldsGet, debug=self.nmbrs.debug)
96
+ return valid_data, invalid_data
97
+
98
+ def _get(self,
99
+ company_id: str,
100
+ extra_field_type: Optional[str] = None) -> pd.DataFrame:
101
+ """
102
+ Internal method to get employee extra fields for a single company.
103
+ """
104
+ params = {}
105
+ if extra_field_type is not None:
106
+ params['extraFieldType'] = extra_field_type
107
+
108
+ request = requests.Request(
109
+ method='GET',
110
+ url=f"{self.nmbrs.base_url}companies/{company_id}/employees/extraFields",
111
+ params=params
112
+ )
113
+
114
+ data = self.nmbrs.get_paginated_result(request)
115
+
116
+ # Normalize nested data structure
117
+ df = pd.json_normalize(
118
+ data,
119
+ record_path='extraFields',
120
+ meta=['employeeId']
121
+ )
122
+
123
+ if not df.empty:
124
+ df['companyId'] = company_id
125
+
126
+ return df
@@ -1,14 +1,17 @@
1
+ from typing import Any, Dict, Union
2
+
1
3
  import pandas as pd
2
4
  import requests
5
+ from zeep.exceptions import Fault
6
+ from zeep.helpers import serialize_object
7
+
3
8
  from brynq_sdk_functions import Functions as BrynQFunctions
4
- import math
5
- from typing import Dict, Any, Union
9
+
6
10
  from .schemas.function import (
7
- EmployeeFunctionGet, FunctionUpdate, FunctionGet,
8
- FunctionCreate, FunctionDelete, FunctionMasterUpdate
11
+ EmployeeFunctionGet,
12
+ FunctionGet,
13
+ FunctionUpdate,
9
14
  )
10
- from zeep.exceptions import Fault
11
- from zeep.helpers import serialize_object
12
15
 
13
16
 
14
17
  class EmployeeFunction:
@@ -39,7 +42,7 @@ class EmployeeFunction:
39
42
  record_path='functions',
40
43
  meta=['employeeId']
41
44
  )
42
- except requests.HTTPError as e:
45
+ except requests.HTTPError:
43
46
  df = pd.DataFrame()
44
47
  return df
45
48
 
@@ -59,9 +62,6 @@ class EmployeeFunction:
59
62
  nested_data = self.nmbrs.flat_dict_to_nested_dict(data, FunctionUpdate)
60
63
  function_model = FunctionUpdate(**nested_data)
61
64
 
62
- if self.nmbrs.mock_mode:
63
- return function_model
64
-
65
65
  # Convert validated model to dict for API payload
66
66
  payload = function_model.model_dump(exclude_none=True, by_alias=True)
67
67
 
@@ -83,9 +83,6 @@ class EmployeeFunction:
83
83
  Returns:
84
84
  DataFrame with current function
85
85
  """
86
- if self.nmbrs.mock_mode:
87
- return pd.DataFrame()
88
-
89
86
  try:
90
87
  response = self.nmbrs.soap_client_employees.service.Function_GetCurrent(
91
88
  EmployeeId=int(employee_id),
@@ -114,102 +111,43 @@ class Functions:
114
111
  def __init__(self, nmbrs):
115
112
  self.nmbrs = nmbrs
116
113
 
117
- def get(self, debtor_id: str) -> (pd.DataFrame, pd.DataFrame):
118
- try:
119
- request = requests.Request(method='GET',
120
- url=f"{self.nmbrs.base_url}debtors/{debtor_id}/functions")
121
-
122
- data = self.nmbrs.get_paginated_result(request)
123
- df = pd.DataFrame(data)
124
- valid_functions, invalid_functions = BrynQFunctions.validate_data(df=df, schema=FunctionGet, debug=True)
125
-
126
- except requests.HTTPError as e:
127
- df = pd.DataFrame()
128
-
129
- return valid_functions, invalid_functions
130
-
131
- def create(self, debtor_id: Union[int, str], data: Dict[str, Any]):
114
+ def get(self, debtor_id: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
132
115
  """
133
- Create a new master function using SOAP API.
116
+ Get all functions for debtor(s).
134
117
 
135
118
  Args:
136
- debtor_id: The ID of the debtor
137
- data: Dictionary containing function data with fields matching FunctionCreate schema
119
+ debtor_id: Optional debtor ID. If not provided, fetches for all debtors.
138
120
 
139
121
  Returns:
140
- Response from the API (function ID)
122
+ Tuple of (valid_functions, invalid_functions) DataFrames
141
123
  """
142
- function_model = FunctionCreate(**data)
124
+ if debtor_id:
125
+ df = self._get(debtor_id)
126
+ else:
127
+ df = pd.DataFrame()
128
+ for debtor in self.nmbrs.debtor_ids:
129
+ df = pd.concat([df, self._get(debtor)])
143
130
 
144
- if self.nmbrs.mock_mode:
145
- return function_model
131
+ if df.empty:
132
+ return df, pd.DataFrame()
146
133
 
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)}")
134
+ valid_functions, invalid_functions = BrynQFunctions.validate_data(df=df, schema=FunctionGet, debug=self.nmbrs.debug)
135
+ return valid_functions, invalid_functions
160
136
 
161
- def delete(self, debtor_id: Union[int, str], function_id: Union[int, str]):
137
+ def _get(self, debtor_id: str) -> pd.DataFrame:
162
138
  """
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
139
+ Internal method to get functions for a single debtor.
171
140
  """
172
- delete_model = FunctionDelete(function_id=int(function_id))
173
-
174
- if self.nmbrs.mock_mode:
175
- return delete_model
176
-
177
141
  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
142
+ request = requests.Request(method='GET',
143
+ url=f"{self.nmbrs.base_url}debtors/{debtor_id}/functions",
144
+ params={"includeArchived": "true"})
194
145
 
195
- Returns:
196
- Response from the API
197
- """
198
- function_model = FunctionMasterUpdate(**data)
146
+ data = self.nmbrs.get_paginated_result(request)
147
+ df = pd.DataFrame(data)
148
+ df['debtorId'] = debtor_id
199
149
 
200
- if self.nmbrs.mock_mode:
201
- return function_model
150
+ except requests.HTTPError:
151
+ df = pd.DataFrame()
202
152
 
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)}")
153
+ return df
brynq_sdk_nmbrs/leave.py CHANGED
@@ -1,11 +1,19 @@
1
- import math
1
+ from typing import Any, Dict
2
+
2
3
  import pandas as pd
3
4
  import requests
4
- from brynq_sdk_functions import Functions
5
- from typing import Dict, Any, Union
6
- from .schemas.leave import LeaveBalanceGet, LeaveGet, LeaveCreate, LeaveDelete, LeaveUpdate
7
5
  from zeep.exceptions import Fault
8
6
 
7
+ from brynq_sdk_functions import Functions
8
+
9
+ from .schemas.leave import (
10
+ LeaveBalanceGet,
11
+ LeaveCreate,
12
+ LeaveDelete,
13
+ LeaveGet,
14
+ LeaveUpdate,
15
+ )
16
+
9
17
 
10
18
  class Leave:
11
19
  def __init__(self, nmbrs):
@@ -38,7 +46,7 @@ class Leave:
38
46
  record_path='EmployeeLeaveRequests',
39
47
  meta=['employeeId']
40
48
  )
41
- except requests.HTTPError as e:
49
+ except requests.HTTPError:
42
50
  df = pd.DataFrame()
43
51
 
44
52
  return df
@@ -164,7 +172,7 @@ class LeaveBalance:
164
172
  record_path='leaveBalances',
165
173
  meta=['employeeId']
166
174
  )
167
- except requests.HTTPError as e:
175
+ except requests.HTTPError:
168
176
  df = pd.DataFrame()
169
177
 
170
178
  return df
@@ -172,65 +180,52 @@ class LeaveBalance:
172
180
 
173
181
  class LeaveGroup:
174
182
  """
175
- LeaveGroup (Leave Type Groups) - uses SOAP CompanyLeaveTypeGroups_Get
183
+ Class for handling company leave groups.
184
+
185
+ Endpoints:
186
+ - GET /api/companies/{companyId}/leaveGroups
176
187
  """
188
+
177
189
  def __init__(self, nmbrs):
178
190
  self.nmbrs = nmbrs
179
191
 
180
- def get(self, company_id: Union[int, str] = None) -> pd.DataFrame:
192
+ def get(self, company_id: str = None) -> pd.DataFrame:
181
193
  """
182
- Get leave type groups using SOAP API.
183
-
194
+ Get leave groups available in a company.
195
+
196
+ A company can have up to 4 leave groups defined.
197
+
184
198
  Args:
185
- company_id: Optional. If provided, get groups for this company only.
186
- If not provided, get groups for all companies.
187
-
199
+ company_id: Optional company ID. If not provided, fetches for all companies.
200
+
188
201
  Returns:
189
- DataFrame with leave type groups
202
+ DataFrame containing leave groups
190
203
  """
191
- if self.nmbrs.mock_mode:
192
- return pd.DataFrame()
204
+ if company_id:
205
+ return self._get(company_id)
193
206
 
194
- from zeep.helpers import serialize_object
195
-
196
- all_groups = []
207
+ leave_groups = pd.DataFrame()
208
+ for company in self.nmbrs.company_ids:
209
+ leave_groups = pd.concat([leave_groups, self._get(company)])
210
+
211
+ return leave_groups
212
+
213
+ def _get(self, company_id: str) -> pd.DataFrame:
214
+ """
215
+ Internal method to get leave groups for a single company.
216
+ """
197
217
  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)}")
218
+ request = requests.Request(
219
+ method='GET',
220
+ url=f"{self.nmbrs.base_url}companies/{company_id}/leaveGroups"
221
+ )
222
+
223
+ data = self.nmbrs.get_paginated_result(request)
224
+ df = pd.DataFrame(data)
225
+
226
+ if not df.empty:
227
+ df['companyId'] = company_id
228
+
229
+ return df
230
+ except requests.HTTPError:
231
+ return pd.DataFrame()