brynq-sdk-nmbrs 2.3.1__py3-none-any.whl → 2.3.2.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brynq_sdk_nmbrs/__init__.py +104 -84
- brynq_sdk_nmbrs/address.py +82 -2
- brynq_sdk_nmbrs/children.py +96 -61
- brynq_sdk_nmbrs/companies.py +45 -1
- brynq_sdk_nmbrs/costcenter.py +53 -8
- brynq_sdk_nmbrs/costunit.py +16 -4
- brynq_sdk_nmbrs/debtors.py +76 -2
- brynq_sdk_nmbrs/department.py +149 -28
- brynq_sdk_nmbrs/document.py +50 -0
- brynq_sdk_nmbrs/employee_wage_tax_settings.py +113 -0
- brynq_sdk_nmbrs/employees.py +119 -24
- brynq_sdk_nmbrs/employment.py +12 -4
- brynq_sdk_nmbrs/function.py +128 -2
- brynq_sdk_nmbrs/leave.py +105 -8
- brynq_sdk_nmbrs/salaries.py +78 -3
- brynq_sdk_nmbrs/schedules.py +77 -3
- brynq_sdk_nmbrs/schemas/address.py +30 -5
- brynq_sdk_nmbrs/schemas/bank.py +0 -2
- brynq_sdk_nmbrs/schemas/children.py +67 -0
- brynq_sdk_nmbrs/schemas/company.py +16 -0
- brynq_sdk_nmbrs/schemas/contracts.py +25 -11
- brynq_sdk_nmbrs/schemas/costcenter.py +57 -18
- brynq_sdk_nmbrs/schemas/costunit.py +0 -2
- brynq_sdk_nmbrs/schemas/days.py +0 -2
- brynq_sdk_nmbrs/schemas/debtor.py +23 -1
- brynq_sdk_nmbrs/schemas/department.py +41 -7
- brynq_sdk_nmbrs/schemas/document.py +13 -0
- brynq_sdk_nmbrs/schemas/employees.py +44 -38
- brynq_sdk_nmbrs/schemas/employment.py +10 -10
- brynq_sdk_nmbrs/schemas/function.py +34 -7
- brynq_sdk_nmbrs/schemas/hours.py +0 -4
- brynq_sdk_nmbrs/schemas/leave.py +12 -1
- brynq_sdk_nmbrs/schemas/manager.py +0 -3
- brynq_sdk_nmbrs/schemas/salary.py +37 -12
- brynq_sdk_nmbrs/schemas/schedules.py +49 -1
- brynq_sdk_nmbrs/schemas/social_insurance.py +39 -6
- brynq_sdk_nmbrs/schemas/wage_tax.py +68 -8
- brynq_sdk_nmbrs/schemas/wage_tax_settings.py +76 -0
- brynq_sdk_nmbrs/schemas/wagecomponents.py +0 -4
- brynq_sdk_nmbrs/social_insurance.py +81 -3
- brynq_sdk_nmbrs/wage_tax.py +105 -4
- {brynq_sdk_nmbrs-2.3.1.dist-info → brynq_sdk_nmbrs-2.3.2.dev0.dist-info}/METADATA +1 -1
- brynq_sdk_nmbrs-2.3.2.dev0.dist-info/RECORD +55 -0
- {brynq_sdk_nmbrs-2.3.1.dist-info → brynq_sdk_nmbrs-2.3.2.dev0.dist-info}/WHEEL +1 -1
- brynq_sdk_nmbrs-2.3.1.dist-info/RECORD +0 -50
- {brynq_sdk_nmbrs-2.3.1.dist-info → brynq_sdk_nmbrs-2.3.2.dev0.dist-info}/top_level.txt +0 -0
brynq_sdk_nmbrs/__init__.py
CHANGED
|
@@ -1,34 +1,39 @@
|
|
|
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 .
|
|
20
|
-
from .
|
|
21
|
+
from .days import FixedDays, VariableDays
|
|
22
|
+
from .debtors import Debtors
|
|
23
|
+
from .department import Departments, EmployeeDepartment
|
|
24
|
+
from .document import EmployeeDocument, 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 .
|
|
28
|
+
from .function import EmployeeFunction, Functions
|
|
29
|
+
from .hours import FixedHours, VariableHours
|
|
30
|
+
from .leave import Leave, LeaveBalance, LeaveGroup
|
|
27
31
|
from .manager import EmployeeManager, Manager
|
|
28
32
|
from .salaries import Salaries
|
|
33
|
+
from .salary_tables import SalaryScales, SalarySteps, SalaryTables
|
|
29
34
|
from .schedules import Schedule
|
|
35
|
+
from .wage_tax import WageTax, WageTaxSettings
|
|
30
36
|
from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
|
|
31
|
-
import os
|
|
32
37
|
|
|
33
38
|
|
|
34
39
|
class Nmbrs(BrynQ):
|
|
@@ -39,21 +44,42 @@ class Nmbrs(BrynQ):
|
|
|
39
44
|
Args:
|
|
40
45
|
label: The label of the system in BrynQ. legacy
|
|
41
46
|
debug: Whether to print debug information
|
|
42
|
-
|
|
47
|
+
mock_mode: If true, data will NOT be sent to Nmbrs but only be tested for validity against Pydantic schemas
|
|
43
48
|
"""
|
|
44
49
|
self.mock_mode = mock_mode
|
|
45
50
|
self.debug = debug
|
|
46
51
|
self.timeout = 3600
|
|
47
52
|
self.system_type = system_type
|
|
53
|
+
|
|
54
|
+
# Initialize classes that work in both mock and real mode
|
|
55
|
+
self.address = Address(self)
|
|
56
|
+
self.bank = Bank(self)
|
|
57
|
+
self.children = Children(self)
|
|
58
|
+
self.contract = Contract(self)
|
|
59
|
+
self.leave = Leave(self)
|
|
60
|
+
self.leave_group = LeaveGroup(self)
|
|
61
|
+
self.function = EmployeeFunction(self)
|
|
62
|
+
self.functions = Functions(self)
|
|
63
|
+
self.employee_document = EmployeeDocument(self)
|
|
64
|
+
self.departments = Departments(self)
|
|
65
|
+
self.debtor = Debtors(self)
|
|
66
|
+
self.companies = Companies(self)
|
|
67
|
+
|
|
48
68
|
if mock_mode is False:
|
|
69
|
+
# Initialize BrynQ parent class for REST API credentials
|
|
49
70
|
super().__init__()
|
|
50
71
|
self.data_interface_id = os.getenv("DATA_INTERFACE_ID")
|
|
72
|
+
|
|
73
|
+
# Get credentials once and store
|
|
74
|
+
self._credentials = self.interfaces.credentials.get(system='nmbrs', system_type=self.system_type)
|
|
75
|
+
self._custom_config = self._credentials.get("custom_data", {})
|
|
76
|
+
|
|
51
77
|
headers = self._get_request_headers()
|
|
52
78
|
self.base_url = "https://api.nmbrsapp.com/api/"
|
|
53
79
|
self.session = requests.Session()
|
|
54
80
|
self.session.headers.update(headers)
|
|
55
81
|
|
|
56
|
-
# Initialize SOAP
|
|
82
|
+
# Initialize SOAP clients
|
|
57
83
|
self.soap_settings = Settings(
|
|
58
84
|
strict=False,
|
|
59
85
|
xml_huge_tree=True,
|
|
@@ -67,99 +93,93 @@ class Nmbrs(BrynQ):
|
|
|
67
93
|
'https://api.nmbrs.nl/soap/v3/EmployeeService.asmx?wsdl',
|
|
68
94
|
settings=self.soap_settings
|
|
69
95
|
)
|
|
70
|
-
self.
|
|
71
|
-
|
|
96
|
+
self.soap_client_debtors = Client(
|
|
97
|
+
'https://api.nmbrs.nl/soap/v3/DebtorService.asmx?wsdl',
|
|
98
|
+
settings=self.soap_settings
|
|
99
|
+
)
|
|
100
|
+
# Create auth headers for each service (namespace must match)
|
|
101
|
+
self.soap_auth_header = self._create_soap_auth_header(self.soap_client_companies)
|
|
102
|
+
self.soap_auth_header_employees = self._create_soap_auth_header(self.soap_client_employees)
|
|
103
|
+
self.soap_auth_header_debtors = self._create_soap_auth_header(self.soap_client_debtors)
|
|
104
|
+
|
|
105
|
+
# REST API dependent classes
|
|
106
|
+
self.employee_department = EmployeeDepartment(self)
|
|
107
|
+
|
|
108
|
+
debtors, _ = self.debtor.get()
|
|
109
|
+
self.debtor_ids = debtors['debtor_id'].to_list()
|
|
110
|
+
self.company_ids = self.companies.get()['companyId'].to_list()
|
|
111
|
+
|
|
112
|
+
# SOAP dependent on REST - companies must be defined first
|
|
72
113
|
if self.soap_auth_header is not None:
|
|
114
|
+
self.employees = Employees(self)
|
|
73
115
|
self.soap_company_ids = self.companies.get_soap_ids()
|
|
116
|
+
self.soap_employee_ids = self.employees.get_soap_ids()
|
|
74
117
|
self.salary_tables = SalaryTables(self)
|
|
75
118
|
self.salary_scales = SalaryScales(self)
|
|
76
119
|
self.salary_steps = SalarySteps(self)
|
|
77
120
|
self.wage_tax = WageTax(self)
|
|
78
121
|
self.absence = Absence(self)
|
|
79
|
-
self.children = Children(self)
|
|
80
|
-
|
|
81
|
-
self.address = Address(self)
|
|
82
|
-
self.bank = Bank(self)
|
|
83
|
-
self.children = Children(self)
|
|
84
|
-
self.debtor = Debtors(self)
|
|
85
|
-
self.companies = Companies(self)
|
|
86
|
-
self.contract = Contract(self)
|
|
87
|
-
self.department = EmployeeDepartment(self)
|
|
88
|
-
debtors, _ = self.debtor.get()
|
|
89
|
-
self.debtor_ids = debtors['debtor_id'].to_list()
|
|
90
|
-
self.company_ids = self.companies.get()['companyId'].to_list()
|
|
91
|
-
self.employees = Employees(self)
|
|
92
|
-
self.employment = Employment(self)
|
|
93
|
-
self.function = EmployeeFunction(self)
|
|
94
|
-
self.fixed_hours = FixedHours(self)
|
|
95
|
-
self.fixed_days = FixedDays(self)
|
|
96
|
-
self.variable_hours = VariableHours(self)
|
|
97
|
-
self.variable_days = VariableDays(self)
|
|
98
|
-
self.manager = Manager(self)
|
|
99
|
-
self.employee_manager = EmployeeManager(self)
|
|
100
|
-
self.salaries = Salaries(self)
|
|
101
|
-
self.schedule = Schedule(self)
|
|
102
|
-
self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
|
|
103
|
-
self.variable_wagecomponents = EmployeeVariableWageComponents(self)
|
|
104
|
-
self.current_period = self.companies.get_current_period()
|
|
105
122
|
|
|
123
|
+
self.employees = Employees(self)
|
|
124
|
+
self.employment = Employment(self)
|
|
125
|
+
self.wage_tax_settings = WageTaxSettings(self)
|
|
126
|
+
self.fixed_hours = FixedHours(self)
|
|
127
|
+
self.fixed_days = FixedDays(self)
|
|
128
|
+
self.variable_hours = VariableHours(self)
|
|
129
|
+
self.variable_days = VariableDays(self)
|
|
130
|
+
self.manager = Manager(self)
|
|
131
|
+
self.employee_manager = EmployeeManager(self)
|
|
132
|
+
self.salaries = Salaries(self)
|
|
133
|
+
self.schedule = Schedule(self)
|
|
134
|
+
self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
|
|
135
|
+
self.variable_wagecomponents = EmployeeVariableWageComponents(self)
|
|
136
|
+
self.current_period = self.companies.get_current_period()
|
|
137
|
+
self.cost_center = Costcenter(self)
|
|
138
|
+
self.employee_cost_center = EmployeeCostcenter(self)
|
|
139
|
+
self.cost_unit = Costunit(self)
|
|
140
|
+
self.employee_wage_tax_settings = EmployeeWageTaxSettings(self)
|
|
106
141
|
def _get_request_headers(self):
|
|
107
|
-
|
|
142
|
+
access_token = self._credentials.get('data').get('access_token')
|
|
143
|
+
subscription_key = self._custom_config.get("subscription_key")
|
|
108
144
|
headers = {
|
|
109
145
|
"accept": "application/json",
|
|
110
|
-
"Authorization": f"Bearer {
|
|
146
|
+
"Authorization": f"Bearer {access_token.strip() if access_token else ''}",
|
|
111
147
|
# partner identifier
|
|
112
|
-
"X-Subscription-Key":
|
|
148
|
+
"X-Subscription-Key": subscription_key.strip() if subscription_key else ''
|
|
113
149
|
}
|
|
114
150
|
|
|
115
151
|
return headers
|
|
116
152
|
|
|
117
|
-
def
|
|
118
|
-
"""
|
|
119
|
-
Creates the SOAP authentication header using credentials from initial_credentials.
|
|
120
|
-
|
|
121
|
-
Returns:
|
|
122
|
-
AuthHeaderWithDomainType: The authentication header for SOAP requests
|
|
153
|
+
def _create_soap_auth_header(self, soap_client):
|
|
123
154
|
"""
|
|
124
|
-
|
|
125
|
-
|
|
155
|
+
Creates SOAP authentication header for a specific SOAP client.
|
|
156
|
+
Each service needs its own auth header with matching namespace.
|
|
126
157
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
else:
|
|
130
|
-
# Get the AuthHeaderWithDomain type from the WSDL
|
|
131
|
-
AuthHeaderWithDomainType = self.soap_client_companies.get_element('ns0:AuthHeaderWithDomain')
|
|
132
|
-
|
|
133
|
-
# Create the auth header using credentials from config
|
|
134
|
-
auth_header = AuthHeaderWithDomainType(
|
|
135
|
-
Username=config.get("soap_api_username"),
|
|
136
|
-
Token=config.get("soap_api_token"),
|
|
137
|
-
Domain=config.get("soap_api_domain")
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
return auth_header
|
|
141
|
-
|
|
142
|
-
def _get_soap_auth_header_employees(self):
|
|
143
|
-
"""
|
|
144
|
-
Creates the SOAP authentication header using credentials from initial_credentials.
|
|
158
|
+
Args:
|
|
159
|
+
soap_client: The zeep SOAP client to create auth header for
|
|
145
160
|
|
|
146
161
|
Returns:
|
|
147
|
-
AuthHeaderWithDomainType: The authentication header for SOAP requests
|
|
162
|
+
AuthHeaderWithDomainType: The authentication header for SOAP requests,
|
|
163
|
+
or None if SOAP credentials are not configured.
|
|
148
164
|
"""
|
|
149
|
-
|
|
150
|
-
|
|
165
|
+
if 'soap_api_token' not in self._custom_config.keys():
|
|
166
|
+
return None
|
|
151
167
|
|
|
152
|
-
|
|
153
|
-
AuthHeaderWithDomainType = self.soap_client_employees.get_element('ns0:AuthHeaderWithDomain')
|
|
168
|
+
AuthHeaderWithDomainType = soap_client.get_element('ns0:AuthHeaderWithDomain')
|
|
154
169
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Domain=config.get("soap_api_domain")
|
|
170
|
+
return AuthHeaderWithDomainType(
|
|
171
|
+
Username=self._custom_config.get("soap_api_username"),
|
|
172
|
+
Token=self._custom_config.get("soap_api_token"),
|
|
173
|
+
Domain=self._custom_config.get("soap_api_domain")
|
|
160
174
|
)
|
|
161
175
|
|
|
162
|
-
|
|
176
|
+
def _get_soap_auth_header(self):
|
|
177
|
+
"""Legacy method - use _create_soap_auth_header instead."""
|
|
178
|
+
return self._create_soap_auth_header(self.soap_client_companies)
|
|
179
|
+
|
|
180
|
+
def _get_soap_auth_header_employees(self):
|
|
181
|
+
"""Legacy method - use soap_auth_header_employees instead."""
|
|
182
|
+
return self.soap_auth_header_employees
|
|
163
183
|
|
|
164
184
|
def get_paginated_result(self, request: requests.Request) -> List:
|
|
165
185
|
has_next_page = True
|
brynq_sdk_nmbrs/address.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
import requests
|
|
3
|
-
from typing import Dict, Any
|
|
4
|
-
from .schemas.address import AddressCreate, AddressGet, Period
|
|
3
|
+
from typing import Dict, Any, Union
|
|
4
|
+
from .schemas.address import AddressCreate, AddressGet, AddressDelete, AddressUpdate, Period
|
|
5
5
|
from brynq_sdk_functions import Functions
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
class Address:
|
|
8
9
|
def __init__(self, nmbrs):
|
|
9
10
|
self.nmbrs = nmbrs
|
|
@@ -64,3 +65,82 @@ class Address:
|
|
|
64
65
|
timeout=self.nmbrs.timeout
|
|
65
66
|
)
|
|
66
67
|
return resp
|
|
68
|
+
|
|
69
|
+
def delete(self, employee_id: Union[int, str], address_id: Union[int, str]) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Delete an address for an employee using SOAP API.
|
|
72
|
+
|
|
73
|
+
REST API does not support address deletion, so we use SOAP as interim solution.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
employee_id: The ID of the employee
|
|
77
|
+
address_id: The ID of the address to delete
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
bool: True if deletion was successful
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
Exception: If SOAP client is not available or deletion fails
|
|
84
|
+
"""
|
|
85
|
+
# Validate input using Pydantic schema
|
|
86
|
+
delete_model = AddressDelete(employeeId=int(employee_id), addressId=int(address_id))
|
|
87
|
+
|
|
88
|
+
if self.nmbrs.mock_mode:
|
|
89
|
+
return delete_model
|
|
90
|
+
|
|
91
|
+
# Call SOAP Address_Delete using EmployeeService auth header
|
|
92
|
+
resp = self.nmbrs.soap_client_employees.service.Address_Delete(
|
|
93
|
+
EmployeeId=delete_model.employee_id,
|
|
94
|
+
AddressID=delete_model.address_id,
|
|
95
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return resp
|
|
99
|
+
|
|
100
|
+
def update(self, employee_id: Union[int, str], data: Dict[str, Any]):
|
|
101
|
+
"""
|
|
102
|
+
Update an address for an employee using SOAP API.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
employee_id: The ID of the employee
|
|
106
|
+
data: Dictionary containing address data with fields matching AddressUpdate schema:
|
|
107
|
+
- id: Address ID to update
|
|
108
|
+
- street: Street name
|
|
109
|
+
- house_number: House number (optional)
|
|
110
|
+
- house_number_addition: House number addition (optional)
|
|
111
|
+
- postal_code: Postal code (optional)
|
|
112
|
+
- city: City name
|
|
113
|
+
- state_province: State or province (optional)
|
|
114
|
+
- country_iso_code: Country ISO code (e.g., "NL")
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Response from the API
|
|
118
|
+
"""
|
|
119
|
+
# Validate with Pydantic model
|
|
120
|
+
address_model = AddressUpdate(**data)
|
|
121
|
+
|
|
122
|
+
if self.nmbrs.mock_mode:
|
|
123
|
+
return address_model
|
|
124
|
+
|
|
125
|
+
# Create EmployeeAddress type
|
|
126
|
+
AddressType = self.nmbrs.soap_client_employees.get_type('ns0:EmployeeAddress')
|
|
127
|
+
soap_address = AddressType(
|
|
128
|
+
Id=address_model.id,
|
|
129
|
+
Default=address_model.default,
|
|
130
|
+
Street=address_model.street,
|
|
131
|
+
HouseNumber=address_model.house_number or '',
|
|
132
|
+
HouseNumberAddition=address_model.house_number_addition or '',
|
|
133
|
+
PostalCode=address_model.postal_code or '',
|
|
134
|
+
City=address_model.city,
|
|
135
|
+
StateProvince=address_model.state_province or '',
|
|
136
|
+
CountryISOCode=address_model.country_iso_code,
|
|
137
|
+
Type=address_model.address_type
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Call SOAP Address_Update
|
|
141
|
+
result = self.nmbrs.soap_client_employees.service.Address_Update(
|
|
142
|
+
EmployeeId=int(employee_id),
|
|
143
|
+
Address=soap_address,
|
|
144
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
145
|
+
)
|
|
146
|
+
return result
|
brynq_sdk_nmbrs/children.py
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Union, Dict, Any
|
|
2
2
|
|
|
3
|
-
from zeep import Client
|
|
4
|
-
from zeep.transports import Transport
|
|
5
3
|
import requests
|
|
6
4
|
import pandas as pd
|
|
7
5
|
|
|
6
|
+
from .schemas.children import ChildCreate, ChildDelete, ChildUpdate
|
|
7
|
+
|
|
8
8
|
|
|
9
9
|
class Children:
|
|
10
10
|
def __init__(self, nmbrs):
|
|
11
11
|
self.nmbrs = nmbrs
|
|
12
|
-
self.client = Client(wsdl='https://api.nmbrs.nl/soap/v3/EmployeeService.asmx?wsdl') #, transport=Transport(session=self.nmbrs.session))
|
|
13
|
-
# self.client.set_default_soapheaders([auth_header])
|
|
14
|
-
AuthHeaderWithDomainType = self.client.get_element('ns0:AuthHeaderWithDomain')
|
|
15
|
-
|
|
16
|
-
auth_header = AuthHeaderWithDomainType(
|
|
17
|
-
Username="erwin.vink@brynq.com",
|
|
18
|
-
Token="cc358715f5c14cda8add964deef99ba3",
|
|
19
|
-
Domain="extdev-brynq"
|
|
20
|
-
)
|
|
21
|
-
self.client.set_default_soapheaders([auth_header])
|
|
22
12
|
|
|
23
13
|
def get(self,
|
|
24
14
|
company_id: str,
|
|
@@ -42,59 +32,104 @@ class Children:
|
|
|
42
32
|
|
|
43
33
|
return df
|
|
44
34
|
|
|
35
|
+
def create(self, employee_id: Union[int, str], data: Dict[str, Any]):
|
|
36
|
+
"""
|
|
37
|
+
Create a new child for an employee using SOAP API.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
employee_id: The ID of the employee
|
|
41
|
+
data: Dictionary containing child data with fields matching ChildCreate schema
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Response from the API (child ID)
|
|
45
|
+
"""
|
|
46
|
+
# Validate with Pydantic model
|
|
47
|
+
child_model = ChildCreate(**data)
|
|
48
|
+
|
|
49
|
+
if self.nmbrs.mock_mode:
|
|
50
|
+
return child_model
|
|
51
|
+
|
|
52
|
+
ChildType = self.nmbrs.soap_client_employees.get_type('ns0:Child')
|
|
53
|
+
child = ChildType(
|
|
54
|
+
Id=0, # Use 0 for new child
|
|
55
|
+
Name=child_model.name,
|
|
56
|
+
FirstName=child_model.first_name,
|
|
57
|
+
Initials=child_model.initials or '',
|
|
58
|
+
Gender=child_model.gender,
|
|
59
|
+
Birthday=child_model.birthday
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Make the API call
|
|
63
|
+
result = self.nmbrs.soap_client_employees.service.Children_Insert(
|
|
64
|
+
EmployeeId=int(employee_id),
|
|
65
|
+
child=child,
|
|
66
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
67
|
+
)
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
def delete(self, employee_id: Union[int, str], child_id: Union[int, str]):
|
|
71
|
+
"""
|
|
72
|
+
Delete a child for an employee using SOAP API.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
employee_id: The ID of the employee
|
|
76
|
+
child_id: The ID of the child to delete
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Response from the API
|
|
80
|
+
"""
|
|
81
|
+
# Validate with Pydantic model
|
|
82
|
+
delete_model = ChildDelete(employeeId=int(employee_id), childId=int(child_id))
|
|
83
|
+
|
|
84
|
+
if self.nmbrs.mock_mode:
|
|
85
|
+
return delete_model
|
|
45
86
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
87
|
+
# Call SOAP Child_Delete
|
|
88
|
+
resp = self.nmbrs.soap_client_employees.service.Child_Delete(
|
|
89
|
+
EmployeeId=delete_model.employee_id,
|
|
90
|
+
ChildId=delete_model.child_id,
|
|
91
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
92
|
+
)
|
|
93
|
+
return resp
|
|
94
|
+
|
|
95
|
+
def update(self, employee_id: Union[int, str], data: Dict[str, Any]):
|
|
96
|
+
"""
|
|
97
|
+
Update a child for an employee using SOAP API.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
employee_id: The ID of the employee
|
|
101
|
+
data: Dictionary containing child data with fields matching ChildUpdate schema:
|
|
102
|
+
- id: Child ID to update
|
|
103
|
+
- name: Last name
|
|
104
|
+
- first_name: First name
|
|
105
|
+
- initials: Initials (optional)
|
|
106
|
+
- gender: Gender (male/female/unknown/undefined)
|
|
107
|
+
- birthday: Birthday
|
|
49
108
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
109
|
+
Returns:
|
|
110
|
+
Response from the API
|
|
111
|
+
"""
|
|
112
|
+
# Validate with Pydantic model
|
|
113
|
+
child_model = ChildUpdate(**data)
|
|
53
114
|
|
|
54
|
-
|
|
115
|
+
if self.nmbrs.mock_mode:
|
|
116
|
+
return child_model
|
|
117
|
+
|
|
118
|
+
ChildType = self.nmbrs.soap_client_employees.get_type('ns0:Child')
|
|
55
119
|
child = ChildType(
|
|
56
|
-
Id=
|
|
57
|
-
Name=
|
|
58
|
-
FirstName=
|
|
59
|
-
Initials=
|
|
60
|
-
Gender=
|
|
61
|
-
Birthday=
|
|
120
|
+
Id=child_model.id,
|
|
121
|
+
Name=child_model.name,
|
|
122
|
+
FirstName=child_model.first_name,
|
|
123
|
+
Initials=child_model.initials or '',
|
|
124
|
+
Gender=child_model.gender,
|
|
125
|
+
Birthday=child_model.birthday
|
|
62
126
|
)
|
|
63
127
|
|
|
64
128
|
# Make the API call
|
|
65
|
-
result = self.
|
|
66
|
-
EmployeeId=employee_id,
|
|
67
|
-
child=child
|
|
129
|
+
result = self.nmbrs.soap_client_employees.service.Children_Update(
|
|
130
|
+
EmployeeId=int(employee_id),
|
|
131
|
+
child=child,
|
|
132
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header_employees}
|
|
68
133
|
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:emp="https://api.nmbrs.nl/soap/v3/EmployeeService">
|
|
73
|
-
# <soap:Header>
|
|
74
|
-
# <emp:AuthHeaderWithDomain>
|
|
75
|
-
# <!--Optional:-->
|
|
76
|
-
# <emp:Username>erwin.vink@brynq.com</emp:Username>
|
|
77
|
-
# <!--Optional:-->
|
|
78
|
-
# <emp:Token>cc358715f5c14cda8add964deef99ba3</emp:Token>
|
|
79
|
-
# <!--Optional:-->
|
|
80
|
-
# <emp:Domain>extdev-brynq</emp:Domain>
|
|
81
|
-
# </emp:AuthHeaderWithDomain>
|
|
82
|
-
# </soap:Header>
|
|
83
|
-
# <soap:Body>
|
|
84
|
-
# <emp:Children_Insert>
|
|
85
|
-
# <emp:EmployeeId>11</emp:EmployeeId>
|
|
86
|
-
# <!--Optional:-->
|
|
87
|
-
# <emp:child>
|
|
88
|
-
# <emp:Id>1</emp:Id>
|
|
89
|
-
# <!--Optional:-->
|
|
90
|
-
# <emp:Name>Doe</emp:Name>
|
|
91
|
-
# <!--Optional:-->
|
|
92
|
-
# <emp:FirstName>John</emp:FirstName>
|
|
93
|
-
# <!--Optional:-->
|
|
94
|
-
# <emp:Initials>J.</emp:Initials>
|
|
95
|
-
# <emp:Gender>male</emp:Gender>
|
|
96
|
-
# <emp:Birthday>2020-01-01T00:00:00</emp:Birthday>
|
|
97
|
-
# </emp:child>
|
|
98
|
-
# </emp:Children_Insert>
|
|
99
|
-
# </soap:Body>
|
|
100
|
-
# </soap:Envelope>
|
|
134
|
+
return result
|
|
135
|
+
|
brynq_sdk_nmbrs/companies.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
import requests
|
|
3
3
|
import logging
|
|
4
|
+
from typing import Dict, Any
|
|
4
5
|
|
|
5
6
|
from .leave import LeaveGroup
|
|
6
7
|
from .costcenter import Costcenter
|
|
@@ -8,6 +9,7 @@ from .costunit import Costunit
|
|
|
8
9
|
from .hours import Hours
|
|
9
10
|
from .bank import Bank
|
|
10
11
|
from .function import Functions
|
|
12
|
+
from .schemas.company import CompanyCreate
|
|
11
13
|
from zeep.exceptions import Fault
|
|
12
14
|
from zeep.helpers import serialize_object
|
|
13
15
|
|
|
@@ -19,10 +21,13 @@ class Companies:
|
|
|
19
21
|
self.costunits = Costunit(nmbrs)
|
|
20
22
|
self.hours = Hours(nmbrs)
|
|
21
23
|
self.banks = Bank(nmbrs)
|
|
22
|
-
self.soap_client_companies = nmbrs.soap_client_companies
|
|
23
24
|
self.logger = logging.getLogger(__name__)
|
|
24
25
|
self.leave_groups = LeaveGroup(nmbrs)
|
|
25
26
|
|
|
27
|
+
@property
|
|
28
|
+
def soap_client_companies(self):
|
|
29
|
+
return self.nmbrs.soap_client_companies
|
|
30
|
+
|
|
26
31
|
def get(self) -> pd.DataFrame:
|
|
27
32
|
try:
|
|
28
33
|
request = requests.Request(method='GET',
|
|
@@ -91,3 +96,42 @@ class Companies:
|
|
|
91
96
|
except Exception as e:
|
|
92
97
|
self.logger.exception("Exception occurred:")
|
|
93
98
|
raise Exception(f"Failed to get current period: {str(e)}")
|
|
99
|
+
|
|
100
|
+
def create(self, data: Dict[str, Any]) -> int:
|
|
101
|
+
"""
|
|
102
|
+
Create a new company using SOAP API.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
data: Dictionary containing company data with fields matching CompanyCreate schema:
|
|
106
|
+
- debtor_id: Debtor ID
|
|
107
|
+
- company_name: Company name
|
|
108
|
+
- period_type: Period type (1=Monthly, 2=4-Weekly, 3=Weekly, 4=Quarterly)
|
|
109
|
+
- default_company_id: Default company ID to copy settings from (0 for none)
|
|
110
|
+
- labour_agreement_settings_group_guid: Labour agreement settings group GUID
|
|
111
|
+
- pay_in_advance: Pay in advance
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
The ID of the newly created company.
|
|
115
|
+
"""
|
|
116
|
+
company_model = CompanyCreate(**data)
|
|
117
|
+
|
|
118
|
+
if self.nmbrs.mock_mode:
|
|
119
|
+
return 12345 # Mock ID
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
response = self.soap_client_companies.service.Company_Insert(
|
|
123
|
+
DebtorId=company_model.debtor_id,
|
|
124
|
+
CompanyName=company_model.company_name,
|
|
125
|
+
PeriodType=company_model.period_type,
|
|
126
|
+
DefaultCompanyId=company_model.default_company_id,
|
|
127
|
+
LabourAgreementSettingsGroupGuid=company_model.labour_agreement_settings_group_guid or "00000000-0000-0000-0000-000000000000",
|
|
128
|
+
PayInAdvance=company_model.pay_in_advance,
|
|
129
|
+
_soapheaders={'AuthHeaderWithDomain': self.nmbrs.soap_auth_header}
|
|
130
|
+
)
|
|
131
|
+
return response
|
|
132
|
+
|
|
133
|
+
except Fault as e:
|
|
134
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
135
|
+
except Exception as e:
|
|
136
|
+
self.logger.exception("Exception occurred:")
|
|
137
|
+
raise Exception(f"Failed to create company: {str(e)}")
|