1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/l10n_be_soda/models/account_journal.py
2024-12-10 09:04:09 +07:00

117 lines
5.4 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import io
from lxml import etree
import re
from odoo import _, fields, models
from odoo.exceptions import UserError
class AccountJournal(models.Model):
_inherit = 'account.journal'
def create_document_from_attachment(self, attachment_ids=None):
# OVERRIDE
journal = self or self.browse(self.env.context.get('default_journal_id'))
if journal.type == 'general':
attachments = self.env['ir.attachment'].browse(attachment_ids or [])
if not attachments:
raise UserError(_("No attachment was provided"))
if all(journal._l10n_be_check_soda_format(attachment) for attachment in attachments):
return journal._l10n_be_parse_soda_file(attachments)
return super().create_document_from_attachment(attachment_ids)
def _l10n_be_check_soda_format(self, attachment):
try:
return (
(attachment.mimetype in ('application/xml', 'text/xml')
# XML files sent by email have text/plain as mimetype
or (attachment.mimetype == 'text/plain' and attachment.name.lower().endswith('.xml')))
and etree.parse(io.BytesIO(attachment.raw)).getroot().tag == 'SocialDocument'
)
except etree.XMLSyntaxError:
return False
def _l10n_be_parse_soda_file(self, attachments, skip_wizard=False):
self.ensure_one()
# We keep a dict mapping the SODA reference to a dict with a list of `entries` and an `attachment_id`
# {
# 'soda_reference_1': {
# 'entries': [
# {
# 'code': '1200',
# 'name': 'Line Description',
# 'debit': '150.0',
# 'credit': '0.0',
# },
# ...
# ],
# 'attachment_id': 'attachment_id_1',
# },
# ...
# }
soda_files = {}
soda_code_to_name_mapping = {}
for attachment in attachments:
parsed_attachment = etree.parse(io.BytesIO(attachment.raw))
# The document VAT number must match the journal's company's VAT number
journal_company_vat = self.company_id.vat or self.company_id.company_registry
journal_company_vat = journal_company_vat and re.sub("[^0-9]", "", journal_company_vat)
parsed_ent_num = parsed_attachment.find('.//EntNum')
ent_num = parsed_ent_num.text and re.search(r'\d+', parsed_ent_num.text).group()
if ent_num != journal_company_vat:
if len(attachments) == 1:
message = _('The Soda Entry could not be created: \n'
'The imported document doesn\'t seem to correspond to this company\'s VAT number nor company id')
else:
message = _('The SODA Entry could not be created: \n'
'The company VAT number found in at least one document doesn\'t seem to correspond to this company\'s VAT number nor company id')
raise UserError(message)
# account.move.ref is SocialNumber+SequenceNumber+AccountPeriodYYYY/AccountPeriodmm : check that this move has not already been imported
account_period = parsed_attachment.find('.//AccountPeriod').text
ref = "%s-%s-%s/%s" % (parsed_attachment.find('.//Source').text, parsed_attachment.find('.//SeqNumber').text, account_period[:4], account_period[4:])
existing_move = self.env['account.move'].search([('ref', '=', ref)])
if existing_move:
if self._context.get('raise_no_imported_file', True):
raise UserError(
_('The entry %s has already been uploaded (%s).', ref, existing_move.name))
else:
return
soda_files[ref] = {
'entries': [],
'attachment_id': attachment.id,
'date': parsed_attachment.findtext('.//GenDate') or fields.Date.today().strftime("%Y-%m-%d"),
}
# Retrieve aml's infos
for _idx, elem in enumerate(parsed_attachment.findall('.//Accounting')):
code = elem.find('./Account').text
name = elem.find('./Label').text
soda_files[ref]['entries'].append({
'code': code,
'name': name,
'debit': float(elem.find('./Amount/Debit').text),
'credit': float(elem.find('./Amount/Credit').text),
})
soda_code_to_name_mapping[code] = name
wizard = self.env['soda.import.wizard'].create({
'soda_files': soda_files,
'soda_code_to_name_mapping': soda_code_to_name_mapping,
'company_id': self.company_id.id,
'journal_id': self.id,
})
if skip_wizard:
return wizard._action_save_and_import()
return {
'name': _('SODA Import'),
'type': 'ir.actions.act_window',
'views': [(False, 'form')],
'view_mode': 'form',
'view_id': self.env.ref('l10n_be_soda.soda_import_wizard_view_form').id,
'res_model': 'soda.import.wizard',
'res_id': wizard.id,
'target': 'new',
}