From 4080fc6426e2726a3fff94b4473501d517da6ebc Mon Sep 17 00:00:00 2001 From: "Atchuthan, Sodexis" Date: Wed, 16 Dec 2020 20:01:50 +0530 Subject: [PATCH 1/2] [MIG] Migrate to v14 --- account_avatax/README.rst | 17 +---- account_avatax/__manifest__.py | 5 +- account_avatax/models/account_move.py | 65 +++++++++++++++---- account_avatax/models/avalara_salestax.py | 10 +++ account_avatax/models/avatax_rest_api.py | 2 +- account_avatax/models/partner.py | 32 +-------- account_avatax/readme/CREDITS.rst | 22 +++++-- account_avatax/security/ir.model.access.csv | 10 +-- account_avatax/views/account_move_view.xml | 3 +- .../views/avalara_salestax_view.xml | 1 + account_avatax/views/partner_view.xml | 1 - account_avatax_sale/README.rst | 2 + account_avatax_sale/__manifest__.py | 9 ++- account_avatax_sale/models/__init__.py | 1 + .../models/avalara_salestax.py | 15 +++++ account_avatax_sale/models/sale_order.py | 55 +++++++++++++++- .../views/avalara_salestax_view.xml | 14 ++++ account_avatax_sale/views/sale_order_view.xml | 1 + 18 files changed, 187 insertions(+), 78 deletions(-) create mode 100644 account_avatax_sale/models/avalara_salestax.py create mode 100644 account_avatax_sale/views/avalara_salestax_view.xml diff --git a/account_avatax/README.rst b/account_avatax/README.rst index 07360a43c..58addab3d 100644 --- a/account_avatax/README.rst +++ b/account_avatax/README.rst @@ -25,21 +25,6 @@ Avalara Avatax Connector |badge1| |badge2| |badge3| |badge4| |badge5| -.. |avataxbadge1| image:: https://raw.githubusercontent.com/OCA/account-fiscal-rule/13.0/account_avatax/static/description/SalesTax.png - :target: https://developer.avalara.com/certification/avatax/sales-tax-badge/ - :alt: Sales Tax Certification - :width: 250 -.. |avataxbadge2| image:: https://raw.githubusercontent.com/OCA/account-fiscal-rule/13.0/account_avatax/static/description/Refunds.png - :target: https://developer.avalara.com/certification/avatax/refunds-credit-memos-badge/ - :alt: Refunds Certification - :width: 250 -.. |avataxbadge3| image:: https://raw.githubusercontent.com/OCA/account-fiscal-rule/13.0/account_avatax/static/description/AddressValidation.png - :target: https://developer.avalara.com/certification/avatax/address-validation-badge/ - :alt: Address Validation Certification - :width: 250 - -|avataxbadge1| |avataxbadge2| |avataxbadge3| - Odoo provides integration with AvaTax, a tax solution software by Avalara which includes sales tax calculation for all US states and territories and all Canadian provinces and territories (including GST, PST, and HST). @@ -368,7 +353,9 @@ Credits Authors ~~~~~~~ +* Open Source Integrators * Fabrice Henrion +* Odoo SA Contributors ~~~~~~~~~~~~ diff --git a/account_avatax/__manifest__.py b/account_avatax/__manifest__.py index 0d16e36e5..20d1cc6fc 100644 --- a/account_avatax/__manifest__.py +++ b/account_avatax/__manifest__.py @@ -1,7 +1,8 @@ { "name": "Avalara Avatax Connector", "version": "14.0.1.0.0", - "author": "Fabrice Henrion, Odoo Community Association (OCA)", + "author": "Open Source Integrators, Fabrice Henrion," + "Sodexis, Odoo Community Association (OCA)", "summary": "Automatic Tax application using the Avalara Avatax Service", "license": "AGPL-3", "category": "Accounting", @@ -26,6 +27,6 @@ "installable": True, "application": True, "external_dependencies": {"python": ["Avalara"]}, - "development_status": "Beta", + "development_status": "Alpha", "maintainers": ["dreispt"], } diff --git a/account_avatax/models/account_move.py b/account_avatax/models/account_move.py index 2560b8d9a..c91273c99 100644 --- a/account_avatax/models/account_move.py +++ b/account_avatax/models/account_move.py @@ -63,6 +63,7 @@ def onchange_warehouse_id(self): ) warehouse_id = fields.Many2one("stock.warehouse", "Warehouse") avatax_amount = fields.Float(string="AvaTax", copy=False) + calculate_tax_on_save = fields.Boolean() @api.depends( "line_ids.debit", @@ -80,7 +81,7 @@ def _compute_amount(self): if inv.avatax_amount: inv.amount_tax = abs(inv.avatax_amount) inv.amount_total = inv.amount_untaxed + inv.amount_tax - sign = inv.type in ["in_refund", "out_refund"] and -1 or 1 + sign = inv.move_type in ["in_refund", "out_refund"] and -1 or 1 inv.amount_total_signed = inv.amount_total * sign @api.depends("tax_on_shipping_address", "partner_id", "partner_shipping_id") @@ -114,7 +115,7 @@ def get_origin_tax_date(self): # Same as v12 def _get_avatax_doc_type(self, commit=True): self.ensure_one() - if "refund" in self.type: + if "refund" in self.move_type: doc_type = "ReturnInvoice" if commit else "ReturnOrder" else: doc_type = "SalesInvoice" if commit else "SalesOrder" @@ -126,7 +127,7 @@ def _avatax_prepare_lines(self, doc_type=None): Prepare the lines to use for Avatax computation. Returns a list of dicts """ - sign = 1 if self.type.startswith("out") else -1 + sign = 1 if self.move_type.startswith("out") else -1 lines = [ line._avatax_prepare_line(sign, doc_type) for line in self.invoice_line_ids @@ -159,9 +160,9 @@ def _avatax_compute_tax(self, commit=False): commit, tax_date, # TODO: can we report self.invoice_doc_no? - self.name if self.type == "out_refund" else "", + self.name if self.move_type == "out_refund" else "", self.location_code or "", - is_override=self.type == "out_refund", + is_override=self.move_type == "out_refund", currency_id=self.currency_id, ignore_error=300 if commit else None, ) @@ -208,7 +209,6 @@ def _avatax_compute_tax(self, commit=False): return tax_result - # Same as v12 def avatax_compute_taxes(self, commit=False): """ Called from Invoice's Action menu. @@ -229,8 +229,7 @@ def avatax_commit_taxes(self): avatax_config.commit_transaction(invoice.name, doc_type) return True - # action_invoice_open in v12 - def post(self): + def action_post(self): avatax_config = self.company_id.get_avatax_config_company() if avatax_config and avatax_config.force_address_validation: for addr in [self.partner_id, self.partner_shipping_id]: @@ -243,11 +242,11 @@ def post(self): # However, we can't save the invoice because it wasn't assigned a # number yet self.avatax_compute_taxes(commit=False) - super().post() + res = super().action_post() # We can only commit to Avatax after validating the invoice # because we need the generated Invoice number self.avatax_compute_taxes(commit=True) - return True + return res # prepare_return in v12 def _reverse_move_vals(self, default_values, cancel=True): @@ -277,7 +276,7 @@ def button_draft(self): """ for invoice in self: if ( - invoice.type in ["out_invoice", "out_refund"] + invoice.move_type in ["out_invoice", "out_refund"] and self.fiscal_position_id.is_avatax and invoice.state == "posted" ): @@ -286,6 +285,50 @@ def button_draft(self): avatax.void_transaction(invoice.name, doc_type) return super(AccountMove, self).button_draft() + @api.onchange( + "invoice_line_ids", + "warehouse_id", + "tax_address_id", + "tax_on_shipping_address", + ) + def onchange_avatax_calculation(self): + avatax_config = self.env["avalara.salestax"].sudo().search([], limit=1) + self.calculate_tax_on_save = False + if avatax_config.invoice_calculate_tax: + if ( + self._origin.warehouse_id != self.warehouse_id + or self._origin.tax_address_id.street != self.tax_address_id.street + or self._origin.tax_on_shipping_address != self.tax_on_shipping_address + ): + self.calculate_tax_on_save = True + return + for line in self.invoice_line_ids: + if ( + line._origin.price_unit != line.price_unit + or line._origin.discount != line.discount + or line._origin.quantity != line.quantity + ): + self.calculate_tax_on_save = True + break + + def write(self, vals): + result = super(AccountMove, self).write(vals) + avatax_config = self.env["avalara.salestax"].sudo().search([], limit=1) + for record in self: + if ( + avatax_config.invoice_calculate_tax + and record.calculate_tax_on_save + and record.state == "draft" + and not self._context.get("skip_second_write", False) + ): + record.with_context(skip_second_write=True).write( + { + "calculate_tax_on_save": False, + } + ) + self.avatax_compute_taxes() + return result + class AccountMoveLine(models.Model): _inherit = "account.move.line" diff --git a/account_avatax/models/avalara_salestax.py b/account_avatax/models/avalara_salestax.py index 6224784e0..e25cda232 100644 --- a/account_avatax/models/avalara_salestax.py +++ b/account_avatax/models/avalara_salestax.py @@ -136,6 +136,12 @@ def _get_avatax_supported_countries(self): help="Allows ean13 to be reported in place of Item Reference" " as upc identifier.", ) + invoice_calculate_tax = fields.Boolean( + "Auto Calculate Tax on Invoice Save", + help="Automatically triggers API to calculate tax If changes made on" + "Invoice's warehouse_id, tax_on_shipping_address, " + "Invoice line's price_unit, discount, quantity", + ) # TODO: add option to Display Prices with Tax Included # constraints on uniq records creation with account_number and company_id @@ -165,6 +171,7 @@ def get_avatax_rest_service(self): self.service_url, self.request_timeout, self.logging, + config=self, ) def create_transaction( @@ -279,6 +286,7 @@ def create_transaction( def commit_transaction(self, doc_code, doc_type): self.ensure_one() + result = False if not self.disable_tax_reporting: avatax = self.get_avatax_rest_service() result = avatax.call( @@ -288,6 +296,7 @@ def commit_transaction(self, doc_code, doc_type): def void_transaction(self, doc_code, doc_type): self.ensure_one() + result = False if not self.disable_tax_reporting: avatax = self.get_avatax_rest_service() result = avatax.call( @@ -297,6 +306,7 @@ def void_transaction(self, doc_code, doc_type): def unvoid_transaction(self, doc_code, doc_type): self.ensure_one() + result = False if not self.disable_tax_reporting: avatax = self.get_avatax_rest_service() result = avatax.call("unvoid_transaction", self.company_code, doc_code) diff --git a/account_avatax/models/avatax_rest_api.py b/account_avatax/models/avatax_rest_api.py index 745c7df67..1e4541638 100644 --- a/account_avatax/models/avatax_rest_api.py +++ b/account_avatax/models/avatax_rest_api.py @@ -77,7 +77,7 @@ def get_result(self, response, ignore_error=None): if w_message.get("refersTo", "").startswith("Address"): raise UserError( _( - "AvaTax: Warning vaTax could not validate the" + "AvaTax: Warning AvaTax could not validate the" " address:\n%s\n\n" "You can save the address and AvaTax will make an" " attempt to " diff --git a/account_avatax/models/partner.py b/account_avatax/models/partner.py index 9a744c7f6..edb472091 100644 --- a/account_avatax/models/partner.py +++ b/account_avatax/models/partner.py @@ -18,36 +18,6 @@ class ResPartner(models.Model): _inherit = "res.partner" - @api.model - def _migrate_exemption_data(self): - """ - Migrate values from old exemption fields - into the new per company property fields - """ - companies = self.env["res.company"].search([]) - for company in companies: - Partner = self.env["res.partner"].with_context(force_company=company.id) - pending_exempt_partners = Partner.search( - [ - ("exemption_code_id", "!=", False), - ("property_exemption_code_id", "=", False), - ] - ) - if pending_exempt_partners: - _LOGGER.info( - "Migrating exemption data on %d partners for company %s", - len(pending_exempt_partners), - company.display_name, - ) - for partner in pending_exempt_partners: - partner.write( - { - "property_tax_exempt": partner.tax_exempt, - "property_exemption_code_id": partner.exemption_code_id.id, - "property_exemption_number": partner.exemption_number, - } - ) - date_validation = fields.Date( "Last Validation Date", readonly=True, @@ -66,7 +36,7 @@ def _migrate_exemption_data(self): help="Indicates if the address is already validated on save" " before calling the wizard", ) - customer_code = fields.Char("Customer Code") + customer_code = fields.Char("Customer Code", copy=False) tax_exempt = fields.Boolean( "Is Tax Exempt (Deprecated))", deprecated=True, diff --git a/account_avatax/readme/CREDITS.rst b/account_avatax/readme/CREDITS.rst index ae3f98375..d081f89c2 100644 --- a/account_avatax/readme/CREDITS.rst +++ b/account_avatax/readme/CREDITS.rst @@ -1,10 +1,18 @@ -This module was originally developed by Frabrice Henrion at Odoo SA. +This module was originally developed by Frabrice Henrion at Odoo SA, +and maintained up to version 11. -Upon release od Odoo 12, Fabrice and Odoo SA stopped supporting the module, -and invited Odoo partners to maintain it. +For version 12, Fabrice invited partners to migrate this modules to +later version, and maintain it. -Open Source Integrators decided to contribute the module to the OCA, -on the Odoo 13 refactoring. +Open Source Integrators performed the migration to Odoo 12 +, and later added support for the more up to date REST API +, alongside with the legacy SOAP API. -For previous Odoo versions, from Odoo 9 to Odoo 12, the code is -maintained at https://github.com/sodexis/avatax_connector. +With the addition of the REST API, a deep refactor was introduced, +changing the tax calculation approach, from just setting the total +tax amount, to instead adding the tax rates to each document line +and then having Odoo do all the other computations. + +For Odoo 13, the legacy SOAP support was supported, and +additional refactoring was done to contribute the module +to the odoo Community Association. diff --git a/account_avatax/security/ir.model.access.csv b/account_avatax/security/ir.model.access.csv index dbe30434f..38e06ae23 100644 --- a/account_avatax/security/ir.model.access.csv +++ b/account_avatax/security/ir.model.access.csv @@ -1,7 +1,9 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_avalara_salestax_manager,avalara.salestax.manager,model_avalara_salestax,account.group_account_manager,1,1,1,1 access_avalara_salestax_employee,avalara.salestax.employee,model_avalara_salestax,base.group_user,1,0,0,0 -access_product_tax_code employee,product.tax.code.employee,model_product_tax_code,base.group_user,1,0,0,0 -access_product_tax_code manager,product.tax.code.manager,model_product_tax_code,account.group_account_manager,1,1,1,1 -access_exemption_code manager,exemption.code.manager,model_exemption_code,account.group_account_manager,1,1,1,1 -access_exemption_code employee,exemption.code.employee,model_exemption_code,base.group_user,1,0,0,0 +access_product_tax_code_employee,product.tax.code.employee,model_product_tax_code,base.group_user,1,0,0,0 +access_product_tax_code_manager,product.tax.code.manager,model_product_tax_code,account.group_account_manager,1,1,1,1 +access_exemption_code_manager,exemption.code.manager,model_exemption_code,account.group_account_manager,1,1,1,1 +access_exemption_code_employee,exemption.code.employee,model_exemption_code,base.group_user,1,0,0,0 +access_avalara_salestax_ping,avalara_salestax_ping,model_avalara_salestax_ping,base.group_user,1,1,1,1 +access_avalara_salestax_address_validate,avalara_salestax_address_validate,model_avalara_salestax_address_validate,base.group_user,1,1,1,1 diff --git a/account_avatax/views/account_move_view.xml b/account_avatax/views/account_move_view.xml index 6ed3d9ef9..9fd73f55d 100644 --- a/account_avatax/views/account_move_view.xml +++ b/account_avatax/views/account_move_view.xml @@ -19,9 +19,10 @@ + diff --git a/account_avatax/views/avalara_salestax_view.xml b/account_avatax/views/avalara_salestax_view.xml index d3686818d..7e145c0ca 100644 --- a/account_avatax/views/avalara_salestax_view.xml +++ b/account_avatax/views/avalara_salestax_view.xml @@ -51,6 +51,7 @@ + - diff --git a/account_avatax_sale/README.rst b/account_avatax_sale/README.rst index 00b49cc48..6c44947ee 100644 --- a/account_avatax_sale/README.rst +++ b/account_avatax_sale/README.rst @@ -108,7 +108,9 @@ Credits Authors ~~~~~~~ +* Open Source Integrators * Fabrice Henrion +* Odoo SA Contributors ~~~~~~~~~~~~ diff --git a/account_avatax_sale/__manifest__.py b/account_avatax_sale/__manifest__.py index 390853bb9..f52de296b 100644 --- a/account_avatax_sale/__manifest__.py +++ b/account_avatax_sale/__manifest__.py @@ -1,13 +1,18 @@ { "name": "Avalara Avatax Connector for Sales Orders", "version": "14.0.1.0.0", - "author": "Fabrice Henrion, Odoo Community Association (OCA)", + "author": "Open Source Integrators, Fabrice Henrion," + "Sodexis, Odoo Community Association (OCA)", "summary": "Sales Orders with automatic Tax application using Avatax", "license": "AGPL-3", "category": "Accounting", "website": "https://github.com/OCA/account-fiscal-rule", "depends": ["account_avatax", "sale"], - "data": ["views/sale_order_view.xml", "views/partner_view.xml"], + "data": [ + "views/sale_order_view.xml", + "views/partner_view.xml", + "views/avalara_salestax_view.xml", + ], "auto_install": True, "development_status": "Beta", "maintainers": ["dreispt"], diff --git a/account_avatax_sale/models/__init__.py b/account_avatax_sale/models/__init__.py index 1e916d8cc..4f20c7661 100644 --- a/account_avatax_sale/models/__init__.py +++ b/account_avatax_sale/models/__init__.py @@ -1,3 +1,4 @@ from . import account_move from . import partner from . import sale_order +from . import avalara_salestax diff --git a/account_avatax_sale/models/avalara_salestax.py b/account_avatax_sale/models/avalara_salestax.py new file mode 100644 index 000000000..af4db0831 --- /dev/null +++ b/account_avatax_sale/models/avalara_salestax.py @@ -0,0 +1,15 @@ +from odoo import fields, models + + +class AvalaraSalestax(models.Model): + _inherit = "avalara.salestax" + + use_partner_invoice_id = fields.Boolean( + "Use Invoice partner's customer code in SO", + ) + sale_calculate_tax = fields.Boolean( + "Auto Calculate Tax on SO Save", + help="Automatically triggers API to calculate tax If changes made on" + "SO's warehouse_id, tax_on_shipping_address, " + "SO line's price_unit, discount, product_uom_qty", + ) diff --git a/account_avatax_sale/models/sale_order.py b/account_avatax_sale/models/sale_order.py index e8623979b..1aca468d7 100644 --- a/account_avatax_sale/models/sale_order.py +++ b/account_avatax_sale/models/sale_order.py @@ -38,8 +38,8 @@ def _compute_onchange_exemption(self): ) )[:1] # Force Company to get the correct values form the Property fields - exemption_address = exemption_address_naive.with_context( - force_company=order.company_id.id + exemption_address = exemption_address_naive.with_company( + order.company_id.id ) order.exemption_code = exemption_address.property_exemption_number order.exemption_code_id = exemption_address.property_exemption_code_id @@ -122,6 +122,7 @@ def _compute_tax_address_id(self): store=True, ) location_code = fields.Char("Location Code", help="Origin address location code") + calculate_tax_on_save = fields.Boolean() def _get_avatax_doc_type(self, commit=False): return "SalesOrder" @@ -143,12 +144,17 @@ def _avatax_compute_tax(self): doc_type = self._get_avatax_doc_type() Tax = self.env["account.tax"] avatax_config = self.company_id.get_avatax_config_company() + if not avatax_config: + return False + partner = self.partner_id + if avatax_config.use_partner_invoice_id: + partner = self.partner_invoice_id taxable_lines = self._avatax_prepare_lines(self.order_line) tax_result = avatax_config.create_transaction( self.date_order, self.name, doc_type, - self.partner_id, + partner, self.warehouse_id.partner_id or self.company_id.partner_id, self.tax_address_id or self.partner_id, taxable_lines, @@ -196,6 +202,49 @@ def action_confirm(self): self.avalara_compute_taxes() return res + @api.onchange( + "order_line", + "tax_on_shipping_address", + "tax_address_id", + ) + def onchange_avatax_calculation(self): + avatax_config = self.env["avalara.salestax"].sudo().search([], limit=1) + self.calculate_tax_on_save = False + if avatax_config.sale_calculate_tax: + if ( + self._origin.tax_address_id.street != self.tax_address_id.street + or self._origin.tax_on_shipping_address != self.tax_on_shipping_address + ): + self.calculate_tax_on_save = True + return + for line in self.order_line: + if ( + line._origin.product_uom_qty != line.product_uom_qty + or line._origin.discount != line.discount + or line._origin.price_unit != line.price_unit + or line._origin.warehouse_id != line.warehouse_id + ): + self.calculate_tax_on_save = True + break + + def write(self, vals): + result = super(SaleOrder, self).write(vals) + avatax_config = self.env["avalara.salestax"].sudo().search([], limit=1) + for record in self: + if ( + avatax_config.sale_calculate_tax + and record.calculate_tax_on_save + and record.state == "draft" + and not self._context.get("skip_second_write", False) + ): + record.with_context(skip_second_write=True).write( + { + "calculate_tax_on_save": False, + } + ) + self.avalara_compute_taxes() + return result + class SaleOrderLine(models.Model): _inherit = "sale.order.line" diff --git a/account_avatax_sale/views/avalara_salestax_view.xml b/account_avatax_sale/views/avalara_salestax_view.xml new file mode 100644 index 000000000..f1ba79894 --- /dev/null +++ b/account_avatax_sale/views/avalara_salestax_view.xml @@ -0,0 +1,14 @@ + + + + avalara.salestax.inherit + avalara.salestax + + + + + + + + + diff --git a/account_avatax_sale/views/sale_order_view.xml b/account_avatax_sale/views/sale_order_view.xml index 8f2b6003f..6b98f13e7 100644 --- a/account_avatax_sale/views/sale_order_view.xml +++ b/account_avatax_sale/views/sale_order_view.xml @@ -26,6 +26,7 @@ options='{"always_reload": True}' readonly="1" /> + From 06cd950e25c9c5fe98270e1dbc1da4450d638cda Mon Sep 17 00:00:00 2001 From: "Atchuthan, Sodexis" Date: Mon, 21 Dec 2020 12:00:22 +0530 Subject: [PATCH 2/2] minor fix in CREDITS.rst --- account_avatax/readme/CREDITS.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/account_avatax/readme/CREDITS.rst b/account_avatax/readme/CREDITS.rst index d081f89c2..2f889c822 100644 --- a/account_avatax/readme/CREDITS.rst +++ b/account_avatax/readme/CREDITS.rst @@ -1,4 +1,4 @@ -This module was originally developed by Frabrice Henrion at Odoo SA, +This module was originally developed by Fabrice Henrion at Odoo SA, and maintained up to version 11. For version 12, Fabrice invited partners to migrate this modules to @@ -15,4 +15,4 @@ and then having Odoo do all the other computations. For Odoo 13, the legacy SOAP support was supported, and additional refactoring was done to contribute the module -to the odoo Community Association. +to the Odoo Community Association.