212 lines
12 KiB
Python
212 lines
12 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
from ..api.swissdec_declarations import SwissdecDeclaration
|
|
from .hr_version import CANTONS
|
|
|
|
import json
|
|
import base64
|
|
import re
|
|
|
|
XSD_SKIP_VALUE = "XSDSKIP"
|
|
|
|
|
|
class L10nCHSwissdecDeclaration(models.Model):
|
|
_name = 'l10n.ch.swissdec.declaration'
|
|
_inherit = 'mail.thread'
|
|
_description = 'Swissdec Declaration'
|
|
_order = "transmission_date desc"
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
if self.env.company.country_id.code != "CH":
|
|
raise UserError(_('You must be logged in a Swiss company to use this feature'))
|
|
return super().default_get(fields)
|
|
|
|
res_model = fields.Char(
|
|
'Declaration Model Name', required=True, index=True)
|
|
res_id = fields.Many2oneReference(
|
|
'Declaration Model Id', index=True, model_field='res_model', required=True)
|
|
name = fields.Char(required=True, readonly=True)
|
|
state = fields.Selection(selection=[("waiting", "Waiting for Status"),
|
|
("plausibility", "Verifying Plausibility"),
|
|
("processing", "Processing"),
|
|
("finished", "Finished")], default="waiting", required=True)
|
|
year = fields.Integer(readonly=True)
|
|
month = fields.Char(readonly=True)
|
|
transmission_date = fields.Datetime(required=True, readonly=True)
|
|
test_transmission = fields.Boolean(string="Test Transmission")
|
|
swissdec_declaration_id = fields.Char(string="Declaration ID", required=True, readonly=True)
|
|
job_key = fields.Char(required=True, readonly=True)
|
|
general_warnings = fields.Json()
|
|
l10n_ch_swissdec_job_result_ids = fields.One2many('l10n.ch.swissdec.job.result', 'declaration_id', string="Results")
|
|
|
|
def _process_responses(self, vals):
|
|
self.ensure_one()
|
|
current_running_jobs = self.l10n_ch_swissdec_job_result_ids.grouped('institution_id_ref')
|
|
lines_to_create = []
|
|
for institution in vals:
|
|
existing_job = current_running_jobs.get(institution["institution_id_ref"], self.env["l10n.ch.swissdec.job.result"])
|
|
if existing_job:
|
|
existing_job.update(institution)
|
|
else:
|
|
lines_to_create.append((0, 0, institution))
|
|
self.update({'l10n_ch_swissdec_job_result_ids': lines_to_create})
|
|
|
|
def _match_foreign_canton(self, notifications):
|
|
"""
|
|
When salary certificates are for foreign employees, the canton it is sent to is not straightforward and has to be guessed from notifications
|
|
"""
|
|
pattern = re.compile(
|
|
r'\b(' + '|'.join(re.escape(code[0]) for code in CANTONS) + r')\b'
|
|
)
|
|
canton = False
|
|
for notification in notifications:
|
|
match = pattern.search(notification.get("Description", ""))
|
|
if match:
|
|
canton = match.group(1)
|
|
break
|
|
if not canton: # We have to fallback in case swissdec did not provide a notification
|
|
canton = self.env.company.l10n_ch_work_location_ids[0].canton
|
|
return canton
|
|
|
|
def get_status_from_declare_salary(self):
|
|
self.ensure_one()
|
|
if self.state != "finished":
|
|
swissdec_declaration = SwissdecDeclaration()
|
|
declaration = swissdec_declaration.create_get_status_from_declare_salary(
|
|
job_key=self.job_key,
|
|
)
|
|
|
|
result = self.env.company._l10n_ch_swissdec_request('get_status_from_declare_salary', data=declaration, is_test=self.test_transmission)
|
|
response = result['soap_response']
|
|
message_archive = result['request_xml']
|
|
response_archive = result['response_xml']
|
|
|
|
if response:
|
|
attachment_request = self.env['ir.attachment'].create({
|
|
'name': _("Get_Status_%s_request.xml", self.swissdec_declaration_id),
|
|
'datas': base64.encodebytes(message_archive.encode()),
|
|
'res_id': self.id,
|
|
'res_model': self._name,
|
|
})
|
|
|
|
attachment_response = self.env['ir.attachment'].create({
|
|
'name': _("Declaration_%s_response.xml", self.swissdec_declaration_id),
|
|
'datas': base64.encodebytes(response_archive.encode()),
|
|
'res_id': self.id,
|
|
'res_model': self._name,
|
|
})
|
|
self.message_post(attachment_ids=[attachment_request.id, attachment_response.id], body=_('Get Status Archive'))
|
|
|
|
# response = serialize_object(response)
|
|
corresponding_report = self.env[self.res_model].browse(self.res_id)
|
|
institution_descriptions = swissdec_declaration.get_mapped_institution_descriptions(institutions=corresponding_report._get_institutions(), skip_bvg_payroll_unit=True)
|
|
job_responses = []
|
|
|
|
plausiblity_state = response.get("PlausibilityState", {}).get("Plausible")
|
|
if not plausiblity_state:
|
|
self.state = "plausibility"
|
|
else:
|
|
if response.get("JobFinished", False):
|
|
self.state = "finished"
|
|
else:
|
|
self.state = "processing"
|
|
if plausiblity_state.get("Info", None) or plausiblity_state.get("Warning", None):
|
|
self.general_warnings = {
|
|
"Info": plausiblity_state.get("Info", None),
|
|
"Warning": plausiblity_state.get("Warning", None)
|
|
}
|
|
else:
|
|
self.general_warnings = False
|
|
|
|
job_state = plausiblity_state.get("JobState")
|
|
for domain, institutions in job_state.items():
|
|
if institutions:
|
|
for institution in iter([institutions] if not isinstance(institutions, list) else institutions):
|
|
if domain not in ["Tax", "Statistic"]:
|
|
institution_description = institution_descriptions.get(institution.get("institutionIDRef", False))
|
|
id_ref = institution.get("institutionIDRef", False)
|
|
elif domain == "Tax":
|
|
id_ref = institution.get("canton")
|
|
if id_ref == 'EX':
|
|
notifications_success = institution.get("Success", {})
|
|
notifications = []
|
|
if notifications_success:
|
|
notifications = notifications_success.get("ResponseState", {}).get("Info", {}).get("Notification", [])
|
|
canton = self._match_foreign_canton(notifications)
|
|
else:
|
|
canton = id_ref
|
|
institution_description = {
|
|
"CantonID": canton
|
|
}
|
|
else:
|
|
id_ref = "#BFS"
|
|
institution_description = False
|
|
|
|
meta_data = {
|
|
"institution_id_ref": id_ref,
|
|
"institution_description": institution_description,
|
|
}
|
|
job_vals = {
|
|
"domain": domain,
|
|
"declaration_id": self.id,
|
|
"institution_id_ref": id_ref
|
|
}
|
|
|
|
if institution.get("Ignored"):
|
|
job_vals.update({
|
|
"general_state": "Ignored",
|
|
})
|
|
if institution.get("NotSupported"):
|
|
job_vals.update({
|
|
"general_state": "NotSupported",
|
|
})
|
|
elif institution.get("Processing"):
|
|
job_vals.update({
|
|
"general_state": "Processing",
|
|
})
|
|
elif institution.get("Error"):
|
|
job_vals.update({
|
|
"general_state": "Error",
|
|
"status_response_json": json.loads(json.dumps(institution.get("Error"), default=str)),
|
|
})
|
|
elif institution.get("Success"):
|
|
job_vals.update({
|
|
"general_state": "Success",
|
|
})
|
|
if institution.get("Success").get("ResponseState", {}).get("Info", {}) or institution.get("Success").get("ResponseState", {}).get("Warning", {}):
|
|
job_vals.update({
|
|
"status_response_json": json.loads(json.dumps(institution.get("Success"), default=str)),
|
|
})
|
|
success_vals = institution.get("Success")
|
|
if success_vals.get("CompletionAndResult"):
|
|
completion_result = success_vals.get("CompletionAndResult")
|
|
job_vals.update({
|
|
"success_state": "CompletionAndResult",
|
|
"completion_url": completion_result.get("Completion", {}).get("Url"),
|
|
"credential_key": completion_result.get("Credentials", {}).get("Key"),
|
|
"credential_password": completion_result.get("Credentials", {}).get("Password")
|
|
})
|
|
elif success_vals.get("Result"):
|
|
institution_result = success_vals.get("Result")
|
|
job_vals.update({
|
|
"success_state": "Result",
|
|
"credential_key": institution_result.get("Credentials", {}).get("Key"),
|
|
"credential_password": institution_result.get("Credentials", {}).get("Password")
|
|
})
|
|
elif success_vals.get("DialogAndResult"):
|
|
dialog_result = success_vals.get("DialogAndResult")
|
|
job_vals.update({
|
|
"success_state": "DialogAndResult",
|
|
"credential_key": dialog_result.get("Credentials", {}).get("Key"),
|
|
"credential_password": dialog_result.get("Credentials", {}).get("Password")
|
|
})
|
|
job_vals.update({
|
|
"result_meta_data": meta_data
|
|
})
|
|
job_responses.append(job_vals)
|
|
|
|
self._process_responses(job_responses)
|