brynq-sdk-nmbrs 2.3.3.dev0__py3-none-any.whl → 2.4.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brynq_sdk_nmbrs/__init__.py +95 -91
- brynq_sdk_nmbrs/absence.py +1 -4
- brynq_sdk_nmbrs/address.py +5 -81
- brynq_sdk_nmbrs/bank.py +1 -2
- brynq_sdk_nmbrs/companies.py +130 -39
- brynq_sdk_nmbrs/contract.py +0 -1
- brynq_sdk_nmbrs/days.py +1 -1
- brynq_sdk_nmbrs/debtors.py +53 -65
- brynq_sdk_nmbrs/department.py +23 -111
- brynq_sdk_nmbrs/document.py +195 -4
- brynq_sdk_nmbrs/employee_wage_tax_settings.py +13 -5
- brynq_sdk_nmbrs/employees.py +55 -21
- brynq_sdk_nmbrs/employment.py +0 -2
- brynq_sdk_nmbrs/extra_fields.py +126 -0
- brynq_sdk_nmbrs/function.py +35 -97
- brynq_sdk_nmbrs/leave.py +53 -58
- brynq_sdk_nmbrs/manager.py +51 -35
- brynq_sdk_nmbrs/salaries.py +48 -65
- brynq_sdk_nmbrs/salary_tables.py +1 -4
- brynq_sdk_nmbrs/schedules.py +5 -78
- brynq_sdk_nmbrs/schemas/absence.py +26 -16
- brynq_sdk_nmbrs/schemas/address.py +2 -29
- brynq_sdk_nmbrs/schemas/children.py +0 -2
- brynq_sdk_nmbrs/schemas/contracts.py +7 -6
- brynq_sdk_nmbrs/schemas/costcenter.py +2 -2
- brynq_sdk_nmbrs/schemas/costunit.py +2 -0
- brynq_sdk_nmbrs/schemas/days.py +13 -11
- brynq_sdk_nmbrs/schemas/debtor.py +6 -28
- brynq_sdk_nmbrs/schemas/department.py +5 -39
- brynq_sdk_nmbrs/schemas/document.py +0 -2
- brynq_sdk_nmbrs/schemas/employee_wage_tax_settings.py +75 -0
- brynq_sdk_nmbrs/schemas/employees.py +2 -0
- brynq_sdk_nmbrs/schemas/extra_fields.py +56 -0
- brynq_sdk_nmbrs/schemas/function.py +5 -32
- brynq_sdk_nmbrs/schemas/hours.py +5 -1
- brynq_sdk_nmbrs/schemas/leave.py +17 -6
- brynq_sdk_nmbrs/schemas/manager.py +16 -24
- brynq_sdk_nmbrs/schemas/salary.py +11 -1
- brynq_sdk_nmbrs/schemas/schedules.py +2 -54
- brynq_sdk_nmbrs/schemas/svw_settings.py +116 -0
- brynq_sdk_nmbrs/schemas/wage_tax.py +53 -21
- brynq_sdk_nmbrs/schemas/wagecomponents.py +6 -9
- brynq_sdk_nmbrs/svw_settings.py +213 -0
- brynq_sdk_nmbrs/wage_tax.py +120 -11
- {brynq_sdk_nmbrs-2.3.3.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/METADATA +1 -1
- brynq_sdk_nmbrs-2.4.5.dist-info/RECORD +58 -0
- {brynq_sdk_nmbrs-2.3.3.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/WHEEL +1 -1
- brynq_sdk_nmbrs/schemas/social_insurance.py +0 -73
- brynq_sdk_nmbrs/social_insurance.py +0 -130
- brynq_sdk_nmbrs-2.3.3.dev0.dist-info/RECORD +0 -55
- {brynq_sdk_nmbrs-2.3.3.dev0.dist-info → brynq_sdk_nmbrs-2.4.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import pandera as pa
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
from brynq_sdk_functions import BrynQPanderaDataFrameModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# ---------------------------
|
|
11
|
+
# Get Schemas - Company SVW Settings
|
|
12
|
+
# ---------------------------
|
|
13
|
+
class CompanySVWSettingsGet(BrynQPanderaDataFrameModel):
|
|
14
|
+
"""Schema for validating company SVW settings data."""
|
|
15
|
+
svw_settings_id: pd.StringDtype = pa.Field(coerce=True, description="SVW Settings ID", alias="SVWSettingsId")
|
|
16
|
+
own_risk_dif_wga: pd.BooleanDtype = pa.Field(coerce=True, nullable=True, description="Own risk DIF WGA", alias="ownRiskDifWGA")
|
|
17
|
+
own_risk_health_care: pd.BooleanDtype = pa.Field(coerce=True, nullable=True, description="Own risk health care", alias="ownRiskHealthCare")
|
|
18
|
+
aof: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="AOF (Low/High)", alias="AOF")
|
|
19
|
+
diff_wga_wn: pd.Float64Dtype = pa.Field(coerce=True, nullable=True, description="Diff WGA Wn", alias="diffWGAWn")
|
|
20
|
+
diff_wga_wg: pd.Float64Dtype = pa.Field(coerce=True, nullable=True, description="Diff WGA Wg", alias="diffWGAWg")
|
|
21
|
+
zw_wg: pd.Float64Dtype = pa.Field(coerce=True, nullable=True, description="ZW Wg", alias="zwWg")
|
|
22
|
+
|
|
23
|
+
class _Annotation:
|
|
24
|
+
primary_key = "svw_settings_id"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ---------------------------
|
|
28
|
+
# Get Schemas - Employee SVW Settings
|
|
29
|
+
# ---------------------------
|
|
30
|
+
class EmployeeSVWSettingsGet(BrynQPanderaDataFrameModel):
|
|
31
|
+
"""Schema for validating employee SVW settings data."""
|
|
32
|
+
employee_id: pd.StringDtype = pa.Field(coerce=True, description="Employee ID", alias="employeeId")
|
|
33
|
+
zw: pd.BooleanDtype = pa.Field(coerce=True, nullable=True, description="ZW (Ziektewet)", alias="ZW")
|
|
34
|
+
ww: pd.BooleanDtype = pa.Field(coerce=True, nullable=True, description="WW (Werkloosheidswet)", alias="WW")
|
|
35
|
+
wao_wia: pd.BooleanDtype = pa.Field(coerce=True, nullable=True, description="WAO/WIA", alias="waoWia")
|
|
36
|
+
nature_of_employment: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="Nature of employment", alias="natureOfEmployment")
|
|
37
|
+
phase_classification: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="Phase classification", alias="phaseClassification")
|
|
38
|
+
tax_sequence_number: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="Tax sequence number", alias="taxSequenceNumber")
|
|
39
|
+
code_zvw: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="Code ZVW", alias="codeZvw")
|
|
40
|
+
income_related_contribution_zvw: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="Income related contribution ZVW", alias="incomeRelatedContributionZvw")
|
|
41
|
+
influence_obliged_insurance: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="Influence obliged insurance", alias="influenceObligedInsurance")
|
|
42
|
+
wage_cost_benefit_code: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="Wage cost benefit code", alias="wageCostBenefit.code")
|
|
43
|
+
wage_cost_benefit_description: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="Wage cost benefit description", alias="wageCostBenefit.description")
|
|
44
|
+
wage_cost_benefit_end_period: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="Wage cost benefit end period", alias="wageCostBenefit.endPeriod")
|
|
45
|
+
wage_cost_benefit_end_year: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="Wage cost benefit end year", alias="wageCostBenefit.endYear")
|
|
46
|
+
cao_code: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="CAO code", alias="CAO.code")
|
|
47
|
+
cao_description: pd.StringDtype = pa.Field(coerce=True, nullable=True, description="CAO description", alias="CAO.CAODescription")
|
|
48
|
+
hirer_cao_code: Optional[pd.Int64Dtype] = pa.Field(
|
|
49
|
+
coerce=True,
|
|
50
|
+
nullable=True,
|
|
51
|
+
description="Hirer CAO code",
|
|
52
|
+
alias="hirerCAO.code",
|
|
53
|
+
)
|
|
54
|
+
hirer_cao_description: Optional[pd.StringDtype] = pa.Field(
|
|
55
|
+
coerce=True,
|
|
56
|
+
nullable=True,
|
|
57
|
+
description="Hirer CAO description",
|
|
58
|
+
alias="hirerCAO.CAODescription",
|
|
59
|
+
)
|
|
60
|
+
period_year: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="Period year", alias="period.year")
|
|
61
|
+
period_period: pd.Int64Dtype = pa.Field(coerce=True, nullable=True, description="Period", alias="period.period")
|
|
62
|
+
created_at: str = pa.Field(coerce=True, nullable=True, description="Created at", alias="createdAt")
|
|
63
|
+
|
|
64
|
+
class _Annotation:
|
|
65
|
+
primary_key = "employee_id"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------
|
|
69
|
+
# Create/Update Schemas
|
|
70
|
+
# ---------------------------
|
|
71
|
+
class Period(BaseModel):
|
|
72
|
+
"""Period model for specifying year and period."""
|
|
73
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
74
|
+
|
|
75
|
+
period_year: int = Field(..., ge=1900, le=2100, example=2021, description="Year", alias="year")
|
|
76
|
+
period_period: int = Field(..., ge=1, le=53, example=4, description="Period", alias="period")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class WageCostBenefit(BaseModel):
|
|
80
|
+
"""Wage cost benefit model."""
|
|
81
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
82
|
+
|
|
83
|
+
code: Optional[int] = Field(None, example=1234, description="Wage cost benefit code", alias="code")
|
|
84
|
+
end_period: Optional[int] = Field(None, ge=1, le=53, example=12, description="End period", alias="endPeriod")
|
|
85
|
+
end_year: Optional[int] = Field(None, ge=1900, le=2100, example=2025, description="End year", alias="endYear")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class SVWSettingsCreate(BaseModel):
|
|
89
|
+
"""Schema for creating employee SVW settings."""
|
|
90
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
91
|
+
|
|
92
|
+
zw: Optional[bool] = Field(None, example=True, description="ZW (Ziektewet)", alias="ZW")
|
|
93
|
+
ww: Optional[bool] = Field(None, example=True, description="WW (Werkloosheidswet)", alias="WW")
|
|
94
|
+
wao_wia: Optional[bool] = Field(None, example=True, description="WAO/WIA", alias="waoWia")
|
|
95
|
+
nature_of_employment: int = Field(..., example=1, description="Nature of employment code (1, 4, 6, 7, 11, 18, 21, 22, 23, 24, 79, 81, 82, 83)", alias="natureOfEmployment")
|
|
96
|
+
phase_classification: Optional[int] = Field(None, example=1, description="Phase classification (only for nature of employment 11)", alias="phaseClassification")
|
|
97
|
+
code_zvw: str = Field(
|
|
98
|
+
...,
|
|
99
|
+
pattern=r'^[ABGHIKLMN]$',
|
|
100
|
+
example="A",
|
|
101
|
+
description="Code ZVW (A, B, G, H, I, K, L, M, N)",
|
|
102
|
+
alias="codeZvw",
|
|
103
|
+
)
|
|
104
|
+
tax_sequence_number: int = Field(..., ge=0, example=0, description="Tax sequence number", alias="taxSequenceNumber")
|
|
105
|
+
income_related_contribution_zvw: int = Field(..., ge=0, example=0, description="Income related contribution ZVW", alias="incomeRelatedContributionZvw")
|
|
106
|
+
influence_obliged_insurance: str = Field(
|
|
107
|
+
...,
|
|
108
|
+
pattern=r'^[0ABDE]$',
|
|
109
|
+
example="0",
|
|
110
|
+
description="Influence obliged insurance (0, A, B, D, E)",
|
|
111
|
+
alias="influenceObligedInsurance",
|
|
112
|
+
)
|
|
113
|
+
wage_cost_benefit: Optional[WageCostBenefit] = Field(None, example={"code": 1234, "endPeriod": 12, "endYear": 2025}, description="Wage cost benefit", alias="wageCostBenefit")
|
|
114
|
+
cao: Optional[int] = Field(None, example=100, description="CAO code", alias="CAO")
|
|
115
|
+
hirer_cao: Optional[int] = Field(None, example=100, description="Hirer CAO code", alias="hirerCAO")
|
|
116
|
+
period: Period = Field(..., example={"year": 2021, "period": 4}, description="Period to apply settings")
|
|
@@ -1,17 +1,36 @@
|
|
|
1
|
-
import math
|
|
2
1
|
from datetime import datetime
|
|
3
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
4
3
|
|
|
5
4
|
import pandas as pd
|
|
6
5
|
import pandera as pa
|
|
7
|
-
import pandera.extensions as extensions
|
|
8
6
|
from pandera import Bool
|
|
9
|
-
from pandera.typing import DateTime,
|
|
10
|
-
from pydantic import BaseModel, Field
|
|
7
|
+
from pandera.typing import DateTime, Series, String
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
11
9
|
|
|
12
10
|
from brynq_sdk_functions import BrynQPanderaDataFrameModel
|
|
13
11
|
|
|
14
12
|
|
|
13
|
+
# ---------------------------
|
|
14
|
+
# REST API Schema - Company Wage Tax Declarations
|
|
15
|
+
# ---------------------------
|
|
16
|
+
class CompanyWageTaxGet(BrynQPanderaDataFrameModel):
|
|
17
|
+
"""Schema for validating company wage tax data from REST API."""
|
|
18
|
+
wage_tax_id: Series[String] = pa.Field(coerce=True, description="Wage Tax ID", alias="wageTaxId")
|
|
19
|
+
serial_number: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Serial Number", alias="serialNumber")
|
|
20
|
+
total_general: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Total General", alias="totalGeneral")
|
|
21
|
+
payment_reference: Series[String] = pa.Field(coerce=True, nullable=True, description="Payment Reference", alias="paymentReference")
|
|
22
|
+
period: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Period", alias="period")
|
|
23
|
+
year: Series[pd.Int64Dtype] = pa.Field(coerce=True, nullable=True, description="Year", alias="year")
|
|
24
|
+
status: Series[String] = pa.Field(coerce=True, nullable=True, description="Status", alias="status")
|
|
25
|
+
sent_at: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Sent At", alias="sentAt")
|
|
26
|
+
period_start: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Period Start", alias="periodStart")
|
|
27
|
+
period_end: Series[DateTime] = pa.Field(coerce=True, nullable=True, description="Period End", alias="periodEnd")
|
|
28
|
+
correction_period_start: Series[DateTime] = pa.Field(nullable=True, coerce=True, description="Correction Period Start", alias="correctionPeriodStart")
|
|
29
|
+
correction_period_end: Series[DateTime] = pa.Field(nullable=True, coerce=True, description="Correction Period End", alias="correctionPeriodEnd")
|
|
30
|
+
|
|
31
|
+
class _Annotation:
|
|
32
|
+
primary_key = "wage_tax_id"
|
|
33
|
+
|
|
15
34
|
# ---------------------------
|
|
16
35
|
# REST API Get Schema (Wage Tax Settings History)
|
|
17
36
|
# ---------------------------
|
|
@@ -70,7 +89,7 @@ class WageTaxSettingsCreate(BaseModel):
|
|
|
70
89
|
# ---------------------------
|
|
71
90
|
# SOAP API Get Schema (Company Wage Tax List)
|
|
72
91
|
# ---------------------------
|
|
73
|
-
class
|
|
92
|
+
class CompanyWageTaxGetSOAP(BrynQPanderaDataFrameModel):
|
|
74
93
|
wagetax_id: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Loonaangifte ID", alias="LoonaangifteID")
|
|
75
94
|
serial_number: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Serial Number", alias="SerialNumber")
|
|
76
95
|
payment_reference: Series[String] = pa.Field(coerce=True, description="Payment Reference", alias="PaymentReference")
|
|
@@ -84,6 +103,9 @@ class CompanyWageTaxGet(BrynQPanderaDataFrameModel):
|
|
|
84
103
|
correction_period_start: Series[DateTime] = pa.Field(nullable=True, coerce=True, description="Correction Tijdvak Start", alias="CorrectionTijdvakStart")
|
|
85
104
|
correction_period_end: Series[DateTime] = pa.Field(nullable=True, coerce=True, description="Correction Tijdvak End", alias="CorrectionTijdvakEnd")
|
|
86
105
|
|
|
106
|
+
class _Annotation:
|
|
107
|
+
primary_key = "wagetax_id"
|
|
108
|
+
|
|
87
109
|
# <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
88
110
|
# <soap:Body>
|
|
89
111
|
# <WageTax_GetListResponse xmlns="https://api.nmbrs.nl/soap/v3/EmployeeService">
|
|
@@ -126,22 +148,32 @@ class WageTaxGet(BrynQPanderaDataFrameModel):
|
|
|
126
148
|
holiday_vouchers: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Vakantie Bonnen", alias="VakantieBonnen")
|
|
127
149
|
code_calculate_30_percent_rule: Series[pd.Int64Dtype] = pa.Field(coerce=True, description="Code Calc 30% Rule", alias="CodeCalc30PercRule")
|
|
128
150
|
|
|
151
|
+
class _Annotation:
|
|
152
|
+
primary_key = "wage_tax_id"
|
|
153
|
+
foreign_keys = {
|
|
154
|
+
"employee_id": {
|
|
155
|
+
"parent_schema": "EmployeeSchema",
|
|
156
|
+
"parent_column": "employee_id",
|
|
157
|
+
"cardinality": "N:1"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
class WageTaxUpdate(BaseModel):
|
|
130
|
-
employee_id: Optional[int] = Field(None, example=
|
|
131
|
-
wage_tax_id: Optional[int] = Field(None, example=
|
|
132
|
-
yearly_salary: Optional[float] = Field(None, example=
|
|
133
|
-
deviation_special_rate_payroll_tax_deduction: Optional[float] = Field(None, example=
|
|
134
|
-
auto_small_jobs: Optional[bool] = Field(None, example=
|
|
135
|
-
payroll_tax_deduction: Optional[bool] = Field(None, example=
|
|
136
|
-
benefit_scheme: Optional[bool] = Field(None, example=
|
|
137
|
-
payroll_tax: Optional[bool] = Field(None, example=
|
|
138
|
-
code_tax_reduction: Optional[int] = Field(None, example=
|
|
139
|
-
color_table: Optional[int] = Field(None, example=
|
|
140
|
-
type_of_income: Optional[int] = Field(None, example=
|
|
141
|
-
special_table: Optional[int] = Field(None, example=
|
|
142
|
-
period_table: Optional[int] = Field(None, example=
|
|
143
|
-
holiday_vouchers: Optional[int] = Field(None, example=
|
|
144
|
-
code_calculate_30_percent_rule: Optional[int] = Field(None, example=
|
|
162
|
+
employee_id: Optional[int] = Field(None, example=1234567890, description="Employee ID", alias="EmployeeId")
|
|
163
|
+
wage_tax_id: Optional[int] = Field(None, example=2998190, description="Wage Tax Settings ID", alias="Id")
|
|
164
|
+
yearly_salary: Optional[float] = Field(None, example=12144.00, description="Yearly Salary", alias="JaarloonBT")
|
|
165
|
+
deviation_special_rate_payroll_tax_deduction: Optional[float] = Field(None, example=0, description="Afw Bijz Tarief LH", alias="AfwBijzTariefLH")
|
|
166
|
+
auto_small_jobs: Optional[bool] = Field(None, example=True, description="Auto Kleine Banen Regeling", alias="AutoKleineBanenRegeling")
|
|
167
|
+
payroll_tax_deduction: Optional[bool] = Field(None, example=True, description="Loonheffingkorting", alias="Loonheffingkorting")
|
|
168
|
+
benefit_scheme: Optional[bool] = Field(None, example=True, description="Voordeelreg", alias="Voordeelreg")
|
|
169
|
+
payroll_tax: Optional[bool] = Field(None, example=True, description="Loonheffing", alias="Loonheffing")
|
|
170
|
+
code_tax_reduction: Optional[int] = Field(None, example=0, description="Code Afdrachtvermindering", alias="CodeAfdrachtvermindering")
|
|
171
|
+
color_table: Optional[int] = Field(None, example=1, description="Kleur Tabel", alias="KleurTabel")
|
|
172
|
+
type_of_income: Optional[int] = Field(None, example=15, description="Soort Inkomen", alias="SoortInkomen")
|
|
173
|
+
special_table: Optional[int] = Field(None, example=0, description="Speciale Tabel", alias="SpecialeTabel")
|
|
174
|
+
period_table: Optional[int] = Field(None, example=2, description="Tijdvak Tabel", alias="TijdvakTabel")
|
|
175
|
+
holiday_vouchers: Optional[int] = Field(None, example=0, description="Vakantie Bonnen", alias="VakantieBonnen")
|
|
176
|
+
code_calculate_30_percent_rule: Optional[int] = Field(None, example=0, description="Code Calc 30% Rule", alias="CodeCalc30PercRule")
|
|
145
177
|
|
|
146
178
|
def to_soap_settings(self, soap_client):
|
|
147
179
|
"""Convert to SOAP WageTaxSettings object"""
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import math
|
|
2
1
|
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import pandas as pd
|
|
@@ -7,8 +6,6 @@ from pandera.typing import Float, Series, String
|
|
|
7
6
|
from pydantic import (
|
|
8
7
|
BaseModel,
|
|
9
8
|
Field,
|
|
10
|
-
confloat,
|
|
11
|
-
conint,
|
|
12
9
|
field_validator,
|
|
13
10
|
model_serializer,
|
|
14
11
|
)
|
|
@@ -69,12 +66,12 @@ class VariableWageComponentGet(BrynQPanderaDataFrameModel):
|
|
|
69
66
|
# Upload Schemas
|
|
70
67
|
# ---------------------------
|
|
71
68
|
class PeriodPost(BaseModel):
|
|
72
|
-
period_details_year: int = Field(
|
|
73
|
-
period_details_period: int = Field(
|
|
69
|
+
period_details_year: Optional[int] = Field(None, ge=1900, le=2100, example=2021, description="Year", alias="year")
|
|
70
|
+
period_details_period: Optional[int] = Field(None, ge=1, le=53, example=4, description="Period", alias="period")
|
|
74
71
|
|
|
75
72
|
class FixedWageComponentCreate(BaseModel):
|
|
76
73
|
code: int = Field(..., ge=1, example=1100, description="Wage Component Code", alias="code")
|
|
77
|
-
value: float = Field(...,
|
|
74
|
+
value: float = Field(..., example=500, description="Wage Component Value", alias="value")
|
|
78
75
|
end_year: Optional[int] = Field(None, ge=1900, le=2100, example=2023, description="End Year", alias="endYear")
|
|
79
76
|
end_period: Optional[int] = Field(None, ge=1, le=53, example=6, description="End Period", alias="endPeriod")
|
|
80
77
|
comment: Optional[str] = Field(None, example="some comment", description="Comment", alias="comment")
|
|
@@ -113,7 +110,7 @@ class FixedWageComponentCreate(BaseModel):
|
|
|
113
110
|
class FixedWageComponentUpdate(BaseModel):
|
|
114
111
|
fixed_wage_component_id: str = Field(..., example="643c6b90-57c6-4199-9e4e-ded553572d78", description="Fixed Wage Component ID", alias="fixedWageComponentId")
|
|
115
112
|
code: Optional[int] = Field(None, ge=1, example=1100, description="Wage Component Code", alias="code")
|
|
116
|
-
value: Optional[float] = Field(None,
|
|
113
|
+
value: Optional[float] = Field(None, example=500, description="Wage Component Value", alias="value")
|
|
117
114
|
end_year: Optional[int] = Field(None, ge=1900, le=2100, example=2023, description="End Year", alias="endYear")
|
|
118
115
|
end_period: Optional[int] = Field(None, ge=1, le=53, example=6, description="End Period", alias="endPeriod")
|
|
119
116
|
comment: Optional[str] = Field(None, example="string", description="Comment", alias="comment")
|
|
@@ -124,7 +121,7 @@ class FixedWageComponentUpdate(BaseModel):
|
|
|
124
121
|
|
|
125
122
|
class VariableWageComponentCreate(BaseModel):
|
|
126
123
|
code: int = Field(..., ge=1, example=3045, description="Wage Component Code", alias="code")
|
|
127
|
-
value: float = Field(...,
|
|
124
|
+
value: float = Field(..., example=200, description="Wage Component Value", alias="value")
|
|
128
125
|
comment: Optional[str] = Field(None, example="comment", description="Comment", alias="comment")
|
|
129
126
|
cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
|
|
130
127
|
cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
|
|
@@ -153,7 +150,7 @@ class VariableWageComponentCreate(BaseModel):
|
|
|
153
150
|
class VariableWageComponentUpdate(BaseModel):
|
|
154
151
|
variable_wage_component_id: str = Field(..., example="7fc59095-daed-4746-a7f8-a454e38e3683", description="Variable Wage Component ID", alias="variableWageComponentId")
|
|
155
152
|
code: Optional[int] = Field(None, ge=1, example=3045, description="Wage Component Code", alias="code")
|
|
156
|
-
value: Optional[float] = Field(None,
|
|
153
|
+
value: Optional[float] = Field(None, example=2200, description="Wage Component Value", alias="value")
|
|
157
154
|
comment: Optional[str] = Field(None, example="comment", description="Comment", alias="comment")
|
|
158
155
|
cost_center_id: Optional[str] = Field(None, example="aa506564-d1db-4fa8-83dc-d68db4cfcd82", description="Cost Center ID", alias="costCenterId")
|
|
159
156
|
cost_unit_id: Optional[str] = Field(None, example="d8ac6afb-2ac6-43bf-9880-2d382cdace43", description="Cost Unit ID", alias="costUnitId")
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from brynq_sdk_functions import Functions
|
|
7
|
+
|
|
8
|
+
from .schemas.svw_settings import (
|
|
9
|
+
CompanySVWSettingsGet,
|
|
10
|
+
EmployeeSVWSettingsGet,
|
|
11
|
+
SVWSettingsCreate,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CompanySVWSettings:
|
|
16
|
+
"""
|
|
17
|
+
Class for handling company-level SVW (Social Insurance) settings.
|
|
18
|
+
|
|
19
|
+
Endpoints:
|
|
20
|
+
- GET /api/companies/{companyId}/svwsettings
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, nmbrs):
|
|
24
|
+
self.nmbrs = nmbrs
|
|
25
|
+
|
|
26
|
+
def get(self,
|
|
27
|
+
company_id: Optional[str] = None,
|
|
28
|
+
year: Optional[int] = None,
|
|
29
|
+
period: Optional[int] = None,
|
|
30
|
+
created_from: Optional[str] = None) -> tuple:
|
|
31
|
+
"""
|
|
32
|
+
Get company SVW settings (SVW table).
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
company_id: Optional company ID. If not provided, fetches for all companies.
|
|
36
|
+
year: Optional year filter
|
|
37
|
+
period: Optional period filter
|
|
38
|
+
created_from: Optional datetime filter for records created after this date
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Tuple of (valid_data, invalid_data) DataFrames
|
|
42
|
+
"""
|
|
43
|
+
if company_id:
|
|
44
|
+
df = self._get(company_id, year, period, created_from)
|
|
45
|
+
else:
|
|
46
|
+
df = pd.DataFrame()
|
|
47
|
+
for company in self.nmbrs.company_ids:
|
|
48
|
+
df = pd.concat([df, self._get(company, year, period, created_from)])
|
|
49
|
+
|
|
50
|
+
if df.empty:
|
|
51
|
+
return df, pd.DataFrame()
|
|
52
|
+
|
|
53
|
+
valid_data, invalid_data = Functions.validate_data(df=df, schema=CompanySVWSettingsGet, debug=self.nmbrs.debug)
|
|
54
|
+
return valid_data, invalid_data
|
|
55
|
+
|
|
56
|
+
def _get(self,
|
|
57
|
+
company_id: str,
|
|
58
|
+
year: Optional[int] = None,
|
|
59
|
+
period: Optional[int] = None,
|
|
60
|
+
created_from: Optional[str] = None) -> pd.DataFrame:
|
|
61
|
+
"""
|
|
62
|
+
Internal method to get SVW settings for a single company.
|
|
63
|
+
"""
|
|
64
|
+
params = {}
|
|
65
|
+
if year is not None:
|
|
66
|
+
params['year'] = year
|
|
67
|
+
if period is not None:
|
|
68
|
+
params['period'] = period
|
|
69
|
+
if created_from is not None:
|
|
70
|
+
params['createdFrom'] = created_from
|
|
71
|
+
|
|
72
|
+
request = requests.Request(
|
|
73
|
+
method='GET',
|
|
74
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/svwsettings",
|
|
75
|
+
params=params
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
79
|
+
df = pd.DataFrame(data)
|
|
80
|
+
|
|
81
|
+
if not df.empty:
|
|
82
|
+
df['companyId'] = company_id
|
|
83
|
+
|
|
84
|
+
return df
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class EmployeeSVWSettings:
|
|
88
|
+
"""
|
|
89
|
+
Class for handling employee-level SVW (Social Insurance) settings.
|
|
90
|
+
|
|
91
|
+
Endpoints:
|
|
92
|
+
- GET /api/companies/{companyId}/employees/svwsettings
|
|
93
|
+
- POST /api/employees/{employeeId}/SVWSettings
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, nmbrs):
|
|
97
|
+
self.nmbrs = nmbrs
|
|
98
|
+
|
|
99
|
+
def get(self,
|
|
100
|
+
company_id: Optional[str] = None,
|
|
101
|
+
employee_id: Optional[str] = None,
|
|
102
|
+
created_from: Optional[str] = None) -> tuple:
|
|
103
|
+
"""
|
|
104
|
+
Get SVW settings history for employees of a company.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
company_id: Optional company ID. If not provided, fetches for all companies.
|
|
108
|
+
employee_id: Optional employee ID filter
|
|
109
|
+
created_from: Optional datetime filter for records created after this date
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Tuple of (valid_data, invalid_data) DataFrames
|
|
113
|
+
"""
|
|
114
|
+
if company_id:
|
|
115
|
+
df = self._get(company_id, employee_id, created_from)
|
|
116
|
+
else:
|
|
117
|
+
df = pd.DataFrame()
|
|
118
|
+
for company in self.nmbrs.company_ids:
|
|
119
|
+
df = pd.concat([df, self._get(company, employee_id, created_from)])
|
|
120
|
+
|
|
121
|
+
if df.empty:
|
|
122
|
+
return df, pd.DataFrame()
|
|
123
|
+
|
|
124
|
+
valid_data, invalid_data = Functions.validate_data(df=df, schema=EmployeeSVWSettingsGet, debug=self.nmbrs.debug)
|
|
125
|
+
return valid_data, invalid_data
|
|
126
|
+
|
|
127
|
+
def _get(self,
|
|
128
|
+
company_id: str,
|
|
129
|
+
employee_id: Optional[str] = None,
|
|
130
|
+
created_from: Optional[str] = None) -> pd.DataFrame:
|
|
131
|
+
"""
|
|
132
|
+
Internal method to get employee SVW settings for a single company.
|
|
133
|
+
"""
|
|
134
|
+
params = {}
|
|
135
|
+
if employee_id is not None:
|
|
136
|
+
params['employeeId'] = employee_id
|
|
137
|
+
if created_from is not None:
|
|
138
|
+
params['createdFrom'] = created_from
|
|
139
|
+
|
|
140
|
+
request = requests.Request(
|
|
141
|
+
method='GET',
|
|
142
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/employees/svwsettings",
|
|
143
|
+
params=params
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
147
|
+
|
|
148
|
+
# Normalize nested data structure without meta key conflicts
|
|
149
|
+
records = []
|
|
150
|
+
for item in data:
|
|
151
|
+
parent_employee_id = item.get("employeeId")
|
|
152
|
+
for record in item.get("employeeSVWSettings", []) or []:
|
|
153
|
+
if "employeeId" not in record:
|
|
154
|
+
record["employeeId"] = parent_employee_id
|
|
155
|
+
records.append(record)
|
|
156
|
+
df = pd.json_normalize(records)
|
|
157
|
+
if 'hirerCAO' in df.columns:
|
|
158
|
+
hirer_cao_df = pd.json_normalize(df['hirerCAO']).add_prefix('hirerCAO.')
|
|
159
|
+
if not hirer_cao_df.empty:
|
|
160
|
+
hirer_cao_df = hirer_cao_df.reindex(df.index)
|
|
161
|
+
df = pd.concat([df.drop(columns=['hirerCAO']), hirer_cao_df], axis=1)
|
|
162
|
+
else:
|
|
163
|
+
df = df.drop(columns=['hirerCAO'])
|
|
164
|
+
|
|
165
|
+
if not df.empty:
|
|
166
|
+
df['companyId'] = company_id
|
|
167
|
+
|
|
168
|
+
return df
|
|
169
|
+
|
|
170
|
+
def create(self, employee_id: str, data: Dict[str, Any]):
|
|
171
|
+
"""
|
|
172
|
+
Create SVW settings for a specific employee.
|
|
173
|
+
|
|
174
|
+
A new SVW settings entry will be created in Nmbrs, starting from the
|
|
175
|
+
applied start period.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
employee_id: The ID of the employee
|
|
179
|
+
data: Dictionary containing SVW settings data matching SVWSettingsCreate schema:
|
|
180
|
+
- ZW: bool (optional) - Ziektewet
|
|
181
|
+
- WW: bool (optional) - Werkloosheidswet
|
|
182
|
+
- waoWia: bool (optional) - WAO/WIA
|
|
183
|
+
- natureOfEmployment: int (required) - Nature of employment code
|
|
184
|
+
- phaseClassification: int (optional) - Phase classification
|
|
185
|
+
- codeZvw: str (required) - Code ZVW (A, B, G, H)
|
|
186
|
+
- taxSequenceNumber: int (required)
|
|
187
|
+
- incomeRelatedContributionZvw: int (required)
|
|
188
|
+
- influenceObligedInsurance: int (required)
|
|
189
|
+
- wageCostBenefit: dict (optional)
|
|
190
|
+
- CAO: int (optional)
|
|
191
|
+
- hirerCAO: int (optional)
|
|
192
|
+
- period: dict (required) with year and period
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Response from the API or validated model in mock mode
|
|
196
|
+
"""
|
|
197
|
+
# Validate with Pydantic model
|
|
198
|
+
nested_data = self.nmbrs.flat_dict_to_nested_dict(data, SVWSettingsCreate)
|
|
199
|
+
svw_model = SVWSettingsCreate(**nested_data)
|
|
200
|
+
|
|
201
|
+
if self.nmbrs.mock_mode:
|
|
202
|
+
return svw_model
|
|
203
|
+
|
|
204
|
+
# Convert validated model to dict for API payload
|
|
205
|
+
payload = svw_model.model_dump(exclude_none=True, by_alias=True)
|
|
206
|
+
|
|
207
|
+
# Send request
|
|
208
|
+
resp = self.nmbrs.session.post(
|
|
209
|
+
url=f"{self.nmbrs.base_url}employees/{employee_id}/SVWSettings",
|
|
210
|
+
json=payload,
|
|
211
|
+
timeout=self.nmbrs.timeout
|
|
212
|
+
)
|
|
213
|
+
return resp
|
brynq_sdk_nmbrs/wage_tax.py
CHANGED
|
@@ -1,16 +1,128 @@
|
|
|
1
|
-
from typing import Any, Dict,
|
|
1
|
+
from typing import Any, Dict, Optional, Tuple
|
|
2
|
+
|
|
2
3
|
import pandas as pd
|
|
3
4
|
import requests
|
|
4
|
-
from .schemas.wage_tax import (
|
|
5
|
-
WageTaxGet, WageTaxUpdate, CompanyWageTaxGet,
|
|
6
|
-
WageTaxSettingsGet, WageTaxSettingsCreate
|
|
7
|
-
)
|
|
8
5
|
from zeep.exceptions import Fault
|
|
9
|
-
from zeep.ns import WSDL, SOAP_ENV_11
|
|
10
|
-
from zeep.xsd import ComplexType, Element, String
|
|
11
6
|
from zeep.helpers import serialize_object
|
|
7
|
+
|
|
12
8
|
from brynq_sdk_functions import Functions
|
|
13
9
|
|
|
10
|
+
from .schemas.wage_tax import (
|
|
11
|
+
CompanyWageTaxGet,
|
|
12
|
+
CompanyWageTaxGetSOAP,
|
|
13
|
+
WageTaxGet,
|
|
14
|
+
WageTaxSettingsCreate,
|
|
15
|
+
WageTaxSettingsGet,
|
|
16
|
+
WageTaxUpdate,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CompanyWageTax:
|
|
21
|
+
"""
|
|
22
|
+
Class for handling company-level wage tax data via REST API.
|
|
23
|
+
|
|
24
|
+
Endpoints:
|
|
25
|
+
- GET /api/companies/{companyId}/wagetaxes
|
|
26
|
+
- GET /api/companies/{companyId}/wagetaxes/{wageTaxId}/xml
|
|
27
|
+
- GET /api/companies/{companyId}/wagetaxes/{wageTaxId}/sepa
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, nmbrs):
|
|
31
|
+
self.nmbrs = nmbrs
|
|
32
|
+
|
|
33
|
+
def get(self,
|
|
34
|
+
company_id: Optional[str] = None,
|
|
35
|
+
year: Optional[int] = None) -> tuple:
|
|
36
|
+
"""
|
|
37
|
+
Get wage tax declarations for a company.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
company_id: Optional company ID. If not provided, fetches for all companies.
|
|
41
|
+
year: Optional year filter. If not provided, returns current year.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (valid_data, invalid_data) DataFrames
|
|
45
|
+
"""
|
|
46
|
+
if company_id:
|
|
47
|
+
df = self._get(company_id, year)
|
|
48
|
+
else:
|
|
49
|
+
df = pd.DataFrame()
|
|
50
|
+
for company in self.nmbrs.company_ids:
|
|
51
|
+
df = pd.concat([df, self._get(company, year)])
|
|
52
|
+
|
|
53
|
+
if df.empty:
|
|
54
|
+
return df, pd.DataFrame()
|
|
55
|
+
|
|
56
|
+
valid_data, invalid_data = Functions.validate_data(df=df, schema=CompanyWageTaxGet, debug=True)
|
|
57
|
+
return valid_data, invalid_data
|
|
58
|
+
|
|
59
|
+
def _get(self,
|
|
60
|
+
company_id: str,
|
|
61
|
+
year: Optional[int] = None) -> pd.DataFrame:
|
|
62
|
+
"""
|
|
63
|
+
Internal method to get wage tax declarations for a single company.
|
|
64
|
+
"""
|
|
65
|
+
params = {}
|
|
66
|
+
if year is not None:
|
|
67
|
+
params['year'] = year
|
|
68
|
+
|
|
69
|
+
request = requests.Request(
|
|
70
|
+
method='GET',
|
|
71
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/wagetaxes",
|
|
72
|
+
params=params
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
data = self.nmbrs.get_paginated_result(request)
|
|
76
|
+
df = pd.DataFrame(data)
|
|
77
|
+
|
|
78
|
+
if not df.empty:
|
|
79
|
+
df['companyId'] = company_id
|
|
80
|
+
|
|
81
|
+
return df
|
|
82
|
+
|
|
83
|
+
def get_xml(self, company_id: str, wage_tax_id: str):
|
|
84
|
+
"""
|
|
85
|
+
Get the XML file of a wage tax declaration.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
company_id: The ID of the company
|
|
89
|
+
wage_tax_id: The ID of the wage tax declaration
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Response containing the task ID for retrieving the XML file
|
|
93
|
+
"""
|
|
94
|
+
resp = self.nmbrs.session.get(
|
|
95
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/wagetaxes/{wage_tax_id}/xml",
|
|
96
|
+
timeout=self.nmbrs.timeout
|
|
97
|
+
)
|
|
98
|
+
resp.raise_for_status()
|
|
99
|
+
return resp.json()
|
|
100
|
+
|
|
101
|
+
def get_sepa(self, company_id: str, wage_tax_id: str, year: int, payment_date: Optional[str] = None):
|
|
102
|
+
"""
|
|
103
|
+
Get the SEPA (payment file) of a wage tax declaration.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
company_id: The ID of the company
|
|
107
|
+
wage_tax_id: The ID of the wage tax declaration
|
|
108
|
+
year: The year of the search (required)
|
|
109
|
+
payment_date: Optional payment date to be added to the payment file (ISO format)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Response containing the task ID for retrieving the SEPA file
|
|
113
|
+
"""
|
|
114
|
+
params = {'year': year}
|
|
115
|
+
if payment_date is not None:
|
|
116
|
+
params['paymentDate'] = payment_date
|
|
117
|
+
|
|
118
|
+
resp = self.nmbrs.session.get(
|
|
119
|
+
url=f"{self.nmbrs.base_url}companies/{company_id}/wagetaxes/{wage_tax_id}/sepa",
|
|
120
|
+
params=params,
|
|
121
|
+
timeout=self.nmbrs.timeout
|
|
122
|
+
)
|
|
123
|
+
resp.raise_for_status()
|
|
124
|
+
return resp.json()
|
|
125
|
+
|
|
14
126
|
|
|
15
127
|
class WageTaxSettings:
|
|
16
128
|
"""Wage Tax Settings History - uses REST API."""
|
|
@@ -134,7 +246,7 @@ class WageTax:
|
|
|
134
246
|
wagetax_settings_temp['companyId'] = company['number']
|
|
135
247
|
wagetax_settings = pd.concat([wagetax_settings, wagetax_settings_temp])
|
|
136
248
|
|
|
137
|
-
valid_wagetax_settings, invalid_wagetax_settings = Functions.validate_data(df=wagetax_settings, schema=
|
|
249
|
+
valid_wagetax_settings, invalid_wagetax_settings = Functions.validate_data(df=wagetax_settings, schema=CompanyWageTaxGetSOAP, debug=True)
|
|
138
250
|
|
|
139
251
|
# No validation schema for now, but could be added later
|
|
140
252
|
return valid_wagetax_settings, invalid_wagetax_settings
|
|
@@ -230,9 +342,6 @@ class WageTax:
|
|
|
230
342
|
if self.nmbrs.mock_mode:
|
|
231
343
|
return wage_tax_model
|
|
232
344
|
|
|
233
|
-
# Get the auth header using the centralized method
|
|
234
|
-
auth_header = self.nmbrs._get_soap_auth_header_employees()
|
|
235
|
-
|
|
236
345
|
# Use the model's built-in SOAP conversion method
|
|
237
346
|
wage_tax_settings = wage_tax_model.to_soap_settings(self.nmbrs.soap_client_employees)
|
|
238
347
|
|