brynq-sdk-nmbrs 2.3.1.dev0__py3-none-any.whl → 2.3.3.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. brynq_sdk_nmbrs/__init__.py +86 -80
  2. brynq_sdk_nmbrs/address.py +82 -2
  3. brynq_sdk_nmbrs/bank.py +5 -2
  4. brynq_sdk_nmbrs/children.py +96 -61
  5. brynq_sdk_nmbrs/companies.py +45 -1
  6. brynq_sdk_nmbrs/debtors.py +76 -2
  7. brynq_sdk_nmbrs/department.py +113 -1
  8. brynq_sdk_nmbrs/document.py +50 -0
  9. brynq_sdk_nmbrs/employee_wage_tax_settings.py +16 -9
  10. brynq_sdk_nmbrs/function.py +128 -2
  11. brynq_sdk_nmbrs/leave.py +105 -8
  12. brynq_sdk_nmbrs/salaries.py +75 -3
  13. brynq_sdk_nmbrs/schedules.py +77 -3
  14. brynq_sdk_nmbrs/schemas/address.py +33 -4
  15. brynq_sdk_nmbrs/schemas/children.py +67 -0
  16. brynq_sdk_nmbrs/schemas/company.py +16 -0
  17. brynq_sdk_nmbrs/schemas/debtor.py +23 -1
  18. brynq_sdk_nmbrs/schemas/department.py +33 -0
  19. brynq_sdk_nmbrs/schemas/document.py +13 -0
  20. brynq_sdk_nmbrs/schemas/employees.py +3 -1
  21. brynq_sdk_nmbrs/schemas/function.py +28 -0
  22. brynq_sdk_nmbrs/schemas/leave.py +12 -0
  23. brynq_sdk_nmbrs/schemas/salary.py +18 -0
  24. brynq_sdk_nmbrs/schemas/schedules.py +81 -16
  25. brynq_sdk_nmbrs/schemas/social_insurance.py +39 -6
  26. brynq_sdk_nmbrs/schemas/wage_tax.py +65 -5
  27. brynq_sdk_nmbrs/schemas/wage_tax_settings.py +42 -29
  28. brynq_sdk_nmbrs/schemas/wagecomponents.py +66 -9
  29. brynq_sdk_nmbrs/social_insurance.py +81 -3
  30. brynq_sdk_nmbrs/wage_tax.py +104 -3
  31. brynq_sdk_nmbrs/wagecomponents.py +81 -45
  32. {brynq_sdk_nmbrs-2.3.1.dev0.dist-info → brynq_sdk_nmbrs-2.3.3.dev0.dist-info}/METADATA +1 -1
  33. brynq_sdk_nmbrs-2.3.3.dev0.dist-info/RECORD +55 -0
  34. {brynq_sdk_nmbrs-2.3.1.dev0.dist-info → brynq_sdk_nmbrs-2.3.3.dev0.dist-info}/WHEEL +1 -1
  35. brynq_sdk_nmbrs-2.3.1.dev0.dist-info/RECORD +0 -52
  36. {brynq_sdk_nmbrs-2.3.1.dev0.dist-info → brynq_sdk_nmbrs-2.3.3.dev0.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,13 @@
1
- import math
2
- from typing import Any, Dict
1
+ from typing import Any, Dict, Union
3
2
 
4
3
  import pandas as pd
5
4
  import requests
5
+ from zeep.exceptions import Fault
6
+ from zeep.helpers import serialize_object
6
7
 
7
8
  from brynq_sdk_functions import Functions
8
9
 
9
- from .schemas.salary import SalaryCreate, SalaryGet
10
+ from .schemas.salary import SalaryCreate, SalaryGet, SalaryUpdate
10
11
 
11
12
 
12
13
  class Salaries:
@@ -86,3 +87,74 @@ class Salaries:
86
87
  timeout=self.nmbrs.timeout
87
88
  )
88
89
  return resp
90
+
91
+ def get_current(self, employee_id: Union[int, str]) -> pd.DataFrame:
92
+ """
93
+ Get current salary for an employee via SOAP.
94
+
95
+ Args:
96
+ employee_id: The ID of the employee
97
+
98
+ 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]):
126
+ """
127
+ Update salary for an employee using SOAP API.
128
+
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)
135
+
136
+ Returns:
137
+ Response from the API
138
+ """
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)}")
@@ -1,10 +1,11 @@
1
1
  import pandas as pd
2
2
  import requests
3
- import math
4
3
  from brynq_sdk_functions import Functions
5
- from .schemas.schedules import ScheduleGet, ScheduleCreate
4
+ from .schemas.schedules import ScheduleGet, ScheduleCreate, ScheduleUpdate
6
5
  from datetime import datetime
7
- from typing import Dict, Any, Optional, Tuple
6
+ from typing import Dict, Any, Tuple, Union
7
+ from zeep.exceptions import Fault
8
+ from zeep.helpers import serialize_object
8
9
 
9
10
 
10
11
  class Schedule:
@@ -82,3 +83,76 @@ class Schedule:
82
83
 
83
84
  except Exception as e:
84
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)}")
@@ -1,12 +1,15 @@
1
+ from typing import Annotated, Optional
2
+
1
3
  import pandas as pd
2
4
  import pandera as pa
3
- from pandera import Bool
4
- from pandera.typing import Series, String, Float, DateTime
5
5
  import pandera.extensions as extensions
6
- from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
- from typing import Optional, Annotated
6
+ from pandera import Bool
7
+ from pandera.typing import DateTime, Float, Series, String
8
8
  from pydantic import BaseModel, Field, StringConstraints
9
9
 
10
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
11
+
12
+
10
13
  # ---------------------------
11
14
  # Get Schemas
12
15
  # ---------------------------
@@ -73,3 +76,29 @@ class AddressCreate(BaseModel):
73
76
  )
74
77
  ] = Field(..., example="NL", description="Country ISO Code", alias="countryISOCode")
75
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
@@ -0,0 +1,67 @@
1
+ import pandas as pd
2
+ import pandera as pa
3
+ from pandera import Bool
4
+ from pandera.typing import Series, String, DateTime
5
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
6
+ from typing import Optional, Literal
7
+ from pydantic import BaseModel, Field
8
+ from datetime import datetime
9
+
10
+ # ---------------------------
11
+ # Get Schemas
12
+ # ---------------------------
13
+ class ChildrenGet(BrynQPanderaDataFrameModel):
14
+ employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
15
+ child_id: Series[String] = pa.Field(coerce=True, description="Child ID", alias="childId")
16
+ name: Series[String] = pa.Field(coerce=True, description="Last Name", alias="name")
17
+ first_name: Series[String] = pa.Field(coerce=True, description="First Name", alias="firstName")
18
+ initials: Series[String] = pa.Field(coerce=True, nullable=True, description="Initials", alias="initials")
19
+ gender: Series[String] = pa.Field(coerce=True, description="Gender", alias="gender")
20
+ birthday: Series[DateTime] = pa.Field(coerce=True, description="Birthday", alias="birthday")
21
+
22
+ class _Annotation:
23
+ primary_key = "child_id"
24
+ foreign_keys = {
25
+ "employee_id": {
26
+ "parent_schema": "EmployeeSchema",
27
+ "parent_column": "employee_id",
28
+ "cardinality": "N:1"
29
+ }
30
+ }
31
+
32
+ # ---------------------------
33
+ # Upload Schemas
34
+ # ---------------------------
35
+ class ChildCreate(BaseModel):
36
+ """Schema for creating a child via SOAP API."""
37
+ name: str = Field(..., min_length=1, max_length=100, example="Doe", description="Last Name", alias="name")
38
+ first_name: str = Field(..., min_length=1, max_length=100, example="John", description="First Name", alias="firstName")
39
+ initials: Optional[str] = Field(None, max_length=20, example="J.D.", description="Initials", alias="initials")
40
+ gender: Literal["male", "female", "unknown", "undefined"] = Field(..., example="male", description="Gender", alias="gender")
41
+ birthday: datetime = Field(..., example="2020-01-01T00:00:00", description="Birthday", alias="birthday")
42
+
43
+ class Config:
44
+ populate_by_name = True
45
+
46
+
47
+ class ChildDelete(BaseModel):
48
+ """Schema for deleting a child via SOAP API."""
49
+ employee_id: int = Field(..., example=12345, description="Employee ID", alias="employeeId")
50
+ child_id: int = Field(..., example=67890, description="Child ID to delete", alias="childId")
51
+
52
+ class Config:
53
+ populate_by_name = True
54
+
55
+
56
+ class ChildUpdate(BaseModel):
57
+ """Schema for updating a child via SOAP API."""
58
+ id: int = Field(..., example=67890, description="Child ID to update", alias="id")
59
+ name: str = Field(..., min_length=1, max_length=100, example="Doe", description="Last Name", alias="name")
60
+ first_name: str = Field(..., min_length=1, max_length=100, example="John", description="First Name", alias="firstName")
61
+ initials: Optional[str] = Field(None, max_length=20, example="J.D.", description="Initials", alias="initials")
62
+ gender: Literal["male", "female", "unknown", "undefined"] = Field(..., example="male", description="Gender", alias="gender")
63
+ birthday: datetime = Field(..., example="2020-01-01T00:00:00", description="Birthday", alias="birthday")
64
+
65
+ class Config:
66
+ populate_by_name = True
67
+
@@ -0,0 +1,16 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import Optional
3
+
4
+
5
+ class CompanyCreate(BaseModel):
6
+ """Schema for creating a company via SOAP API."""
7
+ debtor_id: int = Field(..., description="Debtor ID", alias="debtorId", example=34548)
8
+ company_name: str = Field(..., min_length=1, max_length=200, description="Company name", alias="companyName", example="New Company BV")
9
+ period_type: int = Field(1, ge=1, le=4, description="Period type (1=Monthly, 2=4-Weekly, 3=Weekly, 4=Quarterly)", alias="periodType", example=1)
10
+ default_company_id: int = Field(0, description="Default company ID to copy settings from (0 for none)", alias="defaultCompanyId", example=0)
11
+ labour_agreement_settings_group_guid: Optional[str] = Field(None, description="Labour agreement settings group GUID", alias="labourAgreementSettingsGroupGuid", example="00000000-0000-0000-0000-000000000000")
12
+ pay_in_advance: bool = Field(False, description="Pay in advance", alias="payInAdvance", example=False)
13
+
14
+ class Config:
15
+ populate_by_name = True
16
+
@@ -13,4 +13,26 @@ from pydantic import BaseModel, Field, StringConstraints
13
13
  class DebtorsGet(BrynQPanderaDataFrameModel):
14
14
  debtor_id: Series[String] = pa.Field(coerce=True, description="Debtor ID", alias="debtorId")
15
15
  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")
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")
36
+
37
+ class Config:
38
+ populate_by_name = True
@@ -56,3 +56,36 @@ class EmployeeDepartmentUpdate(BaseModel):
56
56
 
57
57
  class Config:
58
58
  primary_key = "departmentId"
59
+
60
+
61
+ # ---------------------------
62
+ # SOAP Schemas (DebtorService)
63
+ # ---------------------------
64
+ class DepartmentMasterCreate(BaseModel):
65
+ """Schema for creating a master department via SOAP API."""
66
+ debtor_id: int = Field(..., description="Debtor ID", alias="debtorId", example=34548)
67
+ code: int = Field(..., ge=1, description="Department Code", alias="code", example=101)
68
+ description: str = Field(..., min_length=1, max_length=200, description="Department Description", alias="description", example="Engineering")
69
+
70
+ class Config:
71
+ populate_by_name = True
72
+
73
+
74
+ class DepartmentMasterUpdate(BaseModel):
75
+ """Schema for updating a master department via SOAP API."""
76
+ debtor_id: int = Field(..., description="Debtor ID", alias="debtorId", example=34548)
77
+ department_id: int = Field(..., description="Department ID", alias="departmentId", example=12345)
78
+ code: int = Field(..., ge=1, description="Department Code", alias="code", example=101)
79
+ description: str = Field(..., min_length=1, max_length=200, description="Department Description", alias="description", example="Engineering Updated")
80
+
81
+ class Config:
82
+ populate_by_name = True
83
+
84
+
85
+ class DepartmentMasterDelete(BaseModel):
86
+ """Schema for deleting a master department via SOAP API."""
87
+ debtor_id: int = Field(..., description="Debtor ID", alias="debtorId", example=34548)
88
+ department_id: int = Field(..., description="Department ID to delete", alias="departmentId", example=12345)
89
+
90
+ class Config:
91
+ populate_by_name = True
@@ -0,0 +1,13 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import Optional
3
+
4
+
5
+ class DocumentUpload(BaseModel):
6
+ """Schema for uploading a document to an employee via SOAP API."""
7
+ employee_id: int = Field(..., description="Employee ID", alias="employeeId", example=276967)
8
+ document_name: str = Field(..., description="Document name (with extension)", alias="documentName", example="contract.pdf")
9
+ document_type_guid: str = Field(..., description="Document type GUID", alias="documentTypeGuid", example="00000000-0000-0000-0000-000000000000")
10
+
11
+ class Config:
12
+ populate_by_name = True
13
+
@@ -25,7 +25,9 @@ class EmployeeGet(BrynQPanderaDataFrameModel):
25
25
  last_name: Series[String] = pa.Field(coerce=True, description="Last Name", alias="basicInfo.lastName")
26
26
  employee_type: Series[String] = pa.Field(coerce=True, description="Employee Type", alias="basicInfo.employeeType")
27
27
  birth_date: Series[DateTime] = pa.Field(coerce=True, description="Birth Date", alias="birthInfo.birthDate")
28
- birth_country_code_iso: Series[String] = pa.Field(coerce=True, nullable=True, description="Birth Country Code ISO", alias="birthInfo.birthCountry")
28
+ birth_country_code_iso: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Birth Country Code ISO", alias="birthInfo.birthCountry")
29
+ birth_country: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Birth Country Code ISO", alias="birthInfo.birthCountry.codeISO")
30
+ birth_country_description: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Birth Country Code ISO", alias="birthInfo.birthCountry.description")
29
31
  nationality_code_iso: Series[String] = pa.Field(coerce=True, nullable=True, description="Nationality Code ISO", alias="birthInfo.nationality.codeISO")
30
32
  gender: Series[String] = pa.Field(coerce=True, nullable=True, description="Gender", alias="birthInfo.gender")
31
33
  private_email: Series[String] = pa.Field(coerce=True, nullable=True, description="Private Email", alias="contactInfo.privateEmail")
@@ -47,3 +47,31 @@ class FunctionGet(BrynQPanderaDataFrameModel):
47
47
  class FunctionUpdate(BaseModel):
48
48
  function_id: str = Field(..., example="5981", description="Function ID", alias="functionId")
49
49
  period_details: Period = Field(..., alias="periodDetails")
50
+
51
+
52
+ # SOAP Schemas for DebtorService Functions
53
+ class FunctionCreate(BaseModel):
54
+ """Schema for creating a master function via SOAP API."""
55
+ code: int = Field(..., example=101, description="Function Code", alias="code")
56
+ description: str = Field(..., example="Software Developer", description="Function Description", alias="description")
57
+
58
+ class Config:
59
+ populate_by_name = True
60
+
61
+
62
+ class FunctionDelete(BaseModel):
63
+ """Schema for deleting a master function via SOAP API."""
64
+ function_id: int = Field(..., example=12345, description="Function ID to delete", alias="functionId")
65
+
66
+ class Config:
67
+ populate_by_name = True
68
+
69
+
70
+ class FunctionMasterUpdate(BaseModel):
71
+ """Schema for updating a master function via SOAP API."""
72
+ function_id: int = Field(..., example=12345, description="Function ID", alias="functionId")
73
+ code: int = Field(..., example=101, description="Function Code", alias="code")
74
+ description: str = Field(..., example="Senior Software Developer", description="Function Description", alias="description")
75
+
76
+ class Config:
77
+ populate_by_name = True
@@ -46,6 +46,18 @@ class LeaveDelete(BaseModel):
46
46
  leave_request_id: str = Field(..., example="49a69eda-252e-4ccb-a220-38ea90511d4f", description="Leave Request ID", alias="leaveRequestId")
47
47
 
48
48
 
49
+ class LeaveUpdate(BaseModel):
50
+ """Schema for updating leave via SOAP API."""
51
+ employee_id: int = Field(..., example=12345, description="Employee ID", alias="employeeId")
52
+ leave_id: int = Field(..., example=67890, description="Leave ID", alias="leaveId")
53
+ start_date: datetime = Field(..., example="2025-01-01T00:00:00", description="Leave Start Date", alias="startDate")
54
+ end_date: datetime = Field(..., example="2025-01-02T00:00:00", description="Leave End Date", alias="endDate")
55
+ description: Optional[str] = Field(None, example="Vacation leave", description="Description", alias="description")
56
+
57
+ class Config:
58
+ populate_by_name = True
59
+
60
+
49
61
  class LeaveBalanceGet(BrynQPanderaDataFrameModel):
50
62
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
51
63
  leave_group_id: Series[String] = pa.Field(coerce=True, nullable=True, description="Leave Group ID", alias="leaveGroupId")
@@ -76,6 +76,24 @@ class SalaryCreate(BaseModel):
76
76
  data.pop('salaryTable')
77
77
  return data
78
78
 
79
+ class Config:
80
+ populate_by_name = True
81
+
82
+
83
+ class SalaryUpdate(BaseModel):
84
+ """Schema for updating salary via SOAP API (Salary_UpdateCurrent)."""
85
+ employee_id: int = Field(..., example=12345, description="Employee ID", alias="employeeId")
86
+ salary_value: float = Field(..., ge=0, example=5000.00, description="Salary value (gross amount)", alias="salaryValue")
87
+ salary_type: str = Field(
88
+ "Bruto_Salaris_Fulltime",
89
+ example="Bruto_Salaris_Fulltime",
90
+ description="Salary type enum: Bruto_Salaris_Fulltime, Bruto_Salaris_Parttime, Bruto_Uurloon, etc.",
91
+ alias="salaryType"
92
+ )
93
+
94
+ class Config:
95
+ populate_by_name = True
96
+
79
97
  class SalaryTableGet(BrynQPanderaDataFrameModel):
80
98
  code: Series[String] = pa.Field(coerce=True, description="Salary Code", alias="Code")
81
99
  description: Series[String] = pa.Field(coerce=True, description="Salary Description", alias="Description")
@@ -1,20 +1,26 @@
1
1
  from datetime import datetime
2
- from typing import Dict, Any, Optional
2
+ from typing import Any, Dict, Optional
3
+
3
4
  import pandas as pd
4
5
  import pandera as pa
5
- from pandera import Bool, Int
6
- from pandera.typing import Series, String, Float, DateTime
7
6
  import pandera.extensions as extensions
8
- from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
+ from pandera import Bool, Int
8
+ from pandera.typing import DateTime, Float, Series, String
9
9
  from pydantic import BaseModel, Field
10
10
 
11
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
12
+
13
+
11
14
  # ---------------------------
12
15
  # Get Schemas
13
16
  # ---------------------------
14
17
  class ScheduleGet(BrynQPanderaDataFrameModel):
15
18
  schedule_id: Series[String] = pa.Field(coerce=True, description="Schedule ID", alias="scheduleId")
16
- start_date: Series[datetime] = pa.Field(coerce=True, description="Start Date", alias="startDate")
19
+ start_date_schedule: Series[datetime] = pa.Field(coerce=True, description="Start Date", alias="startDate")
17
20
  parttime_percentage: Series[Float] = pa.Field(coerce=True, description="Part-Time Percentage", alias="parttimePercentage")
21
+ hours_per_week: Series[Float] = pa.Field(coerce=True, nullable=True, description="Hours per week", alias="hoursPerWeek")
22
+ days_per_week: Series[Float] = pa.Field(coerce=True, nullable=True, description="Days per week", alias="daysPerWeek")
23
+
18
24
  week1_hours_monday: Series[Float] = pa.Field(coerce=True, description="Week 1 Hours Monday", alias="week1.hoursMonday")
19
25
  week1_hours_tuesday: Series[Float] = pa.Field(coerce=True, description="Week 1 Hours Tuesday", alias="week1.hoursTuesday")
20
26
  week1_hours_wednesday: Series[Float] = pa.Field(coerce=True, description="Week 1 Hours Wednesday", alias="week1.hoursWednesday")
@@ -42,19 +48,28 @@ class ScheduleGet(BrynQPanderaDataFrameModel):
42
48
  }
43
49
  }
44
50
 
45
-
46
51
  # ---------------------------
47
52
  # Upload Schemas
48
53
  # ---------------------------
49
- class ScheduleHours(BaseModel):
54
+ class ScheduleHoursWeek1(BaseModel):
50
55
  """Schedule hours for each day of the week"""
51
- hours_monday: Optional[float] = Field(None, description="Monday hours", alias="hoursMonday")
52
- hours_tuesday: Optional[float] = Field(None, description="Tuesday hours", alias="hoursTuesday")
53
- hours_wednesday: Optional[float] = Field(None, description="Wednesday hours", alias="hoursWednesday")
54
- hours_thursday: Optional[float] = Field(None, description="Thursday hours", alias="hoursThursday")
55
- hours_friday: Optional[float] = Field(None, description="Friday hours", alias="hoursFriday")
56
- hours_saturday: Optional[float] = Field(None, description="Saturday hours", alias="hoursSaturday")
57
- hours_sunday: Optional[float] = Field(None, description="Sunday hours", alias="hoursSunday")
56
+ week1_hours_monday: Optional[float] = Field(None, description="Monday hours", alias="hoursMonday")
57
+ week1_hours_tuesday: Optional[float] = Field(None, description="Tuesday hours", alias="hoursTuesday")
58
+ week1_hours_wednesday: Optional[float] = Field(None, description="Wednesday hours", alias="hoursWednesday")
59
+ week1_hours_thursday: Optional[float] = Field(None, description="Thursday hours", alias="hoursThursday")
60
+ week1_hours_friday: Optional[float] = Field(None, description="Friday hours", alias="hoursFriday")
61
+ week1_hours_saturday: Optional[float] = Field(None, description="Saturday hours", alias="hoursSaturday")
62
+ week1_hours_sunday: Optional[float] = Field(None, description="Sunday hours", alias="hoursSunday")
63
+
64
+ class ScheduleHoursWeek2(BaseModel):
65
+ """Schedule hours for each day of the week"""
66
+ week2_hours_monday: Optional[float] = Field(None, description="Monday hours", alias="hoursMonday")
67
+ week2_hours_tuesday: Optional[float] = Field(None, description="Tuesday hours", alias="hoursTuesday")
68
+ week2_hours_wednesday: Optional[float] = Field(None, description="Wednesday hours", alias="hoursWednesday")
69
+ week2_hours_thursday: Optional[float] = Field(None, description="Thursday hours", alias="hoursThursday")
70
+ week2_hours_friday: Optional[float] = Field(None, description="Friday hours", alias="hoursFriday")
71
+ week2_hours_saturday: Optional[float] = Field(None, description="Saturday hours", alias="hoursSaturday")
72
+ week2_hours_sunday: Optional[float] = Field(None, description="Sunday hours", alias="hoursSunday")
58
73
 
59
74
  class ScheduleCreate(BaseModel):
60
75
  """
@@ -62,8 +77,9 @@ class ScheduleCreate(BaseModel):
62
77
  """
63
78
  start_date_schedule: datetime = Field(..., description="Start date of the schedule", example="2021-01-01T09:29:18Z", alias="startDate")
64
79
  hours_per_week: Optional[float] = Field(None, description="Hours per week", example=40, alias="hoursPerWeek")
65
- week1: Optional[ScheduleHours] = Field(None, description="Week 1 schedule hours", alias="week1")
66
- week2: Optional[ScheduleHours] = Field(None, description="Week 2 schedule hours", alias="week2")
80
+ # split per week schema so we can better sync with get fields.
81
+ week1: Optional[ScheduleHoursWeek1] = Field(None, description="Week 1 schedule hours", alias="week1")
82
+ week2: Optional[ScheduleHoursWeek2] = Field(None, description="Week 2 schedule hours", alias="week2")
67
83
 
68
84
  class Config:
69
85
  json_encoders = {
@@ -93,3 +109,52 @@ class ScheduleCreate(BaseModel):
93
109
  }
94
110
  }
95
111
  }
112
+
113
+
114
+ class ScheduleUpdate(BaseModel):
115
+ """
116
+ Pydantic model for updating a schedule via SOAP API
117
+ """
118
+ employee_id: int = Field(..., example=12345, description="Employee ID", alias="employeeId")
119
+ start_date: datetime = Field(..., example="2025-01-01T00:00:00", description="Start date of the schedule", alias="startDate")
120
+ parttime_percentage: float = Field(..., ge=0, le=100, example=100.0, description="Part-time percentage", alias="parttimePercentage")
121
+ company_rooster_nr: Optional[int] = Field(0, example=0, description="Company Rooster Number (schedule template)", alias="companyRoosterNr")
122
+ hours_monday: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Monday", alias="hoursMonday")
123
+ hours_tuesday: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Tuesday", alias="hoursTuesday")
124
+ hours_wednesday: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Wednesday", alias="hoursWednesday")
125
+ hours_thursday: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Thursday", alias="hoursThursday")
126
+ hours_friday: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Friday", alias="hoursFriday")
127
+ hours_saturday: Optional[float] = Field(0, ge=0, le=24, example=0.0, description="Hours Saturday", alias="hoursSaturday")
128
+ hours_sunday: Optional[float] = Field(0, ge=0, le=24, example=0.0, description="Hours Sunday", alias="hoursSunday")
129
+ hours_monday2: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Monday Week 2", alias="hoursMonday2")
130
+ hours_tuesday2: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Tuesday Week 2", alias="hoursTuesday2")
131
+ hours_wednesday2: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Wednesday Week 2", alias="hoursWednesday2")
132
+ hours_thursday2: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Thursday Week 2", alias="hoursThursday2")
133
+ hours_friday2: Optional[float] = Field(0, ge=0, le=24, example=8.0, description="Hours Friday Week 2", alias="hoursFriday2")
134
+ hours_saturday2: Optional[float] = Field(0, ge=0, le=24, example=0.0, description="Hours Saturday Week 2", alias="hoursSaturday2")
135
+ hours_sunday2: Optional[float] = Field(0, ge=0, le=24, example=0.0, description="Hours Sunday Week 2", alias="hoursSunday2")
136
+
137
+ def to_soap_schedule(self, soap_client):
138
+ """Convert to SOAP Schedule object"""
139
+ ScheduleType = soap_client.get_type('ns0:Schedule')
140
+ return ScheduleType(
141
+ StartDate=self.start_date,
142
+ ParttimePercentage=self.parttime_percentage,
143
+ HoursMonday=self.hours_monday or 0,
144
+ HoursTuesday=self.hours_tuesday or 0,
145
+ HoursWednesday=self.hours_wednesday or 0,
146
+ HoursThursday=self.hours_thursday or 0,
147
+ HoursFriday=self.hours_friday or 0,
148
+ HoursSaturday=self.hours_saturday or 0,
149
+ HoursSunday=self.hours_sunday or 0,
150
+ HoursMonday2=self.hours_monday2 or 0,
151
+ HoursTuesday2=self.hours_tuesday2 or 0,
152
+ HoursWednesday2=self.hours_wednesday2 or 0,
153
+ HoursThursday2=self.hours_thursday2 or 0,
154
+ HoursFriday2=self.hours_friday2 or 0,
155
+ HoursSaturday2=self.hours_saturday2 or 0,
156
+ HoursSunday2=self.hours_sunday2 or 0
157
+ )
158
+
159
+ class Config:
160
+ populate_by_name = True