[IMP] account, _facturx, l10n[be,it]_edi: remove account.invoice.import.wizard

Since the wizard is not shown anymore (skipped to go right to file selection), we can remove it. Small refactoring had to be done for l10n_it in order to upload files with multiple invoices inside and to detect format

closes odoo/odoo#34145

Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
This commit is contained in:
wan
2019-07-19 09:34:59 +00:00
parent 4b66aa58b9
commit f722ef0cb0
11 changed files with 73 additions and 129 deletions
-1
View File
@@ -24,7 +24,6 @@ You could use this simplified accounting in case you work with an (external) acc
'data/digest_data.xml',
'views/account_menuitem.xml',
'views/account_payment_view.xml',
'wizard/account_invoice_import_view.xml',
'wizard/account_unreconcile_view.xml',
'wizard/account_move_reversal_view.xml',
'views/account_move_views.xml',
+29
View File
@@ -9,6 +9,7 @@ from odoo.tools.float_utils import float_round as round, float_compare
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.exceptions import UserError, ValidationError
from odoo import api, fields, models, _
from odoo.tests.common import Form
class AccountAccountType(models.Model):
@@ -953,6 +954,34 @@ class AccountJournal(models.Model):
# We simply call the setup bar function.
return self.env['res.company'].setting_init_bank_account_action()
def create_invoice_from_attachment(self, attachment_ids=[]):
''' Create the invoices from files.
:return: A action redirecting to account.move tree/form view.
'''
attachments = self.env['ir.attachment'].browse(attachment_ids)
if not attachments:
raise UserError(_("No attachment was provided"))
invoices = self.env['account.move']
for attachment in attachments:
invoice = self.env['account.move'].create({})
attachment.write({'res_model': 'account.move', 'res_id': invoice.id})
invoice.message_post(attachment_ids=[attachment.id])
invoices += invoice
action_vals = {
'name': _('Generated Documents'),
'domain': [('id', 'in', invoices.ids)],
'res_model': 'account.move',
'views': [[False, "tree"], [False, "form"]],
'type': 'ir.actions.act_window',
}
if len(invoices) == 1:
action_vals.update({'res_id': invoices[0].id, 'view_mode': 'form'})
else:
action_vals['view_mode'] = 'tree,form'
return action_vals
class ResPartnerBank(models.Model):
_inherit = "res.partner.bank"
@@ -32,15 +32,12 @@ odoo.define('account.upload.bill.mixin', function (require) {
var attachent_ids = attachments.reduce(function(filtered, record) {
if (record.id) {
filtered.push(record.id);
}
}
return filtered;
}, []);
if (!attachent_ids.length) {
return self.do_notify(_t("Error"), _t("An error occurred during the upload"));
}
return this._rpc({
model: 'account.invoice.import.wizard',
method: 'create_invoices',
model: 'account.journal',
method: 'create_invoice_from_attachment',
args: ["", attachent_ids],
context: this.initialState.context,
}).then(function(result) {
@@ -50,9 +47,6 @@ odoo.define('account.upload.bill.mixin', function (require) {
_onUpload: function (event) {
var self = this;
if (!this.initialState.context.journal_type) {
this.initialState.context.journal_type = event.currentTarget.attributes['journal_type'].value;
}
// If hidden upload form don't exists, create it
var $formContainer = this.$('.o_content').find('.o_vendor_bill_upload');
if (!$formContainer.length) {
@@ -4,6 +4,7 @@
<t t-name="account.BillsHiddenUploadForm">
<div class="d-none o_vendor_bill_upload">
<t t-call="HiddenInputFile">
<t t-set="multi_upload" t-value="true"/>
<t t-set="fileupload_id" t-value="widget.fileUploadID"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
<input type="hidden" name="model" t-att-value="'account.move'"/>
@@ -19,11 +20,9 @@
r.push(context.engine.tools.call(context, 'ImportView.import_button', dict));
};
</t>
<t t-if="widget.initialState.context.journal_type === 'purchase'">
<button type="button" class="btn btn-secondary o_button_upload_bill">
Upload
</button>
</t>
<button type="button" class="btn btn-secondary o_button_upload_bill">
Upload
</button>
</t>
</t>
</templates>
</templates>
-1
View File
@@ -3,7 +3,6 @@
from . import account_unreconcile
from . import account_invoice_import
from . import account_validate_account_move
from . import pos_box
from . import account_move_reversal
@@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.tests.common import Form
from odoo.exceptions import UserError
class ImportInvoiceImportWizard(models.TransientModel):
_name = 'account.invoice.import.wizard'
_description = 'Import Your Vendor Bills from Files.'
attachment_ids = fields.Many2many('ir.attachment', string='Files')
def _create_invoice_from_file(self, attachment):
invoice = self.env['account.move'].create({})
attachment.write({'res_model': 'account.move', 'res_id': invoice.id})
invoice.message_post(attachment_ids=[attachment.id])
return invoice
def _create_invoice(self, attachment):
return self._create_invoice_from_file(attachment)
def create_invoices(self, attachment_ids=[]):
''' Create the invoices from files.
:return: A action redirecting to account.invoice tree/form view.
'''
attachments = self.attachment_ids or self.env['ir.attachment'].browse(attachment_ids)
if not attachments:
raise UserError(_("No attachment was provided"))
invoices = self.env['account.move']
for attachment in attachments:
invoices += self._create_invoice(attachment)
action_vals = {
'name': _('Generated Documents'),
'domain': [('id', 'in', invoices.ids)],
'view_type': 'form',
'res_model': 'account.move',
'views': [[False, "tree"], [False, "form"]],
'type': 'ir.actions.act_window',
'context': self.env.context,
}
if len(invoices) == 1:
action_vals.update({'res_id': invoices[0].id, 'view_mode': 'form'})
else:
action_vals['view_mode'] = 'tree,form'
return action_vals
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="account_invoice_import_wizard_form_view" model="ir.ui.view">
<field name="name">account.invoice.import.wizard.form</field>
<field name="model">account.invoice.import.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<field name="attachment_ids" widget="many2many_binary" colspan="2"
string="Upload Files" nolabel="1"/>
</group>
<footer>
<button string="Import" name="create_invoices" type="object" class="oe_highlight"/>
<button string="Cancel" class="btn btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="account_invoice_import_wizard_action" model="ir.actions.act_window">
<field name="name">Import Vendor Bills</field>
<field name="res_model">account.invoice.import.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'default_type': 'in_invoice', 'default_journal_id': active_id}</field>
</record>
</data>
</odoo>
@@ -291,7 +291,7 @@ class AccountMove(models.Model):
:return: a list of triplet (xml_type, check_func, decode_func)
* xml_type: The format name, e.g 'UBL 2.1'
* check_func: A function taking an etree as parameter and returning a dict:
* check_func: A function taking an etree and a file name as parameter and returning a dict:
* flag: The etree is part of this format.
* error: Error message.
* decode_func: A function taking an etree as parameter and returning an invoice record.
@@ -310,20 +310,20 @@ class AccountMove(models.Model):
raise UserError(_('The xml file is badly formatted : {}').format(attachment.name))
for xml_type, check_func, decode_func in decoders:
check_res = check_func(tree)
check_res = check_func(tree, attachment.name)
if check_res.get('flag') and not check_res.get('error'):
invoice = decode_func(tree)
if invoice:
invoice_ids = decode_func(tree)
if invoice_ids:
try:
# don't propose to send to ocr
invoice.extract_state = 'done'
invoice_ids.write({'extract_state': 'done'})
except AttributeError:
# account_invoice_exctract not installed
pass
break
try:
return invoice
return invoice_ids
except UnboundLocalError:
raise UserError(_('No decoder was found for the xml file: {}. The file is badly formatted, not supported or the decoder is not installed').format(attachment.name))
+1 -1
View File
@@ -25,7 +25,7 @@ class AccountMove(models.Model):
return namespaces
@api.model
def _detect_ubl_2_1(self, tree):
def _detect_ubl_2_1(self, tree, file_name):
# Quick check the tree looks like an UBL 2.1 file.
flag = tree.tag == '{urn:oasis:names:specification:ubl:schema:xsd:Invoice-2}Invoice'
error = None
+22 -27
View File
@@ -290,23 +290,22 @@ class AccountMove(models.Model):
)
self.l10n_it_send_state = 'invalid'
def _import_xml_invoice(self, content, attachment):
def _import_xml_invoice(self, tree):
''' Extract invoice values from the E-Invoice xml tree passed as parameter.
:param content: The tree of the xml file.
:return: A dictionary containing account.invoice values to create/update it.
'''
try:
tree = etree.fromstring(content)
except:
_logger.info('Error during decoding XML file')
return self.env['account.move']
invoices = self.env['account.move']
multi = False
# possible to have multiple invoices in the case of an invoice batch, the batch itself is repeated for every invoice of the batch
for body_tree in tree.xpath('//FatturaElettronicaBody', namespaces=tree.nsmap):
if multi:
# make sure all the iterations create a new invoice record (except the first which could have already created one)
self = self.env['account.move']
multi = True
elements = tree.xpath('//DatiGeneraliDocumento/TipoDocumento', namespaces=tree.nsmap)
if elements and elements[0].text and elements[0].text == 'TD01':
@@ -654,7 +653,8 @@ class AccountMove(models.Model):
})
# default_res_id is had to context to avoid facturx to import his content
new_invoice.with_context(default_res_id=new_invoice.id).message_post(
# no_new_invoice to prevent from looping on the message_post that would create a new invoice without it
new_invoice.with_context(no_new_invoice=True, default_res_id=new_invoice.id).message_post(
body=(_("Attachment from XML")),
attachment_ids=[attachment_64.id]
)
@@ -662,10 +662,6 @@ class AccountMove(models.Model):
for message in message_to_log:
new_invoice.message_post(body=message)
if attachment:
new_invoice.l10n_it_einvoice_name = attachment.name
attachment.write({'res_model': 'account.move', 'res_id': new_invoice.id})
new_invoice.message_post(attachment_ids=[attachment.id])
invoices += new_invoice
return invoices
@@ -695,6 +691,20 @@ class AccountMove(models.Model):
output_str += "<li>%s: %s</li>" % (element.tag, text)
return output_str + "</ul>"
@api.model
def _get_xml_decoders(self):
# Override
ubl_decoders = [('Italy', self._detect_italy_edi, self._import_xml_invoice)]
return super(AccountMove, self)._get_xml_decoders() + ubl_decoders
@api.model
def _detect_italy_edi(self, tree, file_name):
# Quick check the file name looks like the one of an Italian EDI XML.
flag = re.search("([A-Z]{2}[A-Za-z0-9]{2,28}_[A-Za-z0-9]{0,5}.(xml.p7m|xml))", file_name)
error = None
return {'flag': flag, 'error': error}
class AccountTax(models.Model):
_name = "account.tax"
_inherit = "account.tax"
@@ -730,18 +740,3 @@ class AccountTax(models.Model):
raise ValidationError("If the tax has exoneration, you must enter a kind of exoneration, a law reference and the amount of the tax must be 0.0.")
if tax.l10n_it_kind_exoneration == 'N6' and tax.l10n_it_vat_due_date == 'S':
raise UserError(_("'Scissione dei pagamenti' is not compatible with exoneration of kind 'N6'"))
class ImportInvoiceImportWizard(models.TransientModel):
_name = 'account.invoice.import.wizard'
_inherit = 'account.invoice.import.wizard'
def _create_invoice_from_file(self, attachment):
if attachment.mimetype == 'application/xml' and re.search("([A-Z]{2}[A-Za-z0-9]{2,28}_[A-Za-z0-9]{0,5}.(xml.p7m|xml))", attachment.name):
if self.env['account.move'].search([('l10n_it_einvoice_name', '=', attachment.name)], limit=1):
# invoice already exist
raise UserError(_('E-invoice already exist: %s') % attachment.name)
self = self.with_context(default_journal_id= self.journal_id.id)
invoice = self.env['account.move']._import_xml_invoice(base64.decodestring(attachment.datas), attachment)
else:
invoice = super(ImportInvoiceImportWizard, self)._create_invoice_from_file(attachment)
return invoice
+7 -2
View File
@@ -19,7 +19,7 @@ from lxml import etree
from datetime import datetime
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError, UserError
_logger = logging.getLogger(__name__)
@@ -137,7 +137,12 @@ class FetchmailServer(models.Model):
'type': 'binary',
})
invoice = self.env['account.move']._import_xml_invoice(att_content, invoice_attachment)
try:
tree = etree.fromstring(att_content)
except Exception:
raise UserError(_('The xml file is badly formatted : {}').format(att_name))
invoice = self.env['account.move']._import_xml_invoice(tree)
invoice.l10n_it_send_state = "new"
invoice.source_email = from_address
self._cr.commit()