diff --git a/l10n_br_account_product/__openerp__.py b/l10n_br_account_product/__openerp__.py index 278352c7cb3d..9a265ef09dd6 100644 --- a/l10n_br_account_product/__openerp__.py +++ b/l10n_br_account_product/__openerp__.py @@ -13,6 +13,7 @@ 'depends': [ 'l10n_br_data_account', 'account_product_fiscal_classification', + 'account_payment', ], 'data': [ 'l10n_br_account_product_sequence.xml', @@ -23,6 +24,7 @@ 'data/l10n_br_account_product_data.xml', 'data/l10n_br_tax.icms_partition.csv', 'data/ir_cron.xml', + 'data/account_payment_term_data.xml', 'views/l10n_br_account_product_view.xml', 'views/l10n_br_account_view.xml', 'views/l10n_br_account_product_view.xml', @@ -35,6 +37,9 @@ 'views/account_product_fiscal_classification_view.xml', 'views/product_view.xml', 'views/res_country_view.xml', + 'views/account_payment_term_view.xml', + 'views/account_invoice_payment.xml', + 'views/account_invoice_payment_line.xml', 'wizard/l10n_br_account_nfe_export_invoice_view.xml', 'wizard/l10n_br_account_nfe_export_view.xml', 'wizard/l10n_br_account_document_status_sefaz_view.xml', diff --git a/l10n_br_account_product/data/account_payment_term_data.xml b/l10n_br_account_product/data/account_payment_term_data.xml new file mode 100644 index 000000000000..3ad680956fb5 --- /dev/null +++ b/l10n_br_account_product/data/account_payment_term_data.xml @@ -0,0 +1,28 @@ + + + + + + + + 0 + + + + 1 + + + + 1 + + + + Sem pagamento + Sem pagamento + 2 + 90 + + + + diff --git a/l10n_br_account_product/models/__init__.py b/l10n_br_account_product/models/__init__.py index e549a70f4cd8..d43442bd537a 100644 --- a/l10n_br_account_product/models/__init__.py +++ b/l10n_br_account_product/models/__init__.py @@ -11,4 +11,7 @@ from . import account from . import product from . import res_country +from . import account_invoice_term +from . import account_invoice_payment +from . import account_invoice_payment_line from . import res_config diff --git a/l10n_br_account_product/models/account.py b/l10n_br_account_product/models/account.py index 527984360a26..d2743f8583ea 100644 --- a/l10n_br_account_product/models/account.py +++ b/l10n_br_account_product/models/account.py @@ -8,14 +8,6 @@ from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -class AccountPaymentTerm(models.Model): - _inherit = 'account.payment.term' - - indPag = fields.Selection( - [('0', u'Pagamento à Vista'), ('1', u'Pagamento à Prazo'), - ('2', 'Outros')], 'Indicador de Pagamento', default='1') - - class AccountTaxTemplate(models.Model): """Implement computation method in taxes""" _inherit = 'account.tax.template' diff --git a/l10n_br_account_product/models/account_invoice.py b/l10n_br_account_product/models/account_invoice.py index a7a27333fdab..0a072bc65bbe 100644 --- a/l10n_br_account_product/models/account_invoice.py +++ b/l10n_br_account_product/models/account_invoice.py @@ -15,6 +15,7 @@ PRODUCT_FISCAL_TYPE_DEFAULT) from .product import PRODUCT_ORIGIN from openerp.addons.l10n_br_account_product.sped.nfe.validator import txt +from .account_invoice_term import FORMA_PAGAMENTO_SEM_PAGAMENTO class AccountInvoice(models.Model): @@ -22,7 +23,7 @@ class AccountInvoice(models.Model): _order = 'date_hour_invoice DESC, internal_number DESC' @api.one - @api.depends('invoice_line', 'tax_line.amount') + @api.depends('invoice_line', 'tax_line.amount', 'account_payment_line_ids') def _compute_amount(self): self.icms_base = 0.0 self.icms_base_other = 0.0 @@ -77,6 +78,14 @@ def _compute_amount(self): self.icms_st_base += line.icms_st_base self.icms_st_value += line.icms_st_value + # Calculando o grupo fatura + self.amount_payment_original = sum(p.amount_original for p in self.account_payment_line_ids) + self.amount_payment_discount = sum(p.amount_discount for p in self.account_payment_line_ids) + self.amount_payment_net = sum(p.amount_net for p in self.account_payment_line_ids) + # Calculando o troco + if self.amount_payment_net: + self.amount_change = self.amount_payment_net - self.amount_total + @api.model @api.returns('l10n_br_account.fiscal_category') def _default_fiscal_category(self): @@ -161,8 +170,9 @@ def _compute_cfops(self): 'Série NF Entrada', size=12, readonly=True, states={'draft': [('readonly', False)]}, help=u"Série do número da Nota Fiscal do Fornecedor") - nfe_version = fields.Selection( - [('1.10', '1.10'), ('2.00', '2.00'), ('3.10', '3.10')], + nfe_version = fields.Selection([ + ('1.10', '1.10'), ('2.00', '2.00'), + ('3.10', '3.10'), ('4.00', '4.00')], u'Versão NFe', readonly=True, default=_default_nfe_version, states={'draft': [('readonly', False)]}) date_hour_invoice = fields.Datetime( @@ -177,11 +187,13 @@ def _compute_cfops(self): states={'draft': [('readonly', False)]}, required=False, help=u'Indica operação com Consumidor final.') ind_pres = fields.Selection([ - ('0', u'Não se aplica'), + ('0', u'Não se aplica (por exemplo,' + u' Nota Fiscal complementar ou de ajuste)'), ('1', u'Operação presencial'), ('2', u'Operação não presencial, pela Internet'), ('3', u'Operação não presencial, Teleatendimento'), ('4', u'NFC-e em operação com entrega em domicílio'), + ('5', u'Operação presencial, fora do estabelecimento'), ('9', u'Operação não presencial, outros'), ], u'Tipo de operação', readonly=True, states={'draft': [('readonly', False)]}, required=False, @@ -389,6 +401,42 @@ def _compute_cfops(self): store=True, digits=dp.get_precision('Account'), compute='_compute_amount') + amount_change = fields.Float( + string='Troco', + store=True, + digits=dp.get_precision('Account'), + compute='_compute_amount') + account_payment_ids = fields.One2many( + string='Dados de Pagamento', + comodel_name='account.invoice.payment', + inverse_name='invoice_id', + ) + account_payment_line_ids = fields.One2many( + string='Dados da cobrança', + comodel_name='account.invoice.payment.line', + inverse_name='invoice_id', + ) + amount_payment_original = fields.Float( + string='Vr Original', + store=True, + digits=dp.get_precision('Account'), + compute='_compute_amount', + help='vOrig' + ) + amount_payment_discount = fields.Float( + string='Vr Desconto', + store=True, + digits=dp.get_precision('Account'), + compute='_compute_amount', + help='vDesc', + ) + amount_payment_net = fields.Float( + string='Vr Liquido', + store=True, + digits=dp.get_precision('Account'), + compute='_compute_amount', + help='vLiq', + ) @api.one @api.constrains('number') @@ -480,6 +528,54 @@ def nfe_check(self, cr, uid, ids, context=None): result = txt.validate(cr, uid, ids, context) return result + def action_payment(self): + """ Processa informações dos pagamentos""" + for invoice in self: + if invoice.nfe_version == '4.00': + if invoice.issuer == '0': + # Preenche a forma de pagamento padrão caso ainda não + # Tenha sido preenchida pelo usuário, mesma funcionalidade + # da venda e compras, mas lá é aplicado através de um + # onchange + # + # Não da pra fazer com onchange na invoice pois a ST e + # outros impostos são calculados somente quando salvamos. + # + if (invoice.payment_term and invoice.amount_total and + not invoice.account_payment_ids): + date_invoice = invoice.date_invoice + + if not date_invoice: + date_invoice = fields.Date.context_today(invoice) + + payment_id = invoice.account_payment_ids.new() + payment_id.payment_term_id = invoice.payment_term + payment_id.amount = invoice.amount_total + payment_id.date = date_invoice + payment_id.onchange_payment_term_id() + invoice.account_payment_ids |= payment_id + + if not invoice.account_payment_ids: + raise UserError( + _(u'A nota fiscal deve conter dados de pagamento') + ) + elif invoice.amount_change < 0: + # + # TODO: Implementar lançamento contábil com troco + # + raise UserError( + _(u'O total de pagamentos deve ser maior ou igual ' + u'ao total da nota.\n'), + _(u'Resta realizar o pagamento de %0.2f' + % invoice.amount_change) + ) + else: + for item, payment in enumerate( + invoice.account_payment_line_ids): + if payment.number: + continue + payment.number = str(item + 1).zfill(3) + @api.multi def action_number(self): # TODO: not correct fix but required a fresh values before reading it. @@ -515,6 +611,7 @@ def action_number(self): 'date_hour_invoice': date_time_invoice, 'date_in_out': date_in_out} ) + self.action_payment() return True @api.onchange('type') @@ -545,8 +642,9 @@ def onchange_fiscal_document_id(self): @api.onchange('fiscal_category_id', 'fiscal_position') def onchange_fiscal(self): + result = {'value': {}} if self.company_id and self.partner_id and self.fiscal_category_id: - result = {'value': {}} + kwargs = { 'company_id': self.company_id.id, 'partner_id': self.partner_id.id, @@ -555,7 +653,7 @@ def onchange_fiscal(self): 'context': self.env.context } result = self._fiscal_position_map(result, **kwargs) - self.update(result['value']) + self.update(result['value']) @api.multi def action_date_assign(self): @@ -648,6 +746,210 @@ def open_fiscal_document(self): result['name'] = _('NF-e') return result + @api.multi + def action_move_create(self): + """ Creates invoice related analytics and financial move lines """ + account_invoice_tax = self.env['account.invoice.tax'] + account_move = self.env['account.move'] + + for inv in self: + if not inv.journal_id.sequence_id: + raise UserError(_('Error!'), _('Please define sequence on the journal related to this invoice.')) + if not inv.invoice_line: + raise UserError(_('No Invoice Lines!'), _('Please create some invoice lines.')) + if inv.move_id: + continue + + ctx = dict(self._context, lang=inv.partner_id.lang) + + if not inv.date_invoice: + inv.with_context(ctx).write({'date_invoice': fields.Date.context_today(self)}) + date_invoice = inv.date_invoice + + company_currency = inv.company_id.currency_id + # create the analytical lines, one move line per invoice line + iml = inv._get_analytic_lines() + # check if taxes are all computed + compute_taxes = account_invoice_tax.compute(inv.with_context(lang=inv.partner_id.lang)) + inv.check_tax_lines(compute_taxes) + + # I disabled the check_total feature + if self.env.user.has_group('account.group_supplier_inv_check_total'): + if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding / 2.0): + raise UserError(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.')) + + # if inv.payment_term: + # total_fixed = total_percent = 0 + # for line in inv.payment_term.line_ids: + # if line.value == 'fixed': + # total_fixed += line.value_amount + # if line.value == 'procent': + # total_percent += line.value_amount + # total_fixed = (total_fixed * 100) / (inv.amount_total or 1.0) + # if (total_fixed + total_percent) > 100: + # raise except_orm(_('Error!'), _("Cannot create the invoice.\nThe related payment term is probably misconfigured as it gives a computed amount greater than the total invoiced amount. In order to avoid rounding issues, the latest line of your payment term must be of type 'balance'.")) + + # one move line per tax line + iml += account_invoice_tax.move_line_get(inv.id) + + if inv.type in ('in_invoice', 'in_refund'): + ref = inv.reference + else: + ref = inv.number + + diff_currency = inv.currency_id != company_currency + # create one move line for the total and possibly adjust the other lines amount + total, total_currency, iml = inv.with_context(ctx).compute_invoice_totals(company_currency, ref, iml) + + name = inv.supplier_invoice_number or inv.name or '/' + totlines = [] + + res_amount_currency = total_currency + ctx['date'] = date_invoice + + for i, t in enumerate(inv.account_payment_line_ids): + + if inv.currency_id != company_currency: + amount_currency = company_currency.with_context(ctx).compute(t.amount_net, inv.currency_id) + else: + amount_currency = False + + # last line: add the diff + res_amount_currency -= amount_currency or 0 + if i + 1 == len(totlines): + amount_currency += res_amount_currency + + iml.append({ + 'type': 'dest', + 'name': name, + 'price': t.amount_net, + 'account_id': inv.account_id.id, + 'date_maturity': t.date_due, + 'amount_currency': diff_currency and amount_currency, + 'currency_id': diff_currency and inv.currency_id.id, + 'ref': ref + '/' + t.number, + }) + + if not inv.account_payment_line_ids: + iml.append({ + 'type': 'dest', + 'name': name, + 'price': total, + 'account_id': inv.account_id.id, + 'date_maturity': inv.date_due, + 'amount_currency': diff_currency and total_currency, + 'currency_id': diff_currency and inv.currency_id.id, + 'ref': ref + }) + + + # if inv.payment_term: + # totlines = inv.with_context(ctx).payment_term.compute(total, date_invoice)[0] + # if totlines: + # res_amount_currency = total_currency + # ctx['date'] = date_invoice + # for i, t in enumerate(totlines): + # if inv.currency_id != company_currency: + # amount_currency = company_currency.with_context(ctx).compute(t[1], inv.currency_id) + # else: + # amount_currency = False + # + # # last line: add the diff + # res_amount_currency -= amount_currency or 0 + # if i + 1 == len(totlines): + # amount_currency += res_amount_currency + # + # iml.append({ + # 'type': 'dest', + # 'name': name, + # 'price': t[1], + # 'account_id': inv.account_id.id, + # 'date_maturity': t[0], + # 'amount_currency': diff_currency and amount_currency, + # 'currency_id': diff_currency and inv.currency_id.id, + # 'ref': ref, + # }) + + date = date_invoice + + part = self.env['res.partner']._find_accounting_partner(inv.partner_id) + + line = [(0, 0, self.line_get_convert(l, part.id, date)) for l in iml] + line = inv.group_lines(iml, line) + + journal = inv.journal_id.with_context(ctx) + if journal.centralisation: + raise UserError(_('User Error!'), + _('You cannot create an invoice on a centralized journal. Uncheck the centralized counterpart box in the related journal from the configuration menu.')) + + line = inv.finalize_invoice_move_lines(line) + + move_vals = { + 'ref': inv.reference or inv.supplier_invoice_number or inv.name, + 'line_id': line, + 'journal_id': journal.id, + 'date': inv.date_invoice, + 'narration': inv.comment, + 'company_id': inv.company_id.id, + } + ctx['company_id'] = inv.company_id.id + period = inv.period_id + if not period: + period = period.with_context(ctx).find(date_invoice)[:1] + if period: + move_vals['period_id'] = period.id + for i in line: + i[2]['period_id'] = period.id + + ctx['invoice'] = inv + ctx_nolang = ctx.copy() + ctx_nolang.pop('lang', None) + move = account_move.with_context(ctx_nolang).create(move_vals) + + # make the invoice point to that move + vals = { + 'move_id': move.id, + 'period_id': period.id, + 'move_name': move.name, + } + inv.with_context(ctx).write(vals) + # Pass invoice in context in method post: used if you want to get the same + # account move reference when creating the same invoice after a cancelled one: + move.post() + self._log_event() + return True + + @api.multi + def onchange_partner_id(self, type, partner_id, date_invoice=False, + payment_term=False, partner_bank_id=False, company_id=False): + result = super(AccountInvoice, self).onchange_partner_id( + type, partner_id, date_invoice, payment_term, + partner_bank_id, company_id + ) + # Sobrescreve o compartamento padrão do core de aplicar a forma de pagamento + # verificar metodo: onchange_fiscal_payment_term + if result.get('value'): + result['value'].pop('payment_term', None) + return result + + @api.onchange('fiscal_category_id', 'fiscal_position_id') + def onchange_fiscal_payment_term(self): + """ Quando a posição fiscal for preenchida temos os dados da categoria e do partner + e então podemos tomar decidir qual o payment_term adequado + :return: + """ + partner_id = self.partner_id + + payment_term = self.env['account.payment.term'] + # Busca do partner + if partner_id and partner_id.property_payment_term: + payment_term = partner_id.property_payment_term + # Sobrecreve a opção do parceiro caso a categoria fiscal tenha uma opção definida + + if self.fiscal_category_id and self.fiscal_category_id.account_payment_term_id: + payment_term = self.fiscal_category_id.account_payment_term_id + self.payment_term = payment_term + class AccountInvoiceLine(models.Model): _inherit = 'account.invoice.line' diff --git a/l10n_br_account_product/models/account_invoice_payment.py b/l10n_br_account_product/models/account_invoice_payment.py new file mode 100644 index 000000000000..410184b8587f --- /dev/null +++ b/l10n_br_account_product/models/account_invoice_payment.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from __future__ import division, print_function, unicode_literals + +from openerp import api, fields, models, _ + +from .account_invoice_term import ( + FORMA_PAGAMENTO_SEM_PAGAMENTO, +) + + + +class AccountInvoicePayment(models.Model): + _name = b'account.invoice.payment' + _description = 'Dados de pagamento' + _order = 'invoice_id, sequence, payment_term_id' + + sequence = fields.Integer( + default=10, + ) + date = fields.Date( + string='Data de referência', + readonly=True, + ) + payment_term_id = fields.Many2one( + comodel_name='account.payment.term', + string='Condição de pagamento', + ondelete='restrict', + domain=[('forma_pagamento', '!=', False)], + ) + currency_id = fields.Many2one( + comodel_name='res.currency', + string='Currency', + ) + amount = fields.Float( + string='Amount', + ) + autorizacao = fields.Char( + string='Autorização nº', + size=20, + ) + nsu = fields.Char( + string='NSU', + help='Numero sequencial unico', + ) + ind_forma_pagamento = fields.Selection( + related='payment_term_id.ind_forma_pagamento', + store=True, + ) + forma_pagamento = fields.Selection( + related='payment_term_id.forma_pagamento', + store=True, + ) + card_brand = fields.Selection( + related='payment_term_id.card_brand', + store=True, + ) + card_integration = fields.Selection( + related='payment_term_id.card_integration', + store=True, + ) + partner_id = fields.Many2one( + related='payment_term_id.partner_id', + store=True, + ondelete='restrict', + ) + cnpj_cpf = fields.Char( + string='CNPJ/CPF', + size=18, + related='partner_id.cnpj_cpf', + readonly=True, + ) + invoice_id = fields.Many2one( + comodel_name='account.invoice', + string='Invoice', + ondelete='set null', # Allow use the same model in sale and purchase + ) + item_ids = fields.One2many( + comodel_name='account.invoice.payment.line', + inverse_name='payment_id', + string='Duplicatas', + ) + + def _set_parent(self, field_parent, field_parent_id): + date = False + + if field_parent == 'invoice_id': + date = field_parent_id.date_invoice + self.invoice_id = field_parent_id + return date or fields.Date.context_today(field_parent_id) + + @api.onchange('payment_term_id', 'amount', 'item_ids', 'date') + def onchange_payment_term_id(self): + + if not (self.payment_term_id and self.amount): + return + + field_parent = self.env.context.get('field_parent') + + if field_parent: + field_parent_id = getattr(self, field_parent) + self.date = self._set_parent(field_parent, field_parent_id)[:10] + + pterm_list = self.payment_term_id.compute(self.amount, self.date)[0] + + self.forma_pagameto = self.payment_term_id.forma_pagamento + + item_ids = [ + (5, 0, {}) + ] + + for term in pterm_list: + item_ids.append( + (0, 0, { + 'payment_id': self.id, + 'date_due': term[0], + 'amount_original': term[1], + 'amount_discount': 0.00, + 'amount_net': term[1], + }) + ) + self.item_ids = item_ids + + def default_payment_term(self, payment_term_id, amount=0.00, object=False, date=False): + + if not (payment_term_id.forma_pagamento == + FORMA_PAGAMENTO_SEM_PAGAMENTO): + + values = { + 'payment_term_id': payment_term_id.id, + 'amount': amount, + 'date': fields.Date.today(self), + } + term = self.create(values) + term._onchange_payment_term_id() + + return term diff --git a/l10n_br_account_product/models/account_invoice_payment_line.py b/l10n_br_account_product/models/account_invoice_payment_line.py new file mode 100644 index 000000000000..403b47815fc4 --- /dev/null +++ b/l10n_br_account_product/models/account_invoice_payment_line.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from __future__ import division, print_function, unicode_literals + +from openerp import api, fields, models, _ + + +class AccountInvoicePaymentLine(models.Model): + + _name = b'account.invoice.payment.line' + _description = 'Dados de cobrança' + _order = 'invoice_id, date_due, payment_id' + _rec_name = 'number' + + payment_id = fields.Many2one( + comodel_name='account.invoice.payment', + string='Pagamento', + ondelete='cascade', + ) + invoice_id = fields.Many2one( + comodel_name='account.invoice', + related='payment_id.invoice_id', + store=True, + ) + number = fields.Char( + string='Número', + size=60, + readonly=True, + ) + date_due = fields.Date( + string='Data de vencimento', + required=True, + ) + currency_id = fields.Many2one( + comodel_name='res.currency', + string='Currency', + # related='payment_id.currency_id', + store=True, + ) + amount_original = fields.Float( + string='Vr Original', + digits=(18, 2), + required=True, + ) + amount_discount = fields.Float( + string='Vr desconto', + digits=(18, 2), + required=True, + ) + amount_net = fields.Float( + string='Vr Liquido', + digits=(18, 2), + required=True, + ) diff --git a/l10n_br_account_product/models/account_invoice_term.py b/l10n_br_account_product/models/account_invoice_term.py new file mode 100644 index 000000000000..de2e9addc735 --- /dev/null +++ b/l10n_br_account_product/models/account_invoice_term.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from __future__ import division, print_function, unicode_literals + +from openerp import api, fields, models, _ + +BANDEIRA_CARTAO = ( + ('01', 'Visa'), + ('02', 'Mastercard'), + ('03', 'American Express'), + ('04', 'Sorocred'), + ('05', 'Diners Club'), + ('06', 'Elo'), + ('07', 'Hipercard'), + ('08', 'Aura'), + ('09', 'Cabal'), + ('99', 'Outros'), +) +BANDEIRA_CARTAO_DICT = dict(BANDEIRA_CARTAO) + +BANDEIRA_CARTAO_VISA = '01' +BANDEIRA_CARTAO_MASTERCARD = '02' +BANDEIRA_CARTAO_AMERICAN_EXPRESS = '03' +BANDEIRA_CARTAO_SOROCRED = '04' +BANDEIRA_CARTAO_DINERS_CLUB = '05' +BANDEIRA_CARTAO_ELO = '06' +BANDEIRA_CARTAO_HIPERCARD = '07' +BANDEIRA_CARTAO_AURA = '08' +BANDEIRA_CARTAO_CABAL = '09' +BANDEIRA_CARTAO_OUTROS = '99' + +INTEGRACAO_CARTAO = ( + ('1', 'Integrado'), + ('2', 'Não integrado'), +) +INTEGRACAO_CARTAO_INTEGRADO = '1' +INTEGRACAO_CARTAO_NAO_INTEGRADO = '2' + +FORMA_PAGAMENTO = ( + ('01', 'Dinheiro'), + ('02', 'Cheque'), + ('03', 'Cartão de crédito'), + ('04', 'Cartão de débito'), + ('05', 'Crédito na loja'), + ('10', 'Vale alimentação'), + ('11', 'Vale refeição'), + ('12', 'Vale presente'), + ('13', 'Vale combustível'), + ('14', 'Duplicata mercantil'), + ('15', 'Boleto bancário'), + ('90', 'Sem pagamento'), + ('99', 'Outros'), +) + +FORMA_PAGAMENTO_DICT = dict(FORMA_PAGAMENTO) + +FORMA_PAGAMENTO_DINHEIRO = '01' +FORMA_PAGAMENTO_CHEQUE = '02' +FORMA_PAGAMENTO_CARTAO_CREDITO = '03' +FORMA_PAGAMENTO_CARTAO_DEBITO = '04' +FORMA_PAGAMENTO_CREDITO_LOJA = '05' +FORMA_PAGAMENTO_VALE_ALIMENTACAO = '10' +FORMA_PAGAMENTO_VALE_REFEICAO = '11' +FORMA_PAGAMENTO_VALE_PRESENTE = '12' +FORMA_PAGAMENTO_VALE_COMBUSTIVEL = '13' +FORMA_PAGAMENTO_DUPLICATA_MERCANTIL = '14' +FORMA_PAGAMENTO_BOLETO = '15' +FORMA_PAGAMENTO_SEM_PAGAMENTO = '90' +FORMA_PAGAMENTO_OUTROS = '99' + +FORMA_PAGAMENTO_CARTOES = ( + FORMA_PAGAMENTO_CARTAO_CREDITO, + FORMA_PAGAMENTO_CARTAO_DEBITO, +) + +IND_FORMA_PAGAMENTO = ( + ('0', 'À vista'), + ('1', 'A prazo'), + ('2', 'Outros/sem pagamento'), +) +IND_FORMA_PAGAMENTO_DICT = dict(IND_FORMA_PAGAMENTO) + + +class AccountPaymentTerm(models.Model): + + _inherit = b'account.payment.term' + _order = 'display_name' + + @api.depends('forma_pagamento', 'name') + def _compute_display_name(self): + return super(AccountPaymentTerm, self)._compute_display_name() + + display_name = fields.Char( + string='Condição da pagamento', + store=True, + compute='_compute_display_name', + ) + # + # Campos para NF-e e SPED + # + ind_forma_pagamento = fields.Selection( + selection=IND_FORMA_PAGAMENTO, + string='Indicador da Forma de Pagamento', + ) + forma_pagamento = fields.Selection( + selection=FORMA_PAGAMENTO, + string='Forma de pagamento', + default=FORMA_PAGAMENTO_OUTROS, + required=True, + ) + card_brand = fields.Selection( + selection=BANDEIRA_CARTAO, + string='Bandeira do cartão', + ) + card_integration = fields.Selection( + selection=INTEGRACAO_CARTAO, + string='Integração do cartão', + default=INTEGRACAO_CARTAO_NAO_INTEGRADO, + ) + partner_id = fields.Many2one( + comodel_name='res.partner', + string='Operadora do cartão', + ondelete='restrict', + ) + + @api.multi + def name_get(self): + res = [] + + for payment_term in self: + display_name = '' + if payment_term.forma_pagamento in FORMA_PAGAMENTO_CARTOES: + if payment_term.forma_pagamento == \ + FORMA_PAGAMENTO_CARTAO_CREDITO: + display_name += '[Crédito ' + elif payment_term.forma_pagamento == \ + FORMA_PAGAMENTO_CARTAO_DEBITO: + display_name += '[Débito ' + + display_name += \ + BANDEIRA_CARTAO_DICT[payment_term.card_brand] + display_name += '] ' + + elif payment_term.forma_pagamento: + display_name += '[' + display_name += \ + FORMA_PAGAMENTO_DICT[payment_term.forma_pagamento] + display_name += '] ' + + display_name += payment_term.name + res.append((payment_term.id, display_name)) + + return res + + @api.model + def name_search(self, name, args=None, operator='ilike', limit=100): + args = args or [] + recs = self.browse() + + if not recs: + recs = self.search( + [ + ('display_name', operator, name), + ] + args, limit=limit) + return recs.name_get() diff --git a/l10n_br_account_product/models/l10n_br_account.py b/l10n_br_account_product/models/l10n_br_account.py index 9ecad816d912..44795eaabfcb 100644 --- a/l10n_br_account_product/models/l10n_br_account.py +++ b/l10n_br_account_product/models/l10n_br_account.py @@ -19,6 +19,10 @@ class L10nBrAccountFiscalCategory(models.Model): PRODUCT_FISCAL_TYPE, 'Tipo Fiscal', required=True, default=PRODUCT_FISCAL_TYPE_DEFAULT) + account_payment_term_id = fields.Many2one( + comodel_name='account.payment.term', + string=u'Condição de pagamento' + ) class L10nBrAccountDocumentSerie(models.Model): _inherit = 'l10n_br_account.document.serie' diff --git a/l10n_br_account_product/models/res_company.py b/l10n_br_account_product/models/res_company.py index b6915c57cd21..a5d61a0305ee 100644 --- a/l10n_br_account_product/models/res_company.py +++ b/l10n_br_account_product/models/res_company.py @@ -28,9 +28,10 @@ def _compute_taxes(self): 'document_serie_product_id', 'Série de Documentos Fiscais', domain="[('company_id', '=', active_id),('active','=',True)," "('fiscal_type','=','product')]") - nfe_version = fields.Selection( - [('1.10', '1.10'), ('2.00', '2.00'), ('3.10', '3.10')], u'Versão NFe', - required=True, default='3.10') + nfe_version = fields.Selection([ + ('1.10', '1.10'), ('2.00', '2.00'), + ('3.10', '3.10'), ('4.00', '4.00')], + u'Versão NFe', required=True, default='4.00') nfe_import_folder = fields.Char('Pasta de Importação', size=254) nfe_export_folder = fields.Char('Pasta de Exportação', size=254) nfe_backup_folder = fields.Char('Pasta de Backup', size=254) diff --git a/l10n_br_account_product/security/ir.model.access.csv b/l10n_br_account_product/security/ir.model.access.csv index 87528af2a0db..3d0d514e10cd 100644 --- a/l10n_br_account_product/security/ir.model.access.csv +++ b/l10n_br_account_product/security/ir.model.access.csv @@ -38,3 +38,11 @@ "l10n_br_tax_icms_partition_user","l10n_br_tax.icms_partition","model_l10n_br_tax_icms_partition","account.group_account_invoice",1,0,0,0 "l10n_br_account_product_cest_user","l10n_br_account_product.cest","model_l10n_br_account_product_cest","base.group_user",1,0,0,0 "l10n_br_account_product_cest_manager","l10n_br_account_product.cest","model_l10n_br_account_product_cest","account.group_account_manager",1,1,1,1 +"access_account_invoice_payments_base_user","account.invoice.payment.base.user","model_account_invoice_payment","base.group_user",1,0,0,0 +"access_account_invoice_payments_user","account.invoice.payment.user","model_account_invoice_payment","account.group_account_user",1,1,1,1 +"access_account_invoice_payments_invoice","account.invoice.payment.invoice","model_account_invoice_payment","account.group_account_invoice",1,1,1,1 +"access_account_invoice_payments_manager","account.invoice.payment.manager","model_account_invoice_payment","account.group_account_manager",1,1,1,1 +"access_account_invoice_payment_line_base_user","account.invoice.payment.line.base.user","model_account_invoice_payment_line","base.group_user",1,0,0,0 +"access_account_invoice_payment_line_user","account.invoice.payment.line.user","model_account_invoice_payment_line","account.group_account_user",1,1,1,1 +"access_account_invoice_payment_line_invoice","account.invoice.payment.line.invoice","model_account_invoice_payment_line","account.group_account_invoice",1,1,1,1 +"access_account_invoice_payment_line_manager","account.invoice.payment.line.manager","model_account_invoice_payment_line","account.group_account_manager",1,1,1,1 diff --git a/l10n_br_account_product/sped/nfe/document.py b/l10n_br_account_product/sped/nfe/document.py index 415032329fc6..5e3bc3f85736 100644 --- a/l10n_br_account_product/sped/nfe/document.py +++ b/l10n_br_account_product/sped/nfe/document.py @@ -12,6 +12,11 @@ from openerp.addons.l10n_br_account.sped.document import FiscalDocument from openerp.addons.l10n_br_base.tools.misc import punctuation_rm +from ...models.account_invoice_term import ( + FORMA_PAGAMENTO_CARTOES, + FORMA_PAGAMENTO_SEM_PAGAMENTO, +) + class NFe200(FiscalDocument): def __init__(self): @@ -69,17 +74,15 @@ def _serializer(self, cr, uid, ids, nfe_environment, context=None): self.nfe.infNFe.det.append(self.det) - if invoice.journal_id.revenue_expense: - for move_line in invoice.move_line_receivable_id: - self.dup = self._get_Dup() - self._encashment_data(invoice, move_line) - self.nfe.infNFe.cobr.dup.append(self.dup) + self._encashment_data(invoice, self.nfe.infNFe.cobr) try: self._carrier_data(invoice) except AttributeError: pass + self._details_pag(invoice, self.nfe.infNFe.pag) + self.vol = self._get_Vol() self._weight_data(invoice) self.nfe.infNFe.transp.vol.append(self.vol) @@ -226,18 +229,24 @@ def _emmiter(self, invoice, company): self.nfe.infNFe.emit.CNPJ.valor = punctuation_rm( invoice.company_id.partner_id.cnpj_cpf) - self.nfe.infNFe.emit.xNome.valor = ( - invoice.company_id.partner_id.legal_name[:60]) + self.nfe.infNFe.emit.xNome.valor = (normalize( + 'NFKD', unicode( + invoice.company_id.partner_id.legal_name[:60])).encode( + 'ASCII', 'ignore')) self.nfe.infNFe.emit.xFant.valor = invoice.company_id.partner_id.name - self.nfe.infNFe.emit.enderEmit.xLgr.valor = company.street or '' + self.nfe.infNFe.emit.enderEmit.xLgr.valor = (normalize( + 'NFKD', unicode(company.street or '')).encode('ASCII', 'ignore')) self.nfe.infNFe.emit.enderEmit.nro.valor = company.number or '' - self.nfe.infNFe.emit.enderEmit.xCpl.valor = company.street2 or '' - self.nfe.infNFe.emit.enderEmit.xBairro.valor = ( - company.district or 'Sem Bairro') + self.nfe.infNFe.emit.enderEmit.xCpl.valor = (normalize( + 'NFKD', unicode(company.street2 or '')).encode('ASCII', 'ignore')) + self.nfe.infNFe.emit.enderEmit.xBairro.valor = (normalize( + 'NFKD', unicode( + company.district or 'Sem Bairro')).encode('ASCII', 'ignore')) self.nfe.infNFe.emit.enderEmit.cMun.valor = '%s%s' % ( company.state_id.ibge_code, company.l10n_br_city_id.ibge_code) - self.nfe.infNFe.emit.enderEmit.xMun.valor = ( - company.l10n_br_city_id.name or '') + self.nfe.infNFe.emit.enderEmit.xMun.valor = (normalize( + 'NFKD', unicode( + company.l10n_br_city_id.name or '')).encode('ASCII', 'ignore')) self.nfe.infNFe.emit.enderEmit.UF.valor = company.state_id.code or '' self.nfe.infNFe.emit.enderEmit.CEP.valor = punctuation_rm( company.zip or '') @@ -281,8 +290,10 @@ def _receiver(self, invoice, company, nfe_environment): address_invoice_city_code = '9999999' else: address_invoice_state_code = invoice.partner_id.state_id.code - address_invoice_city = ( - invoice.partner_id.l10n_br_city_id.name or '') + address_invoice_city = (normalize( + 'NFKD', unicode( + invoice.partner_id.l10n_br_city_id.name or '')).encode( + 'ASCII', 'ignore')) address_invoice_city_code = ('%s%s') % ( invoice.partner_id.state_id.ibge_code, invoice.partner_id.l10n_br_city_id.ibge_code) @@ -294,8 +305,10 @@ def _receiver(self, invoice, company, nfe_environment): self.nfe.infNFe.dest.xNome.valor = ( 'NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL') else: - self.nfe.infNFe.dest.xNome.valor = ( - invoice.partner_id.legal_name[:60] or '') + self.nfe.infNFe.dest.xNome.valor = (normalize( + 'NFKD', unicode( + invoice.partner_id.legal_name[:60] or '') + ).encode('ASCII', 'ignore')) if invoice.partner_id.is_company: self.nfe.infNFe.dest.IE.valor = punctuation_rm( @@ -313,14 +326,17 @@ def _receiver(self, invoice, company, nfe_environment): self.nfe.infNFe.dest.indIEDest.valor = \ invoice.partner_id.partner_fiscal_type_id.ind_ie_dest - self.nfe.infNFe.dest.enderDest.xLgr.valor = ( - invoice.partner_id.street or '') + self.nfe.infNFe.dest.enderDest.xLgr.valor = (normalize( + 'NFKD', unicode( + invoice.partner_id.street or '')).encode('ASCII', 'ignore')) self.nfe.infNFe.dest.enderDest.nro.valor = ( invoice.partner_id.number or '') - self.nfe.infNFe.dest.enderDest.xCpl.valor = ( - invoice.partner_id.street2 or '') - self.nfe.infNFe.dest.enderDest.xBairro.valor = ( - invoice.partner_id.district or 'Sem Bairro') + self.nfe.infNFe.dest.enderDest.xCpl.valor = (normalize( + 'NFKD', unicode( + invoice.partner_id.street2 or '')).encode('ASCII', 'ignore')) + self.nfe.infNFe.dest.enderDest.xBairro.valor = (normalize( + 'NFKD', unicode( + invoice.partner_id.district or 'Sem Bairro')).encode('ASCII', 'ignore')) self.nfe.infNFe.dest.enderDest.cMun.valor = address_invoice_city_code self.nfe.infNFe.dest.enderDest.xMun.valor = address_invoice_city self.nfe.infNFe.dest.enderDest.UF.valor = address_invoice_state_code @@ -341,11 +357,16 @@ def _details(self, invoice, invoice_line, index): self.det.prod.cProd.valor = invoice_line.product_id.code or '' self.det.prod.cEAN.valor = invoice_line.product_id.ean13 or '' self.det.prod.cEANTrib.valor = invoice_line.product_id.ean13 or '' - self.det.prod.xProd.valor = ( - invoice_line.product_id.name[:120] or '') + self.det.prod.xProd.valor = (normalize( + 'NFKD', unicode( + invoice_line.product_id.name[:120] or '') + ).encode('ASCII', 'ignore')) else: self.det.prod.cProd.valor = invoice_line.code or '' - self.det.prod.xProd.valor = invoice_line.name[:120] or '' + self.det.prod.xProd.valor = (normalize( + 'NFKD', unicode( + invoice_line.name[:120] or '') + ).encode('ASCII', 'ignore')) self.det.prod.NCM.valor = punctuation_rm( invoice_line.fiscal_classification_id.code or '')[:8] @@ -527,9 +548,26 @@ def _addition(self, invoice_line_di): self.di_line.vDescDI.valor = str( "%.2f" % invoice_line_di.amount_discount) - def _encashment_data(self, invoice, move_line): + def _encashment_data(self, invoice, cobr): """Dados de Cobrança""" + if invoice.journal_id.revenue_expense: + for move_line in invoice.move_line_receivable_id: + + if invoice.type in ('out_invoice', 'in_refund'): + value = move_line.debit + else: + value = move_line.credit + + dup = self._get_Dup() + + dup.nDup.valor = move_line.name + dup.dVenc.valor = (move_line.date_maturity or + invoice.date_due or + invoice.date_invoice) + dup.vDup.valor = str("%.2f" % value) + cobr.dup.append(dup) + if invoice.type in ('out_invoice', 'in_refund'): value = move_line.debit else: @@ -824,3 +862,117 @@ def _get_AutXML(self): raise UserError( _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) return AutXML_310() + + +class NFe400(NFe310): + def __init__(self): + super(NFe400, self).__init__() + + def _details_pag(self, invoice, pag): + + for pagamento in invoice.account_payment_ids: + pag.detPag.append(self._payment_date(pagamento)) + + pag.vTroco.valor = str("%.2f" % invoice.amount_change) + + def _payment_date(self, pagamento): + + pag = self._get_DetPag() + + # Somente no/ PL_009_V4_2016_002_v160b + # pag.indPag.valor = pagamento.payment_term_id.ind_forma_pagamento or '' + + pag.tPag.valor = pagamento.forma_pagamento + pag.vPag.valor = str(pagamento.amount) + + if pagamento.forma_pagamento in FORMA_PAGAMENTO_CARTOES: + pag.card.tpIntegra.valor = pagamento.card_integration + pag.card.CNPJ.valor = punctuation_rm(pagamento.cnpj_cpf or '') + pag.card.tBand.valor = pagamento.card_brand + pag.card.cAut.valor = pagamento.autorizacao + + return pag + + def _encashment_data(self, invoice, cobr): + """Dados de Cobrança""" + + if FORMA_PAGAMENTO_SEM_PAGAMENTO in invoice.account_payment_ids.mapped('forma_pagamento'): + return + + cobr.fat.nFat.valor = invoice.number + cobr.fat.vOrig.valor = str("%.2f" % invoice.amount_payment_original) + cobr.fat.vDesc.valor = str("%.2f" % invoice.amount_payment_discount) + cobr.fat.vLiq.valor = str("%.2f" % invoice.amount_payment_net) + + for payment_line in invoice.account_payment_line_ids: + dup = self._get_Dup() + dup.nDup.valor = payment_line.number + dup.dVenc.valor = payment_line.date_due + dup.vDup.valor = str("%.2f" % payment_line.amount_net) + cobr.dup.append(dup) + + def get_NFe(self): + try: + from pysped.nfe.leiaute import NFe_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + + return NFe_400() + + def _get_NFRef(self): + try: + from pysped.nfe.leiaute import NFRef_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + + return NFRef_400() + + def _get_Det(self): + try: + from pysped.nfe.leiaute import Det_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + return Det_400() + + def _get_Dup(self): + try: + from pysped.nfe.leiaute import Dup_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + return Dup_400() + + def _get_DI(self): + try: + from pysped.nfe.leiaute import DI_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + return DI_400() + + def _get_Pag(self): + try: + from pysped.nfe.leiaute import Pag_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + return Pag_400() + + def _get_DetPag(self): + try: + from pysped.nfe.leiaute import DetPag_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + return DetPag_400() + + def _get_AutXML(self): + try: + from pysped.nfe.leiaute import AutXML_400 + except ImportError: + raise UserError( + _(u'Erro!'), _(u"Biblioteca PySPED não instalada!")) + return AutXML_400() diff --git a/l10n_br_account_product/sped/nfe/serializer/xml.py b/l10n_br_account_product/sped/nfe/serializer/xml.py index e1b2de635c7b..577cf3633857 100644 --- a/l10n_br_account_product/sped/nfe/serializer/xml.py +++ b/l10n_br_account_product/sped/nfe/serializer/xml.py @@ -4,12 +4,15 @@ from ..document import NFe200 from ..document import NFe310 +from ..document import NFe400 def nfe_export(cr, uid, ids, nfe_environment='1', nfe_version='2.00', context=None): - if nfe_version == '3.10': + if nfe_version == '4.00': + NFe = NFe400() + elif nfe_version == '3.10': NFe = NFe310() else: NFe = NFe200() diff --git a/l10n_br_account_product/views/account_invoice_payment.xml b/l10n_br_account_product/views/account_invoice_payment.xml new file mode 100644 index 000000000000..06cb7da5cb00 --- /dev/null +++ b/l10n_br_account_product/views/account_invoice_payment.xml @@ -0,0 +1,51 @@ + + + + + + + + account.invoice.payment.tree (in l10n_br_account) + account.invoice.payment + + + + + + + + + + + + account.invoice.payment.form (in l10n_br_account) + account.invoice.payment + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+
diff --git a/l10n_br_account_product/views/account_invoice_payment_line.xml b/l10n_br_account_product/views/account_invoice_payment_line.xml new file mode 100644 index 000000000000..f9d6b85c287f --- /dev/null +++ b/l10n_br_account_product/views/account_invoice_payment_line.xml @@ -0,0 +1,24 @@ + + + + + + + + account.invoice.payment.line.tree (in l10n_br_account) + account.invoice.payment.line + + + + + + + + + + + + + + diff --git a/l10n_br_account_product/views/account_invoice_view.xml b/l10n_br_account_product/views/account_invoice_view.xml old mode 100755 new mode 100644 diff --git a/l10n_br_account_product/views/account_payment_term_view.xml b/l10n_br_account_product/views/account_payment_term_view.xml new file mode 100644 index 000000000000..72c5c87b43fb --- /dev/null +++ b/l10n_br_account_product/views/account_payment_term_view.xml @@ -0,0 +1,50 @@ + + + + + + + + account.payment.tree.form (in l10n_br_account_product) + account.payment.term + + + + + + + + + account.payment.term.search (in l10n_br_account_product) + account.payment.term + + + + + + + + + + account.payment.term.form (in l10n_br_account_product) + account.payment.term + + + + + + + + + + + + + + + + + + + diff --git a/l10n_br_account_product/views/account_view.xml b/l10n_br_account_product/views/account_view.xml index 0e9f18e6773b..349fb9ad47ca 100644 --- a/l10n_br_account_product/views/account_view.xml +++ b/l10n_br_account_product/views/account_view.xml @@ -3,17 +3,6 @@ - - l10n_br_account_product.payment.term.form - account.payment.term - - - - - - - - l10n_br_account_product.tax.template.form account.tax.template diff --git a/l10n_br_account_product/views/l10n_br_account_view.xml b/l10n_br_account_product/views/l10n_br_account_view.xml index 07295578f8a8..07e523c3bda0 100644 --- a/l10n_br_account_product/views/l10n_br_account_view.xml +++ b/l10n_br_account_product/views/l10n_br_account_view.xml @@ -10,6 +10,10 @@ 0 + + + + diff --git a/l10n_br_account_product/views/nfe/account_invoice_nfe_view.xml b/l10n_br_account_product/views/nfe/account_invoice_nfe_view.xml index 151525ed4679..878db8ecd323 100755 --- a/l10n_br_account_product/views/nfe/account_invoice_nfe_view.xml +++ b/l10n_br_account_product/views/nfe/account_invoice_nfe_view.xml @@ -171,15 +171,29 @@ - - + + + - + + + + + + + + + + + + + + diff --git a/l10n_br_sale_product/__openerp__.py b/l10n_br_sale_product/__openerp__.py index f49e0de24081..25c672e52919 100644 --- a/l10n_br_sale_product/__openerp__.py +++ b/l10n_br_sale_product/__openerp__.py @@ -14,6 +14,8 @@ 'l10n_br_account_product', ], 'data': [ + 'views/account_invoice_payment_line.xml', + 'views/account_invoice_payment.xml', 'views/sale_view.xml', 'views/res_company_view.xml', 'data/l10n_br_sale_product_data.xml', diff --git a/l10n_br_sale_product/models/__init__.py b/l10n_br_sale_product/models/__init__.py index 48c4bea2f44a..84080487d024 100644 --- a/l10n_br_sale_product/models/__init__.py +++ b/l10n_br_sale_product/models/__init__.py @@ -4,3 +4,5 @@ from . import res_company from . import sale +from . import account_invoice_payment +from . import account_invoice_payment_line diff --git a/l10n_br_sale_product/models/account_invoice_payment.py b/l10n_br_sale_product/models/account_invoice_payment.py new file mode 100644 index 000000000000..43c46735ec8a --- /dev/null +++ b/l10n_br_sale_product/models/account_invoice_payment.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models, _ + + +class AccountInvoicePayment(models.Model): + + _inherit = 'account.invoice.payment' + + sale_id = fields.Many2one( + comodel_name='sale.order', + string='Sale Order', + ondelete='set null', + ) + + def _set_parent(self, field_parent, field_parent_id): + date = super(AccountInvoicePayment, self)._set_parent( + field_parent, field_parent_id + ) + if field_parent == 'sale_id': + date = field_parent_id.date_order + self.sale_id = field_parent_id + return date diff --git a/l10n_br_sale_product/models/account_invoice_payment_line.py b/l10n_br_sale_product/models/account_invoice_payment_line.py new file mode 100644 index 000000000000..20ddbc0b704a --- /dev/null +++ b/l10n_br_sale_product/models/account_invoice_payment_line.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, fields, models, _ + + +class AccountInvoicePaymentLine(models.Model): + + _inherit = 'account.invoice.payment.line' + + sale_id = fields.Many2one( + comodel_name='sale.order', + related='payment_id.sale_id', + store=True, + ) diff --git a/l10n_br_sale_product/models/res_company.py b/l10n_br_sale_product/models/res_company.py index 88f630a8f164..77bed8907105 100644 --- a/l10n_br_sale_product/models/res_company.py +++ b/l10n_br_sale_product/models/res_company.py @@ -9,11 +9,13 @@ class ResCompany(models.Model): _inherit = 'res.company' default_ind_pres = fields.Selection([ - ('0', u'Não se aplica'), + ('0', u'Não se aplica (por exemplo,' + u' Nota Fiscal complementar ou de ajuste)'), ('1', u'Operação presencial'), ('2', u'Operação não presencial, pela Internet'), ('3', u'Operação não presencial, Teleatendimento'), ('4', u'NFC-e em operação com entrega em domicílio'), + ('5', u'Operação presencial, fora do estabelecimento'), ('9', u'Operação não presencial, outros'), ], u'Tipo de operação', help=u'Indicador de presença do comprador no \ diff --git a/l10n_br_sale_product/models/sale.py b/l10n_br_sale_product/models/sale.py index f3a5ff0521c1..e54cf9164919 100644 --- a/l10n_br_sale_product/models/sale.py +++ b/l10n_br_sale_product/models/sale.py @@ -138,14 +138,16 @@ def _set_amount_costs(self): return True ind_pres = fields.Selection([ - ('0', u'Não se aplica'), + ('0', u'Não se aplica (por exemplo,' + u' Nota Fiscal complementar ou de ajuste)'), ('1', u'Operação presencial'), ('2', u'Operação não presencial, pela Internet'), ('3', u'Operação não presencial, Teleatendimento'), ('4', u'NFC-e em operação com entrega em domicílio'), - ('9', u'Operação não presencial, outros')], u'Tipo de operação', - readonly=True, states={'draft': [('readonly', False)]}, - required=False, + ('5', u'Operação presencial, fora do estabelecimento'), + ('9', u'Operação não presencial, outros') + ], u'Tipo de operação', readonly=True, + states={'draft': [('readonly', False)]}, required=False, help=u'Indicador de presença do comprador no estabelecimento \ comercial no momento da operação.', default=_default_ind_pres) amount_untaxed = fields.Float( @@ -183,6 +185,16 @@ def _set_amount_costs(self): compute=_get_costs_value, inverse=_set_amount_insurance, string='Seguro', default=0.00, digits=dp.get_precision('Account'), readonly=True, states={'draft': [('readonly', False)]}) + account_payment_ids = fields.One2many( + string='Dados de Pagamento', + comodel_name='account.invoice.payment', + inverse_name='sale_id', + ) + account_payment_line_ids = fields.One2many( + string='Dados da cobrança', + comodel_name='account.invoice.payment.line', + inverse_name='sale_id', + ) def _fiscal_comment(self, cr, uid, order, context=None): fp_comment = [] @@ -209,9 +221,55 @@ def _make_invoice(self, order, lines): self.env.user.company_id.id) context.update( {'fiscal_document_code': company.product_invoice_id.code}) - return super(SaleOrder, + inv_id = super(SaleOrder, self.with_context(context))._make_invoice(order, lines) + # TODO: Verificar o que deve ser feito com multiplas faturas para uma série de pagamentos + # Devemos mesclar as parcelas? E se o faturamento não for 100%? + # Mantemos a proporção / condição e refazemos as parcelas? + order.account_payment_ids.write({'invoice_id': inv_id}) + return inv_id + + @api.multi + def onchange_partner_id(self, part): + result = super(SaleOrder, self).onchange_partner_id(part) + # Sobrescreve o compartamento padrão do core de aplicar a forma de pagamento + # verificar metodo: onchange_fiscal_payment_term + if result.get('value'): + result['value'].pop('payment_term', None) + return result + + @api.onchange('fiscal_category_id', 'fiscal_position_id') + def onchange_fiscal_payment_term(self): + """ Quando a posição fiscal for preenchida temos os dados da categoria e do partner + e então podemos tomar decidir qual o payment_term adequado + :return: + """ + partner_id = self.partner_invoice_id or self.partner_id + + payment_term = self.env['account.payment.term'] + # Busca do partner + if partner_id and partner_id.property_payment_term: + payment_term = partner_id.property_payment_term + # Sobrecreve a opção do parceiro caso a categoria fiscal tenha uma opção definida + + if self.fiscal_category_id and self.fiscal_category_id.account_payment_term_id: + payment_term = self.fiscal_category_id.account_payment_term_id + self.payment_term = payment_term + + @api.onchange('payment_term', 'amount_total', 'date_order') + def onchange_default_payment_term(self): + """ Preenche a forma de pagamento padrão mantendo compatibilidade com o campo do core + :return: + """ + if (self.payment_term and self.amount_total and + self.date_order and not self.account_payment_ids): + payment_id = self.account_payment_ids.new() + payment_id.payment_term_id = self.payment_term + payment_id.amount = self.amount_total + payment_id.date = self.date_order[:10] + payment_id.onchange_payment_term_id() + self.account_payment_ids |= payment_id class SaleOrderLine(models.Model): diff --git a/l10n_br_sale_product/views/sale_view.xml b/l10n_br_sale_product/views/sale_view.xml index c5d1a74decf6..b2ee8cf529c7 100644 --- a/l10n_br_sale_product/views/sale_view.xml +++ b/l10n_br_sale_product/views/sale_view.xml @@ -16,6 +16,9 @@ + + 1 + @@ -33,6 +36,17 @@ + + + + + + + + + + +