brynq-sdk-acerta 1.0.0__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brynq_sdk_acerta-1.1.1/PKG-INFO +21 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/__init__.py +2 -1
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/acerta.py +2 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/agreements.py +16 -88
- brynq_sdk_acerta-1.1.1/brynq_sdk_acerta/bank_accounts.py +90 -0
- brynq_sdk_acerta-1.1.1/brynq_sdk_acerta/company_cars.py +135 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/family_members.py +2 -1
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/__init__.py +6 -2
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/address.py +3 -0
- brynq_sdk_acerta-1.1.1/brynq_sdk_acerta/schemas/agreement.py +982 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/bank_account.py +22 -19
- brynq_sdk_acerta-1.1.1/brynq_sdk_acerta/schemas/company_car.py +124 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/contact_information.py +3 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/cost_center.py +3 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/employee.py +6 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/family.py +6 -0
- brynq_sdk_acerta-1.1.1/brynq_sdk_acerta.egg-info/PKG-INFO +21 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta.egg-info/SOURCES.txt +2 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/setup.py +1 -1
- brynq_sdk_acerta-1.0.0/PKG-INFO +0 -10
- brynq_sdk_acerta-1.0.0/brynq_sdk_acerta/bank_accounts.py +0 -84
- brynq_sdk_acerta-1.0.0/brynq_sdk_acerta/schemas/agreement.py +0 -627
- brynq_sdk_acerta-1.0.0/brynq_sdk_acerta.egg-info/PKG-INFO +0 -10
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/README.md +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/addresses.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/code_lists.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/contact_information.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/cost_centers.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/employees.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/employees_additional_information.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/employer.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/family_situation.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/inservice.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/salaries.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/employer.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/in_service.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/in_service_config.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/planning.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta/schemas/salaries.py +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta.egg-info/dependency_links.txt +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta.egg-info/not-zip-safe +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta.egg-info/requires.txt +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/brynq_sdk_acerta.egg-info/top_level.txt +0 -0
- {brynq_sdk_acerta-1.0.0 → brynq_sdk_acerta-1.1.1}/setup.cfg +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: brynq_sdk_acerta
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: Acerta wrapper from BrynQ
|
|
5
|
+
Author: BrynQ
|
|
6
|
+
Author-email: support@brynq.com
|
|
7
|
+
License: BrynQ License
|
|
8
|
+
Requires-Dist: pandas<3.0.0,>=2.2.0
|
|
9
|
+
Requires-Dist: pydantic<3.0.0,>=2.5.0
|
|
10
|
+
Requires-Dist: pandera<1.0.0,>=0.16.0
|
|
11
|
+
Requires-Dist: requests<3.0.0,>=2.25.1
|
|
12
|
+
Requires-Dist: brynq-sdk-functions>=2.0.5
|
|
13
|
+
Requires-Dist: brynq-sdk-brynq>=3
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: description
|
|
17
|
+
Dynamic: license
|
|
18
|
+
Dynamic: requires-dist
|
|
19
|
+
Dynamic: summary
|
|
20
|
+
|
|
21
|
+
Acerta wrapper from BrynQ
|
|
@@ -9,5 +9,6 @@ from .code_lists import CodeLists
|
|
|
9
9
|
from .employer import Employer
|
|
10
10
|
from .employees_additional_information import EmployeesAdditionalInformation
|
|
11
11
|
from .inservice import InService
|
|
12
|
+
from .company_cars import CompanyCars
|
|
12
13
|
|
|
13
|
-
__all__ = ['Acerta', 'CodeLists', 'Agreements', 'Employees', 'Employer', 'InService', 'EmployeesAdditionalInformation']
|
|
14
|
+
__all__ = ['Acerta', 'CodeLists', 'Agreements', 'Employees', 'Employer', 'InService', 'EmployeesAdditionalInformation', 'CompanyCars']
|
|
@@ -8,6 +8,7 @@ from brynq_sdk_brynq import BrynQ
|
|
|
8
8
|
from .code_lists import CodeLists
|
|
9
9
|
from .agreements import Agreements
|
|
10
10
|
from .inservice import InService
|
|
11
|
+
from .company_cars import CompanyCars
|
|
11
12
|
from .employees import Employees
|
|
12
13
|
from .employer import Employer
|
|
13
14
|
from requests_oauthlib import OAuth2Session
|
|
@@ -96,6 +97,7 @@ class Acerta(BrynQ):
|
|
|
96
97
|
self.employees = Employees(self)
|
|
97
98
|
self.employers = Employer(self)
|
|
98
99
|
self.salaries = Salaries(self)
|
|
100
|
+
self.company_cars = CompanyCars(self)
|
|
99
101
|
|
|
100
102
|
def _ensure_valid_token(self):
|
|
101
103
|
"""Ensure the OAuth token exists and is not about to expire."""
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
from typing import Dict, Any, Optional, Tuple
|
|
2
2
|
import requests
|
|
3
3
|
import pandas as pd
|
|
4
|
-
from .schemas.agreement import
|
|
4
|
+
from .schemas.agreement import (
|
|
5
|
+
AgreementGet,
|
|
6
|
+
PatchAgreementRequest,
|
|
7
|
+
AgreementBasicInformationGet,
|
|
8
|
+
AgreementEmploymentsGet,
|
|
9
|
+
AgreementWorkingTimeGet,
|
|
10
|
+
AgreementCommutingGet,
|
|
11
|
+
AgreementCustomFieldsGet,
|
|
12
|
+
AgreementCostCenterAllocationGet,
|
|
13
|
+
)
|
|
5
14
|
from .schemas.in_service_config import JointCommitteeGet, FunctionGet
|
|
6
15
|
from brynq_sdk_functions import Functions
|
|
7
16
|
from typing import TYPE_CHECKING
|
|
@@ -67,46 +76,6 @@ class Agreements:
|
|
|
67
76
|
combined = pd.concat(all_rows, ignore_index=True) if all_rows else pd.DataFrame()
|
|
68
77
|
return Functions.validate_data(combined, FunctionGet)
|
|
69
78
|
|
|
70
|
-
def create_bank_account(self, agreement_id: str, data: Dict[str, Any], if_match: str, accept_language: str = "en") -> requests.Response:
|
|
71
|
-
"""
|
|
72
|
-
POST /v1/agreements/{agreementId}/remuneration-bank-account - Agreement Remuneration Bank Account
|
|
73
|
-
|
|
74
|
-
Create or update the remuneration bank account for an agreement. Requires the ETag
|
|
75
|
-
of the resource for optimistic concurrency control.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
agreement_id: Unique identifier of an agreement
|
|
79
|
-
data: Dictionary with bank account data (iban, bic)
|
|
80
|
-
if_match: The ETag of this resource (required for concurrency control)
|
|
81
|
-
accept_language: The language for the request (defaults to "en")
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
requests.Response: Raw response object
|
|
85
|
-
|
|
86
|
-
Raises:
|
|
87
|
-
Exception: If the bank account creation fails
|
|
88
|
-
"""
|
|
89
|
-
# Validate request data
|
|
90
|
-
validated_data = AgreementRemunerationBankAccountCreate(**data)
|
|
91
|
-
|
|
92
|
-
# Prepare headers
|
|
93
|
-
headers = {
|
|
94
|
-
"If-Match": if_match,
|
|
95
|
-
"Accept-Language": accept_language
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# Make API request
|
|
99
|
-
request_headers = self.acerta.session.headers.copy()
|
|
100
|
-
request_headers.update(headers)
|
|
101
|
-
response = self.acerta.session.post(
|
|
102
|
-
url=f"{self.acerta.base_url}/v1/agreements/{agreement_id}/remuneration-bank-account",
|
|
103
|
-
json=validated_data.model_dump(by_alias=True, exclude_none=True),
|
|
104
|
-
headers=request_headers,
|
|
105
|
-
timeout=self.acerta.TIMEOUT,
|
|
106
|
-
)
|
|
107
|
-
response.raise_for_status()
|
|
108
|
-
|
|
109
|
-
return response
|
|
110
79
|
|
|
111
80
|
def get(self) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
112
81
|
"""
|
|
@@ -245,7 +214,7 @@ class Agreements:
|
|
|
245
214
|
)
|
|
246
215
|
frames.append(df)
|
|
247
216
|
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
248
|
-
return combined,
|
|
217
|
+
return Functions.validate_data(combined, AgreementBasicInformationGet, debug=self.acerta.debug)
|
|
249
218
|
|
|
250
219
|
def get_employments(
|
|
251
220
|
self,
|
|
@@ -285,7 +254,7 @@ class Agreements:
|
|
|
285
254
|
)
|
|
286
255
|
frames.append(df)
|
|
287
256
|
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
288
|
-
return combined,
|
|
257
|
+
return Functions.validate_data(combined, AgreementEmploymentsGet, debug=self.acerta.debug)
|
|
289
258
|
|
|
290
259
|
def get_working_time(
|
|
291
260
|
self,
|
|
@@ -327,49 +296,8 @@ class Agreements:
|
|
|
327
296
|
)
|
|
328
297
|
frames.append(df)
|
|
329
298
|
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
330
|
-
return combined,
|
|
299
|
+
return Functions.validate_data(combined, AgreementWorkingTimeGet, debug=self.acerta.debug)
|
|
331
300
|
|
|
332
|
-
def get_remuneration(
|
|
333
|
-
self,
|
|
334
|
-
agreement_id: Optional[str] = None,
|
|
335
|
-
from_date: Optional[str] = None,
|
|
336
|
-
until_date: Optional[str] = None,
|
|
337
|
-
full_segments_only: Optional[bool] = None,
|
|
338
|
-
accept_language: str = "en",
|
|
339
|
-
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
340
|
-
"""GET /v3/agreements/{agreementId}/remuneration"""
|
|
341
|
-
params = {k: v for k, v in {
|
|
342
|
-
"fromDate": from_date,
|
|
343
|
-
"untilDate": until_date,
|
|
344
|
-
"fullSegmentsOnly": str(full_segments_only).lower() if isinstance(full_segments_only, bool) else full_segments_only,
|
|
345
|
-
}.items() if v is not None}
|
|
346
|
-
if not agreement_id and not self.acerta._agreement_ids:
|
|
347
|
-
self.get()
|
|
348
|
-
ids = [agreement_id] if agreement_id else self.acerta._agreement_ids
|
|
349
|
-
frames = []
|
|
350
|
-
headers = {"Accept-Language": accept_language} if accept_language else None
|
|
351
|
-
for agr_id in ids:
|
|
352
|
-
request_headers = self.acerta.session.headers.copy()
|
|
353
|
-
if headers:
|
|
354
|
-
request_headers.update(headers)
|
|
355
|
-
response = self.acerta.session.get(
|
|
356
|
-
url=f"{self.acerta.base_url}/{self.base_uri}/{agr_id}/remuneration",
|
|
357
|
-
params=params,
|
|
358
|
-
headers=request_headers,
|
|
359
|
-
timeout=self.acerta.TIMEOUT,
|
|
360
|
-
)
|
|
361
|
-
response.raise_for_status()
|
|
362
|
-
raw = response.json()
|
|
363
|
-
df = pd.json_normalize(
|
|
364
|
-
raw,
|
|
365
|
-
record_path=["remunerationSegments"],
|
|
366
|
-
meta=["agreementId"],
|
|
367
|
-
errors="ignore",
|
|
368
|
-
sep=".",
|
|
369
|
-
)
|
|
370
|
-
frames.append(df)
|
|
371
|
-
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
372
|
-
return combined, pd.DataFrame()
|
|
373
301
|
|
|
374
302
|
def get_commuting(
|
|
375
303
|
self,
|
|
@@ -411,7 +339,7 @@ class Agreements:
|
|
|
411
339
|
)
|
|
412
340
|
frames.append(df)
|
|
413
341
|
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
414
|
-
return combined,
|
|
342
|
+
return Functions.validate_data(combined, AgreementCommutingGet, debug=self.acerta.debug)
|
|
415
343
|
|
|
416
344
|
def get_custom_fields(
|
|
417
345
|
self,
|
|
@@ -455,7 +383,7 @@ class Agreements:
|
|
|
455
383
|
)
|
|
456
384
|
frames.append(df)
|
|
457
385
|
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
458
|
-
return combined,
|
|
386
|
+
return Functions.validate_data(combined, AgreementCustomFieldsGet, debug=self.acerta.debug)
|
|
459
387
|
|
|
460
388
|
def get_cost_center_allocation(
|
|
461
389
|
self,
|
|
@@ -495,4 +423,4 @@ class Agreements:
|
|
|
495
423
|
)
|
|
496
424
|
frames.append(df)
|
|
497
425
|
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
498
|
-
return combined,
|
|
426
|
+
return Functions.validate_data(combined, AgreementCostCenterAllocationGet, debug=self.acerta.debug)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional, Tuple
|
|
2
|
+
import requests
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from brynq_sdk_functions import Functions
|
|
5
|
+
from .schemas.agreement import AgreementRemunerationBankAccountUpdate
|
|
6
|
+
from .schemas.bank_account import AgreementBankAccountsGet
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .acerta import Acerta
|
|
10
|
+
|
|
11
|
+
class BankAccounts:
|
|
12
|
+
"""Resource class for Employee Bank Accounts (HAL links)."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, acerta):
|
|
15
|
+
self.acerta: Acerta = acerta
|
|
16
|
+
self.base_uri = "v1"
|
|
17
|
+
|
|
18
|
+
def get(self, agreement_id: Optional[str] = None, from_date: Optional[str] = None, until_date: Optional[str] = None, full_segments_only: Optional[bool] = None, accept_language: str = "en") -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
19
|
+
"""
|
|
20
|
+
GET /v3/agreements/{agreementId}/remuneration - derive bank accounts
|
|
21
|
+
|
|
22
|
+
Fetches remuneration segments for agreements and returns only the bank account related fields
|
|
23
|
+
(via methodOfPayment.employeeBankAccounts/employerBankAccounts).
|
|
24
|
+
"""
|
|
25
|
+
# Ensure we have agreement ids
|
|
26
|
+
if not agreement_id and not self.acerta._agreement_ids:
|
|
27
|
+
self.acerta.agreements.get()
|
|
28
|
+
agreement_ids = [agreement_id] if agreement_id else self.acerta._agreement_ids
|
|
29
|
+
|
|
30
|
+
params = {k: v for k, v in {
|
|
31
|
+
"fromDate": from_date,
|
|
32
|
+
"untilDate": until_date,
|
|
33
|
+
"fullSegmentsOnly": str(full_segments_only).lower() if isinstance(full_segments_only, bool) else full_segments_only,
|
|
34
|
+
}.items() if v is not None}
|
|
35
|
+
|
|
36
|
+
frames = []
|
|
37
|
+
headers = {"Accept-Language": accept_language} if accept_language else None
|
|
38
|
+
for agr_id in agreement_ids:
|
|
39
|
+
request_headers = self.acerta.session.headers.copy()
|
|
40
|
+
if headers:
|
|
41
|
+
request_headers.update(headers)
|
|
42
|
+
response = self.acerta.session.get(
|
|
43
|
+
url=f"{self.acerta.base_url}/agreement-data-management/v3/agreements/{agr_id}/remuneration",
|
|
44
|
+
params=params,
|
|
45
|
+
headers=request_headers,
|
|
46
|
+
timeout=self.acerta.TIMEOUT,
|
|
47
|
+
)
|
|
48
|
+
response.raise_for_status()
|
|
49
|
+
raw = response.json()
|
|
50
|
+
df = pd.json_normalize(
|
|
51
|
+
raw,
|
|
52
|
+
record_path=["remunerationSegments"],
|
|
53
|
+
meta=["agreementId"],
|
|
54
|
+
errors="ignore",
|
|
55
|
+
sep=".",
|
|
56
|
+
)
|
|
57
|
+
frames.append(df)
|
|
58
|
+
|
|
59
|
+
combined = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
60
|
+
return Functions.validate_data(combined, AgreementBankAccountsGet, debug=self.acerta.debug)
|
|
61
|
+
|
|
62
|
+
def update(self, agreement_id: str, data: Dict[str, Any]) -> requests.Response:
|
|
63
|
+
"""
|
|
64
|
+
PUT /employee-data-management/v3/employees/agreement/{agreementId}/remuneration-bank-account - Agreement Remuneration Bank Account
|
|
65
|
+
|
|
66
|
+
Set the bank account for the employee and agreement. This endpoint updates the remuneration
|
|
67
|
+
bank account associated with a specific agreement.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
agreement_id: Unique identifier of an agreement
|
|
71
|
+
data: Dictionary with bank account data (iban, bic)
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
requests.Response: Raw response object
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
Exception: If the update fails
|
|
78
|
+
"""
|
|
79
|
+
# Validate the data (no nesting required for this simple schema)
|
|
80
|
+
validated_data = AgreementRemunerationBankAccountUpdate(**data)
|
|
81
|
+
|
|
82
|
+
# Make API request
|
|
83
|
+
response = self.acerta.session.put(
|
|
84
|
+
url=f"{self.acerta.base_url}/employee-data-management/v3/employees/agreement/{agreement_id}/remuneration-bank-account",
|
|
85
|
+
json=validated_data.model_dump(by_alias=True, exclude_none=True),
|
|
86
|
+
timeout=self.acerta.TIMEOUT,
|
|
87
|
+
)
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
|
|
90
|
+
return response
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from typing import Tuple, Dict, Any, Optional
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import requests
|
|
4
|
+
from brynq_sdk_functions import Functions
|
|
5
|
+
from .schemas.company_car import CompanyCarGet, CompanyCarCreate, CompanyCarUpdate
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .acerta import Acerta
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CompanyCars:
|
|
12
|
+
"""Resource class for Company Cars endpoints"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, acerta):
|
|
15
|
+
self.acerta: Acerta = acerta
|
|
16
|
+
self.base_uri = "employer-data-management/v1/employers"
|
|
17
|
+
|
|
18
|
+
def get(
|
|
19
|
+
self,
|
|
20
|
+
from_date: str = "1900-01-01",
|
|
21
|
+
until_date: str = "9999-12-31",
|
|
22
|
+
license_plate: Optional[str] = None,
|
|
23
|
+
size: int = 20,
|
|
24
|
+
page: int = 0,
|
|
25
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
26
|
+
"""
|
|
27
|
+
GET /v1/employers/{employerId}/company-cars - Company cars
|
|
28
|
+
|
|
29
|
+
Retrieve company cars for all configured employer IDs within an optional time window
|
|
30
|
+
and optional license plate filter. Supports basic pagination parameters.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
from_date: The lower bound of the time window (default: "1900-01-01")
|
|
34
|
+
until_date: The upper bound of the time window (default: "9999-12-31")
|
|
35
|
+
license_plate: Optional license plate filter (or part of a plate)
|
|
36
|
+
size: Number of items per page (default: 20)
|
|
37
|
+
page: Zero-based page index (default: 0)
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Tuple[pd.DataFrame, pd.DataFrame]: (valid_df, invalid_df) after normalizing and validating
|
|
41
|
+
"""
|
|
42
|
+
all_valid_data = []
|
|
43
|
+
all_invalid_data = []
|
|
44
|
+
for employer_id in self.acerta._employer_ids:
|
|
45
|
+
params = {
|
|
46
|
+
"fromDate": from_date,
|
|
47
|
+
"untilDate": until_date,
|
|
48
|
+
"size": size,
|
|
49
|
+
"page": page,
|
|
50
|
+
}
|
|
51
|
+
if license_plate:
|
|
52
|
+
params["licensePlate"] = license_plate
|
|
53
|
+
response = self.acerta.session.get(
|
|
54
|
+
url=f"{self.acerta.base_url}/{self.base_uri}/{employer_id}/company-cars",
|
|
55
|
+
params=params,
|
|
56
|
+
timeout=self.acerta.TIMEOUT,
|
|
57
|
+
)
|
|
58
|
+
response.raise_for_status()
|
|
59
|
+
items = response.json().get("companyCars", [])
|
|
60
|
+
df = pd.json_normalize(items, sep='.')
|
|
61
|
+
valid_data, invalid_data = Functions.validate_data(df, CompanyCarGet)
|
|
62
|
+
all_valid_data.append(valid_data)
|
|
63
|
+
all_invalid_data.append(invalid_data)
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
pd.concat(all_valid_data, ignore_index=True) if all_valid_data else pd.DataFrame(),
|
|
67
|
+
pd.concat(all_invalid_data, ignore_index=True) if all_invalid_data else pd.DataFrame(),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def get_by_id(self, employer_id: str, company_car_id: str) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
71
|
+
"""
|
|
72
|
+
GET /v1/employers/{employerId}/company-cars/{companyCarId} - Company car by ID
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
employer_id: Unique identifier of the employer (Acerta key)
|
|
76
|
+
company_car_id: Unique identifier of the company car (6 digits)
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Tuple[pd.DataFrame, pd.DataFrame]: (valid_df, invalid_df)
|
|
80
|
+
"""
|
|
81
|
+
response = self.acerta.session.get(
|
|
82
|
+
url=f"{self.acerta.base_url}/{self.base_uri}/{employer_id}/company-cars/{company_car_id}",
|
|
83
|
+
timeout=self.acerta.TIMEOUT,
|
|
84
|
+
)
|
|
85
|
+
response.raise_for_status()
|
|
86
|
+
raw = response.json()
|
|
87
|
+
if isinstance(raw, dict):
|
|
88
|
+
df = pd.json_normalize(raw, sep='.')
|
|
89
|
+
else:
|
|
90
|
+
df = pd.json_normalize([raw], sep='.')
|
|
91
|
+
return Functions.validate_data(df, CompanyCarGet)
|
|
92
|
+
|
|
93
|
+
def create(self, employer_id: str, data: Dict[str, Any]) -> requests.Response:
|
|
94
|
+
"""
|
|
95
|
+
POST /v1/employers/{employerId}/company-cars - Company car
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
employer_id: Unique identifier of the employer (Acerta key)
|
|
99
|
+
data: Flat dictionary with company car data
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
requests.Response: Raw response object
|
|
103
|
+
"""
|
|
104
|
+
# Convert flat data to nested using Functions.flat_to_nested_with_prefix
|
|
105
|
+
nested = Functions.flat_to_nested_with_prefix(data, CompanyCarCreate)
|
|
106
|
+
validated = CompanyCarCreate(**nested)
|
|
107
|
+
response = self.acerta.session.post(
|
|
108
|
+
url=f"{self.acerta.base_url}/{self.base_uri}/{employer_id}/company-cars",
|
|
109
|
+
json=validated.model_dump(by_alias=True, exclude_none=True),
|
|
110
|
+
timeout=self.acerta.TIMEOUT,
|
|
111
|
+
)
|
|
112
|
+
response.raise_for_status()
|
|
113
|
+
return response
|
|
114
|
+
|
|
115
|
+
def update(self, employer_id: str, company_car_id: str, data: Dict[str, Any]) -> requests.Response:
|
|
116
|
+
"""
|
|
117
|
+
PATCH /v1/employers/{employerId}/company-cars/{companyCarId} - Update company car
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
employer_id: Unique identifier of the employer (Acerta key)
|
|
121
|
+
company_car_id: Unique identifier of the company car
|
|
122
|
+
data: Flat dictionary with update fields
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
requests.Response: Raw response object
|
|
126
|
+
"""
|
|
127
|
+
nested = Functions.flat_to_nested_with_prefix(data, CompanyCarUpdate)
|
|
128
|
+
validated = CompanyCarUpdate(**nested)
|
|
129
|
+
response = self.acerta.session.patch(
|
|
130
|
+
url=f"{self.acerta.base_url}/{self.base_uri}/{employer_id}/company-cars/{company_car_id}",
|
|
131
|
+
json=validated.model_dump(by_alias=True, exclude_none=True),
|
|
132
|
+
timeout=self.acerta.TIMEOUT,
|
|
133
|
+
)
|
|
134
|
+
response.raise_for_status()
|
|
135
|
+
return response
|
|
@@ -9,10 +9,11 @@ if TYPE_CHECKING:
|
|
|
9
9
|
|
|
10
10
|
class FamilyMembers:
|
|
11
11
|
"""Resource class for Family endpoints"""
|
|
12
|
+
# According to Acerta this is hardly ever used, is not relevant for payroll.
|
|
12
13
|
|
|
13
14
|
def __init__(self, acerta):
|
|
14
15
|
self.acerta: Acerta = acerta
|
|
15
|
-
self.base_uri = "employee-data-management/
|
|
16
|
+
self.base_uri = "employee-data-management/v2/employees"
|
|
16
17
|
|
|
17
18
|
def get(self, employee_id: str, from_date: str = "1900-01-01", until_date: str = "9999-12-31",
|
|
18
19
|
full_segments_only: bool = False) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
@@ -19,7 +19,7 @@ from .family import (
|
|
|
19
19
|
Dependants,
|
|
20
20
|
FiscalDetails
|
|
21
21
|
)
|
|
22
|
-
from .bank_account import
|
|
22
|
+
from .bank_account import BankAccountUpdate, BankOwner, OwnerName, BankDetails
|
|
23
23
|
from .employee import (
|
|
24
24
|
AdditionalInformationGet,
|
|
25
25
|
AdditionalInformationUpdate,
|
|
@@ -55,6 +55,7 @@ from .employer import JointCommitteeGet, FunctionGet, SalaryCodeGet
|
|
|
55
55
|
from .in_service import EmploymentCreate, EmploymentRehire
|
|
56
56
|
from .cost_center import CostCenterGet, CostCenterCreate, CostCenterDescription, CostCenterPeriod
|
|
57
57
|
from .salaries import BasicSalaryGet, PatchBasicSalariesRequest
|
|
58
|
+
from .company_car import CompanyCarGet, CompanyCarCreate, CompanyCarUpdate
|
|
58
59
|
|
|
59
60
|
__all__ = [
|
|
60
61
|
'AgreementGet',
|
|
@@ -127,5 +128,8 @@ __all__ = [
|
|
|
127
128
|
'CostCenterDescription',
|
|
128
129
|
'CostCenterPeriod',
|
|
129
130
|
'BasicSalaryGet',
|
|
130
|
-
'PatchBasicSalariesRequest'
|
|
131
|
+
'PatchBasicSalariesRequest',
|
|
132
|
+
'CompanyCarGet',
|
|
133
|
+
'CompanyCarCreate',
|
|
134
|
+
'CompanyCarUpdate'
|
|
131
135
|
]
|
|
@@ -69,6 +69,9 @@ CorrespondenceAddressUpdate = Address
|
|
|
69
69
|
|
|
70
70
|
class AddressUpdate(BaseModel):
|
|
71
71
|
"""Schema for PATCH /employee-data-management/v3/employees/{employeeId}/addresses endpoint"""
|
|
72
|
+
# Function parameters
|
|
73
|
+
employee_id: str = Field(..., description="Employee identifier", alias="employeeId")
|
|
74
|
+
|
|
72
75
|
from_date: Optional[str] = Field(None, example="2022-01-01", description="Date from which this information is valid", alias="fromDate")
|
|
73
76
|
official_address: Optional[Address] = Field(None, description="Official address of the employee", alias="officialAddress", json_schema_extra={"prefix": "official_address_"})
|
|
74
77
|
correspondence_address: Optional[Address] = Field(None, description="Correspondence address of the employee", alias="correspondenceAddress", json_schema_extra={"prefix": "correspondence_address_"})
|