brynq-sdk-alight 1.0.0__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 (44) hide show
  1. brynq_sdk_alight/__init__.py +1019 -0
  2. brynq_sdk_alight/address.py +72 -0
  3. brynq_sdk_alight/archive/flat_wrapper.py +139 -0
  4. brynq_sdk_alight/archive/hrxml_generator.py +280 -0
  5. brynq_sdk_alight/archive/managers.py +132 -0
  6. brynq_sdk_alight/archive/managers_generic.py +114 -0
  7. brynq_sdk_alight/archive/managers_old_complex.py +294 -0
  8. brynq_sdk_alight/archive/managers_simple.py +229 -0
  9. brynq_sdk_alight/employee.py +81 -0
  10. brynq_sdk_alight/job.py +89 -0
  11. brynq_sdk_alight/leave.py +97 -0
  12. brynq_sdk_alight/pay_elements.py +97 -0
  13. brynq_sdk_alight/salary.py +89 -0
  14. brynq_sdk_alight/schemas/__init__.py +26 -0
  15. brynq_sdk_alight/schemas/absence.py +83 -0
  16. brynq_sdk_alight/schemas/address.py +113 -0
  17. brynq_sdk_alight/schemas/employee.py +641 -0
  18. brynq_sdk_alight/schemas/generated_envelope_xsd_schema/__init__.py +38683 -0
  19. brynq_sdk_alight/schemas/generated_envelope_xsd_schema/process_pay_serv_emp.py +622264 -0
  20. brynq_sdk_alight/schemas/generated_xsd_schemas/__init__.py +10965 -0
  21. brynq_sdk_alight/schemas/generated_xsd_schemas/csec_person.py +39808 -0
  22. brynq_sdk_alight/schemas/generated_xsd_schemas/hrxml_indicative_data.py +90318 -0
  23. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_bod.py +33869 -0
  24. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_code_list_currency_code_iso_7_04.py +365 -0
  25. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_code_list_language_code_iso_7_04.py +16 -0
  26. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_code_list_mimemedia_type_code_iana_7_04.py +16 -0
  27. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_code_list_unit_code_unece_7_04.py +14 -0
  28. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_code_lists.py +535 -0
  29. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_qualified_data_types.py +84 -0
  30. brynq_sdk_alight/schemas/generated_xsd_schemas/openapplications_unqualified_data_types.py +1449 -0
  31. brynq_sdk_alight/schemas/job.py +129 -0
  32. brynq_sdk_alight/schemas/leave.py +58 -0
  33. brynq_sdk_alight/schemas/payments.py +207 -0
  34. brynq_sdk_alight/schemas/salary.py +67 -0
  35. brynq_sdk_alight/schemas/termination.py +48 -0
  36. brynq_sdk_alight/schemas/timequota.py +66 -0
  37. brynq_sdk_alight/schemas/utils.py +452 -0
  38. brynq_sdk_alight/termination.py +103 -0
  39. brynq_sdk_alight/time_elements.py +121 -0
  40. brynq_sdk_alight/time_quotas.py +114 -0
  41. brynq_sdk_alight-1.0.0.dist-info/METADATA +20 -0
  42. brynq_sdk_alight-1.0.0.dist-info/RECORD +44 -0
  43. brynq_sdk_alight-1.0.0.dist-info/WHEEL +5 -0
  44. brynq_sdk_alight-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,129 @@
1
+ """
2
+ Flat, user-friendly job/deployment model for Alight SDK.
3
+ """
4
+
5
+ import datetime
6
+ from typing import List, Optional, Dict, Any
7
+ from pydantic import Field, BaseModel
8
+
9
+ from .utils import add_to_nested_path # not used here; kept for parity if needed
10
+
11
+
12
+ class Job(BaseModel):
13
+ """
14
+ Simplified job/deployment model.
15
+ Uses PURE schema-driven conversion - NO hardcoded structure mappings.
16
+ Uses aliases to match expected field names in Employee model.
17
+ """
18
+ model_config = {
19
+ "populate_by_name": True # Allow populating by field name in addition to alias
20
+ }
21
+
22
+ title: str = Field(
23
+ description="Job title",
24
+ alias="indicative_person_dossier.indicative_deployment.job.job_title"
25
+ )
26
+ department: Optional[str] = Field(
27
+ default=None,
28
+ description="Department name",
29
+ alias="indicative_person_dossier.indicative_deployment.department_name"
30
+ )
31
+ position_id: Optional[str] = Field(
32
+ default=None,
33
+ description="Position identifier",
34
+ alias="indicative_person_dossier.indicative_deployment.position_id"
35
+ )
36
+ position_title: Optional[str] = Field(
37
+ default=None,
38
+ description="Position title",
39
+ alias="indicative_person_dossier.indicative_deployment.position_title"
40
+ )
41
+ location_code: Optional[str] = Field(
42
+ default=None,
43
+ description="Work location code",
44
+ alias="indicative_person_dossier.indicative_deployment.work_location.location_id"
45
+ )
46
+ work_location_city_name: Optional[str] = Field(
47
+ default=None,
48
+ description="Work location city name",
49
+ alias="indicative_person_dossier.indicative_deployment.work_location.address.city_name"
50
+ )
51
+ work_location_country_sub_division_code: Optional[str] = Field(
52
+ default=None,
53
+ description="Work location country subdivision/state code",
54
+ alias="indicative_person_dossier.indicative_deployment.work_location.address.country_sub_division_code"
55
+ )
56
+ work_location_postal_code: Optional[str] = Field(
57
+ default=None,
58
+ description="Work location postal code",
59
+ alias="indicative_person_dossier.indicative_deployment.work_location.address.postal_code"
60
+ )
61
+ manager_id: Optional[str] = Field(
62
+ default=None,
63
+ description="Manager employee ID",
64
+ alias="indicative_person_dossier.indicative_deployment.manager_id.id"
65
+ )
66
+
67
+ # Schedule
68
+ weekly_hours: Optional[float] = Field(
69
+ default=None,
70
+ description="Weekly working hours",
71
+ alias="indicative_person_dossier.indicative_deployment.schedule.scheduled_hours[0].value"
72
+ )
73
+ weekly_hours_basis: Optional[str] = Field(
74
+ default="Week",
75
+ description="Schedule basis for weekly hours",
76
+ alias="indicative_person_dossier.indicative_deployment.schedule.scheduled_hours[0].schedule_basis"
77
+ )
78
+ pay_cycle_hours: Optional[float] = Field(
79
+ default=None,
80
+ description="Hours per pay cycle",
81
+ alias="indicative_person_dossier.indicative_deployment.schedule.scheduled_hours[1].value"
82
+ )
83
+ pay_cycle_hours_basis: Optional[str] = Field(
84
+ default=None,
85
+ description="Schedule basis for pay cycle hours",
86
+ alias="indicative_person_dossier.indicative_deployment.schedule.scheduled_hours[1].schedule_basis"
87
+ )
88
+ scheduled_days_per_week: Optional[float] = Field(
89
+ default=None,
90
+ description="Scheduled days per week",
91
+ alias="indicative_person_dossier.indicative_deployment.schedule.scheduled_days[0].value"
92
+ )
93
+ scheduled_days_basis: Optional[str] = Field(
94
+ default=None,
95
+ description="Schedule basis for scheduled days",
96
+ alias="indicative_person_dossier.indicative_deployment.schedule.scheduled_days[0].schedule_basis"
97
+ )
98
+ day_schedule_id: Optional[str] = Field(
99
+ default=None,
100
+ description="Day schedule ID",
101
+ alias="indicative_person_dossier.indicative_deployment.schedule.day_schedule[0].id"
102
+ )
103
+ fte_ratio: Optional[str] = Field(
104
+ default=None,
105
+ description="Full-time equivalent ratio",
106
+ alias="indicative_person_dossier.indicative_deployment.full_time_equivalent_ratio"
107
+ )
108
+ work_level_code: Optional[str] = Field(
109
+ default="FullTime",
110
+ description="FullTime/PartTime",
111
+ alias="indicative_person_dossier.indicative_deployment.work_level_code"
112
+ )
113
+
114
+ # Organization fields removed - these belong in Employee model
115
+
116
+ # Dates - employment lifecycle dates moved to Employee model
117
+ valid_from: Optional[datetime.date] = Field(
118
+ default=None,
119
+ description="Valid from date",
120
+ alias="indicative_person_dossier.indicative_deployment.valid_from"
121
+ )
122
+
123
+ def to_nested_dict(self) -> Dict[str, Any]:
124
+ """
125
+ Convert to nested structure compatible with employee job fields.
126
+ Rely on index-aware aliases.
127
+ """
128
+ data = self.model_dump(exclude_none=True, by_alias=True)
129
+ return data
@@ -0,0 +1,58 @@
1
+ """
2
+ Flat, user-friendly leave model for Alight SDK.
3
+ """
4
+
5
+ import datetime
6
+ from typing import Optional, Dict, Any
7
+ from pydantic import Field, BaseModel
8
+
9
+ from .utils import add_to_nested_path # not used here
10
+
11
+
12
+ class Leave(BaseModel):
13
+ """
14
+ Simplified leave model.
15
+ Uses PURE schema-driven conversion - NO hardcoded structure mappings.
16
+ Uses aliases to match expected field names in Employee model.
17
+ Maps to IndicativeEmployment.employment_lifecycle.leave[0].
18
+ """
19
+ model_config = {
20
+ "populate_by_name": True
21
+ }
22
+
23
+ leave_reason_code: Optional[str] = Field(
24
+ default=None,
25
+ description="Reason for leave",
26
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.leave[0].leave_reason_code"
27
+ )
28
+ leave_status_code: Optional[str] = Field(
29
+ default=None,
30
+ description="Current leave status",
31
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.leave[0].leave_status_code"
32
+ )
33
+ leave_start_date: Optional[datetime.date] = Field(
34
+ default=None,
35
+ description="Leave start date",
36
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.leave[0].leave_start_date"
37
+ )
38
+ last_paid_date: Optional[datetime.date] = Field(
39
+ default=None,
40
+ description="Last paid date during leave",
41
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.leave[0].last_paid_date"
42
+ )
43
+ scheduled_work_return_date: Optional[datetime.date] = Field(
44
+ default=None,
45
+ description="Scheduled work return date",
46
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.leave[0].scheduled_work_return_date"
47
+ )
48
+ work_return_date: Optional[datetime.date] = Field(
49
+ default=None,
50
+ description="Actual work return date",
51
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.leave[0].work_return_date"
52
+ )
53
+
54
+ def to_nested_dict(self) -> Dict[str, Any]:
55
+ """
56
+ Convert to nested structure compatible with employee model.
57
+ """
58
+ return self.model_dump(exclude_none=True, by_alias=True)
@@ -0,0 +1,207 @@
1
+ from typing import Optional, Dict, Any
2
+ import datetime
3
+ from pydantic import Field, BaseModel
4
+
5
+ from .utils import add_to_nested_path, post_process_nested_data, convert_datetime_to_xml, construct_model
6
+ from .absence import Absences
7
+ from .timequota import TimeQuotas
8
+ from ..schemas.generated_envelope_xsd_schema.process_pay_serv_emp import PayServEmpExtension as XsdPayServEmpExtension
9
+ from ..schemas.generated_envelope_xsd_schema.process_pay_serv_emp import PayServEmpPayElements as XsdPayServEmpPayElements
10
+
11
+
12
+ class PayServEmpExtensionCreate(BaseModel):
13
+ """
14
+ Alias-driven flat model for NGA PayServEmpExtension (index-aware paths, no custom logic).
15
+ """
16
+ model_config = {
17
+ "populate_by_name": True
18
+ }
19
+ # Payment Instructions [0]
20
+ payment_valid_from: Optional[datetime.date] = Field(default=None, alias="payment_instructions[0].valid_from")
21
+ payment_type: Optional[str] = Field(default=None, alias="payment_instructions[0].payment_type")
22
+ payment_method: Optional[str] = Field(default=None, alias="payment_instructions[0].payment_method")
23
+ payment_type_code: Optional[str] = Field(default=None, alias="payment_instructions[0].payment_type_code")
24
+ local_payment_method: Optional[str] = Field(default=None, alias="payment_instructions[0].local_payment_method")
25
+ amount: Optional[list[str]] = Field(default=None, alias="payment_instructions[0].amount")
26
+ currency_code: Optional[str] = Field(default=None, alias="payment_instructions[0].currency_code")
27
+ payment_percentage: Optional[list[str]] = Field(default=None, alias="payment_instructions[0].payment_percentage")
28
+ name_on_account: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.name_on_account")
29
+ account_type_code: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.type_code[0]")
30
+ bank_routing_id: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.bank_routing_id[0]")
31
+ bank_routing_id_swift: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.bank_routing_id[1]")
32
+ bank_routing_id_swift_scheme_name: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.bank_routing_id[1].scheme_name")
33
+ account_id: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.account_id[0]")
34
+ iban: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.iban")
35
+ account_country_code: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.country_code[0]")
36
+ account_currency_code: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.currency_code")
37
+ additional_account_id: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.additional_account_id")
38
+ account_postal_code: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.postal_code")
39
+ account_city_name: Optional[str] = Field(default=None, alias="payment_instructions[0].direct_deposit_account.city_name")
40
+
41
+ # Payment Instructions [1] (second instruction, e.g., OTHER)
42
+ payment2_valid_from: Optional[datetime.date] = Field(default=None, alias="payment_instructions[1].valid_from")
43
+ payment2_type: Optional[str] = Field(default=None, alias="payment_instructions[1].payment_type")
44
+ payment2_method: Optional[str] = Field(default=None, alias="payment_instructions[1].payment_method")
45
+ payment2_type_code: Optional[str] = Field(default=None, alias="payment_instructions[1].payment_type_code")
46
+ payment2_amount: Optional[str] = Field(default=None, alias="payment_instructions[1].amount")
47
+ name_on_account2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.name_on_account")
48
+ account_type_code_2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.type_code[0]")
49
+ bank_routing_id2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.bank_routing_id[0]")
50
+ bank_routing_id2_swift: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.bank_routing_id[1]")
51
+ bank_routing_id2_swift_scheme_name: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.bank_routing_id[1].scheme_name")
52
+ account_id2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.account_id[0]")
53
+ additional_account_id2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.additional_account_id")
54
+ iban2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.iban")
55
+ account_country_code2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.country_code[0]")
56
+ account_currency_code2: Optional[str] = Field(default=None, alias="payment_instructions[1].direct_deposit_account.currency_code")
57
+
58
+ # Cost Assignment [0]
59
+ cost_valid_from: Optional[datetime.date] = Field(default=None, alias="cost_assignment[0].valid_from")
60
+ cost_center_code: Optional[str] = Field(default=None, alias="cost_assignment[0].cost_center_code")
61
+ cost_center_name: Optional[str] = Field(default=None, alias="cost_assignment[0].cost_center_name")
62
+ percentage: Optional[int] = Field(default=None, alias="cost_assignment[0].percentage")
63
+
64
+ # Pay Scales (single)
65
+ pay_scales_valid_from: Optional[datetime.date] = Field(default=None, alias="pay_scales.valid_from")
66
+ pay_scale_type: Optional[str] = Field(default=None, alias="pay_scales.pay_scale_type")
67
+ pay_scale_group: Optional[str] = Field(default=None, alias="pay_scales.pay_scale_group")
68
+ pay_scale_level: Optional[str] = Field(default=None, alias="pay_scales.pay_scale_level")
69
+
70
+ # Date Specifications (indices)
71
+ date_active_status_type: Optional[str] = Field(default=None, alias="date_specifications.date[0].date_type")
72
+ date_active_status: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[0].value")
73
+ date_hire_type: Optional[str] = Field(default=None, alias="date_specifications.date[1].date_type")
74
+ date_hire: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[1].value")
75
+ date_continuous_service_type: Optional[str] = Field(default=None, alias="date_specifications.date[2].date_type")
76
+ date_continuous_service: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[2].value")
77
+ date_seniority_type: Optional[str] = Field(default=None, alias="date_specifications.date[3].date_type")
78
+ date_seniority: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[3].value")
79
+ date_benefits_service_type: Optional[str] = Field(default=None, alias="date_specifications.date[4].date_type")
80
+ date_benefits_service: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[4].value")
81
+ date_company_service_type: Optional[str] = Field(default=None, alias="date_specifications.date[5].date_type")
82
+ date_company_service: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[5].value")
83
+ date_first_day_type: Optional[str] = Field(default=None, alias="date_specifications.date[6].date_type")
84
+ date_first_day_of_work: Optional[datetime.date] = Field(default=None, alias="date_specifications.date[6].value")
85
+
86
+ # Approvers [0]
87
+ approver_valid_from: Optional[datetime.date] = Field(default=None, alias="approvers.approver[0].valid_from")
88
+ approver_value: Optional[str] = Field(default=None, alias="approvers.approver[0].value")
89
+ approver_type: Optional[str] = Field(default=None, alias="approvers.approver[0].type_value")
90
+
91
+ # Contract Elements [0]
92
+ contract_valid_from: Optional[datetime.date] = Field(default=None, alias="contract_elements[0].valid_from")
93
+ contract_type: Optional[str] = Field(default=None, alias="contract_elements[0].contract_type")
94
+ contract_start_date: Optional[datetime.date] = Field(default=None, alias="contract_elements[0].contract_start_date")
95
+ contract_end_date: Optional[datetime.date] = Field(default=None, alias="contract_elements[0].contract_end_date")
96
+
97
+ # Payroll Specific Groupings (single object)
98
+ psg_valid_from: Optional[datetime.date] = Field(default=None, alias="payroll_specific_groupings.valid_from")
99
+ psg1: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping1")
100
+ psg2: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping2")
101
+ psg3: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping3")
102
+ psg4: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping4")
103
+ psg5: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping5")
104
+ psg6: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping6")
105
+ psg7: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping7")
106
+ psg8: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping8")
107
+ psg9: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping9")
108
+ psg10: Optional[str] = Field(default=None, alias="payroll_specific_groupings.payroll_specific_grouping10")
109
+
110
+ # Alternate Identifiers
111
+ payroll_exchange_id: Optional[str] = Field(default=None, alias="alternate_identifiers.payroll_exchange_id")
112
+ alternate_id: Optional[str] = Field(default=None, alias="alternate_identifiers.alternate_id[0]")
113
+ alternate_id_type: Optional[str] = Field(default=None, alias="alternate_identifiers.alternate_id[0].type_value")
114
+ pay_serv_id: Optional[str] = Field(default=None, alias="alternate_identifiers.pay_serv_id")
115
+ prior_incorrect_pay_serv_id: Optional[str] = Field(default=None, alias="alternate_identifiers.prior_incorrect_pay_serv_id")
116
+
117
+ # Alternate Descriptions
118
+ alt_desc_valid_from: Optional[datetime.date] = Field(default=None, alias="alternate_descriptions.valid_from")
119
+ alt_desc_1: Optional[str] = Field(default=None, alias="alternate_descriptions.description[0]")
120
+ alt_desc_1_type: Optional[str] = Field(default=None, alias="alternate_descriptions.description[0].type_value")
121
+ alt_desc_2: Optional[str] = Field(default=None, alias="alternate_descriptions.description[1]")
122
+ alt_desc_2_type: Optional[str] = Field(default=None, alias="alternate_descriptions.description[1].type_value")
123
+ alt_desc_3: Optional[str] = Field(default=None, alias="alternate_descriptions.description[2]")
124
+ alt_desc_3_type: Optional[str] = Field(default=None, alias="alternate_descriptions.description[2].type_value")
125
+
126
+ # Time Elements
127
+ time_elements: Optional[Absences] = None
128
+ time_quotas: Optional[TimeQuotas] = None
129
+
130
+ def to_nested_dict(self) -> Dict[str, Any]:
131
+ flat = self.model_dump(exclude_none=True, by_alias=True)
132
+ nested: Dict[str, Any] = {}
133
+ for k, v in flat.items():
134
+ add_to_nested_path(nested, k, v)
135
+ # Merge time elements
136
+ if self.time_elements is not None:
137
+ te = self.time_elements.to_nested_dict()
138
+ if te:
139
+ # Wrap into container
140
+ nested.setdefault("pay_serv_emp_time_elements", {})
141
+ nested["pay_serv_emp_time_elements"].update({"time_element": te.get("time_element", [])})
142
+ # Merge time quotas
143
+ if self.time_quotas is not None:
144
+ tq = self.time_quotas.to_nested_dict()
145
+ if tq:
146
+ nested.setdefault("pay_serv_emp_time_quotas", {})
147
+ nested["pay_serv_emp_time_quotas"].update({"time_quota": tq.get("time_quota", [])})
148
+ post_process_nested_data(nested, XsdPayServEmpExtension)
149
+ return nested
150
+
151
+ def to_model(self) -> XsdPayServEmpExtension:
152
+ nested = self.to_nested_dict()
153
+ try:
154
+ return construct_model(XsdPayServEmpExtension, nested)
155
+ except Exception:
156
+ return XsdPayServEmpExtension.model_validate(nested)
157
+
158
+
159
+ class PayElementCreate(BaseModel):
160
+ """
161
+ Alias-driven flat model for a single PayElement item.
162
+ """
163
+ model_config = {
164
+ "populate_by_name": True
165
+ }
166
+ valid_from: Optional[datetime.date] = Field(default=None, alias="valid_from")
167
+ id: Optional[str] = Field(default=None, alias="id[0].value")
168
+ pay_element_type: Optional[str] = Field(default=None, alias="pay_element_type.value")
169
+ amount: Optional[str] = Field(default=None, alias="amount.value")
170
+ currency_code: Optional[str] = Field(default=None, alias="currency_code.value")
171
+ rate: Optional[str] = Field(default=None, alias="rate.value")
172
+ units: Optional[str] = Field(default=None, alias="units.value")
173
+ unit_type: Optional[str] = Field(default=None, alias="unit_type.value")
174
+ reference_number: Optional[str] = Field(default=None, alias="reference_number.value")
175
+ cost_center_code: Optional[str] = Field(default=None, alias="cost_center_code.value")
176
+ end_date: Optional[datetime.date] = Field(default=None, alias="end_date.value")
177
+ premium_id: Optional[str] = Field(default=None, alias="premium_id.value")
178
+ different_valuation: Optional[str] = Field(default=None, alias="different_valuation.value")
179
+ off_cycle_indicator: Optional[bool] = Field(default=None, alias="off_cycle_indicator.value")
180
+
181
+ def to_nested_dict(self) -> Dict[str, Any]:
182
+ flat = self.model_dump(exclude_none=True, by_alias=True)
183
+ nested: Dict[str, Any] = {}
184
+ for k, v in flat.items():
185
+ add_to_nested_path(nested, k, v)
186
+ # Ensure required wrappers and types explicitly (schema nuances)
187
+ # id must be a list of wrapper dicts
188
+ if "id" in nested:
189
+ if isinstance(nested["id"], list):
190
+ nested["id"] = [item if isinstance(item, dict) and "value" in item else {"value": item} for item in nested["id"]]
191
+ else:
192
+ nested["id"] = [{"value": nested["id"]}]
193
+ # Wrap simple fields (only those not already aliased as .value)
194
+ for key in ("pay_element_type", "amount", "currency_code"):
195
+ if key in nested and not (isinstance(nested[key], dict) and "value" in nested[key]):
196
+ nested[key] = {"value": nested[key]}
197
+ # Convert dates
198
+ if "valid_from" in nested and isinstance(nested["valid_from"], (datetime.date, datetime.datetime)):
199
+ nested["valid_from"] = convert_datetime_to_xml(nested["valid_from"], None)
200
+ if "end_date" in nested and isinstance(nested["end_date"], dict) and "value" in nested["end_date"]:
201
+ inner = nested["end_date"]["value"]
202
+ if isinstance(inner, (datetime.date, datetime.datetime)):
203
+ nested["end_date"]["value"] = convert_datetime_to_xml(inner, None)
204
+ # Final pass through schema container to catch any remaining wrappers
205
+ container = {"pay_element": [nested]}
206
+ post_process_nested_data(container, XsdPayServEmpPayElements)
207
+ return container["pay_element"][0]
@@ -0,0 +1,67 @@
1
+ """
2
+ Flat, user-friendly salary model for Alight SDK.
3
+ """
4
+
5
+ import datetime
6
+ from typing import Optional, Dict, Any
7
+ from pydantic import Field, BaseModel
8
+
9
+ from .utils import add_to_nested_path # not used directly; kept for parity if needed
10
+
11
+
12
+ class Salary(BaseModel):
13
+ """
14
+ Simplified salary/compensation model.
15
+ Uses PURE schema-driven conversion - NO hardcoded structure mappings.
16
+ Uses aliases to match expected field names in Employee model.
17
+ """
18
+ model_config = {
19
+ "populate_by_name": True # Allow populating by field name (base_salary) in addition to alias (pay_amount)
20
+ }
21
+
22
+ base_salary: float = Field(
23
+ description="Base salary amount",
24
+ alias="indicative_person_dossier.pay_cycle_remuneration.remuneration.amount.value"
25
+ )
26
+ currency_code: str = Field(
27
+ default="USD",
28
+ description="Currency code (USD/GBP/EUR)",
29
+ alias="indicative_person_dossier.pay_cycle_remuneration.remuneration.amount.currency_code"
30
+ )
31
+ pay_frequency: Optional[str] = Field(
32
+ default="Monthly",
33
+ description="Monthly/Weekly/Hourly",
34
+ alias="indicative_person_dossier.pay_cycle_remuneration.pay_cycle_interval_code"
35
+ )
36
+
37
+ # Pay Elements
38
+ element_code: Optional[str] = Field(
39
+ default="0010",
40
+ description="Pay element code",
41
+ alias="indicative_person_dossier.pay_cycle_remuneration.remuneration.pay_element.id"
42
+ )
43
+ element_type: Optional[str] = Field(
44
+ default="RECURRING",
45
+ description="RECURRING/BONUS/etc.",
46
+ alias="indicative_person_dossier.pay_cycle_remuneration.remuneration.pay_element.type_code"
47
+ )
48
+
49
+ # Dates
50
+ valid_from: Optional[datetime.date] = Field(
51
+ default=None,
52
+ description="Valid from date",
53
+ alias="indicative_person_dossier.pay_cycle_remuneration.valid_from"
54
+ )
55
+
56
+ def to_nested_dict(self) -> Dict[str, Any]:
57
+ """
58
+ Convert salary data to format suitable for pay elements in the HR-XML.
59
+ Using aliases for direct mapping to employee fields.
60
+ """
61
+ data = self.model_dump(exclude_none=True, by_alias=True)
62
+
63
+ # Ensure amount is a string for XML compatibility
64
+ if 'pay_amount' in data and isinstance(data['pay_amount'], (int, float)):
65
+ data['pay_amount'] = str(data['pay_amount'])
66
+
67
+ return data
@@ -0,0 +1,48 @@
1
+ """
2
+ Flat, user-friendly termination model for Alight SDK.
3
+ """
4
+
5
+ import datetime
6
+ from typing import Optional, Dict, Any
7
+ from pydantic import Field, BaseModel
8
+
9
+ from .utils import add_to_nested_path # not used here
10
+
11
+
12
+ class Termination(BaseModel):
13
+ """
14
+ Simplified termination model.
15
+ Maps to IndicativeEmployment.employment_lifecycle.termination.
16
+ """
17
+ model_config = {
18
+ "populate_by_name": True
19
+ }
20
+
21
+ voluntary_termination_indicator: Optional[bool] = Field(
22
+ default=None,
23
+ description="Voluntary termination indicator",
24
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.termination.voluntary_termination_indicator"
25
+ )
26
+ termination_reason_code: Optional[str] = Field(
27
+ default=None,
28
+ description="Reason for termination",
29
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.termination.termination_reason_code"
30
+ )
31
+ termination_date: Optional[datetime.date] = Field(
32
+ default=None,
33
+ description="Termination date",
34
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.termination.termination_date"
35
+ )
36
+ last_worked_date: Optional[datetime.date] = Field(
37
+ default=None,
38
+ description="Last worked date",
39
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.termination.last_worked_date"
40
+ )
41
+ last_paid_date: Optional[datetime.date] = Field(
42
+ default=None,
43
+ description="Last paid date",
44
+ alias="indicative_person_dossier.indicative_employment.employment_lifecycle.termination.last_paid_date"
45
+ )
46
+
47
+ def to_nested_dict(self) -> Dict[str, Any]:
48
+ return self.model_dump(exclude_none=True, by_alias=True)
@@ -0,0 +1,66 @@
1
+ """
2
+ Flat, user-friendly time quota model for Alight SDK.
3
+ """
4
+
5
+ import datetime
6
+ from typing import Optional, Dict, Any, List
7
+ from pydantic import Field, BaseModel
8
+
9
+ from .utils import add_to_nested_path, convert_datetime_to_xml
10
+
11
+
12
+ class TimeQuota(BaseModel):
13
+ """
14
+ Maps to PayServEmpTimeQuotas.time_quota[0].
15
+ """
16
+ model_config = {"populate_by_name": True}
17
+
18
+ id: Optional[str] = Field(default=None, alias="time_quota[0].id[0].value")
19
+ units: Optional[str] = Field(default=None, alias="time_quota[0].units")
20
+ units_taken: Optional[str] = Field(default=None, alias="time_quota[0].units_taken")
21
+ units_remaining: Optional[str] = Field(default=None, alias="time_quota[0].units_remaining")
22
+ unit_type: Optional[str] = Field(default=None, alias="time_quota[0].unit_type")
23
+ accrued_to_date: Optional[str] = Field(default=None, alias="time_quota[0].accrued_to_date")
24
+ deduction_start: Optional[datetime.date] = Field(default=None, alias="time_quota[0].deduction_start")
25
+ deduction_end: Optional[datetime.date] = Field(default=None, alias="time_quota[0].deduction_end")
26
+ valid_from: Optional[datetime.date] = Field(default=None, alias="time_quota[0].valid_from")
27
+ valid_to: Optional[datetime.date] = Field(default=None, alias="time_quota[0].valid_to")
28
+
29
+ def to_nested_dict(self) -> Dict[str, Any]:
30
+ flat = self.model_dump(exclude_none=True, by_alias=True)
31
+ nested: Dict[str, Any] = {}
32
+ for k, v in flat.items():
33
+ add_to_nested_path(nested, k, v)
34
+ # Convert date/period values to XML-compatible
35
+ for key in ("valid_from", "valid_to", "deduction_start", "deduction_end"):
36
+ node = nested
37
+ parts = key.split(".")
38
+ if key in nested and isinstance(nested[key], (datetime.date, datetime.datetime)):
39
+ nested[key] = convert_datetime_to_xml(nested[key], None)
40
+ return nested
41
+
42
+
43
+ class TimeQuotas(BaseModel):
44
+ """
45
+ Wrapper for multiple TimeQuota items mapping to time_quota[*].
46
+ """
47
+ time_quotas: Optional[List[TimeQuota]] = Field(default=None, description="List of time quota items")
48
+
49
+ def to_nested_dict(self) -> Dict[str, Any]:
50
+ nested: Dict[str, Any] = {}
51
+ if not self.time_quotas:
52
+ return nested
53
+ items: List[Dict[str, Any]] = []
54
+ for idx, item in enumerate(self.time_quotas):
55
+ data = item.to_nested_dict()
56
+ remapped: Dict[str, Any] = {}
57
+ for k, v in data.items():
58
+ remapped[k.replace("time_quota[0]", f"time_quota[{idx}]")] = v
59
+ item_nested: Dict[str, Any] = {}
60
+ for k, v in remapped.items():
61
+ add_to_nested_path(item_nested, k, v)
62
+ if "time_quota" in item_nested and isinstance(item_nested["time_quota"], list):
63
+ items.extend(item_nested["time_quota"])
64
+ if items:
65
+ nested["time_quota"] = items
66
+ return nested