brynq-sdk-nmbrs 2.3.1__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 (50) hide show
  1. brynq_sdk_nmbrs/__init__.py +226 -0
  2. brynq_sdk_nmbrs/absence.py +124 -0
  3. brynq_sdk_nmbrs/address.py +66 -0
  4. brynq_sdk_nmbrs/bank.py +125 -0
  5. brynq_sdk_nmbrs/children.py +100 -0
  6. brynq_sdk_nmbrs/companies.py +93 -0
  7. brynq_sdk_nmbrs/contract.py +132 -0
  8. brynq_sdk_nmbrs/costcenter.py +166 -0
  9. brynq_sdk_nmbrs/costunit.py +90 -0
  10. brynq_sdk_nmbrs/days.py +137 -0
  11. brynq_sdk_nmbrs/debtors.py +25 -0
  12. brynq_sdk_nmbrs/department.py +122 -0
  13. brynq_sdk_nmbrs/document.py +30 -0
  14. brynq_sdk_nmbrs/employees.py +196 -0
  15. brynq_sdk_nmbrs/employment.py +107 -0
  16. brynq_sdk_nmbrs/function.py +89 -0
  17. brynq_sdk_nmbrs/hours.py +252 -0
  18. brynq_sdk_nmbrs/leave.py +139 -0
  19. brynq_sdk_nmbrs/manager.py +294 -0
  20. brynq_sdk_nmbrs/salaries.py +85 -0
  21. brynq_sdk_nmbrs/salary_tables.py +242 -0
  22. brynq_sdk_nmbrs/schedules.py +84 -0
  23. brynq_sdk_nmbrs/schemas/__init__.py +37 -0
  24. brynq_sdk_nmbrs/schemas/absence.py +61 -0
  25. brynq_sdk_nmbrs/schemas/address.py +76 -0
  26. brynq_sdk_nmbrs/schemas/bank.py +83 -0
  27. brynq_sdk_nmbrs/schemas/contracts.py +60 -0
  28. brynq_sdk_nmbrs/schemas/costcenter.py +91 -0
  29. brynq_sdk_nmbrs/schemas/costunit.py +40 -0
  30. brynq_sdk_nmbrs/schemas/days.py +98 -0
  31. brynq_sdk_nmbrs/schemas/debtor.py +16 -0
  32. brynq_sdk_nmbrs/schemas/department.py +57 -0
  33. brynq_sdk_nmbrs/schemas/employees.py +153 -0
  34. brynq_sdk_nmbrs/schemas/employment.py +48 -0
  35. brynq_sdk_nmbrs/schemas/function.py +50 -0
  36. brynq_sdk_nmbrs/schemas/hours.py +121 -0
  37. brynq_sdk_nmbrs/schemas/leave.py +53 -0
  38. brynq_sdk_nmbrs/schemas/manager.py +126 -0
  39. brynq_sdk_nmbrs/schemas/salary.py +92 -0
  40. brynq_sdk_nmbrs/schemas/schedules.py +96 -0
  41. brynq_sdk_nmbrs/schemas/social_insurance.py +40 -0
  42. brynq_sdk_nmbrs/schemas/wage_tax.py +98 -0
  43. brynq_sdk_nmbrs/schemas/wagecomponents.py +114 -0
  44. brynq_sdk_nmbrs/social_insurance.py +52 -0
  45. brynq_sdk_nmbrs/wage_tax.py +164 -0
  46. brynq_sdk_nmbrs/wagecomponents.py +268 -0
  47. brynq_sdk_nmbrs-2.3.1.dist-info/METADATA +21 -0
  48. brynq_sdk_nmbrs-2.3.1.dist-info/RECORD +50 -0
  49. brynq_sdk_nmbrs-2.3.1.dist-info/WHEEL +5 -0
  50. brynq_sdk_nmbrs-2.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,114 @@
1
+ import math
2
+ import pandas as pd
3
+ import pandera as pa
4
+ from pandera.typing import Series, String, Float
5
+ from brynq_sdk_functions import BrynQPanderaDataFrameModel
6
+
7
+ from typing import Optional
8
+ from pydantic import BaseModel, Field, conint, confloat
9
+
10
+
11
+ # ---------------------------
12
+ # Get Schemas
13
+ # ---------------------------
14
+ class FixedWageComponentGet(BrynQPanderaDataFrameModel):
15
+ employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
16
+ fixed_wage_component_id: Series[String] = pa.Field(coerce=True, description="Fixed Wage Component ID", alias="fixedWageComponentId")
17
+ code: Series[String] = pa.Field(coerce=True, description="Wage Component Code", alias="code")
18
+ value: Series[Float] = pa.Field(coerce=True, description="Wage Component Value", alias="value")
19
+ end_year: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True, description="End Year", alias="endYear")
20
+ end_period: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True, description="End Period", alias="endPeriod")
21
+ comment: Series[String] = pa.Field(nullable=True, coerce=True, description="Comment", alias="comment")
22
+ cost_center_id: Series[String] = pa.Field(nullable=True, coerce=True, description="Cost Center ID", alias="costCenterId")
23
+ cost_unit_id: Series[String] = pa.Field(nullable=True, coerce=True, description="Cost Unit ID", alias="costUnitId")
24
+
25
+ class Config:
26
+ coerce = True
27
+
28
+ class _Annotation:
29
+ primary_key = "fixed_wage_component_id"
30
+ foreign_keys = {
31
+ "employee_id": {
32
+ "parent_schema": "EmployeeSchema",
33
+ "parent_column": "employee_id",
34
+ "cardinality": "N:1"
35
+ }
36
+ }
37
+
38
+ class VariableWageComponentGet(BrynQPanderaDataFrameModel):
39
+ employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
40
+ variable_wage_component_id: Series[String] = pa.Field(coerce=True, description="Variable Wage Component ID", alias="variableWageComponentId")
41
+ code: Series[String] = pa.Field(coerce=True, description="Wage Component Code", alias="code")
42
+ value: Series[Float] = pa.Field(coerce=True, description="Wage Component Value", alias="value")
43
+ comment: Series[String] = pa.Field(nullable=True, coerce=True, description="Comment", alias="comment")
44
+ cost_center_id: Series[String] = pa.Field(nullable=True, coerce=True, description="Cost Center ID", alias="costCenterId")
45
+ cost_unit_id: Series[String] = pa.Field(nullable=True, coerce=True, description="Cost Unit ID", alias="costUnitId")
46
+
47
+ class Config:
48
+ coerce = True
49
+
50
+ class _Annotation:
51
+ primary_key = "variable_wage_component_id"
52
+ foreign_keys = {
53
+ "employee_id": {
54
+ "parent_schema": "EmployeeSchema",
55
+ "parent_column": "employee_id",
56
+ "cardinality": "N:1"
57
+ }
58
+ }
59
+
60
+ # ---------------------------
61
+ # Upload Schemas
62
+ # ---------------------------
63
+ class PeriodPost(BaseModel):
64
+ year: int = Field(..., ge=1900, le=2100, example=2021, description="Year", alias="year")
65
+ period: int = Field(..., ge=1, le=53, example=4, description="Period", alias="period")
66
+
67
+ class FixedWageComponentCreate(BaseModel):
68
+ employee_id: str = Field(..., description="Employee identifier", alias="employeeId")
69
+ code: int = Field(..., ge=1, example=1100, description="Wage Component Code", alias="code")
70
+ value: float = Field(..., ge=0, example=500, description="Wage Component Value", alias="value")
71
+ end_year: Optional[int] = Field(None, ge=1900, le=2100, example=2023, description="End Year", alias="endYear")
72
+ end_period: Optional[int] = Field(None, ge=1, le=53, example=6, description="End Period", alias="endPeriod")
73
+ comment: Optional[str] = Field(None, example="some comment", description="Comment", alias="comment")
74
+ cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
75
+ cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
76
+ period_details: PeriodPost
77
+ unprotected_mode: Optional[bool] = Field(None, example=True, description="Unprotected Mode", alias="unprotectedMode")
78
+
79
+ class FixedWageComponentUpdate(BaseModel):
80
+ employee_id: str = Field(..., description="Employee identifier", alias="employeeId")
81
+ fixed_wage_component_id: str = Field(..., example="643c6b90-57c6-4199-9e4e-ded553572d78", description="Fixed Wage Component ID", alias="fixedWageComponentId")
82
+ code: Optional[int] = Field(None, ge=1, example=1100, description="Wage Component Code", alias="code")
83
+ value: Optional[float] = Field(None, ge=0, example=500, description="Wage Component Value", alias="value")
84
+ end_year: Optional[int] = Field(None, ge=1900, le=2100, example=2023, description="End Year", alias="endYear")
85
+ end_period: Optional[int] = Field(None, ge=1, le=53, example=6, description="End Period", alias="endPeriod")
86
+ comment: Optional[str] = Field(None, example="string", description="Comment", alias="comment")
87
+ cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
88
+ cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
89
+ period_details: Optional[PeriodPost] = None
90
+ unprotected_mode: Optional[bool] = Field(None, example=True, description="Unprotected Mode", alias="unprotectedMode")
91
+
92
+ class VariableWageComponentCreate(BaseModel):
93
+ employee_id: str = Field(..., description="Employee identifier", alias="employeeId")
94
+ code: int = Field(..., ge=1, example=3045, description="Wage Component Code", alias="code")
95
+ value: float = Field(..., ge=0, example=200, description="Wage Component Value", alias="value")
96
+ comment: Optional[str] = Field(None, example="comment", description="Comment", alias="comment")
97
+ cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
98
+ cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
99
+ period_details: PeriodPost
100
+ unprotected_mode: Optional[bool] = Field(None, example=True, description="Unprotected Mode", alias="unprotectedMode")
101
+
102
+ class VariableWageComponentUpdate(BaseModel):
103
+ employee_id: str = Field(..., description="Employee identifier", alias="employeeId")
104
+ variable_wage_component_id: str = Field(..., example="7fc59095-daed-4746-a7f8-a454e38e3683", description="Variable Wage Component ID", alias="variableWageComponentId")
105
+ code: Optional[int] = Field(None, ge=1, example=3045, description="Wage Component Code", alias="code")
106
+ value: Optional[float] = Field(None, ge=0, example=2200, description="Wage Component Value", alias="value")
107
+ comment: Optional[str] = Field(None, example="comment", description="Comment", alias="comment")
108
+ cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
109
+ cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
110
+ period_details: Optional[PeriodPost] = None
111
+ unprotected_mode: Optional[bool] = Field(None, example=True, description="Unprotected Mode", alias="unprotectedMode")
112
+
113
+ class WageComponentDelete(BaseModel):
114
+ wage_component_id: str = Field(..., example="7fc59095-daed-4746-a7f8-a454e38e3683", description="Wage Component ID", alias="wageComponentId")
@@ -0,0 +1,52 @@
1
+ from typing import Any, Dict, List, Union, Tuple
2
+ import pandas as pd
3
+
4
+ from .schemas.social_insurance import SocialInsuranceUpdate
5
+ from zeep.exceptions import Fault
6
+ from zeep.helpers import serialize_object
7
+ # import logging
8
+ from brynq_sdk_functions import Functions
9
+
10
+
11
+ class SocialInsurance:
12
+ def __init__(self, nmbrs):
13
+ self.nmbrs = nmbrs
14
+ self.soap_client_employees = nmbrs.soap_client_employees
15
+
16
+ def update(self, data: Dict[str, Any]) -> pd.DataFrame:
17
+ try:
18
+ social_insurance_model = SocialInsuranceUpdate(**data)
19
+
20
+ if self.nmbrs.mock_mode:
21
+ return social_insurance_model
22
+
23
+ # Use the model's built-in SOAP conversion method
24
+ social_insurance_settings = social_insurance_model.to_soap_settings(self.nmbrs.soap_client_employees)
25
+
26
+ # Make SOAP request with clean, simple call
27
+ response = self.nmbrs.soap_client_employees.service.SVW_UpdateCurrent(
28
+ EmployeeId=social_insurance_model.employee_id,
29
+ SVWSettings=social_insurance_settings,
30
+ _soapheaders=[self.nmbrs.soap_auth_header]
31
+ )
32
+
33
+ # Convert response to DataFrame
34
+ if response:
35
+ # Convert Zeep objects to Python dictionaries
36
+ serialized_response = serialize_object(response)
37
+
38
+ # Convert to list if it's not already
39
+ if not isinstance(serialized_response, list):
40
+ serialized_response = [serialized_response]
41
+
42
+ # Convert to DataFrame
43
+ df = pd.DataFrame(serialized_response)
44
+
45
+ return df
46
+ else:
47
+ return pd.DataFrame()
48
+
49
+ except Fault as e:
50
+ raise Exception(f"SOAP request failed: {str(e)}")
51
+ except Exception as e:
52
+ raise Exception(f"Failed to update Social Insurance: {str(e)}")
@@ -0,0 +1,164 @@
1
+ from typing import Any, Dict, List, Union, Tuple
2
+ import pandas as pd
3
+ from .schemas.wage_tax import WageTaxGet, WageTaxUpdate, WageTaxSettingsGet
4
+ from zeep.exceptions import Fault
5
+ from zeep.ns import WSDL, SOAP_ENV_11
6
+ from zeep.xsd import ComplexType, Element, String
7
+ from zeep.helpers import serialize_object
8
+ # import logging
9
+ from brynq_sdk_functions import Functions
10
+
11
+
12
+ class WageTax:
13
+ def __init__(self, nmbrs):
14
+ self.nmbrs = nmbrs
15
+ self.soap_client_companies = nmbrs.soap_client_companies
16
+ self.soap_client_employees = nmbrs.soap_client_employees
17
+
18
+ def get_settings(self, year: int) -> Tuple[pd.DataFrame, pd.DataFrame]:
19
+ """
20
+ Get salary tables for all companies for a specific period and year.
21
+
22
+ Args:
23
+ period (int): The period number
24
+ year (int): The year
25
+
26
+ Returns:
27
+ pd.DataFrame: DataFrame containing the salary tables
28
+ """
29
+ wagetax_settings = pd.DataFrame()
30
+ for company in self.nmbrs.soap_company_ids.to_dict(orient='records'):
31
+ wagetax_settings_temp = self._get(company['i_d'], year)
32
+ if not wagetax_settings_temp.empty:
33
+ wagetax_settings_temp['companyId'] = company['number']
34
+ wagetax_settings = pd.concat([wagetax_settings, wagetax_settings_temp])
35
+
36
+ valid_wagetax_settings, invalid_wagetax_settings = Functions.validate_data(df=wagetax_settings, schema=WageTaxSettingsGet, debug=True)
37
+
38
+ # No validation schema for now, but could be added later
39
+ return valid_wagetax_settings, invalid_wagetax_settings
40
+
41
+ def _get(self, company_id: int, year: int) -> pd.DataFrame:
42
+ """
43
+ Get salary tables for a specific company, period and year.
44
+
45
+ Args:
46
+ company_id (int): The ID of the company
47
+ period (int): The period number
48
+ year (int): The year
49
+
50
+ Returns:
51
+ pd.DataFrame: DataFrame containing the salary tables
52
+ """
53
+ try:
54
+ # Make SOAP request with the proper header structure
55
+ response = self.soap_client_companies.service.WageTax_GetList(
56
+ CompanyId=company_id,
57
+ intYear=year,
58
+ _soapheaders=[self.nmbrs.soap_auth_header]
59
+ )
60
+
61
+ # Convert response to DataFrame
62
+ if response:
63
+ # Convert Zeep objects to Python dictionaries
64
+ serialized_response = serialize_object(response)
65
+
66
+ # Convert to list if it's not already
67
+ if not isinstance(serialized_response, list):
68
+ serialized_response = [serialized_response]
69
+
70
+ # Convert to DataFrame
71
+ df = pd.DataFrame(serialized_response)
72
+
73
+ return df
74
+ else:
75
+ return pd.DataFrame()
76
+
77
+ except Fault as e:
78
+ raise Exception(f"SOAP request failed: {str(e)}")
79
+ except Exception as e:
80
+ raise Exception(f"Failed to get salary tables: {str(e)}")
81
+
82
+ def get(self, employee_id: int) -> Tuple[pd.DataFrame, pd.DataFrame]:
83
+ """
84
+ Get wage tax settings for a specific employee.
85
+
86
+ Args:
87
+ employee_id (int): The ID of the employee
88
+
89
+ Returns:
90
+ pd.DataFrame: DataFrame containing the wage tax settings
91
+ """
92
+ try:
93
+ # Get the auth header using the centralized method
94
+ auth_header = self.nmbrs._get_soap_auth_header_employees()
95
+
96
+ # Make SOAP request with the proper header structure
97
+ response = self.soap_client_employees.service.WageTax_GetCurrent(
98
+ EmployeeId=employee_id,
99
+ _soapheaders=[auth_header]
100
+ )
101
+
102
+ # Convert response to DataFrame
103
+ if response:
104
+ # Convert Zeep objects to Python dictionaries
105
+ serialized_response = serialize_object(response)
106
+
107
+ # Convert to list if it's not already
108
+ if not isinstance(serialized_response, list):
109
+ serialized_response = [serialized_response]
110
+
111
+ # Convert to DataFrame
112
+ df = pd.DataFrame(serialized_response)
113
+ df['EmployeeId'] = employee_id
114
+ valid_wage_tax, invalid_wage_tax = Functions.validate_data(df=df, schema=WageTaxGet, debug=True)
115
+
116
+ return valid_wage_tax, invalid_wage_tax
117
+ else:
118
+ return pd.DataFrame(), 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 wage tax settings: {str(e)}")
124
+
125
+ def update(self, data: Dict[str, Any]) -> pd.DataFrame:
126
+ try:
127
+ wage_tax_model = WageTaxUpdate(**data)
128
+
129
+ if self.nmbrs.mock_mode:
130
+ return wage_tax_model
131
+
132
+ # Get the auth header using the centralized method
133
+ auth_header = self.nmbrs._get_soap_auth_header_employees()
134
+
135
+ # Use the model's built-in SOAP conversion method
136
+ wage_tax_settings = wage_tax_model.to_soap_settings(self.nmbrs.soap_client_employees)
137
+
138
+ # Make SOAP request with clean, simple call
139
+ response = self.nmbrs.soap_client_employees.service.WageTax_UpdateCurrent(
140
+ EmployeeId=wage_tax_model.employee_id,
141
+ LoonheffingSettings=wage_tax_settings,
142
+ _soapheaders=[self.nmbrs.soap_auth_header]
143
+ )
144
+
145
+ # Convert response to DataFrame
146
+ if response:
147
+ # Convert Zeep objects to Python dictionaries
148
+ serialized_response = serialize_object(response)
149
+
150
+ # Convert to list if it's not already
151
+ if not isinstance(serialized_response, list):
152
+ serialized_response = [serialized_response]
153
+
154
+ # Convert to DataFrame
155
+ df = pd.DataFrame(serialized_response)
156
+
157
+ return df
158
+ else:
159
+ return pd.DataFrame()
160
+
161
+ except Fault as e:
162
+ raise Exception(f"SOAP request failed: {str(e)}")
163
+ except Exception as e:
164
+ raise Exception(f"Failed to update WageTax: {str(e)}")
@@ -0,0 +1,268 @@
1
+ import math
2
+ import pandas as pd
3
+ import requests
4
+ from typing import Dict, Any
5
+ from .schemas.wagecomponents import (
6
+ FixedWageComponentGet,
7
+ VariableWageComponentGet,
8
+ FixedWageComponentCreate,
9
+ FixedWageComponentUpdate,
10
+ VariableWageComponentCreate,
11
+ VariableWageComponentUpdate,
12
+ WageComponentDelete
13
+ )
14
+ from brynq_sdk_functions import Functions
15
+
16
+
17
+ class EmployeeFixedWageComponents:
18
+ def __init__(self, nmbrs):
19
+ self.nmbrs = nmbrs
20
+
21
+ def get(self,
22
+ created_from: str = None,
23
+ employee_id: str = None,
24
+ period: int = None,
25
+ year: int = None) -> tuple[pd.DataFrame, pd.DataFrame]:
26
+ wagecomponents = pd.DataFrame()
27
+ for company in self.nmbrs.company_ids:
28
+ wagecomponents = pd.concat([wagecomponents, self._get(company, created_from, employee_id, period, year)])
29
+
30
+ valid_wagecomponents, invalid_wagecomponents = Functions.validate_data(df=wagecomponents, schema=FixedWageComponentGet, debug=True)
31
+
32
+ return valid_wagecomponents, invalid_wagecomponents
33
+
34
+ def _get(self,
35
+ company_id: str,
36
+ created_from: str = None,
37
+ employee_id: str = None,
38
+ period: int = None,
39
+ year: int = None) -> pd.DataFrame:
40
+ params = {}
41
+ if created_from:
42
+ params['createdFrom'] = created_from
43
+ if employee_id:
44
+ params['employeeId'] = employee_id
45
+ if year:
46
+ params['year'] = year
47
+ if period:
48
+ params['period'] = period
49
+ request = requests.Request(method='GET',
50
+ url=f"{self.nmbrs.base_url}companies/{company_id}/employees/fixedwagecomponents",
51
+ params=params)
52
+
53
+ data = self.nmbrs.get_paginated_result(request)
54
+ df = pd.json_normalize(
55
+ data,
56
+ record_path='fixedWageComponents',
57
+ meta=['employeeId']
58
+ )
59
+
60
+ return df
61
+
62
+ def create(self, employee_id: str, data: Dict[str, Any]):
63
+ """
64
+ Create a new fixed wage component for an employee using Pydantic validation.
65
+
66
+ Args:
67
+ employee_id: The ID of the employee
68
+ data: Dictionary containing fixed wage component data with fields matching
69
+ the FixedWageComponentCreate schema (using camelCase field names)
70
+
71
+ Returns:
72
+ Response from the API
73
+ """
74
+ # Validate with Pydantic model - this will raise an error if required fields are missing
75
+ wage_component_model = FixedWageComponentCreate(**data)
76
+
77
+ if self.nmbrs.mock_mode:
78
+ return wage_component_model
79
+
80
+ # Convert validated model to dict for API payload
81
+ payload = wage_component_model.dict(exclude_none=True)
82
+
83
+ # Send request
84
+ resp = self.nmbrs.session.post(
85
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/fixedwagecomponent",
86
+ json=payload,
87
+ timeout=self.nmbrs.timeout
88
+ )
89
+ return resp
90
+
91
+ def update(self, employee_id: str, data: Dict[str, Any]):
92
+ """
93
+ Update an existing fixed wage component for an employee using Pydantic validation.
94
+
95
+ Args:
96
+ employee_id: The ID of the employee
97
+ data: Dictionary containing fixed wage component data with fields matching
98
+ the FixedWageComponentUpdate schema (using camelCase field names)
99
+
100
+ Returns:
101
+ Response from the API
102
+ """
103
+ # Validate with Pydantic model - this will raise an error if required fields are missing
104
+ wage_component_model = FixedWageComponentUpdate(**data)
105
+
106
+ if self.nmbrs.mock_mode:
107
+ return wage_component_model
108
+
109
+ # Convert validated model to dict for API payload
110
+ payload = wage_component_model.dict(exclude_none=True)
111
+
112
+ # Send request
113
+ resp = self.nmbrs.session.put(
114
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/fixedwagecomponent",
115
+ json=payload,
116
+ timeout=self.nmbrs.timeout
117
+ )
118
+ return resp
119
+
120
+ def delete(self, employee_id: str, wagecomponent_id: str):
121
+ """
122
+ Delete a wage component for an employee.
123
+
124
+ Args:
125
+ employee_id: The ID of the employee
126
+ wagecomponent_id: The ID of the wage component to delete
127
+
128
+ Returns:
129
+ Response from the API
130
+ """
131
+ # Validate with Pydantic model
132
+ delete_model = WageComponentDelete(wagecomponentId=wagecomponent_id)
133
+
134
+ if self.nmbrs.mock_mode:
135
+ return delete_model
136
+
137
+ resp = self.nmbrs.session.delete(
138
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/wagecomponents/{wagecomponent_id}",
139
+ timeout=self.nmbrs.timeout
140
+ )
141
+ return resp
142
+
143
+
144
+ class EmployeeVariableWageComponents:
145
+ def __init__(self, nmbrs):
146
+ self.nmbrs = nmbrs
147
+
148
+ def get(self,
149
+ created_from: str = None,
150
+ employee_id: str = None,
151
+ period: int = None,
152
+ year: int = None) -> tuple[pd.DataFrame, pd.DataFrame]:
153
+ wagecomponents = pd.DataFrame()
154
+ for company in self.nmbrs.company_ids:
155
+ wagecomponents = pd.concat([wagecomponents, self._get(company, created_from, employee_id, period, year)])
156
+
157
+ valid_wagecomponents, invalid_wagecomponents = Functions.validate_data(df=wagecomponents, schema=VariableWageComponentGet, debug=True)
158
+
159
+ return valid_wagecomponents, invalid_wagecomponents
160
+
161
+ def _get(self,
162
+ company_id: str,
163
+ created_from: str = None,
164
+ employee_id: str = None,
165
+ period: int = None,
166
+ year: int = None) -> pd.DataFrame:
167
+ params = {}
168
+ if created_from:
169
+ params['createdFrom'] = created_from
170
+ if employee_id:
171
+ params['employeeId'] = employee_id
172
+ if year:
173
+ params['year'] = year
174
+ if period:
175
+ params['period'] = period
176
+ request = requests.Request(method='GET',
177
+ url=f"{self.nmbrs.base_url}companies/{company_id}/employees/variablewagecomponents",
178
+ params=params)
179
+
180
+ data = self.nmbrs.get_paginated_result(request)
181
+ df = pd.json_normalize(
182
+ data,
183
+ record_path='variablewagecomponents',
184
+ meta=['employeeId']
185
+ )
186
+
187
+ return df
188
+
189
+ def create(self, employee_id: str, data: Dict[str, Any]):
190
+ """
191
+ Create a new variable wage component for an employee using Pydantic validation.
192
+
193
+ Args:
194
+ employee_id: The ID of the employee
195
+ data: Dictionary containing variable wage component data with fields matching
196
+ the VariableWageComponentCreate schema (using camelCase field names)
197
+
198
+ Returns:
199
+ Response from the API
200
+ """
201
+ # Validate with Pydantic model - this will raise an error if required fields are missing
202
+ wage_component_model = VariableWageComponentCreate(**data)
203
+
204
+ if self.nmbrs.mock_mode:
205
+ return wage_component_model
206
+
207
+ # Convert validated model to dict for API payload
208
+ payload = wage_component_model.dict(exclude_none=True)
209
+
210
+ # Send request
211
+ resp = self.nmbrs.session.post(
212
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/variablewagecomponent",
213
+ json=payload,
214
+ timeout=self.nmbrs.timeout
215
+ )
216
+ return resp
217
+
218
+ def update(self, employee_id: str, data: Dict[str, Any]):
219
+ """
220
+ Update an existing variable wage component for an employee using Pydantic validation.
221
+
222
+ Args:
223
+ employee_id: The ID of the employee
224
+ data: Dictionary containing variable wage component data with fields matching
225
+ the VariableWageComponentUpdate schema (using camelCase field names)
226
+
227
+ Returns:
228
+ Response from the API
229
+ """
230
+ # Validate with Pydantic model - this will raise an error if required fields are missing
231
+ wage_component_model = VariableWageComponentUpdate(**data)
232
+
233
+ if self.nmbrs.mock_mode:
234
+ return wage_component_model
235
+
236
+ # Convert validated model to dict for API payload
237
+ payload = wage_component_model.dict(exclude_none=True)
238
+
239
+ # Send request
240
+ resp = self.nmbrs.session.put(
241
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/variablewagecomponent",
242
+ json=payload,
243
+ timeout=self.nmbrs.timeout
244
+ )
245
+ return resp
246
+
247
+ def delete(self, employee_id: str, wagecomponent_id: str):
248
+ """
249
+ Delete a wage component for an employee.
250
+
251
+ Args:
252
+ employee_id: The ID of the employee
253
+ wagecomponent_id: The ID of the wage component to delete
254
+
255
+ Returns:
256
+ Response from the API
257
+ """
258
+ # Validate with Pydantic model
259
+ delete_model = WageComponentDelete(wagecomponentId=wagecomponent_id)
260
+
261
+ if self.nmbrs.mock_mode:
262
+ return delete_model
263
+
264
+ resp = self.nmbrs.session.delete(
265
+ url=f"{self.nmbrs.base_url}employees/{employee_id}/wagecomponents/{wagecomponent_id}",
266
+ timeout=self.nmbrs.timeout
267
+ )
268
+ return resp
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: brynq_sdk_nmbrs
3
+ Version: 2.3.1
4
+ Summary: Nmbrs wrapper from BrynQ
5
+ Author: BrynQ
6
+ Author-email: support@brynq.com
7
+ License: BrynQ License
8
+ Requires-Dist: brynq-sdk-brynq<5,>=4
9
+ Requires-Dist: pandas<3.0.0,>=2.2.0
10
+ Requires-Dist: pydantic<3.0.0,>=2.5.0
11
+ Requires-Dist: pandera<1.0.0,>=0.16.0
12
+ Requires-Dist: zeep<5.0.0,>=4.0.0
13
+ Requires-Dist: brynq-sdk-functions>=2.0.5
14
+ Dynamic: author
15
+ Dynamic: author-email
16
+ Dynamic: description
17
+ Dynamic: license
18
+ Dynamic: requires-dist
19
+ Dynamic: summary
20
+
21
+ Nmbrs wrapper from BrynQ
@@ -0,0 +1,50 @@
1
+ brynq_sdk_nmbrs/__init__.py,sha256=O6SxqypTQcyz-CpgFu23RZWR5SekYirLn8iEydp9bAE,9836
2
+ brynq_sdk_nmbrs/absence.py,sha256=xNzoDgw0Jj-JYS68lpwNzbrDy3BKsR-Iz-anlJW7YS0,4658
3
+ brynq_sdk_nmbrs/address.py,sha256=FTzzs04tyNZNoysQ18lei137JXhvZUjYH8vS3Z5Nnk4,2325
4
+ brynq_sdk_nmbrs/bank.py,sha256=2zZ9pCEdPkxvjqyyo0bfZw0JpJpzDBXZVNmO3lHjHGQ,4165
5
+ brynq_sdk_nmbrs/children.py,sha256=hwgh3RKTs6oK6ztZ-TADXXK4Ekq_svo81D9aytCzkE4,3479
6
+ brynq_sdk_nmbrs/companies.py,sha256=fqG9JERkaatkfzLFO3vC37rHr_W6pZ2DOeWS68b-TGc,3426
7
+ brynq_sdk_nmbrs/contract.py,sha256=lccpAbhNjj4x45WOnF7h2LlBG5XCXelnis7WyPvJJrE,4527
8
+ brynq_sdk_nmbrs/costcenter.py,sha256=gz7e486E9NqFXkqt50CctQ8DERC3pI2SPWsQWpIkHos,6087
9
+ brynq_sdk_nmbrs/costunit.py,sha256=GGLwdCOYGU96Zr-zyG86rggzdsccH0IojfrPn5GanTI,3092
10
+ brynq_sdk_nmbrs/days.py,sha256=T2ZdMCN1iE6WseX40Hn8-iNRCz47kWUuwNVRwQJX7TY,4534
11
+ brynq_sdk_nmbrs/debtors.py,sha256=yig5Fdv9deyeoVwlGu8n5wwKdx7gZBDKtJLitQDAX98,785
12
+ brynq_sdk_nmbrs/department.py,sha256=9Yk80w9lME5cN9FjtQ8_w0RDjqVwny54xDkI2FWmJ8M,4216
13
+ brynq_sdk_nmbrs/document.py,sha256=Eb2mpsQRI1t5u0szUsc8YYI8tC_8ARW7wZUZRFA8RzQ,839
14
+ brynq_sdk_nmbrs/employees.py,sha256=9ypKi7dFh5rqXL9mFJ2ltHci40idsk_sbuZjYfT_b_w,7517
15
+ brynq_sdk_nmbrs/employment.py,sha256=P5EQhSAA6LOfeF2VeaAI8NZFTwSpD81ggJ9kUQp-WpE,3789
16
+ brynq_sdk_nmbrs/function.py,sha256=bsafEcLglr2iT3jehbmGAme6-xI2ieOvaAueeVVT64I,3066
17
+ brynq_sdk_nmbrs/hours.py,sha256=3pbl6IA8BBC6wrfTvkJha1LJi4SjrZCBINUANhuDvDQ,8140
18
+ brynq_sdk_nmbrs/leave.py,sha256=Z17nPlRTVy_pqyInrbJNnF_83c8__Z207nh1aWVycCg,4515
19
+ brynq_sdk_nmbrs/manager.py,sha256=wE2UJYPsKUN5jv3HmLpVAERcxcChwoMLfFgja8vxM6U,9441
20
+ brynq_sdk_nmbrs/salaries.py,sha256=0VZgPwIl4YSeil7IUliPg8XFWloIQX2rBoWurSOLa9g,2910
21
+ brynq_sdk_nmbrs/salary_tables.py,sha256=T7_bJE-CtRF08FWyLJnXm5Q3Q5Usozx4iCByqoTPOwo,9390
22
+ brynq_sdk_nmbrs/schedules.py,sha256=jvi3aRdsgU_lalVWAcnfsfg8-g7J4m8pVW42HP-aExA,2928
23
+ brynq_sdk_nmbrs/social_insurance.py,sha256=7sfgZeGDXGrvcsj8tTj7JaFK2fSOzVdtEjJ0wKRr2BI,1899
24
+ brynq_sdk_nmbrs/wage_tax.py,sha256=NgqzEwzc_n28LtWhOM4EfHUo-vy-QwZA6wiL2n8LcdM,6241
25
+ brynq_sdk_nmbrs/wagecomponents.py,sha256=nkk7HGlcdEx2K3VQ2G2zwLbNMFPNHQK4YwOEnFBtDj0,9539
26
+ brynq_sdk_nmbrs/schemas/__init__.py,sha256=rwMb9AJSBXn_50SOa1rIvwOsCrtpj3vQxjXioxrxuyI,2301
27
+ brynq_sdk_nmbrs/schemas/absence.py,sha256=f2ZDpy0qnyUyCjk2JjTNCazB_12lmP462MkI4eyf3ws,3320
28
+ brynq_sdk_nmbrs/schemas/address.py,sha256=jgckNL5GhI23U0CS6VSs8vdl0pd4Goue57pIB8Mg6RU,4151
29
+ brynq_sdk_nmbrs/schemas/bank.py,sha256=h5OtnD9DfJXfNeAoD9gm1434qXchbS6RPLKNRpxQ2RU,4537
30
+ brynq_sdk_nmbrs/schemas/contracts.py,sha256=wb_NJyzKg7pjtWVelKj1_rfI57cs8jQ2QnMVu-aS_jI,3848
31
+ brynq_sdk_nmbrs/schemas/costcenter.py,sha256=vB-cJCaLn57Gc7oAK2PTJvIpnz7zZkEX5ry0iJ_ZaX8,5328
32
+ brynq_sdk_nmbrs/schemas/costunit.py,sha256=dH1nD9yuT94YamBz8phhuZ7E7AEuU4DmL6dXQYI8lu0,1854
33
+ brynq_sdk_nmbrs/schemas/days.py,sha256=GJC9-ELfWCFVA7Z1vq4UANq8omFnPdeiowhjUu7-7a0,6952
34
+ brynq_sdk_nmbrs/schemas/debtor.py,sha256=07FUL9MNKMfGKUifqeWSgQBTq1pVG5NB0OkaFgB9v88,721
35
+ brynq_sdk_nmbrs/schemas/department.py,sha256=Rq88XAOIMNzojpHhb3awdV1ALlmkI_ZjMbiwXKSz6lY,3106
36
+ brynq_sdk_nmbrs/schemas/employees.py,sha256=QblOZk4BCbXGoDW-kdAAykl-aLXzsBmfjewG_vU0qwg,11077
37
+ brynq_sdk_nmbrs/schemas/employment.py,sha256=4KY0WEdZfJ-4x_loaZhBLoPYwQcA3k3TYuWRZj4PDxI,2678
38
+ brynq_sdk_nmbrs/schemas/function.py,sha256=c0LkdAvyLr1_b9e7h7-2KD4v-YHtCKrm3x54Lc6V_sk,2503
39
+ brynq_sdk_nmbrs/schemas/hours.py,sha256=CJOD3-NL80bh3nqIZ6DwoKQ0MGfe5ba84IXv0HsA_pA,8201
40
+ brynq_sdk_nmbrs/schemas/leave.py,sha256=pofHHdYuPTS4hdFQbYQf2NbbYb4QlD3jI5CPGXSpNq8,3313
41
+ brynq_sdk_nmbrs/schemas/manager.py,sha256=NT8Uh1yfoZQ9ODCZBkVUJ50QwQ5GtHNG6_Rx_EO674U,9310
42
+ brynq_sdk_nmbrs/schemas/salary.py,sha256=pq-4hOB7QIo0BG8_Q_JnDbovPAhXOytfCpSuU_kXrGw,5511
43
+ brynq_sdk_nmbrs/schemas/schedules.py,sha256=ITB4GAVNXyQLh0Ji_kJpdp1ZNGtYRlOTNHRvNiLg8wg,5481
44
+ brynq_sdk_nmbrs/schemas/social_insurance.py,sha256=xJX7Duzcnu4vrKWIAan1iCUhNDtRflOLrIg7YBbtu70,2470
45
+ brynq_sdk_nmbrs/schemas/wage_tax.py,sha256=ohbsc0zIHcZ-oj4vctxFA6XWIs0Wv9Uu4jUy7EP61ZU,7342
46
+ brynq_sdk_nmbrs/schemas/wagecomponents.py,sha256=_t00-b22JEgihDdMMjwVw1mzCEnna55RnEZDYFwDZ1c,7769
47
+ brynq_sdk_nmbrs-2.3.1.dist-info/METADATA,sha256=Q0gZCGo8Lx9WF_6Difmfx_ClIhndS-fEgNELrzp0hUo,529
48
+ brynq_sdk_nmbrs-2.3.1.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
49
+ brynq_sdk_nmbrs-2.3.1.dist-info/top_level.txt,sha256=LrSQFzIV7FP02jBHdBKubiCbIy0C_5YnTz3DSYYoQzg,16
50
+ brynq_sdk_nmbrs-2.3.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+