[FIX] l10n_ke_edi_tremol: batch invoice processing
At present the invoice can only be sent to the device from the form view. The functions involved in posting the invoice expect just one invoice. This commit adapts the functions involved in posting the invoice in such a way that multiple moves can be processed. In order for this to happen: The validation check function now creates a list of errors per move, and the error message provided lists the errors under the name of each move. The post function has been adapted to iterate over the moves provided, and construct the params passed to the javascript action as a list of dictionaries, rather than a flat dictionary. The javascript action that forwards the data to the device is adapted to iterate over the params provided to it, instead of sending just the one invoice. A sever action has been added on the account move (tree view) to allow a user to select multiple entries from the invoice tree view and send all of them to the fiscal device. closes odoo/odoo#109520 Task-id: 2950308 X-original-commit: 2b590e69310bdce9201ad54a1b14f22007098173 Signed-off-by: Josse Colpaert <jco@odoo.com> Signed-off-by: Daniel Kosky (dako) <dako@odoo.com>
This commit is contained in:
committed by
Daniel Kosky (dako)
parent
fee5e773d1
commit
fcf3da325c
@@ -39,28 +39,47 @@ class AccountMove(models.Model):
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _l10n_ke_validate_move(self):
|
||||
""" Returns list of errors related to misconfigurations
|
||||
""" Returns list of errors related to misconfigurations per move
|
||||
|
||||
Find misconfigurations on the move, the lines of the move, and the
|
||||
taxes on those lines that would result in rejection by the KRA.
|
||||
"""
|
||||
self.ensure_one()
|
||||
errors = []
|
||||
# The credit note should refer to the control unit number (receipt number) of the original
|
||||
# invoice to which it relates.
|
||||
if self.move_type == 'out_refund' and not self.reversed_entry_id.l10n_ke_cu_invoice_number:
|
||||
errors.append(_("This credit note must reference the previous invoice, and this previous invoice must have already been submitted."))
|
||||
for move in self:
|
||||
move_errors = []
|
||||
if move.country_code != 'KE':
|
||||
move_errors.append(_("This invoice is not a Kenyan invoice and therefore can not be sent to the device."))
|
||||
|
||||
for line in self.invoice_line_ids.filtered(lambda l: l.display_type == 'product'):
|
||||
if not line.tax_ids or len(line.tax_ids) > 1:
|
||||
errors.append(_("On line %s, you must select one and only one tax.", line.name))
|
||||
else:
|
||||
if line.tax_ids.amount == 0 and not (line.product_id and line.product_id.l10n_ke_hsn_code and line.product_id.l10n_ke_hsn_name):
|
||||
errors.append(_("On line %s, a product with a HS Code and HS Name must be selected, since the tax is 0%% or exempt.", line.name))
|
||||
if move.company_id.currency_id != self.env.ref('base.KES'):
|
||||
move_errors.append(_("This invoice's company currency is not in Kenyan Shillings, conversion to KES is not possible."))
|
||||
|
||||
for tax in self.invoice_line_ids.tax_ids:
|
||||
if tax.amount not in (16, 8, 0):
|
||||
errors.append(_("Tax '%s' is used, but only taxes of 16%%, 8%%, 0%% or Exempt can be sent. Please reconfigure or change the tax.", tax.name))
|
||||
if move.state != 'posted':
|
||||
move_errors.append(_("This invoice/credit note has not been posted. Please confirm it to continue."))
|
||||
|
||||
if move.move_type not in ('out_refund', 'out_invoice'):
|
||||
move_errors.append(_("The document being sent should be an invoice or credit note."))
|
||||
|
||||
if any([move.l10n_ke_cu_invoice_number, move.l10n_ke_cu_serial_number, move.l10n_ke_cu_qrcode, move.l10n_ke_cu_datetime]):
|
||||
move_errors.append(_("The document already has details related to the fiscal device. Please make sure that the invoice has not already been sent."))
|
||||
|
||||
# The credit note should refer to the control unit number (receipt number) of the original
|
||||
# invoice to which it relates.
|
||||
if move.move_type == 'out_refund' and not move.reversed_entry_id.l10n_ke_cu_invoice_number:
|
||||
move_errors.append(_("This credit note must reference the previous invoice, and this previous invoice must have already been submitted."))
|
||||
|
||||
for line in self.invoice_line_ids.filtered(lambda l: l.display_type == 'product'):
|
||||
if not line.tax_ids or len(line.tax_ids) > 1:
|
||||
move_errors.append(_("On line %s, you must select one and only one tax.", line.name))
|
||||
else:
|
||||
if line.tax_ids.amount == 0 and not (line.product_id and line.product_id.l10n_ke_hsn_code and line.product_id.l10n_ke_hsn_name):
|
||||
move_errors.append(_("On line %s, a product with a HS Code and HS Name must be selected, since the tax is 0%% or exempt.", line.name))
|
||||
|
||||
for tax in move.invoice_line_ids.tax_ids:
|
||||
if tax.amount not in (16, 8, 0):
|
||||
move_errors.append(_("Tax '%s' is used, but only taxes of 16%%, 8%%, 0%% or Exempt can be sent. Please reconfigure or change the tax.", tax.name))
|
||||
|
||||
if move_errors:
|
||||
errors.append((move.name, move_errors))
|
||||
|
||||
return errors
|
||||
|
||||
@@ -187,6 +206,10 @@ class AccountMove(models.Model):
|
||||
return msgs
|
||||
|
||||
def _l10n_ke_get_cu_messages(self):
|
||||
""" Composes a list of all the command and data parts of the messages
|
||||
required for the fiscal device to open an invoice, add lines and
|
||||
subsequently close it.
|
||||
"""
|
||||
self.ensure_one()
|
||||
msgs = self._l10n_ke_cu_open_invoice_message()
|
||||
msgs += self._l10n_ke_cu_lines_messages()
|
||||
@@ -201,24 +224,38 @@ class AccountMove(models.Model):
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def l10n_ke_action_cu_post(self):
|
||||
self.ensure_one()
|
||||
""" Returns the client action descriptor dictionary for sending the
|
||||
invoice(s) to the fiscal device.
|
||||
"""
|
||||
# Check the configuration of the invoice
|
||||
errors = self._l10n_ke_validate_move()
|
||||
if errors:
|
||||
raise UserError(_("Invalid invoice configuration:\n\n%s") % '\n'.join(errors))
|
||||
error_msg = ""
|
||||
for move, error_list in errors:
|
||||
error_list = '\n'.join(error_list)
|
||||
error_msg += _("Invalid invoice configration on %s:\n\n%s", move, error_list)
|
||||
raise UserError(error_msg)
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'post_send',
|
||||
'params': {
|
||||
'messages': json.dumps([m.decode('cp1251') for m in self._l10n_ke_get_cu_messages()]),
|
||||
'move_id': self.id,
|
||||
'proxy_address': self.company_id.l10n_ke_cu_proxy_address,
|
||||
'company_vat': self.company_id.vat,
|
||||
'invoices': {
|
||||
move.id: {
|
||||
'messages': json.dumps([msg.decode('cp1251') for msg in move._l10n_ke_get_cu_messages()]),
|
||||
'proxy_address': move.company_id.l10n_ke_cu_proxy_address,
|
||||
'company_vat': move.company_id.vat
|
||||
} for move in self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def l10n_ke_cu_response(self, response):
|
||||
move = self.browse(response['move_id'])
|
||||
""" Set the fields related to the fiscal device on the invoice.
|
||||
|
||||
This is intended to be utilized by an RPC call from the javascript
|
||||
client action.
|
||||
"""
|
||||
move = self.browse(int(response['move_id']))
|
||||
replies = [msg for msg in response['replies']]
|
||||
move.update({
|
||||
'l10n_ke_cu_serial_number': response['serial_number'],
|
||||
|
||||
@@ -6,28 +6,44 @@ odoo.define('l10n_ke_edi_tremol.action_post_send_invoice', function (require) {
|
||||
var _t = core._t;
|
||||
|
||||
async function post_send(parent, {params}) {
|
||||
const move_id = params.move_id;
|
||||
await ajax.post(params.proxy_address + '/hw_proxy/l10n_ke_cu_send', params).then(function (res) {
|
||||
const res_obj = JSON.parse(res);
|
||||
if (res_obj.status != "ok") {
|
||||
Dialog.alert(this, "Posting the invoice has failed, with the message: \n" + res_obj.status);
|
||||
} else {
|
||||
rpc.query({
|
||||
model: 'account.move',
|
||||
method: 'l10n_ke_cu_response',
|
||||
args: [[], {'replies': res_obj.replies, 'serial_number': res_obj.serial_number, 'move_id': move_id}],
|
||||
}).then(function () {
|
||||
parent.services.action.doAction({
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
});
|
||||
}, function () {
|
||||
Dialog.alert(this, _t("Error trying to connect to Odoo. Check your internet connection"));
|
||||
})
|
||||
let refresh = false;
|
||||
for (let move_id in params.invoices) {
|
||||
try {
|
||||
const res = await ajax.post(
|
||||
params.invoices[move_id].proxy_address + '/hw_proxy/l10n_ke_cu_send', {
|
||||
messages: params.invoices[move_id].messages,
|
||||
company_vat: params.invoices[move_id].company_vat
|
||||
}
|
||||
);
|
||||
const res_obj = JSON.parse(res);
|
||||
if (res_obj.status === "ok") {
|
||||
try {
|
||||
await rpc.query({
|
||||
model: 'account.move',
|
||||
method: 'l10n_ke_cu_response',
|
||||
args: [[], {'replies': res_obj.replies, 'serial_number': res_obj.serial_number, 'move_id': move_id}],
|
||||
});
|
||||
refresh = true;
|
||||
} catch (e) {
|
||||
Dialog.alert(this, _t('Error trying to connect to Odoo. Check your internet connection.\n Error code: ') + e.status);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Dialog.alert(this, _t("Posting an invoice has failed, with the message: \n") + res_obj.status);
|
||||
break;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(e)
|
||||
Dialog.alert(this, _t('Error trying to connect to the middleware. Is the middleware running?\n Error code: ') + e.status);
|
||||
break;
|
||||
}
|
||||
}, function () {
|
||||
Dialog.alert(this, _t("Error trying to connect to the middleware. Is the middleware running?"));
|
||||
})
|
||||
}
|
||||
if (refresh) {
|
||||
parent.services.action.doAction({
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
});
|
||||
}
|
||||
}
|
||||
core.action_registry.add('post_send', post_send);
|
||||
return post_send;
|
||||
|
||||
@@ -42,6 +42,17 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_send_invoices_to_device" model="ir.actions.server">
|
||||
<field name="name">Send invoices to fiscal device</field>
|
||||
<field name="model_id" ref="account.model_account_move"/>
|
||||
<field name="binding_model_id" ref="account.model_account_move"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = records.l10n_ke_action_cu_post()
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="l10n_ke_inherit_account_move_search_view" model="ir.ui.view">
|
||||
<field name="name">l10n.ke.inherit.account.move.search</field>
|
||||
<field name="model">account.move</field>
|
||||
|
||||
Reference in New Issue
Block a user