brynq-sdk-bob 2.2.1__tar.gz → 2.4.0__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.
Files changed (32) hide show
  1. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/PKG-INFO +1 -1
  2. brynq_sdk_bob-2.4.0/brynq_sdk_bob/payments.py +114 -0
  3. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/payments.py +15 -1
  4. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/people.py +2 -2
  5. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/salary.py +1 -1
  6. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/timeoff.py +18 -0
  7. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/timeoff.py +33 -1
  8. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob.egg-info/PKG-INFO +1 -1
  9. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/setup.py +1 -1
  10. brynq_sdk_bob-2.2.1/brynq_sdk_bob/payments.py +0 -23
  11. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/__init__.py +0 -0
  12. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/bank.py +0 -0
  13. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/company.py +0 -0
  14. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/custom_tables.py +0 -0
  15. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/documents.py +0 -0
  16. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/employment.py +0 -0
  17. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/named_lists.py +0 -0
  18. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/people.py +0 -0
  19. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/salaries.py +0 -0
  20. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/__init__.py +0 -0
  21. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/bank.py +0 -0
  22. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/custom_tables.py +0 -0
  23. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/employment.py +0 -0
  24. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/named_lists.py +0 -0
  25. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/schemas/work.py +0 -0
  26. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob/work.py +0 -0
  27. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob.egg-info/SOURCES.txt +0 -0
  28. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob.egg-info/dependency_links.txt +0 -0
  29. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob.egg-info/not-zip-safe +0 -0
  30. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob.egg-info/requires.txt +0 -0
  31. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/brynq_sdk_bob.egg-info/top_level.txt +0 -0
  32. {brynq_sdk_bob-2.2.1 → brynq_sdk_bob-2.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq_sdk_bob
3
- Version: 2.2.1
3
+ Version: 2.4.0
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -0,0 +1,114 @@
1
+ import pandas as pd
2
+ from typing import Optional, List
3
+ from brynq_sdk_functions import Functions
4
+ from .schemas.payments import VariablePaymentSchema, ActualPaymentsSchema
5
+
6
+
7
+ class Payments:
8
+ def __init__(self, bob):
9
+ self.bob = bob
10
+ self.schema = VariablePaymentSchema
11
+
12
+ def get(self, person_id: str) -> (pd.DataFrame, pd.DataFrame):
13
+ resp = self.bob.session.get(url=f"{self.bob.base_url}people/{person_id}/variable", timeout=self.bob.timeout)
14
+ resp.raise_for_status()
15
+ data = resp.json()
16
+ df = pd.json_normalize(
17
+ data,
18
+ record_path='values'
19
+ )
20
+ df['employee_id'] = person_id
21
+ valid_payments, invalid_payments = Functions.validate_data(df=df, schema=self.schema, debug=True)
22
+
23
+ return valid_payments, invalid_payments
24
+
25
+ def get_actual_payments(
26
+ self,
27
+ limit: int = 200,
28
+ employee_ids: Optional[List[str]] = None,
29
+ pay_date_from: Optional[str] = None,
30
+ pay_date_to: Optional[str] = None
31
+ ) -> (pd.DataFrame, pd.DataFrame):
32
+ """
33
+ Search for actual payments with optional employee and pay date filters.
34
+ This method auto-paginates until all results are fetched.
35
+
36
+ See Bob API: https://apidocs.hibob.com/reference/post_people-actual-payments-search
37
+ See Pagination: https://apidocs.hibob.com/docs/pagination
38
+
39
+ Args:
40
+ limit (int): Number of records per page (default: 50, max: 200).
41
+ employee_ids (Optional[List[str]]): Filter by employee IDs.
42
+ pay_date_from (Optional[str]): Inclusive start date filter (YYYY-MM-DD).
43
+ pay_date_to (Optional[str]): Inclusive end date filter (YYYY-MM-DD).
44
+
45
+ Returns:
46
+ tuple: (valid_payments DataFrame, invalid_payments DataFrame)
47
+ """
48
+ base_payload = {
49
+ "pagination": {
50
+ "limit": limit
51
+ }
52
+ }
53
+
54
+ filters = []
55
+ if employee_ids:
56
+ filters.append({
57
+ "fieldPath": "employeeId",
58
+ "operator": "equals",
59
+ "values": employee_ids
60
+ })
61
+ if pay_date_from:
62
+ filters.append({
63
+ "fieldPath": "payDate",
64
+ "operator": "greaterThanOrEquals",
65
+ "value": pay_date_from
66
+ })
67
+ if pay_date_to:
68
+ filters.append({
69
+ "fieldPath": "payDate",
70
+ "operator": "lessThanOrEquals",
71
+ "value": pay_date_to
72
+ })
73
+
74
+ if filters:
75
+ base_payload["filters"] = filters
76
+
77
+ all_results = []
78
+ next_cursor = None
79
+
80
+ while True:
81
+ payload = dict(base_payload)
82
+ payload["pagination"] = dict(base_payload["pagination"])
83
+ if next_cursor:
84
+ payload["pagination"]["cursor"] = next_cursor
85
+
86
+ resp = self.bob.session.post(
87
+ url=f"{self.bob.base_url}people/actual-payments/search",
88
+ json=payload,
89
+ timeout=self.bob.timeout
90
+ )
91
+ resp.raise_for_status()
92
+ data = resp.json()
93
+
94
+ page_results = data.get('results') or []
95
+ if page_results:
96
+ all_results.extend(page_results)
97
+
98
+ next_cursor = (data.get('response_metadata') or {}).get('next_cursor')
99
+ if not next_cursor:
100
+ break
101
+
102
+ if not all_results:
103
+ empty_df = pd.DataFrame()
104
+ return empty_df, empty_df
105
+
106
+ df = pd.json_normalize(all_results)
107
+
108
+ valid_payments, invalid_payments = Functions.validate_data(
109
+ df=df,
110
+ schema=ActualPaymentsSchema,
111
+ debug=True
112
+ )
113
+
114
+ return valid_payments, invalid_payments
@@ -26,6 +26,20 @@ class VariablePaymentSchema(BrynQPanderaDataFrameModel):
26
26
  change_reason: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Reason", alias="change.reason")
27
27
  change_changed_by: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Changed By", alias="change.changedBy")
28
28
  change_changed_by_id: Series[pd.Int64Dtype] = pa.Field(nullable=True, coerce=True, description="Change Changed By ID", alias="change.changedById")
29
- employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="employeeId") #set manually
29
+ employee_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Employee ID", alias="employee_id") #set manually
30
+ class Config:
31
+ coerce = True
32
+
33
+ class ActualPaymentsSchema(BrynQPanderaDataFrameModel):
34
+ employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
35
+ id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Payment ID", alias="id")
36
+ pay_date: Series[DateTime] = pa.Field(coerce=True, description="Pay Date", alias="payDate")
37
+ pay_type: Series[String] = pa.Field(coerce=True, description="Pay Type", alias="payType")
38
+ amount_value: Series[Float] = pa.Field(coerce=True, description="Amount Value", alias="amount.value")
39
+ amount_currency: Series[String] = pa.Field(coerce=True, description="Amount Currency", alias="amount.currency")
40
+ change_reason: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Reason", alias="change.reason")
41
+ change_changed_by: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Changed By", alias="change.changedBy")
42
+ change_changed_by_id: Series[String] = pa.Field(nullable=True, coerce=True, description="Change Changed By ID", alias="change.changedById")
43
+
30
44
  class Config:
31
45
  coerce = True
@@ -16,7 +16,7 @@ def check_list(x):
16
16
 
17
17
 
18
18
  class PeopleSchema(BrynQPanderaDataFrameModel):
19
- id: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, description="Person ID", alias="id")
19
+ id: Optional[Series[String]] = pa.Field(coerce=True, description="Person ID", alias="id")
20
20
  display_name: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Display Name", alias="displayName")
21
21
  company_id: Optional[Series[String]] = pa.Field(coerce=True, description="Company ID", alias="companyId")
22
22
  email: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Email", alias="email")
@@ -63,7 +63,7 @@ class PeopleSchema(BrynQPanderaDataFrameModel):
63
63
  work_active_effective_date: Optional[Series[DateTime]] = pa.Field(coerce=True, nullable=True, description="Work Active Effective Date", alias="work.activeEffectiveDate")
64
64
  work_direct_reports: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Direct Reports", alias="work.directReports")
65
65
  # work_work_change_type: Series[String] = pa.Field(coerce=True, nullable=True)
66
- work_second_level_manager: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Second Level Manager", alias="work.secondLevelManager")
66
+ work_second_level_manager: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Work Second Level Manager", alias="work.secondLevelManager")
67
67
  work_days_of_previous_service: Optional[Series[pd.Int64Dtype]] = pa.Field(coerce=True, nullable=True, description="Work Days of Previous Service", alias="work.daysOfPreviousService")
68
68
  work_years_of_service: Optional[Series[Float]] = pa.Field(coerce=True, nullable=True, description="Work Years of Service", alias="work.yearsOfService")
69
69
  payroll_employment_contract: Optional[Series[String]] = pa.Field(coerce=True, nullable=True, description="Contract Type", alias="payroll.employment.contract")
@@ -7,7 +7,7 @@ from brynq_sdk_functions import BrynQPanderaDataFrameModel
7
7
 
8
8
 
9
9
  class SalarySchema(BrynQPanderaDataFrameModel):
10
- id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Salary ID", alias="id")
10
+ id: Series[String] = pa.Field(coerce=True, description="Salary ID", alias="id")
11
11
  employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
12
12
  pay_frequency: Series[String] = pa.Field(coerce=True, nullable=True, description="Pay Frequency", alias="payFrequency") # has a list of possible values , isin=['Monthly']
13
13
  creation_date: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Creation Date", alias="creationDate")
@@ -27,3 +27,21 @@ class TimeOffSchema(BrynQPanderaDataFrameModel):
27
27
 
28
28
  class Config:
29
29
  coerce = True
30
+
31
+
32
+ class TimeOffBalanceSchema(BrynQPanderaDataFrameModel):
33
+ employee_id: Series[String] = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
34
+ policy_type_name: Series[String] = pa.Field(coerce=True, description="Policy Type Name", alias="policyTypeName")
35
+ policy_type_display_name: Series[String] = pa.Field(coerce=True, description="Policy Type Display Name", alias="policyTypeDisplayName")
36
+ balance: Series[Float] = pa.Field(coerce=True, description="Current Balance", alias="balance")
37
+ used: Series[Float] = pa.Field(coerce=True, description="Used Balance", alias="used")
38
+ available: Series[Float] = pa.Field(coerce=True, description="Available Balance", alias="available")
39
+ approved_requests: Series[Float] = pa.Field(coerce=True, description="Approved Requests", alias="approvedRequests")
40
+ pending_requests: Series[Float] = pa.Field(coerce=True, description="Pending Requests", alias="pendingRequests")
41
+ as_of_date: Series[String] = pa.Field(coerce=True, description="As of Date", alias="asOfDate")
42
+ accrual_start_date: Series[String] = pa.Field(nullable=True, coerce=True, description="Accrual Start Date", alias="accrualStartDate")
43
+ expiry_date: Series[String] = pa.Field(nullable=True, coerce=True, description="Expiry Date", alias="expiryDate")
44
+ duration_unit: Series[String] = pa.Field(coerce=True, description="Duration Unit", alias="durationUnit")
45
+
46
+ class Config:
47
+ coerce = True
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime, timezone, timedelta
2
2
  import pandas as pd
3
3
  from brynq_sdk_functions import Functions
4
- from .schemas.timeoff import TimeOffSchema
4
+ from .schemas.timeoff import TimeOffSchema, TimeOffBalanceSchema
5
5
  import warnings
6
6
 
7
7
 
@@ -9,6 +9,7 @@ class TimeOff:
9
9
  def __init__(self, bob):
10
10
  self.bob = bob
11
11
  self.schema = TimeOffSchema
12
+ self.balance_schema = TimeOffBalanceSchema
12
13
 
13
14
  def get(self, since: datetime = None) -> tuple[pd.DataFrame, pd.DataFrame]:
14
15
  """
@@ -49,3 +50,34 @@ class TimeOff:
49
50
  valid_timeoff, invalid_timeoff = Functions.validate_data(df=df, schema=self.schema, debug=True)
50
51
 
51
52
  return valid_timeoff, invalid_timeoff
53
+
54
+ def get_balance(self, employee_id: str, policy_type: str = None, as_of_date: str = None) -> tuple[pd.DataFrame, pd.DataFrame]:
55
+ """
56
+ Get time off balance for a specific employee
57
+
58
+ Args:
59
+ employee_id (str): The ID of the employee
60
+ policy_type (str, optional): The policy type to filter by. Defaults to None.
61
+ as_of_date (str, optional): The date to get balance as of (YYYY-MM-DD format). Defaults to None.
62
+
63
+ Returns:
64
+ tuple[pd.DataFrame, pd.DataFrame]: A tuple of (valid_balance, invalid_balance) as pandas DataFrames
65
+ """
66
+ params = {}
67
+ if policy_type:
68
+ params['policyType'] = policy_type
69
+ if as_of_date:
70
+ params['asOfDate'] = as_of_date
71
+
72
+ resp = self.bob.session.get(
73
+ url=f"{self.bob.base_url}timeoff/employees/{employee_id}/balance",
74
+ params=params,
75
+ timeout=self.bob.timeout
76
+ )
77
+ resp.raise_for_status()
78
+ data = resp.json()
79
+ df = pd.DataFrame(data)
80
+
81
+ valid_balance, invalid_balance = Functions.validate_data(df=df, schema=self.balance_schema, debug=True)
82
+
83
+ return valid_balance, invalid_balance
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: brynq-sdk-bob
3
- Version: 2.2.1
3
+ Version: 2.4.0
4
4
  Summary: Bob wrapper from BrynQ
5
5
  Home-page: UNKNOWN
6
6
  Author: BrynQ
@@ -2,7 +2,7 @@ from setuptools import setup, find_namespace_packages
2
2
 
3
3
  setup(
4
4
  name='brynq_sdk_bob',
5
- version='2.2.1',
5
+ version='2.4.0',
6
6
  description='Bob wrapper from BrynQ',
7
7
  long_description='Bob wrapper from BrynQ',
8
8
  author='BrynQ',
@@ -1,23 +0,0 @@
1
- from datetime import datetime
2
- import pandas as pd
3
- from brynq_sdk_functions import Functions
4
- from .schemas.payments import VariablePaymentSchema
5
-
6
-
7
- class Payments:
8
- def __init__(self, bob):
9
- self.bob = bob
10
- self.schema = VariablePaymentSchema
11
-
12
- def get(self, person_id: str) -> (pd.DataFrame, pd.DataFrame):
13
- resp = self.bob.session.get(url=f"{self.bob.base_url}people/{person_id}/variable", timeout=self.bob.timeout)
14
- resp.raise_for_status()
15
- data = resp.json()
16
- df = pd.json_normalize(
17
- data,
18
- record_path='values'
19
- )
20
- df['employee_id'] = person_id
21
- valid_payments, invalid_payments = Functions.validate_data(df=df, schema=self.schema, debug=True)
22
-
23
- return valid_payments, invalid_payments
File without changes