brynq-sdk-nmbrs 1.2.0__tar.gz → 1.3.1__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-1.2.0 → brynq_sdk_nmbrs-1.3.1}/PKG-INFO +1 -1
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/__init__.py +58 -19
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/address.py +66 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/bank.py +123 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/children.py +0 -1
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/companies.py +3 -6
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/contract.py +128 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/costcenter.py +234 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/costunit.py +112 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/debtors.py +2 -1
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/department.py +141 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/document.py +3 -2
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/employees.py +169 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/employment.py +129 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/function.py +138 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/hours.py +246 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/salaries.py +136 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/salary_tables.py +0 -11
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs/schedules.py +64 -9
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/__init__.py +31 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/address.py +105 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/bank.py +82 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/contracts.py +58 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/costcenter.py +87 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/costunit.py +38 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/department.py +51 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/employees.py +138 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/employment.py +46 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/function.py +50 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/hours.py +114 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/salary.py +77 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/schedules.py +105 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/schemas/wagecomponents.py +110 -0
- brynq_sdk_nmbrs-1.3.1/brynq_sdk_nmbrs/wagecomponents.py +268 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs.egg-info/PKG-INFO +1 -1
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs.egg-info/SOURCES.txt +1 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs.egg-info/requires.txt +2 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/setup.py +3 -1
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/address.py +0 -70
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/bank.py +0 -95
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/contract.py +0 -102
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/costcenter.py +0 -120
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/department.py +0 -86
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/employees.py +0 -219
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/employment.py +0 -92
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/function.py +0 -84
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/hours.py +0 -152
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/salaries.py +0 -96
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/__init__.py +0 -31
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/address.py +0 -34
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/bank.py +0 -41
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/contracts.py +0 -29
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/costcenter.py +0 -26
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/costunit.py +0 -11
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/department.py +0 -29
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/employees.py +0 -38
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/employment.py +0 -28
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/function.py +0 -28
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/hours.py +0 -88
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/salary.py +0 -29
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/schedules.py +0 -79
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/schemas/wagecomponents.py +0 -53
- brynq_sdk_nmbrs-1.2.0/brynq_sdk_nmbrs/wagecomponents.py +0 -215
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs.egg-info/not-zip-safe +0 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/brynq_sdk_nmbrs.egg-info/top_level.txt +0 -0
- {brynq_sdk_nmbrs-1.2.0 → brynq_sdk_nmbrs-1.3.1}/setup.cfg +0 -0
|
@@ -4,43 +4,81 @@ import re
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import requests
|
|
6
6
|
from brynq_sdk_brynq import BrynQ
|
|
7
|
+
from .address import Address
|
|
8
|
+
from .bank import Bank
|
|
7
9
|
from zeep import Client, Settings
|
|
8
10
|
from .children import Children
|
|
9
11
|
from .companies import Companies
|
|
10
12
|
from .debtors import Debtors
|
|
13
|
+
from .contract import Contract
|
|
14
|
+
from .costcenter import EmployeeCostcenter, Costcenter
|
|
15
|
+
from .costunit import Costunit
|
|
16
|
+
from .department import EmployeeDepartment
|
|
17
|
+
from .document import Payslip
|
|
11
18
|
from .employees import Employees
|
|
12
19
|
from .salary_tables import SalaryTables, SalaryScales, SalarySteps
|
|
20
|
+
from .employment import Employment
|
|
21
|
+
from .function import EmployeeFunction
|
|
22
|
+
from .hours import VariableHours, FixedHours
|
|
23
|
+
from .salaries import Salaries
|
|
24
|
+
from .schedules import Schedule
|
|
25
|
+
from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
|
|
13
26
|
|
|
14
27
|
|
|
15
28
|
class Nmbrs(BrynQ):
|
|
16
|
-
def __init__(self, label: Union[str, List], debug: bool = False):
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
self.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
def __init__(self, label: Union[str, List], debug: bool = False, mock_mode: bool = True):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the Nmbrs class.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
label: The label of the system in BrynQ. legacy
|
|
35
|
+
debug: Whether to print debug information
|
|
36
|
+
mock_mode: If true, data will NOT be send to Nmbrs but only be tested for validity against Pydantic schemas
|
|
37
|
+
"""
|
|
38
|
+
self.mock_mode = mock_mode
|
|
39
|
+
if mock_mode is False:
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.label = label
|
|
42
|
+
headers = self._get_request_headers(label=label)
|
|
43
|
+
self.base_url = "https://api.nmbrsapp.com/api/"
|
|
44
|
+
self.session = requests.Session()
|
|
45
|
+
self.session.headers.update(headers)
|
|
46
|
+
|
|
47
|
+
# Initialize SOAP client
|
|
48
|
+
self.soap_settings = Settings(
|
|
49
|
+
strict=False,
|
|
50
|
+
xml_huge_tree=True,
|
|
51
|
+
force_https=True
|
|
52
|
+
)
|
|
53
|
+
self.soap_client = Client(
|
|
54
|
+
'https://api.nmbrs.nl/soap/v3/CompanyService.asmx?wsdl',
|
|
55
|
+
settings=self.soap_settings
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self.address = Address(self)
|
|
59
|
+
self.bank = Bank(self)
|
|
60
|
+
self.children = Children(self)
|
|
34
61
|
self.debtor = Debtors(self)
|
|
35
62
|
self.companies = Companies(self)
|
|
63
|
+
self.contract = Contract(self)
|
|
64
|
+
self.department = EmployeeDepartment(self)
|
|
36
65
|
self.company_ids = self.companies.get()['company_id']
|
|
37
66
|
self.soap_company_ids = self.companies.get_soap_ids()
|
|
38
67
|
self.employees = Employees(self)
|
|
68
|
+
self.employment = Employment(self)
|
|
69
|
+
self.function = EmployeeFunction(self)
|
|
70
|
+
self.fixed_hours = FixedHours(self)
|
|
71
|
+
self.variable_hours = VariableHours(self)
|
|
72
|
+
self.salaries = Salaries(self)
|
|
73
|
+
self.schedule = Schedule(self)
|
|
74
|
+
self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
|
|
75
|
+
self.variable_wagecomponents = EmployeeVariableWageComponents(self)
|
|
39
76
|
self.children = Children(self)
|
|
40
77
|
self.salary_tables = SalaryTables(self)
|
|
41
78
|
self.salary_scales = SalaryScales(self)
|
|
42
79
|
self.salary_steps = SalarySteps(self)
|
|
43
80
|
self.debug = debug
|
|
81
|
+
self.timeout = 3600
|
|
44
82
|
|
|
45
83
|
def _get_request_headers(self, label):
|
|
46
84
|
initial_credentials = self.get_system_credential(system='nmbrs', label=label)
|
|
@@ -82,7 +120,7 @@ class Nmbrs(BrynQ):
|
|
|
82
120
|
while has_next_page:
|
|
83
121
|
prepped = request.prepare()
|
|
84
122
|
prepped.headers.update(self.session.headers)
|
|
85
|
-
resp = self.session.send(prepped)
|
|
123
|
+
resp = self.session.send(prepped, timeout=self.nmbrs.timeout)
|
|
86
124
|
resp.raise_for_status()
|
|
87
125
|
response_data = resp.json()
|
|
88
126
|
result_data += response_data['data']
|
|
@@ -119,3 +157,4 @@ class Nmbrs(BrynQ):
|
|
|
119
157
|
df.columns = map(camel_to_snake_case, df.columns)
|
|
120
158
|
|
|
121
159
|
return df
|
|
160
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from typing import Dict, Any
|
|
4
|
+
from .schemas.address import AddressCreate, AddressGet, Period
|
|
5
|
+
from brynq_sdk_functions import Functions
|
|
6
|
+
|
|
7
|
+
class Address:
|
|
8
|
+
def __init__(self, nmbrs):
|
|
9
|
+
self.nmbrs = nmbrs
|
|
10
|
+
|
|
11
|
+
def get(self,
|
|
12
|
+
created_from: str = None) -> pd.DataFrame:
|
|
13
|
+
addresses = pd.DataFrame()
|
|
14
|
+
for company in self.nmbrs.company_ids:
|
|
15
|
+
addresses = pd.concat([addresses, self._get(company, created_from)])
|
|
16
|
+
|
|
17
|
+
valid_addresses, invalid_addresses = Functions.validate_data(df=addresses, schema=AddressGet, debug=True)
|
|
18
|
+
|
|
19
|
+
return valid_addresses, invalid_addresses
|
|
20
|
+
|
|
21
|
+
def _get(self,
|
|
22
|
+
company_id: str,
|
|
23
|
+
created_from: str = None) -> pd.DataFrame:
|
|
24
|
+
params = {} if created_from is None else {'createdFrom': created_from}
|
|
25
|
+
request = requests.Request(method='GET',
|
|
26
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/addresses",
|
|
27
|
+
params=params)
|
|
28
|
+
|
|
29
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
30
|
+
df = pd.json_normalize(
|
|
31
|
+
data,
|
|
32
|
+
record_path='addresses',
|
|
33
|
+
meta=['employeeId']
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return df
|
|
37
|
+
|
|
38
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
39
|
+
"""
|
|
40
|
+
Create a new address for an employee using Pydantic validation.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
employee_id: The ID of the employee
|
|
44
|
+
data: Dictionary containing address data with fields matching
|
|
45
|
+
the AddressCreate schema (using camelCase field names)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Response from the API
|
|
49
|
+
"""
|
|
50
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
51
|
+
address_model = AddressCreate(**data)
|
|
52
|
+
|
|
53
|
+
if self.nmbrs.mock_mode:
|
|
54
|
+
return address_model
|
|
55
|
+
|
|
56
|
+
# Convert validated model to dict for API payload
|
|
57
|
+
payload = address_model.dict(exclude_none=True)
|
|
58
|
+
|
|
59
|
+
# Send request
|
|
60
|
+
resp = self.nmbrs.session.post(
|
|
61
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/address",
|
|
62
|
+
json=payload,
|
|
63
|
+
timeout=self.nmbrs.timeout
|
|
64
|
+
)
|
|
65
|
+
return resp
|
|
66
|
+
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import requests
|
|
4
|
+
from brynq_sdk_functions import Functions
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
from .schemas.bank import BankGet, BankCreate, BankUpdate, BankDelete
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Bank:
|
|
10
|
+
def __init__(self, nmbrs):
|
|
11
|
+
self.nmbrs = nmbrs
|
|
12
|
+
|
|
13
|
+
def get(self,
|
|
14
|
+
created_from: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
15
|
+
banks = pd.DataFrame()
|
|
16
|
+
for company in self.nmbrs.company_ids:
|
|
17
|
+
banks = pd.concat([banks, self._get(company, created_from)])
|
|
18
|
+
|
|
19
|
+
valid_banks, invalid_banks = Functions.validate_data(df=banks, schema=BankGet, debug=True)
|
|
20
|
+
|
|
21
|
+
return valid_banks, invalid_banks
|
|
22
|
+
|
|
23
|
+
def _get(self,
|
|
24
|
+
company_id: str,
|
|
25
|
+
created_from: str = None) -> pd.DataFrame:
|
|
26
|
+
params = {}
|
|
27
|
+
if created_from:
|
|
28
|
+
params['createdFrom'] = created_from
|
|
29
|
+
try:
|
|
30
|
+
request = requests.Request(method='GET',
|
|
31
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/bankaccounts",
|
|
32
|
+
params=params)
|
|
33
|
+
|
|
34
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
35
|
+
df = pd.json_normalize(
|
|
36
|
+
data,
|
|
37
|
+
record_path='bankAccounts',
|
|
38
|
+
meta=['employeeId']
|
|
39
|
+
)
|
|
40
|
+
except requests.HTTPError as e:
|
|
41
|
+
df = pd.DataFrame()
|
|
42
|
+
|
|
43
|
+
return df
|
|
44
|
+
|
|
45
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
46
|
+
"""
|
|
47
|
+
Create a new bank account for an employee using Pydantic validation.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
employee_id: The ID of the employee
|
|
51
|
+
data: Dictionary containing bank account data in the format matching BankCreate schema
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Response from the API
|
|
55
|
+
"""
|
|
56
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
57
|
+
bank_model = BankCreate(**data)
|
|
58
|
+
|
|
59
|
+
if self.nmbrs.mock_mode:
|
|
60
|
+
return bank_model
|
|
61
|
+
|
|
62
|
+
# Convert validated model to dict for API payload
|
|
63
|
+
payload = bank_model.dict(exclude_none=True)
|
|
64
|
+
|
|
65
|
+
# Send request
|
|
66
|
+
resp = self.nmbrs.session.post(
|
|
67
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/bankaccount",
|
|
68
|
+
json=payload,
|
|
69
|
+
timeout=self.nmbrs.timeout
|
|
70
|
+
)
|
|
71
|
+
return resp
|
|
72
|
+
|
|
73
|
+
def update(self, employee_id: str, data: Dict[str, Any]):
|
|
74
|
+
"""
|
|
75
|
+
Update a bank account for an employee using Pydantic validation.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
employee_id: The ID of the employee
|
|
79
|
+
data: Dictionary containing bank account data in the format matching BankUpdate schema
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Response from the API
|
|
83
|
+
"""
|
|
84
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
85
|
+
bank_model = BankUpdate(**data)
|
|
86
|
+
|
|
87
|
+
if self.nmbrs.mock_mode:
|
|
88
|
+
return bank_model
|
|
89
|
+
|
|
90
|
+
# Convert validated model to dict for API payload
|
|
91
|
+
payload = bank_model.dict(exclude_none=True)
|
|
92
|
+
|
|
93
|
+
# Send request
|
|
94
|
+
resp = self.nmbrs.session.put(
|
|
95
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/bankaccount",
|
|
96
|
+
json=payload,
|
|
97
|
+
timeout=self.nmbrs.timeout
|
|
98
|
+
)
|
|
99
|
+
return resp
|
|
100
|
+
|
|
101
|
+
def delete(self, employee_id: str, bank_account_id: str):
|
|
102
|
+
"""
|
|
103
|
+
Delete a bank account for an employee.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
employee_id: The ID of the employee
|
|
107
|
+
bank_account_id: The ID of the bank account to delete
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Response from the API
|
|
111
|
+
"""
|
|
112
|
+
# Create and validate a BankDelete model
|
|
113
|
+
bank_model = BankDelete(bankAccountId=bank_account_id)
|
|
114
|
+
|
|
115
|
+
if self.nmbrs.mock_mode:
|
|
116
|
+
return bank_model
|
|
117
|
+
|
|
118
|
+
# Send request
|
|
119
|
+
resp = self.nmbrs.session.delete(
|
|
120
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/bankaccounts/{bank_account_id}",
|
|
121
|
+
timeout=self.nmbrs.timeout
|
|
122
|
+
)
|
|
123
|
+
return resp
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
import requests
|
|
3
3
|
import logging
|
|
4
|
-
from .costcenter import Costcenter
|
|
4
|
+
from .costcenter import Costcenter
|
|
5
|
+
from .costunit import Costunit
|
|
5
6
|
from .hours import Hours
|
|
6
7
|
from .bank import Bank
|
|
7
8
|
from .function import Functions
|
|
@@ -23,7 +24,7 @@ class Companies:
|
|
|
23
24
|
request = requests.Request(method='GET',
|
|
24
25
|
url=f"{self.nmbrs.base_url}companies")
|
|
25
26
|
data = self.nmbrs.get_paginated_result(request)
|
|
26
|
-
df =
|
|
27
|
+
df = pd.DataFrame(data)
|
|
27
28
|
|
|
28
29
|
return df
|
|
29
30
|
|
|
@@ -51,10 +52,6 @@ class Companies:
|
|
|
51
52
|
# Convert to DataFrame
|
|
52
53
|
df = pd.DataFrame(serialized_response)
|
|
53
54
|
|
|
54
|
-
# Rename columns to snake_case
|
|
55
|
-
if not df.empty:
|
|
56
|
-
df = self.nmbrs._rename_camel_columns_to_snake_case(df)
|
|
57
|
-
|
|
58
55
|
return df
|
|
59
56
|
else:
|
|
60
57
|
return pd.DataFrame()
|
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
contract_model = ContractCreate(**data)
|
|
62
|
+
|
|
63
|
+
if self.nmbrs.mock_mode:
|
|
64
|
+
return contract_model
|
|
65
|
+
|
|
66
|
+
# Convert validated model to dict for API payload
|
|
67
|
+
payload = contract_model.dict(exclude_none=True)
|
|
68
|
+
|
|
69
|
+
# Send request
|
|
70
|
+
resp = self.nmbrs.session.post(
|
|
71
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/contract",
|
|
72
|
+
json=payload,
|
|
73
|
+
timeout=self.nmbrs.timeout
|
|
74
|
+
)
|
|
75
|
+
return resp
|
|
76
|
+
|
|
77
|
+
def update(self, employee_id: str, data: Dict[str, Any]):
|
|
78
|
+
"""
|
|
79
|
+
Update a contract for an employee using Pydantic validation.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
employee_id: The ID of the employee
|
|
83
|
+
data: Dictionary containing contract data with fields matching
|
|
84
|
+
the ContractUpdate schema (using camelCase field names)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Response from the API
|
|
88
|
+
"""
|
|
89
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
90
|
+
contract_model = ContractUpdate(**data)
|
|
91
|
+
|
|
92
|
+
if self.nmbrs.mock_mode:
|
|
93
|
+
return contract_model
|
|
94
|
+
|
|
95
|
+
# Convert validated model to dict for API payload
|
|
96
|
+
payload = contract_model.dict(exclude_none=True)
|
|
97
|
+
|
|
98
|
+
# Send request
|
|
99
|
+
resp = self.nmbrs.session.put(
|
|
100
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/contract",
|
|
101
|
+
json=payload,
|
|
102
|
+
timeout=self.nmbrs.timeout
|
|
103
|
+
)
|
|
104
|
+
return resp
|
|
105
|
+
|
|
106
|
+
def delete(self, employee_id: str, contract_id: str):
|
|
107
|
+
"""
|
|
108
|
+
Delete a contract for an employee.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
employee_id: The ID of the employee
|
|
112
|
+
contract_id: The ID of the contract to delete
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Response from the API
|
|
116
|
+
"""
|
|
117
|
+
# Create and validate a ContractDelete model
|
|
118
|
+
contract_model = ContractDelete(contractId=contract_id)
|
|
119
|
+
|
|
120
|
+
if self.nmbrs.mock_mode:
|
|
121
|
+
return contract_model
|
|
122
|
+
|
|
123
|
+
# Send request
|
|
124
|
+
resp = self.nmbrs.session.delete(
|
|
125
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/contracts/{contract_id}",
|
|
126
|
+
timeout=self.nmbrs.timeout
|
|
127
|
+
)
|
|
128
|
+
return resp
|