Files
enterprise-addons/l10n_au_hr_payroll_api/models/res_company.py
T

216 lines
9.7 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import requests
from requests.exceptions import HTTPError, ConnectionError, MissingSchema, Timeout
from urllib3.exceptions import NewConnectionError
from werkzeug.urls import url_join
from odoo import api, models, fields, _, tools, modules
from odoo.exceptions import ValidationError, UserError
from ..exceptions import _l10n_au_raise_user_error
_logger = logging.getLogger(__name__)
class ResCompany(models.Model):
_name = "res.company"
_inherit = ["res.company", "l10n_au.audit.logging.mixin"]
l10n_au_payroll_proxy_user_id = fields.Many2one(
comodel_name="account_edi_proxy_client.user",
compute="_compute_l10n_au_payroll_proxy_user_id",
)
l10n_au_payroll_mode = fields.Selection(
selection=[
('test', 'Testing'),
('prod', 'Production'),
],
string="Payroll Mode",
default="test",
required=True
)
l10n_au_abn_valid = fields.Boolean("ABN Validation State", readonly=True, tracking=True)
l10n_au_employer_registration_ids = fields.One2many(
comodel_name="l10n_au.employer.registration",
inverse_name="company_id",
string="Employer Registration",
)
l10n_au_employer_registration_id = fields.Many2one(
comodel_name="l10n_au.employer.registration",
string="Current Employer Registration",
compute="_compute_l10n_au_employer_registration_id",
store=True,
)
l10n_au_registration_status = fields.Selection(
selection=[
('pending', 'Pending'),
('ongoing', 'Ongoing'),
('registered_ongoing', 'Registered (Ongoing)'),
('registered', 'Registered'),
('expired', 'Expired'),
],
string="Registration Status",
compute="_compute_l10n_au_employer_registration",
)
# --------------------------------
# ORM methods
# --------------------------------
def write(self, vals):
if "vat" in vals or "name" in vals:
self.l10n_au_abn_valid = False
return super().write(vals)
@api.depends("account_edi_proxy_client_ids", 'l10n_au_payroll_mode')
def _compute_l10n_au_payroll_proxy_user_id(self):
""" Each company is expected to have at most one proxy user for malaysia for each mode.
Thus, we can easily find said user.
"""
for company in self:
company.l10n_au_payroll_proxy_user_id = company.account_edi_proxy_client_ids.filtered(
lambda u: u.proxy_type == 'l10n_au_payroll' and u.edi_mode == company.l10n_au_payroll_mode
)
@api.depends("l10n_au_employer_registration_ids", "l10n_au_employer_registration_ids.status")
def _compute_l10n_au_employer_registration_id(self):
""" It uses the latest registered registration if available, otherwise it
falls back to the most recent one.
"""
for record in self:
record.l10n_au_employer_registration_id = record.l10n_au_employer_registration_ids.filtered(
lambda r: r.status == 'registered'
) or record.l10n_au_employer_registration_ids.sorted(
key=lambda r: r.create_date, reverse=True
)[:1]
@api.depends("l10n_au_employer_registration_ids", "l10n_au_employer_registration_ids.status")
def _compute_l10n_au_employer_registration(self):
for record in self:
if not record.l10n_au_employer_registration_ids:
record.l10n_au_registration_status = 'pending'
else:
latest_registration = record.l10n_au_employer_registration_ids.sorted(
key=lambda r: r.create_date, reverse=True
)[0]
if latest_registration.status == 'pending':
if record.l10n_au_employer_registration_id.status == 'registered':
record.l10n_au_registration_status = 'registered_ongoing'
else:
record.l10n_au_registration_status = 'ongoing'
else:
record.l10n_au_registration_status = latest_registration.status
# --------------------------------
# Business methods
# --------------------------------
def _l10n_au_make_public_request(self, endpoint, params=None, timeout=30):
""" Make a public http request to Payroll Proxy server. """
if tools.config['test_enable'] or modules.module.current_test:
raise UserError(_("Superchoice API Connection disabled in testing environment."))
host = self.env["account_edi_proxy_client.user"]._get_server_url("l10n_au_payroll", self.l10n_au_payroll_mode or "test")
params = params or {}
params.update({
"db_uuid": self.env['ir.config_parameter'].sudo().get_param('database.uuid'),
"is_neutralised": self.env['ir.config_parameter'].sudo().get_param('database.is_neutralized', False),
})
try:
response = requests.get(
url=url_join(host, "/api/l10n_au_payroll/1" + endpoint),
params=params,
timeout=timeout
)
response = response.json()
except (ConnectionError, MissingSchema, Timeout, HTTPError, NewConnectionError) as e:
_l10n_au_raise_user_error(e)
return response
def _l10n_au_payroll_create_proxy_user(self):
""" This method will create a new proxy user for the current company based on the selected mode, if no users already exists. """
self.ensure_one()
self.env['account_edi_proxy_client.user']._register_proxy_user(self, 'l10n_au_payroll', self.l10n_au_payroll_mode)
def _l10n_au_payroll_get_proxy_user(self, catch_errors=False):
""" This method will return the proxy user for the current company based on the selected mode. """
self.ensure_one()
if (not self.l10n_au_payroll_proxy_user_id
and (proxy := self.l10n_au_employer_registration_ids.filtered_domain([("status", "in", ["registered"])]))
and proxy.status != self.l10n_au_payroll_mode):
error_msg = _("The payroll mode has changed to %(payroll_mode)s, please register the payroll for %(payroll_mode)s mode. "
"Make sure this is a %(payroll_mode)s database before proceeding with %(payroll_mode)s mode.",
payroll_mode=self.l10n_au_payroll_mode)
if catch_errors:
_logger.error(error_msg)
return False
raise UserError(error_msg)
return self.l10n_au_payroll_proxy_user_id
def register_payroll(self):
if self.l10n_au_employer_registration_id.status != "registered":
raise UserError(_("No employer registration found for this company. "
"Please use the payroll onboarding wizard to register your company."))
if not self.env["l10n_au.super.fund"].search([("is_valid", "=", True)], limit=1):
self.env["l10n_au.super.fund"]._update_active_funds()
# Create a proxy user if it does not exist.
if not self.l10n_au_payroll_proxy_user_id:
self.env['account_edi_proxy_client.user']._l10n_au_register_proxy_user(
self,
self.l10n_au_payroll_mode,
self.l10n_au_employer_registration_id._prepare_employer_registration()
)
# Update the payroll registration of the proxy user if already exists
else:
response = self._l10n_au_payroll_get_proxy_user()._l10n_au_payroll_request(
"/register",
{
"registration_details": self.l10n_au_employer_registration_id._prepare_employer_registration()
}
)
self.l10n_au_bms_id = response.get("client_bms_id")
for employee in self.env["hr.employee"].search([("company_id", "=", self.id), ("l10n_au_payroll_id", "=", False)]):
employee.l10n_au_payroll_id = employee._l10n_au_generate_payroll_id(
employee.name,
employee.l10n_au_tfn,
self.vat
)
def action_check_abn(self):
self.ensure_one()
response = self._l10n_au_make_public_request("/verify_abn", {"abn": self.vat, "legal_name": self.name})
if response.get("success", False):
self.l10n_au_abn_valid = True
elif "error" in response:
raise ValidationError(response["error"])
else:
raise ValidationError(_("Unable to verify this ABN. Please contact your system administrator."))
def action_view_payroll_onboarding(self):
self.ensure_one()
registration = self.l10n_au_employer_registration_ids.filtered(
lambda r: r.status == "pending"
)
if not registration:
registration = self.env["l10n_au.employer.registration"].sudo().create({
"company_id": self.id,
"status": "pending",
})
wizard = self.env["l10n_au.payroll.register.wizard"].create({
"registration_id": registration.id,
})
return {
"name": "Payroll Onboarding",
"view_mode": "form",
"res_model": "l10n_au.payroll.register.wizard",
"view_id": self.env.ref("l10n_au_hr_payroll_api.l10n_au_payroll_register_wizard_form_view").id,
"res_id": wizard.id,
"type": "ir.actions.act_window",
"target": "new",
}
# --------------------------------
# Audit Logging Mixin methods
# --------------------------------
def _get_audit_logging_fields(self):
return ["name", "vat", "l10n_au_hr_super_responsible_id", "l10n_au_stp_responsible_id", "l10n_au_bms_id"]