[ADD] account_taxcloud: Integrate TaxCloud API to get Sales Tax
- add a new checkbox on fiscal position: TaxCloud API
- when computing this fiscal position, it will:
- return an existing tax if one exists for the same tax
percentage, state, zip and TIC Category
- or create and return this tax if it does not exists yet
- Create a data fiscal position: country U.S., tax cloud API
- In l10n_us, add a settings in accounting: compute tax automatically using tax cloud.
NB:
- The signature of map_tax has been modified to optionally specify the product
and the partner on which the tax is applied
- The avoid the validationerrors that may occur while evaluation the tax with Taxcloud
when a public user is on the frontend shop (website_sale), we have to execute the
error silently by logging it.
This commit is contained in:
committed by
Yannick Tivisse
parent
84b0fb5bbc
commit
6ba6213fa9
@@ -1179,7 +1179,7 @@ class AccountInvoiceLine(models.Model):
|
||||
company_id = self.company_id or self.env.user.company_id
|
||||
taxes = taxes.filtered(lambda r: r.company_id == company_id)
|
||||
|
||||
self.invoice_line_tax_ids = fp_taxes = self.invoice_id.fiscal_position_id.map_tax(taxes)
|
||||
self.invoice_line_tax_ids = fp_taxes = self.invoice_id.fiscal_position_id.map_tax(taxes, self.product_id, self.invoice_id.partner_id)
|
||||
|
||||
fix_price = self.env['account.tax']._fix_tax_included_price
|
||||
if self.invoice_id.type in ('in_invoice', 'in_refund'):
|
||||
@@ -1250,7 +1250,7 @@ class AccountInvoiceLine(models.Model):
|
||||
return
|
||||
if not self.product_id:
|
||||
fpos = self.invoice_id.fiscal_position_id
|
||||
self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids).ids
|
||||
self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids, partner=self.partner_id).ids
|
||||
elif not self.price_unit:
|
||||
self._set_taxes()
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class AccountFiscalPosition(models.Model):
|
||||
return True
|
||||
|
||||
@api.v7
|
||||
def map_tax(self, cr, uid, fposition_id, taxes, context=None):
|
||||
def map_tax(self, cr, uid, fposition_id, taxes, product=None, partner=None, context=None):
|
||||
if not taxes:
|
||||
return []
|
||||
if not fposition_id:
|
||||
@@ -63,7 +63,7 @@ class AccountFiscalPosition(models.Model):
|
||||
return list(result)
|
||||
|
||||
@api.v8 # noqa
|
||||
def map_tax(self, taxes):
|
||||
def map_tax(self, taxes, product=None, partner=None):
|
||||
result = self.env['account.tax'].browse()
|
||||
for tax in taxes:
|
||||
tax_count = 0
|
||||
|
||||
@@ -37,13 +37,15 @@
|
||||
<page name="tax_mapping" string="Tax Mapping">
|
||||
<group>
|
||||
<field name="tax_ids" widget="one2many_list" nolabel="1">
|
||||
<tree string="Tax Mapping" editable="bottom">
|
||||
<tree name="tax_map_tree" string="Tax Mapping" editable="bottom">
|
||||
<field name="tax_src_id" domain="[('type_tax_use', '!=', None)]"/>
|
||||
<field name="tax_dest_id" domain="[('type_tax_use', '!=', None), ('price_include', '=', False)]"/>
|
||||
</tree>
|
||||
<form string="Tax Mapping">
|
||||
<field name="tax_src_id" domain="[('type_tax_use', '!=', None)]"/>
|
||||
<field name="tax_dest_id" domain="[('type_tax_use', '!=', None), ('price_include', '=', False)]"/>
|
||||
<form name="tax_map_form" string="Tax Mapping">
|
||||
<group>
|
||||
<field name="tax_src_id" domain="[('type_tax_use', '!=', None)]"/>
|
||||
<field name="tax_dest_id" domain="[('type_tax_use', '!=', None), ('price_include', '=', False)]"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</group>
|
||||
|
||||
@@ -80,7 +80,7 @@ class SaleOrder(models.Model):
|
||||
taxes = carrier.product_id.taxes_id.filtered(lambda t: t.company_id.id == self.company_id.id)
|
||||
taxes_ids = taxes.ids
|
||||
if self.partner_id and self.fiscal_position_id:
|
||||
taxes_ids = self.fiscal_position_id.map_tax(taxes).ids
|
||||
taxes_ids = self.fiscal_position_id.map_tax(taxes, carrier.product_id, self.partner_id).ids
|
||||
|
||||
# Create the sale order line
|
||||
values = {
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import models
|
||||
|
||||
@@ -11,6 +11,7 @@ United States - Chart of accounts.
|
||||
'depends': ['l10n_generic_coa', 'report'],
|
||||
'data': [
|
||||
'data/res_company.xml',
|
||||
'views/res_config_view.xml'
|
||||
],
|
||||
'demo': [],
|
||||
'test': [],
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import res_config
|
||||
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
class AccountConfigSettings(models.TransientModel):
|
||||
_inherit = 'account.config.settings'
|
||||
|
||||
module_account_taxcloud = fields.Boolean("Compute sales tax automatically in the United States using TaxCloud.",
|
||||
help='TaxCloud is an online provider that is committed to making it easy for retailers to collect sales tax online'
|
||||
'in the United States.')
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="account_config_settings_inherit_form_view" model="ir.ui.view">
|
||||
<field name="name">account settings</field>
|
||||
<field name="model">account.config.settings</field>
|
||||
<field name="inherit_id" ref="account.view_account_config_settings"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='invoice_taxes']" position="inside">
|
||||
<div name="taxcloud_tax" class="o_row">
|
||||
<field name="module_account_taxcloud"/>
|
||||
<label for="module_account_taxcloud"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -22,7 +22,7 @@ class PosOrder(models.Model):
|
||||
def _amount_line_tax(self, cr, uid, line, fiscal_position_id, context=None):
|
||||
taxes = line.tax_ids.filtered(lambda t: t.company_id.id == line.order_id.company_id.id)
|
||||
if fiscal_position_id:
|
||||
taxes = fiscal_position_id.map_tax(taxes)
|
||||
taxes = fiscal_position_id.map_tax(taxes, line.product_id, line.order_id.partner_id)
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
taxes = taxes.compute_all(price, line.order_id.pricelist_id.currency_id, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)['taxes']
|
||||
return sum(tax.get('amount', 0.0) for tax in taxes)
|
||||
@@ -184,7 +184,7 @@ class PosOrder(models.Model):
|
||||
invoice_line.invoice_line_tax_ids = invoice_line.invoice_line_tax_ids.filtered(lambda t: t.company_id.id == line.order_id.company_id.id).ids
|
||||
fiscal_position_id = line.order_id.fiscal_position_id
|
||||
if fiscal_position_id:
|
||||
invoice_line.invoice_line_tax_ids = fiscal_position_id.map_tax(invoice_line.invoice_line_tax_ids)
|
||||
invoice_line.invoice_line_tax_ids = fiscal_position_id.map_tax(invoice_line.invoice_line_tax_ids, line.product_id, line.order_id.partner_id)
|
||||
invoice_line.invoice_line_tax_ids = invoice_line.invoice_line_tax_ids.ids
|
||||
# We convert a new id object back to a dictionary to write to
|
||||
# bridge between old and new api
|
||||
@@ -739,7 +739,7 @@ class PosOrderLine(models.Model):
|
||||
taxes = line.tax_ids.filtered(lambda tax: tax.company_id.id == line.order_id.company_id.id)
|
||||
fiscal_position_id = line.order_id.fiscal_position_id
|
||||
if fiscal_position_id:
|
||||
taxes = fiscal_position_id.map_tax(taxes)
|
||||
taxes = fiscal_position_id.map_tax(taxes, line.product_id, line.order_id.partner_id)
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
line.price_subtotal = line.price_subtotal_incl = price * line.qty
|
||||
if taxes:
|
||||
@@ -778,7 +778,7 @@ class PosOrderLine(models.Model):
|
||||
@api.multi
|
||||
def _get_tax_ids_after_fiscal_position(self):
|
||||
for line in self:
|
||||
line.tax_ids_after_fiscal_position = line.order_id.fiscal_position_id.map_tax(line.tax_ids)
|
||||
line.tax_ids_after_fiscal_position = line.order_id.fiscal_position_id.map_tax(line.tax_ids, line.product_id, line.order_id.partner_id)
|
||||
|
||||
|
||||
class PosOrderLineLot(models.Model):
|
||||
|
||||
+1
-1
@@ -639,7 +639,7 @@ class SaleOrderLine(models.Model):
|
||||
fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.property_account_position_id
|
||||
# If company_id is set, always filter taxes by the company
|
||||
taxes = line.product_id.taxes_id.filtered(lambda r: not line.company_id or r.company_id == line.company_id)
|
||||
line.tax_id = fpos.map_tax(taxes) if fpos else taxes
|
||||
line.tax_id = fpos.map_tax(taxes, line.product_id, line.order_id.partner_id) if fpos else taxes
|
||||
|
||||
@api.multi
|
||||
def _prepare_order_line_procurement(self, group_id=False):
|
||||
|
||||
@@ -59,7 +59,8 @@ class AccountAnalyticLine(models.Model):
|
||||
last_sequence = last_so_line.sequence + 1 if last_so_line else 100
|
||||
|
||||
fpos = order.fiscal_position_id or order.partner_id.property_account_position_id
|
||||
taxes = fpos.map_tax(self.product_id.taxes_id)
|
||||
taxes = fpos.map_tax(self.product_id.taxes_id, self.product_id, order.partner_id)
|
||||
|
||||
return {
|
||||
'order_id': order.id,
|
||||
'name': self.name,
|
||||
|
||||
@@ -5,7 +5,7 @@ import random
|
||||
|
||||
from odoo import api, models, fields, tools, _
|
||||
from odoo.http import request
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -92,7 +92,11 @@ class SaleOrder(models.Model):
|
||||
if not order_line:
|
||||
values = self._website_product_id_change(self.id, product_id, qty=1)
|
||||
order_line = SaleOrderLineSudo.create(values)
|
||||
order_line._compute_tax_id()
|
||||
try:
|
||||
order_line._compute_tax_id()
|
||||
except ValidationError as e:
|
||||
# The validation may occur in backend (eg: taxcloud) but should fail silently in frontend
|
||||
_logger.debug("ValidationError occurs during tax compute. %s" % (e))
|
||||
if add_qty:
|
||||
add_qty -= 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user