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.
- brynq_sdk_nmbrs/__init__.py +226 -0
- brynq_sdk_nmbrs/absence.py +124 -0
- brynq_sdk_nmbrs/address.py +66 -0
- brynq_sdk_nmbrs/bank.py +125 -0
- brynq_sdk_nmbrs/children.py +100 -0
- brynq_sdk_nmbrs/companies.py +93 -0
- brynq_sdk_nmbrs/contract.py +132 -0
- brynq_sdk_nmbrs/costcenter.py +166 -0
- brynq_sdk_nmbrs/costunit.py +90 -0
- brynq_sdk_nmbrs/days.py +137 -0
- brynq_sdk_nmbrs/debtors.py +25 -0
- brynq_sdk_nmbrs/department.py +122 -0
- brynq_sdk_nmbrs/document.py +30 -0
- brynq_sdk_nmbrs/employees.py +196 -0
- brynq_sdk_nmbrs/employment.py +107 -0
- brynq_sdk_nmbrs/function.py +89 -0
- brynq_sdk_nmbrs/hours.py +252 -0
- brynq_sdk_nmbrs/leave.py +139 -0
- brynq_sdk_nmbrs/manager.py +294 -0
- brynq_sdk_nmbrs/salaries.py +85 -0
- brynq_sdk_nmbrs/salary_tables.py +242 -0
- brynq_sdk_nmbrs/schedules.py +84 -0
- brynq_sdk_nmbrs/schemas/__init__.py +37 -0
- brynq_sdk_nmbrs/schemas/absence.py +61 -0
- brynq_sdk_nmbrs/schemas/address.py +76 -0
- brynq_sdk_nmbrs/schemas/bank.py +83 -0
- brynq_sdk_nmbrs/schemas/contracts.py +60 -0
- brynq_sdk_nmbrs/schemas/costcenter.py +91 -0
- brynq_sdk_nmbrs/schemas/costunit.py +40 -0
- brynq_sdk_nmbrs/schemas/days.py +98 -0
- brynq_sdk_nmbrs/schemas/debtor.py +16 -0
- brynq_sdk_nmbrs/schemas/department.py +57 -0
- brynq_sdk_nmbrs/schemas/employees.py +153 -0
- brynq_sdk_nmbrs/schemas/employment.py +48 -0
- brynq_sdk_nmbrs/schemas/function.py +50 -0
- brynq_sdk_nmbrs/schemas/hours.py +121 -0
- brynq_sdk_nmbrs/schemas/leave.py +53 -0
- brynq_sdk_nmbrs/schemas/manager.py +126 -0
- brynq_sdk_nmbrs/schemas/salary.py +92 -0
- brynq_sdk_nmbrs/schemas/schedules.py +96 -0
- brynq_sdk_nmbrs/schemas/social_insurance.py +40 -0
- brynq_sdk_nmbrs/schemas/wage_tax.py +98 -0
- brynq_sdk_nmbrs/schemas/wagecomponents.py +114 -0
- brynq_sdk_nmbrs/social_insurance.py +52 -0
- brynq_sdk_nmbrs/wage_tax.py +164 -0
- brynq_sdk_nmbrs/wagecomponents.py +268 -0
- brynq_sdk_nmbrs-2.3.1.dist-info/METADATA +21 -0
- brynq_sdk_nmbrs-2.3.1.dist-info/RECORD +50 -0
- brynq_sdk_nmbrs-2.3.1.dist-info/WHEEL +5 -0
- brynq_sdk_nmbrs-2.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from .leave import LeaveGroup
|
|
6
|
+
from .costcenter import Costcenter
|
|
7
|
+
from .costunit import Costunit
|
|
8
|
+
from .hours import Hours
|
|
9
|
+
from .bank import Bank
|
|
10
|
+
from .function import Functions
|
|
11
|
+
from zeep.exceptions import Fault
|
|
12
|
+
from zeep.helpers import serialize_object
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Companies:
|
|
16
|
+
def __init__(self, nmbrs):
|
|
17
|
+
self.nmbrs = nmbrs
|
|
18
|
+
self.costcenters = Costcenter(nmbrs)
|
|
19
|
+
self.costunits = Costunit(nmbrs)
|
|
20
|
+
self.hours = Hours(nmbrs)
|
|
21
|
+
self.banks = Bank(nmbrs)
|
|
22
|
+
self.soap_client_companies = nmbrs.soap_client_companies
|
|
23
|
+
self.logger = logging.getLogger(__name__)
|
|
24
|
+
self.leave_groups = LeaveGroup(nmbrs)
|
|
25
|
+
|
|
26
|
+
def get(self) -> pd.DataFrame:
|
|
27
|
+
try:
|
|
28
|
+
request = requests.Request(method='GET',
|
|
29
|
+
url=f"{self.nmbrs.base_url}companies")
|
|
30
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
31
|
+
df = pd.DataFrame(data)
|
|
32
|
+
except requests.HTTPError as e:
|
|
33
|
+
if e.response.status_code == 403 and e.response.json().get("title") == "ForbiddenMultiDebtor.":
|
|
34
|
+
df = pd.DataFrame()
|
|
35
|
+
for debtor_id in self.nmbrs.debtor_ids:
|
|
36
|
+
request = requests.Request(method='GET',
|
|
37
|
+
url=f"{self.nmbrs.base_url}debtors/{debtor_id}/companies")
|
|
38
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
39
|
+
df = pd.concat([df, pd.DataFrame(data)])
|
|
40
|
+
else:
|
|
41
|
+
raise e
|
|
42
|
+
# TODO: add validation
|
|
43
|
+
|
|
44
|
+
return df
|
|
45
|
+
|
|
46
|
+
def get_soap_ids(self) -> pd.DataFrame:
|
|
47
|
+
"""
|
|
48
|
+
Get all companies using the SOAP API.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
pd.DataFrame: DataFrame containing all companies
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
# Make SOAP request with the proper header structure
|
|
55
|
+
response = self.soap_client_companies.service.List_GetAll(
|
|
56
|
+
_soapheaders=[self.nmbrs.soap_auth_header]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Convert response to DataFrame
|
|
60
|
+
if response:
|
|
61
|
+
# Convert Zeep objects to Python dictionaries
|
|
62
|
+
serialized_response = serialize_object(response)
|
|
63
|
+
|
|
64
|
+
# TODO: add validation here
|
|
65
|
+
# Convert to DataFrame
|
|
66
|
+
df = pd.DataFrame(serialized_response)
|
|
67
|
+
df = self.nmbrs._rename_camel_columns_to_snake_case(df)
|
|
68
|
+
|
|
69
|
+
return df
|
|
70
|
+
else:
|
|
71
|
+
return pd.DataFrame()
|
|
72
|
+
|
|
73
|
+
except Fault as e:
|
|
74
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
self.logger.exception("Exception occurred:")
|
|
77
|
+
raise Exception(f"Failed to get companies: {str(e)}")
|
|
78
|
+
|
|
79
|
+
def get_current_period(self) -> int:
|
|
80
|
+
try:
|
|
81
|
+
df = pd.DataFrame()
|
|
82
|
+
for company_id in self.nmbrs.company_ids:
|
|
83
|
+
request = requests.Request(method='GET',
|
|
84
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/period")
|
|
85
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
86
|
+
df_temp = pd.json_normalize(data)
|
|
87
|
+
df_temp['company_id'] = company_id
|
|
88
|
+
df = pd.concat([df, df_temp])
|
|
89
|
+
|
|
90
|
+
return df
|
|
91
|
+
except Exception as e:
|
|
92
|
+
self.logger.exception("Exception occurred:")
|
|
93
|
+
raise Exception(f"Failed to get current period: {str(e)}")
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import requests
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
from .schemas.contracts import ContractGet, ContractCreate, ContractUpdate, ContractDelete
|
|
6
|
+
from brynq_sdk_functions import Functions
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Contract:
|
|
10
|
+
def __init__(self, nmbrs):
|
|
11
|
+
self.nmbrs = nmbrs
|
|
12
|
+
|
|
13
|
+
def get(self,
|
|
14
|
+
created_from: str = None,
|
|
15
|
+
employee_id: str = None) -> (pd.DataFrame, pd.DataFrame):
|
|
16
|
+
contracts = pd.DataFrame()
|
|
17
|
+
for company in self.nmbrs.company_ids:
|
|
18
|
+
contracts = pd.concat([contracts, self._get(company, created_from, employee_id)])
|
|
19
|
+
|
|
20
|
+
valid_contracts, invalid_contracts = Functions.validate_data(df=contracts, schema=ContractGet, debug=True)
|
|
21
|
+
|
|
22
|
+
return valid_contracts, invalid_contracts
|
|
23
|
+
|
|
24
|
+
def _get(self,
|
|
25
|
+
company_id: str,
|
|
26
|
+
created_from: str = None,
|
|
27
|
+
employee_id: str = None) -> pd.DataFrame:
|
|
28
|
+
params = {}
|
|
29
|
+
if created_from:
|
|
30
|
+
params['createdFrom'] = created_from
|
|
31
|
+
if employee_id:
|
|
32
|
+
params['employeeId'] = employee_id
|
|
33
|
+
request = requests.Request(method='GET',
|
|
34
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/contracts",
|
|
35
|
+
params=params)
|
|
36
|
+
|
|
37
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
38
|
+
df = pd.json_normalize(
|
|
39
|
+
data,
|
|
40
|
+
record_path='contracts',
|
|
41
|
+
meta=['employeeId']
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
df['company_id'] = company_id
|
|
45
|
+
|
|
46
|
+
return df
|
|
47
|
+
|
|
48
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
49
|
+
"""
|
|
50
|
+
Create a new contract for an employee using Pydantic validation.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
employee_id: The ID of the employee
|
|
54
|
+
data: Dictionary containing contract data with fields matching
|
|
55
|
+
the ContractCreate schema (using camelCase field names)
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Response from the API
|
|
59
|
+
"""
|
|
60
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
61
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, ContractCreate)
|
|
62
|
+
contract_model = ContractCreate(**nested_data)
|
|
63
|
+
|
|
64
|
+
if self.nmbrs.mock_mode:
|
|
65
|
+
return contract_model
|
|
66
|
+
|
|
67
|
+
# Convert validated model to dict for API payload
|
|
68
|
+
payload = contract_model.model_dump_json(exclude_none=True, by_alias=True)
|
|
69
|
+
|
|
70
|
+
# Send request
|
|
71
|
+
resp = self.nmbrs.session.post(
|
|
72
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/contract",
|
|
73
|
+
data=payload,
|
|
74
|
+
headers={"Content-Type": "application/json"},
|
|
75
|
+
timeout=self.nmbrs.timeout
|
|
76
|
+
)
|
|
77
|
+
return resp
|
|
78
|
+
|
|
79
|
+
def update(self, employee_id: str, data: Dict[str, Any]):
|
|
80
|
+
"""
|
|
81
|
+
Update a contract for an employee using Pydantic validation.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
employee_id: The ID of the employee
|
|
85
|
+
data: Dictionary containing contract data with fields matching
|
|
86
|
+
the ContractUpdate schema (using camelCase field names)
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Response from the API
|
|
90
|
+
"""
|
|
91
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
92
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, ContractUpdate)
|
|
93
|
+
contract_model = ContractUpdate(**nested_data)
|
|
94
|
+
|
|
95
|
+
if self.nmbrs.mock_mode:
|
|
96
|
+
return contract_model
|
|
97
|
+
|
|
98
|
+
# Convert validated model to dict for API payload
|
|
99
|
+
payload = contract_model.model_dump_json(exclude_none=True, by_alias=True)
|
|
100
|
+
|
|
101
|
+
# Send request
|
|
102
|
+
resp = self.nmbrs.session.put(
|
|
103
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/contract",
|
|
104
|
+
data=payload,
|
|
105
|
+
headers={"Content-Type": "application/json"},
|
|
106
|
+
timeout=self.nmbrs.timeout
|
|
107
|
+
)
|
|
108
|
+
return resp
|
|
109
|
+
|
|
110
|
+
def delete(self, employee_id: str, contract_id: str):
|
|
111
|
+
"""
|
|
112
|
+
Delete a contract for an employee.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
employee_id: The ID of the employee
|
|
116
|
+
contract_id: The ID of the contract to delete
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Response from the API
|
|
120
|
+
"""
|
|
121
|
+
# Create and validate a ContractDelete model
|
|
122
|
+
contract_model = ContractDelete(contractId=contract_id)
|
|
123
|
+
|
|
124
|
+
if self.nmbrs.mock_mode:
|
|
125
|
+
return contract_model
|
|
126
|
+
|
|
127
|
+
# Send request
|
|
128
|
+
resp = self.nmbrs.session.delete(
|
|
129
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/contracts/{contract_id}",
|
|
130
|
+
timeout=self.nmbrs.timeout
|
|
131
|
+
)
|
|
132
|
+
return resp
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from dateutil.utils import today
|
|
4
|
+
from requests import HTTPError
|
|
5
|
+
from typing import Dict, Any, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from brynq_sdk_functions import Functions
|
|
8
|
+
from .schemas.costcenter import CostcenterGet, EmployeeCostcenterGet
|
|
9
|
+
from .schemas.costcenter import EmployeeCostcenterUpdate, EmployeeCostcenterDelete
|
|
10
|
+
from .schemas.costcenter import CostcenterCreate, CostcenterUpdate, CostcenterDelete
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from brynq_sdk_nmbrs import Nmbrs
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EmployeeCostcenter:
|
|
16
|
+
def __init__(self, nmbrs):
|
|
17
|
+
self.nmbrs: Nmbrs = nmbrs
|
|
18
|
+
|
|
19
|
+
def get(self,
|
|
20
|
+
created_from: str = None,
|
|
21
|
+
employee_id: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
22
|
+
costcenters = pd.DataFrame()
|
|
23
|
+
for company in self.nmbrs.company_ids:
|
|
24
|
+
costcenters = pd.concat([costcenters, self._get(company, created_from, employee_id)])
|
|
25
|
+
|
|
26
|
+
valid_costcenters, invalid_costcenters = Functions.validate_data(df=costcenters, schema=EmployeeCostcenterGet, debug=True)
|
|
27
|
+
|
|
28
|
+
return valid_costcenters, invalid_costcenters
|
|
29
|
+
|
|
30
|
+
def _get(self,
|
|
31
|
+
company_id: str,
|
|
32
|
+
created_from: str = None,
|
|
33
|
+
employee_id: str = None) -> pd.DataFrame:
|
|
34
|
+
params = {}
|
|
35
|
+
if created_from:
|
|
36
|
+
params['createdFrom'] = created_from
|
|
37
|
+
if employee_id:
|
|
38
|
+
params['employeeId'] = employee_id
|
|
39
|
+
request = requests.Request(method='GET',
|
|
40
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/costcenters",
|
|
41
|
+
params=params)
|
|
42
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
43
|
+
df = pd.json_normalize(
|
|
44
|
+
data,
|
|
45
|
+
record_path='employeeCostCenters',
|
|
46
|
+
meta=['employeeId']
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return df
|
|
50
|
+
|
|
51
|
+
def update(self, employee_id: str, data: Dict[str, Any]):
|
|
52
|
+
"""
|
|
53
|
+
Update a costcenter for an employee using Pydantic validation.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
employee_id: The ID of the employee
|
|
57
|
+
data: Dictionary containing costcenter data with fields matching
|
|
58
|
+
the EmployeeCostcenterUpdate schema (using camelCase field names)
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Response from the API
|
|
62
|
+
"""
|
|
63
|
+
# this is the Nmbrs GUID that is returned after creating an employee, for some reason also included in body here.
|
|
64
|
+
data['employee_id'] = employee_id
|
|
65
|
+
data['default'] = True
|
|
66
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
67
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, EmployeeCostcenterUpdate)
|
|
68
|
+
costcenter_model = EmployeeCostcenterUpdate(**nested_data)
|
|
69
|
+
|
|
70
|
+
if self.nmbrs.mock_mode:
|
|
71
|
+
return costcenter_model
|
|
72
|
+
|
|
73
|
+
# Convert validated model to dict for API payload
|
|
74
|
+
payload = costcenter_model.model_dump(exclude_none=True, by_alias=True)
|
|
75
|
+
|
|
76
|
+
# Send request
|
|
77
|
+
resp = self.nmbrs.session.put(
|
|
78
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/employeecostcenter",
|
|
79
|
+
json=payload,
|
|
80
|
+
timeout=self.nmbrs.timeout
|
|
81
|
+
)
|
|
82
|
+
return resp
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Costcenter:
|
|
87
|
+
def __init__(self, nmbrs):
|
|
88
|
+
self.nmbrs = nmbrs
|
|
89
|
+
|
|
90
|
+
def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
91
|
+
costcenters = pd.DataFrame()
|
|
92
|
+
for company in self.nmbrs.company_ids:
|
|
93
|
+
costcenters = pd.concat([costcenters, self._get(company)])
|
|
94
|
+
|
|
95
|
+
valid_costcenters, invalid_costcenters = Functions.validate_data(df=costcenters, schema=CostcenterGet, debug=True)
|
|
96
|
+
|
|
97
|
+
return valid_costcenters, invalid_costcenters
|
|
98
|
+
|
|
99
|
+
def _get(self,
|
|
100
|
+
company_id: str):
|
|
101
|
+
request = requests.Request(method='GET',
|
|
102
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costcenters")
|
|
103
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
104
|
+
df = pd.DataFrame(data)
|
|
105
|
+
|
|
106
|
+
return df
|
|
107
|
+
|
|
108
|
+
def create(self, company_id: str, data: Dict[str, Any]):
|
|
109
|
+
"""
|
|
110
|
+
Create a new costcenter using Pydantic validation.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
company_id: The ID of the company
|
|
114
|
+
data: Dictionary containing costcenter data with fields matching
|
|
115
|
+
the CostcenterCreate schema (using camelCase field names)
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Response from the API
|
|
119
|
+
"""
|
|
120
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
121
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, CostcenterCreate)
|
|
122
|
+
costcenter_model = CostcenterCreate(**nested_data)
|
|
123
|
+
|
|
124
|
+
if self.nmbrs.mock_mode:
|
|
125
|
+
return costcenter_model
|
|
126
|
+
|
|
127
|
+
# Convert validated model to dict for API payload
|
|
128
|
+
payload = costcenter_model.model_dump(exclude_none=True, by_alias=True)
|
|
129
|
+
|
|
130
|
+
# Send request
|
|
131
|
+
resp = self.nmbrs.session.post(
|
|
132
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costcenter",
|
|
133
|
+
json=payload,
|
|
134
|
+
timeout=self.nmbrs.timeout
|
|
135
|
+
)
|
|
136
|
+
return resp
|
|
137
|
+
|
|
138
|
+
def update(self, company_id: str, data: Dict[str, Any]):
|
|
139
|
+
"""
|
|
140
|
+
Update a costcenter using Pydantic validation.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
company_id: The ID of the company
|
|
144
|
+
data: Dictionary containing costcenter data with fields matching
|
|
145
|
+
the CostcenterUpdate schema (using camelCase field names)
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Response from the API
|
|
149
|
+
"""
|
|
150
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
151
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, CostcenterUpdate)
|
|
152
|
+
costcenter_model = CostcenterUpdate(**nested_data)
|
|
153
|
+
|
|
154
|
+
if self.nmbrs.mock_mode:
|
|
155
|
+
return costcenter_model
|
|
156
|
+
|
|
157
|
+
# Convert validated model to dict for API payload
|
|
158
|
+
payload = costcenter_model.model_dump(exclude_none=True, by_alias=True)
|
|
159
|
+
|
|
160
|
+
# Send request
|
|
161
|
+
resp = self.nmbrs.session.put(
|
|
162
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costcenter",
|
|
163
|
+
json=payload,
|
|
164
|
+
timeout=self.nmbrs.timeout
|
|
165
|
+
)
|
|
166
|
+
return resp
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
|
|
5
|
+
from brynq_sdk_functions import Functions
|
|
6
|
+
from .schemas.costunit import CostunitGet, CostunitCreate, CostunitUpdate, CostunitDelete
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Costunit:
|
|
10
|
+
def __init__(self, nmbrs):
|
|
11
|
+
self.nmbrs = nmbrs
|
|
12
|
+
|
|
13
|
+
def get(self) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
14
|
+
costunits = pd.DataFrame()
|
|
15
|
+
for company in self.nmbrs.company_ids:
|
|
16
|
+
costunits = pd.concat([costunits, self._get(company)])
|
|
17
|
+
|
|
18
|
+
valid_costunits, invalid_costunits = Functions.validate_data(df=costunits, schema=CostunitGet, debug=True)
|
|
19
|
+
|
|
20
|
+
return valid_costunits, invalid_costunits
|
|
21
|
+
|
|
22
|
+
def _get(self,
|
|
23
|
+
company_id: str):
|
|
24
|
+
request = requests.Request(method='GET',
|
|
25
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costunits")
|
|
26
|
+
|
|
27
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
28
|
+
df = pd.DataFrame(data)
|
|
29
|
+
|
|
30
|
+
return df
|
|
31
|
+
|
|
32
|
+
def create(self, company_id: str, data: Dict[str, Any]):
|
|
33
|
+
"""
|
|
34
|
+
Create a new costunit using Pydantic validation.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
company_id: The ID of the company
|
|
38
|
+
data: Dictionary containing costunit data with fields matching
|
|
39
|
+
the CostunitCreate schema (using camelCase field names)
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Response from the API
|
|
43
|
+
"""
|
|
44
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
45
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, CostunitCreate)
|
|
46
|
+
costunit_model = CostunitCreate(**nested_data)
|
|
47
|
+
|
|
48
|
+
if self.nmbrs.mock_mode:
|
|
49
|
+
return costunit_model
|
|
50
|
+
|
|
51
|
+
# Convert validated model to dict for API payload
|
|
52
|
+
payload = costunit_model.model_dump(exclude_none=True, by_alias=True)
|
|
53
|
+
|
|
54
|
+
# Send request
|
|
55
|
+
resp = self.nmbrs.session.post(
|
|
56
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costunit",
|
|
57
|
+
json=payload,
|
|
58
|
+
timeout=self.nmbrs.timeout
|
|
59
|
+
)
|
|
60
|
+
return resp
|
|
61
|
+
|
|
62
|
+
def update(self, company_id: str, data: Dict[str, Any]):
|
|
63
|
+
"""
|
|
64
|
+
Update a costunit using Pydantic validation.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
company_id: The ID of the company
|
|
68
|
+
data: Dictionary containing costunit data with fields matching
|
|
69
|
+
the CostunitUpdate 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
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, CostunitUpdate)
|
|
76
|
+
costunit_model = CostunitUpdate(**nested_data)
|
|
77
|
+
|
|
78
|
+
if self.nmbrs.mock_mode:
|
|
79
|
+
return costunit_model
|
|
80
|
+
|
|
81
|
+
# Convert validated model to dict for API payload
|
|
82
|
+
payload = costunit_model.model_dump(exclude_none=True, by_alias=True)
|
|
83
|
+
|
|
84
|
+
# Send request
|
|
85
|
+
resp = self.nmbrs.session.put(
|
|
86
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/costunit",
|
|
87
|
+
json=payload,
|
|
88
|
+
timeout=self.nmbrs.timeout
|
|
89
|
+
)
|
|
90
|
+
return resp
|
brynq_sdk_nmbrs/days.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
from .schemas.days import (
|
|
5
|
+
VariableDaysGet, VariableDaysCreate, FixedDaysCreate, FixedDaysGet
|
|
6
|
+
)
|
|
7
|
+
from brynq_sdk_functions import Functions
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VariableDays:
|
|
11
|
+
def __init__(self, nmbrs):
|
|
12
|
+
self.nmbrs = nmbrs
|
|
13
|
+
|
|
14
|
+
def get(self,
|
|
15
|
+
employee_id: str,
|
|
16
|
+
created_from: str = None,
|
|
17
|
+
period: int = None,
|
|
18
|
+
year: int = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
19
|
+
params = {}
|
|
20
|
+
if created_from:
|
|
21
|
+
params['createdFrom'] = created_from
|
|
22
|
+
if period:
|
|
23
|
+
params['period'] = period
|
|
24
|
+
if year:
|
|
25
|
+
params['year'] = year
|
|
26
|
+
request = requests.Request(method='GET',
|
|
27
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/variabledays",
|
|
28
|
+
params=params)
|
|
29
|
+
|
|
30
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
31
|
+
|
|
32
|
+
df = pd.DataFrame(data)
|
|
33
|
+
|
|
34
|
+
df['employeeId'] = employee_id # Add employee_id for tracking
|
|
35
|
+
df['period'] = period
|
|
36
|
+
df['year'] = year
|
|
37
|
+
# Validate data using the schema
|
|
38
|
+
valid_days, invalid_days = Functions.validate_data(df=df, schema=VariableDaysGet, debug=True)
|
|
39
|
+
|
|
40
|
+
return valid_days, invalid_days
|
|
41
|
+
|
|
42
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
43
|
+
"""
|
|
44
|
+
Create variable hours for an employee using Pydantic validation.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
employee_id: The ID of the employee
|
|
48
|
+
data: Dictionary containing hours data with fields matching
|
|
49
|
+
the VariableHoursCreate schema (using camelCase field names)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Response from the API
|
|
53
|
+
"""
|
|
54
|
+
# Validate with Pydantic model
|
|
55
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, VariableDaysCreate)
|
|
56
|
+
hours_model = VariableDaysCreate(**nested_data)
|
|
57
|
+
|
|
58
|
+
if self.nmbrs.mock_mode:
|
|
59
|
+
return hours_model
|
|
60
|
+
|
|
61
|
+
# Convert validated model to dict for API payload
|
|
62
|
+
payload = hours_model.model_dump(exclude_none=True, by_alias=True)
|
|
63
|
+
|
|
64
|
+
# Send request
|
|
65
|
+
resp = self.nmbrs.session.post(
|
|
66
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/variabledays",
|
|
67
|
+
json=payload,
|
|
68
|
+
timeout=self.nmbrs.timeout
|
|
69
|
+
)
|
|
70
|
+
return resp
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class FixedDays:
|
|
74
|
+
def __init__(self, nmbrs):
|
|
75
|
+
self.nmbrs = nmbrs
|
|
76
|
+
|
|
77
|
+
def get(self,
|
|
78
|
+
company_id: str,
|
|
79
|
+
created_from: str = None,
|
|
80
|
+
employee_id: str = None,
|
|
81
|
+
period: int = None,
|
|
82
|
+
year: int = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
83
|
+
params = {}
|
|
84
|
+
if created_from:
|
|
85
|
+
params['createdFrom'] = created_from
|
|
86
|
+
if employee_id:
|
|
87
|
+
params['employeeId'] = employee_id
|
|
88
|
+
if period:
|
|
89
|
+
params['period'] = period
|
|
90
|
+
if year:
|
|
91
|
+
params['year'] = year
|
|
92
|
+
request = requests.Request(method='GET',
|
|
93
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/fixeddays",
|
|
94
|
+
params=params)
|
|
95
|
+
|
|
96
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
97
|
+
df = pd.DataFrame(data)
|
|
98
|
+
df['employeeId'] = employee_id # Add employee_id for tracking
|
|
99
|
+
df['period'] = period
|
|
100
|
+
df['year'] = year
|
|
101
|
+
|
|
102
|
+
# Validate data using the schema
|
|
103
|
+
valid_days, invalid_days = Functions.validate_data(df=df, schema=VariableDaysGet, debug=True)
|
|
104
|
+
|
|
105
|
+
return valid_days, invalid_days
|
|
106
|
+
|
|
107
|
+
return df
|
|
108
|
+
|
|
109
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
110
|
+
"""
|
|
111
|
+
Create fixed hours for an employee using Pydantic validation.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
employee_id: The ID of the employee
|
|
115
|
+
data: Dictionary containing hours data with fields matching
|
|
116
|
+
the FixedHoursCreate schema (using camelCase field names)
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Response from the API
|
|
120
|
+
"""
|
|
121
|
+
# Validate with Pydantic model
|
|
122
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, FixedDaysCreate)
|
|
123
|
+
hours_model = FixedDaysCreate(**nested_data)
|
|
124
|
+
|
|
125
|
+
if self.nmbrs.mock_mode:
|
|
126
|
+
return hours_model
|
|
127
|
+
|
|
128
|
+
# Convert validated model to dict for API payload
|
|
129
|
+
payload = hours_model.model_dump(exclude_none=True, by_alias=True)
|
|
130
|
+
|
|
131
|
+
# Send request
|
|
132
|
+
resp = self.nmbrs.session.post(
|
|
133
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/fixeddays",
|
|
134
|
+
json=payload,
|
|
135
|
+
timeout=self.nmbrs.timeout
|
|
136
|
+
)
|
|
137
|
+
return resp
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from brynq_sdk_functions import Functions
|
|
4
|
+
from .department import Departments
|
|
5
|
+
from .function import Functions as NmbrsFunctions
|
|
6
|
+
from .schemas.debtor import DebtorsGet
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Debtors:
|
|
10
|
+
def __init__(self, nmbrs):
|
|
11
|
+
self.nmbrs = nmbrs
|
|
12
|
+
self.departments = Departments(nmbrs)
|
|
13
|
+
self.functions = NmbrsFunctions(nmbrs)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get(self) -> (pd.DataFrame, pd.DataFrame):
|
|
17
|
+
request = requests.Request(method='GET',
|
|
18
|
+
url=f"{self.nmbrs.base_url}debtors")
|
|
19
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
20
|
+
|
|
21
|
+
df = pd.DataFrame(data)
|
|
22
|
+
|
|
23
|
+
valid_debtors, invalid_debtors = Functions.validate_data(df=df, schema=DebtorsGet, debug=True)
|
|
24
|
+
|
|
25
|
+
return valid_debtors, invalid_debtors
|