Skip to content

Commit

Permalink
Merge 587b4fb into 05359b3
Browse files Browse the repository at this point in the history
  • Loading branch information
lasley committed Dec 11, 2017
2 parents 05359b3 + 587b4fb commit 99f5ac6
Show file tree
Hide file tree
Showing 23 changed files with 1,595 additions and 0 deletions.
66 changes: 66 additions & 0 deletions base_tax_connector/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
|License LGPL-3| | |Build Status| | |Test Coverage|

==================
Tax Connector Base
==================


Installation
============


Configuration
=============


Known Issues / Road Map
=======================

* In order for rates to work, the reference record must actually be saved.
This is because the reference fields require explicit ``(model_name,id)``
formatting, but with an unsaved record the ID is ``NewId``.
* Transaction refund method is on ``account.tax.transaction``, but it should
possibly be on ``account.tax.transaction.line`` instead. This was done to
simplify the addition of new adapters, but may over-complicate things.
* Add a registration system for connectors, with a more centralized method for
determining if the connector should be used.
* Add a hook for the update of a tax transaction. TBD what would actually trigger
an update, because invoices can't be edited.
* Need a way to handle tax differentiation at the invoice line item level, so
connectors can send individual lines instead of just whole orders.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/LasLabs/odoo-connector-taxjar/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.

Credits
=======

Contributors
------------

* Dave Lasley <dave@laslabs.com>

Do not contact contributors directly about support or help with technical issues.

Maintainer
----------

.. image:: https://laslabs.com/logo.png
:alt: LasLabs Inc.
:target: https://laslabs.com

This module is maintained by LasLabs Inc.


.. |Build Status| image:: https://img.shields.io/travis/LasLabs/odoo-connector-taxjar/master.svg
:target: https://travis-ci.org/LasLabs/odoo-connector-taxjar
.. |Test Coverage| image:: https://img.shields.io/codecov/c/github/LasLabs/odoo-connector-taxjar/master.svg
:target: https://codecov.io/gh/LasLabs/odoo-connector-taxjar
.. |License LGPL-3| image:: https://img.shields.io/badge/license-LGPL--3-blue.svg
:target: https://www.gnu.org/licenses/lgpl
:alt: License: LGPL-3
5 changes: 5 additions & 0 deletions base_tax_connector/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

from . import models
from . import wizards
27 changes: 27 additions & 0 deletions base_tax_connector/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

# pylint: disable=C8101
{
"name": "Tax Connector Base",
"summary": "Provides centralized logic for connection with external tax"
"connectors and subsequent caching of results.",
"version": "10.0.1.0.0",
"category": "Connector",
"website": "https://laslabs.com",
"author": "LasLabs",
"license": "LGPL-3",
"application": False,
"installable": True,
"data": [
'security/ir.model.access.csv',
'views/account_tax_transaction_view.xml',
],
"depends": [
"account",
# @TODO: Figure out how to install below in a hook when testing.
"purchase",
"sale",
],
}
12 changes: 12 additions & 0 deletions base_tax_connector/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

from . import account_tax
from . import account_tax_group
from . import account_tax_transaction
from . import account_tax_transaction_line

# Line computation injections
from . import account_invoice
from . import account_invoice_line
from . import sale_order_line
28 changes: 28 additions & 0 deletions base_tax_connector/models/account_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

import logging

from odoo import api, models

_logger = logging.getLogger(__name__)


class AccountInvoice(models.Model):

_inherit = 'account.invoice'

@api.multi
def action_move_create(self):
"""Add tax purchase/refund logic into invoice validation."""
res = super(AccountInvoice, self).action_move_create()
for invoice in self:
if 'refund' in invoice.type:
method = self.env['account.tax.group'].invoice_tax_refund
else:
method = self.env['account.tax.group'].invoice_tax_purchase
_logger.info('Running %s on %s with type %s',
method, invoice, invoice.type)
method(invoice)
return res
30 changes: 30 additions & 0 deletions base_tax_connector/models/account_invoice_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

import logging

from odoo import api, fields, models


_logger = logging.getLogger(__name__)


class AccountInvoiceLine(models.Model):

_inherit = 'account.invoice.line'

@api.multi
def _compute_price(self):
origin = getattr(self, '_origin', None)
for invoice in self.mapped('invoice_id'):
self.env['account.tax.rate'].get(
'account.invoice.tax.rate',
invoice,
origin and origin.invoice_id,
)
_logger.info(
'Rates primed for invoice lines. In onchange? %s & %s',
self.env.in_onchange, self.env.in_draft,
)
return super(AccountInvoiceLine, self)._compute_price()
44 changes: 44 additions & 0 deletions base_tax_connector/models/account_tax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

import logging

from collections import defaultdict

from odoo import api, models, fields


_logger = logging.getLogger(__name__)


class AccountTax(models.Model):

_inherit = 'account.tax'

amount_type = fields.Selection(
selection_add=[('cache', 'Cached Data')],
)

@api.multi
def _compute_amount(self, base_amount, price_unit, quantity=1.0,
product=None, partner=None):
"""Return the cached rate if it's a cached tax, otherwise super."""
self.ensure_one()
if self.amount_type == 'cache':
cached = self.env['account.tax.rate.line'].get(
self, base_amount, price_unit, quantity, product, partner,
)
_logger.info('Got cached rate %s' % cached.price_tax)
return cached and cached.price_tax or 0.0
return super(AccountTax, self)._compute_amount(
base_amount, price_unit, quantity, product, partner,
)

@api.multi
def _get_by_group(self):
"""Return the taxes in a dictionary keyed by tax group."""
grouped = defaultdict(self.env['account.tax'].browse)
for tax in self:
grouped[tax.tax_group_id] += tax
return grouped
96 changes: 96 additions & 0 deletions base_tax_connector/models/account_tax_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
# Copyright 2017 LasLabs Inc.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

import logging

from odoo import api, models, fields

_logger = logging.getLogger(__name__)


class AccountTaxGroup(models.Model):

_inherit = 'account.tax.group'

cache_name = fields.Char(
help='This is the name of the cache store. It is an '
'arbitrary string that is primarily meant for connectors to '
'identify themselves.',
)

@api.multi
def compute_cache_rate(self, base_amount, rate_line_values):
"""Compute the cached rates for the ``account.tax.rate.line`` records.
This method should be inherited by connectors in order to get rates
from an external source. A singleton is guaranteed.
Connector methods should call super, then edit and return the values.
Args:
base_amount (float): The base amount that the taxes are being
applied on. This is typically the subtotal.
rate_line_values (list(dict)): List of rate line values.
Returns:
rate_line_values (list(dict)): The same input, but with rates
adjusted by any tax connectors. They will be written to the
cache during ``account.tax.rate.get_rate_values``.
"""
self.ensure_one()
_logger.debug('Computing rate for %s with base amount %s. Lines:\n%s',
self, base_amount, rate_line_values)
return rate_line_values

@api.multi
def do_tax_purchase(self, account_invoice_tax):
"""Purchase a set of taxes.
This method should be inherited by connectors in order to purchase
taxes from an external source. A singleton is guaranteed.
Connector methods should call super and perform any actual
transactions. They can edit the transaction record that is returned
if necessary, but that probably won't be the case.
"""
self.ensure_one()
_logger.debug('Purchasing tax for %s', account_invoice_tax)
return self.env['account.tax.transaction'].buy(account_invoice_tax)

@api.multi
def do_tax_refund(self, account_invoice_tax):
"""Purchase a set of taxes.
This method should be inherited by connectors in order to refund
taxes from an external source. A singleton is guaranteed.
Connector methods should call super and perform any actual
transactions. They can edit the transaction record that is returned
if necessary, but that probably won't be the case.
"""
self.ensure_one()
_logger.debug('Refunding tax for %s', account_invoice_tax)
return self.env['account.tax.transaction'].refund(account_invoice_tax)

@api.model
def invoice_tax_purchase(self, invoice):
"""Perform a tax purchase for an invoice."""
taxes = invoice.mapped('invoice_line_ids.invoice_line_tax_ids')
grouped_taxes = taxes._get_by_group()
for group, taxes in grouped_taxes.items():
invoice_taxes = invoice.tax_line_ids.filtered(
lambda r: r.tax_id in taxes
)
group.do_tax_purchase(invoice_taxes)

@api.model
def invoice_tax_refund(self, invoice):
"""Perform a tax refund for an invoice."""
taxes = invoice.mapped('invoice_line_ids.invoice_line_tax_ids')
grouped_taxes = taxes._get_by_group()
for group, taxes in grouped_taxes.items():
invoice_taxes = invoice.tax_line_ids.filtered(
lambda r: r.tax_id in taxes
)
group.do_tax_refund(invoice_taxes)
Loading

0 comments on commit 99f5ac6

Please sign in to comment.