brynq-sdk-nmbrs 2.3.3.dev0__py3-none-any.whl → 2.4.5__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.
Files changed (51) hide show
  1. brynq_sdk_nmbrs/__init__.py +95 -91
  2. brynq_sdk_nmbrs/absence.py +1 -4
  3. brynq_sdk_nmbrs/address.py +5 -81
  4. brynq_sdk_nmbrs/bank.py +1 -2
  5. brynq_sdk_nmbrs/companies.py +130 -39
  6. brynq_sdk_nmbrs/contract.py +0 -1
  7. brynq_sdk_nmbrs/days.py +1 -1
  8. brynq_sdk_nmbrs/debtors.py +53 -65
  9. brynq_sdk_nmbrs/department.py +23 -111
  10. brynq_sdk_nmbrs/document.py +195 -4
  11. brynq_sdk_nmbrs/employee_wage_tax_settings.py +13 -5
  12. brynq_sdk_nmbrs/employees.py +55 -21
  13. brynq_sdk_nmbrs/employment.py +0 -2
  14. brynq_sdk_nmbrs/extra_fields.py +126 -0
  15. brynq_sdk_nmbrs/function.py +35 -97
  16. brynq_sdk_nmbrs/leave.py +53 -58
  17. brynq_sdk_nmbrs/manager.py +51 -35
  18. brynq_sdk_nmbrs/salaries.py +48 -65
  19. brynq_sdk_nmbrs/salary_tables.py +1 -4
  20. brynq_sdk_nmbrs/schedules.py +5 -78
  21. brynq_sdk_nmbrs/schemas/absence.py +26 -16
  22. brynq_sdk_nmbrs/schemas/address.py +2 -29
  23. brynq_sdk_nmbrs/schemas/children.py +0 -2
  24. brynq_sdk_nmbrs/schemas/contracts.py +7 -6
  25. brynq_sdk_nmbrs/schemas/costcenter.py +2 -2
  26. brynq_sdk_nmbrs/schemas/costunit.py +2 -0
  27. brynq_sdk_nmbrs/schemas/days.py +13 -11
  28. brynq_sdk_nmbrs/schemas/debtor.py +6 -28
  29. brynq_sdk_nmbrs/schemas/department.py +5 -39
  30. brynq_sdk_nmbrs/schemas/document.py +0 -2
  31. brynq_sdk_nmbrs/schemas/employee_wage_tax_settings.py +75 -0
  32. brynq_sdk_nmbrs/schemas/employees.py +2 -0
  33. brynq_sdk_nmbrs/schemas/extra_fields.py +56 -0
  34. brynq_sdk_nmbrs/schemas/function.py +5 -32
  35. brynq_sdk_nmbrs/schemas/hours.py +5 -1
  36. brynq_sdk_nmbrs/schemas/leave.py +17 -6
  37. brynq_sdk_nmbrs/schemas/manager.py +16 -24
  38. brynq_sdk_nmbrs/schemas/salary.py +11 -1
  39. brynq_sdk_nmbrs/schemas/schedules.py +2 -54
  40. brynq_sdk_nmbrs/schemas/svw_settings.py +116 -0
  41. brynq_sdk_nmbrs/schemas/wage_tax.py +53 -21
  42. brynq_sdk_nmbrs/schemas/wagecomponents.py +6 -9
  43. brynq_sdk_nmbrs/svw_settings.py +213 -0
  44. brynq_sdk_nmbrs/wage_tax.py +120 -11
  45. {brynq_sdk_nmbrs-2.3.3.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/METADATA +1 -1
  46. brynq_sdk_nmbrs-2.4.5.dist-info/RECORD +58 -0
  47. {brynq_sdk_nmbrs-2.3.3.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/WHEEL +1 -1
  48. brynq_sdk_nmbrs/schemas/social_insurance.py +0 -73
  49. brynq_sdk_nmbrs/social_insurance.py +0 -130
  50. brynq_sdk_nmbrs-2.3.3.dev0.dist-info/RECORD +0 -55
  51. {brynq_sdk_nmbrs-2.3.3.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/top_level.txt +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 EmployeeDocument, Payslip
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 .wage_tax import WageTax, WageTaxSettings
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, mock_mode: bool = True):
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
- label: The label of the system in BrynQ. legacy
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
- self.mock_mode = mock_mode
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 classes that work in both mock and real mode
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.children = Children(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.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)
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('data').get('access_token')
143
- subscription_key = self._custom_config.get("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, List, Union, Tuple
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
 
@@ -1,9 +1,12 @@
1
+ from typing import Any, Dict
2
+
1
3
  import pandas as pd
2
4
  import requests
3
- from typing import Dict, Any, Union
4
- from .schemas.address import AddressCreate, AddressGet, AddressDelete, AddressUpdate, Period
5
+
5
6
  from brynq_sdk_functions import Functions
6
7
 
8
+ from .schemas.address import AddressCreate, AddressGet
9
+
7
10
 
8
11
  class Address:
9
12
  def __init__(self, nmbrs):
@@ -65,82 +68,3 @@ class Address:
65
68
  timeout=self.nmbrs.timeout
66
69
  )
67
70
  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/bank.py CHANGED
@@ -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 as e:
42
+ except requests.HTTPError:
44
43
  df = pd.DataFrame()
45
44
 
46
45
  return df
@@ -1,22 +1,24 @@
1
+ import logging
2
+ from typing import TYPE_CHECKING, Optional
3
+
1
4
  import pandas as pd
2
5
  import requests
3
- import logging
4
- from typing import Dict, Any
6
+ from zeep.exceptions import Fault
7
+ from zeep.helpers import serialize_object
5
8
 
6
- from .leave import LeaveGroup
9
+ from .bank import Bank
7
10
  from .costcenter import Costcenter
8
11
  from .costunit import Costunit
9
12
  from .hours import Hours
10
- from .bank import Bank
11
- from .function import Functions
12
- from .schemas.company import CompanyCreate
13
- from zeep.exceptions import Fault
14
- from zeep.helpers import serialize_object
13
+ from .leave import LeaveGroup
14
+
15
+ if TYPE_CHECKING:
16
+ from brynq_sdk_nmbrs import Nmbrs
15
17
 
16
18
 
17
19
  class Companies:
18
20
  def __init__(self, nmbrs):
19
- self.nmbrs = nmbrs
21
+ self.nmbrs: Nmbrs = nmbrs
20
22
  self.costcenters = Costcenter(nmbrs)
21
23
  self.costunits = Costunit(nmbrs)
22
24
  self.hours = Hours(nmbrs)
@@ -57,7 +59,7 @@ class Companies:
57
59
  """
58
60
  try:
59
61
  # Make SOAP request with the proper header structure
60
- response = self.soap_client_companies.service.List_GetAll(
62
+ response = self.nmbrs.soap_client_companies.service.List_GetAll(
61
63
  _soapheaders=[self.nmbrs.soap_auth_header]
62
64
  )
63
65
 
@@ -97,41 +99,130 @@ class Companies:
97
99
  self.logger.exception("Exception occurred:")
98
100
  raise Exception(f"Failed to get current period: {str(e)}")
99
101
 
100
- def create(self, data: Dict[str, Any]) -> int:
102
+ def get_wage_models(self,
103
+ company_id: Optional[str] = None,
104
+ period: Optional[int] = None,
105
+ year: Optional[int] = None) -> pd.DataFrame:
101
106
  """
102
- Create a new company using SOAP API.
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.
103
111
 
104
112
  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
113
+ company_id: Optional company ID. If not provided, fetches for all companies.
114
+ period: Optional period filter
115
+ year: Optional year filter
112
116
 
113
117
  Returns:
114
- The ID of the newly created company.
118
+ DataFrame containing wage models
115
119
  """
116
- company_model = CompanyCreate(**data)
120
+ if company_id:
121
+ return self._get_wage_models(company_id, period, year)
117
122
 
118
- if self.nmbrs.mock_mode:
119
- return 12345 # Mock ID
123
+ df = pd.DataFrame()
124
+ for company in self.nmbrs.company_ids:
125
+ df = pd.concat([df, self._get_wage_models(company, period, year)])
120
126
 
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
127
+ return df
132
128
 
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)}")
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
@@ -1,4 +1,3 @@
1
- import math
2
1
  import pandas as pd
3
2
  import requests
4
3
  from typing import Dict, Any
brynq_sdk_nmbrs/days.py CHANGED
@@ -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, FixedDaysGet
5
+ VariableDaysGet, VariableDaysCreate, FixedDaysCreate
6
6
  )
7
7
  from brynq_sdk_functions import Functions
8
8