216 lines
9.7 KiB
Python
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"]
|