brynq-sdk-nmbrs 2.3.1.dev0__tar.gz → 2.3.3.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 → brynq_sdk_nmbrs-2.3.3.dev0}/PKG-INFO +1 -1
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/__init__.py +86 -80
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/address.py +146 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/bank.py +5 -2
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/children.py +135 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/companies.py +45 -1
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/debtors.py +99 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/department.py +113 -1
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/document.py +80 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/employee_wage_tax_settings.py +16 -9
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/function.py +215 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/leave.py +105 -8
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/salaries.py +75 -3
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schedules.py +158 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/address.py +33 -4
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/children.py +67 -0
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/company.py +16 -0
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/debtor.py +38 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/department.py +33 -0
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/document.py +13 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/employees.py +3 -1
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/function.py +28 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/leave.py +12 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/salary.py +18 -0
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/schedules.py +160 -0
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/social_insurance.py +73 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/wage_tax.py +65 -5
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/wage_tax_settings.py +76 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/wagecomponents.py +66 -9
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/social_insurance.py +130 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/wage_tax.py +104 -3
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/wagecomponents.py +81 -45
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs.egg-info/PKG-INFO +1 -1
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs.egg-info/SOURCES.txt +3 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/setup.py +1 -1
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/address.py +0 -66
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/children.py +0 -100
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/debtors.py +0 -25
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/document.py +0 -30
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/function.py +0 -89
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/schedules.py +0 -84
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/schemas/debtor.py +0 -16
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/schemas/schedules.py +0 -95
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/schemas/social_insurance.py +0 -40
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/schemas/wage_tax_settings.py +0 -63
- brynq_sdk_nmbrs-2.3.1.dev0/brynq_sdk_nmbrs/social_insurance.py +0 -52
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/absence.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/contract.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/costcenter.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/costunit.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/days.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/employees.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/employment.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/hours.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/manager.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/salary_tables.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/__init__.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/absence.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/bank.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/contracts.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/costcenter.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/costunit.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/days.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/employment.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/hours.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs/schemas/manager.py +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs.egg-info/not-zip-safe +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs.egg-info/requires.txt +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/brynq_sdk_nmbrs.egg-info/top_level.txt +0 -0
- {brynq_sdk_nmbrs-2.3.1.dev0 → brynq_sdk_nmbrs-2.3.3.dev0}/setup.cfg +0 -0
|
@@ -21,17 +21,18 @@ from .costunit import Costunit
|
|
|
21
21
|
from .days import FixedDays, VariableDays
|
|
22
22
|
from .debtors import Debtors
|
|
23
23
|
from .department import Departments, EmployeeDepartment
|
|
24
|
-
from .document import Payslip
|
|
24
|
+
from .document import EmployeeDocument, Payslip
|
|
25
25
|
from .employee_wage_tax_settings import EmployeeWageTaxSettings
|
|
26
26
|
from .employees import Employees
|
|
27
27
|
from .employment import Employment
|
|
28
28
|
from .function import EmployeeFunction, Functions
|
|
29
29
|
from .hours import FixedHours, VariableHours
|
|
30
|
+
from .leave import Leave, LeaveBalance, LeaveGroup
|
|
30
31
|
from .manager import EmployeeManager, Manager
|
|
31
32
|
from .salaries import Salaries
|
|
32
33
|
from .salary_tables import SalaryScales, SalarySteps, SalaryTables
|
|
33
34
|
from .schedules import Schedule
|
|
34
|
-
from .wage_tax import WageTax
|
|
35
|
+
from .wage_tax import WageTax, WageTaxSettings
|
|
35
36
|
from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
|
|
36
37
|
|
|
37
38
|
|
|
@@ -43,21 +44,42 @@ class Nmbrs(BrynQ):
|
|
|
43
44
|
Args:
|
|
44
45
|
label: The label of the system in BrynQ. legacy
|
|
45
46
|
debug: Whether to print debug information
|
|
46
|
-
|
|
47
|
+
mock_mode: If true, data will NOT be sent to Nmbrs but only be tested for validity against Pydantic schemas
|
|
47
48
|
"""
|
|
48
49
|
self.mock_mode = mock_mode
|
|
49
50
|
self.debug = debug
|
|
50
51
|
self.timeout = 3600
|
|
51
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
|
+
|
|
52
68
|
if mock_mode is False:
|
|
69
|
+
# Initialize BrynQ parent class for REST API credentials
|
|
53
70
|
super().__init__()
|
|
54
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
|
+
|
|
55
77
|
headers = self._get_request_headers()
|
|
56
78
|
self.base_url = "https://api.nmbrsapp.com/api/"
|
|
57
79
|
self.session = requests.Session()
|
|
58
80
|
self.session.headers.update(headers)
|
|
59
81
|
|
|
60
|
-
# Initialize SOAP
|
|
82
|
+
# Initialize SOAP clients
|
|
61
83
|
self.soap_settings = Settings(
|
|
62
84
|
strict=False,
|
|
63
85
|
xml_huge_tree=True,
|
|
@@ -71,11 +93,24 @@ class Nmbrs(BrynQ):
|
|
|
71
93
|
'https://api.nmbrs.nl/soap/v3/EmployeeService.asmx?wsdl',
|
|
72
94
|
settings=self.soap_settings
|
|
73
95
|
)
|
|
74
|
-
self.
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
77
113
|
if self.soap_auth_header is not None:
|
|
78
|
-
self.companies = Companies(self)
|
|
79
114
|
self.employees = Employees(self)
|
|
80
115
|
self.soap_company_ids = self.companies.get_soap_ids()
|
|
81
116
|
self.soap_employee_ids = self.employees.get_soap_ids()
|
|
@@ -84,96 +119,67 @@ class Nmbrs(BrynQ):
|
|
|
84
119
|
self.salary_steps = SalarySteps(self)
|
|
85
120
|
self.wage_tax = WageTax(self)
|
|
86
121
|
self.absence = Absence(self)
|
|
87
|
-
self.children = Children(self)
|
|
88
|
-
|
|
89
|
-
self.address = Address(self)
|
|
90
|
-
self.bank = Bank(self)
|
|
91
|
-
self.children = Children(self)
|
|
92
|
-
self.debtor = Debtors(self)
|
|
93
|
-
self.companies = Companies(self)
|
|
94
|
-
self.contract = Contract(self)
|
|
95
|
-
self.employee_department = EmployeeDepartment(self)
|
|
96
|
-
self.department = Departments(self)
|
|
97
|
-
debtors, _ = self.debtor.get()
|
|
98
|
-
self.debtor_ids = debtors['debtor_id'].to_list()
|
|
99
|
-
self.company_ids = self.companies.get()['companyId'].to_list()
|
|
100
|
-
self.employees = Employees(self)
|
|
101
|
-
self.employment = Employment(self)
|
|
102
|
-
self.employee_function = EmployeeFunction(self)
|
|
103
|
-
self.functions = Functions(self)
|
|
104
|
-
self.fixed_hours = FixedHours(self)
|
|
105
|
-
self.fixed_days = FixedDays(self)
|
|
106
|
-
self.variable_hours = VariableHours(self)
|
|
107
|
-
self.variable_days = VariableDays(self)
|
|
108
|
-
self.manager = Manager(self)
|
|
109
|
-
self.employee_manager = EmployeeManager(self)
|
|
110
|
-
self.salaries = Salaries(self)
|
|
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)
|
|
116
|
-
self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
|
|
117
|
-
self.variable_wagecomponents = EmployeeVariableWageComponents(self)
|
|
118
|
-
self.current_period = self.companies.get_current_period()
|
|
119
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)
|
|
120
141
|
def _get_request_headers(self):
|
|
121
|
-
|
|
142
|
+
access_token = self._credentials.get('data').get('access_token')
|
|
143
|
+
subscription_key = self._custom_config.get("subscription_key")
|
|
122
144
|
headers = {
|
|
123
145
|
"accept": "application/json",
|
|
124
|
-
"Authorization": f"Bearer {
|
|
146
|
+
"Authorization": f"Bearer {access_token.strip() if access_token else ''}",
|
|
125
147
|
# partner identifier
|
|
126
|
-
"X-Subscription-Key":
|
|
148
|
+
"X-Subscription-Key": subscription_key.strip() if subscription_key else ''
|
|
127
149
|
}
|
|
128
150
|
|
|
129
151
|
return headers
|
|
130
152
|
|
|
131
|
-
def
|
|
153
|
+
def _create_soap_auth_header(self, soap_client):
|
|
132
154
|
"""
|
|
133
|
-
Creates
|
|
134
|
-
|
|
135
|
-
Returns:
|
|
136
|
-
AuthHeaderWithDomainType: The authentication header for SOAP requests
|
|
137
|
-
"""
|
|
138
|
-
initial_credentials = self.interfaces.credentials.get(system='nmbrs', system_type=self.system_type)
|
|
139
|
-
config = initial_credentials.get("custom_data", {})
|
|
140
|
-
|
|
141
|
-
if 'soap_api_token' not in config.keys():
|
|
142
|
-
return None
|
|
143
|
-
else:
|
|
144
|
-
# Get the AuthHeaderWithDomain type from the WSDL
|
|
145
|
-
AuthHeaderWithDomainType = self.soap_client_companies.get_element('ns0:AuthHeaderWithDomain')
|
|
146
|
-
|
|
147
|
-
# Create the auth header using credentials from config
|
|
148
|
-
auth_header = AuthHeaderWithDomainType(
|
|
149
|
-
Username=config.get("soap_api_username"),
|
|
150
|
-
Token=config.get("soap_api_token"),
|
|
151
|
-
Domain=config.get("soap_api_domain")
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
return auth_header
|
|
155
|
+
Creates SOAP authentication header for a specific SOAP client.
|
|
156
|
+
Each service needs its own auth header with matching namespace.
|
|
155
157
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
Creates the SOAP authentication header using credentials from initial_credentials.
|
|
158
|
+
Args:
|
|
159
|
+
soap_client: The zeep SOAP client to create auth header for
|
|
159
160
|
|
|
160
161
|
Returns:
|
|
161
|
-
AuthHeaderWithDomainType: The authentication header for SOAP requests
|
|
162
|
+
AuthHeaderWithDomainType: The authentication header for SOAP requests,
|
|
163
|
+
or None if SOAP credentials are not configured.
|
|
162
164
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
if 'soap_api_token' not in self._custom_config.keys():
|
|
166
|
+
return None
|
|
165
167
|
|
|
166
|
-
|
|
167
|
-
AuthHeaderWithDomainType = self.soap_client_employees.get_element('ns0:AuthHeaderWithDomain')
|
|
168
|
+
AuthHeaderWithDomainType = soap_client.get_element('ns0:AuthHeaderWithDomain')
|
|
168
169
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Domain=custom_data.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")
|
|
174
174
|
)
|
|
175
175
|
|
|
176
|
-
|
|
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
|
|
177
183
|
|
|
178
184
|
def get_paginated_result(self, request: requests.Request) -> List:
|
|
179
185
|
has_next_page = True
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
from typing import Dict, Any, Union
|
|
4
|
+
from .schemas.address import AddressCreate, AddressGet, AddressDelete, AddressUpdate, Period
|
|
5
|
+
from brynq_sdk_functions import Functions
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Address:
|
|
9
|
+
def __init__(self, nmbrs):
|
|
10
|
+
self.nmbrs = nmbrs
|
|
11
|
+
|
|
12
|
+
def get(self,
|
|
13
|
+
created_from: str = None) -> pd.DataFrame:
|
|
14
|
+
addresses = pd.DataFrame()
|
|
15
|
+
for company in self.nmbrs.company_ids:
|
|
16
|
+
addresses = pd.concat([addresses, self._get(company, created_from)])
|
|
17
|
+
|
|
18
|
+
valid_addresses, invalid_addresses = Functions.validate_data(df=addresses, schema=AddressGet, debug=True)
|
|
19
|
+
|
|
20
|
+
return valid_addresses, invalid_addresses
|
|
21
|
+
|
|
22
|
+
def _get(self,
|
|
23
|
+
company_id: str,
|
|
24
|
+
created_from: str = None) -> pd.DataFrame:
|
|
25
|
+
params = {} if created_from is None else {'createdFrom': created_from}
|
|
26
|
+
request = requests.Request(method='GET',
|
|
27
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/addresses",
|
|
28
|
+
params=params)
|
|
29
|
+
|
|
30
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
31
|
+
df = pd.json_normalize(
|
|
32
|
+
data,
|
|
33
|
+
record_path='addresses',
|
|
34
|
+
meta=['employeeId']
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return df
|
|
38
|
+
|
|
39
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
40
|
+
"""
|
|
41
|
+
Create a new address for an employee using Pydantic validation.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
employee_id: The ID of the employee
|
|
45
|
+
data: Dictionary containing address data with fields matching
|
|
46
|
+
the AddressCreate schema (using camelCase field names)
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Response from the API
|
|
50
|
+
"""
|
|
51
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
52
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, AddressCreate)
|
|
53
|
+
address_model = AddressCreate(**nested_data)
|
|
54
|
+
|
|
55
|
+
if self.nmbrs.mock_mode:
|
|
56
|
+
return address_model
|
|
57
|
+
|
|
58
|
+
# Convert validated model to dict for API payload
|
|
59
|
+
payload = address_model.model_dump(exclude_none=True, by_alias=True)
|
|
60
|
+
|
|
61
|
+
# Send request
|
|
62
|
+
resp = self.nmbrs.session.post(
|
|
63
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/address",
|
|
64
|
+
json=payload,
|
|
65
|
+
timeout=self.nmbrs.timeout
|
|
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
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
2
4
|
import pandas as pd
|
|
3
5
|
import requests
|
|
6
|
+
|
|
4
7
|
from brynq_sdk_functions import Functions
|
|
5
|
-
|
|
6
|
-
from .schemas.bank import
|
|
8
|
+
|
|
9
|
+
from .schemas.bank import BankCreate, BankDelete, BankGet, BankUpdate
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
class Bank:
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from typing import Union, Dict, Any
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from .schemas.children import ChildCreate, ChildDelete, ChildUpdate
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Children:
|
|
10
|
+
def __init__(self, nmbrs):
|
|
11
|
+
self.nmbrs = nmbrs
|
|
12
|
+
|
|
13
|
+
def get(self,
|
|
14
|
+
company_id: str,
|
|
15
|
+
created_from: str = None) -> pd.DataFrame:
|
|
16
|
+
params = {}
|
|
17
|
+
if created_from:
|
|
18
|
+
params['createdFrom'] = created_from
|
|
19
|
+
try:
|
|
20
|
+
request = requests.Request(method='GET',
|
|
21
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/functions",
|
|
22
|
+
params=params)
|
|
23
|
+
|
|
24
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
25
|
+
df = pd.json_normalize(
|
|
26
|
+
data,
|
|
27
|
+
record_path='functions',
|
|
28
|
+
meta=['employeeId']
|
|
29
|
+
)
|
|
30
|
+
except requests.HTTPError as e:
|
|
31
|
+
df = pd.DataFrame()
|
|
32
|
+
|
|
33
|
+
return df
|
|
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
|
|
86
|
+
|
|
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
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Response from the API
|
|
111
|
+
"""
|
|
112
|
+
# Validate with Pydantic model
|
|
113
|
+
child_model = ChildUpdate(**data)
|
|
114
|
+
|
|
115
|
+
if self.nmbrs.mock_mode:
|
|
116
|
+
return child_model
|
|
117
|
+
|
|
118
|
+
ChildType = self.nmbrs.soap_client_employees.get_type('ns0:Child')
|
|
119
|
+
child = ChildType(
|
|
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
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Make the API call
|
|
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}
|
|
133
|
+
)
|
|
134
|
+
return result
|
|
135
|
+
|
|
@@ -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)}")
|