# -*- coding: utf-8 -*-
# Copyright (C) 2009 Renato Lima - Akretion
# Copyright (C) 2012 Raphaël Valyi - Akretion
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from openerp import models, fields, api
from openerp.addons import decimal_precision as dp
[documentos]class SaleOrder(models.Model):
_inherit = 'sale.order'
@api.multi
@api.depends('order_line.price_unit', 'order_line.tax_id',
'order_line.discount', 'order_line.product_uom_qty')
def _amount_all_wrapper(self):
""" Wrapper because of direct method passing
as parameter for function fields """
return self._amount_all()
@api.multi
def _amount_all(self):
for order in self:
order.amount_untaxed = 0.0
order.amount_tax = 0.0
order.amount_total = 0.0
order.amount_discount = 0.0
order.amount_gross = 0.0
amount_tax = amount_untaxed = \
amount_discount = amount_gross = 0.0
for line in order.order_line:
amount_tax += self._amount_line_tax(line)
amount_untaxed += line.price_subtotal
amount_discount += line.discount_value
amount_gross += line.price_gross
order.amount_tax = order.pricelist_id.currency_id.round(
amount_tax)
order.amount_untaxed = order.pricelist_id.currency_id.round(
amount_untaxed)
order.amount_total = (order.amount_untaxed +
order.amount_tax)
order.amount_discount = order.pricelist_id.currency_id.round(
amount_discount)
order.amount_gross = order.pricelist_id.currency_id.round(
amount_gross)
@api.model
def _amount_line_tax(self, line):
value = 0.0
price = line._calc_line_base_price()
qty = line._calc_line_quantity()
for computed in line.tax_id.compute_all(
price, qty, product=line.product_id,
partner=line.order_id.partner_invoice_id,
fiscal_position=line.fiscal_position)['taxes']:
tax = self.env['account.tax'].browse(computed['id'])
if not tax.tax_code_id.tax_discount:
value += computed.get('amount', 0.0)
return value
def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
result = {}
for sale in self.browse(cursor, user, ids, context=context):
if sale.invoiced:
result[sale.id] = 100.0
continue
tot = 0.0
for invoice in sale.invoice_ids:
if invoice.state not in ('draft', 'cancel') and \
invoice.fiscal_category_id.id == \
sale.fiscal_category_id.id:
tot += invoice.amount_untaxed
if tot:
result[sale.id] = min(100.0, tot * 100.0 / (
sale.amount_untaxed or 1.00))
else:
result[sale.id] = 0.0
return result
@api.model
@api.returns('l10n_br_account.fiscal_category')
def _default_fiscal_category(self):
company = self.env['res.company'].browse(self.env.user.company_id.id)
return company.sale_fiscal_category_id
fiscal_category_id = fields.Many2one(
'l10n_br_account.fiscal.category', 'Categoria Fiscal',
domain="""[('type', '=', 'output'), ('journal_type', '=', 'sale'),
('state', '=', 'approved')]""",
readonly=True, states={'draft': [('readonly', False)]},
default=_default_fiscal_category)
fiscal_position = fields.Many2one(
'account.fiscal.position', 'Fiscal Position',
domain="[('fiscal_category_id', '=', fiscal_category_id)]",
readonly=True, states={'draft': [('readonly', False)]})
invoiced_rate = fields.Float(compute='_invoiced_rate', string='Invoiced')
copy_note = fields.Boolean(u'Copiar Observação no documentos fiscal')
amount_untaxed = fields.Float(
compute='_amount_all_wrapper', string='Untaxed Amount',
digits=dp.get_precision('Account'), store=True,
help="The amount without tax.", track_visibility='always')
amount_tax = fields.Float(
compute='_amount_all_wrapper', string='Taxes', store=True,
digits=dp.get_precision('Account'), help="The tax amount.")
amount_total = fields.Float(
compute='_amount_all_wrapper', string='Total', store=True,
digits=dp.get_precision('Account'), help="The total amount.")
amount_discount = fields.Float(
compute='_amount_all_wrapper', string='Desconto (-)',
digits=dp.get_precision('Account'), store=True,
help="The discount amount.")
amount_gross = fields.Float(
compute='_amount_all_wrapper', string='Vlr. Bruto',
digits=dp.get_precision('Account'),
store=True, help="The discount amount.")
discount_rate = fields.Float(
'Desconto', readonly=True, states={'draft': [('readonly', False)]})
cnpj_cpf = fields.Char(
string=u'CNPJ/CPF',
related='partner_id.cnpj_cpf',
)
legal_name = fields.Char(
string=u'Razão Social',
related='partner_id.legal_name',
)
ie = fields.Char(
string=u'Inscrição Estadual',
related='partner_id.inscr_est',
)
@api.model
def _fiscal_position_map(self, result, **kwargs):
ctx = dict(self.env.context)
if not kwargs.get('fiscal_category_id'):
kwargs['fiscal_category_id'] = ctx.get('fiscal_category_id')
ctx.update({
'use_domain': ('use_sale', '=', True),
'fiscal_category_id': ctx.get('fiscal_category_id')})
return self.env['account.fiscal.position.rule'].with_context(
ctx).apply_fiscal_mapping(result, **kwargs)
@api.onchange('discount_rate')
[documentos] def onchange_discount_rate(self):
for sale_order in self:
for sale_line in sale_order.order_line:
sale_line.discount = sale_order.discount_rate
@api.onchange('fiscal_category_id')
[documentos] def onchange_fiscal(self):
"""Método chamado ao mudar a categoria fiscal
para refinir a posição fiscal de acordo com as
regras de posição fiscal"""
if self.company_id and self.partner_id and self.fiscal_category_id:
result = {'value': {'fiscal_position': False}}
kwargs = {
'partner_id': self.partner_id.id,
'partner_invoice_id': self.partner_invoice_id.id,
'fiscal_category_id': self.fiscal_category_id.id,
'company_id': self.company_id.id,
'context': self.env.context
}
result = self.env[
'account.fiscal.position.rule'].apply_fiscal_mapping(
result, **kwargs)
self.fiscal_position = result['value'].get('fiscal_position')
@api.model
def _fiscal_comment(self, order):
fp_comment = []
fp_ids = []
for line in order.order_line:
if line.fiscal_position and \
line.fiscal_position.inv_copy_note and \
line.fiscal_position.note:
if line.fiscal_position.id not in fp_ids:
fp_comment.append(line.fiscal_position.note)
fp_ids.append(line.fiscal_position.id)
return fp_comment
@api.model
def _prepare_invoice(self, order, lines):
"""Prepare the dict of values to create the new invoice for a
sale order. This method may be overridden to implement custom
invoice generation (making sure to call super() to establish
a clean extension chain).
:param browse_record order: sale.order record to invoice
:param list(int) line: list of invoice line IDs that must be
attached to the invoice
:return: dict of value to create() the invoice
"""
result = super(SaleOrder, self)._prepare_invoice(order, lines)
context = self.env.context
inv_lines = self.env['account.invoice.line'].browse(lines)
if (context.get('fiscal_type') == 'service' and
inv_lines and inv_lines[0].fiscal_category_id):
fiscal_category_id = inv_lines[0].fiscal_category_id.id
result['fiscal_position'] = inv_lines[0].fiscal_position.id
else:
fiscal_category_id = order.fiscal_category_id.id
if fiscal_category_id:
fiscal_category = self.env[
'l10n_br_account.fiscal.category'].browse(fiscal_category_id)
if fiscal_category:
result['journal_id'] = fiscal_category[0].property_journal.id
result['partner_shipping_id'] = order.partner_shipping_id.id
comment = []
if order.note and order.copy_note:
comment.append(order.note)
fiscal_comment = self._fiscal_comment(order)
result['comment'] = " - ".join(comment)
result['fiscal_comment'] = " - ".join(fiscal_comment)
result['fiscal_category_id'] = fiscal_category_id
return result
[documentos]class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
def _calc_line_base_price(self):
return self.price_unit * (1 - (self.discount or 0.0) / 100.0)
def _calc_line_quantity(self):
return self.product_uom_qty
def _calc_price_gross(self, qty):
return self.price_unit * qty
@api.one
@api.depends('price_unit', 'tax_id', 'discount', 'product_uom_qty')
def _amount_line(self):
price = self._calc_line_base_price()
qty = self._calc_line_quantity()
taxes = self.tax_id.compute_all(
price, qty,
product=self.product_id,
partner=self.order_id.partner_invoice_id,
fiscal_position=self.fiscal_position)
self.price_subtotal = self.order_id.pricelist_id.currency_id.round(
taxes['total'])
self.price_gross = self._calc_price_gross(qty)
self.discount_value = self.order_id.pricelist_id.currency_id.round(
self.price_gross - (price * qty))
fiscal_category_id = fields.Many2one(
'l10n_br_account.fiscal.category', 'Categoria Fiscal',
domain="[('type', '=', 'output'), ('journal_type', '=', 'sale')]",
readonly=True, states={'draft': [('readonly', False)],
'sent': [('readonly', False)]})
fiscal_position = fields.Many2one(
'account.fiscal.position', 'Fiscal Position',
domain="[('fiscal_category_id','=',fiscal_category_id)]",
readonly=True, states={'draft': [('readonly', False)],
'sent': [('readonly', False)]})
discount_value = fields.Float(compute='_amount_line',
string='Vlr. Desc. (-)',
digits=dp.get_precision('Sale Price'))
price_gross = fields.Float(
compute='_amount_line', string='Vlr. Bruto',
digits=dp.get_precision('Sale Price'))
price_subtotal = fields.Float(
compute='_amount_line', string='Subtotal',
digits=dp.get_precision('Sale Price'))
@api.model
def _fiscal_position_map(self, result, **kwargs):
context = dict(self.env.context)
context.update({'use_domain': ('use_sale', '=', True)})
fp_rule_obj = self.env['account.fiscal.position.rule']
partner_invoice = self.env['res.partner'].browse(
kwargs.get('partner_invoice_id'))
product_fc_id = fp_rule_obj.with_context(
context).product_fiscal_category_map(
kwargs.get('product_id'),
kwargs.get('fiscal_category_id'),
partner_invoice.state_id.id)
if product_fc_id:
kwargs['fiscal_category_id'] = product_fc_id
result['value']['fiscal_category_id'] = kwargs.get(
'fiscal_category_id')
result.update(fp_rule_obj.with_context(context).apply_fiscal_mapping(
result, **kwargs))
fiscal_position = result['value'].get('fiscal_position')
product_id = kwargs.get('product_id')
if product_id and fiscal_position:
obj_fposition = self.env['account.fiscal.position'].browse(
fiscal_position)
obj_product = self.env['product.product'].browse(product_id)
context.update({
'fiscal_type': obj_product.fiscal_type,
'type_tax_use': 'sale', 'product_id': product_id})
taxes = obj_product.taxes_id
tax_ids = obj_fposition.with_context(context).map_tax(taxes)
result['value']['tax_id'] = tax_ids
return result
@api.multi
[documentos] def product_id_change(self, pricelist, product, qty=0,
uom=False, qty_uos=0, uos=False, name='',
partner_id=False, lang=False, update_tax=True,
date_order=False, packaging=False,
fiscal_position=False, flag=False):
context = dict(self.env.context)
self = self.with_context(context)
parent_fiscal_category_id = context.get('parent_fiscal_category_id')
company_id = context.get('company_id')
partner_invoice_id = context.get('partner_invoice_id')
result = {'value': {}}
if parent_fiscal_category_id and product and partner_invoice_id \
and company_id:
kwargs = {
'company_id': company_id,
'partner_id': partner_id,
'product_id': product,
'partner_invoice_id': partner_invoice_id,
'fiscal_category_id': parent_fiscal_category_id,
'context': context
}
result.update(self._fiscal_position_map(result, **kwargs))
if result['value'].get('fiscal_position'):
fiscal_position = result['value'].get('fiscal_position')
obj_product = self.env['product.product'].browse(product)
context.update({'fiscal_type': obj_product.fiscal_type,
'type_tax_use': 'sale', 'product_id': product})
result_super = super(SaleOrderLine, self).product_id_change(
pricelist, product, qty, uom, qty_uos, uos, name, partner_id,
lang, update_tax, date_order, packaging, fiscal_position, flag)
result_super['value'].update(result['value'])
return result_super
@api.onchange('fiscal_category_id', 'fiscal_position')
[documentos] def onchange_fiscal(self):
if self.order_id.company_id and self.order_id.partner_id \
and self.fiscal_category_id:
result = {'value': {}}
kwargs = {
'company_id': self.order_id.company_id.id,
'partner_id': self.order_id.partner_id.id,
'partner_invoice_id': self.order_id.partner_invoice_id.id,
'product_id': self.product_id.id,
'fiscal_category_id': self.fiscal_category_id.id,
'context': self.env.context
}
result = self._fiscal_position_map(result, **kwargs)
kwargs.update({
'fiscal_category_id': self.fiscal_category_id.id,
'fiscal_position': self.fiscal_position.id,
'tax_id': [(6, 0, self.tax_id.ids)],
})
self.update(result['value'])
@api.model
def _prepare_order_line_invoice_line(self, line, account_id=False):
result = super(SaleOrderLine, self)._prepare_order_line_invoice_line(
line, account_id)
result['fiscal_category_id'] = line.fiscal_category_id.id or \
line.order_id.fiscal_category_id.id or False
result['fiscal_position'] = line.fiscal_position.id or \
line.order_id.fiscal_position.id or False
return result