Merge lp:~npg-team/openobject-addons/account_salestax_avatax_us into lp:openobject-addons

Proposed by Novapoint Group
Status: Rejected
Rejected by: Fabien (Open ERP)
Proposed branch: lp:~npg-team/openobject-addons/account_salestax_avatax_us
Merge into: lp:openobject-addons
Diff against target: 2228 lines (+2104/-0)
22 files modified
account_salestax_avatax/__init__.py (+33/-0)
account_salestax_avatax/__openerp__.py (+77/-0)
account_salestax_avatax/account.py (+92/-0)
account_salestax_avatax/account_invoice_view.xml (+22/-0)
account_salestax_avatax/account_invoice_workflow.xml (+17/-0)
account_salestax_avatax/account_salestax_avatax.py (+147/-0)
account_salestax_avatax/account_salestax_avatax_data.xml (+90/-0)
account_salestax_avatax/account_salestax_avatax_view.xml (+215/-0)
account_salestax_avatax/invoice.py (+252/-0)
account_salestax_avatax/partner.py (+169/-0)
account_salestax_avatax/partner_view.xml (+63/-0)
account_salestax_avatax/product.py (+57/-0)
account_salestax_avatax/product_view.xml (+108/-0)
account_salestax_avatax/sale.py (+118/-0)
account_salestax_avatax/security/account_salestax_avatax_security.xml (+29/-0)
account_salestax_avatax/security/ir.model.access.csv (+11/-0)
account_salestax_avatax/suds_client.py (+295/-0)
account_salestax_avatax/wizard/__init__.py (+26/-0)
account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py (+135/-0)
account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml (+53/-0)
account_salestax_avatax/wizard/account_salestax_avatax_ping.py (+58/-0)
account_salestax_avatax/wizard/account_salestax_avatax_ping.xml (+37/-0)
To merge this branch: bzr merge lp:~npg-team/openobject-addons/account_salestax_avatax_us
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+76639@code.launchpad.net

This proposal supersedes a proposal from 2011-09-02.

Description of the change

Improvements in account_salestax_avatax module developed by Novapoint Group for sales tax calculation using AvaTax service.

To post a comment you must log in.

Unmerged revisions

2. By Novapoint Group

Improvements in code, security

1. By Novapoint Group

Added account_salestax_avatax module for sales tax calculation using AvaTax service

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'account_salestax_avatax'
=== added file 'account_salestax_avatax/__init__.py'
--- account_salestax_avatax/__init__.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/__init__.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,33 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23import product
24import account_salestax_avatax
25import partner
26import suds_client
27import sale
28import invoice
29import account
30import wizard
31
32
33# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
034
=== added file 'account_salestax_avatax/__openerp__.py'
--- account_salestax_avatax/__openerp__.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/__openerp__.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,77 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23{
24 "name" : "Use AvaTax web service to calculate tax",
25 "version" : "1.0",
26 "author" : 'Novapoint Group LLC',
27 "description": """ This module supports calculating sales and use taxes, validates addresses
28using the AvaTax subscription service from AVALARA. www.avalara.com. It is targeted for the US, Canadian, and in the future the international markets
29supported by AVALARA. Users of the module need to subscribe to a service plan from AVALARA and obtain the proper login credentials prior
30to using this module. As the US and Canadian tax programs have more
31than 22,000 unique tax jurisdictions based on a GEO-Location matrix segmented by product category,
32and tax collection legislation continues to change in the US to include new
33areas like internet transactions, we believe using a tax service from AvaTax to calculate applicable taxes due simplifies
34a company's management, time and effort to be in compliance with tax rules.
35
36It is intended that the tax calculation service is called whenever an existing tax calculation is calculated within OpenERP.
37
38The process to setup the system is as follows:
39A company would initially install the module.
40A company would sign-up for the AVATAX service.
41A company enter their AVATAX credentials in OpenERP.
42A company then should setup their Chart of Accounts with the appropriate accounts to capture detailed tax information (see your accountant).
43A company then sets up the AVATAX service to cater to the products and services they provide.
44A company then configures OpenERP properly for tax processing with AVATAX.
45A company then tests the AVATAX connection and service.
46
47AVATAX also offers a service to manage the submission of taxes due to various tax jurisdictions electronically, and manually.
48
49Please review the module documentation prior to installing the module for prerequisites.
50
51NOTE: Use of this module is "at your own risk", and does not in any way make NovaPoint Group or OpenERP liable for any issues or construe any
52obligation that calculations are correct, or that tax calculations are sufficient to meet regulatory or legislative requirements.
53
54""",
55 "category" : "Generic Modules/Accounting",
56 "website" : "http://www.novapointgroup.com/",
57 "depends" : ["sale_negotiated_shipping"],
58 "init_xml" : [],
59 "demo_xml" : [],
60 "update_xml" : [
61 "account_salestax_avatax_data.xml",
62 "wizard/account_salestax_avatax_ping.xml",
63 "wizard/account_salestax_avatax_address_validate.xml",
64 "account_salestax_avatax_view.xml",
65 "partner_view.xml",
66 "product_view.xml",
67 "account_invoice_workflow.xml",
68 "account_invoice_view.xml",
69 "security/account_salestax_avatax_security.xml",
70 "security/ir.model.access.csv",
71 ],
72 "test" : [],
73 "active": False,
74 "installable": True,
75}
76
77# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
078
=== added file 'account_salestax_avatax/account.py'
--- account_salestax_avatax/account.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/account.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,92 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22import time
23import string
24
25from osv import osv, fields
26from tools.translate import _
27
28from suds_client import AvaTaxService, BaseAddress, Line
29
30class account_tax(osv.osv):
31 _inherit = "account.tax"
32
33 def _check_compute_tax(self, cr, uid, avatax_config, doc_date, doc_code, doc_type, partner, ship_from_address_id, shipping_address_id,
34 lines, shipping_charge, user=None, commit=False, invoice_date=False, reference_code=False, context=None):
35 address_obj = self.pool.get('res.partner.address')
36 if not lines:
37 raise osv.except_osv(_('Error !'), _('AvaTax needs atleast one sale order line defined for tax calculation.'))
38 if avatax_config.force_address_validation:
39 if not shipping_address.date_validation:
40 raise osv.except_osv(_('Address Not Validated !'), _('Please validate the shipping address for the partner %s.'
41 % (partner.name)))
42 if not ship_from_address_id:
43 raise osv.except_osv(_('No Ship from Address Defined !'), _('There is no company address defined.'))
44 if not shipping_address_id:
45 raise osv.except_osv(_('No Shipping Address Defined !'), _('There is no shipping address defined for the partner.'))
46
47 ship_from_address = address_obj.browse(cr, uid, ship_from_address_id, context=context)
48 shipping_address = address_obj.browse(cr, uid, shipping_address_id, context=context)
49 if not ship_from_address.date_validation:
50 raise osv.except_osv(_('Address Not Validated !'), _('Please validate the company address.'))
51
52 if shipping_charge:
53 lines.append({
54 'qty': 1,
55 'amount': shipping_charge,
56 'itemcode': '',
57 'description': '',
58 'tax_code': avatax_config.default_shipping_code_id.name
59 })
60 avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key,
61 avatax_config.service_url, avatax_config.request_timeout, avatax_config.logging)
62 avapoint.create_tax_service()
63 addSvc = avapoint.create_address_service().addressSvc
64 origin = BaseAddress(addSvc, ship_from_address.street or None,
65 ship_from_address.street2 or None,
66 ship_from_address.city, ship_from_address.zip,
67 ship_from_address.state_id and ship_from_address.state_id.code or None,
68 ship_from_address.country_id and ship_from_address.country_id.code or None, 0).data
69 destination = BaseAddress(addSvc, shipping_address.street or None,
70 shipping_address.street2 or None,
71 shipping_address.city, shipping_address.zip,
72 shipping_address.state_id and shipping_address.state_id.code or None,
73 shipping_address.country_id and shipping_address.country_id.code or None, 1).data
74 result = avapoint.get_tax(avatax_config.company_code, doc_date, doc_type,
75 partner.name, doc_code, origin, destination,
76 lines, partner.exemption_number or None,
77 partner.exemption_code_id and partner.exemption_code_id.code or None,
78 user and user.name or None, commit, invoice_date, reference_code)
79
80 return result
81
82 def cancel_tax(self, cr, uid, avatax_config, doc_code, doc_type, cancel_code):
83 avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key,
84 avatax_config.service_url, avatax_config.request_timeout,
85 avatax_config.logging)
86 avapoint.create_tax_service()
87 result = avapoint.cancel_tax(avatax_config.company_code, doc_code, doc_type, cancel_code)
88 return result
89
90account_tax()
91
92# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
093
=== added file 'account_salestax_avatax/account_invoice_view.xml'
--- account_salestax_avatax/account_invoice_view.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/account_invoice_view.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,22 @@
1<?xml version="1.0"?>
2<openerp>
3 <data>
4
5 <!--
6 Customer Invoice/Credit Memo
7 -->
8
9 <record id="view_account_invoice_form_avatax_inherit" model="ir.ui.view">
10 <field name="name">account.invoice.form.avatax.inherit</field>
11 <field name="model">account.invoice</field>
12 <field name="type">form</field>
13 <field name="inherit_id" ref="account.invoice_form"/>
14 <field name="arch" type="xml">
15 <field name="payment_term" position="after">
16 <field name="invoice_doc_no" attrs="{'required': [('type','=','out_refund')],'invisible': [('type','!=','out_refund')]}"/>
17 </field>
18 </field>
19 </record>
20
21 </data>
22</openerp>
0\ No newline at end of file23\ No newline at end of file
124
=== added file 'account_salestax_avatax/account_invoice_workflow.xml'
--- account_salestax_avatax/account_invoice_workflow.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/account_invoice_workflow.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="account.act_open" model="workflow.activity">
6 <field name="wkf_id" ref="account.wkf"/>
7 <field name="name">open</field>
8 <field name="action">action_date_assign()
9action_move_create()
10action_number()
11write({'state':'open'})
12action_commit_tax()</field>
13 <field name="kind">function</field>
14 </record>
15
16 </data>
17</openerp>
0\ No newline at end of file18\ No newline at end of file
119
=== added file 'account_salestax_avatax/account_salestax_avatax.py'
--- account_salestax_avatax/account_salestax_avatax.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/account_salestax_avatax.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,147 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22from osv import osv, fields
23from tools.translate import _
24import decimal_precision as dp
25
26class tax_schedule(osv.osv):
27 _name = "tax.schedule"
28 _description = "Tax Schedule"
29 _columns = {
30 'name': fields.char('Name', size=64, required=True),
31 'code': fields.char('Code', size=32),
32 'jurisdiction_code_ids': fields.one2many('jurisdiction.code', 'tax_schedule_id', 'Jurisdiction Codes'),
33 'company_id': fields.many2one('res.company', 'Company', required=True),
34 'country_id': fields.many2one('res.country', 'Country', required=True),
35 }
36 _defaults = {
37 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'tax.schedule', context=c),
38 }
39
40tax_schedule()
41
42class jurisdiction_code(osv.osv):
43 _name = "jurisdiction.code"
44 _description = "Jurisdiction Code"
45 _columns = {
46 'name': fields.char('Description', size=32, required=True),
47 'type': fields.selection([('country', 'Country'), ('composite', 'Composite'), ('state', 'State'),
48 ('county', 'County'), ('city', 'City'), ('special', 'Special')], 'Type', required=True,
49 help="Type of tax jurisdiction"),
50 'state_id': fields.many2one('res.country.state', 'State', required=True, help="State for which the tax jurisdiction is defined"),
51 'code':fields.char('Code', size=32),
52 'tax_schedule_id': fields.many2one('tax.schedule', 'Tax Schedule'),
53 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account', required=True, help="Use this tax account for Invoices"),
54 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account', required=True, help="Use this tax account for Refunds"),
55 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this base code for the Invoices"),
56 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this tax code for the Invoices"),
57 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1"),
58 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1"),
59 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this base code for the Refunds"),
60 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this tax code for the Refunds"),
61 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1"),
62 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1"),
63 }
64 _defaults = {
65 'ref_tax_sign': 1,
66 'ref_base_sign': 1,
67 'tax_sign': 1,
68 'base_sign': 1,
69 }
70
71jurisdiction_code()
72
73class exemption_code(osv.osv):
74 _name = 'exemption.code'
75 _description = 'Exemption Code'
76 _columns = {
77 'name': fields.char('Name', size=64),
78 'code': fields.char('Code', size=2)
79 }
80
81 def name_get(self, cr, uid, ids, context=None):
82 if not ids:
83 return []
84 reads = self.read(cr, uid, ids, ['name', 'code'], context=context)
85 res = []
86 for record in reads:
87 name = record['name']
88 if record['code']:
89 name = '(' + record['code'] + ')' + ' ' + name
90 res.append((record['id'], name))
91 return res
92
93exemption_code()
94
95class account_salestax_avatax(osv.osv):
96 _name = 'account.salestax.avatax'
97 _description = 'AvaTax Configuration'
98 __rec_name = 'account_number'
99
100 def _get_avatax_supported_countries(self, cr, uid, context=None):
101 """ Returns the countries supported by AvaTax Address Validation Service."""
102
103 country_pool = self.pool.get('res.country')
104 return country_pool.search(cr, uid, [('code', 'in', ['US', 'CA'])], context=context)
105
106 _columns = {
107 'account_number':fields.char('Account Number', size=64, required=True, help="Account Number provided by AvaTax"),
108 'license_key': fields.char('License Key', size=64, required=True, help="License Key provided by AvaTax"),
109 'service_url': fields.char('Service URL', size=64, required=True, help="The url to connect with"),
110 'date_expiration': fields.date('Service Expiration Date', readonly=True, help="The expiration date of the service"),
111 'request_timeout': fields.integer('Request Timeout', help="Defines AvaTax request time out length, AvaTax best practices prescribes default setting of 300 seconds"),
112 'company_code': fields.char('Company Code', size=64, required=True, help="The company code as defined in the Admin Console of AvaTax"),
113 'logging': fields.boolean('Enable Logging', help="Enables detailed AvaTax transaction logging within application"),
114 'address_validation': fields.boolean('Disable Address Validation', help="Check to disable address validation"),
115 'result_in_uppercase': fields.boolean('Results in Upper Case', help="Check is address validation results desired to be in upper case"),
116 'validation_on_save': fields.boolean('Address Validation on Save', help="Check if each address when saved should be validated"),
117 'force_address_validation': fields.boolean('Force Address Validation', help="Check if address validation should be done before tax calculation"),
118 'disable_tax_calculation': fields.boolean('Disable Tax Calculation', help="Check to disable tax calculation"),
119 'default_tax_schedule_id': fields.many2one('tax.schedule', 'Default Tax Schedule', help="Identifies customers using AVATAX. Only customers with AVATAX designation triggers tax calculation from Avatax otherwise it will follow the normal tax calculation that OpenERP provides"),
120 'default_shipping_code_id': fields.many2one('product.tax.code', 'Default Shipping Code', help="The default shipping code which will be passed to Avalara"),
121 'country_ids': fields.many2many('res.country', 'account_salestax_avatax_country_rel', 'account_salestax_avatax_id', 'country_id', 'Countries', help="Countries where address validation will be used"),
122 'active': fields.boolean('Active', help="Uncheck the active field to hide the record"),
123 'company_id': fields.many2one('res.company', 'Company', required=True, help="Company which has subscribed to the AvaTax service"),
124 }
125 _defaults = {
126 'active': True,
127 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.salestax.avatax', context=c),
128 'request_timeout': 300,
129 'country_ids': _get_avatax_supported_countries
130 }
131
132 _sql_constraints = [
133 ('code_company_uniq', 'unique (company_code)', 'The code of the company must be unique!'),
134 ('account_number_company_uniq', 'unique (account_number, company_id)', 'The account number must be unique per company!'),
135 ]
136
137 def _get_avatax_config_company(self, cr, uid, context=None):
138 """ Returns the AvaTax configuration for the user company """
139
140 user_obj = self.pool.get('res.users')
141 user = user_obj.browse(cr, uid, uid, context=context)
142 avatax_config_ids = self.search(cr, uid, [('company_id', '=', user.company_id.id)], context=context)
143 return avatax_config_ids and self.browse(cr, uid, avatax_config_ids[0], context=context) or False
144
145account_salestax_avatax()
146
147# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0\ No newline at end of file148\ No newline at end of file
1149
=== added file 'account_salestax_avatax/account_salestax_avatax_data.xml'
--- account_salestax_avatax/account_salestax_avatax_data.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/account_salestax_avatax_data.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,90 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data noupdate="1">
4
5 <!--
6 Partner Exemption Code
7 -->
8
9 <record id="federal_government_type" model="exemption.code">
10 <field name="name">Federal Government</field>
11 <field name="code">A</field>
12 </record>
13
14 <record id="state_government_type" model="exemption.code">
15 <field name="name">State Government</field>
16 <field name="code">B</field>
17 </record>
18
19 <record id="tribe_indian_band_type" model="exemption.code">
20 <field name="name">Tribe / Status Indian / Indian Band</field>
21 <field name="code">C</field>
22 </record>
23
24 <record id="foreign_diplomat_type" model="exemption.code">
25 <field name="name">Foreign Diplomat</field>
26 <field name="code">D</field>
27 </record>
28
29 <record id="charitable_org_type" model="exemption.code">
30 <field name="name">Charitable or Benevolent Org</field>
31 <field name="code">E</field>
32 </record>
33
34 <record id="religious_eductional_org_type" model="exemption.code">
35 <field name="name">Religious or Educational Org</field>
36 <field name="code">F</field>
37 </record>
38
39 <record id="resale_type" model="exemption.code">
40 <field name="name">Resale</field>
41 <field name="code">G</field>
42 </record>
43
44 <record id="commercial_agriculture_production_type" model="exemption.code">
45 <field name="name">Commercial Agricultural Production</field>
46 <field name="code">H</field>
47 </record>
48
49 <record id="industrial_manufacturer_type" model="exemption.code">
50 <field name="name">Industrial Production / Manufacturer</field>
51 <field name="code">I</field>
52 </record>
53
54 <record id="direct_pay_permit_type" model="exemption.code">
55 <field name="name">Direct Pay Permit</field>
56 <field name="code">J</field>
57 </record>
58
59 <record id="direct_mail_type" model="exemption.code">
60 <field name="name">Direct Mail</field>
61 <field name="code">K</field>
62 </record>
63
64 <record id="other_type" model="exemption.code">
65 <field name="name">Other</field>
66 <field name="code">L</field>
67 </record>
68
69 <record id="local_government_type" model="exemption.code">
70 <field name="name">Local Government</field>
71 <field name="code">N</field>
72 </record>
73
74 <record id="commercial_aquaculture_type" model="exemption.code">
75 <field name="name">Commercial Aquaculture</field>
76 <field name="code">P</field>
77 </record>
78
79 <record id="commercial_fishery_type" model="exemption.code">
80 <field name="name">Commercial Fishery</field>
81 <field name="code">Q</field>
82 </record>
83
84 <record id="non_resident_type" model="exemption.code">
85 <field name="name">Non-Resident</field>
86 <field name="code">R</field>
87 </record>
88
89 </data>
90</openerp>
0\ No newline at end of file91\ No newline at end of file
192
=== added file 'account_salestax_avatax/account_salestax_avatax_view.xml'
--- account_salestax_avatax/account_salestax_avatax_view.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/account_salestax_avatax_view.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,215 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <!--
6 AvaTax API Configuration in OpenERP
7 -->
8
9 <record id="view_account_salestax_avatax_form" model="ir.ui.view">
10 <field name="name">account.salestax.avatax.form</field>
11 <field name="model">account.salestax.avatax</field>
12 <field name="type">form</field>
13 <field name="arch" type="xml">
14 <form string="AvaTax API">
15 <group col="6" colspan="4">
16 <field name="company_id" groups="base.group_multi_company"/>
17 <field name="company_code"/>
18 </group>
19 <notebook>
20 <page string="Configuration">
21 <group colspan="2" col="2">
22 <separator string="Connection" colspan="2"/>
23 <field name="account_number"/>
24 <field name="license_key" password="True"/>
25 <field name="service_url"/>
26 <group colspan="2" col="4">
27 <field name="date_expiration"/>
28 <button name="%(action_account_salestax_avatax_ping)d" string="Test Connection" type="action" icon="gtk-go-forward"/>
29 </group>
30 </group>
31 <group colspan="2" col="2">
32 <separator string="Adaptor" colspan="2"/>
33 <field name="request_timeout"/>
34 <field name="logging"/>
35 </group>
36 <group colspan="2" col="2" expand="1">
37 <separator string="Address Validation" colspan="2"/>
38 <field name="address_validation"/>
39 <field name="result_in_uppercase"/>
40 <field name="validation_on_save"/>
41 <separator string="Countries" colspan="2"/>
42 <field name="country_ids" colspan="2" nolabel="1"/>
43 </group>
44 <group colspan="2" col="2">
45 <separator string="Tax Calculation" colspan="2"/>
46 <field name="disable_tax_calculation"/>
47 <field name="force_address_validation"/>
48 <field name="default_tax_schedule_id" attrs="{'required': [('disable_tax_calculation','=', False)]}"/>
49 <field name="default_shipping_code_id" attrs="{'required': [('disable_tax_calculation','=', False)]}" domain="[('type', '=', 'freight')]"/>
50 </group>
51 </page>
52 <page string="About AvaTax">
53 <label string="Use the following for technical support:"/>
54 <newline/>
55 <label string="Publisher: Avalara.Inc"/>
56 <newline/>
57 <label string="For Technical Support: support@avalara.com"/>
58 <newline/>
59 <label string="Support Information: http://www.avalara.com/Contact-Us"/>
60 <newline/>
61 <label string="Support Telephone: 877-780-4848"/>
62 </page>
63 </notebook>
64 </form>
65 </field>
66 </record>
67
68 <record id="view_account_salestax_avatax_tree" model="ir.ui.view">
69 <field name="name">account.salestax.avatax.tree</field>
70 <field name="model">account.salestax.avatax</field>
71 <field name="type">tree</field>
72 <field name="arch" type="xml">
73 <tree string="AvaTax API">
74 <field name="company_id" groups="base.group_multi_company"/>
75 <field name="company_code"/>
76 <field name="account_number"/>
77 <field name="service_url"/>
78 <field name="date_expiration"/>
79 </tree>
80 </field>
81 </record>
82
83 <record id="action_account_salestax_avatax" model="ir.actions.act_window">
84 <field name="name">AvaTax API</field>
85 <field name="res_model">account.salestax.avatax</field>
86 <field name="view_type">form</field>
87 <field name="view_mode">tree,form</field>
88 <field name="help">Configuration of AvaTax in OpenERP</field>
89 </record>
90
91 <menuitem id="menu_avatax" name="AvaTax" parent="account.menu_finance_accounting" sequence="30"/>
92
93 <menuitem action="action_account_salestax_avatax" id="menu_avatax_api" name="AvaTax API" parent="menu_avatax" sequence="30"/>
94
95 <!--
96 Jurisdiction Code
97 -->
98
99 <record id="view_jurisdiction_code_form" model="ir.ui.view">
100 <field name="name">jurisdiction.code.form</field>
101 <field name="model">jurisdiction.code</field>
102 <field name="type">form</field>
103 <field name="arch" type="xml">
104 <form string="Tax Jurisdiction Code">
105 <group col="6" colspan="4">
106 <field name="name"/>
107 <field name="code"/>
108 <field name="type"/>
109 <field name="state_id" domain="[('country_id','=',country_id)]"/>
110 </group>
111 <separator colspan="4" string="Accounting Information"/>
112 <field name="account_collected_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
113 <field name="account_paid_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
114 <separator colspan="4" string="Tax Declaration: Invoices"/>
115 <field name="base_code_id"/>
116 <field name="base_sign"/>
117 <field name="tax_code_id"/>
118 <field name="tax_sign"/>
119 <separator colspan="4" string="Tax Declaration: Credit Notes"/>
120 <field name="ref_base_code_id"/>
121 <field name="ref_base_sign"/>
122 <field name="ref_tax_code_id"/>
123 <field name="ref_tax_sign"/>
124 </form>
125 </field>
126 </record>
127
128 <record id="view_jurisdiction_code_tree" model="ir.ui.view">
129 <field name="name">jurisdiction.code.tree</field>
130 <field name="model">jurisdiction.code</field>
131 <field name="type">tree</field>
132 <field name="arch" type="xml">
133 <tree string="Tax Jurisdiction Code">
134 <field name="code"/>
135 <field name="name"/>
136 <field name="type"/>
137 <field name="state_id"/>
138 </tree>
139 </field>
140 </record>
141
142 <!--
143 Tax Schedule
144 -->
145
146 <record id="view_tax_schedule_tree" model="ir.ui.view">
147 <field name="name">tax.schedule.tree</field>
148 <field name="model">tax.schedule</field>
149 <field name="type">tree</field>
150 <field name="arch" type="xml">
151 <tree string="Tax Schedule">
152 <field name="code"/>
153 <field name="name"/>
154 <field name="country_id"/>
155 <field name="company_id" groups="base.group_multi_company"/>
156 </tree>
157 </field>
158 </record>
159
160 <record id="view_tax_schedule_form" model="ir.ui.view">
161 <field name="name">tax.schedule.form</field>
162 <field name="model">tax.schedule</field>
163 <field name="type">form</field>
164 <field name="arch" type="xml">
165 <form string="Tax Schedule">
166 <group col="6" colspan="4">
167 <field name="name"/>
168 <field name="code"/>
169 <field name="country_id"/>
170 <field name="company_id" groups="base.group_multi_company"/>
171 </group>
172 <field name="jurisdiction_code_ids" nolabel="1">
173 <form string="Tax Jurisdiction Codes">
174 <group col="6" colspan="4">
175 <field name="name"/>
176 <field name="code"/>
177 <field name="type"/>
178 <field name="state_id" domain="[('country_id','=',parent.country_id)]"/>
179 </group>
180 <separator colspan="4" string="Accounting Information"/>
181 <field name="account_collected_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
182 <field name="account_paid_id" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
183 <separator colspan="4" string="Tax Declaration: Invoices"/>
184 <field name="base_code_id"/>
185 <field name="base_sign"/>
186 <field name="tax_code_id"/>
187 <field name="tax_sign"/>
188 <separator colspan="4" string="Tax Declaration: Credit Notes"/>
189 <field name="ref_base_code_id"/>
190 <field name="ref_base_sign"/>
191 <field name="ref_tax_code_id"/>
192 <field name="ref_tax_sign"/>
193 </form>
194 <tree string="Tax Jurisdiction Codes">
195 <field name="code"/>
196 <field name="name"/>
197 <field name="state_id"/>
198 <field name="type"/>
199 </tree>
200 </field>
201 </form>
202 </field>
203 </record>
204
205 <record id="action_tax_schedule" model="ir.actions.act_window">
206 <field name="name">Tax Schedules</field>
207 <field name="res_model">tax.schedule</field>
208 <field name="view_type">form</field>
209 <field name="view_mode">tree,form</field>
210 </record>
211
212 <menuitem action="action_tax_schedule" id="menu_tax_schedule" name="Tax Schedules" parent="menu_avatax" sequence="29"/>
213
214 </data>
215</openerp>
0\ No newline at end of file216\ No newline at end of file
1217
=== added file 'account_salestax_avatax/invoice.py'
--- account_salestax_avatax/invoice.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/invoice.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,252 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22import time
23import string
24
25from osv import osv, fields
26from tools.translate import _
27import decimal_precision as dp
28
29class account_invoice(osv.osv):
30 _inherit = "account.invoice"
31
32 def _avatax_calc(self, cr, uid, ids, name, args, context=None):
33 res = {}
34 avatax_config_obj = self.pool.get('account.salestax.avatax')
35 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid)
36
37 for invoice in self.browse(cr, uid, ids, context=context):
38 if invoice.type in ['out_invoice', 'out_refund'] and \
39 avatax_config and not avatax_config.disable_tax_calculation and \
40 avatax_config.default_tax_schedule_id.id == invoice.partner_id.tax_schedule_id.id:
41 res[invoice.id] = True
42 else:
43 res[invoice.id] = False
44 return res
45
46 _columns = {
47 'invoice_doc_no': fields.char('Invoice No', size=32, readonly=True, states={'draft':[('readonly',False)]}, help="Reference of the invoice"),
48 'invoice_date': fields.date('Invoice Date', readonly=True),
49 'avatax_calc': fields.function(_avatax_calc, method=True, string='Avatax Calculation', type='boolean', store=True)
50 }
51
52 def action_commit_tax(self, cr, uid, ids, context=None):
53 avatax_config_obj = self.pool.get('account.salestax.avatax')
54 account_tax_obj = self.pool.get('account.tax')
55 partner_obj = self.pool.get('res.partner')
56 for invoice in self.browse(cr, uid, ids, context=context):
57 if invoice.avatax_calc:
58 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid)
59 sign = invoice.type == 'out_invoice' and 1 or -1
60 lines = self.create_lines(cr, uid, invoice.invoice_line, sign)
61 address = partner_obj.address_get(cr, uid, [invoice.company_id.partner_id.id], ['contact'])
62 account_tax_obj._check_compute_tax(cr, uid, avatax_config, invoice.date_invoice,
63 invoice.internal_number, not invoice.invoice_doc_no and 'SalesInvoice' or 'ReturnInvoice',
64 invoice.partner_id, address['contact'],
65 invoice.address_invoice_id.id, lines, invoice.shipcharge, invoice.user_id,
66 True, invoice.invoice_date,
67 invoice.invoice_doc_no, context=context)
68 return True
69
70 def create_lines(self, cr, uid, invoice_lines, sign):
71 lines = []
72 for line in invoice_lines:
73 lines.append({
74 'qty': line.quantity,
75 'itemcode': line.product_id and line.product_id.default_code or None,
76 'description': line.name,
77 'amount': sign * line.price_unit * (1-(line.discount or 0.0)/100.0) * line.quantity,
78 'tax_code': line.product_id and ((line.product_id.tax_code_id and line.product_id.tax_code_id.name) or
79 (line.product_id.categ_id.tax_code_id and line.product_id.categ_id.tax_code_id.name)) or None
80 })
81 return lines
82
83 def refund(self, cr, uid, ids, date=None, period_id=None, description=None, journal_id=None):
84 refund_ids = super(account_invoice, self).refund(cr, uid, ids, date, period_id, description, journal_id)
85 invoice = self.browse(cr, uid, ids[0])
86 if invoice.avatax_calc:
87 self.write(cr, uid, refund_ids[0], {
88 'invoice_doc_no': invoice.internal_number,
89 'invoice_date': invoice.date_invoice
90 })
91 return refund_ids
92
93 def action_cancel(self, cr, uid, ids, *args):
94 account_tax_obj = self.pool.get('account.tax')
95 res = super(account_invoice, self).action_cancel(cr, uid, ids, *args)
96
97 for invoice in self.browse(cr, uid, ids, *args):
98 if invoice.avatax_calc and invoice.internal_number:
99 doc_type = invoice.type == 'out_invoice' and 'SalesInvoice' or 'ReturnInvoice'
100 account_tax_obj.cancel_tax(cr, uid, avatax_config, invoice.internal_number, doc_type, 'DocVoided')
101 return res
102
103 def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj):
104 super(account_invoice, self).check_tax_lines(cr, uid, inv, compute_taxes, ait_obj)
105 if inv.avatax_calc:
106 for tax in inv.tax_line:
107 key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
108 if abs(compute_taxes[key]['amount'] - tax.amount) > inv.company_id.currency_id.rounding:
109 raise osv.except_osv(_('Warning !'), _('Tax amount different !\nClick on compute to update tax base'))
110
111account_invoice()
112
113class account_invoice_tax(osv.osv):
114 _inherit = "account.invoice.tax"
115
116 def compute(self, cr, uid, invoice_id, context=None):
117 avatax_config_obj = self.pool.get('account.salestax.avatax')
118 invoice_obj = self.pool.get('account.invoice')
119 partner_obj = self.pool.get('res.partner')
120 account_tax_obj = self.pool.get('account.tax')
121 jurisdiction_code_obj = self.pool.get('jurisdiction.code')
122 cur_obj = self.pool.get('res.currency')
123 state_obj = self.pool.get('res.country.state')
124 invoice = invoice_obj.browse(cr, uid, invoice_id, context=context)
125 tax_grouped = {}
126 if invoice.avatax_calc:
127 vals = {}
128 cur = invoice.currency_id
129 company_currency = invoice.company_id.currency_id.id
130 lines = invoice_obj.create_lines(cr, uid, invoice.invoice_line, 1)
131 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid)
132 # to check for company address
133 company_address = partner_obj.address_get(cr, uid, [invoice.company_id.partner_id.id], ['default'])
134 for tax in account_tax_obj._check_compute_tax(cr, uid, avatax_config,
135 invoice.date_invoice or time.strftime('%Y-%m-%d'),
136 invoice.internal_number, 'SalesOrder', invoice.partner_id,
137 company_address['default'], invoice.address_invoice_id.id,
138 lines, invoice.shipcharge, invoice.user_id, False,
139 invoice.date_invoice or time.strftime('%Y-%m-%d'),
140 context=context).TaxSummary[0]:
141 val = {}
142 state_ids = state_obj.search(cr, uid, [('code', '=', tax.Region)], context=context)
143 state_id = state_ids and state_ids[0] or False
144 jurisdiction_code_ids = jurisdiction_code_obj.search(cr, uid, [('type', '=', tax['JurisType'].lower()),
145 ('tax_schedule_id', '=', avatax_config.default_tax_schedule_id.id),
146 ('state_id', '=', state_id)],
147 context=context)
148 if not jurisdiction_code_ids:
149 raise osv.except_osv(
150 _('Jurisdiction Code is not defined !'),
151 _('You must define a jurisdiction code for %s type for %s state in the tax schedule for %s.'
152 % (tax['JurisType'], tax['Region'], avatax_config.default_tax_schedule_id.name)))
153 jurisdiction_code = jurisdiction_code_obj.browse(cr, uid, jurisdiction_code_ids[0], context=context)
154
155 val['invoice_id'] = invoice.id
156 val['name'] = tax['TaxName'] or '/'
157 val['amount'] = tax['Tax']
158 val['manual'] = False
159 val['base'] = tax['Base']
160
161 if invoice.type == 'out_invoice':
162 val['base_code_id'] = jurisdiction_code.base_code_id.id
163 val['tax_code_id'] = jurisdiction_code.tax_code_id.id
164 val['base_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id,
165 company_currency, val['base'] * jurisdiction_code.base_sign,
166 context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
167 val['tax_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id,
168 company_currency, val['amount'] * jurisdiction_code.tax_sign,
169 context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
170 val['account_id'] = jurisdiction_code.account_collected_id.id
171 val['base_sign'] = jurisdiction_code.base_sign
172 else:
173 val['base_code_id'] = jurisdiction_code.ref_base_code_id.id
174 val['ref_base_code_id'] = jurisdiction_code.ref_base_code_id.id
175 val['tax_code_id'] = jurisdiction_code.ref_tax_code_id.id
176 val['base_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id,
177 company_currency, val['base'] * jurisdiction_code.ref_base_sign,
178 context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
179 val['tax_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id,
180 company_currency, val['amount'] * jurisdiction_code.ref_tax_sign,
181 context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
182 val['account_id'] = jurisdiction_code.account_paid_id.id
183 val['ref_base_sign'] = jurisdiction_code.ref_base_sign
184
185 key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
186 if not key in tax_grouped:
187 tax_grouped[key] = val
188 else:
189 tax_grouped[key]['amount'] += val['amount']
190 tax_grouped[key]['base'] += val['base']
191 tax_grouped[key]['base_amount'] += val['base_amount']
192 tax_grouped[key]['tax_amount'] += val['tax_amount']
193
194 for t in tax_grouped.values():
195 t['base'] = cur_obj.round(cr, uid, cur, t['base'])
196 t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
197 t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
198 t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
199 return tax_grouped
200 return super(account_invoice_tax, self).compute(cr, uid, invoice_id, context=context)
201
202account_invoice_tax()
203
204class account_invoice_line(osv.osv):
205 _inherit = "account.invoice.line"
206
207 def move_line_get(self, cr, uid, invoice_id, context=None):
208 res = []
209 tax_obj = self.pool.get('account.tax')
210 cur_obj = self.pool.get('res.currency')
211 invoice_obj = self.pool.get('account.invoice')
212 ait_obj = self.pool.get('account.invoice.tax')
213 invoice = invoice_obj.browse(cr, uid, invoice_id, context=context)
214 company_currency = invoice.company_id.currency_id.id
215
216 if invoice.avatax_calc:
217 for line in invoice.invoice_line:
218 mres = self.move_line_get_item(cr, uid, line, context)
219 if not mres:
220 continue
221 res.append(mres)
222 tax_code_found= False
223
224 for tax in ait_obj.compute(cr, uid, invoice_id, context=context).values():
225 if invoice.type == 'out_invoice':
226 tax_code_id = tax['base_code_id']
227 tax_amount = line.price_subtotal * tax['base_sign']
228 else:
229 tax_code_id = tax['ref_base_code_id']
230 tax_amount = line.price_subtotal * tax['ref_base_sign']
231
232 if tax_code_found:
233 if not tax_code_id:
234 continue
235 res.append(self.move_line_get_item(cr, uid, line, context))
236 res[-1]['price'] = 0.0
237 res[-1]['account_analytic_id'] = False
238 elif not tax_code_id:
239 continue
240 tax_code_found = True
241
242 res[-1]['tax_code_id'] = tax_code_id
243 res[-1]['tax_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id, company_currency,
244 tax_amount, context={'date': invoice.date_invoice})
245
246 else:
247 res = super(account_invoice_line, self).move_line_get( cr, uid, invoice_id, context=context)
248 return res
249
250account_invoice_line()
251
252# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0\ No newline at end of file253\ No newline at end of file
1254
=== added file 'account_salestax_avatax/partner.py'
--- account_salestax_avatax/partner.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/partner.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,169 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22import time
23
24from osv import osv, fields
25from tools.translate import _
26import decimal_precision as dp
27
28from suds_client import AvaTaxService, BaseAddress
29
30class res_partner(osv.osv):
31 _inherit = 'res.partner'
32 _columns = {
33 'exemption_number': fields.char('Exemption Number', size=64, help="Indicates if the customer is exempt or not"),
34 'exemption_code_id': fields.many2one('exemption.code', 'Exemption Code', help="Indicates the type of exemption the customer may have"),
35 'tax_schedule_id': fields.many2one('tax.schedule', 'Tax Schedule', help="Identifies customers using AVATAX. Only customers with AVATAX designation triggers tax calculation from Avatax otherwise it will follow the normal tax calculation that OpenERP provides")
36 }
37
38res_partner()
39
40class res_partner_address(osv.osv):
41 _inherit = 'res.partner.address'
42 _columns = {
43 'date_validation': fields.date('Last Validation Date', readonly=True, help="The date the address was last validated by AvaTax and accepted"),
44 'validation_method': fields.selection([('avatax', 'AVATAX'), ('usps', 'USPS'), ('other', 'Other')], 'Address Validation Method', readonly=True, help="It gets populated when the address is validated by the method"),
45 'latitude': fields.char('Latitude', size=32),
46 'longitude': fields.char('Longitude', size=32),
47 'validated_on_save': fields.boolean('Validated On Save', help="Indicates if the address is already validated on save before calling the wizard")
48 }
49
50 def check_avatax_support(self, cr, uid, avatax_config, country_id, context=None):
51 """ Checks if address validation pre-condition meets. """
52
53 if avatax_config.address_validation:
54 raise osv.except_osv(_('Address Validation is Disabled'), _("The AvaTax Address Validation Service is disabled by the administrator. Please make sure it's enabled for the address validation"))
55 if country_id and country_id not in [x.id for x in avatax_config.country_ids]:
56 raise osv.except_osv(_('Address Validation not Supported for this country'), _("The AvaTax Address Validation Service does not support this country in the configuration, please continue with your normal process."))
57 return True
58
59 def get_state_id(self, cr, uid, code, context=None):
60 """ Returns the id of the state from the code. """
61
62 state_obj = self.pool.get('res.country.state')
63 return state_obj.search(cr, uid, [('code', '=', code)], context=context)[0]
64
65 def get_country_id(self, cr, uid, code, context=None):
66 """ Returns the id of the country from the code. """
67
68 country_obj = self.pool.get('res.country')
69 return country_obj.search(cr, uid, [('code', '=', code)], context=context)[0]
70
71 def get_state_code(self, cr, uid, state_id, context=None):
72 """ Returns the code from the id of the state. """
73
74 state_obj = self.pool.get('res.country.state')
75 return state_id and state_obj.browse(cr, uid, state_id, context=context).code
76
77 def get_country_code(self, cr, uid, country_id, context=None):
78 """ Returns the code from the id of the country. """
79
80 country_obj = self.pool.get('res.country')
81 return country_id and country_obj.browse(cr, uid, country_id, context=context).code
82
83 def _validate_address(self, cr, uid, address, avatax_config=False, context=None):
84 """ Returns the valid address from the AvaTax Address Validation Service. """
85
86 avatax_config_obj= self.pool.get('account.salestax.avatax')
87 if context is None:
88 context = {}
89
90 if not avatax_config:
91 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid, context=context)
92
93 # Create the AvaTax Address service with the configuration parameters set for the instance
94 avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key,
95 avatax_config.service_url, avatax_config.request_timeout, avatax_config.logging)
96 addSvc = avapoint.create_address_service().addressSvc
97
98 # Obtain the state code & country code and create a BaseAddress Object
99 state_code = address.get('state_id') and self.get_state_code(cr, uid, address['state_id'], context=context)
100 country_code = address.get('country_id') and self.get_country_code(cr, uid, address['country_id'], context=context)
101 baseaddress = BaseAddress(addSvc, address.get('street') or None, address.get('street2') or None,
102 address.get('city'), address.get('zip'), state_code, country_code, 0).data
103 result = avapoint.validate_address(baseaddress, avatax_config.result_in_uppercase and 'Upper' or 'Default')
104 valid_address = result.ValidAddresses[0][0]
105 return valid_address
106
107 def update_address(self, cr, uid, vals, ids=None, from_write=False, context=None):
108 """ Updates the vals dictionary with the valid address as returned from the AvaTax Address Validation. """
109
110 address = vals
111
112 if (vals.get('street') or vals.get('street2') or vals.get('zip') or vals.get('city') or \
113 vals.get('country_id') or vals.get('state_id')):
114
115 address_obj = self.pool.get('res.partner.address')
116 avatax_config_obj= self.pool.get('account.salestax.avatax')
117 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid, context=context)
118
119 if avatax_config and avatax_config.validation_on_save:
120 # It implies that there is AvaTax configuration existing for the user company with
121 # option 'Address Validation when a address is saved'
122 # Check if the other conditions are met
123 self.check_avatax_support(cr, uid, avatax_config, address.get('country_id'), context=context)
124
125 # If this method is called from the 'write' method then we also need to pass
126 # the previous address along with the modifications in the vals dictionary
127 if from_write:
128 fields_to_read = filter(lambda x: x not in vals, ['street', 'street2', 'city', 'state_id', 'zip', 'country_id'])
129 address = fields_to_read and address_obj.read(cr, uid, ids, fields_to_read, context=context)[0] or {}
130 address['state_id'] = address.get('state_id') and address['state_id'][0]
131 address['country_id'] = address.get('country_id') and address['country_id'][0]
132 address.update(vals)
133
134 valid_address = self._validate_address(cr, uid, address, avatax_config, context=context)
135 vals.update({
136 'street': valid_address.Line1,
137 'street2': valid_address.Line2,
138 'city': valid_address.City,
139 'state_id': self.get_state_id(cr, uid, valid_address.Region, context=context),
140 'zip': valid_address.PostalCode,
141 'country_id': self.get_country_id(cr, uid, valid_address.Country, context=context),
142 'latitude': valid_address.Latitude,
143 'longitude': valid_address.Longitude,
144 'date_validation': time.strftime('%Y-%m-%d'),
145 'validation_method': 'avatax',
146 'validated_on_save': True
147 })
148 return vals
149
150
151 def create(self, cr, uid, vals, context=None):
152 vals = self.update_address(cr, uid, vals, context=context)
153 return super(res_partner_address, self).create(cr, uid, vals, context)
154
155 def write(self, cr, uid, ids, vals, context=None):
156 if context is None:
157 context = {}
158
159 # Follow the normal write process if it's a write operation from the wizard
160 if context.get('from_validate_button', False):
161 return super(res_partner_address, self).write(cr, uid, ids, vals, context)
162
163 if context.get('active_id', False):
164 vals = self.update_address(cr, uid, vals, ids, True, context=context)
165 return super(res_partner_address, self).write(cr, uid, ids, vals, context)
166
167res_partner_address()
168
169# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0\ No newline at end of file170\ No newline at end of file
1171
=== added file 'account_salestax_avatax/partner_view.xml'
--- account_salestax_avatax/partner_view.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/partner_view.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,63 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <!--
6 Partner Tax & Address Validation
7 -->
8
9 <record id="view_partner_details_form_inherit" model="ir.ui.view">
10 <field name="name">res.partner.details.form.inherit</field>
11 <field name="model">res.partner</field>
12 <field name="type">form</field>
13 <field name="inherit_id" ref="base.view_partner_form"/>
14 <field name="arch" type="xml">
15 <xpath expr="/form/notebook/page[@string='Accounting']/field[@name='bank_ids']" position="before">
16 <group col="1" colspan="2">
17 <separator string="Tax Details" colspan="2" col="2"/>
18 <field name="tax_schedule_id"/>
19 <field name="exemption_number"/>
20 <field name="exemption_code_id"/>
21 </group>
22 </xpath>
23 <xpath expr="/form/notebook/page[@string='General']/field[@name='address']/form" position="inside">
24 <group col="4" colspan="2">
25 <field name="latitude"/>
26 <field name="longitude"/>
27 </group>
28 <newline/>
29 <group colspan="2" col="4">
30 <separator string="Validation" colspan="4" col="4"/>
31 <field name="date_validation"/>
32 <field name="validation_method"/>
33 <button name="%(action_account_salestax_avatax_address_validate)d" string="_Validate" type="action" icon="terp-camera_test" colspan="2" context="{'from_validate_button': True}"/>
34 </group>
35 </xpath>
36 </field>
37 </record>
38
39 <!--
40 Partner Address
41 -->
42
43 <record id="view_partner_address_details_form_inherit" model="ir.ui.view">
44 <field name="name">res.partner.address.details.form.inherit</field>
45 <field name="model">res.partner.address</field>
46 <field name="type">form</field>
47 <field name="inherit_id" ref="base.view_partner_address_form1"/>
48 <field name="arch" type="xml">
49 <xpath expr="/form[@string='Address']/group[@col='2']/field[@name='state_id']" position="after">
50 <field name="latitude"/>
51 <field name="longitude"/>
52 <group colspan="2" col="4">
53 <separator string="Validation" colspan="4" col="4"/>
54 <field name="date_validation"/>
55 <field name="validation_method"/>
56 <button name="%(action_account_salestax_avatax_address_validate)d" string="_Validate" type="action" icon="gtk-go-forward" colspan="2" context="{'from_validate_button': True}"/>
57 </group>
58 </xpath>
59 </field>
60 </record>
61
62 </data>
63</openerp>
064
=== added file 'account_salestax_avatax/product.py'
--- account_salestax_avatax/product.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/product.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,57 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22from osv import osv, fields
23
24class product_tax_code(osv.osv):
25 _name = 'product.tax.code'
26 _description = 'Tax Code'
27 _columns = {
28 'name': fields.char('Code', size=8, required=True),
29 'description': fields.char('Description', size=64),
30 'type': fields.selection([('product', 'Product'), ('freight', 'Freight'), ('service', 'Service'),
31 ('digital', 'Digital'), ('other', 'Other')], 'Type', required=True, help="Type of tax code as defined in AvaTax"),
32 'company_id': fields.many2one('res.company', 'Company', required=True),
33 }
34 _defaults = {
35 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.tax.code', context=c),
36 }
37
38product_tax_code()
39
40
41class product_template(osv.osv):
42 _inherit = "product.template"
43 _columns = {
44 'tax_code_id': fields.many2one('product.tax.code', 'Tax Code', help="AvaTax Tax Code")
45 }
46
47product_template()
48
49class product_category(osv.osv):
50 _inherit = "product.category"
51 _columns = {
52 'tax_code_id': fields.many2one('product.tax.code', 'Tax Code', help="AvaTax Tax Code")
53 }
54
55product_category()
56
57# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0\ No newline at end of file58\ No newline at end of file
159
=== added file 'account_salestax_avatax/product_view.xml'
--- account_salestax_avatax/product_view.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/product_view.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,108 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <!--
6 Product Tax Code
7 -->
8
9 <record id="view_product_tax_code_tree" model="ir.ui.view">
10 <field name="name">product.tax.code.tree</field>
11 <field name="model">product.tax.code</field>
12 <field name="type">tree</field>
13 <field name="arch" type="xml">
14 <tree string="Product Tax Code">
15 <field name="name"/>
16 <field name="description"/>
17 <field name="type"/>
18 <field name="company_id" groups="base.group_multi_company"/>
19 </tree>
20 </field>
21 </record>
22
23 <record id="view_product_tax_code_form" model="ir.ui.view">
24 <field name="name">product.tax.code.form</field>
25 <field name="model">product.tax.code</field>
26 <field name="type">form</field>
27 <field name="arch" type="xml">
28 <form string="Product Tax Code">
29 <field name="name"/>
30 <field name="company_id" groups="base.group_multi_company"/>
31 <field name="description"/>
32 <field name="type"/>
33 </form>
34 </field>
35 </record>
36
37
38 <record id="action_product_tax_code" model="ir.actions.act_window">
39 <field name="name">Product Tax Codes</field>
40 <field name="res_model">product.tax.code</field>
41 <field name="view_type">form</field>
42 <field name="view_mode">tree,form</field>
43 </record>
44
45 <menuitem action="action_product_tax_code" id="menu_product_tax_code" name="Product Tax Codes" parent="menu_avatax" sequence="29"/>
46
47 <!--
48 Product
49 -->
50
51 <record id="view_product_normal_form_avatax_inherit" model="ir.ui.view">
52 <field name="name">product.normal.form.avatax.inherit</field>
53 <field name="model">product.product</field>
54 <field name="type">form</field>
55 <field name="inherit_id" ref="product.product_normal_form_view"/>
56 <field name="priority">30</field>
57 <field name="arch" type="xml">
58 <xpath expr="/form/notebook/page[@string='Accounting']/group[@name='properties']" position="inside">
59 <separator string="AvaTax Properties" colspan="4"/>
60 <group colspan="2" col="2">
61 <field name="tax_code_id"/>
62 </group>
63 </xpath>
64 </field>
65 </record>
66
67 <!--
68 Product Template
69 -->
70
71 <record id="view_product_template_form_avatax_inherit" model="ir.ui.view">
72 <field name="name">product.template.form.avatax.inherit</field>
73 <field name="model">product.template</field>
74 <field name="type">form</field>
75 <field name="inherit_id" ref="product.product_template_form_view"/>
76 <field name="priority">30</field>
77 <field name="arch" type="xml">
78 <xpath expr="/form/notebook/page[@string='Accounting']" position="inside">
79 <separator string="AvaTax Properties" colspan="4"/>
80 <group colspan="2" col="2">
81 <field name="tax_code_id"/>
82 </group>
83 <newline/>
84 </xpath>
85 </field>
86 </record>
87
88 <!--
89 Product Category
90 -->
91
92 <record id="view_product_category_form_avatax_inherit" model="ir.ui.view">
93 <field name="name">product.category.form.avatax.inherit</field>
94 <field name="model">product.category</field>
95 <field name="type">form</field>
96 <field name="inherit_id" ref="product.product_category_form_view"/>
97 <field name="arch" type="xml">
98 <form position="inside">
99 <group col="2" colspan="2">
100 <separator string="AvaTax Properties" colspan="2"/>
101 <field name="tax_code_id"/>
102 </group>
103 </form>
104 </field>
105 </record>
106
107 </data>
108</openerp>
0109
=== added file 'account_salestax_avatax/sale.py'
--- account_salestax_avatax/sale.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/sale.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,118 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22from osv import osv, fields
23from tools.translate import _
24import decimal_precision as dp
25
26class sale_order(osv.osv):
27 _inherit = "sale.order"
28
29 def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
30 currency_obj = self.pool.get('res.currency')
31 res = {}
32 avatax_config_obj = self.pool.get('account.salestax.avatax')
33 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid)
34 res = super(sale_order, self)._amount_all(cr, uid, ids, field_name, arg, context=context)
35 for order in self.browse(cr, uid, ids, context=context):
36 if avatax_config and not avatax_config.disable_tax_calculation and \
37 avatax_config.default_tax_schedule_id.id == order.partner_id.tax_schedule_id.id:
38
39 res[order.id] = {
40 'amount_untaxed': 0.0,
41 'amount_tax': 0.0,
42 'amount_total': 0.0,
43 }
44 for line in order.order_line:
45 res[order.id]['amount_untaxed'] += line.price_subtotal
46 res[order.id]['amount_tax'] = currency_obj.round(cr, uid, order.pricelist_id.currency_id, order.tax_amount)
47 res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
48
49 return res
50
51 def _get_order(self, cr, uid, ids, context=None):
52 return super(sale_order, self)._get_order(cr, uid, ids, context=context)
53
54 _columns = {
55 'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Untaxed Amount',
56 store = {
57 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
58 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
59 },
60 multi='sums', help="The amount without tax."),
61 'amount_tax': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Taxes',
62 store = {
63 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
64 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
65 },
66 multi='sums', help="The tax amount."),
67 'amount_total': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Total',
68 store = {
69 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
70 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
71 },
72 multi='sums', help="The total amount."),
73 'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Sale Price')),
74 }
75
76 def create_lines(self, cr, uid, order_lines):
77 lines = []
78 for line in order_lines:
79 lines.append({
80 'qty': line.product_uom_qty,
81 'itemcode': line.product_id and line.product_id.default_code or None,
82 'description': line.name,
83 'amount': line.price_unit * (1-(line.discount or 0.0)/100.0) * line.product_uom_qty,
84 'tax_code': line.product_id and ((line.product_id.tax_code_id and line.product_id.tax_code_id.name) or
85 (line.product_id.categ_id.tax_code_id and line.product_id.categ_id.tax_code_id.name)) or None
86 })
87 return lines
88
89 def compute_tax(self, cr, uid, ids, context=None):
90 avatax_config_obj = self.pool.get('account.salestax.avatax')
91 account_tax_obj = self.pool.get('account.tax')
92 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid)
93 partner_obj = self.pool.get('res.partner')
94
95 for order in self.browse(cr, uid, ids):
96 if avatax_config and not avatax_config.disable_tax_calculation and \
97 avatax_config.default_tax_schedule_id.id == order.partner_id.tax_schedule_id.id:
98 address = partner_obj.address_get(cr, uid, [order.company_id.partner_id.id], ['contact'])
99 lines = self.create_lines(cr, uid, order.order_line)
100 tax_amount = account_tax_obj._check_compute_tax(cr, uid, avatax_config, order.date_confirm or order.date_order,
101 order.name, 'SalesOrder', order.partner_id, address['contact'],
102 order.partner_shipping_id.id, lines, order.shipcharge, order.user_id,
103 context=context).TotalTax
104 self.write(cr, uid, [order.id], {'tax_amount': tax_amount, 'order_line': []})
105 return True
106
107 def button_dummy(self, cr, uid, ids, context=None):
108 self.compute_tax(cr, uid, ids, context=context)
109 return super(sale_order, self).button_dummy(cr, uid, ids, context=context)
110
111 def action_wait(self, cr, uid, ids, *args):
112 res = super(sale_order, self).action_wait(cr, uid, ids)
113 self.compute_tax(cr, uid, ids)
114 return True
115
116sale_order()
117
118# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0119
=== added directory 'account_salestax_avatax/security'
=== added file 'account_salestax_avatax/security/account_salestax_avatax_security.xml'
--- account_salestax_avatax/security/account_salestax_avatax_security.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/security/account_salestax_avatax_security.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,29 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data noupdate="1">
4
5 <!-- Rule for multi-company -->
6
7 <record id="account_salestax_avatax_comp_rule" model="ir.rule">
8 <field name="name" >AvaTax multi-company</field>
9 <field name="model_id" ref="model_account_salestax_avatax"/>
10 <field name="global" eval="True"/>
11 <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
12 </record>
13
14 <record id="account_salestax_avatax_tax_schedule_rule" model="ir.rule">
15 <field name="name" >AvaTax Tax Schedule multi-company</field>
16 <field name="model_id" ref="model_tax_schedule"/>
17 <field name="global" eval="True"/>
18 <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
19 </record>
20
21 <record id="account_salestax_avatax_tax_codes_rule" model="ir.rule">
22 <field name="name" >AvaTax Tax Code multi-company</field>
23 <field name="model_id" ref="model_product_tax_code"/>
24 <field name="global" eval="True"/>
25 <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
26 </record>
27
28 </data>
29</openerp>
030
=== added file 'account_salestax_avatax/security/ir.model.access.csv'
--- account_salestax_avatax/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/security/ir.model.access.csv 2011-09-22 18:37:20 +0000
@@ -0,0 +1,11 @@
1"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
2"access_account_salestax_avatax manager","account.salestax.avatax.manager","model_account_salestax_avatax","account.group_account_manager",1,1,1,1
3"access_account_salestax_avatax employee","account.salestax.avatax.employee","model_account_salestax_avatax","base.group_user",1,0,0,0
4"access_tax_schedule employee","tax.schedule.employee","model_tax_schedule","base.group_user",1,0,0,0
5"access_tax_schedule manager","tax.schedule.manager","model_tax_schedule","account.group_account_manager",1,1,1,1
6"access_jurisdiction_code employee","jurisdiction.code.employee","model_jurisdiction_code","base.group_user",1,0,0,0
7"access_jurisdiction_code manager","jurisdiction.code.manager","model_jurisdiction_code","account.group_account_manager",1,1,1,1
8"access_product_tax_code employee","product.tax.code.employee","model_product_tax_code","base.group_user",1,0,0,0
9"access_product_tax_code manager","product.tax.code.manager","model_product_tax_code","account.group_account_manager",1,1,1,1
10"access_exemption_code manager","exemption.code.manager","model_exemption_code","account.group_account_manager",1,1,1,1
11"access_exemption_code employee","exemption.code.employee","model_exemption_code","base.group_user",1,0,0,0
012
=== added file 'account_salestax_avatax/suds_client.py'
--- account_salestax_avatax/suds_client.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/suds_client.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,295 @@
1import suds
2import socket
3import urllib2
4import string
5import os
6import datetime
7
8from tools.translate import _
9from osv import osv
10
11class AvaTaxService:
12
13 def enable_log(self):
14 import logging, tempfile
15 logger = logging.getLogger('suds.client')
16 logger.setLevel(logging.DEBUG)
17 handler = logging.FileHandler(os.path.join(tempfile.gettempdir(), "soap-messages.log"))
18 logger.propagate = False
19 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
20 handler.setFormatter(formatter)
21 logger.addHandler(handler)
22
23 def __init__(self, username, password, url, timeout, enable_log=False):
24 self.username = username #This is the company's Development/Production Account number
25 self.password = password #Put in the License Key received from AvaTax
26 self.url = url
27 self.timeout = timeout
28 enable_log and self.enable_log()
29
30 def create_tax_service(self):
31 self.taxSvc = self.service('tax')
32 return self
33
34 def create_address_service(self):
35 self.addressSvc = self.service('address')
36 return self
37
38 def service(self, name):
39 nameCap = string.capitalize(name) # So this will be 'Tax' or 'Address'
40 # The Python SUDS library can fetch the WSDL down from the server
41 # or use a local file URL. We'll use a local file URL.
42# wsdl_url = 'file:///' + os.getcwd().replace('\\', '/') + '/%ssvc.wsdl.xml' % name
43 # If you want to fetch the WSDL from the server, use this instead:
44 wsdl_url = 'https://avatax.avalara.net/%s/%ssvc.wsdl' % (nameCap, nameCap)
45 #wsdl_url = 'file:///home/rima/Downloads/AvaTax.wsdl'
46 # So now we'll fetch the WSDL and build the service proxy,
47 # handling any errors appropriately.
48 try:
49 svc = suds.client.Client(wsdl_url)
50 except urllib2.URLError, details:
51 raise osv.except_osv(_('Server Failed to Response'), _(details))
52 else:
53 svc.set_options(service='%sSvc' % nameCap)
54 svc.set_options(port='%sSvcSoap' % nameCap)
55 svc.set_options(location='%s/%s/%sSvc.asmx' % (self.url, nameCap, nameCap))
56 svc.set_options(wsse=self.my_security(self.username, self.password))
57 svc.set_options(soapheaders=self.my_profile())
58 svc.set_options(timeout=self.timeout)
59 return svc
60
61 def my_security(self, username, password):
62
63 token = suds.wsse.UsernameToken(username, password)
64
65 # AvaTax leaves the WSSE Nonce and Created elements as
66 # optional. As explained in XXX, you should include these if at
67 # all possible, to make your connection more secure.
68 # Nonce (optional) is a randomly generated, cryptographic token
69 # used to prevent theft and replay attacks. We recommend sending
70 # it if your SOAP client library supports it.
71 token.setnonce()
72
73 # Created (optional) identifies when the message was created and
74 # prevents replay attacks. We recommend sending it if your SOAP
75 # client library supports it.
76 token.setcreated()
77 security = suds.wsse.Security()
78 security.tokens.append(token)
79 return security
80
81 def my_profile(self):
82
83 # Set elements adapter defaults
84 ADAPTER = 'Novapoint Group,0.1'
85
86 # Profile Client.
87 CLIENT = 'Novapoint Group,0.1'
88
89 #Build the Profile element
90 profileNameSpace = ('ns1', 'http://avatax.avalara.com/services')
91 profile = suds.sax.element.Element('Profile', ns=profileNameSpace)
92 profile.append(suds.sax.element.Element('Client', ns=profileNameSpace).setText(CLIENT))
93 profile.append(suds.sax.element.Element('Adapter', ns=profileNameSpace).setText(ADAPTER))
94 hostname = socket.gethostname()
95 profile.append(suds.sax.element.Element('Machine', ns=profileNameSpace).setText(hostname))
96 return profile
97
98 def get_result(self, svc, operation, request):
99 try:
100 result = operation(request)
101 except suds.WebFault, e:
102 raise osv.except_osv(_('Error'), _(e.fault.faultstring))
103 except urllib2.HTTPError, e:
104 raise osv.except_osv(_('Server Failed to Response'), _(e.code))
105 except urllib2.URLError, details:
106 # We could also print the SOAP request here:
107 # print svc.last_sent()
108 raise osv.except_osv(_('Failed to reach the server'), _(details.reason))
109 else:
110 if (result.ResultCode != 'Success'):
111 raise osv.except_osv(('Error'), _(AvaTaxError(result.ResultCode, result.Messages)))
112 else:
113 return result
114
115 def ping(self):
116 return self.get_result(self.taxSvc, self.taxSvc.service.Ping, '')
117
118 def is_authorized(self):
119 return self.get_result(self.taxSvc, self.taxSvc.service.IsAuthorized, 'GetTax,PostTax')
120
121 def validate_address(self, baseaddress, textcase='Default'):
122 # The Validate() operation needs a complex parameter, a
123 # ValidateRequest. SUDS gives us a factory method on the service
124 # object that creates a proxy class we can use.
125 request = self.addressSvc.factory.create('ValidateRequest')
126 # SUDS allows us to get defaults set up for us automatically. But
127 # to keep this sample code simple, we won't do that here.
128 # These are the elements required by the WSDL, with the defaults
129 # set by the .NET adapter.
130 # See the PHP sample code or .NET SDK Help File for what each of these is for.
131 # TextCase, as with many other elements, is defined in the WSDL as
132 # an enumeration. Depending on your SOAP client library, language
133 # and platform, you may have these available to you. If you can
134 # use enumerations rather than strings you should do so, because
135 # it offers compile-time checking that can save your development
136 # time. We're just using strings elsewhere in this sample code, to
137 # keep things simple. But for TextCase we'll use SUDS
138 # enumerations, just to show you how this sort of thing would
139 # work.
140 # This is the SUDS documentation on enumerations:
141 # https://fedorahosted.org/suds/wiki/Documentation#ENUMERATIONS
142 # So this is how we'll do things if we're just using strings
143 #
144 # request.TextCase = 'Default'
145 #
146 textCase = self.addressSvc.factory.create('TextCase')
147 request.TextCase = textcase
148 request.Coordinates = True
149 request.Taxability = False
150 request.Date = '1900-01-01'
151 request.Address = baseaddress
152
153 result = self.get_result(self.addressSvc, self.addressSvc.service.Validate, request)
154 return result
155
156 def get_tax(self, company_code, doc_date, doc_type, partner_code, doc_code, origin, destination,
157 received_lines, exemption_no=None, customer_usage_type=None, salesman_code=None, commit=False, invoice_date=None, reference_code=None):
158 lineslist = []
159 request = self.taxSvc.factory.create('GetTaxRequest')
160 # We'll first default everything just as the .NET adapter does
161 request.Commit = commit
162 request.DetailLevel = 'Diagnostic'
163 request.Discount = 0.0
164 request.ServiceMode = 'Automatic' # PHP defaults this to Automatic; Java likewise
165 request.PaymentDate = '1900-01-01'
166 request.ExchangeRate = 45
167 request.ExchangeRateEffDate = '2011-07-07'
168 request.HashCode = 0
169 request.ReferenceCode = reference_code
170 if invoice_date:
171 taxoverride = self.taxSvc.factory.create('TaxOverride')
172 taxoverride.TaxOverrideType = 'TaxDate'
173 taxoverride.TaxDate = invoice_date
174 taxoverride.TaxAmount = 0
175 taxoverride.Reason = 'Return Items'
176 request.TaxOverride = taxoverride
177 # We'll now set the properties as we would normally, including
178 # some that have already been defaulted.
179 # The GetTax call will exactly match that in the PHP sample
180 # code. So you can compare the XML from them line-by-line. We
181 # suggest you try to get the same call set up in whatever language
182 # and platform you're using, so you can compare your XML likewise.
183 # See the PHP sample code for an explanation of each of these.
184 request.CompanyCode = company_code
185 request.DocDate = doc_date
186 request.DocType = doc_type
187 request.DocCode = doc_code
188 request.CustomerCode = partner_code
189 request.ExemptionNo = exemption_no
190 request.CustomerUsageType = customer_usage_type
191 request.SalespersonCode = salesman_code
192 # Now the AddressCode, which we'll reference later. Note that
193 # although it's a string and so you could use 'Origin' and
194 # 'Destination' here, because addresses could be defined at the
195 # line level you're best off just numbering them.
196 #
197 # Furthermore, any Messages you get back in an error from the
198 # service will, in Message.RefersTo, reference the addresses
199 # indexed as they appear in Addresses, starting at zero, so if you
200 # use AddressCode = '0', '1' and so on, the index in the Message
201 # will match the appropriate item.
202 # Be very careful to make the addresscodes all match up. If, for
203 # example, origin.AddressCode does not match an entry in
204 # Addresses, the origin address will be treated as if it were
205 # blank.
206 # Now we'll set some of the other properties as usual
207 addresses = self.taxSvc.factory.create('ArrayOfBaseAddress')
208 addresses.BaseAddress = [origin, destination]
209 request.Addresses = addresses
210 request.OriginCode = '0' # Referencing an address above
211 request.DestinationCode = '1' # Referencing an address above
212 for line in range(0, len(received_lines)):
213 line1 = self.taxSvc.factory.create('Line')
214 line1.Qty = received_lines[line].get('qty', 1)
215 line1.Discounted = False
216 line1.No = '%d' %line
217 line1.ItemCode = received_lines[line].get('itemcode', None)
218 line1.Description = received_lines[line].get('description', None)
219 line1.Amount = received_lines[line].get('amount', 0.0)
220 line1.TaxCode = received_lines[line].get('tax_code', None)
221 lineslist.append(line1)
222 # So now we build request.Lines
223 lines = self.taxSvc.factory.create('ArrayOfLine')
224 lines.Line = lineslist
225 request.Lines = lines
226 # And we're ready to make the call
227 result = self.get_result(self.taxSvc, self.taxSvc.service.GetTax, request)
228 return result
229
230 def cancel_tax(self, company_code, doc_code, doc_type, cancel_code):
231 request = self.taxSvc.factory.create('CancelTaxRequest')
232 request.CompanyCode = company_code
233 request.DocCode = doc_code
234 request.DocType = doc_type
235 request.CancelCode = cancel_code
236 result = self.get_result(self.taxSvc, self.taxSvc.service.CancelTax, request)
237 return result
238
239class Error(Exception):
240 """Base class for exceptions in this module."""
241 pass
242
243class AvaTaxError(Error):
244 """Exception raised for errors calling AvaTax.
245
246 Attributes:
247 resultCode -- result.ResultCode
248 messages -- result.Messages
249 """
250
251 def __init__(self, resultCode, messages):
252 self.resultCode = resultCode
253 self.messages = messages
254
255 def __str__(self):
256 str = ''
257 for item in self.messages:
258 message = item[1][0] # SUDS gives us the message in a list, in a tuple
259
260 str = "Severity: %s\n\nDetails: %s\n\n RefersTo: %s\n\n Summary: %s" \
261 % (message.Severity, message.Details, message.RefersTo, message.Summary)
262 return str
263
264class BaseAddress:
265
266 def __init__(self, addSvc, Line1=None, Line2=None, City=None, PostalCode=None, Region=None, Country=None, AddressCode=None):
267 self.data = addSvc.factory.create('BaseAddress')
268 self.data.TaxRegionId = 0
269 self.data.Line1 = Line1
270 self.data.Line2 = Line2
271 self.data.City = City
272 self.data.PostalCode = PostalCode
273 self.data.Region = Region
274 self.data.Country = Country
275 self.data.AddressCode = AddressCode
276
277class Line:
278
279 def __init__(self, taxSvc, ItemCode, Amount, Qty, Description=None, TaxCode=None):
280 self.taxSvc = taxSvc
281 # We're not setting No here
282 self.data = self.defaultLine()
283 self.data.ItemCode = ItemCode
284 self.data.Amount = Amount
285 self.data.Qty = Qty
286 self.data.Description = Description
287 self.data.TaxCode = TaxCode
288
289 def defaultLine(self):
290 line = self.taxSvc.factory.create('Line')
291 line.Qty = 1
292 line.Discounted = False
293 return line
294
295# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0\ No newline at end of file296\ No newline at end of file
1297
=== added directory 'account_salestax_avatax/wizard'
=== added file 'account_salestax_avatax/wizard/__init__.py'
--- account_salestax_avatax/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/wizard/__init__.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,26 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23import account_salestax_avatax_ping
24import account_salestax_avatax_address_validate
25
26# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0\ No newline at end of file27\ No newline at end of file
128
=== added file 'account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py'
--- account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,135 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22import time
23
24from osv import osv, fields
25from tools.translate import _
26
27class account_salestax_avatax_address_validate(osv.osv_memory):
28 _name = 'account.salestax.avatax.address.validate'
29 _description = 'Address Validation using AvaTax'
30 _columns = {
31 'original_street': fields.char('Street', size=64, readonly=True),
32 'original_street2': fields.char('Street2', size=64, readonly=True),
33 'original_city': fields.char('City', size=64, readonly=True),
34 'original_zip': fields.char('Zip', size=64, readonly=True),
35 'original_state': fields.char('State', size=64, readonly=True),
36 'original_country': fields.char('Country', size=64, readonly=True),
37 'street': fields.char('Street', size=64),
38 'street2': fields.char('Street2', size=64),
39 'city': fields.char('City', size=64),
40 'zip': fields.char('Zip', size=64),
41 'state': fields.char('State', size=64),
42 'country': fields.char('Country', size=64),
43 'latitude': fields.char('Laitude', size=64),
44 'longitude': fields.char('Longitude', size=64)
45 }
46
47 def view_init(self, cr, uid, fields, context=None):
48 """ Checks for precondition before wizard executes. """
49 address_obj = self.pool.get('res.partner.address')
50 avatax_config_obj= self.pool.get('account.salestax.avatax')
51
52 if context is None:
53 context = {}
54
55 # Check if there is avatax tax service active for the user company.
56 # Prevent validating the address if the address validation is disabled by the administrator.
57
58 if context.get('active_id', False) and context.get('active_model', False) == 'res.partner.address':
59 avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid, context=context)
60 if not avatax_config:
61 raise osv.except_osv(_('Service Not Setup'), _("The AvaTax Tax Service is not active. To sign-up for the AvaTax Tax Service please visit www.novapointgroup.com."))
62 address = address_obj.browse(cr, uid, context['active_id'], context=context)
63 if address.validated_on_save and avatax_config.validation_on_save:
64 raise osv.except_osv(_('Address Already Validated'), _("Address Validation on Save is already active in the AvaTax Configuration."))
65 address_obj.check_avatax_support(cr, uid, avatax_config, address.country_id and address.country_id.id or False, context=context)
66 return True
67
68 def default_get(self, cr, uid, fields, context=None):
69 """ Returns the default values for the fields. """
70
71 res = super(account_salestax_avatax_address_validate, self).default_get(cr, uid, fields, context)
72
73 if context.get('active_id', False) and context.get('active_model', False) == 'res.partner.address':
74 address_obj = self.pool.get('res.partner.address')
75 address = address_obj.read(cr, uid, context['active_id'], ['street', 'street2', 'city', 'state_id', 'zip', 'country_id'], context=context)
76 address['state_id'] = address.get('state_id') and address['state_id'][0]
77 address['country_id'] = address.get('country_id') and address['country_id'][0]
78
79 # Get the valid result from the AvaTax Address Validation Service
80 valid_address = address_obj._validate_address(cr, uid, address, context=context)
81
82 if 'original_street' in fields:
83 res.update({'original_street': address['street']})
84 if 'original_street2' in fields:
85 res.update({'original_street2': address['street2']})
86 if 'original_city' in fields:
87 res.update({'original_city': address['city']})
88 if 'original_state' in fields:
89 res.update({'original_state': address_obj.get_state_code(cr, uid, address['state_id'], context=context)})
90 if 'original_zip' in fields:
91 res.update({'original_zip': address['zip']})
92 if 'original_country' in fields:
93 res.update({'original_country': address_obj.get_country_code(cr, uid, address['country_id'], context=context)})
94 if 'street' in fields:
95 res.update({'street': valid_address.Line1})
96 if 'street2' in fields:
97 res.update({'street2': valid_address.Line2})
98 if 'city' in fields:
99 res.update({'city': valid_address.City})
100 if 'state' in fields:
101 res.update({'state': valid_address.Region})
102 if 'zip' in fields:
103 res.update({'zip': valid_address.PostalCode})
104 if 'country' in fields:
105 res.update({'country': valid_address.Country})
106 if 'latitude' in fields:
107 res.update({'latitude': valid_address.Latitude})
108 if 'longitude' in fields:
109 res.update({'longitude': valid_address.Longitude})
110 return res
111
112 def accept_valid_address(self, cr, uid, ids, context=None):
113 """ Updates the existing address with the valid address returned by the service. """
114
115 valid_address = self.read(cr, uid, ids, context=context)[0]
116 if context.get('active_id', False):
117 address_obj = self.pool.get('res.partner.address')
118 address_result = {
119 'street': valid_address['street'],
120 'street2': valid_address['street2'],
121 'city': valid_address['city'],
122 'state_id': address_obj.get_state_id(cr, uid, valid_address['state'], context=context),
123 'zip': valid_address['zip'],
124 'country_id': address_obj.get_country_id(cr, uid, valid_address['country'], context=context),
125 'latitude': valid_address['latitude'],
126 'longitude': valid_address['longitude'],
127 'date_validation': time.strftime('%Y-%m-%d'),
128 'validation_method': 'avatax'
129 }
130 address_obj.write(cr, uid, [context['active_id']], address_result, context=context)
131 return {'type': 'ir.actions.act_window_close'}
132
133account_salestax_avatax_address_validate()
134
135# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0136
=== added file 'account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml'
--- account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,53 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <!-- Partner Address Validate -->
6
7 <record id="view_account_salestax_avatax_address_validate" model="ir.ui.view">
8 <field name="name">Address Validatation</field>
9 <field name="model">account.salestax.avatax.address.validate</field>
10 <field name="type">form</field>
11 <field name="arch" type="xml">
12 <form string="Address Validation">
13 <group colspan="4" col="8">
14 <group colspan="1">
15 <separator string="Original Address" colspan="4"/>
16 <field name="original_street" colspan="4" width="220"/>
17 <field name="original_street2" colspan="4" width="220"/>
18 <field name="original_city" colspan="4" width="220"/>
19 <field name="original_state" colspan="4" width="220"/>
20 <field name="original_zip" colspan="4" width="220"/>
21 <field name="original_country" colspan="4" width="220"/>
22 </group>
23 <separator orientation="vertical" rowspan="10"/>
24 <group colspan="6">
25 <separator string="Validated Address" colspan="4"/>
26 <field name="street" colspan="4" width="220"/>
27 <field name="street2" colspan="4"/>
28 <field name="city" colspan="4"/>
29 <field name="state" colspan="4"/>
30 <field name="zip" colspan="4"/>
31 <field name="country" colspan="4"/>
32 </group>
33 <group colspan="8" col="8">
34 <button special="cancel" string="Cancel" icon="gtk-cancel"/>
35 <button name="accept_valid_address" type="object" icon="gtk-ok" string="_Accept"/>
36 </group>
37 </group>
38 </form>
39 </field>
40 </record>
41
42 <record id="action_account_salestax_avatax_address_validate" model="ir.actions.act_window">
43 <field name="name">Address Validation</field>
44 <field name="type">ir.actions.act_window</field>
45 <field name="res_model">account.salestax.avatax.address.validate</field>
46 <field name="view_type">form</field>
47 <field name="view_mode">form</field>
48 <field name="view_id" ref="view_account_salestax_avatax_address_validate"/>
49 <field name="target">new</field>
50 </record>
51
52 </data>
53</openerp>
0\ No newline at end of file54\ No newline at end of file
155
=== added file 'account_salestax_avatax/wizard/account_salestax_avatax_ping.py'
--- account_salestax_avatax/wizard/account_salestax_avatax_ping.py 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/wizard/account_salestax_avatax_ping.py 2011-09-22 18:37:20 +0000
@@ -0,0 +1,58 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>)
6# Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>)
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as
10# published by the Free Software Foundation, either version 3 of the
11# License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20#
21##############################################################################
22
23from osv import osv, fields
24from tools.translate import _
25
26from account_salestax_avatax.suds_client import AvaTaxService
27
28class account_salestax_avatax_ping(osv.osv_memory):
29 _name = 'account.salestax.avatax.ping'
30 _description = 'Ping Service'
31 _columns = {
32 'name': fields.char('Name', size=64)
33 }
34
35 def default_get(self, cr, uid, fields_list, context=None):
36 res = super(account_salestax_avatax_ping, self).default_get(cr, uid, fields_list, context)
37 return self.ping(cr, uid, context=context)
38
39 def ping(self, cr, uid, context=None):
40 """ Call the Avatax's Ping Service to test the connection. """
41
42 if context is None:
43 context = {}
44
45 if context.get('active_id', False):
46 avatax_pool = self.pool.get('account.salestax.avatax')
47 avatax_config = avatax_pool.browse(cr, uid, context['active_id'], context=context)
48 avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key,
49 avatax_config.service_url, avatax_config.request_timeout, avatax_config.logging)
50 taxSvc = avapoint.create_tax_service().taxSvc # Create 'tax' service for Ping and is_authorized calls
51 avapoint.ping()
52 result = avapoint.is_authorized()
53 avatax_pool.write(cr, uid, avatax_config.id, {'date_expiration': result.Expires})
54 return {'type': 'ir.actions.act_window_close'}
55
56account_salestax_avatax_ping()
57
58# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
059
=== added file 'account_salestax_avatax/wizard/account_salestax_avatax_ping.xml'
--- account_salestax_avatax/wizard/account_salestax_avatax_ping.xml 1970-01-01 00:00:00 +0000
+++ account_salestax_avatax/wizard/account_salestax_avatax_ping.xml 2011-09-22 18:37:20 +0000
@@ -0,0 +1,37 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <!-- Ping AvaTax Tax Service -->
6
7 <record id="view_account_salestax_avatax_ping" model="ir.ui.view">
8 <field name="name">Test Connection</field>
9 <field name="model">account.salestax.avatax.ping</field>
10 <field name="type">form</field>
11 <field name="arch" type="xml">
12 <form string="Test Connection">
13 <group height="70" width="130">
14 <label align="0.5" string="Test Connection Successful!" colspan="4"/>
15 <newline/>
16 <group colspan="2" col="4">
17 <label string=""/>
18 <button icon="gtk-ok" special="cancel" string="_OK"/>
19 </group>
20 </group>
21 </form>
22 </field>
23 </record>
24
25 <record id="action_account_salestax_avatax_ping" model="ir.actions.act_window">
26 <field name="name">Test Connection</field>
27 <field name="type">ir.actions.act_window</field>
28 <field name="res_model">account.salestax.avatax.ping</field>
29 <field name="view_type">form</field>
30 <field name="view_mode">form</field>
31 <field name="view_id" ref="view_account_salestax_avatax_ping"/>
32 <field name="context">{'record_id': active_id}</field>
33 <field name="target">new</field>
34 </record>
35
36 </data>
37</openerp>
0\ No newline at end of file38\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: