brynq-sdk-nmbrs 2.3.0__tar.gz → 2.3.1.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brynq_sdk_nmbrs-2.3.1.dev0/PKG-INFO +21 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/__init__.py +36 -22
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/costcenter.py +53 -8
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/costunit.py +16 -4
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/department.py +36 -27
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/employee_wage_tax_settings.py +106 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/employees.py +119 -24
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/employment.py +12 -4
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/salaries.py +5 -2
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/address.py +4 -4
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/contracts.py +25 -9
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/costcenter.py +57 -16
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/department.py +8 -5
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/employees.py +42 -36
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/employment.py +10 -8
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/function.py +6 -6
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/salary.py +19 -11
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/wage_tax.py +4 -4
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/schemas/wage_tax_settings.py +63 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/wage_tax.py +1 -1
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs.egg-info/PKG-INFO +21 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs.egg-info/SOURCES.txt +2 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/setup.py +2 -2
- brynq_sdk_nmbrs-2.3.0/PKG-INFO +0 -10
- brynq_sdk_nmbrs-2.3.0/brynq_sdk_nmbrs.egg-info/PKG-INFO +0 -10
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/absence.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/address.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/bank.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/children.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/companies.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/contract.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/days.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/debtors.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/document.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/function.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/hours.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/leave.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/manager.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/salary_tables.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schedules.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/__init__.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/absence.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/bank.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/costunit.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/days.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/debtor.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/hours.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/leave.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/manager.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/schedules.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/social_insurance.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/schemas/wagecomponents.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/social_insurance.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs/wagecomponents.py +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs.egg-info/not-zip-safe +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs.egg-info/requires.txt +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/brynq_sdk_nmbrs.egg-info/top_level.txt +0 -0
- {brynq_sdk_nmbrs-2.3.0 → brynq_sdk_nmbrs-2.3.1.dev0}/setup.cfg +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: brynq_sdk_nmbrs
|
|
3
|
+
Version: 2.3.1.dev0
|
|
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
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
from typing import Union, List, Literal, Optional, get_args, get_origin
|
|
1
|
+
import os
|
|
3
2
|
import re
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import List, Literal, Optional, Union, get_args, get_origin
|
|
5
|
+
|
|
4
6
|
import pandas as pd
|
|
5
|
-
from pydantic import BaseModel
|
|
6
7
|
import requests
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from zeep import Client, Settings
|
|
10
|
+
|
|
7
11
|
from brynq_sdk_brynq import BrynQ
|
|
12
|
+
|
|
8
13
|
from .absence import Absence
|
|
9
|
-
from .wage_tax import WageTax
|
|
10
14
|
from .address import Address
|
|
11
15
|
from .bank import Bank
|
|
12
|
-
from zeep import Client, Settings
|
|
13
16
|
from .children import Children
|
|
14
17
|
from .companies import Companies
|
|
15
|
-
from .debtors import Debtors
|
|
16
18
|
from .contract import Contract
|
|
17
|
-
from .costcenter import
|
|
19
|
+
from .costcenter import Costcenter, EmployeeCostcenter
|
|
18
20
|
from .costunit import Costunit
|
|
19
|
-
from .
|
|
21
|
+
from .days import FixedDays, VariableDays
|
|
22
|
+
from .debtors import Debtors
|
|
23
|
+
from .department import Departments, EmployeeDepartment
|
|
20
24
|
from .document import Payslip
|
|
25
|
+
from .employee_wage_tax_settings import EmployeeWageTaxSettings
|
|
21
26
|
from .employees import Employees
|
|
22
|
-
from .salary_tables import SalaryTables, SalaryScales, SalarySteps
|
|
23
27
|
from .employment import Employment
|
|
24
|
-
from .function import EmployeeFunction
|
|
25
|
-
from .hours import
|
|
26
|
-
from .days import VariableDays, FixedDays
|
|
28
|
+
from .function import EmployeeFunction, Functions
|
|
29
|
+
from .hours import FixedHours, VariableHours
|
|
27
30
|
from .manager import EmployeeManager, Manager
|
|
28
31
|
from .salaries import Salaries
|
|
32
|
+
from .salary_tables import SalaryScales, SalarySteps, SalaryTables
|
|
29
33
|
from .schedules import Schedule
|
|
34
|
+
from .wage_tax import WageTax
|
|
30
35
|
from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
|
|
31
|
-
import os
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
class Nmbrs(BrynQ):
|
|
@@ -67,10 +71,14 @@ class Nmbrs(BrynQ):
|
|
|
67
71
|
'https://api.nmbrs.nl/soap/v3/EmployeeService.asmx?wsdl',
|
|
68
72
|
settings=self.soap_settings
|
|
69
73
|
)
|
|
70
|
-
self.soap_auth_header = self.
|
|
74
|
+
self.soap_auth_header = self._get_soap_auth_header_companies()
|
|
75
|
+
self.soap_auth_header_employees = self._get_soap_auth_header_employees()
|
|
71
76
|
# Following methods can only be used if the SOAP authentication header is set (optional, this is not always in scope for all integrations)
|
|
72
77
|
if self.soap_auth_header is not None:
|
|
78
|
+
self.companies = Companies(self)
|
|
79
|
+
self.employees = Employees(self)
|
|
73
80
|
self.soap_company_ids = self.companies.get_soap_ids()
|
|
81
|
+
self.soap_employee_ids = self.employees.get_soap_ids()
|
|
74
82
|
self.salary_tables = SalaryTables(self)
|
|
75
83
|
self.salary_scales = SalaryScales(self)
|
|
76
84
|
self.salary_steps = SalarySteps(self)
|
|
@@ -84,13 +92,15 @@ class Nmbrs(BrynQ):
|
|
|
84
92
|
self.debtor = Debtors(self)
|
|
85
93
|
self.companies = Companies(self)
|
|
86
94
|
self.contract = Contract(self)
|
|
87
|
-
self.
|
|
95
|
+
self.employee_department = EmployeeDepartment(self)
|
|
96
|
+
self.department = Departments(self)
|
|
88
97
|
debtors, _ = self.debtor.get()
|
|
89
98
|
self.debtor_ids = debtors['debtor_id'].to_list()
|
|
90
99
|
self.company_ids = self.companies.get()['companyId'].to_list()
|
|
91
100
|
self.employees = Employees(self)
|
|
92
101
|
self.employment = Employment(self)
|
|
93
|
-
self.
|
|
102
|
+
self.employee_function = EmployeeFunction(self)
|
|
103
|
+
self.functions = Functions(self)
|
|
94
104
|
self.fixed_hours = FixedHours(self)
|
|
95
105
|
self.fixed_days = FixedDays(self)
|
|
96
106
|
self.variable_hours = VariableHours(self)
|
|
@@ -99,6 +109,10 @@ class Nmbrs(BrynQ):
|
|
|
99
109
|
self.employee_manager = EmployeeManager(self)
|
|
100
110
|
self.salaries = Salaries(self)
|
|
101
111
|
self.schedule = Schedule(self)
|
|
112
|
+
self.cost_center = Costcenter(self)
|
|
113
|
+
self.employee_cost_center = EmployeeCostcenter(self)
|
|
114
|
+
self.cost_unit = Costunit(self)
|
|
115
|
+
self.employee_wage_tax_settings = EmployeeWageTaxSettings(self)
|
|
102
116
|
self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
|
|
103
117
|
self.variable_wagecomponents = EmployeeVariableWageComponents(self)
|
|
104
118
|
self.current_period = self.companies.get_current_period()
|
|
@@ -114,7 +128,7 @@ class Nmbrs(BrynQ):
|
|
|
114
128
|
|
|
115
129
|
return headers
|
|
116
130
|
|
|
117
|
-
def
|
|
131
|
+
def _get_soap_auth_header_companies(self):
|
|
118
132
|
"""
|
|
119
133
|
Creates the SOAP authentication header using credentials from initial_credentials.
|
|
120
134
|
|
|
@@ -146,17 +160,17 @@ class Nmbrs(BrynQ):
|
|
|
146
160
|
Returns:
|
|
147
161
|
AuthHeaderWithDomainType: The authentication header for SOAP requests
|
|
148
162
|
"""
|
|
149
|
-
initial_credentials = self.
|
|
150
|
-
|
|
163
|
+
initial_credentials = self.interfaces.credentials.get(system='nmbrs', system_type=self.system_type)
|
|
164
|
+
custom_data = initial_credentials.get("custom_data", {})
|
|
151
165
|
|
|
152
166
|
# Get the AuthHeaderWithDomain type from the WSDL
|
|
153
167
|
AuthHeaderWithDomainType = self.soap_client_employees.get_element('ns0:AuthHeaderWithDomain')
|
|
154
168
|
|
|
155
169
|
# Create the auth header using credentials from config
|
|
156
170
|
auth_header = AuthHeaderWithDomainType(
|
|
157
|
-
Username=
|
|
158
|
-
Token=
|
|
159
|
-
Domain=
|
|
171
|
+
Username=custom_data.get("soap_api_username"),
|
|
172
|
+
Token=custom_data.get("soap_api_token"),
|
|
173
|
+
Domain=custom_data.get("soap_api_domain")
|
|
160
174
|
)
|
|
161
175
|
|
|
162
176
|
return auth_header
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict
|
|
2
|
+
|
|
1
3
|
import pandas as pd
|
|
2
4
|
import requests
|
|
3
|
-
from dateutil.utils import today
|
|
4
|
-
from requests import HTTPError
|
|
5
|
-
from typing import Dict, Any, TYPE_CHECKING
|
|
6
5
|
|
|
7
6
|
from brynq_sdk_functions import Functions
|
|
8
|
-
|
|
9
|
-
from .schemas.costcenter import
|
|
10
|
-
|
|
7
|
+
|
|
8
|
+
from .schemas.costcenter import (
|
|
9
|
+
CostcenterCreate,
|
|
10
|
+
CostcenterGet,
|
|
11
|
+
CostCentersResponse,
|
|
12
|
+
CostcenterUpdate,
|
|
13
|
+
EmployeeCostcenterGet,
|
|
14
|
+
EmployeeCostCentersResponse,
|
|
15
|
+
EmployeeCostcenterUpdate,
|
|
16
|
+
)
|
|
17
|
+
|
|
11
18
|
if TYPE_CHECKING:
|
|
12
19
|
from brynq_sdk_nmbrs import Nmbrs
|
|
13
20
|
|
|
@@ -40,12 +47,40 @@ class EmployeeCostcenter:
|
|
|
40
47
|
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/costcenters",
|
|
41
48
|
params=params)
|
|
42
49
|
data = self.nmbrs.get_paginated_result(request)
|
|
50
|
+
|
|
51
|
+
# Parse and validate API response using Pydantic models
|
|
52
|
+
if not data:
|
|
53
|
+
return pd.DataFrame()
|
|
54
|
+
|
|
55
|
+
# Validate response structure with Pydantic
|
|
56
|
+
response = EmployeeCostCentersResponse(data=data)
|
|
57
|
+
|
|
58
|
+
# Serialize models to dicts using model_dump with by_alias=True
|
|
59
|
+
serialized_data = [item.model_dump(by_alias=True, mode='json') for item in response.data]
|
|
60
|
+
|
|
61
|
+
# Use json_normalize to flatten nested structure efficiently
|
|
43
62
|
df = pd.json_normalize(
|
|
44
|
-
|
|
63
|
+
serialized_data,
|
|
45
64
|
record_path='employeeCostCenters',
|
|
46
65
|
meta=['employeeId']
|
|
47
66
|
)
|
|
48
67
|
|
|
68
|
+
# Flatten nested costUnits object if it exists
|
|
69
|
+
if 'costUnits' in df.columns:
|
|
70
|
+
# Check if costUnits contains dict-like objects
|
|
71
|
+
cost_units_dicts = df['costUnits'].dropna()
|
|
72
|
+
if not cost_units_dicts.empty and isinstance(cost_units_dicts.iloc[0], dict):
|
|
73
|
+
cost_units_expanded = pd.json_normalize(cost_units_dicts)
|
|
74
|
+
cost_units_expanded.columns = [f'costUnits.{col}' for col in cost_units_expanded.columns]
|
|
75
|
+
# Reindex to match original DataFrame index and fill missing values with None
|
|
76
|
+
cost_units_expanded = cost_units_expanded.reindex(df.index)
|
|
77
|
+
df = pd.concat([df.drop(columns=['costUnits']), cost_units_expanded], axis=1)
|
|
78
|
+
else:
|
|
79
|
+
# If costUnits is not dict-like or all None, create columns with None
|
|
80
|
+
for col in ['costUnits.costUnitId', 'costUnits.code', 'costUnits.description']:
|
|
81
|
+
df[col] = None
|
|
82
|
+
df = df.drop(columns=['costUnits'])
|
|
83
|
+
|
|
49
84
|
return df
|
|
50
85
|
|
|
51
86
|
def update(self, employee_id: str, data: Dict[str, Any]):
|
|
@@ -101,7 +136,17 @@ class Costcenter:
|
|
|
101
136
|
request = requests.Request(method='GET',
|
|
102
137
|
url=f"{self.nmbrs.base_url}companies/{company_id}/costcenters")
|
|
103
138
|
data = self.nmbrs.get_paginated_result(request)
|
|
104
|
-
|
|
139
|
+
|
|
140
|
+
# Parse and validate API response using Pydantic models
|
|
141
|
+
if not data:
|
|
142
|
+
return pd.DataFrame()
|
|
143
|
+
|
|
144
|
+
# Validate response structure with Pydantic
|
|
145
|
+
response = CostCentersResponse(data=data)
|
|
146
|
+
|
|
147
|
+
# Serialize models to dicts and convert to DataFrame efficiently
|
|
148
|
+
serialized_data = [item.model_dump(by_alias=True, mode='json') for item in response.data]
|
|
149
|
+
df = pd.DataFrame(serialized_data)
|
|
105
150
|
|
|
106
151
|
return df
|
|
107
152
|
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
1
3
|
import pandas as pd
|
|
2
4
|
import requests
|
|
3
|
-
from typing import Dict, Any
|
|
4
5
|
|
|
5
6
|
from brynq_sdk_functions import Functions
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
from .schemas.costunit import (
|
|
9
|
+
CostunitCreate,
|
|
10
|
+
CostunitDelete,
|
|
11
|
+
CostunitGet,
|
|
12
|
+
CostunitUpdate,
|
|
13
|
+
)
|
|
7
14
|
|
|
8
15
|
|
|
9
16
|
class Costunit:
|
|
@@ -13,7 +20,9 @@ class Costunit:
|
|
|
13
20
|
def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
14
21
|
costunits = pd.DataFrame()
|
|
15
22
|
for company in self.nmbrs.company_ids:
|
|
16
|
-
|
|
23
|
+
company_costunits = self._get(company)
|
|
24
|
+
if not company_costunits.empty:
|
|
25
|
+
costunits = pd.concat([costunits, company_costunits])
|
|
17
26
|
|
|
18
27
|
valid_costunits, invalid_costunits = Functions.validate_data(df=costunits, schema=CostunitGet, debug=True)
|
|
19
28
|
|
|
@@ -22,9 +31,12 @@ class Costunit:
|
|
|
22
31
|
def _get(self,
|
|
23
32
|
company_id: str):
|
|
24
33
|
request = requests.Request(method='GET',
|
|
25
|
-
url=f"{self.nmbrs.base_url}companies/{company_id}/
|
|
34
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costUnits")
|
|
26
35
|
|
|
27
36
|
data = self.nmbrs.get_paginated_result(request)
|
|
37
|
+
if not data:
|
|
38
|
+
return pd.DataFrame()
|
|
39
|
+
|
|
28
40
|
df = pd.DataFrame(data)
|
|
29
41
|
|
|
30
42
|
return df
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
2
4
|
import pandas as pd
|
|
3
5
|
import requests
|
|
4
|
-
|
|
5
|
-
from .schemas.department import DepartmentCreate, EmployeeDepartmentUpdate, Period, EmployeeDepartmentGet, DepartmentGet
|
|
6
|
+
|
|
6
7
|
from brynq_sdk_functions import Functions
|
|
7
8
|
|
|
9
|
+
from .schemas.department import (
|
|
10
|
+
DepartmentCreate,
|
|
11
|
+
DepartmentGet,
|
|
12
|
+
EmployeeDepartmentGet,
|
|
13
|
+
EmployeeDepartmentUpdate,
|
|
14
|
+
Period,
|
|
15
|
+
)
|
|
16
|
+
|
|
8
17
|
|
|
9
18
|
class EmployeeDepartment:
|
|
10
19
|
def __init__(self, nmbrs):
|
|
@@ -43,35 +52,35 @@ class EmployeeDepartment:
|
|
|
43
52
|
|
|
44
53
|
return df
|
|
45
54
|
|
|
46
|
-
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
# def create(self, employee_id: str, data: Dict[str, Any]):
|
|
56
|
+
# """
|
|
57
|
+
# Create a new department for an employee using Pydantic validation.
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
# Args:
|
|
60
|
+
# employee_id: The ID of the employee
|
|
61
|
+
# data: Dictionary containing department data with fields matching
|
|
62
|
+
# the DepartmentCreate schema (using camelCase field names)
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
# Returns:
|
|
65
|
+
# Response from the API
|
|
66
|
+
# """
|
|
67
|
+
# # Validate with Pydantic model
|
|
68
|
+
# nested_data = self.nmbrs.flat_dict_to_nested_dict(data, DepartmentCreate)
|
|
69
|
+
# department_model = DepartmentCreate(**nested_data)
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
# if self.nmbrs.mock_mode:
|
|
72
|
+
# return department_model
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
# # Convert validated model to dict for API payload
|
|
75
|
+
# payload = department_model.model_dump(exclude_none=True, by_alias=True)
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
# # Send request
|
|
78
|
+
# resp = self.nmbrs.session.post(
|
|
79
|
+
# url=f"{self.nmbrs.base_url}employees/{employee_id}/department",
|
|
80
|
+
# json=payload,
|
|
81
|
+
# timeout=self.nmbrs.timeout
|
|
82
|
+
# )
|
|
83
|
+
# return resp
|
|
75
84
|
|
|
76
85
|
def update(self, employee_id: str, data: Dict[str, Any]):
|
|
77
86
|
"""
|
|
@@ -93,7 +102,7 @@ class EmployeeDepartment:
|
|
|
93
102
|
return department_model
|
|
94
103
|
|
|
95
104
|
# Convert validated model to dict for API payload
|
|
96
|
-
payload = department_model.model_dump(exclude_none=True, by_alias=True)
|
|
105
|
+
payload = department_model.model_dump(exclude_none=True, by_alias=True, mode='json')
|
|
97
106
|
|
|
98
107
|
# Send request
|
|
99
108
|
resp = self.nmbrs.session.put(
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from brynq_sdk_functions import Functions
|
|
7
|
+
|
|
8
|
+
from .schemas.wage_tax_settings import (
|
|
9
|
+
EmployeeWageTaxSettingsCreate,
|
|
10
|
+
EmployeeWageTaxSettingsGet,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EmployeeWageTaxSettings:
|
|
15
|
+
def __init__(self, nmbrs):
|
|
16
|
+
self.nmbrs = nmbrs
|
|
17
|
+
|
|
18
|
+
def get(self,
|
|
19
|
+
employee_id: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
20
|
+
"""
|
|
21
|
+
Get wage tax settings history for employees in companies.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
employee_id: Optional filter for a specific employee ID
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Tuple of (valid_data, invalid_data) DataFrames
|
|
28
|
+
"""
|
|
29
|
+
wage_tax_settings = pd.DataFrame()
|
|
30
|
+
for company in self.nmbrs.company_ids:
|
|
31
|
+
company_settings = self._get(company, employee_id)
|
|
32
|
+
if not company_settings.empty:
|
|
33
|
+
wage_tax_settings = pd.concat([wage_tax_settings, company_settings])
|
|
34
|
+
|
|
35
|
+
valid_settings, invalid_settings = Functions.validate_data(
|
|
36
|
+
df=wage_tax_settings, schema=EmployeeWageTaxSettingsGet, debug=True
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return valid_settings, invalid_settings
|
|
40
|
+
|
|
41
|
+
def _get(self,
|
|
42
|
+
company_id: str,
|
|
43
|
+
employee_id: str = None) -> pd.DataFrame:
|
|
44
|
+
"""
|
|
45
|
+
Get wage tax settings history for a specific company.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
company_id: The ID of the company
|
|
49
|
+
employee_id: Optional filter for a specific employee ID
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
DataFrame with wage tax settings
|
|
53
|
+
"""
|
|
54
|
+
params = {}
|
|
55
|
+
if employee_id:
|
|
56
|
+
params['employeeId'] = employee_id
|
|
57
|
+
|
|
58
|
+
request = requests.Request(
|
|
59
|
+
method='GET',
|
|
60
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/wagetaxsettings",
|
|
61
|
+
params=params
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
65
|
+
|
|
66
|
+
if not data:
|
|
67
|
+
return pd.DataFrame()
|
|
68
|
+
|
|
69
|
+
# Normalize nested structure similar to departments/employments
|
|
70
|
+
df = pd.json_normalize(
|
|
71
|
+
data,
|
|
72
|
+
record_path='wagetaxsettings',
|
|
73
|
+
meta=['employeeId']
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return df
|
|
77
|
+
|
|
78
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
79
|
+
"""
|
|
80
|
+
Create wage tax settings for an employee using Pydantic validation.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
employee_id: The ID of the employee
|
|
84
|
+
data: Dictionary containing wage tax settings data with fields matching
|
|
85
|
+
the EmployeeWageTaxSettingsCreate schema (using camelCase field names)
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Response from the API
|
|
89
|
+
"""
|
|
90
|
+
# Validate with Pydantic model
|
|
91
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, EmployeeWageTaxSettingsCreate)
|
|
92
|
+
settings_model = EmployeeWageTaxSettingsCreate(**nested_data)
|
|
93
|
+
|
|
94
|
+
if self.nmbrs.mock_mode:
|
|
95
|
+
return settings_model
|
|
96
|
+
|
|
97
|
+
# Convert validated model to dict for API payload
|
|
98
|
+
payload = settings_model.model_dump(exclude_none=True, by_alias=True, mode='json')
|
|
99
|
+
|
|
100
|
+
# Send request
|
|
101
|
+
resp = self.nmbrs.session.post(
|
|
102
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/wagetaxsettings",
|
|
103
|
+
json=payload,
|
|
104
|
+
timeout=self.nmbrs.timeout
|
|
105
|
+
)
|
|
106
|
+
return resp
|