brynq-sdk-sage-germany 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.
- brynq_sdk_sage_germany/__init__.py +278 -0
- brynq_sdk_sage_germany/absences.py +175 -0
- brynq_sdk_sage_germany/allowances.py +100 -0
- brynq_sdk_sage_germany/contracts.py +145 -0
- brynq_sdk_sage_germany/cost_centers.py +89 -0
- brynq_sdk_sage_germany/employees.py +140 -0
- brynq_sdk_sage_germany/helpers.py +391 -0
- brynq_sdk_sage_germany/organization.py +90 -0
- brynq_sdk_sage_germany/payroll.py +167 -0
- brynq_sdk_sage_germany/payslips.py +106 -0
- brynq_sdk_sage_germany/salaries.py +95 -0
- brynq_sdk_sage_germany/schemas/__init__.py +44 -0
- brynq_sdk_sage_germany/schemas/absences.py +311 -0
- brynq_sdk_sage_germany/schemas/allowances.py +147 -0
- brynq_sdk_sage_germany/schemas/cost_centers.py +46 -0
- brynq_sdk_sage_germany/schemas/employees.py +487 -0
- brynq_sdk_sage_germany/schemas/organization.py +172 -0
- brynq_sdk_sage_germany/schemas/organization_assignment.py +61 -0
- brynq_sdk_sage_germany/schemas/payroll.py +287 -0
- brynq_sdk_sage_germany/schemas/payslips.py +34 -0
- brynq_sdk_sage_germany/schemas/salaries.py +101 -0
- brynq_sdk_sage_germany/schemas/start_end_dates.py +194 -0
- brynq_sdk_sage_germany/schemas/vacation_account.py +117 -0
- brynq_sdk_sage_germany/schemas/work_hours.py +94 -0
- brynq_sdk_sage_germany/start_end_dates.py +123 -0
- brynq_sdk_sage_germany/vacation_account.py +70 -0
- brynq_sdk_sage_germany/work_hours.py +97 -0
- brynq_sdk_sage_germany-1.0.0.dist-info/METADATA +21 -0
- brynq_sdk_sage_germany-1.0.0.dist-info/RECORD +31 -0
- brynq_sdk_sage_germany-1.0.0.dist-info/WHEEL +5 -0
- brynq_sdk_sage_germany-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schemas for Sage Germany salary payloads.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import pandera as pa
|
|
9
|
+
from pandera.typing import Series
|
|
10
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
11
|
+
|
|
12
|
+
from brynq_sdk_functions import BrynQPanderaDataFrameModel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SalaryGet(BrynQPanderaDataFrameModel):
|
|
16
|
+
"""
|
|
17
|
+
Pandera schema for employee salary records.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Base identifiers
|
|
21
|
+
key_date: Series[pd.StringDtype] = pa.Field(alias="Key__Date", coerce=True, nullable=False, description="Snapshot timestamp.")
|
|
22
|
+
company_id: Series[pd.Int64Dtype] = pa.Field(alias="Key__MdNr", coerce=True, nullable=False, description="Company id.")
|
|
23
|
+
employee_number: Series[pd.Int64Dtype] = pa.Field(alias="Key__AnNr", coerce=True, nullable=False, description="Employee number.")
|
|
24
|
+
combined_key: Series[pd.StringDtype] = pa.Field(alias="Key__CombinedKey", coerce=True, nullable=False, description="Combined tenant/employee key.")
|
|
25
|
+
key_is_empty: Optional[Series[bool]] = pa.Field(alias="Key__IsEmpty", coerce=True, nullable=True, description="Indicates empty key payload.")
|
|
26
|
+
|
|
27
|
+
# Salary configuration flags
|
|
28
|
+
transfer_fixed_allowances: Optional[Series[bool]] = pa.Field(alias="FesteBezuegeUebernehmen", coerce=True, nullable=True, description="Transfer fixed allowances flag.")
|
|
29
|
+
collective_bargain_table: Optional[Series[bool]] = pa.Field(alias="Tariftabelle", coerce=True, nullable=True, description="Collective bargain table flag.")
|
|
30
|
+
|
|
31
|
+
# Agreed salary
|
|
32
|
+
agreed_salary_amount: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Gehalt", coerce=True, nullable=True, description="Agreed salary amount.")
|
|
33
|
+
allowance_one_is_percentage: Optional[Series[bool]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage1Prozentual", coerce=True, nullable=True, description="Allowance 1 percentage flag.")
|
|
34
|
+
allowance_two_is_percentage: Optional[Series[bool]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage2Prozentual", coerce=True, nullable=True, description="Allowance 2 percentage flag.")
|
|
35
|
+
allowance_three_is_percentage: Optional[Series[bool]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage3Prozentual", coerce=True, nullable=True, description="Allowance 3 percentage flag.")
|
|
36
|
+
allowance_one_amount: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage1Betrag", coerce=True, nullable=True, description="Allowance 1 amount.")
|
|
37
|
+
allowance_two_amount: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage2Betrag", coerce=True, nullable=True, description="Allowance 2 amount.")
|
|
38
|
+
allowance_three_amount: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage3Betrag", coerce=True, nullable=True, description="Allowance 3 amount.")
|
|
39
|
+
allowance_one_percentage: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage1Prozent", coerce=True, nullable=True, description="Allowance 1 percentage.")
|
|
40
|
+
allowance_two_percentage: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage2Prozent", coerce=True, nullable=True, description="Allowance 2 percentage.")
|
|
41
|
+
allowance_three_percentage: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbartesEntgelt__Zulagen__Zulage3Prozent", coerce=True, nullable=True, description="Allowance 3 percentage.")
|
|
42
|
+
|
|
43
|
+
# Hourly rates
|
|
44
|
+
hourly_rate_1: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz1", coerce=True, nullable=True, description="Hourly rate 1.")
|
|
45
|
+
hourly_rate_2: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz2", coerce=True, nullable=True, description="Hourly rate 2.")
|
|
46
|
+
hourly_rate_3: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz3", coerce=True, nullable=True, description="Hourly rate 3.")
|
|
47
|
+
hourly_rate_4: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz4", coerce=True, nullable=True, description="Hourly rate 4.")
|
|
48
|
+
hourly_rate_5: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz5", coerce=True, nullable=True, description="Hourly rate 5.")
|
|
49
|
+
hourly_rate_6: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz6", coerce=True, nullable=True, description="Hourly rate 6.")
|
|
50
|
+
hourly_rate_7: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz7", coerce=True, nullable=True, description="Hourly rate 7.")
|
|
51
|
+
hourly_rate_8: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz8", coerce=True, nullable=True, description="Hourly rate 8.")
|
|
52
|
+
hourly_rate_9: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz9", coerce=True, nullable=True, description="Hourly rate 9.")
|
|
53
|
+
hourly_rate_10: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="VereinbarteStundenloehne__Stundensatz10", coerce=True, nullable=True, description="Hourly rate 10.")
|
|
54
|
+
|
|
55
|
+
# Night/Sunday/Holiday allowances
|
|
56
|
+
automatic_base_wage_calculation: Optional[Series[bool]] = pa.Field(alias="SonnFeiertagNachtZuschlaege__AutomatischeGrundlohnermittlung", coerce=True, nullable=True, description="Automatic base wage calculation flag.")
|
|
57
|
+
base_hourly_wage: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="SonnFeiertagNachtZuschlaege__Grundstundenlohn", coerce=True, nullable=True, description="Base hourly wage.")
|
|
58
|
+
|
|
59
|
+
# Advance payments
|
|
60
|
+
automatic_advance_payment: Optional[Series[bool]] = pa.Field(alias="Abschlagszahlung__AutomatischerAbschlag", coerce=True, nullable=True, description="Automatic advance flag.")
|
|
61
|
+
advance_payment_amount: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Abschlagszahlung__Abschlag", coerce=True, nullable=True, description="Advance payment amount.")
|
|
62
|
+
|
|
63
|
+
# Overtime / absence config
|
|
64
|
+
overtime_calculation_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="UeberstundenFehlstunden__Berechnungsart__Id", coerce=True, nullable=True, description="Overtime calculation identifier.")
|
|
65
|
+
overtime_calculation_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__Berechnungsart__Text", coerce=True, nullable=True, description="Overtime calculation text.")
|
|
66
|
+
overtime_calculation_id_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__Berechnungsart__IdMitText", coerce=True, nullable=True, description="Overtime calculation id-text.")
|
|
67
|
+
overtime_payroll_code: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__LohnartUeberstunden", coerce=True, nullable=True, description="Overtime payroll code.")
|
|
68
|
+
absence_payroll_code: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__LohnartFehlstunden", coerce=True, nullable=True, description="Absence payroll code.")
|
|
69
|
+
employer_prepayment_payroll_code: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__LohnartFehlstundenArbeitgeberVorausleistung", coerce=True, nullable=True, description="Employer prepayment payroll code.")
|
|
70
|
+
additional_overtime_threshold: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="UeberstundenFehlstunden__ZusaetzlicheUeberstundenAb", coerce=True, nullable=True, description="Additional overtime threshold.")
|
|
71
|
+
additional_overtime_payroll_code: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__LohnartZusaetzlicheUeberstunden", coerce=True, nullable=True, description="Additional overtime payroll code.")
|
|
72
|
+
unpaid_absence_threshold: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="UeberstundenFehlstunden__UnbezahlteFehlstundenAb", coerce=True, nullable=True, description="Unpaid absence threshold.")
|
|
73
|
+
unpaid_absence_payroll_code: Optional[Series[pd.StringDtype]] = pa.Field(alias="UeberstundenFehlstunden__LohnartUnbezahlteFehlstunden", coerce=True, nullable=True, description="Unpaid absence payroll code.")
|
|
74
|
+
|
|
75
|
+
class _Annotation:
|
|
76
|
+
primary_key = "combined_key"
|
|
77
|
+
|
|
78
|
+
class Config:
|
|
79
|
+
coerce = True
|
|
80
|
+
strict = "filter"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SalaryImport(BaseModel):
|
|
84
|
+
"""
|
|
85
|
+
Pydantic schema for gross salary (Bruttolohn) import payloads.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
company_id: Union[int, str] = Field(alias="Mdnr", description="Company id (Mandant).", example=1)
|
|
89
|
+
employee_number: Union[int, str] = Field(alias="Annr", description="Employee number.", example=100)
|
|
90
|
+
year: Union[int, str] = Field(alias="AbrJahr", description="Payroll year.", example=2024)
|
|
91
|
+
month: Union[int, str] = Field(alias="AbrMon", description="Payroll month.", example=1)
|
|
92
|
+
day: Union[int, str] = Field(alias="Tag", description="Day of month.", example=15)
|
|
93
|
+
wage_type: Union[int, str] = Field(alias="Lanr", description="Wage type number.", example=1000)
|
|
94
|
+
quantity: Optional[Union[int, float, str]] = Field(default=None, alias="Anzahl", description="Quantity value.", example=8.0)
|
|
95
|
+
amount: Optional[Union[int, float, str]] = Field(default=None, alias="Betrag", description="Amount value.", example=150.00)
|
|
96
|
+
cost_center: Optional[Union[int, str]] = Field(default=None, alias="KoSt1", description="Cost center.", example="CC001")
|
|
97
|
+
cost_unit: Optional[Union[int, str]] = Field(default=None, alias="KoTr1", description="Cost unit.", example="CU001")
|
|
98
|
+
aa1: Optional[Union[int, str]] = Field(default=None, alias="AA1", description="Additional classification field.", example="A1")
|
|
99
|
+
surcharge: Optional[Union[int, float, str]] = Field(default=None, alias="Zuschlag", description="Surcharge value.", example=25.00)
|
|
100
|
+
|
|
101
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schemas for Sage Germany employment period payloads.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import pandera as pa
|
|
10
|
+
from pandera.typing import Series
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
from brynq_sdk_functions import BrynQPanderaDataFrameModel
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"StartEndDatesGet",
|
|
17
|
+
"StartEndDatesKey",
|
|
18
|
+
"StartEndDatesInterval",
|
|
19
|
+
"StartEndDatesEntryExit",
|
|
20
|
+
"StartEndDatesCreate",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class StartEndDatesKey(BaseModel):
|
|
25
|
+
"""
|
|
26
|
+
Employee key used by employment period endpoints.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
company_id: int = Field(alias="MdNr", description="Company id (Mandant).", example=1)
|
|
30
|
+
employee_number: int = Field(alias="AnNr", description="Employee number.", example=210)
|
|
31
|
+
combined_key: Optional[str] = Field(
|
|
32
|
+
default=None,
|
|
33
|
+
alias="CombinedKey",
|
|
34
|
+
description="Optional combined key value.",
|
|
35
|
+
example="1_210",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class StartEndDatesInterval(BaseModel):
|
|
42
|
+
"""
|
|
43
|
+
Generic Id/Text object used for probation interval, contract limits, and reasons.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
id: Optional[int] = Field(default=None, alias="Id", description="Identifier value.", example=1)
|
|
47
|
+
text: Optional[str] = Field(default=None, alias="Text", description="Display text value.", example="Monthly")
|
|
48
|
+
|
|
49
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class StartEndDatesEntryExit(BaseModel):
|
|
53
|
+
"""
|
|
54
|
+
Single entry/exit record within the employment period payload.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
key: StartEndDatesKey = Field(
|
|
58
|
+
alias="Key",
|
|
59
|
+
description="Employee key for the entry/exit item.",
|
|
60
|
+
)
|
|
61
|
+
id: Optional[int] = Field(default=None, alias="Id", description="Entry/exit identifier.", example=1)
|
|
62
|
+
entry: datetime = Field(alias="Eintritt", description="Employment start date.", example="2024-01-01T00:00:00")
|
|
63
|
+
exit: Optional[datetime] = Field(default=None, alias="Austritt", description="Employment end date.", example="2024-12-31T00:00:00")
|
|
64
|
+
limited: Optional[StartEndDatesInterval] = Field(
|
|
65
|
+
default=None,
|
|
66
|
+
alias="Befristet",
|
|
67
|
+
description="Limited contract metadata.",
|
|
68
|
+
json_schema_extra={"prefix": "entry_exit_limited_"},
|
|
69
|
+
)
|
|
70
|
+
exit_reason: Optional[StartEndDatesInterval] = Field(
|
|
71
|
+
default=None,
|
|
72
|
+
alias="AustrittGrund",
|
|
73
|
+
description="Exit reason metadata.",
|
|
74
|
+
json_schema_extra={"prefix": "entry_exit_exit_reason_"},
|
|
75
|
+
)
|
|
76
|
+
exit_custom_reason: Optional[StartEndDatesInterval] = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
alias="AustrittBenutzerdefinierterGrund",
|
|
79
|
+
description="Custom exit reason metadata.",
|
|
80
|
+
json_schema_extra={"prefix": "entry_exit_exit_custom_reason_"},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class StartEndDatesCreate(BaseModel):
|
|
87
|
+
"""
|
|
88
|
+
Pydantic schema for POST /employeenew/person/beschaeftigungszeiten.
|
|
89
|
+
Accepts nested structure; helper converts flat prefixed keys to this shape.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
key: StartEndDatesKey = Field(
|
|
93
|
+
alias="Key",
|
|
94
|
+
description="Employee key for the employment period record.",
|
|
95
|
+
json_schema_extra={"prefix": "key_"},
|
|
96
|
+
)
|
|
97
|
+
corporate_entry: Optional[datetime] = Field(
|
|
98
|
+
default=None,
|
|
99
|
+
alias="KonzernEintritt",
|
|
100
|
+
description="Corporate entry date.",
|
|
101
|
+
example="2024-01-01T00:00:00",
|
|
102
|
+
)
|
|
103
|
+
probation_number: Optional[int] = Field(
|
|
104
|
+
default=None,
|
|
105
|
+
alias="ProbezeitZahl",
|
|
106
|
+
description="Probation duration number.",
|
|
107
|
+
example=6,
|
|
108
|
+
)
|
|
109
|
+
probation_interval: Optional[StartEndDatesInterval] = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
alias="ProbezeitIntervall",
|
|
112
|
+
description="Probation interval metadata.",
|
|
113
|
+
json_schema_extra={"prefix": "probation_interval_"},
|
|
114
|
+
)
|
|
115
|
+
probation_end: Optional[datetime] = Field(
|
|
116
|
+
default=None,
|
|
117
|
+
alias="Probezeit",
|
|
118
|
+
description="Probation end date.",
|
|
119
|
+
example="2024-07-01T00:00:00",
|
|
120
|
+
)
|
|
121
|
+
entry_exit_periods: Optional[List[StartEndDatesEntryExit]] = Field(
|
|
122
|
+
default=None,
|
|
123
|
+
alias="EinAustritte",
|
|
124
|
+
description="List of entry/exit records.",
|
|
125
|
+
example=[],
|
|
126
|
+
json_schema_extra={"prefix": "entry_exit_"},
|
|
127
|
+
)
|
|
128
|
+
service_time: Optional[datetime] = Field(
|
|
129
|
+
default=None,
|
|
130
|
+
alias="Dienstzeit",
|
|
131
|
+
description="Service time date.",
|
|
132
|
+
example="2024-01-01T00:00:00",
|
|
133
|
+
)
|
|
134
|
+
service_time_duration: Optional[str] = Field(
|
|
135
|
+
default=None,
|
|
136
|
+
alias="DienstzeitDauer",
|
|
137
|
+
description="Service time duration string.",
|
|
138
|
+
example="1 year",
|
|
139
|
+
)
|
|
140
|
+
employment_time: Optional[datetime] = Field(
|
|
141
|
+
default=None,
|
|
142
|
+
alias="Beschaeftigungszeit",
|
|
143
|
+
description="Employment time date.",
|
|
144
|
+
example="2024-01-01T00:00:00",
|
|
145
|
+
)
|
|
146
|
+
employment_time_duration: Optional[str] = Field(
|
|
147
|
+
default=None,
|
|
148
|
+
alias="BeschaeftigungszeitDauer",
|
|
149
|
+
description="Employment time duration string.",
|
|
150
|
+
example="1 year",
|
|
151
|
+
)
|
|
152
|
+
allowance_claim: Optional[StartEndDatesInterval] = Field(
|
|
153
|
+
default=None,
|
|
154
|
+
alias="AnspruchZuwendung",
|
|
155
|
+
description="Allowance claim metadata.",
|
|
156
|
+
json_schema_extra={"prefix": "allowance_claim_"},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class StartEndDatesGet(BrynQPanderaDataFrameModel):
|
|
163
|
+
"""
|
|
164
|
+
Pandera schema for employee start/end date records.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
# Base identifiers
|
|
168
|
+
company_id: Series[pd.Int64Dtype] = pa.Field(alias="Key__MdNr", coerce=True, nullable=False, description="Company id.")
|
|
169
|
+
employee_number: Series[pd.Int64Dtype] = pa.Field(alias="Key__AnNr", coerce=True, nullable=False, description="Employee number.")
|
|
170
|
+
combined_key: Series[pd.StringDtype] = pa.Field(alias="Key__CombinedKey", coerce=True, nullable=False, description="Combined key.")
|
|
171
|
+
key_is_empty: Optional[Series[bool]] = pa.Field(alias="Key__IsEmpty", coerce=True, nullable=True, description="Key empty flag.")
|
|
172
|
+
|
|
173
|
+
# Employment metadata
|
|
174
|
+
corporate_entry: Optional[Series[pd.StringDtype]] = pa.Field(alias="KonzernEintritt", coerce=True, nullable=True, description="Corporate entry date.")
|
|
175
|
+
probation_number: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="ProbezeitZahl", coerce=True, nullable=True, description="Probation duration number.")
|
|
176
|
+
probation_interval: Optional[Series[pd.StringDtype]] = pa.Field(alias="ProbezeitIntervall", coerce=True, nullable=True, description="Probation interval.")
|
|
177
|
+
probation_end: Optional[Series[pd.StringDtype]] = pa.Field(alias="Probezeit", coerce=True, nullable=True, description="Probation end date.")
|
|
178
|
+
|
|
179
|
+
# Aggregated durations
|
|
180
|
+
service_time: Optional[Series[pd.StringDtype]] = pa.Field(alias="Dienstzeit", coerce=True, nullable=True, description="Service time string.")
|
|
181
|
+
service_time_duration: Optional[Series[pd.StringDtype]] = pa.Field(alias="DienstzeitDauer", coerce=True, nullable=True, description="Service time duration.")
|
|
182
|
+
employment_time: Optional[Series[pd.StringDtype]] = pa.Field(alias="Beschaeftigungszeit", coerce=True, nullable=True, description="Employment time string.")
|
|
183
|
+
employment_time_duration: Optional[Series[pd.StringDtype]] = pa.Field(alias="BeschaeftigungszeitDauer", coerce=True, nullable=True, description="Employment time duration.")
|
|
184
|
+
allowance_claim: Optional[Series[pd.StringDtype]] = pa.Field(alias="AnspruchZuwendung", coerce=True, nullable=True, description="Allowance claim info.")
|
|
185
|
+
|
|
186
|
+
# Entry/exit payload
|
|
187
|
+
entry_exit_payload: Optional[Series[pd.StringDtype]] = pa.Field(alias="EinAustritte", coerce=True, nullable=True, description="Entry/exit history payload.")
|
|
188
|
+
|
|
189
|
+
class _Annotation:
|
|
190
|
+
primary_key = "combined_key"
|
|
191
|
+
|
|
192
|
+
class Config:
|
|
193
|
+
coerce = True
|
|
194
|
+
strict = "filter"
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schemas for Sage Germany vacation account (Urlaubskonto) payloads.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import pandera as pa
|
|
9
|
+
from pandera.typing import Series
|
|
10
|
+
|
|
11
|
+
from brynq_sdk_functions import BrynQPanderaDataFrameModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class VacationAccountGet(BrynQPanderaDataFrameModel):
|
|
15
|
+
"""
|
|
16
|
+
Pandera schema for employee vacation account balances.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# Base identifiers
|
|
20
|
+
key_date: Series[pd.StringDtype] = pa.Field(alias="Key__Date", coerce=True, nullable=False, description="Snapshot date.")
|
|
21
|
+
company_id: Series[pd.Int64Dtype] = pa.Field(alias="Key__MdNr", coerce=True, nullable=False, description="Company id.")
|
|
22
|
+
employee_number: Series[pd.Int64Dtype] = pa.Field(alias="Key__AnNr", coerce=True, nullable=False, description="Employee number.")
|
|
23
|
+
combined_key: Series[pd.StringDtype] = pa.Field(alias="Key__CombinedKey", coerce=True, nullable=False, description="Combined key.")
|
|
24
|
+
key_is_empty: Optional[Series[bool]] = pa.Field(alias="Key__IsEmpty", coerce=True, nullable=True, description="Key empty flag.")
|
|
25
|
+
|
|
26
|
+
# Vacation table metadata
|
|
27
|
+
vacation_table_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Urlaubstabelle__Id", coerce=True, nullable=True, description="Vacation table id.")
|
|
28
|
+
vacation_table_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="Urlaubstabelle__Text", coerce=True, nullable=True, description="Vacation table text.")
|
|
29
|
+
vacation_table_label: Optional[Series[pd.StringDtype]] = pa.Field(alias="Urlaubstabelle__IdMitText", coerce=True, nullable=True, description="Vacation table label.")
|
|
30
|
+
is_previous_calendar_year: Optional[Series[bool]] = pa.Field(alias="VergangenesKalenderjahr", coerce=True, nullable=True, description="Indicates previous calendar year view.")
|
|
31
|
+
|
|
32
|
+
# Employee info
|
|
33
|
+
data_company_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Daten__EmployeeKey__MdNr", coerce=True, nullable=True, description="Data company id.")
|
|
34
|
+
data_employee_number: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Daten__EmployeeKey__AnNr", coerce=True, nullable=True, description="Data employee number.")
|
|
35
|
+
data_combined_key: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__EmployeeKey__CombinedKey", coerce=True, nullable=True, description="Data combined key.")
|
|
36
|
+
employee_first_name: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__EmployeeName__Firstname", coerce=True, nullable=True, description="Employee first name.")
|
|
37
|
+
employee_last_name: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__EmployeeName__Lastname", coerce=True, nullable=True, description="Employee last name.")
|
|
38
|
+
employee_full_name: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__EmployeeName__Fullname", coerce=True, nullable=True, description="Employee full name.")
|
|
39
|
+
unit: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__Unit", coerce=True, nullable=True, description="Unit label.")
|
|
40
|
+
|
|
41
|
+
# Annual entitlements and usage
|
|
42
|
+
entitlement_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchKJ", coerce=True, nullable=True, description="Current year entitlement.")
|
|
43
|
+
entitlement_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchVJ", coerce=True, nullable=True, description="Previous year entitlement.")
|
|
44
|
+
entitlement_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchGesamt", coerce=True, nullable=True, description="Total entitlement.")
|
|
45
|
+
taken_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenKJ", coerce=True, nullable=True, description="Taken days current year.")
|
|
46
|
+
taken_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenVJ", coerce=True, nullable=True, description="Taken days previous year.")
|
|
47
|
+
taken_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenGesamt", coerce=True, nullable=True, description="Taken days total.")
|
|
48
|
+
rest_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestKJ", coerce=True, nullable=True, description="Remaining current year.")
|
|
49
|
+
rest_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestVJ", coerce=True, nullable=True, description="Remaining previous year.")
|
|
50
|
+
rest_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestGesamt", coerce=True, nullable=True, description="Remaining total.")
|
|
51
|
+
rest_total_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestGesamtVJ", coerce=True, nullable=True, description="Remaining total previous year.")
|
|
52
|
+
rest_total_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestGesamtKJ", coerce=True, nullable=True, description="Remaining total current year.")
|
|
53
|
+
|
|
54
|
+
entitlement_base_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchGrundKJ", coerce=True, nullable=True, description="Base entitlement current year.")
|
|
55
|
+
entitlement_carried_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchVortragVJ", coerce=True, nullable=True, description="Carried over previous year.")
|
|
56
|
+
entitlement_carried_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchVortragKJ", coerce=True, nullable=True, description="Carried over current year.")
|
|
57
|
+
|
|
58
|
+
# Additional/special leave
|
|
59
|
+
entitlement_additional_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchZusatzKJ", coerce=True, nullable=True, description="Additional leave current year.")
|
|
60
|
+
entitlement_additional_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchZusatzVJ", coerce=True, nullable=True, description="Additional leave previous year.")
|
|
61
|
+
entitlement_additional_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchZusatzGesamt", coerce=True, nullable=True, description="Additional leave total.")
|
|
62
|
+
entitlement_additional_raw: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchZusatzKJohneKorrektur", coerce=True, nullable=True, description="Additional leave raw current year.")
|
|
63
|
+
entitlement_special_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AnspruchSonderUrlTgKJ", coerce=True, nullable=True, description="Special leave entitlement current year.")
|
|
64
|
+
|
|
65
|
+
taken_additional_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenZusatzKJ", coerce=True, nullable=True, description="Additional leave taken current year.")
|
|
66
|
+
taken_additional_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenZusatzVJ", coerce=True, nullable=True, description="Additional leave taken previous year.")
|
|
67
|
+
taken_additional_total_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenZusatzGesamtVJ", coerce=True, nullable=True, description="Additional leave taken total previous year.")
|
|
68
|
+
taken_additional_total_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenZusatzGesamtKJ", coerce=True, nullable=True, description="Additional leave taken total current year.")
|
|
69
|
+
taken_without_entitlement: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__GenommenOhneAnspruch", coerce=True, nullable=True, description="Leave taken without entitlement.")
|
|
70
|
+
|
|
71
|
+
rest_additional_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestZusatzKJ", coerce=True, nullable=True, description="Additional leave remaining current year.")
|
|
72
|
+
rest_additional_total_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestZusatzGesamtVJ", coerce=True, nullable=True, description="Additional leave remaining total previous year.")
|
|
73
|
+
rest_special_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestSonderurlaubKJ", coerce=True, nullable=True, description="Special leave remaining current year.")
|
|
74
|
+
|
|
75
|
+
# Expiration / compensation
|
|
76
|
+
expiration_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__VerfallKJ", coerce=True, nullable=True, description="Expiration current year.")
|
|
77
|
+
expiration_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__VerfallVJ", coerce=True, nullable=True, description="Expiration previous year.")
|
|
78
|
+
expiration_additional_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__VerfallZusatzKJ", coerce=True, nullable=True, description="Additional expiration current year.")
|
|
79
|
+
expiration_additional_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__VerfallZusatzVJ", coerce=True, nullable=True, description="Additional expiration previous year.")
|
|
80
|
+
expiration_disability_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__VerfallSchwerbehinderungKJ", coerce=True, nullable=True, description="Disability expiration current year.")
|
|
81
|
+
compensation_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AbgeltKJ", coerce=True, nullable=True, description="Compensation current year.")
|
|
82
|
+
compensation_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AbgeltVJ", coerce=True, nullable=True, description="Compensation previous year.")
|
|
83
|
+
compensation_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AbgeltGesamt", coerce=True, nullable=True, description="Compensation total.")
|
|
84
|
+
expiration_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__VerfallGesamt", coerce=True, nullable=True, description="Expiration total.")
|
|
85
|
+
compensation_expiration_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__AbgeltVerfallGesamt", coerce=True, nullable=True, description="Compensation + expiration total.")
|
|
86
|
+
rest_total_incl_expiration: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestGesamtInklVerfall", coerce=True, nullable=True, description="Remaining total including expiration.")
|
|
87
|
+
rest_total_incl_planned: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestInklGeplant", coerce=True, nullable=True, description="Remaining including planned.")
|
|
88
|
+
|
|
89
|
+
# Planning and info
|
|
90
|
+
planned_leave_total: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__UrlaubGeplantGesamt", coerce=True, nullable=True, description="Planned leave total.")
|
|
91
|
+
planned_leave_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__UrlaubBeantragtKJ", coerce=True, nullable=True, description="Planned leave current year.")
|
|
92
|
+
recorded_leave_current_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__UrlaubErfasstKJ", coerce=True, nullable=True, description="Recorded leave current year.")
|
|
93
|
+
rest_leave_requested_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestUrlaubBeantragtVJ", coerce=True, nullable=True, description="Remaining leave requested previous year.")
|
|
94
|
+
rest_leave_recorded_previous_year: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Daten__RestUrlaubErfasstVJ", coerce=True, nullable=True, description="Remaining leave recorded previous year.")
|
|
95
|
+
expiration_date: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__UrlaubsVerfallsDatum", coerce=True, nullable=True, description="Leave expiration date.")
|
|
96
|
+
vacation_table_id_value: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Daten__UrlTabId", coerce=True, nullable=True, description="Vacation table id reference.")
|
|
97
|
+
vacation_table_name_value: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__UrlTabName", coerce=True, nullable=True, description="Vacation table name reference.")
|
|
98
|
+
keep_rest_current_year: Optional[Series[bool]] = pa.Field(alias="Daten__ResturlaubNichtBehaltenAktuellesJahr", coerce=True, nullable=True, description="Rest leave not retained current year.")
|
|
99
|
+
keep_rest_next_year: Optional[Series[bool]] = pa.Field(alias="Daten__ResturlaubNichtBehaltenFolgejahr", coerce=True, nullable=True, description="Rest leave not retained next year.")
|
|
100
|
+
expires_in_previous_year: Optional[Series[bool]] = pa.Field(alias="Daten__VerfaelltImVorjahr", coerce=True, nullable=True, description="Expires in previous year flag.")
|
|
101
|
+
rest_previous_year_auto: Optional[Series[bool]] = pa.Field(alias="Daten__RestVJAutomatischErmittelt", coerce=True, nullable=True, description="Rest previous year auto calculated.")
|
|
102
|
+
reference_year: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Daten__Year", coerce=True, nullable=True, description="Reference year.")
|
|
103
|
+
info_disability: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__InfoSchwerbehinderung", coerce=True, nullable=True, description="Disability info.")
|
|
104
|
+
info_base_entitlement: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__InfoGrundanspruch", coerce=True, nullable=True, description="Base entitlement info.")
|
|
105
|
+
info_additional_leave: Optional[Series[pd.StringDtype]] = pa.Field(alias="Daten__InfoZusatzurlaub", coerce=True, nullable=True, description="Additional leave info.")
|
|
106
|
+
recalculation_status: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Daten__Neuberechnungsstatus", coerce=True, nullable=True, description="Recalculation status.")
|
|
107
|
+
|
|
108
|
+
# Flags
|
|
109
|
+
has_vacation_table: Optional[Series[bool]] = pa.Field(alias="HatUrlaubstabelle", coerce=True, nullable=True, description="Has vacation table flag.")
|
|
110
|
+
has_disability_additional_leave: Optional[Series[bool]] = pa.Field(alias="HatZusatzurlaubSchwerbehinderung", coerce=True, nullable=True, description="Has disability additional leave.")
|
|
111
|
+
|
|
112
|
+
class _Annotation:
|
|
113
|
+
primary_key = "combined_key"
|
|
114
|
+
|
|
115
|
+
class Config:
|
|
116
|
+
coerce = True
|
|
117
|
+
strict = "filter"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schemas for Sage Germany work hours payloads.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import pandera as pa
|
|
9
|
+
from pandera.typing import Series
|
|
10
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
11
|
+
|
|
12
|
+
from brynq_sdk_functions import BrynQPanderaDataFrameModel
|
|
13
|
+
|
|
14
|
+
__all__ = ["WorkHoursGet", "WorkHoursCreate"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WorkHoursCreate(BaseModel):
|
|
18
|
+
"""
|
|
19
|
+
Pydantic schema for POST /employeenew/person/arbeitszeiten.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
company_id: int = Field(alias="MdNr", description="Company id (Mandant).", example=1)
|
|
23
|
+
employee_number: int = Field(alias="AnNr", description="Employee number.", example=100)
|
|
24
|
+
valid_from: str = Field(alias="ValidFrom", description="Validity start date ISO-8601.", example="2024-01-01T00:00:00")
|
|
25
|
+
|
|
26
|
+
work_schedule_label: Optional[str] = Field(default=None, alias="AnAzTab", description="Work schedule label.", example="STANDARD")
|
|
27
|
+
monthly_hours: Optional[float] = Field(default=None, alias="AnAzMon", description="Monthly hours.", example=160.0)
|
|
28
|
+
weekly_hours_average: Optional[float] = Field(default=None, alias="AnAzWoDuchschnitt", description="Average weekly hours.", example=40.0)
|
|
29
|
+
has_deviation_from_base: Optional[bool] = Field(
|
|
30
|
+
default=None, alias="AbweichendeBasisWochenStunden", description="Deviation from base weekly hours flag.", example=False
|
|
31
|
+
)
|
|
32
|
+
base_weekly_hours: Optional[float] = Field(default=None, alias="BasisWochenStunden", description="Base weekly hours.", example=40.0)
|
|
33
|
+
days_per_week: Optional[float] = Field(default=None, alias="TageProWoche", description="Days per week.", example=5.0)
|
|
34
|
+
is_industrial: Optional[bool] = Field(default=None, alias="IstGewerblich", description="Industrial employee flag.", example=False)
|
|
35
|
+
|
|
36
|
+
work_schedule_table_id: Optional[int] = Field(default=None, alias="Arbeitszeittabelle.Id", description="Schedule table id.", example=1)
|
|
37
|
+
work_schedule_table_text: Optional[str] = Field(default=None, alias="Arbeitszeittabelle.Text", description="Schedule table text.", example="Standard Schedule")
|
|
38
|
+
work_schedule_table_weeks: Optional[int] = Field(
|
|
39
|
+
default=None, alias="Arbeitszeittabelle.AnzahlWochen", description="Schedule table week count.", example=1
|
|
40
|
+
)
|
|
41
|
+
work_schedule_table_valid_from: Optional[str] = Field(
|
|
42
|
+
default=None, alias="ArbeitszeittabelleGueltigAb", description="Schedule table valid-from date.", example="2024-01-01"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
part_time_factor: Optional[float] = Field(default=None, alias="Teilzeitfaktor", description="Part-time factor.", example=1.0)
|
|
46
|
+
change_reason_id: Optional[int] = Field(default=None, alias="AenderungsGrund.Id", description="Change reason id.", example=1)
|
|
47
|
+
change_reason_text: Optional[str] = Field(default=None, alias="AenderungsGrund.Text", description="Change reason text.", example="Initial Setup")
|
|
48
|
+
work_time_changed: Optional[bool] = Field(default=None, alias="ArbeitszeitHatSichGeaendert", description="Work time changed flag.", example=False)
|
|
49
|
+
|
|
50
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class WorkHoursGet(BrynQPanderaDataFrameModel):
|
|
54
|
+
"""
|
|
55
|
+
Pandera schema for employee work hours (Arbeitszeiten) data.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
company_id: Series[pd.Int64Dtype] = pa.Field(alias="MdNr", coerce=True, nullable=False, description="Company id.")
|
|
59
|
+
employee_number: Series[pd.Int64Dtype] = pa.Field(alias="AnNr", coerce=True, nullable=False, description="Employee number.")
|
|
60
|
+
combined_key: Series[pd.StringDtype] = pa.Field(alias="combined_key", coerce=True, nullable=False, description="Synthetic MdNr_AnNr combined key.")
|
|
61
|
+
valid_from: Optional[Series[pd.StringDtype]] = pa.Field(alias="ValidFrom", coerce=True, nullable=True, description="Validity start date.")
|
|
62
|
+
work_schedule_label: Optional[Series[pd.StringDtype]] = pa.Field(alias="AnAzTab", coerce=True, nullable=True, description="Work schedule label.")
|
|
63
|
+
monthly_hours: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="AnAzMon", coerce=True, nullable=True, description="Monthly hours.")
|
|
64
|
+
weekly_hours_average: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="AnAzWoDuchschnitt", coerce=True, nullable=True, description="Average weekly hours.")
|
|
65
|
+
has_deviation_from_base: Optional[Series[bool]] = pa.Field(alias="AbweichendeBasisWochenStunden", coerce=True, nullable=True, description="Deviation from base weekly hours flag.")
|
|
66
|
+
base_weekly_hours: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="BasisWochenStunden", coerce=True, nullable=True, description="Base weekly hours.")
|
|
67
|
+
days_per_week: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="TageProWoche", coerce=True, nullable=True, description="Days per week.")
|
|
68
|
+
is_industrial: Optional[Series[bool]] = pa.Field(alias="IstGewerblich", coerce=True, nullable=True, description="Industrial employee flag.")
|
|
69
|
+
|
|
70
|
+
work_schedule_table_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Arbeitszeittabelle__Id", coerce=True, nullable=True, description="Work schedule table id.")
|
|
71
|
+
work_schedule_table_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="Arbeitszeittabelle__Text", coerce=True, nullable=True, description="Work schedule table text.")
|
|
72
|
+
work_schedule_table_weeks: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Arbeitszeittabelle__AnzahlWochen", coerce=True, nullable=True, description="Work schedule table week count.")
|
|
73
|
+
work_schedule_table_valid_from: Optional[Series[pd.StringDtype]] = pa.Field(alias="ArbeitszeittabelleGueltigAb", coerce=True, nullable=True, description="Work schedule table valid-from date.")
|
|
74
|
+
|
|
75
|
+
part_time_factor: Optional[Series[pd.Float64Dtype]] = pa.Field(alias="Teilzeitfaktor", coerce=True, nullable=True, description="Part-time factor.")
|
|
76
|
+
change_reason_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="AenderungsGrund__Id", coerce=True, nullable=True, description="Change reason id.")
|
|
77
|
+
change_reason_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="AenderungsGrund__Text", coerce=True, nullable=True, description="Change reason text.")
|
|
78
|
+
change_reason_id_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="AenderungsGrund__IdMitText", coerce=True, nullable=True, description="Change reason id-text.")
|
|
79
|
+
work_time_changed: Optional[Series[bool]] = pa.Field(alias="ArbeitszeitHatSichGeaendert", coerce=True, nullable=True, description="Work time changed flag.")
|
|
80
|
+
|
|
81
|
+
calendar_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Kalender__Id", coerce=True, nullable=True, description="Calendar id.")
|
|
82
|
+
calendar_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="Kalender__Text", coerce=True, nullable=True, description="Calendar text.")
|
|
83
|
+
calendar_id_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="Kalender__IdMitText", coerce=True, nullable=True, description="Calendar id-text.")
|
|
84
|
+
tenant_calendar_id: Optional[Series[pd.Int64Dtype]] = pa.Field(alias="Mandantenkalender__Id", coerce=True, nullable=True, description="Tenant calendar id.")
|
|
85
|
+
tenant_calendar_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="Mandantenkalender__Text", coerce=True, nullable=True, description="Tenant calendar text.")
|
|
86
|
+
tenant_calendar_id_text: Optional[Series[pd.StringDtype]] = pa.Field(alias="Mandantenkalender__IdMitText", coerce=True, nullable=True, description="Tenant calendar id-text.")
|
|
87
|
+
calendar_source: Optional[Series[pd.StringDtype]] = pa.Field(alias="KalenderQuelle", coerce=True, nullable=True, description="Calendar source.")
|
|
88
|
+
|
|
89
|
+
class _Annotation:
|
|
90
|
+
primary_key = "combined_key"
|
|
91
|
+
|
|
92
|
+
class Config:
|
|
93
|
+
coerce = True
|
|
94
|
+
strict = "filter"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Start/end date endpoint implementations for Sage Germany.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
from brynq_sdk_functions import Functions
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
|
|
14
|
+
from .helpers import start_end_flat_to_nested
|
|
15
|
+
from .schemas.start_end_dates import StartEndDatesCreate, StartEndDatesGet
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .employees import Employees
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StartEndDates:
|
|
22
|
+
"""
|
|
23
|
+
Handles employment period operations scoped to employees.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, sage) -> None:
|
|
27
|
+
self.sage = sage
|
|
28
|
+
self.base_url = "/employeenew/person/beschaeftigungszeiten"
|
|
29
|
+
|
|
30
|
+
def get(
|
|
31
|
+
self,
|
|
32
|
+
date: Optional[str] = None,
|
|
33
|
+
company_id: Optional[int] = None,
|
|
34
|
+
employee_number: Optional[int] = None,
|
|
35
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
36
|
+
"""
|
|
37
|
+
Retrieve employment period data for one or many employees.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
if company_id is not None and employee_number is not None:
|
|
41
|
+
employee_keys = [{"MdNr": company_id, "AnNr": employee_number}]
|
|
42
|
+
else:
|
|
43
|
+
employee_keys = self.sage._employee_search()
|
|
44
|
+
|
|
45
|
+
if not employee_keys:
|
|
46
|
+
return pd.DataFrame(), pd.DataFrame()
|
|
47
|
+
|
|
48
|
+
period_records = []
|
|
49
|
+
effective_date = date or datetime.now(timezone.utc).strftime("%Y-%m-%dT00:00:00")
|
|
50
|
+
|
|
51
|
+
for key in employee_keys:
|
|
52
|
+
response = self.sage.get(
|
|
53
|
+
path=self.base_url,
|
|
54
|
+
params={"MdNr": key["MdNr"], "AnNr": key["AnNr"], "date": effective_date},
|
|
55
|
+
)
|
|
56
|
+
response.raise_for_status()
|
|
57
|
+
payload = response.json()
|
|
58
|
+
if isinstance(payload, dict):
|
|
59
|
+
period_records.append(payload)
|
|
60
|
+
|
|
61
|
+
if not period_records:
|
|
62
|
+
return pd.DataFrame(), pd.DataFrame()
|
|
63
|
+
|
|
64
|
+
dataframe = pd.json_normalize(period_records, sep="__")
|
|
65
|
+
|
|
66
|
+
valid_data, invalid_data = Functions.validate_data(
|
|
67
|
+
df=dataframe,
|
|
68
|
+
schema=StartEndDatesGet,
|
|
69
|
+
)
|
|
70
|
+
return valid_data, invalid_data
|
|
71
|
+
except Exception as exc:
|
|
72
|
+
raise RuntimeError("Failed to retrieve employment period data.") from exc
|
|
73
|
+
|
|
74
|
+
def create(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
75
|
+
"""
|
|
76
|
+
Create or update employment period (Beschaeftigungszeiten) for an employee.
|
|
77
|
+
|
|
78
|
+
Accepts flat snake_case keys; converts to nested Sage payload using schema prefixes.
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
nested_payload = start_end_flat_to_nested(data, StartEndDatesCreate)
|
|
82
|
+
validated = StartEndDatesCreate(**nested_payload)
|
|
83
|
+
payload = validated.model_dump(by_alias=True, exclude_none=True, mode="json")
|
|
84
|
+
|
|
85
|
+
response = self.sage.post(
|
|
86
|
+
path=self.base_url,
|
|
87
|
+
body=payload,
|
|
88
|
+
)
|
|
89
|
+
response.raise_for_status()
|
|
90
|
+
return response
|
|
91
|
+
except Exception as exc:
|
|
92
|
+
raise RuntimeError("Failed to create Sage Germany employment period.") from exc
|
|
93
|
+
|
|
94
|
+
def delete(
|
|
95
|
+
self,
|
|
96
|
+
company_id: int,
|
|
97
|
+
employee_number: int,
|
|
98
|
+
entry_date: str,
|
|
99
|
+
combined_key: Optional[str] = None,
|
|
100
|
+
) -> Dict[str, Any]:
|
|
101
|
+
"""
|
|
102
|
+
Delete an employment period by MdNr/AnNr/Eintritt (query params).
|
|
103
|
+
"""
|
|
104
|
+
params: Dict[str, Any] = {
|
|
105
|
+
"MdNr": company_id,
|
|
106
|
+
"AnNr": employee_number,
|
|
107
|
+
"Eintritt": entry_date,
|
|
108
|
+
}
|
|
109
|
+
if combined_key is not None:
|
|
110
|
+
params["CombinedKey"] = combined_key
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
response = self.sage.delete(
|
|
114
|
+
path=self.base_url,
|
|
115
|
+
params=params,
|
|
116
|
+
)
|
|
117
|
+
response.raise_for_status()
|
|
118
|
+
try:
|
|
119
|
+
return response.json()
|
|
120
|
+
except Exception:
|
|
121
|
+
return {"raw_response": response.text}
|
|
122
|
+
except Exception as exc:
|
|
123
|
+
raise RuntimeError("Failed to delete Sage Germany employment period.") from exc
|