167 lines
8.2 KiB
Python
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'
|