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,11 +1,19 @@
1
+ from typing import Any, Dict
2
+
1
3
  import pandas as pd
2
4
  import requests
5
+
3
6
  from brynq_sdk_functions import Functions as BrynQFunctions
4
- import math
5
- from typing import Dict, Any
7
+
6
8
  from .schemas.manager import (
7
- ManagerGet, ManagerBasicGet, EmployeeManagerGet, ManagerHistoricBasicGet,
8
- ManagerCreate, ManagerUpdate, ManagerDelete, UpdateEmployeeManager
9
+ EmployeeManagerGet,
10
+ ManagerBasicGet,
11
+ ManagerCreate,
12
+ ManagerDelete,
13
+ ManagerGet,
14
+ ManagerHistoricBasicGet,
15
+ ManagerUpdate,
16
+ UpdateEmployeeManager,
9
17
  )
10
18
 
11
19
 
@@ -127,16 +135,17 @@ class EmployeeManager:
127
135
  Returns:
128
136
  Response from the API
129
137
  """
130
- # Validate with Pydantic model
131
138
  manager_model = UpdateEmployeeManager(**data)
132
139
 
133
140
  if self.nmbrs.mock_mode:
134
141
  return manager_model
135
142
 
136
- # Convert validated model to dict for API payload
137
- payload = manager_model.dict(exclude_none=True)
143
+ payload = manager_model.model_dump(by_alias=True, exclude_none=True)
144
+
145
+ # because of nmbrs bug, it needs to be passed (without year and period) as empty dict if not in the data.
146
+ if 'periodDetails' not in payload or payload.get('periodDetails') is None:
147
+ payload['periodDetails'] = {}
138
148
 
139
- # Send request
140
149
  resp = self.nmbrs.session.put(
141
150
  url=f"{self.nmbrs.base_url}employees/{employee_id}/manager",
142
151
  json=payload,
@@ -149,16 +158,33 @@ class Manager:
149
158
  def __init__(self, nmbrs):
150
159
  self.nmbrs = nmbrs
151
160
 
152
- def get(self, debtor_id: str) -> tuple[pd.DataFrame, pd.DataFrame]:
161
+ def get(self, debtor_id: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
153
162
  """
154
- Get all managers for a specific debtor.
163
+ Get all managers for debtor(s).
155
164
 
156
165
  Args:
157
- debtor_id: The ID of the debtor
166
+ debtor_id: Optional debtor ID. If not provided, fetches for all debtors.
158
167
 
159
168
  Returns:
160
169
  Tuple of (valid_managers, invalid_managers) DataFrames
161
170
  """
171
+ if debtor_id:
172
+ df = self._get(debtor_id)
173
+ else:
174
+ df = pd.DataFrame()
175
+ for debtor in self.nmbrs.debtor_ids:
176
+ df = pd.concat([df, self._get(debtor)])
177
+
178
+ if df.empty:
179
+ return df, pd.DataFrame()
180
+
181
+ valid_managers, invalid_managers = BrynQFunctions.validate_data(df=df, schema=ManagerGet, debug=self.nmbrs.debug)
182
+ return valid_managers, invalid_managers
183
+
184
+ def _get(self, debtor_id: str) -> pd.DataFrame:
185
+ """
186
+ Internal method to get managers for a single debtor.
187
+ """
162
188
  try:
163
189
  request = requests.Request(
164
190
  method='GET',
@@ -169,42 +195,32 @@ class Manager:
169
195
  df = pd.DataFrame(data)
170
196
  df['debtorId'] = debtor_id
171
197
 
172
- valid_managers, invalid_managers = BrynQFunctions.validate_data(df=df, schema=ManagerGet, debug=True)
173
-
174
- except requests.HTTPError as e:
198
+ except requests.HTTPError:
175
199
  df = pd.DataFrame()
176
- valid_managers = df
177
- invalid_managers = df
178
200
 
179
- return valid_managers, invalid_managers
201
+ return df
180
202
 
181
- def get_basic(self, debtor_id: str) -> tuple[pd.DataFrame, pd.DataFrame]:
203
+ def get_basic(self, debtor_id: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
182
204
  """
183
- Get basic manager information for a specific debtor.
205
+ Get basic manager information for debtor(s).
184
206
 
185
207
  Args:
186
- debtor_id: The ID of the debtor
208
+ debtor_id: Optional debtor ID. If not provided, fetches for all debtors.
187
209
 
188
210
  Returns:
189
211
  Tuple of (valid_managers, invalid_managers) DataFrames
190
212
  """
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:
213
+ if debtor_id:
214
+ df = self._get(debtor_id)
215
+ else:
204
216
  df = pd.DataFrame()
205
- valid_managers = df
206
- invalid_managers = df
217
+ for debtor in self.nmbrs.debtor_ids:
218
+ df = pd.concat([df, self._get(debtor)])
219
+
220
+ if df.empty:
221
+ return df, pd.DataFrame()
207
222
 
223
+ valid_managers, invalid_managers = BrynQFunctions.validate_data(df=df, schema=ManagerBasicGet, debug=self.nmbrs.debug)
208
224
  return valid_managers, invalid_managers
209
225
 
210
226
  def create(self, debtor_id: str, data: Dict[str, Any]):
@@ -1,13 +1,11 @@
1
- from typing import Any, Dict, Union
1
+ from typing import Any, Dict, Optional
2
2
 
3
3
  import pandas as pd
4
4
  import requests
5
- from zeep.exceptions import Fault
6
- from zeep.helpers import serialize_object
7
5
 
8
6
  from brynq_sdk_functions import Functions
9
7
 
10
- from .schemas.salary import SalaryCreate, SalaryGet, SalaryUpdate
8
+ from .schemas.salary import SalaryCreate, SalaryGet
11
9
 
12
10
 
13
11
  class Salaries:
@@ -88,73 +86,58 @@ class Salaries:
88
86
  )
89
87
  return resp
90
88
 
91
- def get_current(self, employee_id: Union[int, str]) -> pd.DataFrame:
89
+ def get_cost_per_hour(self,
90
+ company_id: Optional[str] = None,
91
+ employee_id: Optional[str] = None,
92
+ year: Optional[int] = None,
93
+ period: Optional[int] = None) -> pd.DataFrame:
92
94
  """
93
- Get current salary for an employee via SOAP.
95
+ Get the estimated cost per hour of employees.
94
96
 
95
97
  Args:
96
- employee_id: The ID of the employee
98
+ company_id: Optional company ID. If not provided, fetches for all companies.
99
+ employee_id: Optional employee ID filter
100
+ year: Optional year filter
101
+ period: Optional period filter
97
102
 
98
103
  Returns:
99
- DataFrame with current salary
100
- """
101
- if self.nmbrs.mock_mode:
102
- return pd.DataFrame()
103
-
104
- try:
105
- response = self.nmbrs.soap_client_employees.service.Salary_GetCurrent(
106
- EmployeeId=int(employee_id),
107
- _soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
108
- )
109
-
110
- if response:
111
- serialized = serialize_object(response)
112
- if not isinstance(serialized, list):
113
- serialized = [serialized]
114
- df = pd.DataFrame(serialized)
115
- df['employee_id'] = str(employee_id)
116
- return df
117
- else:
118
- return pd.DataFrame()
119
-
120
- except Fault as e:
121
- raise Exception(f"SOAP request failed: {str(e)}")
122
- except Exception as e:
123
- raise Exception(f"Failed to get Salary: {str(e)}")
124
-
125
- def update(self, data: Dict[str, Any]):
104
+ DataFrame containing cost per hour data
126
105
  """
127
- Update salary for an employee using SOAP API.
106
+ if company_id:
107
+ return self._get_cost_per_hour(company_id, employee_id, year, period)
128
108
 
129
- Args:
130
- data: Dictionary containing salary data with fields matching SalaryUpdate schema:
131
- - employee_id: Employee ID
132
- - salary_value: Salary value (gross amount)
133
- - salary_type: Salary type (1=FulltimeSalary, 2=ParttimeSalary, etc.)
134
- - start_date: Start date (optional)
109
+ df = pd.DataFrame()
110
+ for company in self.nmbrs.company_ids:
111
+ df = pd.concat([df, self._get_cost_per_hour(company, employee_id, year, period)])
135
112
 
136
- Returns:
137
- Response from the API
113
+ return df
114
+
115
+ def _get_cost_per_hour(self,
116
+ company_id: str,
117
+ employee_id: Optional[str] = None,
118
+ year: Optional[int] = None,
119
+ period: Optional[int] = None) -> pd.DataFrame:
120
+ """
121
+ Internal method to get cost per hour for a single company.
138
122
  """
139
- try:
140
- salary_model = SalaryUpdate(**data)
141
-
142
- if self.nmbrs.mock_mode:
143
- return salary_model
144
-
145
- # Make SOAP request with dict - SalaryInput only has Value, Type, SalaryTable
146
- response = self.nmbrs.soap_client_employees.service.Salary_UpdateCurrent(
147
- EmployeeId=salary_model.employee_id,
148
- Salary={
149
- 'Value': salary_model.salary_value,
150
- 'Type': salary_model.salary_type
151
- },
152
- _soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
153
- )
154
-
155
- return response
156
-
157
- except Fault as e:
158
- raise Exception(f"SOAP request failed: {str(e)}")
159
- except Exception as e:
160
- raise Exception(f"Failed to update Salary: {str(e)}")
123
+ params = {}
124
+ if employee_id is not None:
125
+ params['employeeId'] = employee_id
126
+ if year is not None:
127
+ params['year'] = year
128
+ if period is not None:
129
+ params['period'] = period
130
+
131
+ request = requests.Request(
132
+ method='GET',
133
+ url=f"{self.nmbrs.base_url}companies/{company_id}/employees/costperhour",
134
+ params=params
135
+ )
136
+
137
+ data = self.nmbrs.get_paginated_result(request)
138
+ df = pd.DataFrame(data)
139
+
140
+ if not df.empty:
141
+ df['companyId'] = company_id
142
+
143
+ return df
@@ -1,10 +1,7 @@
1
- from typing import Dict, List, Union, Tuple
1
+ from typing import Dict, Tuple
2
2
  import pandas as pd
3
3
  from zeep.exceptions import Fault
4
- from zeep.ns import WSDL, SOAP_ENV_11
5
- from zeep.xsd import ComplexType, Element, String
6
4
  from zeep.helpers import serialize_object
7
- # import logging
8
5
  from brynq_sdk_functions import Functions
9
6
  from .schemas.salary import SalaryTableGet, SalaryScalesGet, SalaryStepsGet
10
7
 
@@ -1,11 +1,11 @@
1
+ from typing import Any, Dict, Tuple
2
+
1
3
  import pandas as pd
2
4
  import requests
5
+
3
6
  from brynq_sdk_functions import Functions
4
- from .schemas.schedules import ScheduleGet, ScheduleCreate, ScheduleUpdate
5
- from datetime import datetime
6
- from typing import Dict, Any, Tuple, Union
7
- from zeep.exceptions import Fault
8
- from zeep.helpers import serialize_object
7
+
8
+ from .schemas.schedules import ScheduleCreate, ScheduleGet
9
9
 
10
10
 
11
11
  class Schedule:
@@ -83,76 +83,3 @@ class Schedule:
83
83
 
84
84
  except Exception as e:
85
85
  raise ValueError(f"Schedule validation failed: {str(e)}")
86
-
87
- def update(self, data: Dict[str, Any]):
88
- """
89
- Update a schedule for an employee using SOAP API.
90
-
91
- Args:
92
- data: Dictionary containing schedule data with fields matching ScheduleUpdate schema:
93
- - employeeId: Employee ID
94
- - startDate: Start date of the schedule
95
- - parttimePercentage: Part-time percentage (0-100)
96
- - hoursMonday, hoursTuesday, etc.: Hours for each day of week 1
97
- - hoursMonday2, hoursTuesday2, etc.: Hours for each day of week 2
98
-
99
- Returns:
100
- Response from the API
101
- """
102
- try:
103
- schedule_model = ScheduleUpdate(**data)
104
-
105
- if self.nmbrs.mock_mode:
106
- return schedule_model
107
-
108
- # Convert to SOAP object
109
- schedule_soap = schedule_model.to_soap_schedule(self.nmbrs.soap_client_employees)
110
-
111
- # Make SOAP request - CompanyRoosterNr is separate parameter (0 = no template)
112
- response = self.nmbrs.soap_client_employees.service.Schedule_UpdateCurrent(
113
- EmployeeId=schedule_model.employee_id,
114
- Schedule=schedule_soap,
115
- CompanyRoosterNr=schedule_model.company_rooster_nr or 0,
116
- _soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
117
- )
118
-
119
- return response
120
-
121
- except Fault as e:
122
- raise Exception(f"SOAP request failed: {str(e)}")
123
- except Exception as e:
124
- raise Exception(f"Failed to update Schedule: {str(e)}")
125
-
126
- def get_current(self, employee_id: Union[int, str]) -> pd.DataFrame:
127
- """
128
- Get current schedule for an employee via SOAP.
129
-
130
- Args:
131
- employee_id: The ID of the employee
132
-
133
- Returns:
134
- DataFrame with current schedule
135
- """
136
- if self.nmbrs.mock_mode:
137
- return pd.DataFrame()
138
-
139
- try:
140
- response = self.nmbrs.soap_client_employees.service.Schedule_GetCurrent(
141
- EmployeeId=int(employee_id),
142
- _soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
143
- )
144
-
145
- if response:
146
- serialized = serialize_object(response)
147
- if not isinstance(serialized, list):
148
- serialized = [serialized]
149
- df = pd.DataFrame(serialized)
150
- df['employee_id'] = str(employee_id)
151
- return df
152
- else:
153
- return pd.DataFrame()
154
-
155
- except Fault as e:
156
- raise Exception(f"SOAP request failed: {str(e)}")
157
- except Exception as e:
158
- raise Exception(f"Failed to get Schedule: {str(e)}")
@@ -22,28 +22,38 @@ from datetime import datetime
22
22
  class AbsenceGet(BrynQPanderaDataFrameModel):
23
23
  employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="EmployeeId")
24
24
  absence_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Absence ID", alias="AbsenceId")
25
- comment: Series[String] = pa.Field(coerce=True, description="Comment", alias="Comment")
25
+ comment: Series[String] = pa.Field(coerce=True, nullable=True, description="Comment", alias="Comment")
26
26
  percentage: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Percentage", alias="Percentage")
27
27
  start: Series[DateTime] = pa.Field(coerce=True, description="Start", alias="Start")
28
28
  registration_start_date: Series[DateTime] = pa.Field(coerce=True, description="Registration Start Date", alias="RegistrationStartDate")
29
- end: Series[DateTime] = pa.Field(coerce=True, description="End", alias="End")
30
- registration_end_date: Series[DateTime] = pa.Field(coerce=True, description="Registration End Date", alias="RegistrationEndDate")
31
- dossier: Series[String] = pa.Field(coerce=True, description="Dossier", alias="Dossier")
32
- dossiernr: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Dossier Number", alias="Dossiernr")
29
+ end: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="End", alias="End")
30
+ registration_end_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Registration End Date", alias="RegistrationEndDate")
31
+ dossier: Series[String] = pa.Field(coerce=True, nullable=True, description="Dossier", alias="Dossier")
32
+ dossiernr: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Dossier Number", alias="Dossiernr")
33
+
34
+ class _Annotation:
35
+ primary_key = "absence_id"
36
+ foreign_keys = {
37
+ "employee_id": {
38
+ "parent_schema": "EmployeeSchema",
39
+ "parent_column": "employee_id",
40
+ "cardinality": "N:1"
41
+ }
42
+ }
33
43
 
34
44
 
35
45
  class AbsenceCreate(BaseModel):
36
- employee_id: Optional[int] = Field(None, example="1234567890", description="Employee ID", alias="EmployeeId")
37
- absence_id: int = Field(coerce=True, description="Absence ID", alias="AbsenceId")
38
- comment: str = Field(coerce=True, description="Comment", alias="Comment")
39
- percentage: int = Field(coerce=True, description="Percentage", alias="Percentage")
40
- start: datetime = Field(coerce=True, description="Start", alias="Start")
41
- registration_start_date: datetime = Field(coerce=True, description="Registration Start Date", alias="RegistrationStartDate")
42
- end: datetime = Field(coerce=True, description="End", alias="End")
43
- registration_end_date: datetime = Field(coerce=True, description="Registration End Date", alias="RegistrationEndDate")
44
- dossier: str = Field(coerce=True, description="Dossier", alias="Dossier")
45
- dossiernr: int = Field(coerce=True, description="Dossier Number", alias="Dossiernr")
46
- new_dossier: bool = pa.Field(coerce=True, description="New Dossier", alias="NewDossier")
46
+ employee_id: Optional[int] = Field(None, example=1234567890, description="Employee ID", alias="EmployeeId")
47
+ absence_id: int = Field(..., example=1, description="Absence ID", alias="AbsenceId")
48
+ comment: Optional[str] = Field(None, example="Sick leave", description="Comment", alias="Comment")
49
+ percentage: int = Field(..., ge=0, le=100, example=100, description="Percentage", alias="Percentage")
50
+ start: datetime = Field(..., example="2021-01-01T00:00:00Z", description="Start", alias="Start")
51
+ registration_start_date: datetime = Field(..., example="2021-01-01T00:00:00Z", description="Registration Start Date", alias="RegistrationStartDate")
52
+ end: Optional[datetime] = Field(None, example="2021-01-10T00:00:00Z", description="End", alias="End")
53
+ registration_end_date: Optional[datetime] = Field(None, example="2021-01-10T00:00:00Z", description="Registration End Date", alias="RegistrationEndDate")
54
+ dossier: Optional[str] = Field(None, example="Sick leave dossier", description="Dossier", alias="Dossier")
55
+ dossiernr: Optional[int] = Field(None, example=1, description="Dossier Number", alias="Dossiernr")
56
+ new_dossier: bool = Field(..., example=True, description="New Dossier", alias="NewDossier")
47
57
 
48
58
 
49
59
  def to_soap_settings(self, soap_client):
@@ -2,9 +2,8 @@ from typing import Annotated, Optional
2
2
 
3
3
  import pandas as pd
4
4
  import pandera as pa
5
- import pandera.extensions as extensions
6
5
  from pandera import Bool
7
- from pandera.typing import DateTime, Float, Series, String
6
+ from pandera.typing import Series, String
8
7
  from pydantic import BaseModel, Field, StringConstraints
9
8
 
10
9
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
@@ -75,30 +74,4 @@ class AddressCreate(BaseModel):
75
74
  max_length=3
76
75
  )
77
76
  ] = Field(..., example="NL", description="Country ISO Code", alias="countryISOCode")
78
- period: Period
79
-
80
-
81
- class AddressDelete(BaseModel):
82
- """Schema for deleting an address via SOAP API."""
83
- employee_id: int = Field(..., example=12345, description="Employee ID", alias="employeeId")
84
- address_id: int = Field(..., example=67890, description="Address ID to delete", alias="addressId")
85
-
86
- class Config:
87
- populate_by_name = True
88
-
89
-
90
- class AddressUpdate(BaseModel):
91
- """Schema for updating an address via SOAP API."""
92
- id: int = Field(..., example=67890, description="Address ID to update", alias="id")
93
- default: bool = Field(False, example=False, description="Is default address", alias="default")
94
- street: str = Field(..., min_length=1, max_length=200, example="Naritaweg", description="Street", alias="street")
95
- house_number: Optional[str] = Field(None, max_length=20, example="70", description="House Number", alias="houseNumber")
96
- house_number_addition: Optional[str] = Field(None, max_length=20, example="A", description="House Number Addition", alias="houseNumberAddition")
97
- postal_code: Optional[str] = Field(None, max_length=15, example="1043BZ", description="Postal Code", alias="postalCode")
98
- city: str = Field(..., min_length=1, max_length=100, example="Amsterdam", description="City", alias="city")
99
- state_province: Optional[str] = Field(None, max_length=100, example="Noord-Holland", description="State or Province", alias="stateProvince")
100
- country_iso_code: str = Field(..., min_length=2, max_length=3, example="NL", description="Country ISO Code", alias="countryISOCode")
101
- address_type: str = Field("HomeAddress", example="HomeAddress", description="Address type", alias="type")
102
-
103
- class Config:
104
- populate_by_name = True
77
+ period: Period = Field(..., example={"year": 2021, "period": 4}, description="Period details")
@@ -1,6 +1,4 @@
1
- import pandas as pd
2
1
  import pandera as pa
3
- from pandera import Bool
4
2
  from pandera.typing import Series, String, DateTime
5
3
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
6
4
  from typing import Optional, Literal
@@ -1,12 +1,13 @@
1
- import pandas as pd
1
+ from datetime import datetime
2
+ from typing import Optional
3
+
2
4
  import pandera as pa
3
5
  from pandera import Bool
4
- from pandera.typing import Series, String, Float, DateTime
5
- import pandera.extensions as extensions
6
- from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
- from typing import Optional
6
+ from pandera.typing import DateTime, Float, Series, String
8
7
  from pydantic import BaseModel, Field, field_validator
9
- from datetime import datetime
8
+
9
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
10
+
10
11
 
11
12
  # ---------------------------
12
13
  # Get Schemas
@@ -98,8 +98,6 @@ class CostcenterTable(BaseModel):
98
98
  default: Optional[bool] = Field(True, example=True, description="Default", alias="default")
99
99
 
100
100
 
101
-
102
-
103
101
  class EmployeeCostcenterUpdate(BaseModel):
104
102
  employee_id: str = Field(..., example="c605f980-1c4c-42c1-8ddb-2d90c58da0b3", description="Employee Cost Center ID", alias="employeeId")
105
103
  employee_cost_centers: List[CostcenterTable] = Field(..., description="Employee Cost Centers", alias="employeeCostCenters")
@@ -115,10 +113,12 @@ class EmployeeCostcenterDelete(BaseModel):
115
113
  # CostCenter CRUD schemas - These are hypothetical since the API doesn't have create/update/delete endpoints
116
114
  # but we add them for consistency with other schema files
117
115
  class CostcenterCreate(BaseModel):
116
+ company_id: str = Field(..., description="Company identifier", alias="companyId")
118
117
  code: str = Field(..., example="CC001", description="Code", alias="code")
119
118
  description: str = Field(..., example="Sales Department", description="Description", alias="description")
120
119
 
121
120
  class CostcenterUpdate(BaseModel):
121
+ company_id: str = Field(..., description="Company identifier", alias="companyId")
122
122
  cost_center_id: str = Field(..., example="a405f980-1c4c-42c1-8ddb-2d90c58da0b1", description="Cost Center ID", alias="costCenterId")
123
123
  code: str = Field(..., example="CC001", description="Code", alias="code")
124
124
  description: str = Field(..., example="Sales Department", description="Description", alias="description")
@@ -23,10 +23,12 @@ class CostunitGet(BrynQPanderaDataFrameModel):
23
23
  # Upload Schemas
24
24
  # ---------------------------
25
25
  class CostunitCreate(BaseModel):
26
+ company_id: str = Field(..., description="Company identifier", alias="companyId")
26
27
  code: str = Field(..., example="CU001", description="Code", alias="code")
27
28
  description: str = Field(..., example="Marketing Unit", description="Description", alias="description")
28
29
 
29
30
  class CostunitUpdate(BaseModel):
31
+ company_id: str = Field(..., description="Company identifier", alias="companyId")
30
32
  cost_unit_id: str = Field(..., example="b505f980-1c4c-42c1-8ddb-2d90c58da0b2", description="Cost Unit ID", alias="costUnitId")
31
33
  code: str = Field(..., example="CU001", description="Code", alias="code")
32
34
  description: str = Field(..., example="Marketing Unit", description="Description", alias="description")
@@ -15,22 +15,22 @@ from pydantic import BaseModel, Field
15
15
  # ---------------------------
16
16
  # PANDERA SCHEMA
17
17
  class VariableDaysGet(BrynQPanderaDataFrameModel):
18
- days: Series[String] = pa.Field(coerce=True, description="Hour Component ID", alias="days")
19
- days_for_wage_components_per_day: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Hour Code", alias="daysForWageComponentsPerDay")
18
+ days: Series[String] = pa.Field(coerce=True, description="Days", alias="days")
19
+ days_for_wage_components_per_day: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Days for Wage Components per Day", alias="daysForWageComponentsPerDay")
20
20
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
21
21
 
22
- class Config:
23
- coerce = True
22
+ class _Annotation:
23
+ primary_key = "employee_id"
24
24
 
25
25
  class FixedDaysGet(BrynQPanderaDataFrameModel):
26
- days: Series[String] = pa.Field(coerce=True, description="Hour Component ID", alias="days")
27
- days_for_wage_components_per_day: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Hour Code", alias="daysForWageComponentsPerDay")
26
+ days: Series[String] = pa.Field(coerce=True, description="Days", alias="days")
27
+ days_for_wage_components_per_day: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Days for Wage Components per Day", alias="daysForWageComponentsPerDay")
28
28
  end_year: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True, description="End Year", alias="endYear")
29
29
  end_period: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True, description="End Period", alias="endPeriod")
30
30
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
31
31
 
32
- class Config:
33
- coerce = True
32
+ class _Annotation:
33
+ primary_key = "employee_id"
34
34
 
35
35
  # ---------------------------
36
36
  # Upload Schemas
@@ -70,7 +70,7 @@ class VariableHoursCreate(BaseModel):
70
70
  cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
71
71
  cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
72
72
  comment: Optional[str] = Field(None, example="Shift hours", description="Comment", alias="comment")
73
- period_details: Optional[PeriodPost] = Field(None, example="Period details", description="Period details", alias="periodDetails")
73
+ period_details: Optional[PeriodPost] = Field(None, example={"period": {"year": 2025, "period": 1}, "unprotectedMode": False}, description="Period details", alias="periodDetails")
74
74
 
75
75
  class VariableHoursUpdate(BaseModel):
76
76
  hour_component_id: str = Field(..., example="49a69eda-252e-4ccb-a220-38ea90511d4f", description="Hour Component ID", alias="hourComponentId")
@@ -86,11 +86,13 @@ class HoursDelete(BaseModel):
86
86
 
87
87
 
88
88
  class FixedDaysCreate(BaseModel):
89
+ employee_id: str = Field(..., example="3054d4cf-b449-489d-8d2e-5dd30e5ab994", description="Employee identifier", alias="employeeId")
89
90
  number_of_days: int = Field(..., ge=0, le=1000, example=40, description="Days", alias="days")
90
91
  days_for_wage_components_per_day: Optional[int] = Field(None, ge=0, le=1000, example=40, description="Days for Wage Components per Day", alias="daysForWageComponentsPerDay")
91
- period_details: Optional[PeriodPost] = Field(None, example="Period details", description="Period details", alias="periodDetails")
92
+ period_details: Optional[PeriodPost] = Field(None, example={"period": {"year": 2025, "period": 1}, "unprotectedMode": False}, description="Period details", alias="periodDetails")
92
93
 
93
94
  class VariableDaysCreate(BaseModel):
95
+ employee_id: str = Field(..., example="3054d4cf-b449-489d-8d2e-5dd30e5ab994", description="Employee identifier", alias="employeeId")
94
96
  number_of_days: int = Field(..., ge=0, le=1000, example=40, description="Days", alias="days")
95
97
  days_for_wage_components_per_day: Optional[int] = Field(None, ge=0, le=1000, example=40, description="Days for Wage Components per Day", alias="daysForWageComponentsPerDay")
96
- period_details: Optional[PeriodPost] = Field(None, example="Period details", description="Period details", alias="periodDetails")
98
+ period_details: Optional[PeriodPost] = Field(None, example={"period": {"year": 2025, "period": 1}, "unprotectedMode": False}, description="Period details", alias="periodDetails")
@@ -1,11 +1,8 @@
1
- import pandas as pd
2
1
  import pandera as pa
3
- from pandera import Bool
4
- from pandera.typing import Series, String, Float, DateTime
5
- import pandera.extensions as extensions
2
+ from pandera.typing import Series, String
3
+
6
4
  from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
- from typing import Optional, Annotated
8
- from pydantic import BaseModel, Field, StringConstraints
5
+
9
6
 
10
7
  # ---------------------------
11
8
  # Get Schemas
@@ -13,26 +10,7 @@ from pydantic import BaseModel, Field, StringConstraints
13
10
  class DebtorsGet(BrynQPanderaDataFrameModel):
14
11
  debtor_id: Series[String] = pa.Field(coerce=True, description="Debtor ID", alias="debtorId")
15
12
  number: Series[String] = pa.Field(coerce=True, description="Debtor number", alias="number")
16
- name: Series[Bool] = pa.Field(coerce=True, description="Debtor name", alias="name")
17
-
18
-
19
- # ---------------------------
20
- # SOAP Schemas
21
- # ---------------------------
22
- class DebtorCreate(BaseModel):
23
- """Schema for creating a debtor via SOAP API."""
24
- number: int = Field(..., ge=1, description="Debtor number", alias="number", example=1001)
25
- name: str = Field(..., min_length=1, max_length=200, description="Debtor name", alias="name", example="New Debtor Company")
26
-
27
- class Config:
28
- populate_by_name = True
29
-
30
-
31
- class DebtorUpdate(BaseModel):
32
- """Schema for updating a debtor via SOAP API."""
33
- debtor_id: int = Field(..., description="Debtor ID", alias="debtorId", example=34548)
34
- number: int = Field(..., ge=1, description="Debtor number", alias="number", example=1001)
35
- name: str = Field(..., min_length=1, max_length=200, description="Debtor name", alias="name", example="Updated Debtor Company")
13
+ name: Series[String] = pa.Field(coerce=True, description="Debtor name", alias="name")
36
14
 
37
- class Config:
38
- populate_by_name = True
15
+ class _Annotation:
16
+ primary_key = "debtor_id"