Files

1033 lines
45 KiB
Python

from copy import deepcopy
import datetime
from odoo import fields
from odoo.tools.misc import format_date
from odoo.tools.float_utils import float_round
import re
import uuid
from collections import defaultdict
SALARY_TYPE_MAPPING = {
"AHV-AVS-Salaries": "AHV-AVS-Salary",
"FAK-CAF-Salaries": "FAK-CAF-Salary",
"UVG-LAA-Salaries": "UVG-LAA-Salary",
"UVGZ-LAAC-Salaries": "UVGZ-LAAC-Salary",
"KTG-AMC-Salaries": "KTG-AMC-Salary",
"BVG-LPP-Salaries": "BVG-LPP-Salary",
"TaxCrossborderSalaries": "TaxCrossborderSalary",
"TaxSalaries": "TaxSalary",
"TaxAtSourceSalaries": "TaxAtSourceSalary",
"StatisticSalaries": "StatisticSalary"
}
XSD_SKIP_VALUE = "XSDSKIP"
INSTITUTION_MODEL_MAPPING = {
"l10n.ch.social.insurance": "AHV-AVS",
"l10n.ch.compensation.fund": "FAK-CAF",
"l10n.ch.sickness.insurance": "KTG-AMC",
"l10n.ch.accident.insurance": "UVG-LAA",
"l10n.ch.additional.accident.insurance": "UVGZ-LAAC",
"l10n.ch.lpp.insurance": "BVG-LPP",
"l10n.ch.location.unit": "TaxCrossborder",
"l10n.ch.source.tax.institution": "TaxAtSource",
"BFS": "Statistic",
"Tax": "Tax"
}
MAPPED_DOMAIN_IDENTIFICATION = {
"AHV-AVS": "AHV-AVS-Identification",
"FAK-CAF": "FAK-CAF-Identification",
"UVG-LAA": "UVG-LAA-Identification",
"UVGZ-LAAC": "UVGZ-LAAC-Identification",
"KTG-AMC": "KTG-AMC-Identification",
"BVG-LPP": "BVG-LPP-Identification",
"Tax": "TaxIdentification",
"TaxAtSource": "TaxAtSourceIdentification",
"TaxCrossborder": "TaxCrossborderIdentification",
"Statistic": "StatisticIdentification"
}
IS_REASON_MAPPING = {
'entryCompany': ('Entry', 'entryCompany'),
'entryCanton': ('Entry', 'cantonChange'),
'entryOther': ('Entry', 'others'),
'withdrawalCompany': ('Withdrawal', 'withdrawalCompany'),
'withdrawalNat': ('Withdrawal', 'naturalization'),
'withdrawalSettled': ('Withdrawal', 'settled-C'),
'withdrawalCanton': ('Withdrawal', 'cantonChange'),
'withdrawalOther': ('Withdrawal', 'others'),
'civilstate': ('Mutation', 'civilstate'),
'partnerWork': ('Mutation', 'partnerWork'),
'partnerWorkplaceChangeCHAbroad': ('Mutation', 'partnerWorkplaceChangeCHAbroad'),
'residence': ('Mutation', 'residence'),
'childrenDeduction': ('Mutation', 'childrenDeduction'),
'churchTax': ('Mutation', 'churchTax'),
'others': ('Mutation', 'others')
}
IS_CODE_PATTERN = r"^[ABCDEFGHLNMPQRSTU][0-9][YN]$"
missing_value = "XSDMISSING"
XSD_YMONTH = "XSDGYEARMONTH"
class SwissdecInstitution:
def __init__(self, institution, pay_agreement=None, payroll_unit=None):
if institution:
assert isinstance(institution, str)
if pay_agreement:
assert isinstance(pay_agreement, str)
if payroll_unit:
assert isinstance(payroll_unit, str)
self._name = institution
self.pay_agreement = pay_agreement
self.payroll_unit = payroll_unit
class SwissdecDeclaration:
"""
Helper Class to Display and perform operations on Swissdec declare salary objects
documentation : https://www.swissdec.ch/fr/elm
"""
@staticmethod
def amount2str(amount):
return f"{amount:.2f}"
def create_ahv_avs_ema(self, institution_id, declaration_category, valid_as_of, month, year, acc_from, acc_until, ceo_rel=False):
ema = {
"institutionIDRef": self.get_institution_id_ref(institution_id),
"DeclarationCategory": {
declaration_category: {"ValidAsOf": valid_as_of}
},
"AccountingTime": {
"from": acc_from,
"until": acc_until,
},
"AHV-AVS-Income": "0.00",
"ALV-AC-Income": "0.00"
}
if ceo_rel:
ema["DegreeOfRelationship"] = ceo_rel
return ema
def create_ahv_avs_salary(self, institution_id, accounting_from, accounting_to, avs_salary, ac_salary, acc_salary, avs_open, ac_open, splits, ceo_rel=False):
if not (avs_salary or ac_salary or acc_salary or avs_open or ac_open):
return False
avs_salaries = {
"AHV-AVS-Income": self.amount2str(avs_salary),
"ALV-AC-Income": self.amount2str(ac_salary)
}
if acc_salary:
avs_salaries["ALVZ-ACS-Income"] = self.amount2str(acc_salary)
if avs_open:
avs_salaries["AHV-AVS-Open"] = self.amount2str(avs_open)
if ac_open:
avs_salaries["ALV-AC-Open"] = self.amount2str(ac_open)
if ceo_rel:
avs_salaries["DegreeOfRelationship"] = ceo_rel
if splits:
if splits.additional_delivery_date:
income_splits = {
"AdditionalDeliveryDate": format_date(splits.env, splits.additional_delivery_date, date_format='yyyy-MM-dd'),
}
else:
income_splits = {
"Splits": [{
"SplitCurrentYearIncome": self.amount2str(splits.income_to_split - sum(splits.avs_split_lines.mapped('income'))),
"SplitPreviousYear": {
"Period": {
"from": format_date(line.env, line.date_from, date_format='yyyy-MM-dd'),
"until": format_date(line.env, line.date_to, date_format='yyyy-MM-dd'),
},
"Income": self.amount2str(line.income)
}
} for line in splits.avs_split_lines]
}
avs_salaries["AHV-AVS-IncomeSplits"] = income_splits
return {
"institutionIDRef": self.get_institution_id_ref(institution=institution_id),
"AccountingTime": {
"from": accounting_from,
"until": accounting_to,
},
**avs_salaries
}
def create_fak_caf_ema(self, institution_id, canton, declaration_category, valid_as_of, reason, period_from, period_until):
return {
"institutionIDRef": self.get_institution_id_ref(institution_id),
"DeclarationCategory": {
declaration_category: {
"ValidAsOf": valid_as_of,
"Reason": reason
},
},
"FAK-CAF-Period": {
"from": period_from,
"until": period_until,
},
"FAK-CAF-ContributorySalary": "0.00",
"FAK-CAF-WorkplaceCanton": canton
}
def create_bvg_lpp_ema(self, institution_id, declarations, codes, bvg_lpp_annual_basis):
declaration_category = {}
if declarations.get("Entry"):
declaration_category["Entry"] = declarations.get("Entry")
if declarations.get("Withdrawal"):
declaration_category["Withdrawal"] = declarations.get("Withdrawal")
if declarations.get("Mutation"):
declaration_category["Mutation"] = declarations.get("Mutation")
ema = {
"institutionIDRef": self.get_institution_id_ref(institution_id),
"BVG-LPP-AnnualBasis": self.amount2str(bvg_lpp_annual_basis),
}
if declaration_category:
ema["DeclarationCategory"] = declaration_category
if codes:
ema["BVG-LPP-Code"] = codes[0]
return ema
@staticmethod
def source_tax_ema_to_dict(st_ema_objects):
declaration_category = {}
for ema in st_ema_objects:
ema_declaration_category, reason = IS_REASON_MAPPING[ema.reason]
declaration_category[ema_declaration_category] = declaration_category.get(
ema_declaration_category, []) + [{"Reason": reason, "ValidAsOf": format_date(ema.env, ema.valid_as_of,
date_format='yyyy-MM-dd')}]
if declaration_category:
return {
"DeclarationCategory": declaration_category
}
else:
return {}
@staticmethod
def get_salary_tag_counter(staff):
counters = {
("NumberOf-AHV-AVS-Salary-Tags", "AHV-AVS-Salaries"): 0,
("NumberOf-FAK-CAF-Salary-Tags", "FAK-CAF-Salaries"): 0,
("NumberOf-UVG-LAA-Salary-Tags", "UVG-LAA-Salaries"): 0,
("NumberOf-UVGZ-LAAC-Salary-Tags", "UVGZ-LAAC-Salaries"): 0,
("NumberOf-BVG-LPP-Salary-Tags", "BVG-LPP-Salaries"): 0,
("NumberOf-KTG-AMC-Salary-Tags", "KTG-AMC-Salaries"): 0,
("NumberOf-TaxSalary-Tags", "TaxSalaries"): 0,
("NumberOf-TaxCrossborderSalary-Tags", "TaxCrossborderSalaries"): 0,
("NumberOf-TaxAtSourceSalary-Tags", "TaxAtSourceSalaries"): 0,
("NumberOf-StatisticSalary-Tags", "StatisticSalaries"): 0
}
for person in staff["Staff"]["Person"]:
for tag in counters:
if tag[1] in person:
counters[tag] += len(person[tag[1]][SALARY_TYPE_MAPPING[tag[1]]])
return {
tag[0]: counters[tag] for tag in counters if counters[tag] > 0
}
@staticmethod
def _reverse_is_code_type(is_code):
if is_code:
if is_code in ['NON', 'NOY', 'HEN', 'HEY', 'MEN', 'MEY', 'SFN']:
return "CategoryPredefined"
elif re.match(IS_CODE_PATTERN, is_code):
return "TaxAtSourceCode"
else:
return "CategoryOpen"
else:
return "CategoryPredefined"
@staticmethod
def _round_to_5_cents(total):
return SwissdecDeclaration.amount2str(float_round(total, precision_rounding=0.05, rounding_method="HALF-UP"))
@staticmethod
def get_salary_totals(staff, **kwargs):
def convert_floats_to_str(data):
for key, value in data.items():
if key != "CurrentMonth" and key != "Month":
if isinstance(value, float):
data[key] = SwissdecDeclaration.amount2str(value)
elif isinstance(value, dict):
convert_floats_to_str(value)
elif isinstance(value, list):
for item in value:
if isinstance(item, dict):
convert_floats_to_str(item)
else:
data[key] = value
salary_totals = dict()
avs_totals = defaultdict(lambda: {
"Total-AHV-AVS-Incomes": 0,
"Total-AHV-AVS-Open": 0,
"Total-ALV-AC-Incomes": 0,
"Total-ALVZ-ACS-Incomes": 0,
"Total-ALV-AC-Open": 0,
})
# Institution - Canton - Totals
fak_totals = defaultdict(lambda: defaultdict(lambda: {
"Total-FAK-CAF-ContributorySalary": 0,
"Total-FAK-CAF-FamilyIncomeSupplement": 0,
}))
# Institution - Branch - Sex - Code
uvg_totals = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(float))))
uvg_gender_totals = defaultdict(lambda: {
"F": set(),
"M": set()
})
# Institution - Code - Sex - Total
uvgz_totals = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))
# Institution - Code - Sex - Total
ijm_totals = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))
# QST
source_tax_totals = defaultdict(lambda: {
"TotalTaxableEarning": "0.00",
"TotalTaxAtSource": "0.00",
"TotalCommission": "0.00",
"CurrentMonth": kwargs.get("CurrentMonth", False)
})
# QST - month
source_tax_correction_totals = defaultdict(lambda: defaultdict(lambda: {
"TotalTaxableEarning": "0.00",
"TotalTaxAtSource": "0.00",
"TotalCommission": "0.00",
}))
source_tax_commission_rates = kwargs.get("source_tax_commission_rates", defaultdict(float))
# Txb totals: QST - Taxable earning
txb_totals = defaultdict(defaultdict)
for person in staff:
particular = person.get("Particulars", {})
sex = particular["Sex"]
for avs_salary in person.get("AHV-AVS-Salaries", {}).get("AHV-AVS-Salary", []):
avs_totals[avs_salary["institutionIDRef"]]["Total-AHV-AVS-Incomes"] += float(avs_salary.get("AHV-AVS-Income", 0))
avs_totals[avs_salary["institutionIDRef"]]["Total-AHV-AVS-Open"] += float(avs_salary.get("AHV-AVS-Open", 0))
avs_totals[avs_salary["institutionIDRef"]]["Total-ALV-AC-Incomes"] += float(avs_salary.get("ALV-AC-Income", 0))
avs_totals[avs_salary["institutionIDRef"]]["Total-ALVZ-ACS-Incomes"] += float(avs_salary.get("ALVZ-ACS-Income", 0))
avs_totals[avs_salary["institutionIDRef"]]["Total-ALV-AC-Open"] += float(avs_salary.get("ALV-AC-Open", 0))
for caf_salary in person.get("FAK-CAF-Salaries", {}).get("FAK-CAF-Salary", []):
fak_totals[caf_salary["institutionIDRef"]][caf_salary["FAK-CAF-WorkplaceCanton"]]["Total-FAK-CAF-ContributorySalary"] += float(caf_salary.get("FAK-CAF-ContributorySalary", 0))
fak_totals[caf_salary["institutionIDRef"]][caf_salary["FAK-CAF-WorkplaceCanton"]]["Total-FAK-CAF-FamilyIncomeSupplement"] += float(caf_salary.get("FAK-CAF-FamilyIncomeSupplement", {}).get("FAK-CAF-FamilyIncomeSupplementPerPerson", 0))
for uvg_salary in person.get("UVG-LAA-Salaries", {}).get("UVG-LAA-Salary", []):
if kwargs.get("uvg_month", False) >= 9:
current_valid_from = uvg_salary.get("AccountingTime", {}).get("from", False)
current_valid_until = uvg_salary.get("AccountingTime", {}).get("until", False)
if current_valid_from and current_valid_until:
valid_from_date_time = fields.Date.from_string(current_valid_from)
valid_until_date_time = fields.Date.from_string(current_valid_until)
current_withdrawal = person.get("Work", {}).get("WithdrawalDate", False)
current_withdrawal_date_time = fields.Date.from_string(current_withdrawal) if current_withdrawal else False
end_of_september = datetime.date(kwargs.get("uvg_year", valid_from_date_time.year), 9, 30)
if valid_from_date_time <= end_of_september and valid_until_date_time >= end_of_september and (not current_withdrawal_date_time or current_withdrawal_date_time > end_of_september):
uvg_gender_totals[uvg_salary["institutionIDRef"]][sex].add(person["Particulars"]["EmployeeNumber"])
uvg_totals[uvg_salary["institutionIDRef"]][uvg_salary["UVG-LAA-Code"][0]][sex][uvg_salary["UVG-LAA-Code"][1]] += float(uvg_salary.get("UVG-LAA-ContributorySalary", 0))
for uvgz_salary in person.get("UVGZ-LAAC-Salaries", {}).get("UVGZ-LAAC-Salary", []):
uvgz_totals[uvgz_salary["institutionIDRef"]][uvgz_salary["UVGZ-LAAC-Code"]][sex] += float(uvgz_salary.get("UVGZ-LAAC-ContributorySalary", 0))
for ijm_salary in person.get("KTG-AMC-Salaries", {}).get("KTG-AMC-Salary", []):
ijm_totals[ijm_salary["institutionIDRef"]][ijm_salary["KTG-AMC-Code"]][sex] += float(ijm_salary.get("KTG-AMC-ContributorySalary", 0))
for qst_salary in person.get("TaxAtSourceSalaries", {}).get("TaxAtSourceSalary", []):
qst_id_ref = qst_salary["institutionIDRef"]
current_taxable_earning = float(source_tax_totals[qst_id_ref]["TotalTaxableEarning"])
current_tax_at_souce = float(source_tax_totals[qst_id_ref]["TotalTaxAtSource"])
source_tax_totals[qst_id_ref]["TotalTaxableEarning"] = SwissdecDeclaration.amount2str(current_taxable_earning + float(qst_salary.get("Current", {}).get("TaxableEarning", 0)))
source_tax_totals[qst_id_ref]["TotalTaxAtSource"] = SwissdecDeclaration.amount2str(current_tax_at_souce + float(qst_salary.get("Current", {}).get("TaxAtSource", 0)))
for correction in qst_salary.get("Correction", []):
month = tuple(correction["Month"])
new = correction.get("New", dict())
old = correction.get("Old", dict())
for key, value in new.items():
total_key = f"Total{key}"
if total_key in source_tax_correction_totals[qst_id_ref][month]:
source_tax_correction_totals[qst_id_ref][month][total_key] = SwissdecDeclaration.amount2str(float(source_tax_correction_totals[qst_id_ref][month][total_key]) + float(value))
for key, value in old.items():
total_key = f"Total{key}"
if total_key in source_tax_correction_totals[qst_id_ref][month]:
source_tax_correction_totals[qst_id_ref][month][total_key] = SwissdecDeclaration.amount2str(float(source_tax_correction_totals[qst_id_ref][month][total_key]) + float(value))
for correction in qst_salary.get("CorrectionConfirmed", []):
month = tuple(correction["Month"])
for key, value in correction.items():
if key != "Month":
total_key = f"Total{key}"
if total_key in source_tax_correction_totals[qst_id_ref][month]:
source_tax_correction_totals[qst_id_ref][month][total_key] = SwissdecDeclaration.amount2str(float(source_tax_correction_totals[qst_id_ref][month][total_key]) + float(value))
for qst_institution in source_tax_totals:
source_tax_totals[qst_institution]['TotalCommission'] = SwissdecDeclaration._round_to_5_cents(total=float(source_tax_totals[qst_institution]['TotalTaxAtSource']) * source_tax_commission_rates[qst_institution])
for qst_institution in source_tax_correction_totals:
for month in source_tax_correction_totals[qst_institution]:
source_tax_correction_totals[qst_institution][month]['TotalCommission'] = SwissdecDeclaration._round_to_5_cents(total=float(source_tax_correction_totals[qst_institution][month]['TotalTaxAtSource']) * source_tax_commission_rates[qst_institution])
global_txb_period = kwargs.get("global_txb_periods", {})
for txb_salary in person.get("TaxCrossborderSalaries", {}).get("TaxCrossborderSalary", []):
existing_txb = txb_totals[txb_salary["institutionIDRef"]]
if existing_txb:
current_from = global_txb_period[int(existing_txb["Period"]["from"].split("-")[0])]["from"]
current_until = global_txb_period[int(existing_txb["Period"]["until"].split("-")[0])]["until"]
if txb_salary["Period"]["from"] > current_from:
txb_totals[txb_salary["institutionIDRef"]]["Period"]["from"] = current_from
if txb_salary["Period"]["until"] < current_until:
txb_totals[txb_salary["institutionIDRef"]]["Period"]["until"] = current_until
else:
txb_totals[txb_salary["institutionIDRef"]]["Period"] = {
"from": global_txb_period[int(txb_salary["Period"]["from"].split('-')[0])]["from"],
"until": global_txb_period[int(txb_salary["Period"]["from"].split('-')[0])]["until"]
}
txb_totals[txb_salary["institutionIDRef"]]["TaxableEarning"] = "0.00"
txb_totals[txb_salary["institutionIDRef"]]["TaxAtSourceCanton"] = txb_salary["TaxAtSourceCanton"]
txb_totals[txb_salary["institutionIDRef"]]["TaxableEarning"] = SwissdecDeclaration.amount2str(float(txb_salary.get("TaxableEarning", "0.00")) + float(txb_totals[txb_salary["institutionIDRef"]]["TaxableEarning"]))
qst_totals = []
for institution in source_tax_totals:
qst_total = {
"TotalMonth": source_tax_totals[institution],
"institutionIDRef": institution
}
if source_tax_correction_totals[institution]:
qst_total["CorrectionMonth"] = sorted([{"Month": corr_month, **source_tax_correction_totals[institution][corr_month]} for corr_month in source_tax_correction_totals[institution]], key=lambda corr_d: (corr_d["Month"][1], corr_d["Month"][2]))
qst_totals.append(qst_total)
txb_totals_l = [{"institutionIDRef": institution, **txb_totals[institution]} for institution in txb_totals]
ahv_total = [{
**{key: SwissdecDeclaration.amount2str(avs_totals[institution][key]) for key in avs_totals[institution]},
"institutionIDRef": institution
} for institution in avs_totals]
fak_caf_totals = [
{
"institutionIDRef": institution,
"Total-FAK-CAF-PerCanton": [
{
"Canton": canton,
**{
key: SwissdecDeclaration.amount2str(fak_totals[institution][canton].get(key, 0))
for key in fak_totals[institution][canton]
},
} for canton in fak_totals[institution]
]
}
for institution in fak_totals
]
if ahv_total:
salary_totals["AHV-AVS-Totals"] = ahv_total
if fak_caf_totals:
salary_totals["FAK-CAF-Totals"] = fak_caf_totals
uvg_laa_totals = []
for institution in uvg_totals:
result = {
"institutionIDRef": institution,
"UVG-LAA-BranchTotals": {
"UVG-LAA-BranchTotal": []
},
"UVG-LAA-MasterTotal": 0.0,
"NumberOfFemalePersons": 0,
"NumberOfMalePersons": 0
}
master_total = 0.0
female_count = 0
male_count = 0
# Iterate over branches and gender totals in `uvg_totals`
for branch, genders in uvg_totals[institution].items():
branch_total = {
"BranchIdentifier": branch,
"Female-Totals": {"NBU-BU-ANP-AP-Total": 0.0, "BU-AP-Total": 0.0},
"Male-Totals": {"NBU-BU-ANP-AP-Total": 0.0, "BU-AP-Total": 0.0}
}
for gender, codes in genders.items():
gender_totals = {"NBU-BU-ANP-AP-Total": 0.0, "BU-AP-Total": 0.0}
for code, amount in codes.items():
if code in ["1", "2"]: # Map to NBU-BU-ANP-AP-Total
gender_totals["NBU-BU-ANP-AP-Total"] += amount
elif code == "3": # Map to BU-AP-Total
gender_totals["BU-AP-Total"] += amount
# Add gender totals to branch totals
if gender == "F":
branch_total["Female-Totals"] = gender_totals
female_count += 1
elif gender == "M":
branch_total["Male-Totals"] = gender_totals
male_count += 1
# Accumulate to master total
master_total += sum(gender_totals.values())
# Append the branch total to the branch totals list
result["UVG-LAA-BranchTotals"]["UVG-LAA-BranchTotal"].append(branch_total)
# Populate master total and counts
result["UVG-LAA-MasterTotal"] = master_total
result["NumberOfFemalePersons"] = len(uvg_gender_totals[institution]["F"])
result["NumberOfMalePersons"] = len(uvg_gender_totals[institution]["M"])
convert_floats_to_str(result)
uvg_laa_totals.append(deepcopy(result))
uvgz_laac_totals = []
for institution in uvgz_totals:
result = {
"institutionIDRef": institution,
"UVGZ-LAAC-CategoryTotals": {
"UVGZ-LAAC-CategoryTotal": []
},
"UVGZ-LAAC-MasterTotal": 0.0
}
# Variable to accumulate the master total
master_total = 0.0
# Iterate over categories and gender totals in `uvgz_totals`
for category_code, genders in uvgz_totals[institution].items():
# Initialize totals for the current category
category_total = {
"CategoryCode": category_code,
"Female-Total": 0.0,
"Male-Total": 0.0
}
# Calculate totals for each gender
for gender, total in genders.items():
if gender == "F":
category_total["Female-Total"] += total
elif gender == "M":
category_total["Male-Total"] += total
# Add category totals to the master total
master_total += category_total["Female-Total"] + category_total["Male-Total"]
# Append category total to the category totals list
result["UVGZ-LAAC-CategoryTotals"]["UVGZ-LAAC-CategoryTotal"].append(category_total)
# Set the master total in the result
result["UVGZ-LAAC-MasterTotal"] = master_total
convert_floats_to_str(result)
uvgz_laac_totals.append(deepcopy(result))
ktg_amc_totals = []
for institution in ijm_totals:
result = {
"institutionIDRef": institution,
"KTG-AMC-CategoryTotals": {
"KTG-AMC-CategoryTotal": []
},
"KTG-AMC-MasterTotal": 0.0
}
# Variable to accumulate the master total
master_total = 0.0
# Iterate over categories and gender totals in `uvgz_totals`
for category_code, genders in ijm_totals[institution].items():
# Initialize totals for the current category
category_total = {
"CategoryCode": category_code,
"Female-Total": 0.0,
"Male-Total": 0.0
}
# Calculate totals for each gender
for gender, total in genders.items():
if gender == "F":
category_total["Female-Total"] += total
elif gender == "M":
category_total["Male-Total"] += total
# Add category totals to the master total
master_total += category_total["Female-Total"] + category_total["Male-Total"]
# Append category total to the category totals list
result["KTG-AMC-CategoryTotals"]["KTG-AMC-CategoryTotal"].append(category_total)
# Set the master total in the result
result["KTG-AMC-MasterTotal"] = master_total
convert_floats_to_str(result)
ktg_amc_totals.append(deepcopy(result))
if uvg_laa_totals:
salary_totals["UVG-LAA-Totals"] = uvg_laa_totals
if uvgz_laac_totals:
salary_totals["UVGZ-LAAC-Totals"] = uvgz_laac_totals
if ktg_amc_totals:
salary_totals["KTG-AMC-Totals"] = ktg_amc_totals
if qst_totals:
salary_totals["TaxAtSourceTotals"] = qst_totals
if txb_totals_l:
salary_totals["TaxCrossborderTotals"] = txb_totals_l
return salary_totals
def get_instution_description(self, institution, **kwargs):
description = {}
if INSTITUTION_MODEL_MAPPING[institution._name] == "AHV-AVS":
description = {
"AK-CC-BranchNumber": institution.insurance_code,
"AK-CC-CustomerNumber": institution.member_number,
}
if not kwargs.get('skip_id_ref'):
if institution.laa_insurance_id:
uvg_laa = {
"Name": institution.laa_insurance_id.name,
"ValidAsOf": format_date(institution.env, institution.laa_insurance_from,
date_format='yyyy-MM-dd')
}
if institution.laa_insurance_id.uid_bfs_number:
uvg_laa["UID-BFS"] = {
"UID": institution.laa_insurance_id.uid_bfs_number
}
else:
uvg_laa["UID-BFS"] = {
"Unknown": XSD_SKIP_VALUE
}
else:
uvg_laa = {
"NoneWithReason": institution.no_laa_reason
}
if institution.lpp_insurance_id:
bvg_lpp = {
"Name": institution.lpp_insurance_id.name,
"ValidAsOf": format_date(institution.env, institution.lpp_insurance_from,
date_format='yyyy-MM-dd')
}
if institution.lpp_insurance_id.uid_bfs:
bvg_lpp["UID-BFS"] = {
"UID": institution.lpp_insurance_id.uid_bfs
}
else:
bvg_lpp["UID-BFS"] = {
"Unknown": XSD_SKIP_VALUE
}
else:
bvg_lpp = {
"NoneWithReason": institution.no_lpp_reason
}
description.update({
"UVG-LAA-Insurance": uvg_laa,
"BVG-LPP-Insurance": bvg_lpp
})
if institution.member_subnumber:
description["AK-CC-SubNumber"] = institution.member_subnumber
elif INSTITUTION_MODEL_MAPPING[institution._name] == "FAK-CAF":
description = {
"FAK-CAF-BranchNumber": institution.insurance_code,
"FAK-CAF-CustomerNumber": institution.member_number,
}
if institution.member_subnumber:
description["FAK-CAF-SubNumber"] = institution.member_subnumber
elif INSTITUTION_MODEL_MAPPING[institution._name] == "BVG-LPP":
description = {
"InsuranceID": institution.insurance_code,
"InsuranceCompanyName": institution.name,
"CustomerIdentity": institution.customer_number,
"ContractIdentity": institution.contract_number,
}
if not kwargs.get('skip_id_ref'):
description["GeneralValidAsOf"] = kwargs.get("general_validasof", missing_value)
if institution.fund_number and not kwargs.get('skip_bvg_payroll_unit'):
description["PayrollUnit"] = institution.fund_number
elif INSTITUTION_MODEL_MAPPING[institution._name] in ["UVG-LAA", "KTG-AMC", "UVGZ-LAAC"]:
description = {
"InsuranceID": institution.insurance_code,
"InsuranceCompanyName": institution.name,
"CustomerIdentity": institution.customer_number,
"ContractIdentity": institution.contract_number,
}
if kwargs.get("incomplete_declaration"):
description['DeclarationIncomplete'] = [XSD_SKIP_VALUE]
elif INSTITUTION_MODEL_MAPPING[institution._name] == "TaxCrossborder":
description = {
"CantonID": institution.canton,
"CustomerIdentity": institution.dpi_number,
}
"""
todo: still
if institution.company_number:
txb["PayrollUnit"] = institution.company_number
return txb
"""
elif INSTITUTION_MODEL_MAPPING[institution._name] == "TaxAtSource":
description = {
"CantonID": institution.canton,
"CustomerIdentity": institution.dpi_number,
}
if institution.company_number:
description["PayrollUnit"] = institution.company_number
elif INSTITUTION_MODEL_MAPPING[institution._name] == "Statistic":
description = {
"PayAgreement": institution.pay_agreement
}
if institution.payroll_unit:
description["PayrollUnit"] = institution.payroll_unit
if not kwargs.get('skip_id_ref'):
description["institutionID"] = self.get_institution_id_ref(institution)
return description
def get_workplace_id_ref(self, workplace):
workplace_id_ref = f"{workplace.bur_ree_number or workplace.in_house_id or workplace.id}"
return f"#W_{workplace_id_ref}"
def get_workplace_working_hours(self, workplace):
workplace_id_ref = self.get_workplace_id_ref(workplace)
if workplace.weekly_hours > 0 and workplace.weekly_lessons == 0:
return {
"WeeklyHours": {
"_value_1": self.amount2str(workplace.weekly_hours),
"companyWeeklyHoursID": f"#WT_{workplace_id_ref}"
}
}
elif workplace.weekly_hours == 0 and workplace.weekly_lessons > 0:
return {
"WeeklyLessons": {
"_value_1": self.amount2str(workplace.weekly_lessons),
"companyWeeklyLessonsID": f"#WT_{workplace_id_ref}"
}
}
elif workplace.weekly_hours > 0 and workplace.weekly_lessons > 0:
return {
"WeeklyHoursAndLessons": {
"WeeklyHours": self.amount2str(workplace.weekly_hours),
"WeeklyLessons": self.amount2str(workplace.weekly_lessons),
"companyWeeklyHoursAndLessonsID": f"#WT_{workplace_id_ref}"
},
}
else:
return missing_value
def get_institution_id_ref(self, institution):
if institution:
if INSTITUTION_MODEL_MAPPING[institution._name] not in ["FAK-CAF", "TaxCrossborder", "TaxAtSource", "Tax", "Statistic"]:
return f"#{INSTITUTION_MODEL_MAPPING.get(institution._name)}_{institution.insurance_code}"
elif INSTITUTION_MODEL_MAPPING[institution._name] == "FAK-CAF":
return f"#{INSTITUTION_MODEL_MAPPING.get(institution._name)}_{institution.insurance_code}_{institution.member_number}"
elif INSTITUTION_MODEL_MAPPING[institution._name] == "TaxCrossborder":
return f"#{INSTITUTION_MODEL_MAPPING.get(institution._name)}_{institution.canton}"
elif INSTITUTION_MODEL_MAPPING[institution._name] == "TaxAtSource":
return f"#QST_{institution.canton}"
elif INSTITUTION_MODEL_MAPPING[institution._name] in ["Tax", "Statistic"]:
return f"#{institution._name}"
else:
return False
def get_institution_job(self, institution):
return {
"institutionIDRef": self.get_institution_id_ref(institution),
"ProcessByDistributor": "true"
}
def get_institutions(self, institutions, **kwargs):
institution_description = {}
for institution in institutions:
institution_type = INSTITUTION_MODEL_MAPPING.get(institution._name, False)
if institution._name == "l10n.ch.source.tax.institution":
if kwargs.get("txb"):
institution_type = "TaxCrossborder"
if institution_type and institution_type not in ["Tax", "Statistic"]:
if institution_type in institution_description:
institution_description[institution_type].append(self.get_instution_description(institution, **kwargs))
else:
institution_description[institution_type] = [self.get_instution_description(institution, **kwargs)]
elif institution_type == "Statistic":
institution_description[institution_type] = self.get_instution_description(institution, **kwargs)
return {
"Institutions": institution_description
}
def get_mapped_institution_descriptions(self, institutions, **kwargs):
institution_description = dict()
for institution in institutions:
id_ref = self.get_institution_id_ref(institution)
institution_description[id_ref] = self.get_instution_description(institution, skip_id_ref=True, **kwargs)
return institution_description
def get_job(self, institutions, staff_institutions):
job = {}
to_process = [self.get_institution_id_ref(i) for i in institutions]
for domain, i_true in staff_institutions.items():
if domain != "Statistic":
job[domain] = []
for id_ref in i_true:
job[domain].append({
"institutionIDRef": id_ref,
"ProcessByDistributor": id_ref in to_process
})
else:
job["Statistic"] = {
"institutionIDRef": "#BFS",
"ProcessByDistributor": "#BFS" in to_process
}
if "#Tax" in to_process:
job["Tax"] = {
"ProcessByDistributor": True
}
return {
"Addressees": [job]
}
def get_company_description(self, company_id):
company_description = {
"Name": {
"HR-RC-Name": company_id.name
}
}
if company_id.l10n_ch_uid:
company_description["UID-BFS"] = {
"UID": company_id.l10n_ch_uid
}
else:
company_description["UID-BFS"] = {
"Unknown": XSD_SKIP_VALUE
}
address = {
"ZIP-Code": company_id.zip or missing_value,
"City": company_id.city or missing_value
}
if company_id.street:
address["Street"] = company_id.street
if company_id.street2:
address["ComplementaryLine"]: company_id.street2
if company_id.l10n_ch_post_box:
address["Postbox"] = company_id.l10n_ch_post_box
if company_id.country_id:
address["Country"] = company_id.country_id.name.upper()
workplaces = []
for workplace in company_id.l10n_ch_work_location_ids:
workplace_description = dict()
workplace_id_ref = self.get_workplace_id_ref(workplace)
workplace_description["workplaceID"] = workplace_id_ref
if workplace.bur_ree_number:
workplace_description["BUR-REE-Number"] = workplace.bur_ree_number
if workplace.in_house_id and not workplace.bur_ree_number:
workplace_description["InHouseID"] = workplace.in_house_id
address_extended = {
"ComplementaryLine": workplace.partner_id.name,
"ZIP-Code": workplace.partner_id.zip or missing_value,
"City": workplace.partner_id.city or missing_value
}
if workplace.partner_id.street:
address_extended["Street"] = workplace.partner_id.street
if workplace.partner_id.country_id:
address_extended["Country"] = workplace.partner_id.country_id.name.upper()
if workplace.canton:
address_extended["Canton"] = workplace.canton
if workplace.municipality:
address_extended["MunicipalityID"] = workplace.municipality
workplaces.append({
**workplace_description,
"AddressExtended": address_extended,
"CompanyWorkingTime": self.get_workplace_working_hours(workplace)
})
if company_id.l10n_ch_uses_delegate:
delegate_desc = {
"Name": {
"HR-RC-Name": company_id.l10n_ch_swissdec_delegate_name or missing_value,
},
"Address": {
"ZIP-Code": company_id.l10n_ch_delegate_zip or missing_value,
"City": company_id.l10n_ch_delegate_city or missing_value
},
}
if company_id.l10n_ch_swissdec_delegate_ch_uid:
delegate_desc["UID-BFS"] = company_id.l10n_ch_swissdec_delegate_ch_uid
if company_id.l10n_ch_delegate_street:
delegate_desc["Address"]["Street"] = company_id.l10n_ch_delegate_street
if company_id.l10n_ch_delegate_street2:
delegate_desc["Address"]["ComplementaryLine"] = company_id.l10n_ch_delegate_street2
if company_id.l10n_ch_delegate_Po_Box:
delegate_desc["Address"]["Postbox"] = company_id.l10n_ch_delegate_Po_Box
if company_id.l10n_ch_delegate_country_id:
delegate_desc["Address"]["Country"] = company_id.l10n_ch_delegate_country_id.name.upper()
company_description["Delegate"] = delegate_desc
return {
"CompanyDescription": {
**company_description,
"Address": address,
"Workplace": workplaces
}
}
def _get_contact_person(self, company_id):
contact_person = dict()
if company_id.l10n_ch_contact_person_name and company_id.l10n_ch_contact_person_email and company_id.l10n_ch_contact_person_phone:
contact_person["Name"] = company_id.l10n_ch_contact_person_name
contact_person["EmailAddress"] = company_id.l10n_ch_contact_person_email
contact_person["PhoneNumber"] = company_id.l10n_ch_contact_person_phone
return {
"ContactPerson": contact_person
}
else:
return {}
def create_declare_salary(self, institutions_to_process, company_id, staff, declaration_year, test_case=False, substitution_declaration_id=None, **kwargs):
staff_institutions = {}
if staff:
for domain, institutions in staff.get("Institutions", {}).items():
if isinstance(institutions, list):
staff_institutions[domain] = []
for institution in institutions:
staff_institutions[domain].append(institution.get("institutionID"))
else:
staff_institutions[domain] = [institutions.get("institutionID")]
job = {
"Job": self.get_job(institutions_to_process, staff_institutions)
}
salary_declaration = {"SalaryDeclaration": {
"schemaVersion": "3.0",
"Company": {
**staff
},
"GeneralSalaryDeclarationDescription": {
"CreationDate": datetime.datetime.utcnow().isoformat() + 'Z',
"AccountingPeriod": [XSD_YMONTH, declaration_year, False],
**self._get_contact_person(company_id)
},
}}
if substitution_declaration_id:
declaration = {
**job,
"Substitution": {
"PredecessorDeclarationIDWithAcceptedState": substitution_declaration_id
},
**salary_declaration
}
else:
declaration = {
**job,
**salary_declaration
}
return declaration
else:
return {}
def create_get_status_from_declare_salary(self, job_key):
return {
"JobKey": job_key
}
def create_get_result_from_declare_salary(self, domain, key, password, institution_description):
domain_data = {
MAPPED_DOMAIN_IDENTIFICATION[domain]: {
"Key": key,
"Password": password,
}
}
if institution_description:
domain_data[MAPPED_DOMAIN_IDENTIFICATION[domain]]["Institution"] = institution_description
return {
"Domain": domain_data
}
def create_reply_dialog(self, domain, key, password, institution_description, dialog):
domain_data = {
MAPPED_DOMAIN_IDENTIFICATION[domain]: {
"Key": key,
"Password": password,
}
}
if institution_description:
domain_data[MAPPED_DOMAIN_IDENTIFICATION[domain]]["Institution"] = institution_description
return {
"Domain": domain_data,
"DialogMessages": {
"DialogMessage": [dialog]
}
}
def create_poll_dialog(self, domain, key, password, institution_description, dialog_story_id):
polling_dialog = {
"Creation": fields.Datetime.now().isoformat(),
"StoryID": str(uuid.uuid4().hex),
"StandardDialogID": "0000.0002.0001-001",
"Title": "Polling DialogMessage ELM",
"Description": "Polling of a DialogMessage in ProcessingState",
"Paragraph": [
{
"ID": "100",
"Label": "StoryID of DialogMessage to poll",
"Value": {
"String": dialog_story_id
}
}
]
}
domain_data = {
MAPPED_DOMAIN_IDENTIFICATION[domain]: {
"Key": key,
"Password": password,
}
}
if institution_description:
domain_data[MAPPED_DOMAIN_IDENTIFICATION[domain]]["Institution"] = institution_description
return {
"Domain": domain_data,
"DialogMessages": {
"DialogMessage": [polling_dialog]
}
}