Files
enterprise-addons/l10n_uk_reports_cis/models/hmrc_transaction.py
T

167 lines
8.2 KiB
Python

from dateutil.relativedelta import relativedelta
from lxml import etree
from urllib.parse import urlparse
from odoo import api, fields, models
from odoo.tools.misc import format_date
from odoo.tools import float_repr, DEFAULT_SERVER_DATETIME_FORMAT
from odoo.addons.l10n_uk_hmrc.models.hmrc_transaction import _send_request
class HmrcTransaction(models.Model):
_inherit = "l10n_uk.hmrc.transaction"
transaction_type = fields.Selection(
string="Transaction type",
selection_add=[('cis_monthly_return', "CIS Monthly Return")],
ondelete={
'cis_monthly_return': 'set default',
},
)
document_file = fields.Binary(string="Document")
document_filename = fields.Char()
def _generate_cis_mr_xml_data(self, credentials, document_data):
self.ensure_one()
nil_return_indicator = not bool(document_data['subcontractor_return_ids'])
xml_data = {
'credentials': credentials,
'document': {
'subcontractors': [],
'contractor': {
'type': self.company_id.partner_id.company_type,
'unique_taxpayer_reference': self.company_id.l10n_uk_hmrc_unique_taxpayer_reference,
'account_office_reference': self.company_id.l10n_uk_hmrc_account_office_reference,
},
'period_end': self.period_end,
'nil_return_indicator': nil_return_indicator,
'employment_status': document_data['employment_status'] if not nil_return_indicator else False,
'subcontractor_verification': document_data['subcontractor_verification'] if not nil_return_indicator else False,
'inactivity_indicator': document_data['inactivity_indicator'],
},
'transaction': {
'mode': self.env['ir.config_parameter'].sudo().get_param("l10n_uk_hmrc.api_mode", 'production'),
'class': self._get_transaction_class(),
},
}
if not nil_return_indicator:
for subcontractor_return_dict in document_data['subcontractor_return_ids']:
partner = self.env['res.partner'].browse(subcontractor_return_dict['id'])
partner.with_prefetch(document_data['subcontractor_ids'])
xml_data['document']['subcontractors'].append({
'id': partner.id,
'type': partner.company_type,
'trading_name': partner.name,
'forename': partner.l10n_uk_reports_cis_forename,
'second_forename': partner.l10n_uk_reports_cis_second_forename,
'surname': partner.l10n_uk_reports_cis_surname,
'unique_taxpayer_reference': partner.l10n_uk_hmrc_unique_taxpayer_reference,
'company_registration_number': partner.l10n_uk_hmrc_company_registration_number,
'national_insurance_number': partner.l10n_uk_hmrc_national_insurance_number,
'cis_verification_number': partner.l10n_uk_reports_cis_verification_number,
'deduction_rate': partner.l10n_uk_reports_cis_deduction_rate,
'total_payment_made': float_repr(subcontractor_return_dict['total_payment_made'], 2),
'direct_cost_of_materials': float_repr(subcontractor_return_dict['direct_cost_of_materials'], 2),
'total_amount_deducted': float_repr(subcontractor_return_dict['total_amount_deducted'], 2),
})
xml_data['ir_mark'] = self._generate_ir_mark(self._get_request_body_xml_path(), xml_data)
return xml_data
def _submit_cis_mr_transaction(self, credentials, document_data):
self.ensure_one()
xml_data = self._generate_cis_mr_xml_data(credentials, document_data)
monthly_return_document = self._generate_xml_document(xml_data)
# We render it twice. The second time without credentials to store it in an attachment
xml_data['credentials'] = {}
monthly_return_document_safe = self._generate_xml_document(xml_data)
self.document_filename = f'hmrc_transaction_document_cis_{self.period_end}.xml'
self.document_file = etree.tostring(etree.fromstring(monthly_return_document_safe), pretty_print=True, xml_declaration=True, encoding='UTF-8')
response, header = _send_request(self, monthly_return_document)
if header['qualifier'] != 'acknowledgement':
self._handle_submission_error(response, header)
response_write_vals = {
'next_endpoint': urlparse(header['response_end_point']).path,
'next_polling': fields.Datetime.add(fields.Datetime.now(), seconds=int(header['poll_interval'])).strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'correlation_id': header['correlation_id'],
'state': 'polling',
}
self.write(response_write_vals)
self.env.ref('l10n_uk_hmrc.ir_cron_l10n_uk_hmrc_process_transactions')._trigger()
def _handle_success(self):
super()._handle_success()
email_template = self.env.ref('l10n_uk_reports_cis.email_template_cis_response_success', raise_if_not_found=False)
if self.transaction_type == 'cis_monthly_return' and email_template:
email_template.send_mail(
self.id,
force_send=True,
email_values={'attachments': [(self.response_filename, self.response_file)]},
)
def _handle_request_error(self, error_data, transaction_type):
super()._handle_request_error(error_data, transaction_type)
email_template = self.env.ref('l10n_uk_reports_cis.email_template_cis_response_failure', raise_if_not_found=False)
if self.transaction_type == 'cis_monthly_return' and transaction_type == 'polling' and email_template:
email_template.send_mail(
self.id,
force_send=True,
email_values={'attachments': [(self.response_filename, self.response_file)]},
)
@api.model
def _cron_notify_cis_return(self):
# This cron is executed every day. When it's the 6th day of the month meaning the end of the cis period,
# we send a reminder to inform the user that he has 14 days to post it.
today = fields.Date.today()
if today.day != 6:
return
email_template = self.env.ref('l10n_uk_reports_cis.email_template_cis_notification', raise_if_not_found=False)
if not email_template:
return
companies = self.env['res.company'].search([('partner_id.country_code', '=', 'GB'), ('l10n_uk_hmrc_unique_taxpayer_reference', '!=', False)])
period_end = today + relativedelta(days=-1) # As we are always the sixth of the month, we just need to subtract one day
transaction_by_companies = self.env['l10n_uk.hmrc.transaction'].search([('company_id', 'in', companies.ids), ('period_end', '=', period_end)]).grouped('company_id')
for company in companies:
if company.id in transaction_by_companies:
continue
email_body = self.env['ir.qweb']._render(
'l10n_uk_reports_cis.email_body_template_cis_notification',
{
'partner_name': company.partner_id.name,
'period_end': format_date(self.env, period_end, company.partner_id.lang),
'period_due': format_date(self.env, period_end + relativedelta(days=14), company.partner_id.lang),
},
)
email_body = self.env['mail.render.mixin']._replace_local_links(email_body)
email_template.send_mail(
company.id,
force_send=True,
email_values={'body_html': email_body},
)
def _get_transaction_class(self):
if self.transaction_type == 'cis_monthly_return':
return 'IR-CIS-CIS300MR'
super()._get_transaction_class()
def _get_request_body_xml_path(self):
"""
Returns the xml path of a file containing the template of CIS monthly return request body
"""
if self.transaction_type != 'cis_monthly_return':
return super()._get_request_body_xml_path()
return 'l10n_uk_reports_cis/templates/hmrc_transaction_body_cis.xml'