brynq-sdk-nmbrs 2.3.3.dev0__tar.gz → 2.4.5__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.3.dev0 → brynq_sdk_nmbrs-2.4.5}/PKG-INFO +1 -1
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/__init__.py +95 -91
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/absence.py +1 -4
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/address.py +70 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/bank.py +1 -2
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/companies.py +228 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/contract.py +0 -1
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/days.py +1 -1
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/debtors.py +87 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/department.py +23 -111
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/document.py +271 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/employee_wage_tax_settings.py +13 -5
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/employees.py +55 -21
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/employment.py +0 -2
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/extra_fields.py +126 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/function.py +35 -97
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/leave.py +53 -58
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/manager.py +51 -35
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/salaries.py +48 -65
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/salary_tables.py +1 -4
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/schedules.py +85 -0
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/schemas/absence.py +71 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/address.py +2 -29
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/children.py +0 -2
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/contracts.py +7 -6
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/costcenter.py +2 -2
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/costunit.py +2 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/days.py +13 -11
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/schemas/debtor.py +16 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/department.py +5 -39
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/document.py +0 -2
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/schemas/employee_wage_tax_settings.py +75 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/employees.py +2 -0
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/schemas/extra_fields.py +56 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/function.py +5 -32
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/hours.py +5 -1
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/leave.py +17 -6
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/manager.py +16 -24
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/salary.py +11 -1
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/schedules.py +2 -54
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/schemas/svw_settings.py +116 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/wage_tax.py +53 -21
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/wagecomponents.py +6 -9
- brynq_sdk_nmbrs-2.4.5/brynq_sdk_nmbrs/svw_settings.py +213 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/wage_tax.py +120 -11
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs.egg-info/PKG-INFO +1 -1
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs.egg-info/SOURCES.txt +5 -2
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/setup.py +1 -1
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/address.py +0 -146
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/companies.py +0 -137
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/debtors.py +0 -99
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/document.py +0 -80
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schedules.py +0 -158
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/absence.py +0 -61
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/debtor.py +0 -38
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/schemas/social_insurance.py +0 -73
- brynq_sdk_nmbrs-2.3.3.dev0/brynq_sdk_nmbrs/social_insurance.py +0 -130
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/children.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/costcenter.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/costunit.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/hours.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/__init__.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/bank.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/company.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/employment.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/schemas/wage_tax_settings.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs/wagecomponents.py +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs.egg-info/not-zip-safe +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs.egg-info/requires.txt +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/brynq_sdk_nmbrs.egg-info/top_level.txt +0 -0
- {brynq_sdk_nmbrs-2.3.3.dev0 → brynq_sdk_nmbrs-2.4.5}/setup.cfg +0 -0
|
@@ -17,14 +17,14 @@ from .children import Children
|
|
|
17
17
|
from .companies import Companies
|
|
18
18
|
from .contract import Contract
|
|
19
19
|
from .costcenter import Costcenter, EmployeeCostcenter
|
|
20
|
-
from .costunit import Costunit
|
|
21
20
|
from .days import FixedDays, VariableDays
|
|
22
21
|
from .debtors import Debtors
|
|
23
22
|
from .department import Departments, EmployeeDepartment
|
|
24
|
-
from .document import
|
|
23
|
+
from .document import Document
|
|
25
24
|
from .employee_wage_tax_settings import EmployeeWageTaxSettings
|
|
26
25
|
from .employees import Employees
|
|
27
26
|
from .employment import Employment
|
|
27
|
+
from .extra_fields import EmployeeExtraFields, ExtraFieldSettings
|
|
28
28
|
from .function import EmployeeFunction, Functions
|
|
29
29
|
from .hours import FixedHours, VariableHours
|
|
30
30
|
from .leave import Leave, LeaveBalance, LeaveGroup
|
|
@@ -32,115 +32,119 @@ from .manager import EmployeeManager, Manager
|
|
|
32
32
|
from .salaries import Salaries
|
|
33
33
|
from .salary_tables import SalaryScales, SalarySteps, SalaryTables
|
|
34
34
|
from .schedules import Schedule
|
|
35
|
-
from .
|
|
35
|
+
from .svw_settings import CompanySVWSettings, EmployeeSVWSettings
|
|
36
|
+
from .wage_tax import CompanyWageTax, WageTax
|
|
36
37
|
from .wagecomponents import EmployeeFixedWageComponents, EmployeeVariableWageComponents
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
class Nmbrs(BrynQ):
|
|
40
|
-
def __init__(self, system_type: Optional[Literal['source', 'target']] = None, debug: bool = False
|
|
41
|
+
def __init__(self, system_type: Optional[Literal['source', 'target']] = None, debug: bool = False):
|
|
41
42
|
"""
|
|
42
43
|
Initialize the Nmbrs class.
|
|
43
44
|
|
|
44
45
|
Args:
|
|
45
|
-
|
|
46
|
+
system_type: The type of system ('source' or 'target')
|
|
46
47
|
debug: Whether to print debug information
|
|
47
|
-
mock_mode: If true, data will NOT be sent to Nmbrs but only be tested for validity against Pydantic schemas
|
|
48
48
|
"""
|
|
49
|
-
|
|
49
|
+
super().__init__()
|
|
50
|
+
self.mock_mode = False # Always disabled, kept for backwards compatibility with existing code
|
|
50
51
|
self.debug = debug
|
|
51
52
|
self.timeout = 3600
|
|
52
53
|
self.system_type = system_type
|
|
54
|
+
self.data_interface_id = os.getenv("DATA_INTERFACE_ID")
|
|
55
|
+
|
|
56
|
+
# Fetch and store credentials
|
|
57
|
+
credentials = self.interfaces.credentials.get(system="nmbrs", system_type=system_type)
|
|
58
|
+
if isinstance(credentials, list):
|
|
59
|
+
credentials = credentials[0] if credentials else {}
|
|
60
|
+
if not isinstance(credentials, dict):
|
|
61
|
+
credentials = {}
|
|
62
|
+
self._credentials = credentials.get("data", credentials) or {}
|
|
63
|
+
self._custom_config = (
|
|
64
|
+
credentials.get("custom_config")
|
|
65
|
+
or credentials.get("custom_data")
|
|
66
|
+
or {}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
headers = self._get_request_headers()
|
|
70
|
+
self.base_url = "https://api.nmbrsapp.com/api/"
|
|
71
|
+
self.session = requests.Session()
|
|
72
|
+
self.session.headers.update(headers)
|
|
53
73
|
|
|
54
|
-
# Initialize
|
|
74
|
+
# Initialize SOAP client
|
|
75
|
+
self.soap_settings = Settings(
|
|
76
|
+
strict=False,
|
|
77
|
+
xml_huge_tree=True,
|
|
78
|
+
force_https=True
|
|
79
|
+
)
|
|
80
|
+
self.soap_client_companies = Client(
|
|
81
|
+
'https://api.nmbrs.nl/soap/v3/CompanyService.asmx?wsdl',
|
|
82
|
+
settings=self.soap_settings
|
|
83
|
+
)
|
|
84
|
+
self.soap_client_employees = Client(
|
|
85
|
+
'https://api.nmbrs.nl/soap/v3/EmployeeService.asmx?wsdl',
|
|
86
|
+
settings=self.soap_settings
|
|
87
|
+
)
|
|
88
|
+
self.soap_auth_header = self._get_soap_auth_header()
|
|
89
|
+
self.companies = Companies(self)
|
|
90
|
+
# Following methods can only be used if the SOAP authentication header is set (optional, this is not always in scope for all integrations)
|
|
91
|
+
if self.soap_auth_header is not None:
|
|
92
|
+
self.soap_company_ids = self.companies.get_soap_ids()
|
|
93
|
+
self.salary_tables = SalaryTables(self)
|
|
94
|
+
self.salary_scales = SalaryScales(self)
|
|
95
|
+
self.salary_steps = SalarySteps(self)
|
|
96
|
+
self.wage_tax = WageTax(self)
|
|
97
|
+
self.absence = Absence(self)
|
|
98
|
+
self.children = Children(self)
|
|
55
99
|
self.address = Address(self)
|
|
56
100
|
self.bank = Bank(self)
|
|
57
|
-
self.
|
|
101
|
+
self.cost_center = Costcenter(self)
|
|
102
|
+
self.employee_cost_center = EmployeeCostcenter(self)
|
|
103
|
+
if self.soap_auth_header is None:
|
|
104
|
+
self.children = Children(self)
|
|
105
|
+
self.debtor = Debtors(self)
|
|
58
106
|
self.contract = Contract(self)
|
|
107
|
+
self.employee_department = EmployeeDepartment(self)
|
|
108
|
+
self.department = Departments(self)
|
|
109
|
+
debtors, _ = self.debtor.get()
|
|
110
|
+
self.debtor_ids = debtors['debtor_id'].to_list()
|
|
111
|
+
self.company_ids = self.companies.get()['companyId'].to_list()
|
|
112
|
+
self.employees = Employees(self)
|
|
113
|
+
self.employment = Employment(self)
|
|
114
|
+
self.employee_function = EmployeeFunction(self)
|
|
115
|
+
self.function = Functions(self)
|
|
116
|
+
self.fixed_hours = FixedHours(self)
|
|
117
|
+
self.fixed_days = FixedDays(self)
|
|
118
|
+
self.variable_hours = VariableHours(self)
|
|
119
|
+
self.variable_days = VariableDays(self)
|
|
120
|
+
self.manager = Manager(self)
|
|
121
|
+
self.employee_manager = EmployeeManager(self)
|
|
122
|
+
self.salaries = Salaries(self)
|
|
123
|
+
self.schedule = Schedule(self)
|
|
124
|
+
self.fixed_wagecomponents = EmployeeFixedWageComponents(self)
|
|
125
|
+
self.variable_wagecomponents = EmployeeVariableWageComponents(self)
|
|
126
|
+
self.current_period = self.companies.get_current_period()
|
|
127
|
+
|
|
128
|
+
self.company_wage_tax = CompanyWageTax(self)
|
|
129
|
+
self.documents = Document(self)
|
|
130
|
+
self.company_svw_settings = CompanySVWSettings(self)
|
|
131
|
+
self.employee_svw_settings = EmployeeSVWSettings(self)
|
|
132
|
+
self.employee_wage_tax_settings = EmployeeWageTaxSettings(self)
|
|
133
|
+
self.extra_field_settings = ExtraFieldSettings(self)
|
|
134
|
+
self.employee_extra_fields = EmployeeExtraFields(self)
|
|
59
135
|
self.leave = Leave(self)
|
|
60
|
-
self.
|
|
61
|
-
self.
|
|
62
|
-
|
|
63
|
-
self.employee_document = EmployeeDocument(self)
|
|
64
|
-
self.departments = Departments(self)
|
|
65
|
-
self.debtor = Debtors(self)
|
|
66
|
-
self.companies = Companies(self)
|
|
136
|
+
self.leave_balance = LeaveBalance(self)
|
|
137
|
+
self.leave_groups = LeaveGroup(self)
|
|
138
|
+
|
|
67
139
|
|
|
68
|
-
if mock_mode is False:
|
|
69
|
-
# Initialize BrynQ parent class for REST API credentials
|
|
70
|
-
super().__init__()
|
|
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
|
-
|
|
77
|
-
headers = self._get_request_headers()
|
|
78
|
-
self.base_url = "https://api.nmbrsapp.com/api/"
|
|
79
|
-
self.session = requests.Session()
|
|
80
|
-
self.session.headers.update(headers)
|
|
81
|
-
|
|
82
|
-
# Initialize SOAP clients
|
|
83
|
-
self.soap_settings = Settings(
|
|
84
|
-
strict=False,
|
|
85
|
-
xml_huge_tree=True,
|
|
86
|
-
force_https=True
|
|
87
|
-
)
|
|
88
|
-
self.soap_client_companies = Client(
|
|
89
|
-
'https://api.nmbrs.nl/soap/v3/CompanyService.asmx?wsdl',
|
|
90
|
-
settings=self.soap_settings
|
|
91
|
-
)
|
|
92
|
-
self.soap_client_employees = Client(
|
|
93
|
-
'https://api.nmbrs.nl/soap/v3/EmployeeService.asmx?wsdl',
|
|
94
|
-
settings=self.soap_settings
|
|
95
|
-
)
|
|
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
|
|
113
|
-
if self.soap_auth_header is not None:
|
|
114
|
-
self.employees = Employees(self)
|
|
115
|
-
self.soap_company_ids = self.companies.get_soap_ids()
|
|
116
|
-
self.soap_employee_ids = self.employees.get_soap_ids()
|
|
117
|
-
self.salary_tables = SalaryTables(self)
|
|
118
|
-
self.salary_scales = SalaryScales(self)
|
|
119
|
-
self.salary_steps = SalarySteps(self)
|
|
120
|
-
self.wage_tax = WageTax(self)
|
|
121
|
-
self.absence = Absence(self)
|
|
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)
|
|
141
140
|
def _get_request_headers(self):
|
|
142
|
-
access_token = self._credentials.get('
|
|
143
|
-
subscription_key =
|
|
141
|
+
access_token = self._credentials.get('access_token')
|
|
142
|
+
subscription_key = (
|
|
143
|
+
self._custom_config.get("subscription_key")
|
|
144
|
+
or self._custom_config.get("subscriptionKey")
|
|
145
|
+
or self._credentials.get("subscription_key")
|
|
146
|
+
or self._credentials.get("subscriptionKey")
|
|
147
|
+
)
|
|
144
148
|
headers = {
|
|
145
149
|
"accept": "application/json",
|
|
146
150
|
"Authorization": f"Bearer {access_token.strip() if access_token else ''}",
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
from typing import Any, Dict,
|
|
1
|
+
from typing import Any, Dict, Tuple
|
|
2
2
|
import pandas as pd
|
|
3
3
|
from .schemas.absence import AbsenceCreate, AbsenceGet
|
|
4
4
|
from zeep.exceptions import Fault
|
|
5
|
-
from zeep.ns import WSDL, SOAP_ENV_11
|
|
6
|
-
from zeep.xsd import ComplexType, Element, String
|
|
7
5
|
from zeep.helpers import serialize_object
|
|
8
|
-
# import logging
|
|
9
6
|
from brynq_sdk_functions import Functions
|
|
10
7
|
|
|
11
8
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from brynq_sdk_functions import Functions
|
|
7
|
+
|
|
8
|
+
from .schemas.address import AddressCreate, AddressGet
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Address:
|
|
12
|
+
def __init__(self, nmbrs):
|
|
13
|
+
self.nmbrs = nmbrs
|
|
14
|
+
|
|
15
|
+
def get(self,
|
|
16
|
+
created_from: str = None) -> pd.DataFrame:
|
|
17
|
+
addresses = pd.DataFrame()
|
|
18
|
+
for company in self.nmbrs.company_ids:
|
|
19
|
+
addresses = pd.concat([addresses, self._get(company, created_from)])
|
|
20
|
+
|
|
21
|
+
valid_addresses, invalid_addresses = Functions.validate_data(df=addresses, schema=AddressGet, debug=True)
|
|
22
|
+
|
|
23
|
+
return valid_addresses, invalid_addresses
|
|
24
|
+
|
|
25
|
+
def _get(self,
|
|
26
|
+
company_id: str,
|
|
27
|
+
created_from: str = None) -> pd.DataFrame:
|
|
28
|
+
params = {} if created_from is None else {'createdFrom': created_from}
|
|
29
|
+
request = requests.Request(method='GET',
|
|
30
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/addresses",
|
|
31
|
+
params=params)
|
|
32
|
+
|
|
33
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
34
|
+
df = pd.json_normalize(
|
|
35
|
+
data,
|
|
36
|
+
record_path='addresses',
|
|
37
|
+
meta=['employeeId']
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return df
|
|
41
|
+
|
|
42
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
43
|
+
"""
|
|
44
|
+
Create a new address for an employee using Pydantic validation.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
employee_id: The ID of the employee
|
|
48
|
+
data: Dictionary containing address data with fields matching
|
|
49
|
+
the AddressCreate schema (using camelCase field names)
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Response from the API
|
|
53
|
+
"""
|
|
54
|
+
# Validate with Pydantic model - this will raise an error if required fields are missing
|
|
55
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, AddressCreate)
|
|
56
|
+
address_model = AddressCreate(**nested_data)
|
|
57
|
+
|
|
58
|
+
if self.nmbrs.mock_mode:
|
|
59
|
+
return address_model
|
|
60
|
+
|
|
61
|
+
# Convert validated model to dict for API payload
|
|
62
|
+
payload = address_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}/address",
|
|
67
|
+
json=payload,
|
|
68
|
+
timeout=self.nmbrs.timeout
|
|
69
|
+
)
|
|
70
|
+
return resp
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import math
|
|
2
1
|
from typing import Any, Dict
|
|
3
2
|
|
|
4
3
|
import pandas as pd
|
|
@@ -40,7 +39,7 @@ class Bank:
|
|
|
40
39
|
record_path='bankAccounts',
|
|
41
40
|
meta=['employeeId']
|
|
42
41
|
)
|
|
43
|
-
except requests.HTTPError
|
|
42
|
+
except requests.HTTPError:
|
|
44
43
|
df = pd.DataFrame()
|
|
45
44
|
|
|
46
45
|
return df
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import requests
|
|
6
|
+
from zeep.exceptions import Fault
|
|
7
|
+
from zeep.helpers import serialize_object
|
|
8
|
+
|
|
9
|
+
from .bank import Bank
|
|
10
|
+
from .costcenter import Costcenter
|
|
11
|
+
from .costunit import Costunit
|
|
12
|
+
from .hours import Hours
|
|
13
|
+
from .leave import LeaveGroup
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from brynq_sdk_nmbrs import Nmbrs
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Companies:
|
|
20
|
+
def __init__(self, nmbrs):
|
|
21
|
+
self.nmbrs: Nmbrs = nmbrs
|
|
22
|
+
self.costcenters = Costcenter(nmbrs)
|
|
23
|
+
self.costunits = Costunit(nmbrs)
|
|
24
|
+
self.hours = Hours(nmbrs)
|
|
25
|
+
self.banks = Bank(nmbrs)
|
|
26
|
+
self.logger = logging.getLogger(__name__)
|
|
27
|
+
self.leave_groups = LeaveGroup(nmbrs)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def soap_client_companies(self):
|
|
31
|
+
return self.nmbrs.soap_client_companies
|
|
32
|
+
|
|
33
|
+
def get(self) -> pd.DataFrame:
|
|
34
|
+
try:
|
|
35
|
+
request = requests.Request(method='GET',
|
|
36
|
+
url=f"{self.nmbrs.base_url}companies")
|
|
37
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
38
|
+
df = pd.DataFrame(data)
|
|
39
|
+
except requests.HTTPError as e:
|
|
40
|
+
if e.response.status_code == 403 and e.response.json().get("title") == "ForbiddenMultiDebtor.":
|
|
41
|
+
df = pd.DataFrame()
|
|
42
|
+
for debtor_id in self.nmbrs.debtor_ids:
|
|
43
|
+
request = requests.Request(method='GET',
|
|
44
|
+
url=f"{self.nmbrs.base_url}debtors/{debtor_id}/companies")
|
|
45
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
46
|
+
df = pd.concat([df, pd.DataFrame(data)])
|
|
47
|
+
else:
|
|
48
|
+
raise e
|
|
49
|
+
# TODO: add validation
|
|
50
|
+
|
|
51
|
+
return df
|
|
52
|
+
|
|
53
|
+
def get_soap_ids(self) -> pd.DataFrame:
|
|
54
|
+
"""
|
|
55
|
+
Get all companies using the SOAP API.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
pd.DataFrame: DataFrame containing all companies
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
# Make SOAP request with the proper header structure
|
|
62
|
+
response = self.nmbrs.soap_client_companies.service.List_GetAll(
|
|
63
|
+
_soapheaders=[self.nmbrs.soap_auth_header]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Convert response to DataFrame
|
|
67
|
+
if response:
|
|
68
|
+
# Convert Zeep objects to Python dictionaries
|
|
69
|
+
serialized_response = serialize_object(response)
|
|
70
|
+
|
|
71
|
+
# TODO: add validation here
|
|
72
|
+
# Convert to DataFrame
|
|
73
|
+
df = pd.DataFrame(serialized_response)
|
|
74
|
+
df = self.nmbrs._rename_camel_columns_to_snake_case(df)
|
|
75
|
+
|
|
76
|
+
return df
|
|
77
|
+
else:
|
|
78
|
+
return pd.DataFrame()
|
|
79
|
+
|
|
80
|
+
except Fault as e:
|
|
81
|
+
raise Exception(f"SOAP request failed: {str(e)}")
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self.logger.exception("Exception occurred:")
|
|
84
|
+
raise Exception(f"Failed to get companies: {str(e)}")
|
|
85
|
+
|
|
86
|
+
def get_current_period(self) -> int:
|
|
87
|
+
try:
|
|
88
|
+
df = pd.DataFrame()
|
|
89
|
+
for company_id in self.nmbrs.company_ids:
|
|
90
|
+
request = requests.Request(method='GET',
|
|
91
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/period")
|
|
92
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
93
|
+
df_temp = pd.json_normalize(data)
|
|
94
|
+
df_temp['company_id'] = company_id
|
|
95
|
+
df = pd.concat([df, df_temp])
|
|
96
|
+
|
|
97
|
+
return df
|
|
98
|
+
except Exception as e:
|
|
99
|
+
self.logger.exception("Exception occurred:")
|
|
100
|
+
raise Exception(f"Failed to get current period: {str(e)}")
|
|
101
|
+
|
|
102
|
+
def get_wage_models(self,
|
|
103
|
+
company_id: Optional[str] = None,
|
|
104
|
+
period: Optional[int] = None,
|
|
105
|
+
year: Optional[int] = None) -> pd.DataFrame:
|
|
106
|
+
"""
|
|
107
|
+
Get company wage models per period.
|
|
108
|
+
|
|
109
|
+
Returns the available wage codes for employees of this company in the
|
|
110
|
+
period consulted. If no period is used it returns the current period information.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
company_id: Optional company ID. If not provided, fetches for all companies.
|
|
114
|
+
period: Optional period filter
|
|
115
|
+
year: Optional year filter
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
DataFrame containing wage models
|
|
119
|
+
"""
|
|
120
|
+
if company_id:
|
|
121
|
+
return self._get_wage_models(company_id, period, year)
|
|
122
|
+
|
|
123
|
+
df = pd.DataFrame()
|
|
124
|
+
for company in self.nmbrs.company_ids:
|
|
125
|
+
df = pd.concat([df, self._get_wage_models(company, period, year)])
|
|
126
|
+
|
|
127
|
+
return df
|
|
128
|
+
|
|
129
|
+
def _get_wage_models(self,
|
|
130
|
+
company_id: str,
|
|
131
|
+
period: Optional[int] = None,
|
|
132
|
+
year: Optional[int] = None) -> pd.DataFrame:
|
|
133
|
+
"""
|
|
134
|
+
Internal method to get wage models for a single company.
|
|
135
|
+
"""
|
|
136
|
+
params = {}
|
|
137
|
+
if period is not None:
|
|
138
|
+
params['period'] = period
|
|
139
|
+
if year is not None:
|
|
140
|
+
params['year'] = year
|
|
141
|
+
|
|
142
|
+
request = requests.Request(
|
|
143
|
+
method='GET',
|
|
144
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/wagemodels",
|
|
145
|
+
params=params
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
149
|
+
df = pd.DataFrame(data)
|
|
150
|
+
|
|
151
|
+
if not df.empty:
|
|
152
|
+
df['companyId'] = company_id
|
|
153
|
+
|
|
154
|
+
return df
|
|
155
|
+
|
|
156
|
+
def get_addresses(self, company_id: Optional[str] = None) -> pd.DataFrame:
|
|
157
|
+
"""
|
|
158
|
+
Get addresses history for a company.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
company_id: Optional company ID. If not provided, fetches for all companies.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
DataFrame containing company addresses
|
|
165
|
+
"""
|
|
166
|
+
if company_id:
|
|
167
|
+
return self._get_addresses(company_id)
|
|
168
|
+
|
|
169
|
+
df = pd.DataFrame()
|
|
170
|
+
for company in self.nmbrs.company_ids:
|
|
171
|
+
df = pd.concat([df, self._get_addresses(company)])
|
|
172
|
+
|
|
173
|
+
return df
|
|
174
|
+
|
|
175
|
+
def _get_addresses(self, company_id: str) -> pd.DataFrame:
|
|
176
|
+
"""
|
|
177
|
+
Internal method to get addresses for a single company.
|
|
178
|
+
"""
|
|
179
|
+
request = requests.Request(
|
|
180
|
+
method='GET',
|
|
181
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/addresses"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
185
|
+
df = pd.DataFrame(data)
|
|
186
|
+
|
|
187
|
+
if not df.empty:
|
|
188
|
+
df['companyId'] = company_id
|
|
189
|
+
|
|
190
|
+
return df
|
|
191
|
+
|
|
192
|
+
def get_contact_persons(self, company_id: Optional[str] = None) -> pd.DataFrame:
|
|
193
|
+
"""
|
|
194
|
+
Get contact persons for a company.
|
|
195
|
+
|
|
196
|
+
There's no history on these values.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
company_id: Optional company ID. If not provided, fetches for all companies.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
DataFrame containing contact persons
|
|
203
|
+
"""
|
|
204
|
+
if company_id:
|
|
205
|
+
return self._get_contact_persons(company_id)
|
|
206
|
+
|
|
207
|
+
df = pd.DataFrame()
|
|
208
|
+
for company in self.nmbrs.company_ids:
|
|
209
|
+
df = pd.concat([df, self._get_contact_persons(company)])
|
|
210
|
+
|
|
211
|
+
return df
|
|
212
|
+
|
|
213
|
+
def _get_contact_persons(self, company_id: str) -> pd.DataFrame:
|
|
214
|
+
"""
|
|
215
|
+
Internal method to get contact persons for a single company.
|
|
216
|
+
"""
|
|
217
|
+
request = requests.Request(
|
|
218
|
+
method='GET',
|
|
219
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/contactpersons"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
223
|
+
df = pd.DataFrame(data)
|
|
224
|
+
|
|
225
|
+
if not df.empty:
|
|
226
|
+
df['companyId'] = company_id
|
|
227
|
+
|
|
228
|
+
return df
|
|
@@ -2,7 +2,7 @@ import pandas as pd
|
|
|
2
2
|
import requests
|
|
3
3
|
from typing import Dict, Any
|
|
4
4
|
from .schemas.days import (
|
|
5
|
-
VariableDaysGet, VariableDaysCreate, FixedDaysCreate
|
|
5
|
+
VariableDaysGet, VariableDaysCreate, FixedDaysCreate
|
|
6
6
|
)
|
|
7
7
|
from brynq_sdk_functions import Functions
|
|
8
8
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from brynq_sdk_functions import Functions
|
|
6
|
+
|
|
7
|
+
from .department import Departments
|
|
8
|
+
from .function import Functions as NmbrsFunctions
|
|
9
|
+
from .schemas.debtor import DebtorsGet
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Debtors:
|
|
13
|
+
"""
|
|
14
|
+
Class for handling debtor information.
|
|
15
|
+
|
|
16
|
+
Endpoints:
|
|
17
|
+
- GET /api/debtors
|
|
18
|
+
- GET /api/debtors/info
|
|
19
|
+
- GET /api/debtors/endcontractreasons/{Year}
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, nmbrs):
|
|
23
|
+
self.nmbrs = nmbrs
|
|
24
|
+
self.departments = Departments(nmbrs)
|
|
25
|
+
self.functions = NmbrsFunctions(nmbrs)
|
|
26
|
+
|
|
27
|
+
def get(self) -> tuple:
|
|
28
|
+
"""
|
|
29
|
+
Get a list of all debtors for the authenticated user.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Tuple of (valid_data, invalid_data) DataFrames
|
|
33
|
+
"""
|
|
34
|
+
request = requests.Request(method='GET',
|
|
35
|
+
url=f"{self.nmbrs.base_url}debtors")
|
|
36
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
37
|
+
|
|
38
|
+
df = pd.DataFrame(data)
|
|
39
|
+
|
|
40
|
+
valid_debtors, invalid_debtors = Functions.validate_data(df=df, schema=DebtorsGet, debug=True)
|
|
41
|
+
|
|
42
|
+
return valid_debtors, invalid_debtors
|
|
43
|
+
|
|
44
|
+
def get_info(self) -> pd.DataFrame:
|
|
45
|
+
"""
|
|
46
|
+
Get information about the authenticated debtor.
|
|
47
|
+
|
|
48
|
+
Returns info about the debtor that the API keys belong to,
|
|
49
|
+
including current settings and subscription details.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
DataFrame containing debtor info
|
|
53
|
+
"""
|
|
54
|
+
resp = self.nmbrs.session.get(
|
|
55
|
+
url=f"{self.nmbrs.base_url}debtors/info",
|
|
56
|
+
timeout=self.nmbrs.timeout
|
|
57
|
+
)
|
|
58
|
+
resp.raise_for_status()
|
|
59
|
+
data = resp.json()
|
|
60
|
+
|
|
61
|
+
# Normalize the response if it's a dict
|
|
62
|
+
if isinstance(data, dict):
|
|
63
|
+
df = pd.json_normalize(data)
|
|
64
|
+
else:
|
|
65
|
+
df = pd.DataFrame(data)
|
|
66
|
+
|
|
67
|
+
return df
|
|
68
|
+
|
|
69
|
+
def get_end_contract_reasons(self, year: int) -> pd.DataFrame:
|
|
70
|
+
"""
|
|
71
|
+
Get the available end contract reasons for a specific year.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
year: The year to get end contract reasons for
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
DataFrame containing end contract reasons
|
|
78
|
+
"""
|
|
79
|
+
request = requests.Request(
|
|
80
|
+
method='GET',
|
|
81
|
+
url=f"{self.nmbrs.base_url}debtors/endcontractreasons/{year}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
85
|
+
df = pd.DataFrame(data)
|
|
86
|
+
|
|
87
|
+
return df
|