diff --git a/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js index 4877ddedc895..97dd1e3d3410 100644 --- a/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js +++ b/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js @@ -41,16 +41,20 @@ odoo.define("l10n_br_pos.PaymentScreen", function (require) { async _isOrderValid(isForceValidate) { var result = super._isOrderValid(isForceValidate); - var order = this.env.pos.get_order(); - const valid_cpf_cnpj = this.check_valid_cpf_cnpj(order); - if (valid_cpf_cnpj) { - result = await order.document_send(this); - return result; + if (this.env.pos.config.simplified_document_type) { + var order = this.env.pos.get_order(); + const valid_cpf_cnpj = this.check_valid_cpf_cnpj(order); + if (valid_cpf_cnpj) { + result = await order.document_send(this); + } else { + Gui.showPopup("ErrorPopup", { + title: _t("Invalid CNPJ / CPF !"), + body: _t("Enter a valid CNPJ / CPF number"), + }); + } } - Gui.showPopup("ErrorPopup", { - title: _t("Invalid CNPJ / CPF !"), - body: _t("Enter a valid CNPJ / CPF number"), - }); + + return result; } }; diff --git a/l10n_br_pos/static/src/js/models.js b/l10n_br_pos/static/src/js/models.js index d7b8d32474ae..7e446b91131e 100644 --- a/l10n_br_pos/static/src/js/models.js +++ b/l10n_br_pos/static/src/js/models.js @@ -384,7 +384,6 @@ odoo.define("l10n_br_pos.models", function (require) { label: "Iniciando Processo de Transmissão", }); this.state_edoc = SITUACAO_EDOC_A_ENVIAR; - this._document_status_popup(); var result = false; var processor_result = null; // Verifica se os campos do documento fiscal são válidos @@ -393,6 +392,7 @@ odoo.define("l10n_br_pos.models", function (require) { // Obtem o responsável pelo envio do documento fiscal; var processor = await this._document_get_processor(); if (processor) { + this._document_status_popup(); // Efetivamente envia o documento fiscal processor_result = await processor.send_order(this); // Valida se foi emitido corretamente e salva os dados do resulto diff --git a/l10n_br_pos_nfce/__init__.py b/l10n_br_pos_nfce/__init__.py index e69de29bb2d1..0650744f6bc6 100644 --- a/l10n_br_pos_nfce/__init__.py +++ b/l10n_br_pos_nfce/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_br_pos_nfce/__manifest__.py b/l10n_br_pos_nfce/__manifest__.py index 8f6f28392d63..245880695468 100644 --- a/l10n_br_pos_nfce/__manifest__.py +++ b/l10n_br_pos_nfce/__manifest__.py @@ -13,9 +13,12 @@ "maintainers": ["mileo", "sadamo", "gabrielcardoso21", "lfdivino"], "depends": [ "l10n_br_pos", + "l10n_br_account_nfe", ], "data": [ + "views/pos_payment_method.xml", "views/pos_template.xml", + "views/pos_config_view.xml", ], "demo": [], "installable": True, diff --git a/l10n_br_pos_nfce/models/__init__.py b/l10n_br_pos_nfce/models/__init__.py new file mode 100644 index 000000000000..7988578e24d1 --- /dev/null +++ b/l10n_br_pos_nfce/models/__init__.py @@ -0,0 +1,3 @@ +from . import pos_config +from . import pos_order +from . import pos_payment_method diff --git a/l10n_br_pos_nfce/models/pos_config.py b/l10n_br_pos_nfce/models/pos_config.py new file mode 100644 index 000000000000..2c074e7c4526 --- /dev/null +++ b/l10n_br_pos_nfce/models/pos_config.py @@ -0,0 +1,13 @@ +# Copyright (C) 2023 Renato Lima - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class PosConfig(models.Model): + _inherit = "pos.config" + + nfce_document_serie_id = fields.Many2one( + string="Document Serie", + comodel_name="l10n_br_fiscal.document.serie", + ) diff --git a/l10n_br_pos_nfce/models/pos_order.py b/l10n_br_pos_nfce/models/pos_order.py new file mode 100644 index 000000000000..88b5c89872db --- /dev/null +++ b/l10n_br_pos_nfce/models/pos_order.py @@ -0,0 +1,85 @@ +# Copyright (C) 2023 Renato Lima - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import models + + +class PosOrder(models.Model): + _inherit = "pos.order" + + def _prepare_invoice_vals(self): + vals = super(PosOrder, self)._prepare_invoice_vals() + + pos_config_id = self.session_id.config_id + payment_mode_id = self.payment_ids[0].payment_method_id.payment_mode_id + + vals.update( + { + "document_type_id": pos_config_id.simplified_document_type_id.id, + "fiscal_operation_id": pos_config_id.out_pos_fiscal_operation_id.id, + "ind_pres": "1", + "document_serie_id": pos_config_id.nfce_document_serie_id.id, + "partner_id": pos_config_id.partner_id.id, + "payment_mode_id": payment_mode_id.id, + } + ) + + return vals + + def _generate_pos_order_invoice(self): + res = super(PosOrder, self)._generate_pos_order_invoice() + + self.account_move.fiscal_document_id.action_document_confirm() + + return res + + def _prepare_invoice_line(self, order_line): + vals = super(PosOrder, self)._prepare_invoice_line(order_line) + pos_fiscal_map_ids = order_line.product_id.pos_fiscal_map_ids + fiscal_tax_ids = [ + ( + 6, + 0, + [ + pos_fiscal_map_ids.icms_tax_id.id, + pos_fiscal_map_ids.ipi_tax_id.id, + pos_fiscal_map_ids.cofins_tax_id.id, + pos_fiscal_map_ids.pis_tax_id.id, + ], + ) + ] + vals.update( + { + "product_uom_id": order_line.product_uom_id.id, + "fiscal_operation_id": pos_fiscal_map_ids.fiscal_operation_id.id, + "tax_icms_or_issqn": "icms", + "uom_id": order_line.product_id.uom_id.id, + "ncm_id": order_line.product_id.ncm_id.id, + "fiscal_operation_line_id": pos_fiscal_map_ids.fiscal_operation_line_id.id, + "cfop_id": pos_fiscal_map_ids.cfop_id.id, + "uot_id": pos_fiscal_map_ids.uot_id.id, + "fiscal_genre_id": order_line.product_id.fiscal_genre_id.id, + "icms_tax_id": pos_fiscal_map_ids.icms_tax_id.id, + "icms_cst_id": pos_fiscal_map_ids.icms_cst_id.id, + "icms_base": pos_fiscal_map_ids.icms_base, + "icms_percent": pos_fiscal_map_ids.icms_percent, + "icms_value": pos_fiscal_map_ids.icms_value, + "ipi_tax_id": pos_fiscal_map_ids.ipi_tax_id.id, + "ipi_cst_id": pos_fiscal_map_ids.ipi_cst_id.id, + "ipi_base": pos_fiscal_map_ids.ipi_base, + "ipi_percent": pos_fiscal_map_ids.ipi_percent, + "ipi_value": pos_fiscal_map_ids.ipi_value, + "cofins_tax_id": pos_fiscal_map_ids.cofins_tax_id.id, + "cofins_cst_id": pos_fiscal_map_ids.cofins_cst_id.id, + "cofins_base": pos_fiscal_map_ids.cofins_base, + "cofins_percent": pos_fiscal_map_ids.cofins_percent, + "cofins_value": pos_fiscal_map_ids.cofins_value, + "pis_tax_id": pos_fiscal_map_ids.pis_tax_id.id, + "pis_cst_id": pos_fiscal_map_ids.pis_cst_id.id, + "pis_base": pos_fiscal_map_ids.pis_base, + "pis_percent": pos_fiscal_map_ids.pis_percent, + "pis_value": pos_fiscal_map_ids.pis_value, + "fiscal_tax_ids": fiscal_tax_ids, + } + ) + return vals diff --git a/l10n_br_pos_nfce/models/pos_payment_method.py b/l10n_br_pos_nfce/models/pos_payment_method.py new file mode 100644 index 000000000000..bdf21247d955 --- /dev/null +++ b/l10n_br_pos_nfce/models/pos_payment_method.py @@ -0,0 +1,15 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class PosPaymentMethod(models.Model): + + _inherit = "pos.payment.method" + + payment_mode_id = fields.Many2one( + comodel_name="account.payment.mode", + required=True, + string="NFe Account Payment Mode", + ) diff --git a/l10n_br_pos_nfce/static/src/js/Screens/PaymentScreen.js b/l10n_br_pos_nfce/static/src/js/Screens/PaymentScreen.js new file mode 100644 index 000000000000..aadb73a4043b --- /dev/null +++ b/l10n_br_pos_nfce/static/src/js/Screens/PaymentScreen.js @@ -0,0 +1,26 @@ +/* +Copyright (C) 2016-Today KMEE (https://kmee.com.br) +@author: Luis Felipe Mileo +@author: Luiz Felipe do Divino +@author: Gabriel Cardoso + License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +*/ + +odoo.define("l10n_br_pos_nfce.PaymentScreen", function (require) { + "use strict"; + + const PaymentScreen = require("point_of_sale.PaymentScreen"); + const Registries = require("point_of_sale.Registries"); + + const L10nBrPosNFCePaymentScreen = (PaymentScreen) => + class extends PaymentScreen { + toggleIsToInvoice() { + if (!this.document_type === "65") { + super.toggleIsToInvoice(); + } + } + }; + Registries.Component.extend(PaymentScreen, L10nBrPosNFCePaymentScreen); + + return L10nBrPosNFCePaymentScreen; +}); diff --git a/l10n_br_pos_nfce/static/src/js/models.js b/l10n_br_pos_nfce/static/src/js/models.js index f8b6704dd0e0..dfbc49986c45 100644 --- a/l10n_br_pos_nfce/static/src/js/models.js +++ b/l10n_br_pos_nfce/static/src/js/models.js @@ -4,7 +4,96 @@ Copyright (C) 2022-Today KMEE (https://kmee.com.br) License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). */ -odoo.define("l10n_br_pos.models", function () { +odoo.define("l10n_br_pos_nfce.models", function (require) { "use strict"; + const models = require("point_of_sale.models"); + + var _super_order = models.Order.prototype; + models.Order = models.Order.extend({ + initialize: function (attributes, options) { + // CORE METHODS + _super_order.initialize.apply(this, arguments, options); + if (this.document_type === "65") { + this.to_invoice = true; + } + }, + async document_send(component) { + if (this.document_type !== "65") { + return _super_order.document_send.apply(this, arguments); + } + if (!this.get_client()) { + const anonimous_partner = this.pos.db.get_partner_by_id( + this.pos.config.partner_id[0] + ); + this.set_client(anonimous_partner); + component.trigger("close-popup"); + } + + return true; + }, + }); + + var _super_posmodel = models.PosModel.prototype; + models.PosModel = models.PosModel.extend({ + push_and_invoice_order(order) { + if (!this.document_type === "65") { + return _super_posmodel.push_and_invoice_order.call(this); + } + var self = this; + var invoiced = new Promise(function (resolveInvoiced, rejectInvoiced) { + if (!order.get_client()) { + rejectInvoiced({ + code: 400, + message: "Missing Customer", + data: {}, + }); + } else { + var order_id = self.db.add_order(order.export_as_JSON()); + + self.flush_mutex.exec(function () { + var done = new Promise(function (resolveDone, rejectDone) { + // Send the order to the server + // we have a 30 seconds timeout on this push. + // FIXME: if the server takes more than 30 seconds to accept the order, + // the client will believe it wasn't successfully sent, and very bad + // things will happen as a duplicate will be sent next time + // so we must make sure the server detects and ignores duplicated orders + + var transfer = self._flush_orders( + [self.db.get_order(order_id)], + {timeout: 30000, to_invoice: true} + ); + + transfer.catch(function (error) { + rejectInvoiced(error); + rejectDone(); + }); + + transfer.then(function (order_server_id) { + if (order_server_id.length) { + resolveInvoiced(order_server_id); + resolveDone(); + } else { + // The order has been pushed separately in batch when + // the connection came back. + // The user has to go to the backend to print the invoice + rejectInvoiced({ + code: 401, + message: "Backend Invoice", + data: {order: order}, + }); + rejectDone(); + } + }); + + return done; + }); + }); + } + }); + + return invoiced; + }, + }); }); diff --git a/l10n_br_pos_nfce/views/pos_config_view.xml b/l10n_br_pos_nfce/views/pos_config_view.xml new file mode 100644 index 000000000000..717681370762 --- /dev/null +++ b/l10n_br_pos_nfce/views/pos_config_view.xml @@ -0,0 +1,15 @@ + + + + + pos.config.form (in l10n_br_pos_nfce) + pos.config + + + + + + + + + diff --git a/l10n_br_pos_nfce/views/pos_payment_method.xml b/l10n_br_pos_nfce/views/pos_payment_method.xml new file mode 100644 index 000000000000..e2e3d5dda223 --- /dev/null +++ b/l10n_br_pos_nfce/views/pos_payment_method.xml @@ -0,0 +1,22 @@ + + + + + + pos.payment.method.form (in l10n_br_pos_nfce) + pos.payment.method + + + + + + + + + + + diff --git a/l10n_br_pos_nfce/views/pos_template.xml b/l10n_br_pos_nfce/views/pos_template.xml index 0d5aa5a6e846..d5cf1abddd1c 100644 --- a/l10n_br_pos_nfce/views/pos_template.xml +++ b/l10n_br_pos_nfce/views/pos_template.xml @@ -25,6 +25,10 @@