Merge lp:~npg-team/openobject-addons/account_salestax_avatax_us into lp:openobject-addons
- account_salestax_avatax_us
- Merge into trunk
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 |
Related bugs: |
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.
Commit message
Description of the change
Improvements in account_
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
1 | === added directory 'account_salestax_avatax' | |||
2 | === added file 'account_salestax_avatax/__init__.py' | |||
3 | --- account_salestax_avatax/__init__.py 1970-01-01 00:00:00 +0000 | |||
4 | +++ account_salestax_avatax/__init__.py 2011-09-22 18:37:20 +0000 | |||
5 | @@ -0,0 +1,33 @@ | |||
6 | 1 | # -*- coding: utf-8 -*- | ||
7 | 2 | ############################################################################## | ||
8 | 3 | # | ||
9 | 4 | # OpenERP, Open Source Management Solution | ||
10 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
11 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
12 | 7 | # | ||
13 | 8 | # This program is free software: you can redistribute it and/or modify | ||
14 | 9 | # it under the terms of the GNU Affero General Public License as | ||
15 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
16 | 11 | # License, or (at your option) any later version. | ||
17 | 12 | # | ||
18 | 13 | # This program is distributed in the hope that it will be useful, | ||
19 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | 16 | # GNU Affero General Public License for more details. | ||
22 | 17 | # | ||
23 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
24 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
25 | 20 | # | ||
26 | 21 | ############################################################################## | ||
27 | 22 | |||
28 | 23 | import product | ||
29 | 24 | import account_salestax_avatax | ||
30 | 25 | import partner | ||
31 | 26 | import suds_client | ||
32 | 27 | import sale | ||
33 | 28 | import invoice | ||
34 | 29 | import account | ||
35 | 30 | import wizard | ||
36 | 31 | |||
37 | 32 | |||
38 | 33 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
39 | 0 | 34 | ||
40 | === added file 'account_salestax_avatax/__openerp__.py' | |||
41 | --- account_salestax_avatax/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
42 | +++ account_salestax_avatax/__openerp__.py 2011-09-22 18:37:20 +0000 | |||
43 | @@ -0,0 +1,77 @@ | |||
44 | 1 | # -*- coding: utf-8 -*- | ||
45 | 2 | ############################################################################## | ||
46 | 3 | # | ||
47 | 4 | # OpenERP, Open Source Management Solution | ||
48 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
49 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
50 | 7 | # | ||
51 | 8 | # This program is free software: you can redistribute it and/or modify | ||
52 | 9 | # it under the terms of the GNU Affero General Public License as | ||
53 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
54 | 11 | # License, or (at your option) any later version. | ||
55 | 12 | # | ||
56 | 13 | # This program is distributed in the hope that it will be useful, | ||
57 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
58 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
59 | 16 | # GNU Affero General Public License for more details. | ||
60 | 17 | # | ||
61 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
62 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
63 | 20 | # | ||
64 | 21 | ############################################################################## | ||
65 | 22 | |||
66 | 23 | { | ||
67 | 24 | "name" : "Use AvaTax web service to calculate tax", | ||
68 | 25 | "version" : "1.0", | ||
69 | 26 | "author" : 'Novapoint Group LLC', | ||
70 | 27 | "description": """ This module supports calculating sales and use taxes, validates addresses | ||
71 | 28 | using the AvaTax subscription service from AVALARA. www.avalara.com. It is targeted for the US, Canadian, and in the future the international markets | ||
72 | 29 | supported by AVALARA. Users of the module need to subscribe to a service plan from AVALARA and obtain the proper login credentials prior | ||
73 | 30 | to using this module. As the US and Canadian tax programs have more | ||
74 | 31 | than 22,000 unique tax jurisdictions based on a GEO-Location matrix segmented by product category, | ||
75 | 32 | and tax collection legislation continues to change in the US to include new | ||
76 | 33 | areas like internet transactions, we believe using a tax service from AvaTax to calculate applicable taxes due simplifies | ||
77 | 34 | a company's management, time and effort to be in compliance with tax rules. | ||
78 | 35 | |||
79 | 36 | It is intended that the tax calculation service is called whenever an existing tax calculation is calculated within OpenERP. | ||
80 | 37 | |||
81 | 38 | The process to setup the system is as follows: | ||
82 | 39 | A company would initially install the module. | ||
83 | 40 | A company would sign-up for the AVATAX service. | ||
84 | 41 | A company enter their AVATAX credentials in OpenERP. | ||
85 | 42 | A company then should setup their Chart of Accounts with the appropriate accounts to capture detailed tax information (see your accountant). | ||
86 | 43 | A company then sets up the AVATAX service to cater to the products and services they provide. | ||
87 | 44 | A company then configures OpenERP properly for tax processing with AVATAX. | ||
88 | 45 | A company then tests the AVATAX connection and service. | ||
89 | 46 | |||
90 | 47 | AVATAX also offers a service to manage the submission of taxes due to various tax jurisdictions electronically, and manually. | ||
91 | 48 | |||
92 | 49 | Please review the module documentation prior to installing the module for prerequisites. | ||
93 | 50 | |||
94 | 51 | NOTE: 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 | ||
95 | 52 | obligation that calculations are correct, or that tax calculations are sufficient to meet regulatory or legislative requirements. | ||
96 | 53 | |||
97 | 54 | """, | ||
98 | 55 | "category" : "Generic Modules/Accounting", | ||
99 | 56 | "website" : "http://www.novapointgroup.com/", | ||
100 | 57 | "depends" : ["sale_negotiated_shipping"], | ||
101 | 58 | "init_xml" : [], | ||
102 | 59 | "demo_xml" : [], | ||
103 | 60 | "update_xml" : [ | ||
104 | 61 | "account_salestax_avatax_data.xml", | ||
105 | 62 | "wizard/account_salestax_avatax_ping.xml", | ||
106 | 63 | "wizard/account_salestax_avatax_address_validate.xml", | ||
107 | 64 | "account_salestax_avatax_view.xml", | ||
108 | 65 | "partner_view.xml", | ||
109 | 66 | "product_view.xml", | ||
110 | 67 | "account_invoice_workflow.xml", | ||
111 | 68 | "account_invoice_view.xml", | ||
112 | 69 | "security/account_salestax_avatax_security.xml", | ||
113 | 70 | "security/ir.model.access.csv", | ||
114 | 71 | ], | ||
115 | 72 | "test" : [], | ||
116 | 73 | "active": False, | ||
117 | 74 | "installable": True, | ||
118 | 75 | } | ||
119 | 76 | |||
120 | 77 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
121 | 0 | 78 | ||
122 | === added file 'account_salestax_avatax/account.py' | |||
123 | --- account_salestax_avatax/account.py 1970-01-01 00:00:00 +0000 | |||
124 | +++ account_salestax_avatax/account.py 2011-09-22 18:37:20 +0000 | |||
125 | @@ -0,0 +1,92 @@ | |||
126 | 1 | # -*- coding: utf-8 -*- | ||
127 | 2 | ############################################################################## | ||
128 | 3 | # | ||
129 | 4 | # OpenERP, Open Source Management Solution | ||
130 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
131 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
132 | 7 | # | ||
133 | 8 | # This program is free software: you can redistribute it and/or modify | ||
134 | 9 | # it under the terms of the GNU Affero General Public License as | ||
135 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
136 | 11 | # License, or (at your option) any later version. | ||
137 | 12 | # | ||
138 | 13 | # This program is distributed in the hope that it will be useful, | ||
139 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
140 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
141 | 16 | # GNU Affero General Public License for more details. | ||
142 | 17 | # | ||
143 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
144 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
145 | 20 | # | ||
146 | 21 | ############################################################################## | ||
147 | 22 | import time | ||
148 | 23 | import string | ||
149 | 24 | |||
150 | 25 | from osv import osv, fields | ||
151 | 26 | from tools.translate import _ | ||
152 | 27 | |||
153 | 28 | from suds_client import AvaTaxService, BaseAddress, Line | ||
154 | 29 | |||
155 | 30 | class account_tax(osv.osv): | ||
156 | 31 | _inherit = "account.tax" | ||
157 | 32 | |||
158 | 33 | def _check_compute_tax(self, cr, uid, avatax_config, doc_date, doc_code, doc_type, partner, ship_from_address_id, shipping_address_id, | ||
159 | 34 | lines, shipping_charge, user=None, commit=False, invoice_date=False, reference_code=False, context=None): | ||
160 | 35 | address_obj = self.pool.get('res.partner.address') | ||
161 | 36 | if not lines: | ||
162 | 37 | raise osv.except_osv(_('Error !'), _('AvaTax needs atleast one sale order line defined for tax calculation.')) | ||
163 | 38 | if avatax_config.force_address_validation: | ||
164 | 39 | if not shipping_address.date_validation: | ||
165 | 40 | raise osv.except_osv(_('Address Not Validated !'), _('Please validate the shipping address for the partner %s.' | ||
166 | 41 | % (partner.name))) | ||
167 | 42 | if not ship_from_address_id: | ||
168 | 43 | raise osv.except_osv(_('No Ship from Address Defined !'), _('There is no company address defined.')) | ||
169 | 44 | if not shipping_address_id: | ||
170 | 45 | raise osv.except_osv(_('No Shipping Address Defined !'), _('There is no shipping address defined for the partner.')) | ||
171 | 46 | |||
172 | 47 | ship_from_address = address_obj.browse(cr, uid, ship_from_address_id, context=context) | ||
173 | 48 | shipping_address = address_obj.browse(cr, uid, shipping_address_id, context=context) | ||
174 | 49 | if not ship_from_address.date_validation: | ||
175 | 50 | raise osv.except_osv(_('Address Not Validated !'), _('Please validate the company address.')) | ||
176 | 51 | |||
177 | 52 | if shipping_charge: | ||
178 | 53 | lines.append({ | ||
179 | 54 | 'qty': 1, | ||
180 | 55 | 'amount': shipping_charge, | ||
181 | 56 | 'itemcode': '', | ||
182 | 57 | 'description': '', | ||
183 | 58 | 'tax_code': avatax_config.default_shipping_code_id.name | ||
184 | 59 | }) | ||
185 | 60 | avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key, | ||
186 | 61 | avatax_config.service_url, avatax_config.request_timeout, avatax_config.logging) | ||
187 | 62 | avapoint.create_tax_service() | ||
188 | 63 | addSvc = avapoint.create_address_service().addressSvc | ||
189 | 64 | origin = BaseAddress(addSvc, ship_from_address.street or None, | ||
190 | 65 | ship_from_address.street2 or None, | ||
191 | 66 | ship_from_address.city, ship_from_address.zip, | ||
192 | 67 | ship_from_address.state_id and ship_from_address.state_id.code or None, | ||
193 | 68 | ship_from_address.country_id and ship_from_address.country_id.code or None, 0).data | ||
194 | 69 | destination = BaseAddress(addSvc, shipping_address.street or None, | ||
195 | 70 | shipping_address.street2 or None, | ||
196 | 71 | shipping_address.city, shipping_address.zip, | ||
197 | 72 | shipping_address.state_id and shipping_address.state_id.code or None, | ||
198 | 73 | shipping_address.country_id and shipping_address.country_id.code or None, 1).data | ||
199 | 74 | result = avapoint.get_tax(avatax_config.company_code, doc_date, doc_type, | ||
200 | 75 | partner.name, doc_code, origin, destination, | ||
201 | 76 | lines, partner.exemption_number or None, | ||
202 | 77 | partner.exemption_code_id and partner.exemption_code_id.code or None, | ||
203 | 78 | user and user.name or None, commit, invoice_date, reference_code) | ||
204 | 79 | |||
205 | 80 | return result | ||
206 | 81 | |||
207 | 82 | def cancel_tax(self, cr, uid, avatax_config, doc_code, doc_type, cancel_code): | ||
208 | 83 | avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key, | ||
209 | 84 | avatax_config.service_url, avatax_config.request_timeout, | ||
210 | 85 | avatax_config.logging) | ||
211 | 86 | avapoint.create_tax_service() | ||
212 | 87 | result = avapoint.cancel_tax(avatax_config.company_code, doc_code, doc_type, cancel_code) | ||
213 | 88 | return result | ||
214 | 89 | |||
215 | 90 | account_tax() | ||
216 | 91 | |||
217 | 92 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
218 | 0 | 93 | ||
219 | === added file 'account_salestax_avatax/account_invoice_view.xml' | |||
220 | --- account_salestax_avatax/account_invoice_view.xml 1970-01-01 00:00:00 +0000 | |||
221 | +++ account_salestax_avatax/account_invoice_view.xml 2011-09-22 18:37:20 +0000 | |||
222 | @@ -0,0 +1,22 @@ | |||
223 | 1 | <?xml version="1.0"?> | ||
224 | 2 | <openerp> | ||
225 | 3 | <data> | ||
226 | 4 | |||
227 | 5 | <!-- | ||
228 | 6 | Customer Invoice/Credit Memo | ||
229 | 7 | --> | ||
230 | 8 | |||
231 | 9 | <record id="view_account_invoice_form_avatax_inherit" model="ir.ui.view"> | ||
232 | 10 | <field name="name">account.invoice.form.avatax.inherit</field> | ||
233 | 11 | <field name="model">account.invoice</field> | ||
234 | 12 | <field name="type">form</field> | ||
235 | 13 | <field name="inherit_id" ref="account.invoice_form"/> | ||
236 | 14 | <field name="arch" type="xml"> | ||
237 | 15 | <field name="payment_term" position="after"> | ||
238 | 16 | <field name="invoice_doc_no" attrs="{'required': [('type','=','out_refund')],'invisible': [('type','!=','out_refund')]}"/> | ||
239 | 17 | </field> | ||
240 | 18 | </field> | ||
241 | 19 | </record> | ||
242 | 20 | |||
243 | 21 | </data> | ||
244 | 22 | </openerp> | ||
245 | 0 | \ No newline at end of file | 23 | \ No newline at end of file |
246 | 1 | 24 | ||
247 | === added file 'account_salestax_avatax/account_invoice_workflow.xml' | |||
248 | --- account_salestax_avatax/account_invoice_workflow.xml 1970-01-01 00:00:00 +0000 | |||
249 | +++ account_salestax_avatax/account_invoice_workflow.xml 2011-09-22 18:37:20 +0000 | |||
250 | @@ -0,0 +1,17 @@ | |||
251 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
252 | 2 | <openerp> | ||
253 | 3 | <data> | ||
254 | 4 | |||
255 | 5 | <record id="account.act_open" model="workflow.activity"> | ||
256 | 6 | <field name="wkf_id" ref="account.wkf"/> | ||
257 | 7 | <field name="name">open</field> | ||
258 | 8 | <field name="action">action_date_assign() | ||
259 | 9 | action_move_create() | ||
260 | 10 | action_number() | ||
261 | 11 | write({'state':'open'}) | ||
262 | 12 | action_commit_tax()</field> | ||
263 | 13 | <field name="kind">function</field> | ||
264 | 14 | </record> | ||
265 | 15 | |||
266 | 16 | </data> | ||
267 | 17 | </openerp> | ||
268 | 0 | \ No newline at end of file | 18 | \ No newline at end of file |
269 | 1 | 19 | ||
270 | === added file 'account_salestax_avatax/account_salestax_avatax.py' | |||
271 | --- account_salestax_avatax/account_salestax_avatax.py 1970-01-01 00:00:00 +0000 | |||
272 | +++ account_salestax_avatax/account_salestax_avatax.py 2011-09-22 18:37:20 +0000 | |||
273 | @@ -0,0 +1,147 @@ | |||
274 | 1 | # -*- coding: utf-8 -*- | ||
275 | 2 | ############################################################################## | ||
276 | 3 | # | ||
277 | 4 | # OpenERP, Open Source Management Solution | ||
278 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
279 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
280 | 7 | # | ||
281 | 8 | # This program is free software: you can redistribute it and/or modify | ||
282 | 9 | # it under the terms of the GNU Affero General Public License as | ||
283 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
284 | 11 | # License, or (at your option) any later version. | ||
285 | 12 | # | ||
286 | 13 | # This program is distributed in the hope that it will be useful, | ||
287 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
288 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
289 | 16 | # GNU Affero General Public License for more details. | ||
290 | 17 | # | ||
291 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
292 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
293 | 20 | # | ||
294 | 21 | ############################################################################## | ||
295 | 22 | from osv import osv, fields | ||
296 | 23 | from tools.translate import _ | ||
297 | 24 | import decimal_precision as dp | ||
298 | 25 | |||
299 | 26 | class tax_schedule(osv.osv): | ||
300 | 27 | _name = "tax.schedule" | ||
301 | 28 | _description = "Tax Schedule" | ||
302 | 29 | _columns = { | ||
303 | 30 | 'name': fields.char('Name', size=64, required=True), | ||
304 | 31 | 'code': fields.char('Code', size=32), | ||
305 | 32 | 'jurisdiction_code_ids': fields.one2many('jurisdiction.code', 'tax_schedule_id', 'Jurisdiction Codes'), | ||
306 | 33 | 'company_id': fields.many2one('res.company', 'Company', required=True), | ||
307 | 34 | 'country_id': fields.many2one('res.country', 'Country', required=True), | ||
308 | 35 | } | ||
309 | 36 | _defaults = { | ||
310 | 37 | 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'tax.schedule', context=c), | ||
311 | 38 | } | ||
312 | 39 | |||
313 | 40 | tax_schedule() | ||
314 | 41 | |||
315 | 42 | class jurisdiction_code(osv.osv): | ||
316 | 43 | _name = "jurisdiction.code" | ||
317 | 44 | _description = "Jurisdiction Code" | ||
318 | 45 | _columns = { | ||
319 | 46 | 'name': fields.char('Description', size=32, required=True), | ||
320 | 47 | 'type': fields.selection([('country', 'Country'), ('composite', 'Composite'), ('state', 'State'), | ||
321 | 48 | ('county', 'County'), ('city', 'City'), ('special', 'Special')], 'Type', required=True, | ||
322 | 49 | help="Type of tax jurisdiction"), | ||
323 | 50 | 'state_id': fields.many2one('res.country.state', 'State', required=True, help="State for which the tax jurisdiction is defined"), | ||
324 | 51 | 'code':fields.char('Code', size=32), | ||
325 | 52 | 'tax_schedule_id': fields.many2one('tax.schedule', 'Tax Schedule'), | ||
326 | 53 | 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account', required=True, help="Use this tax account for Invoices"), | ||
327 | 54 | 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account', required=True, help="Use this tax account for Refunds"), | ||
328 | 55 | 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this base code for the Invoices"), | ||
329 | 56 | 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this tax code for the Invoices"), | ||
330 | 57 | 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1"), | ||
331 | 58 | 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1"), | ||
332 | 59 | 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this base code for the Refunds"), | ||
333 | 60 | 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this tax code for the Refunds"), | ||
334 | 61 | 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1"), | ||
335 | 62 | 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1"), | ||
336 | 63 | } | ||
337 | 64 | _defaults = { | ||
338 | 65 | 'ref_tax_sign': 1, | ||
339 | 66 | 'ref_base_sign': 1, | ||
340 | 67 | 'tax_sign': 1, | ||
341 | 68 | 'base_sign': 1, | ||
342 | 69 | } | ||
343 | 70 | |||
344 | 71 | jurisdiction_code() | ||
345 | 72 | |||
346 | 73 | class exemption_code(osv.osv): | ||
347 | 74 | _name = 'exemption.code' | ||
348 | 75 | _description = 'Exemption Code' | ||
349 | 76 | _columns = { | ||
350 | 77 | 'name': fields.char('Name', size=64), | ||
351 | 78 | 'code': fields.char('Code', size=2) | ||
352 | 79 | } | ||
353 | 80 | |||
354 | 81 | def name_get(self, cr, uid, ids, context=None): | ||
355 | 82 | if not ids: | ||
356 | 83 | return [] | ||
357 | 84 | reads = self.read(cr, uid, ids, ['name', 'code'], context=context) | ||
358 | 85 | res = [] | ||
359 | 86 | for record in reads: | ||
360 | 87 | name = record['name'] | ||
361 | 88 | if record['code']: | ||
362 | 89 | name = '(' + record['code'] + ')' + ' ' + name | ||
363 | 90 | res.append((record['id'], name)) | ||
364 | 91 | return res | ||
365 | 92 | |||
366 | 93 | exemption_code() | ||
367 | 94 | |||
368 | 95 | class account_salestax_avatax(osv.osv): | ||
369 | 96 | _name = 'account.salestax.avatax' | ||
370 | 97 | _description = 'AvaTax Configuration' | ||
371 | 98 | __rec_name = 'account_number' | ||
372 | 99 | |||
373 | 100 | def _get_avatax_supported_countries(self, cr, uid, context=None): | ||
374 | 101 | """ Returns the countries supported by AvaTax Address Validation Service.""" | ||
375 | 102 | |||
376 | 103 | country_pool = self.pool.get('res.country') | ||
377 | 104 | return country_pool.search(cr, uid, [('code', 'in', ['US', 'CA'])], context=context) | ||
378 | 105 | |||
379 | 106 | _columns = { | ||
380 | 107 | 'account_number':fields.char('Account Number', size=64, required=True, help="Account Number provided by AvaTax"), | ||
381 | 108 | 'license_key': fields.char('License Key', size=64, required=True, help="License Key provided by AvaTax"), | ||
382 | 109 | 'service_url': fields.char('Service URL', size=64, required=True, help="The url to connect with"), | ||
383 | 110 | 'date_expiration': fields.date('Service Expiration Date', readonly=True, help="The expiration date of the service"), | ||
384 | 111 | 'request_timeout': fields.integer('Request Timeout', help="Defines AvaTax request time out length, AvaTax best practices prescribes default setting of 300 seconds"), | ||
385 | 112 | 'company_code': fields.char('Company Code', size=64, required=True, help="The company code as defined in the Admin Console of AvaTax"), | ||
386 | 113 | 'logging': fields.boolean('Enable Logging', help="Enables detailed AvaTax transaction logging within application"), | ||
387 | 114 | 'address_validation': fields.boolean('Disable Address Validation', help="Check to disable address validation"), | ||
388 | 115 | 'result_in_uppercase': fields.boolean('Results in Upper Case', help="Check is address validation results desired to be in upper case"), | ||
389 | 116 | 'validation_on_save': fields.boolean('Address Validation on Save', help="Check if each address when saved should be validated"), | ||
390 | 117 | 'force_address_validation': fields.boolean('Force Address Validation', help="Check if address validation should be done before tax calculation"), | ||
391 | 118 | 'disable_tax_calculation': fields.boolean('Disable Tax Calculation', help="Check to disable tax calculation"), | ||
392 | 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"), | ||
393 | 120 | 'default_shipping_code_id': fields.many2one('product.tax.code', 'Default Shipping Code', help="The default shipping code which will be passed to Avalara"), | ||
394 | 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"), | ||
395 | 122 | 'active': fields.boolean('Active', help="Uncheck the active field to hide the record"), | ||
396 | 123 | 'company_id': fields.many2one('res.company', 'Company', required=True, help="Company which has subscribed to the AvaTax service"), | ||
397 | 124 | } | ||
398 | 125 | _defaults = { | ||
399 | 126 | 'active': True, | ||
400 | 127 | 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.salestax.avatax', context=c), | ||
401 | 128 | 'request_timeout': 300, | ||
402 | 129 | 'country_ids': _get_avatax_supported_countries | ||
403 | 130 | } | ||
404 | 131 | |||
405 | 132 | _sql_constraints = [ | ||
406 | 133 | ('code_company_uniq', 'unique (company_code)', 'The code of the company must be unique!'), | ||
407 | 134 | ('account_number_company_uniq', 'unique (account_number, company_id)', 'The account number must be unique per company!'), | ||
408 | 135 | ] | ||
409 | 136 | |||
410 | 137 | def _get_avatax_config_company(self, cr, uid, context=None): | ||
411 | 138 | """ Returns the AvaTax configuration for the user company """ | ||
412 | 139 | |||
413 | 140 | user_obj = self.pool.get('res.users') | ||
414 | 141 | user = user_obj.browse(cr, uid, uid, context=context) | ||
415 | 142 | avatax_config_ids = self.search(cr, uid, [('company_id', '=', user.company_id.id)], context=context) | ||
416 | 143 | return avatax_config_ids and self.browse(cr, uid, avatax_config_ids[0], context=context) or False | ||
417 | 144 | |||
418 | 145 | account_salestax_avatax() | ||
419 | 146 | |||
420 | 147 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
421 | 0 | \ No newline at end of file | 148 | \ No newline at end of file |
422 | 1 | 149 | ||
423 | === added file 'account_salestax_avatax/account_salestax_avatax_data.xml' | |||
424 | --- account_salestax_avatax/account_salestax_avatax_data.xml 1970-01-01 00:00:00 +0000 | |||
425 | +++ account_salestax_avatax/account_salestax_avatax_data.xml 2011-09-22 18:37:20 +0000 | |||
426 | @@ -0,0 +1,90 @@ | |||
427 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
428 | 2 | <openerp> | ||
429 | 3 | <data noupdate="1"> | ||
430 | 4 | |||
431 | 5 | <!-- | ||
432 | 6 | Partner Exemption Code | ||
433 | 7 | --> | ||
434 | 8 | |||
435 | 9 | <record id="federal_government_type" model="exemption.code"> | ||
436 | 10 | <field name="name">Federal Government</field> | ||
437 | 11 | <field name="code">A</field> | ||
438 | 12 | </record> | ||
439 | 13 | |||
440 | 14 | <record id="state_government_type" model="exemption.code"> | ||
441 | 15 | <field name="name">State Government</field> | ||
442 | 16 | <field name="code">B</field> | ||
443 | 17 | </record> | ||
444 | 18 | |||
445 | 19 | <record id="tribe_indian_band_type" model="exemption.code"> | ||
446 | 20 | <field name="name">Tribe / Status Indian / Indian Band</field> | ||
447 | 21 | <field name="code">C</field> | ||
448 | 22 | </record> | ||
449 | 23 | |||
450 | 24 | <record id="foreign_diplomat_type" model="exemption.code"> | ||
451 | 25 | <field name="name">Foreign Diplomat</field> | ||
452 | 26 | <field name="code">D</field> | ||
453 | 27 | </record> | ||
454 | 28 | |||
455 | 29 | <record id="charitable_org_type" model="exemption.code"> | ||
456 | 30 | <field name="name">Charitable or Benevolent Org</field> | ||
457 | 31 | <field name="code">E</field> | ||
458 | 32 | </record> | ||
459 | 33 | |||
460 | 34 | <record id="religious_eductional_org_type" model="exemption.code"> | ||
461 | 35 | <field name="name">Religious or Educational Org</field> | ||
462 | 36 | <field name="code">F</field> | ||
463 | 37 | </record> | ||
464 | 38 | |||
465 | 39 | <record id="resale_type" model="exemption.code"> | ||
466 | 40 | <field name="name">Resale</field> | ||
467 | 41 | <field name="code">G</field> | ||
468 | 42 | </record> | ||
469 | 43 | |||
470 | 44 | <record id="commercial_agriculture_production_type" model="exemption.code"> | ||
471 | 45 | <field name="name">Commercial Agricultural Production</field> | ||
472 | 46 | <field name="code">H</field> | ||
473 | 47 | </record> | ||
474 | 48 | |||
475 | 49 | <record id="industrial_manufacturer_type" model="exemption.code"> | ||
476 | 50 | <field name="name">Industrial Production / Manufacturer</field> | ||
477 | 51 | <field name="code">I</field> | ||
478 | 52 | </record> | ||
479 | 53 | |||
480 | 54 | <record id="direct_pay_permit_type" model="exemption.code"> | ||
481 | 55 | <field name="name">Direct Pay Permit</field> | ||
482 | 56 | <field name="code">J</field> | ||
483 | 57 | </record> | ||
484 | 58 | |||
485 | 59 | <record id="direct_mail_type" model="exemption.code"> | ||
486 | 60 | <field name="name">Direct Mail</field> | ||
487 | 61 | <field name="code">K</field> | ||
488 | 62 | </record> | ||
489 | 63 | |||
490 | 64 | <record id="other_type" model="exemption.code"> | ||
491 | 65 | <field name="name">Other</field> | ||
492 | 66 | <field name="code">L</field> | ||
493 | 67 | </record> | ||
494 | 68 | |||
495 | 69 | <record id="local_government_type" model="exemption.code"> | ||
496 | 70 | <field name="name">Local Government</field> | ||
497 | 71 | <field name="code">N</field> | ||
498 | 72 | </record> | ||
499 | 73 | |||
500 | 74 | <record id="commercial_aquaculture_type" model="exemption.code"> | ||
501 | 75 | <field name="name">Commercial Aquaculture</field> | ||
502 | 76 | <field name="code">P</field> | ||
503 | 77 | </record> | ||
504 | 78 | |||
505 | 79 | <record id="commercial_fishery_type" model="exemption.code"> | ||
506 | 80 | <field name="name">Commercial Fishery</field> | ||
507 | 81 | <field name="code">Q</field> | ||
508 | 82 | </record> | ||
509 | 83 | |||
510 | 84 | <record id="non_resident_type" model="exemption.code"> | ||
511 | 85 | <field name="name">Non-Resident</field> | ||
512 | 86 | <field name="code">R</field> | ||
513 | 87 | </record> | ||
514 | 88 | |||
515 | 89 | </data> | ||
516 | 90 | </openerp> | ||
517 | 0 | \ No newline at end of file | 91 | \ No newline at end of file |
518 | 1 | 92 | ||
519 | === added file 'account_salestax_avatax/account_salestax_avatax_view.xml' | |||
520 | --- account_salestax_avatax/account_salestax_avatax_view.xml 1970-01-01 00:00:00 +0000 | |||
521 | +++ account_salestax_avatax/account_salestax_avatax_view.xml 2011-09-22 18:37:20 +0000 | |||
522 | @@ -0,0 +1,215 @@ | |||
523 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
524 | 2 | <openerp> | ||
525 | 3 | <data> | ||
526 | 4 | |||
527 | 5 | <!-- | ||
528 | 6 | AvaTax API Configuration in OpenERP | ||
529 | 7 | --> | ||
530 | 8 | |||
531 | 9 | <record id="view_account_salestax_avatax_form" model="ir.ui.view"> | ||
532 | 10 | <field name="name">account.salestax.avatax.form</field> | ||
533 | 11 | <field name="model">account.salestax.avatax</field> | ||
534 | 12 | <field name="type">form</field> | ||
535 | 13 | <field name="arch" type="xml"> | ||
536 | 14 | <form string="AvaTax API"> | ||
537 | 15 | <group col="6" colspan="4"> | ||
538 | 16 | <field name="company_id" groups="base.group_multi_company"/> | ||
539 | 17 | <field name="company_code"/> | ||
540 | 18 | </group> | ||
541 | 19 | <notebook> | ||
542 | 20 | <page string="Configuration"> | ||
543 | 21 | <group colspan="2" col="2"> | ||
544 | 22 | <separator string="Connection" colspan="2"/> | ||
545 | 23 | <field name="account_number"/> | ||
546 | 24 | <field name="license_key" password="True"/> | ||
547 | 25 | <field name="service_url"/> | ||
548 | 26 | <group colspan="2" col="4"> | ||
549 | 27 | <field name="date_expiration"/> | ||
550 | 28 | <button name="%(action_account_salestax_avatax_ping)d" string="Test Connection" type="action" icon="gtk-go-forward"/> | ||
551 | 29 | </group> | ||
552 | 30 | </group> | ||
553 | 31 | <group colspan="2" col="2"> | ||
554 | 32 | <separator string="Adaptor" colspan="2"/> | ||
555 | 33 | <field name="request_timeout"/> | ||
556 | 34 | <field name="logging"/> | ||
557 | 35 | </group> | ||
558 | 36 | <group colspan="2" col="2" expand="1"> | ||
559 | 37 | <separator string="Address Validation" colspan="2"/> | ||
560 | 38 | <field name="address_validation"/> | ||
561 | 39 | <field name="result_in_uppercase"/> | ||
562 | 40 | <field name="validation_on_save"/> | ||
563 | 41 | <separator string="Countries" colspan="2"/> | ||
564 | 42 | <field name="country_ids" colspan="2" nolabel="1"/> | ||
565 | 43 | </group> | ||
566 | 44 | <group colspan="2" col="2"> | ||
567 | 45 | <separator string="Tax Calculation" colspan="2"/> | ||
568 | 46 | <field name="disable_tax_calculation"/> | ||
569 | 47 | <field name="force_address_validation"/> | ||
570 | 48 | <field name="default_tax_schedule_id" attrs="{'required': [('disable_tax_calculation','=', False)]}"/> | ||
571 | 49 | <field name="default_shipping_code_id" attrs="{'required': [('disable_tax_calculation','=', False)]}" domain="[('type', '=', 'freight')]"/> | ||
572 | 50 | </group> | ||
573 | 51 | </page> | ||
574 | 52 | <page string="About AvaTax"> | ||
575 | 53 | <label string="Use the following for technical support:"/> | ||
576 | 54 | <newline/> | ||
577 | 55 | <label string="Publisher: Avalara.Inc"/> | ||
578 | 56 | <newline/> | ||
579 | 57 | <label string="For Technical Support: support@avalara.com"/> | ||
580 | 58 | <newline/> | ||
581 | 59 | <label string="Support Information: http://www.avalara.com/Contact-Us"/> | ||
582 | 60 | <newline/> | ||
583 | 61 | <label string="Support Telephone: 877-780-4848"/> | ||
584 | 62 | </page> | ||
585 | 63 | </notebook> | ||
586 | 64 | </form> | ||
587 | 65 | </field> | ||
588 | 66 | </record> | ||
589 | 67 | |||
590 | 68 | <record id="view_account_salestax_avatax_tree" model="ir.ui.view"> | ||
591 | 69 | <field name="name">account.salestax.avatax.tree</field> | ||
592 | 70 | <field name="model">account.salestax.avatax</field> | ||
593 | 71 | <field name="type">tree</field> | ||
594 | 72 | <field name="arch" type="xml"> | ||
595 | 73 | <tree string="AvaTax API"> | ||
596 | 74 | <field name="company_id" groups="base.group_multi_company"/> | ||
597 | 75 | <field name="company_code"/> | ||
598 | 76 | <field name="account_number"/> | ||
599 | 77 | <field name="service_url"/> | ||
600 | 78 | <field name="date_expiration"/> | ||
601 | 79 | </tree> | ||
602 | 80 | </field> | ||
603 | 81 | </record> | ||
604 | 82 | |||
605 | 83 | <record id="action_account_salestax_avatax" model="ir.actions.act_window"> | ||
606 | 84 | <field name="name">AvaTax API</field> | ||
607 | 85 | <field name="res_model">account.salestax.avatax</field> | ||
608 | 86 | <field name="view_type">form</field> | ||
609 | 87 | <field name="view_mode">tree,form</field> | ||
610 | 88 | <field name="help">Configuration of AvaTax in OpenERP</field> | ||
611 | 89 | </record> | ||
612 | 90 | |||
613 | 91 | <menuitem id="menu_avatax" name="AvaTax" parent="account.menu_finance_accounting" sequence="30"/> | ||
614 | 92 | |||
615 | 93 | <menuitem action="action_account_salestax_avatax" id="menu_avatax_api" name="AvaTax API" parent="menu_avatax" sequence="30"/> | ||
616 | 94 | |||
617 | 95 | <!-- | ||
618 | 96 | Jurisdiction Code | ||
619 | 97 | --> | ||
620 | 98 | |||
621 | 99 | <record id="view_jurisdiction_code_form" model="ir.ui.view"> | ||
622 | 100 | <field name="name">jurisdiction.code.form</field> | ||
623 | 101 | <field name="model">jurisdiction.code</field> | ||
624 | 102 | <field name="type">form</field> | ||
625 | 103 | <field name="arch" type="xml"> | ||
626 | 104 | <form string="Tax Jurisdiction Code"> | ||
627 | 105 | <group col="6" colspan="4"> | ||
628 | 106 | <field name="name"/> | ||
629 | 107 | <field name="code"/> | ||
630 | 108 | <field name="type"/> | ||
631 | 109 | <field name="state_id" domain="[('country_id','=',country_id)]"/> | ||
632 | 110 | </group> | ||
633 | 111 | <separator colspan="4" string="Accounting Information"/> | ||
634 | 112 | <field name="account_collected_id" domain="[('type','<>','view'),('type','<>','consolidation')]"/> | ||
635 | 113 | <field name="account_paid_id" domain="[('type','<>','view'),('type','<>','consolidation')]"/> | ||
636 | 114 | <separator colspan="4" string="Tax Declaration: Invoices"/> | ||
637 | 115 | <field name="base_code_id"/> | ||
638 | 116 | <field name="base_sign"/> | ||
639 | 117 | <field name="tax_code_id"/> | ||
640 | 118 | <field name="tax_sign"/> | ||
641 | 119 | <separator colspan="4" string="Tax Declaration: Credit Notes"/> | ||
642 | 120 | <field name="ref_base_code_id"/> | ||
643 | 121 | <field name="ref_base_sign"/> | ||
644 | 122 | <field name="ref_tax_code_id"/> | ||
645 | 123 | <field name="ref_tax_sign"/> | ||
646 | 124 | </form> | ||
647 | 125 | </field> | ||
648 | 126 | </record> | ||
649 | 127 | |||
650 | 128 | <record id="view_jurisdiction_code_tree" model="ir.ui.view"> | ||
651 | 129 | <field name="name">jurisdiction.code.tree</field> | ||
652 | 130 | <field name="model">jurisdiction.code</field> | ||
653 | 131 | <field name="type">tree</field> | ||
654 | 132 | <field name="arch" type="xml"> | ||
655 | 133 | <tree string="Tax Jurisdiction Code"> | ||
656 | 134 | <field name="code"/> | ||
657 | 135 | <field name="name"/> | ||
658 | 136 | <field name="type"/> | ||
659 | 137 | <field name="state_id"/> | ||
660 | 138 | </tree> | ||
661 | 139 | </field> | ||
662 | 140 | </record> | ||
663 | 141 | |||
664 | 142 | <!-- | ||
665 | 143 | Tax Schedule | ||
666 | 144 | --> | ||
667 | 145 | |||
668 | 146 | <record id="view_tax_schedule_tree" model="ir.ui.view"> | ||
669 | 147 | <field name="name">tax.schedule.tree</field> | ||
670 | 148 | <field name="model">tax.schedule</field> | ||
671 | 149 | <field name="type">tree</field> | ||
672 | 150 | <field name="arch" type="xml"> | ||
673 | 151 | <tree string="Tax Schedule"> | ||
674 | 152 | <field name="code"/> | ||
675 | 153 | <field name="name"/> | ||
676 | 154 | <field name="country_id"/> | ||
677 | 155 | <field name="company_id" groups="base.group_multi_company"/> | ||
678 | 156 | </tree> | ||
679 | 157 | </field> | ||
680 | 158 | </record> | ||
681 | 159 | |||
682 | 160 | <record id="view_tax_schedule_form" model="ir.ui.view"> | ||
683 | 161 | <field name="name">tax.schedule.form</field> | ||
684 | 162 | <field name="model">tax.schedule</field> | ||
685 | 163 | <field name="type">form</field> | ||
686 | 164 | <field name="arch" type="xml"> | ||
687 | 165 | <form string="Tax Schedule"> | ||
688 | 166 | <group col="6" colspan="4"> | ||
689 | 167 | <field name="name"/> | ||
690 | 168 | <field name="code"/> | ||
691 | 169 | <field name="country_id"/> | ||
692 | 170 | <field name="company_id" groups="base.group_multi_company"/> | ||
693 | 171 | </group> | ||
694 | 172 | <field name="jurisdiction_code_ids" nolabel="1"> | ||
695 | 173 | <form string="Tax Jurisdiction Codes"> | ||
696 | 174 | <group col="6" colspan="4"> | ||
697 | 175 | <field name="name"/> | ||
698 | 176 | <field name="code"/> | ||
699 | 177 | <field name="type"/> | ||
700 | 178 | <field name="state_id" domain="[('country_id','=',parent.country_id)]"/> | ||
701 | 179 | </group> | ||
702 | 180 | <separator colspan="4" string="Accounting Information"/> | ||
703 | 181 | <field name="account_collected_id" domain="[('type','<>','view'),('type','<>','consolidation')]"/> | ||
704 | 182 | <field name="account_paid_id" domain="[('type','<>','view'),('type','<>','consolidation')]"/> | ||
705 | 183 | <separator colspan="4" string="Tax Declaration: Invoices"/> | ||
706 | 184 | <field name="base_code_id"/> | ||
707 | 185 | <field name="base_sign"/> | ||
708 | 186 | <field name="tax_code_id"/> | ||
709 | 187 | <field name="tax_sign"/> | ||
710 | 188 | <separator colspan="4" string="Tax Declaration: Credit Notes"/> | ||
711 | 189 | <field name="ref_base_code_id"/> | ||
712 | 190 | <field name="ref_base_sign"/> | ||
713 | 191 | <field name="ref_tax_code_id"/> | ||
714 | 192 | <field name="ref_tax_sign"/> | ||
715 | 193 | </form> | ||
716 | 194 | <tree string="Tax Jurisdiction Codes"> | ||
717 | 195 | <field name="code"/> | ||
718 | 196 | <field name="name"/> | ||
719 | 197 | <field name="state_id"/> | ||
720 | 198 | <field name="type"/> | ||
721 | 199 | </tree> | ||
722 | 200 | </field> | ||
723 | 201 | </form> | ||
724 | 202 | </field> | ||
725 | 203 | </record> | ||
726 | 204 | |||
727 | 205 | <record id="action_tax_schedule" model="ir.actions.act_window"> | ||
728 | 206 | <field name="name">Tax Schedules</field> | ||
729 | 207 | <field name="res_model">tax.schedule</field> | ||
730 | 208 | <field name="view_type">form</field> | ||
731 | 209 | <field name="view_mode">tree,form</field> | ||
732 | 210 | </record> | ||
733 | 211 | |||
734 | 212 | <menuitem action="action_tax_schedule" id="menu_tax_schedule" name="Tax Schedules" parent="menu_avatax" sequence="29"/> | ||
735 | 213 | |||
736 | 214 | </data> | ||
737 | 215 | </openerp> | ||
738 | 0 | \ No newline at end of file | 216 | \ No newline at end of file |
739 | 1 | 217 | ||
740 | === added file 'account_salestax_avatax/invoice.py' | |||
741 | --- account_salestax_avatax/invoice.py 1970-01-01 00:00:00 +0000 | |||
742 | +++ account_salestax_avatax/invoice.py 2011-09-22 18:37:20 +0000 | |||
743 | @@ -0,0 +1,252 @@ | |||
744 | 1 | # -*- coding: utf-8 -*- | ||
745 | 2 | ############################################################################## | ||
746 | 3 | # | ||
747 | 4 | # OpenERP, Open Source Management Solution | ||
748 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
749 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
750 | 7 | # | ||
751 | 8 | # This program is free software: you can redistribute it and/or modify | ||
752 | 9 | # it under the terms of the GNU Affero General Public License as | ||
753 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
754 | 11 | # License, or (at your option) any later version. | ||
755 | 12 | # | ||
756 | 13 | # This program is distributed in the hope that it will be useful, | ||
757 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
758 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
759 | 16 | # GNU Affero General Public License for more details. | ||
760 | 17 | # | ||
761 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
762 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
763 | 20 | # | ||
764 | 21 | ############################################################################## | ||
765 | 22 | import time | ||
766 | 23 | import string | ||
767 | 24 | |||
768 | 25 | from osv import osv, fields | ||
769 | 26 | from tools.translate import _ | ||
770 | 27 | import decimal_precision as dp | ||
771 | 28 | |||
772 | 29 | class account_invoice(osv.osv): | ||
773 | 30 | _inherit = "account.invoice" | ||
774 | 31 | |||
775 | 32 | def _avatax_calc(self, cr, uid, ids, name, args, context=None): | ||
776 | 33 | res = {} | ||
777 | 34 | avatax_config_obj = self.pool.get('account.salestax.avatax') | ||
778 | 35 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid) | ||
779 | 36 | |||
780 | 37 | for invoice in self.browse(cr, uid, ids, context=context): | ||
781 | 38 | if invoice.type in ['out_invoice', 'out_refund'] and \ | ||
782 | 39 | avatax_config and not avatax_config.disable_tax_calculation and \ | ||
783 | 40 | avatax_config.default_tax_schedule_id.id == invoice.partner_id.tax_schedule_id.id: | ||
784 | 41 | res[invoice.id] = True | ||
785 | 42 | else: | ||
786 | 43 | res[invoice.id] = False | ||
787 | 44 | return res | ||
788 | 45 | |||
789 | 46 | _columns = { | ||
790 | 47 | 'invoice_doc_no': fields.char('Invoice No', size=32, readonly=True, states={'draft':[('readonly',False)]}, help="Reference of the invoice"), | ||
791 | 48 | 'invoice_date': fields.date('Invoice Date', readonly=True), | ||
792 | 49 | 'avatax_calc': fields.function(_avatax_calc, method=True, string='Avatax Calculation', type='boolean', store=True) | ||
793 | 50 | } | ||
794 | 51 | |||
795 | 52 | def action_commit_tax(self, cr, uid, ids, context=None): | ||
796 | 53 | avatax_config_obj = self.pool.get('account.salestax.avatax') | ||
797 | 54 | account_tax_obj = self.pool.get('account.tax') | ||
798 | 55 | partner_obj = self.pool.get('res.partner') | ||
799 | 56 | for invoice in self.browse(cr, uid, ids, context=context): | ||
800 | 57 | if invoice.avatax_calc: | ||
801 | 58 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid) | ||
802 | 59 | sign = invoice.type == 'out_invoice' and 1 or -1 | ||
803 | 60 | lines = self.create_lines(cr, uid, invoice.invoice_line, sign) | ||
804 | 61 | address = partner_obj.address_get(cr, uid, [invoice.company_id.partner_id.id], ['contact']) | ||
805 | 62 | account_tax_obj._check_compute_tax(cr, uid, avatax_config, invoice.date_invoice, | ||
806 | 63 | invoice.internal_number, not invoice.invoice_doc_no and 'SalesInvoice' or 'ReturnInvoice', | ||
807 | 64 | invoice.partner_id, address['contact'], | ||
808 | 65 | invoice.address_invoice_id.id, lines, invoice.shipcharge, invoice.user_id, | ||
809 | 66 | True, invoice.invoice_date, | ||
810 | 67 | invoice.invoice_doc_no, context=context) | ||
811 | 68 | return True | ||
812 | 69 | |||
813 | 70 | def create_lines(self, cr, uid, invoice_lines, sign): | ||
814 | 71 | lines = [] | ||
815 | 72 | for line in invoice_lines: | ||
816 | 73 | lines.append({ | ||
817 | 74 | 'qty': line.quantity, | ||
818 | 75 | 'itemcode': line.product_id and line.product_id.default_code or None, | ||
819 | 76 | 'description': line.name, | ||
820 | 77 | 'amount': sign * line.price_unit * (1-(line.discount or 0.0)/100.0) * line.quantity, | ||
821 | 78 | 'tax_code': line.product_id and ((line.product_id.tax_code_id and line.product_id.tax_code_id.name) or | ||
822 | 79 | (line.product_id.categ_id.tax_code_id and line.product_id.categ_id.tax_code_id.name)) or None | ||
823 | 80 | }) | ||
824 | 81 | return lines | ||
825 | 82 | |||
826 | 83 | def refund(self, cr, uid, ids, date=None, period_id=None, description=None, journal_id=None): | ||
827 | 84 | refund_ids = super(account_invoice, self).refund(cr, uid, ids, date, period_id, description, journal_id) | ||
828 | 85 | invoice = self.browse(cr, uid, ids[0]) | ||
829 | 86 | if invoice.avatax_calc: | ||
830 | 87 | self.write(cr, uid, refund_ids[0], { | ||
831 | 88 | 'invoice_doc_no': invoice.internal_number, | ||
832 | 89 | 'invoice_date': invoice.date_invoice | ||
833 | 90 | }) | ||
834 | 91 | return refund_ids | ||
835 | 92 | |||
836 | 93 | def action_cancel(self, cr, uid, ids, *args): | ||
837 | 94 | account_tax_obj = self.pool.get('account.tax') | ||
838 | 95 | res = super(account_invoice, self).action_cancel(cr, uid, ids, *args) | ||
839 | 96 | |||
840 | 97 | for invoice in self.browse(cr, uid, ids, *args): | ||
841 | 98 | if invoice.avatax_calc and invoice.internal_number: | ||
842 | 99 | doc_type = invoice.type == 'out_invoice' and 'SalesInvoice' or 'ReturnInvoice' | ||
843 | 100 | account_tax_obj.cancel_tax(cr, uid, avatax_config, invoice.internal_number, doc_type, 'DocVoided') | ||
844 | 101 | return res | ||
845 | 102 | |||
846 | 103 | def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj): | ||
847 | 104 | super(account_invoice, self).check_tax_lines(cr, uid, inv, compute_taxes, ait_obj) | ||
848 | 105 | if inv.avatax_calc: | ||
849 | 106 | for tax in inv.tax_line: | ||
850 | 107 | key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) | ||
851 | 108 | if abs(compute_taxes[key]['amount'] - tax.amount) > inv.company_id.currency_id.rounding: | ||
852 | 109 | raise osv.except_osv(_('Warning !'), _('Tax amount different !\nClick on compute to update tax base')) | ||
853 | 110 | |||
854 | 111 | account_invoice() | ||
855 | 112 | |||
856 | 113 | class account_invoice_tax(osv.osv): | ||
857 | 114 | _inherit = "account.invoice.tax" | ||
858 | 115 | |||
859 | 116 | def compute(self, cr, uid, invoice_id, context=None): | ||
860 | 117 | avatax_config_obj = self.pool.get('account.salestax.avatax') | ||
861 | 118 | invoice_obj = self.pool.get('account.invoice') | ||
862 | 119 | partner_obj = self.pool.get('res.partner') | ||
863 | 120 | account_tax_obj = self.pool.get('account.tax') | ||
864 | 121 | jurisdiction_code_obj = self.pool.get('jurisdiction.code') | ||
865 | 122 | cur_obj = self.pool.get('res.currency') | ||
866 | 123 | state_obj = self.pool.get('res.country.state') | ||
867 | 124 | invoice = invoice_obj.browse(cr, uid, invoice_id, context=context) | ||
868 | 125 | tax_grouped = {} | ||
869 | 126 | if invoice.avatax_calc: | ||
870 | 127 | vals = {} | ||
871 | 128 | cur = invoice.currency_id | ||
872 | 129 | company_currency = invoice.company_id.currency_id.id | ||
873 | 130 | lines = invoice_obj.create_lines(cr, uid, invoice.invoice_line, 1) | ||
874 | 131 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid) | ||
875 | 132 | # to check for company address | ||
876 | 133 | company_address = partner_obj.address_get(cr, uid, [invoice.company_id.partner_id.id], ['default']) | ||
877 | 134 | for tax in account_tax_obj._check_compute_tax(cr, uid, avatax_config, | ||
878 | 135 | invoice.date_invoice or time.strftime('%Y-%m-%d'), | ||
879 | 136 | invoice.internal_number, 'SalesOrder', invoice.partner_id, | ||
880 | 137 | company_address['default'], invoice.address_invoice_id.id, | ||
881 | 138 | lines, invoice.shipcharge, invoice.user_id, False, | ||
882 | 139 | invoice.date_invoice or time.strftime('%Y-%m-%d'), | ||
883 | 140 | context=context).TaxSummary[0]: | ||
884 | 141 | val = {} | ||
885 | 142 | state_ids = state_obj.search(cr, uid, [('code', '=', tax.Region)], context=context) | ||
886 | 143 | state_id = state_ids and state_ids[0] or False | ||
887 | 144 | jurisdiction_code_ids = jurisdiction_code_obj.search(cr, uid, [('type', '=', tax['JurisType'].lower()), | ||
888 | 145 | ('tax_schedule_id', '=', avatax_config.default_tax_schedule_id.id), | ||
889 | 146 | ('state_id', '=', state_id)], | ||
890 | 147 | context=context) | ||
891 | 148 | if not jurisdiction_code_ids: | ||
892 | 149 | raise osv.except_osv( | ||
893 | 150 | _('Jurisdiction Code is not defined !'), | ||
894 | 151 | _('You must define a jurisdiction code for %s type for %s state in the tax schedule for %s.' | ||
895 | 152 | % (tax['JurisType'], tax['Region'], avatax_config.default_tax_schedule_id.name))) | ||
896 | 153 | jurisdiction_code = jurisdiction_code_obj.browse(cr, uid, jurisdiction_code_ids[0], context=context) | ||
897 | 154 | |||
898 | 155 | val['invoice_id'] = invoice.id | ||
899 | 156 | val['name'] = tax['TaxName'] or '/' | ||
900 | 157 | val['amount'] = tax['Tax'] | ||
901 | 158 | val['manual'] = False | ||
902 | 159 | val['base'] = tax['Base'] | ||
903 | 160 | |||
904 | 161 | if invoice.type == 'out_invoice': | ||
905 | 162 | val['base_code_id'] = jurisdiction_code.base_code_id.id | ||
906 | 163 | val['tax_code_id'] = jurisdiction_code.tax_code_id.id | ||
907 | 164 | val['base_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id, | ||
908 | 165 | company_currency, val['base'] * jurisdiction_code.base_sign, | ||
909 | 166 | context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False) | ||
910 | 167 | val['tax_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id, | ||
911 | 168 | company_currency, val['amount'] * jurisdiction_code.tax_sign, | ||
912 | 169 | context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False) | ||
913 | 170 | val['account_id'] = jurisdiction_code.account_collected_id.id | ||
914 | 171 | val['base_sign'] = jurisdiction_code.base_sign | ||
915 | 172 | else: | ||
916 | 173 | val['base_code_id'] = jurisdiction_code.ref_base_code_id.id | ||
917 | 174 | val['ref_base_code_id'] = jurisdiction_code.ref_base_code_id.id | ||
918 | 175 | val['tax_code_id'] = jurisdiction_code.ref_tax_code_id.id | ||
919 | 176 | val['base_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id, | ||
920 | 177 | company_currency, val['base'] * jurisdiction_code.ref_base_sign, | ||
921 | 178 | context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False) | ||
922 | 179 | val['tax_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id, | ||
923 | 180 | company_currency, val['amount'] * jurisdiction_code.ref_tax_sign, | ||
924 | 181 | context={'date': invoice.date_invoice or time.strftime('%Y-%m-%d')}, round=False) | ||
925 | 182 | val['account_id'] = jurisdiction_code.account_paid_id.id | ||
926 | 183 | val['ref_base_sign'] = jurisdiction_code.ref_base_sign | ||
927 | 184 | |||
928 | 185 | key = (val['tax_code_id'], val['base_code_id'], val['account_id']) | ||
929 | 186 | if not key in tax_grouped: | ||
930 | 187 | tax_grouped[key] = val | ||
931 | 188 | else: | ||
932 | 189 | tax_grouped[key]['amount'] += val['amount'] | ||
933 | 190 | tax_grouped[key]['base'] += val['base'] | ||
934 | 191 | tax_grouped[key]['base_amount'] += val['base_amount'] | ||
935 | 192 | tax_grouped[key]['tax_amount'] += val['tax_amount'] | ||
936 | 193 | |||
937 | 194 | for t in tax_grouped.values(): | ||
938 | 195 | t['base'] = cur_obj.round(cr, uid, cur, t['base']) | ||
939 | 196 | t['amount'] = cur_obj.round(cr, uid, cur, t['amount']) | ||
940 | 197 | t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount']) | ||
941 | 198 | t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount']) | ||
942 | 199 | return tax_grouped | ||
943 | 200 | return super(account_invoice_tax, self).compute(cr, uid, invoice_id, context=context) | ||
944 | 201 | |||
945 | 202 | account_invoice_tax() | ||
946 | 203 | |||
947 | 204 | class account_invoice_line(osv.osv): | ||
948 | 205 | _inherit = "account.invoice.line" | ||
949 | 206 | |||
950 | 207 | def move_line_get(self, cr, uid, invoice_id, context=None): | ||
951 | 208 | res = [] | ||
952 | 209 | tax_obj = self.pool.get('account.tax') | ||
953 | 210 | cur_obj = self.pool.get('res.currency') | ||
954 | 211 | invoice_obj = self.pool.get('account.invoice') | ||
955 | 212 | ait_obj = self.pool.get('account.invoice.tax') | ||
956 | 213 | invoice = invoice_obj.browse(cr, uid, invoice_id, context=context) | ||
957 | 214 | company_currency = invoice.company_id.currency_id.id | ||
958 | 215 | |||
959 | 216 | if invoice.avatax_calc: | ||
960 | 217 | for line in invoice.invoice_line: | ||
961 | 218 | mres = self.move_line_get_item(cr, uid, line, context) | ||
962 | 219 | if not mres: | ||
963 | 220 | continue | ||
964 | 221 | res.append(mres) | ||
965 | 222 | tax_code_found= False | ||
966 | 223 | |||
967 | 224 | for tax in ait_obj.compute(cr, uid, invoice_id, context=context).values(): | ||
968 | 225 | if invoice.type == 'out_invoice': | ||
969 | 226 | tax_code_id = tax['base_code_id'] | ||
970 | 227 | tax_amount = line.price_subtotal * tax['base_sign'] | ||
971 | 228 | else: | ||
972 | 229 | tax_code_id = tax['ref_base_code_id'] | ||
973 | 230 | tax_amount = line.price_subtotal * tax['ref_base_sign'] | ||
974 | 231 | |||
975 | 232 | if tax_code_found: | ||
976 | 233 | if not tax_code_id: | ||
977 | 234 | continue | ||
978 | 235 | res.append(self.move_line_get_item(cr, uid, line, context)) | ||
979 | 236 | res[-1]['price'] = 0.0 | ||
980 | 237 | res[-1]['account_analytic_id'] = False | ||
981 | 238 | elif not tax_code_id: | ||
982 | 239 | continue | ||
983 | 240 | tax_code_found = True | ||
984 | 241 | |||
985 | 242 | res[-1]['tax_code_id'] = tax_code_id | ||
986 | 243 | res[-1]['tax_amount'] = cur_obj.compute(cr, uid, invoice.currency_id.id, company_currency, | ||
987 | 244 | tax_amount, context={'date': invoice.date_invoice}) | ||
988 | 245 | |||
989 | 246 | else: | ||
990 | 247 | res = super(account_invoice_line, self).move_line_get( cr, uid, invoice_id, context=context) | ||
991 | 248 | return res | ||
992 | 249 | |||
993 | 250 | account_invoice_line() | ||
994 | 251 | |||
995 | 252 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
996 | 0 | \ No newline at end of file | 253 | \ No newline at end of file |
997 | 1 | 254 | ||
998 | === added file 'account_salestax_avatax/partner.py' | |||
999 | --- account_salestax_avatax/partner.py 1970-01-01 00:00:00 +0000 | |||
1000 | +++ account_salestax_avatax/partner.py 2011-09-22 18:37:20 +0000 | |||
1001 | @@ -0,0 +1,169 @@ | |||
1002 | 1 | # -*- coding: utf-8 -*- | ||
1003 | 2 | ############################################################################## | ||
1004 | 3 | # | ||
1005 | 4 | # OpenERP, Open Source Management Solution | ||
1006 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
1007 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
1008 | 7 | # | ||
1009 | 8 | # This program is free software: you can redistribute it and/or modify | ||
1010 | 9 | # it under the terms of the GNU Affero General Public License as | ||
1011 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
1012 | 11 | # License, or (at your option) any later version. | ||
1013 | 12 | # | ||
1014 | 13 | # This program is distributed in the hope that it will be useful, | ||
1015 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1016 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1017 | 16 | # GNU Affero General Public License for more details. | ||
1018 | 17 | # | ||
1019 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
1020 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1021 | 20 | # | ||
1022 | 21 | ############################################################################## | ||
1023 | 22 | import time | ||
1024 | 23 | |||
1025 | 24 | from osv import osv, fields | ||
1026 | 25 | from tools.translate import _ | ||
1027 | 26 | import decimal_precision as dp | ||
1028 | 27 | |||
1029 | 28 | from suds_client import AvaTaxService, BaseAddress | ||
1030 | 29 | |||
1031 | 30 | class res_partner(osv.osv): | ||
1032 | 31 | _inherit = 'res.partner' | ||
1033 | 32 | _columns = { | ||
1034 | 33 | 'exemption_number': fields.char('Exemption Number', size=64, help="Indicates if the customer is exempt or not"), | ||
1035 | 34 | 'exemption_code_id': fields.many2one('exemption.code', 'Exemption Code', help="Indicates the type of exemption the customer may have"), | ||
1036 | 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") | ||
1037 | 36 | } | ||
1038 | 37 | |||
1039 | 38 | res_partner() | ||
1040 | 39 | |||
1041 | 40 | class res_partner_address(osv.osv): | ||
1042 | 41 | _inherit = 'res.partner.address' | ||
1043 | 42 | _columns = { | ||
1044 | 43 | 'date_validation': fields.date('Last Validation Date', readonly=True, help="The date the address was last validated by AvaTax and accepted"), | ||
1045 | 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"), | ||
1046 | 45 | 'latitude': fields.char('Latitude', size=32), | ||
1047 | 46 | 'longitude': fields.char('Longitude', size=32), | ||
1048 | 47 | 'validated_on_save': fields.boolean('Validated On Save', help="Indicates if the address is already validated on save before calling the wizard") | ||
1049 | 48 | } | ||
1050 | 49 | |||
1051 | 50 | def check_avatax_support(self, cr, uid, avatax_config, country_id, context=None): | ||
1052 | 51 | """ Checks if address validation pre-condition meets. """ | ||
1053 | 52 | |||
1054 | 53 | if avatax_config.address_validation: | ||
1055 | 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")) | ||
1056 | 55 | if country_id and country_id not in [x.id for x in avatax_config.country_ids]: | ||
1057 | 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.")) | ||
1058 | 57 | return True | ||
1059 | 58 | |||
1060 | 59 | def get_state_id(self, cr, uid, code, context=None): | ||
1061 | 60 | """ Returns the id of the state from the code. """ | ||
1062 | 61 | |||
1063 | 62 | state_obj = self.pool.get('res.country.state') | ||
1064 | 63 | return state_obj.search(cr, uid, [('code', '=', code)], context=context)[0] | ||
1065 | 64 | |||
1066 | 65 | def get_country_id(self, cr, uid, code, context=None): | ||
1067 | 66 | """ Returns the id of the country from the code. """ | ||
1068 | 67 | |||
1069 | 68 | country_obj = self.pool.get('res.country') | ||
1070 | 69 | return country_obj.search(cr, uid, [('code', '=', code)], context=context)[0] | ||
1071 | 70 | |||
1072 | 71 | def get_state_code(self, cr, uid, state_id, context=None): | ||
1073 | 72 | """ Returns the code from the id of the state. """ | ||
1074 | 73 | |||
1075 | 74 | state_obj = self.pool.get('res.country.state') | ||
1076 | 75 | return state_id and state_obj.browse(cr, uid, state_id, context=context).code | ||
1077 | 76 | |||
1078 | 77 | def get_country_code(self, cr, uid, country_id, context=None): | ||
1079 | 78 | """ Returns the code from the id of the country. """ | ||
1080 | 79 | |||
1081 | 80 | country_obj = self.pool.get('res.country') | ||
1082 | 81 | return country_id and country_obj.browse(cr, uid, country_id, context=context).code | ||
1083 | 82 | |||
1084 | 83 | def _validate_address(self, cr, uid, address, avatax_config=False, context=None): | ||
1085 | 84 | """ Returns the valid address from the AvaTax Address Validation Service. """ | ||
1086 | 85 | |||
1087 | 86 | avatax_config_obj= self.pool.get('account.salestax.avatax') | ||
1088 | 87 | if context is None: | ||
1089 | 88 | context = {} | ||
1090 | 89 | |||
1091 | 90 | if not avatax_config: | ||
1092 | 91 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid, context=context) | ||
1093 | 92 | |||
1094 | 93 | # Create the AvaTax Address service with the configuration parameters set for the instance | ||
1095 | 94 | avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key, | ||
1096 | 95 | avatax_config.service_url, avatax_config.request_timeout, avatax_config.logging) | ||
1097 | 96 | addSvc = avapoint.create_address_service().addressSvc | ||
1098 | 97 | |||
1099 | 98 | # Obtain the state code & country code and create a BaseAddress Object | ||
1100 | 99 | state_code = address.get('state_id') and self.get_state_code(cr, uid, address['state_id'], context=context) | ||
1101 | 100 | country_code = address.get('country_id') and self.get_country_code(cr, uid, address['country_id'], context=context) | ||
1102 | 101 | baseaddress = BaseAddress(addSvc, address.get('street') or None, address.get('street2') or None, | ||
1103 | 102 | address.get('city'), address.get('zip'), state_code, country_code, 0).data | ||
1104 | 103 | result = avapoint.validate_address(baseaddress, avatax_config.result_in_uppercase and 'Upper' or 'Default') | ||
1105 | 104 | valid_address = result.ValidAddresses[0][0] | ||
1106 | 105 | return valid_address | ||
1107 | 106 | |||
1108 | 107 | def update_address(self, cr, uid, vals, ids=None, from_write=False, context=None): | ||
1109 | 108 | """ Updates the vals dictionary with the valid address as returned from the AvaTax Address Validation. """ | ||
1110 | 109 | |||
1111 | 110 | address = vals | ||
1112 | 111 | |||
1113 | 112 | if (vals.get('street') or vals.get('street2') or vals.get('zip') or vals.get('city') or \ | ||
1114 | 113 | vals.get('country_id') or vals.get('state_id')): | ||
1115 | 114 | |||
1116 | 115 | address_obj = self.pool.get('res.partner.address') | ||
1117 | 116 | avatax_config_obj= self.pool.get('account.salestax.avatax') | ||
1118 | 117 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid, context=context) | ||
1119 | 118 | |||
1120 | 119 | if avatax_config and avatax_config.validation_on_save: | ||
1121 | 120 | # It implies that there is AvaTax configuration existing for the user company with | ||
1122 | 121 | # option 'Address Validation when a address is saved' | ||
1123 | 122 | # Check if the other conditions are met | ||
1124 | 123 | self.check_avatax_support(cr, uid, avatax_config, address.get('country_id'), context=context) | ||
1125 | 124 | |||
1126 | 125 | # If this method is called from the 'write' method then we also need to pass | ||
1127 | 126 | # the previous address along with the modifications in the vals dictionary | ||
1128 | 127 | if from_write: | ||
1129 | 128 | fields_to_read = filter(lambda x: x not in vals, ['street', 'street2', 'city', 'state_id', 'zip', 'country_id']) | ||
1130 | 129 | address = fields_to_read and address_obj.read(cr, uid, ids, fields_to_read, context=context)[0] or {} | ||
1131 | 130 | address['state_id'] = address.get('state_id') and address['state_id'][0] | ||
1132 | 131 | address['country_id'] = address.get('country_id') and address['country_id'][0] | ||
1133 | 132 | address.update(vals) | ||
1134 | 133 | |||
1135 | 134 | valid_address = self._validate_address(cr, uid, address, avatax_config, context=context) | ||
1136 | 135 | vals.update({ | ||
1137 | 136 | 'street': valid_address.Line1, | ||
1138 | 137 | 'street2': valid_address.Line2, | ||
1139 | 138 | 'city': valid_address.City, | ||
1140 | 139 | 'state_id': self.get_state_id(cr, uid, valid_address.Region, context=context), | ||
1141 | 140 | 'zip': valid_address.PostalCode, | ||
1142 | 141 | 'country_id': self.get_country_id(cr, uid, valid_address.Country, context=context), | ||
1143 | 142 | 'latitude': valid_address.Latitude, | ||
1144 | 143 | 'longitude': valid_address.Longitude, | ||
1145 | 144 | 'date_validation': time.strftime('%Y-%m-%d'), | ||
1146 | 145 | 'validation_method': 'avatax', | ||
1147 | 146 | 'validated_on_save': True | ||
1148 | 147 | }) | ||
1149 | 148 | return vals | ||
1150 | 149 | |||
1151 | 150 | |||
1152 | 151 | def create(self, cr, uid, vals, context=None): | ||
1153 | 152 | vals = self.update_address(cr, uid, vals, context=context) | ||
1154 | 153 | return super(res_partner_address, self).create(cr, uid, vals, context) | ||
1155 | 154 | |||
1156 | 155 | def write(self, cr, uid, ids, vals, context=None): | ||
1157 | 156 | if context is None: | ||
1158 | 157 | context = {} | ||
1159 | 158 | |||
1160 | 159 | # Follow the normal write process if it's a write operation from the wizard | ||
1161 | 160 | if context.get('from_validate_button', False): | ||
1162 | 161 | return super(res_partner_address, self).write(cr, uid, ids, vals, context) | ||
1163 | 162 | |||
1164 | 163 | if context.get('active_id', False): | ||
1165 | 164 | vals = self.update_address(cr, uid, vals, ids, True, context=context) | ||
1166 | 165 | return super(res_partner_address, self).write(cr, uid, ids, vals, context) | ||
1167 | 166 | |||
1168 | 167 | res_partner_address() | ||
1169 | 168 | |||
1170 | 169 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
1171 | 0 | \ No newline at end of file | 170 | \ No newline at end of file |
1172 | 1 | 171 | ||
1173 | === added file 'account_salestax_avatax/partner_view.xml' | |||
1174 | --- account_salestax_avatax/partner_view.xml 1970-01-01 00:00:00 +0000 | |||
1175 | +++ account_salestax_avatax/partner_view.xml 2011-09-22 18:37:20 +0000 | |||
1176 | @@ -0,0 +1,63 @@ | |||
1177 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
1178 | 2 | <openerp> | ||
1179 | 3 | <data> | ||
1180 | 4 | |||
1181 | 5 | <!-- | ||
1182 | 6 | Partner Tax & Address Validation | ||
1183 | 7 | --> | ||
1184 | 8 | |||
1185 | 9 | <record id="view_partner_details_form_inherit" model="ir.ui.view"> | ||
1186 | 10 | <field name="name">res.partner.details.form.inherit</field> | ||
1187 | 11 | <field name="model">res.partner</field> | ||
1188 | 12 | <field name="type">form</field> | ||
1189 | 13 | <field name="inherit_id" ref="base.view_partner_form"/> | ||
1190 | 14 | <field name="arch" type="xml"> | ||
1191 | 15 | <xpath expr="/form/notebook/page[@string='Accounting']/field[@name='bank_ids']" position="before"> | ||
1192 | 16 | <group col="1" colspan="2"> | ||
1193 | 17 | <separator string="Tax Details" colspan="2" col="2"/> | ||
1194 | 18 | <field name="tax_schedule_id"/> | ||
1195 | 19 | <field name="exemption_number"/> | ||
1196 | 20 | <field name="exemption_code_id"/> | ||
1197 | 21 | </group> | ||
1198 | 22 | </xpath> | ||
1199 | 23 | <xpath expr="/form/notebook/page[@string='General']/field[@name='address']/form" position="inside"> | ||
1200 | 24 | <group col="4" colspan="2"> | ||
1201 | 25 | <field name="latitude"/> | ||
1202 | 26 | <field name="longitude"/> | ||
1203 | 27 | </group> | ||
1204 | 28 | <newline/> | ||
1205 | 29 | <group colspan="2" col="4"> | ||
1206 | 30 | <separator string="Validation" colspan="4" col="4"/> | ||
1207 | 31 | <field name="date_validation"/> | ||
1208 | 32 | <field name="validation_method"/> | ||
1209 | 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}"/> | ||
1210 | 34 | </group> | ||
1211 | 35 | </xpath> | ||
1212 | 36 | </field> | ||
1213 | 37 | </record> | ||
1214 | 38 | |||
1215 | 39 | <!-- | ||
1216 | 40 | Partner Address | ||
1217 | 41 | --> | ||
1218 | 42 | |||
1219 | 43 | <record id="view_partner_address_details_form_inherit" model="ir.ui.view"> | ||
1220 | 44 | <field name="name">res.partner.address.details.form.inherit</field> | ||
1221 | 45 | <field name="model">res.partner.address</field> | ||
1222 | 46 | <field name="type">form</field> | ||
1223 | 47 | <field name="inherit_id" ref="base.view_partner_address_form1"/> | ||
1224 | 48 | <field name="arch" type="xml"> | ||
1225 | 49 | <xpath expr="/form[@string='Address']/group[@col='2']/field[@name='state_id']" position="after"> | ||
1226 | 50 | <field name="latitude"/> | ||
1227 | 51 | <field name="longitude"/> | ||
1228 | 52 | <group colspan="2" col="4"> | ||
1229 | 53 | <separator string="Validation" colspan="4" col="4"/> | ||
1230 | 54 | <field name="date_validation"/> | ||
1231 | 55 | <field name="validation_method"/> | ||
1232 | 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}"/> | ||
1233 | 57 | </group> | ||
1234 | 58 | </xpath> | ||
1235 | 59 | </field> | ||
1236 | 60 | </record> | ||
1237 | 61 | |||
1238 | 62 | </data> | ||
1239 | 63 | </openerp> | ||
1240 | 0 | 64 | ||
1241 | === added file 'account_salestax_avatax/product.py' | |||
1242 | --- account_salestax_avatax/product.py 1970-01-01 00:00:00 +0000 | |||
1243 | +++ account_salestax_avatax/product.py 2011-09-22 18:37:20 +0000 | |||
1244 | @@ -0,0 +1,57 @@ | |||
1245 | 1 | # -*- coding: utf-8 -*- | ||
1246 | 2 | ############################################################################## | ||
1247 | 3 | # | ||
1248 | 4 | # OpenERP, Open Source Management Solution | ||
1249 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
1250 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
1251 | 7 | # | ||
1252 | 8 | # This program is free software: you can redistribute it and/or modify | ||
1253 | 9 | # it under the terms of the GNU Affero General Public License as | ||
1254 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
1255 | 11 | # License, or (at your option) any later version. | ||
1256 | 12 | # | ||
1257 | 13 | # This program is distributed in the hope that it will be useful, | ||
1258 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1259 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1260 | 16 | # GNU Affero General Public License for more details. | ||
1261 | 17 | # | ||
1262 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
1263 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1264 | 20 | # | ||
1265 | 21 | ############################################################################## | ||
1266 | 22 | from osv import osv, fields | ||
1267 | 23 | |||
1268 | 24 | class product_tax_code(osv.osv): | ||
1269 | 25 | _name = 'product.tax.code' | ||
1270 | 26 | _description = 'Tax Code' | ||
1271 | 27 | _columns = { | ||
1272 | 28 | 'name': fields.char('Code', size=8, required=True), | ||
1273 | 29 | 'description': fields.char('Description', size=64), | ||
1274 | 30 | 'type': fields.selection([('product', 'Product'), ('freight', 'Freight'), ('service', 'Service'), | ||
1275 | 31 | ('digital', 'Digital'), ('other', 'Other')], 'Type', required=True, help="Type of tax code as defined in AvaTax"), | ||
1276 | 32 | 'company_id': fields.many2one('res.company', 'Company', required=True), | ||
1277 | 33 | } | ||
1278 | 34 | _defaults = { | ||
1279 | 35 | 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.tax.code', context=c), | ||
1280 | 36 | } | ||
1281 | 37 | |||
1282 | 38 | product_tax_code() | ||
1283 | 39 | |||
1284 | 40 | |||
1285 | 41 | class product_template(osv.osv): | ||
1286 | 42 | _inherit = "product.template" | ||
1287 | 43 | _columns = { | ||
1288 | 44 | 'tax_code_id': fields.many2one('product.tax.code', 'Tax Code', help="AvaTax Tax Code") | ||
1289 | 45 | } | ||
1290 | 46 | |||
1291 | 47 | product_template() | ||
1292 | 48 | |||
1293 | 49 | class product_category(osv.osv): | ||
1294 | 50 | _inherit = "product.category" | ||
1295 | 51 | _columns = { | ||
1296 | 52 | 'tax_code_id': fields.many2one('product.tax.code', 'Tax Code', help="AvaTax Tax Code") | ||
1297 | 53 | } | ||
1298 | 54 | |||
1299 | 55 | product_category() | ||
1300 | 56 | |||
1301 | 57 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
1302 | 0 | \ No newline at end of file | 58 | \ No newline at end of file |
1303 | 1 | 59 | ||
1304 | === added file 'account_salestax_avatax/product_view.xml' | |||
1305 | --- account_salestax_avatax/product_view.xml 1970-01-01 00:00:00 +0000 | |||
1306 | +++ account_salestax_avatax/product_view.xml 2011-09-22 18:37:20 +0000 | |||
1307 | @@ -0,0 +1,108 @@ | |||
1308 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
1309 | 2 | <openerp> | ||
1310 | 3 | <data> | ||
1311 | 4 | |||
1312 | 5 | <!-- | ||
1313 | 6 | Product Tax Code | ||
1314 | 7 | --> | ||
1315 | 8 | |||
1316 | 9 | <record id="view_product_tax_code_tree" model="ir.ui.view"> | ||
1317 | 10 | <field name="name">product.tax.code.tree</field> | ||
1318 | 11 | <field name="model">product.tax.code</field> | ||
1319 | 12 | <field name="type">tree</field> | ||
1320 | 13 | <field name="arch" type="xml"> | ||
1321 | 14 | <tree string="Product Tax Code"> | ||
1322 | 15 | <field name="name"/> | ||
1323 | 16 | <field name="description"/> | ||
1324 | 17 | <field name="type"/> | ||
1325 | 18 | <field name="company_id" groups="base.group_multi_company"/> | ||
1326 | 19 | </tree> | ||
1327 | 20 | </field> | ||
1328 | 21 | </record> | ||
1329 | 22 | |||
1330 | 23 | <record id="view_product_tax_code_form" model="ir.ui.view"> | ||
1331 | 24 | <field name="name">product.tax.code.form</field> | ||
1332 | 25 | <field name="model">product.tax.code</field> | ||
1333 | 26 | <field name="type">form</field> | ||
1334 | 27 | <field name="arch" type="xml"> | ||
1335 | 28 | <form string="Product Tax Code"> | ||
1336 | 29 | <field name="name"/> | ||
1337 | 30 | <field name="company_id" groups="base.group_multi_company"/> | ||
1338 | 31 | <field name="description"/> | ||
1339 | 32 | <field name="type"/> | ||
1340 | 33 | </form> | ||
1341 | 34 | </field> | ||
1342 | 35 | </record> | ||
1343 | 36 | |||
1344 | 37 | |||
1345 | 38 | <record id="action_product_tax_code" model="ir.actions.act_window"> | ||
1346 | 39 | <field name="name">Product Tax Codes</field> | ||
1347 | 40 | <field name="res_model">product.tax.code</field> | ||
1348 | 41 | <field name="view_type">form</field> | ||
1349 | 42 | <field name="view_mode">tree,form</field> | ||
1350 | 43 | </record> | ||
1351 | 44 | |||
1352 | 45 | <menuitem action="action_product_tax_code" id="menu_product_tax_code" name="Product Tax Codes" parent="menu_avatax" sequence="29"/> | ||
1353 | 46 | |||
1354 | 47 | <!-- | ||
1355 | 48 | Product | ||
1356 | 49 | --> | ||
1357 | 50 | |||
1358 | 51 | <record id="view_product_normal_form_avatax_inherit" model="ir.ui.view"> | ||
1359 | 52 | <field name="name">product.normal.form.avatax.inherit</field> | ||
1360 | 53 | <field name="model">product.product</field> | ||
1361 | 54 | <field name="type">form</field> | ||
1362 | 55 | <field name="inherit_id" ref="product.product_normal_form_view"/> | ||
1363 | 56 | <field name="priority">30</field> | ||
1364 | 57 | <field name="arch" type="xml"> | ||
1365 | 58 | <xpath expr="/form/notebook/page[@string='Accounting']/group[@name='properties']" position="inside"> | ||
1366 | 59 | <separator string="AvaTax Properties" colspan="4"/> | ||
1367 | 60 | <group colspan="2" col="2"> | ||
1368 | 61 | <field name="tax_code_id"/> | ||
1369 | 62 | </group> | ||
1370 | 63 | </xpath> | ||
1371 | 64 | </field> | ||
1372 | 65 | </record> | ||
1373 | 66 | |||
1374 | 67 | <!-- | ||
1375 | 68 | Product Template | ||
1376 | 69 | --> | ||
1377 | 70 | |||
1378 | 71 | <record id="view_product_template_form_avatax_inherit" model="ir.ui.view"> | ||
1379 | 72 | <field name="name">product.template.form.avatax.inherit</field> | ||
1380 | 73 | <field name="model">product.template</field> | ||
1381 | 74 | <field name="type">form</field> | ||
1382 | 75 | <field name="inherit_id" ref="product.product_template_form_view"/> | ||
1383 | 76 | <field name="priority">30</field> | ||
1384 | 77 | <field name="arch" type="xml"> | ||
1385 | 78 | <xpath expr="/form/notebook/page[@string='Accounting']" position="inside"> | ||
1386 | 79 | <separator string="AvaTax Properties" colspan="4"/> | ||
1387 | 80 | <group colspan="2" col="2"> | ||
1388 | 81 | <field name="tax_code_id"/> | ||
1389 | 82 | </group> | ||
1390 | 83 | <newline/> | ||
1391 | 84 | </xpath> | ||
1392 | 85 | </field> | ||
1393 | 86 | </record> | ||
1394 | 87 | |||
1395 | 88 | <!-- | ||
1396 | 89 | Product Category | ||
1397 | 90 | --> | ||
1398 | 91 | |||
1399 | 92 | <record id="view_product_category_form_avatax_inherit" model="ir.ui.view"> | ||
1400 | 93 | <field name="name">product.category.form.avatax.inherit</field> | ||
1401 | 94 | <field name="model">product.category</field> | ||
1402 | 95 | <field name="type">form</field> | ||
1403 | 96 | <field name="inherit_id" ref="product.product_category_form_view"/> | ||
1404 | 97 | <field name="arch" type="xml"> | ||
1405 | 98 | <form position="inside"> | ||
1406 | 99 | <group col="2" colspan="2"> | ||
1407 | 100 | <separator string="AvaTax Properties" colspan="2"/> | ||
1408 | 101 | <field name="tax_code_id"/> | ||
1409 | 102 | </group> | ||
1410 | 103 | </form> | ||
1411 | 104 | </field> | ||
1412 | 105 | </record> | ||
1413 | 106 | |||
1414 | 107 | </data> | ||
1415 | 108 | </openerp> | ||
1416 | 0 | 109 | ||
1417 | === added file 'account_salestax_avatax/sale.py' | |||
1418 | --- account_salestax_avatax/sale.py 1970-01-01 00:00:00 +0000 | |||
1419 | +++ account_salestax_avatax/sale.py 2011-09-22 18:37:20 +0000 | |||
1420 | @@ -0,0 +1,118 @@ | |||
1421 | 1 | # -*- coding: utf-8 -*- | ||
1422 | 2 | ############################################################################## | ||
1423 | 3 | # | ||
1424 | 4 | # OpenERP, Open Source Management Solution | ||
1425 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
1426 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
1427 | 7 | # | ||
1428 | 8 | # This program is free software: you can redistribute it and/or modify | ||
1429 | 9 | # it under the terms of the GNU Affero General Public License as | ||
1430 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
1431 | 11 | # License, or (at your option) any later version. | ||
1432 | 12 | # | ||
1433 | 13 | # This program is distributed in the hope that it will be useful, | ||
1434 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1435 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1436 | 16 | # GNU Affero General Public License for more details. | ||
1437 | 17 | # | ||
1438 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
1439 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1440 | 20 | # | ||
1441 | 21 | ############################################################################## | ||
1442 | 22 | from osv import osv, fields | ||
1443 | 23 | from tools.translate import _ | ||
1444 | 24 | import decimal_precision as dp | ||
1445 | 25 | |||
1446 | 26 | class sale_order(osv.osv): | ||
1447 | 27 | _inherit = "sale.order" | ||
1448 | 28 | |||
1449 | 29 | def _amount_all(self, cr, uid, ids, field_name, arg, context=None): | ||
1450 | 30 | currency_obj = self.pool.get('res.currency') | ||
1451 | 31 | res = {} | ||
1452 | 32 | avatax_config_obj = self.pool.get('account.salestax.avatax') | ||
1453 | 33 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid) | ||
1454 | 34 | res = super(sale_order, self)._amount_all(cr, uid, ids, field_name, arg, context=context) | ||
1455 | 35 | for order in self.browse(cr, uid, ids, context=context): | ||
1456 | 36 | if avatax_config and not avatax_config.disable_tax_calculation and \ | ||
1457 | 37 | avatax_config.default_tax_schedule_id.id == order.partner_id.tax_schedule_id.id: | ||
1458 | 38 | |||
1459 | 39 | res[order.id] = { | ||
1460 | 40 | 'amount_untaxed': 0.0, | ||
1461 | 41 | 'amount_tax': 0.0, | ||
1462 | 42 | 'amount_total': 0.0, | ||
1463 | 43 | } | ||
1464 | 44 | for line in order.order_line: | ||
1465 | 45 | res[order.id]['amount_untaxed'] += line.price_subtotal | ||
1466 | 46 | res[order.id]['amount_tax'] = currency_obj.round(cr, uid, order.pricelist_id.currency_id, order.tax_amount) | ||
1467 | 47 | res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax'] | ||
1468 | 48 | |||
1469 | 49 | return res | ||
1470 | 50 | |||
1471 | 51 | def _get_order(self, cr, uid, ids, context=None): | ||
1472 | 52 | return super(sale_order, self)._get_order(cr, uid, ids, context=context) | ||
1473 | 53 | |||
1474 | 54 | _columns = { | ||
1475 | 55 | 'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Untaxed Amount', | ||
1476 | 56 | store = { | ||
1477 | 57 | 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), | ||
1478 | 58 | 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), | ||
1479 | 59 | }, | ||
1480 | 60 | multi='sums', help="The amount without tax."), | ||
1481 | 61 | 'amount_tax': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Taxes', | ||
1482 | 62 | store = { | ||
1483 | 63 | 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), | ||
1484 | 64 | 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), | ||
1485 | 65 | }, | ||
1486 | 66 | multi='sums', help="The tax amount."), | ||
1487 | 67 | 'amount_total': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Total', | ||
1488 | 68 | store = { | ||
1489 | 69 | 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), | ||
1490 | 70 | 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), | ||
1491 | 71 | }, | ||
1492 | 72 | multi='sums', help="The total amount."), | ||
1493 | 73 | 'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Sale Price')), | ||
1494 | 74 | } | ||
1495 | 75 | |||
1496 | 76 | def create_lines(self, cr, uid, order_lines): | ||
1497 | 77 | lines = [] | ||
1498 | 78 | for line in order_lines: | ||
1499 | 79 | lines.append({ | ||
1500 | 80 | 'qty': line.product_uom_qty, | ||
1501 | 81 | 'itemcode': line.product_id and line.product_id.default_code or None, | ||
1502 | 82 | 'description': line.name, | ||
1503 | 83 | 'amount': line.price_unit * (1-(line.discount or 0.0)/100.0) * line.product_uom_qty, | ||
1504 | 84 | 'tax_code': line.product_id and ((line.product_id.tax_code_id and line.product_id.tax_code_id.name) or | ||
1505 | 85 | (line.product_id.categ_id.tax_code_id and line.product_id.categ_id.tax_code_id.name)) or None | ||
1506 | 86 | }) | ||
1507 | 87 | return lines | ||
1508 | 88 | |||
1509 | 89 | def compute_tax(self, cr, uid, ids, context=None): | ||
1510 | 90 | avatax_config_obj = self.pool.get('account.salestax.avatax') | ||
1511 | 91 | account_tax_obj = self.pool.get('account.tax') | ||
1512 | 92 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid) | ||
1513 | 93 | partner_obj = self.pool.get('res.partner') | ||
1514 | 94 | |||
1515 | 95 | for order in self.browse(cr, uid, ids): | ||
1516 | 96 | if avatax_config and not avatax_config.disable_tax_calculation and \ | ||
1517 | 97 | avatax_config.default_tax_schedule_id.id == order.partner_id.tax_schedule_id.id: | ||
1518 | 98 | address = partner_obj.address_get(cr, uid, [order.company_id.partner_id.id], ['contact']) | ||
1519 | 99 | lines = self.create_lines(cr, uid, order.order_line) | ||
1520 | 100 | tax_amount = account_tax_obj._check_compute_tax(cr, uid, avatax_config, order.date_confirm or order.date_order, | ||
1521 | 101 | order.name, 'SalesOrder', order.partner_id, address['contact'], | ||
1522 | 102 | order.partner_shipping_id.id, lines, order.shipcharge, order.user_id, | ||
1523 | 103 | context=context).TotalTax | ||
1524 | 104 | self.write(cr, uid, [order.id], {'tax_amount': tax_amount, 'order_line': []}) | ||
1525 | 105 | return True | ||
1526 | 106 | |||
1527 | 107 | def button_dummy(self, cr, uid, ids, context=None): | ||
1528 | 108 | self.compute_tax(cr, uid, ids, context=context) | ||
1529 | 109 | return super(sale_order, self).button_dummy(cr, uid, ids, context=context) | ||
1530 | 110 | |||
1531 | 111 | def action_wait(self, cr, uid, ids, *args): | ||
1532 | 112 | res = super(sale_order, self).action_wait(cr, uid, ids) | ||
1533 | 113 | self.compute_tax(cr, uid, ids) | ||
1534 | 114 | return True | ||
1535 | 115 | |||
1536 | 116 | sale_order() | ||
1537 | 117 | |||
1538 | 118 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
1539 | 0 | 119 | ||
1540 | === added directory 'account_salestax_avatax/security' | |||
1541 | === added file 'account_salestax_avatax/security/account_salestax_avatax_security.xml' | |||
1542 | --- account_salestax_avatax/security/account_salestax_avatax_security.xml 1970-01-01 00:00:00 +0000 | |||
1543 | +++ account_salestax_avatax/security/account_salestax_avatax_security.xml 2011-09-22 18:37:20 +0000 | |||
1544 | @@ -0,0 +1,29 @@ | |||
1545 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
1546 | 2 | <openerp> | ||
1547 | 3 | <data noupdate="1"> | ||
1548 | 4 | |||
1549 | 5 | <!-- Rule for multi-company --> | ||
1550 | 6 | |||
1551 | 7 | <record id="account_salestax_avatax_comp_rule" model="ir.rule"> | ||
1552 | 8 | <field name="name" >AvaTax multi-company</field> | ||
1553 | 9 | <field name="model_id" ref="model_account_salestax_avatax"/> | ||
1554 | 10 | <field name="global" eval="True"/> | ||
1555 | 11 | <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> | ||
1556 | 12 | </record> | ||
1557 | 13 | |||
1558 | 14 | <record id="account_salestax_avatax_tax_schedule_rule" model="ir.rule"> | ||
1559 | 15 | <field name="name" >AvaTax Tax Schedule multi-company</field> | ||
1560 | 16 | <field name="model_id" ref="model_tax_schedule"/> | ||
1561 | 17 | <field name="global" eval="True"/> | ||
1562 | 18 | <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> | ||
1563 | 19 | </record> | ||
1564 | 20 | |||
1565 | 21 | <record id="account_salestax_avatax_tax_codes_rule" model="ir.rule"> | ||
1566 | 22 | <field name="name" >AvaTax Tax Code multi-company</field> | ||
1567 | 23 | <field name="model_id" ref="model_product_tax_code"/> | ||
1568 | 24 | <field name="global" eval="True"/> | ||
1569 | 25 | <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> | ||
1570 | 26 | </record> | ||
1571 | 27 | |||
1572 | 28 | </data> | ||
1573 | 29 | </openerp> | ||
1574 | 0 | 30 | ||
1575 | === added file 'account_salestax_avatax/security/ir.model.access.csv' | |||
1576 | --- account_salestax_avatax/security/ir.model.access.csv 1970-01-01 00:00:00 +0000 | |||
1577 | +++ account_salestax_avatax/security/ir.model.access.csv 2011-09-22 18:37:20 +0000 | |||
1578 | @@ -0,0 +1,11 @@ | |||
1579 | 1 | "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" | ||
1580 | 2 | "access_account_salestax_avatax manager","account.salestax.avatax.manager","model_account_salestax_avatax","account.group_account_manager",1,1,1,1 | ||
1581 | 3 | "access_account_salestax_avatax employee","account.salestax.avatax.employee","model_account_salestax_avatax","base.group_user",1,0,0,0 | ||
1582 | 4 | "access_tax_schedule employee","tax.schedule.employee","model_tax_schedule","base.group_user",1,0,0,0 | ||
1583 | 5 | "access_tax_schedule manager","tax.schedule.manager","model_tax_schedule","account.group_account_manager",1,1,1,1 | ||
1584 | 6 | "access_jurisdiction_code employee","jurisdiction.code.employee","model_jurisdiction_code","base.group_user",1,0,0,0 | ||
1585 | 7 | "access_jurisdiction_code manager","jurisdiction.code.manager","model_jurisdiction_code","account.group_account_manager",1,1,1,1 | ||
1586 | 8 | "access_product_tax_code employee","product.tax.code.employee","model_product_tax_code","base.group_user",1,0,0,0 | ||
1587 | 9 | "access_product_tax_code manager","product.tax.code.manager","model_product_tax_code","account.group_account_manager",1,1,1,1 | ||
1588 | 10 | "access_exemption_code manager","exemption.code.manager","model_exemption_code","account.group_account_manager",1,1,1,1 | ||
1589 | 11 | "access_exemption_code employee","exemption.code.employee","model_exemption_code","base.group_user",1,0,0,0 | ||
1590 | 0 | 12 | ||
1591 | === added file 'account_salestax_avatax/suds_client.py' | |||
1592 | --- account_salestax_avatax/suds_client.py 1970-01-01 00:00:00 +0000 | |||
1593 | +++ account_salestax_avatax/suds_client.py 2011-09-22 18:37:20 +0000 | |||
1594 | @@ -0,0 +1,295 @@ | |||
1595 | 1 | import suds | ||
1596 | 2 | import socket | ||
1597 | 3 | import urllib2 | ||
1598 | 4 | import string | ||
1599 | 5 | import os | ||
1600 | 6 | import datetime | ||
1601 | 7 | |||
1602 | 8 | from tools.translate import _ | ||
1603 | 9 | from osv import osv | ||
1604 | 10 | |||
1605 | 11 | class AvaTaxService: | ||
1606 | 12 | |||
1607 | 13 | def enable_log(self): | ||
1608 | 14 | import logging, tempfile | ||
1609 | 15 | logger = logging.getLogger('suds.client') | ||
1610 | 16 | logger.setLevel(logging.DEBUG) | ||
1611 | 17 | handler = logging.FileHandler(os.path.join(tempfile.gettempdir(), "soap-messages.log")) | ||
1612 | 18 | logger.propagate = False | ||
1613 | 19 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') | ||
1614 | 20 | handler.setFormatter(formatter) | ||
1615 | 21 | logger.addHandler(handler) | ||
1616 | 22 | |||
1617 | 23 | def __init__(self, username, password, url, timeout, enable_log=False): | ||
1618 | 24 | self.username = username #This is the company's Development/Production Account number | ||
1619 | 25 | self.password = password #Put in the License Key received from AvaTax | ||
1620 | 26 | self.url = url | ||
1621 | 27 | self.timeout = timeout | ||
1622 | 28 | enable_log and self.enable_log() | ||
1623 | 29 | |||
1624 | 30 | def create_tax_service(self): | ||
1625 | 31 | self.taxSvc = self.service('tax') | ||
1626 | 32 | return self | ||
1627 | 33 | |||
1628 | 34 | def create_address_service(self): | ||
1629 | 35 | self.addressSvc = self.service('address') | ||
1630 | 36 | return self | ||
1631 | 37 | |||
1632 | 38 | def service(self, name): | ||
1633 | 39 | nameCap = string.capitalize(name) # So this will be 'Tax' or 'Address' | ||
1634 | 40 | # The Python SUDS library can fetch the WSDL down from the server | ||
1635 | 41 | # or use a local file URL. We'll use a local file URL. | ||
1636 | 42 | # wsdl_url = 'file:///' + os.getcwd().replace('\\', '/') + '/%ssvc.wsdl.xml' % name | ||
1637 | 43 | # If you want to fetch the WSDL from the server, use this instead: | ||
1638 | 44 | wsdl_url = 'https://avatax.avalara.net/%s/%ssvc.wsdl' % (nameCap, nameCap) | ||
1639 | 45 | #wsdl_url = 'file:///home/rima/Downloads/AvaTax.wsdl' | ||
1640 | 46 | # So now we'll fetch the WSDL and build the service proxy, | ||
1641 | 47 | # handling any errors appropriately. | ||
1642 | 48 | try: | ||
1643 | 49 | svc = suds.client.Client(wsdl_url) | ||
1644 | 50 | except urllib2.URLError, details: | ||
1645 | 51 | raise osv.except_osv(_('Server Failed to Response'), _(details)) | ||
1646 | 52 | else: | ||
1647 | 53 | svc.set_options(service='%sSvc' % nameCap) | ||
1648 | 54 | svc.set_options(port='%sSvcSoap' % nameCap) | ||
1649 | 55 | svc.set_options(location='%s/%s/%sSvc.asmx' % (self.url, nameCap, nameCap)) | ||
1650 | 56 | svc.set_options(wsse=self.my_security(self.username, self.password)) | ||
1651 | 57 | svc.set_options(soapheaders=self.my_profile()) | ||
1652 | 58 | svc.set_options(timeout=self.timeout) | ||
1653 | 59 | return svc | ||
1654 | 60 | |||
1655 | 61 | def my_security(self, username, password): | ||
1656 | 62 | |||
1657 | 63 | token = suds.wsse.UsernameToken(username, password) | ||
1658 | 64 | |||
1659 | 65 | # AvaTax leaves the WSSE Nonce and Created elements as | ||
1660 | 66 | # optional. As explained in XXX, you should include these if at | ||
1661 | 67 | # all possible, to make your connection more secure. | ||
1662 | 68 | # Nonce (optional) is a randomly generated, cryptographic token | ||
1663 | 69 | # used to prevent theft and replay attacks. We recommend sending | ||
1664 | 70 | # it if your SOAP client library supports it. | ||
1665 | 71 | token.setnonce() | ||
1666 | 72 | |||
1667 | 73 | # Created (optional) identifies when the message was created and | ||
1668 | 74 | # prevents replay attacks. We recommend sending it if your SOAP | ||
1669 | 75 | # client library supports it. | ||
1670 | 76 | token.setcreated() | ||
1671 | 77 | security = suds.wsse.Security() | ||
1672 | 78 | security.tokens.append(token) | ||
1673 | 79 | return security | ||
1674 | 80 | |||
1675 | 81 | def my_profile(self): | ||
1676 | 82 | |||
1677 | 83 | # Set elements adapter defaults | ||
1678 | 84 | ADAPTER = 'Novapoint Group,0.1' | ||
1679 | 85 | |||
1680 | 86 | # Profile Client. | ||
1681 | 87 | CLIENT = 'Novapoint Group,0.1' | ||
1682 | 88 | |||
1683 | 89 | #Build the Profile element | ||
1684 | 90 | profileNameSpace = ('ns1', 'http://avatax.avalara.com/services') | ||
1685 | 91 | profile = suds.sax.element.Element('Profile', ns=profileNameSpace) | ||
1686 | 92 | profile.append(suds.sax.element.Element('Client', ns=profileNameSpace).setText(CLIENT)) | ||
1687 | 93 | profile.append(suds.sax.element.Element('Adapter', ns=profileNameSpace).setText(ADAPTER)) | ||
1688 | 94 | hostname = socket.gethostname() | ||
1689 | 95 | profile.append(suds.sax.element.Element('Machine', ns=profileNameSpace).setText(hostname)) | ||
1690 | 96 | return profile | ||
1691 | 97 | |||
1692 | 98 | def get_result(self, svc, operation, request): | ||
1693 | 99 | try: | ||
1694 | 100 | result = operation(request) | ||
1695 | 101 | except suds.WebFault, e: | ||
1696 | 102 | raise osv.except_osv(_('Error'), _(e.fault.faultstring)) | ||
1697 | 103 | except urllib2.HTTPError, e: | ||
1698 | 104 | raise osv.except_osv(_('Server Failed to Response'), _(e.code)) | ||
1699 | 105 | except urllib2.URLError, details: | ||
1700 | 106 | # We could also print the SOAP request here: | ||
1701 | 107 | # print svc.last_sent() | ||
1702 | 108 | raise osv.except_osv(_('Failed to reach the server'), _(details.reason)) | ||
1703 | 109 | else: | ||
1704 | 110 | if (result.ResultCode != 'Success'): | ||
1705 | 111 | raise osv.except_osv(('Error'), _(AvaTaxError(result.ResultCode, result.Messages))) | ||
1706 | 112 | else: | ||
1707 | 113 | return result | ||
1708 | 114 | |||
1709 | 115 | def ping(self): | ||
1710 | 116 | return self.get_result(self.taxSvc, self.taxSvc.service.Ping, '') | ||
1711 | 117 | |||
1712 | 118 | def is_authorized(self): | ||
1713 | 119 | return self.get_result(self.taxSvc, self.taxSvc.service.IsAuthorized, 'GetTax,PostTax') | ||
1714 | 120 | |||
1715 | 121 | def validate_address(self, baseaddress, textcase='Default'): | ||
1716 | 122 | # The Validate() operation needs a complex parameter, a | ||
1717 | 123 | # ValidateRequest. SUDS gives us a factory method on the service | ||
1718 | 124 | # object that creates a proxy class we can use. | ||
1719 | 125 | request = self.addressSvc.factory.create('ValidateRequest') | ||
1720 | 126 | # SUDS allows us to get defaults set up for us automatically. But | ||
1721 | 127 | # to keep this sample code simple, we won't do that here. | ||
1722 | 128 | # These are the elements required by the WSDL, with the defaults | ||
1723 | 129 | # set by the .NET adapter. | ||
1724 | 130 | # See the PHP sample code or .NET SDK Help File for what each of these is for. | ||
1725 | 131 | # TextCase, as with many other elements, is defined in the WSDL as | ||
1726 | 132 | # an enumeration. Depending on your SOAP client library, language | ||
1727 | 133 | # and platform, you may have these available to you. If you can | ||
1728 | 134 | # use enumerations rather than strings you should do so, because | ||
1729 | 135 | # it offers compile-time checking that can save your development | ||
1730 | 136 | # time. We're just using strings elsewhere in this sample code, to | ||
1731 | 137 | # keep things simple. But for TextCase we'll use SUDS | ||
1732 | 138 | # enumerations, just to show you how this sort of thing would | ||
1733 | 139 | # work. | ||
1734 | 140 | # This is the SUDS documentation on enumerations: | ||
1735 | 141 | # https://fedorahosted.org/suds/wiki/Documentation#ENUMERATIONS | ||
1736 | 142 | # So this is how we'll do things if we're just using strings | ||
1737 | 143 | # | ||
1738 | 144 | # request.TextCase = 'Default' | ||
1739 | 145 | # | ||
1740 | 146 | textCase = self.addressSvc.factory.create('TextCase') | ||
1741 | 147 | request.TextCase = textcase | ||
1742 | 148 | request.Coordinates = True | ||
1743 | 149 | request.Taxability = False | ||
1744 | 150 | request.Date = '1900-01-01' | ||
1745 | 151 | request.Address = baseaddress | ||
1746 | 152 | |||
1747 | 153 | result = self.get_result(self.addressSvc, self.addressSvc.service.Validate, request) | ||
1748 | 154 | return result | ||
1749 | 155 | |||
1750 | 156 | def get_tax(self, company_code, doc_date, doc_type, partner_code, doc_code, origin, destination, | ||
1751 | 157 | received_lines, exemption_no=None, customer_usage_type=None, salesman_code=None, commit=False, invoice_date=None, reference_code=None): | ||
1752 | 158 | lineslist = [] | ||
1753 | 159 | request = self.taxSvc.factory.create('GetTaxRequest') | ||
1754 | 160 | # We'll first default everything just as the .NET adapter does | ||
1755 | 161 | request.Commit = commit | ||
1756 | 162 | request.DetailLevel = 'Diagnostic' | ||
1757 | 163 | request.Discount = 0.0 | ||
1758 | 164 | request.ServiceMode = 'Automatic' # PHP defaults this to Automatic; Java likewise | ||
1759 | 165 | request.PaymentDate = '1900-01-01' | ||
1760 | 166 | request.ExchangeRate = 45 | ||
1761 | 167 | request.ExchangeRateEffDate = '2011-07-07' | ||
1762 | 168 | request.HashCode = 0 | ||
1763 | 169 | request.ReferenceCode = reference_code | ||
1764 | 170 | if invoice_date: | ||
1765 | 171 | taxoverride = self.taxSvc.factory.create('TaxOverride') | ||
1766 | 172 | taxoverride.TaxOverrideType = 'TaxDate' | ||
1767 | 173 | taxoverride.TaxDate = invoice_date | ||
1768 | 174 | taxoverride.TaxAmount = 0 | ||
1769 | 175 | taxoverride.Reason = 'Return Items' | ||
1770 | 176 | request.TaxOverride = taxoverride | ||
1771 | 177 | # We'll now set the properties as we would normally, including | ||
1772 | 178 | # some that have already been defaulted. | ||
1773 | 179 | # The GetTax call will exactly match that in the PHP sample | ||
1774 | 180 | # code. So you can compare the XML from them line-by-line. We | ||
1775 | 181 | # suggest you try to get the same call set up in whatever language | ||
1776 | 182 | # and platform you're using, so you can compare your XML likewise. | ||
1777 | 183 | # See the PHP sample code for an explanation of each of these. | ||
1778 | 184 | request.CompanyCode = company_code | ||
1779 | 185 | request.DocDate = doc_date | ||
1780 | 186 | request.DocType = doc_type | ||
1781 | 187 | request.DocCode = doc_code | ||
1782 | 188 | request.CustomerCode = partner_code | ||
1783 | 189 | request.ExemptionNo = exemption_no | ||
1784 | 190 | request.CustomerUsageType = customer_usage_type | ||
1785 | 191 | request.SalespersonCode = salesman_code | ||
1786 | 192 | # Now the AddressCode, which we'll reference later. Note that | ||
1787 | 193 | # although it's a string and so you could use 'Origin' and | ||
1788 | 194 | # 'Destination' here, because addresses could be defined at the | ||
1789 | 195 | # line level you're best off just numbering them. | ||
1790 | 196 | # | ||
1791 | 197 | # Furthermore, any Messages you get back in an error from the | ||
1792 | 198 | # service will, in Message.RefersTo, reference the addresses | ||
1793 | 199 | # indexed as they appear in Addresses, starting at zero, so if you | ||
1794 | 200 | # use AddressCode = '0', '1' and so on, the index in the Message | ||
1795 | 201 | # will match the appropriate item. | ||
1796 | 202 | # Be very careful to make the addresscodes all match up. If, for | ||
1797 | 203 | # example, origin.AddressCode does not match an entry in | ||
1798 | 204 | # Addresses, the origin address will be treated as if it were | ||
1799 | 205 | # blank. | ||
1800 | 206 | # Now we'll set some of the other properties as usual | ||
1801 | 207 | addresses = self.taxSvc.factory.create('ArrayOfBaseAddress') | ||
1802 | 208 | addresses.BaseAddress = [origin, destination] | ||
1803 | 209 | request.Addresses = addresses | ||
1804 | 210 | request.OriginCode = '0' # Referencing an address above | ||
1805 | 211 | request.DestinationCode = '1' # Referencing an address above | ||
1806 | 212 | for line in range(0, len(received_lines)): | ||
1807 | 213 | line1 = self.taxSvc.factory.create('Line') | ||
1808 | 214 | line1.Qty = received_lines[line].get('qty', 1) | ||
1809 | 215 | line1.Discounted = False | ||
1810 | 216 | line1.No = '%d' %line | ||
1811 | 217 | line1.ItemCode = received_lines[line].get('itemcode', None) | ||
1812 | 218 | line1.Description = received_lines[line].get('description', None) | ||
1813 | 219 | line1.Amount = received_lines[line].get('amount', 0.0) | ||
1814 | 220 | line1.TaxCode = received_lines[line].get('tax_code', None) | ||
1815 | 221 | lineslist.append(line1) | ||
1816 | 222 | # So now we build request.Lines | ||
1817 | 223 | lines = self.taxSvc.factory.create('ArrayOfLine') | ||
1818 | 224 | lines.Line = lineslist | ||
1819 | 225 | request.Lines = lines | ||
1820 | 226 | # And we're ready to make the call | ||
1821 | 227 | result = self.get_result(self.taxSvc, self.taxSvc.service.GetTax, request) | ||
1822 | 228 | return result | ||
1823 | 229 | |||
1824 | 230 | def cancel_tax(self, company_code, doc_code, doc_type, cancel_code): | ||
1825 | 231 | request = self.taxSvc.factory.create('CancelTaxRequest') | ||
1826 | 232 | request.CompanyCode = company_code | ||
1827 | 233 | request.DocCode = doc_code | ||
1828 | 234 | request.DocType = doc_type | ||
1829 | 235 | request.CancelCode = cancel_code | ||
1830 | 236 | result = self.get_result(self.taxSvc, self.taxSvc.service.CancelTax, request) | ||
1831 | 237 | return result | ||
1832 | 238 | |||
1833 | 239 | class Error(Exception): | ||
1834 | 240 | """Base class for exceptions in this module.""" | ||
1835 | 241 | pass | ||
1836 | 242 | |||
1837 | 243 | class AvaTaxError(Error): | ||
1838 | 244 | """Exception raised for errors calling AvaTax. | ||
1839 | 245 | |||
1840 | 246 | Attributes: | ||
1841 | 247 | resultCode -- result.ResultCode | ||
1842 | 248 | messages -- result.Messages | ||
1843 | 249 | """ | ||
1844 | 250 | |||
1845 | 251 | def __init__(self, resultCode, messages): | ||
1846 | 252 | self.resultCode = resultCode | ||
1847 | 253 | self.messages = messages | ||
1848 | 254 | |||
1849 | 255 | def __str__(self): | ||
1850 | 256 | str = '' | ||
1851 | 257 | for item in self.messages: | ||
1852 | 258 | message = item[1][0] # SUDS gives us the message in a list, in a tuple | ||
1853 | 259 | |||
1854 | 260 | str = "Severity: %s\n\nDetails: %s\n\n RefersTo: %s\n\n Summary: %s" \ | ||
1855 | 261 | % (message.Severity, message.Details, message.RefersTo, message.Summary) | ||
1856 | 262 | return str | ||
1857 | 263 | |||
1858 | 264 | class BaseAddress: | ||
1859 | 265 | |||
1860 | 266 | def __init__(self, addSvc, Line1=None, Line2=None, City=None, PostalCode=None, Region=None, Country=None, AddressCode=None): | ||
1861 | 267 | self.data = addSvc.factory.create('BaseAddress') | ||
1862 | 268 | self.data.TaxRegionId = 0 | ||
1863 | 269 | self.data.Line1 = Line1 | ||
1864 | 270 | self.data.Line2 = Line2 | ||
1865 | 271 | self.data.City = City | ||
1866 | 272 | self.data.PostalCode = PostalCode | ||
1867 | 273 | self.data.Region = Region | ||
1868 | 274 | self.data.Country = Country | ||
1869 | 275 | self.data.AddressCode = AddressCode | ||
1870 | 276 | |||
1871 | 277 | class Line: | ||
1872 | 278 | |||
1873 | 279 | def __init__(self, taxSvc, ItemCode, Amount, Qty, Description=None, TaxCode=None): | ||
1874 | 280 | self.taxSvc = taxSvc | ||
1875 | 281 | # We're not setting No here | ||
1876 | 282 | self.data = self.defaultLine() | ||
1877 | 283 | self.data.ItemCode = ItemCode | ||
1878 | 284 | self.data.Amount = Amount | ||
1879 | 285 | self.data.Qty = Qty | ||
1880 | 286 | self.data.Description = Description | ||
1881 | 287 | self.data.TaxCode = TaxCode | ||
1882 | 288 | |||
1883 | 289 | def defaultLine(self): | ||
1884 | 290 | line = self.taxSvc.factory.create('Line') | ||
1885 | 291 | line.Qty = 1 | ||
1886 | 292 | line.Discounted = False | ||
1887 | 293 | return line | ||
1888 | 294 | |||
1889 | 295 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
1890 | 0 | \ No newline at end of file | 296 | \ No newline at end of file |
1891 | 1 | 297 | ||
1892 | === added directory 'account_salestax_avatax/wizard' | |||
1893 | === added file 'account_salestax_avatax/wizard/__init__.py' | |||
1894 | --- account_salestax_avatax/wizard/__init__.py 1970-01-01 00:00:00 +0000 | |||
1895 | +++ account_salestax_avatax/wizard/__init__.py 2011-09-22 18:37:20 +0000 | |||
1896 | @@ -0,0 +1,26 @@ | |||
1897 | 1 | # -*- coding: utf-8 -*- | ||
1898 | 2 | ############################################################################## | ||
1899 | 3 | # | ||
1900 | 4 | # OpenERP, Open Source Management Solution | ||
1901 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
1902 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
1903 | 7 | # | ||
1904 | 8 | # This program is free software: you can redistribute it and/or modify | ||
1905 | 9 | # it under the terms of the GNU Affero General Public License as | ||
1906 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
1907 | 11 | # License, or (at your option) any later version. | ||
1908 | 12 | # | ||
1909 | 13 | # This program is distributed in the hope that it will be useful, | ||
1910 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1911 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1912 | 16 | # GNU Affero General Public License for more details. | ||
1913 | 17 | # | ||
1914 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
1915 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1916 | 20 | # | ||
1917 | 21 | ############################################################################## | ||
1918 | 22 | |||
1919 | 23 | import account_salestax_avatax_ping | ||
1920 | 24 | import account_salestax_avatax_address_validate | ||
1921 | 25 | |||
1922 | 26 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
1923 | 0 | \ No newline at end of file | 27 | \ No newline at end of file |
1924 | 1 | 28 | ||
1925 | === added file 'account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py' | |||
1926 | --- account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py 1970-01-01 00:00:00 +0000 | |||
1927 | +++ account_salestax_avatax/wizard/account_salestax_avatax_address_validate.py 2011-09-22 18:37:20 +0000 | |||
1928 | @@ -0,0 +1,135 @@ | |||
1929 | 1 | # -*- coding: utf-8 -*- | ||
1930 | 2 | ############################################################################## | ||
1931 | 3 | # | ||
1932 | 4 | # OpenERP, Open Source Management Solution | ||
1933 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
1934 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
1935 | 7 | # | ||
1936 | 8 | # This program is free software: you can redistribute it and/or modify | ||
1937 | 9 | # it under the terms of the GNU Affero General Public License as | ||
1938 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
1939 | 11 | # License, or (at your option) any later version. | ||
1940 | 12 | # | ||
1941 | 13 | # This program is distributed in the hope that it will be useful, | ||
1942 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1943 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1944 | 16 | # GNU Affero General Public License for more details. | ||
1945 | 17 | # | ||
1946 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
1947 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1948 | 20 | # | ||
1949 | 21 | ############################################################################## | ||
1950 | 22 | import time | ||
1951 | 23 | |||
1952 | 24 | from osv import osv, fields | ||
1953 | 25 | from tools.translate import _ | ||
1954 | 26 | |||
1955 | 27 | class account_salestax_avatax_address_validate(osv.osv_memory): | ||
1956 | 28 | _name = 'account.salestax.avatax.address.validate' | ||
1957 | 29 | _description = 'Address Validation using AvaTax' | ||
1958 | 30 | _columns = { | ||
1959 | 31 | 'original_street': fields.char('Street', size=64, readonly=True), | ||
1960 | 32 | 'original_street2': fields.char('Street2', size=64, readonly=True), | ||
1961 | 33 | 'original_city': fields.char('City', size=64, readonly=True), | ||
1962 | 34 | 'original_zip': fields.char('Zip', size=64, readonly=True), | ||
1963 | 35 | 'original_state': fields.char('State', size=64, readonly=True), | ||
1964 | 36 | 'original_country': fields.char('Country', size=64, readonly=True), | ||
1965 | 37 | 'street': fields.char('Street', size=64), | ||
1966 | 38 | 'street2': fields.char('Street2', size=64), | ||
1967 | 39 | 'city': fields.char('City', size=64), | ||
1968 | 40 | 'zip': fields.char('Zip', size=64), | ||
1969 | 41 | 'state': fields.char('State', size=64), | ||
1970 | 42 | 'country': fields.char('Country', size=64), | ||
1971 | 43 | 'latitude': fields.char('Laitude', size=64), | ||
1972 | 44 | 'longitude': fields.char('Longitude', size=64) | ||
1973 | 45 | } | ||
1974 | 46 | |||
1975 | 47 | def view_init(self, cr, uid, fields, context=None): | ||
1976 | 48 | """ Checks for precondition before wizard executes. """ | ||
1977 | 49 | address_obj = self.pool.get('res.partner.address') | ||
1978 | 50 | avatax_config_obj= self.pool.get('account.salestax.avatax') | ||
1979 | 51 | |||
1980 | 52 | if context is None: | ||
1981 | 53 | context = {} | ||
1982 | 54 | |||
1983 | 55 | # Check if there is avatax tax service active for the user company. | ||
1984 | 56 | # Prevent validating the address if the address validation is disabled by the administrator. | ||
1985 | 57 | |||
1986 | 58 | if context.get('active_id', False) and context.get('active_model', False) == 'res.partner.address': | ||
1987 | 59 | avatax_config = avatax_config_obj._get_avatax_config_company(cr, uid, context=context) | ||
1988 | 60 | if not avatax_config: | ||
1989 | 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.")) | ||
1990 | 62 | address = address_obj.browse(cr, uid, context['active_id'], context=context) | ||
1991 | 63 | if address.validated_on_save and avatax_config.validation_on_save: | ||
1992 | 64 | raise osv.except_osv(_('Address Already Validated'), _("Address Validation on Save is already active in the AvaTax Configuration.")) | ||
1993 | 65 | address_obj.check_avatax_support(cr, uid, avatax_config, address.country_id and address.country_id.id or False, context=context) | ||
1994 | 66 | return True | ||
1995 | 67 | |||
1996 | 68 | def default_get(self, cr, uid, fields, context=None): | ||
1997 | 69 | """ Returns the default values for the fields. """ | ||
1998 | 70 | |||
1999 | 71 | res = super(account_salestax_avatax_address_validate, self).default_get(cr, uid, fields, context) | ||
2000 | 72 | |||
2001 | 73 | if context.get('active_id', False) and context.get('active_model', False) == 'res.partner.address': | ||
2002 | 74 | address_obj = self.pool.get('res.partner.address') | ||
2003 | 75 | address = address_obj.read(cr, uid, context['active_id'], ['street', 'street2', 'city', 'state_id', 'zip', 'country_id'], context=context) | ||
2004 | 76 | address['state_id'] = address.get('state_id') and address['state_id'][0] | ||
2005 | 77 | address['country_id'] = address.get('country_id') and address['country_id'][0] | ||
2006 | 78 | |||
2007 | 79 | # Get the valid result from the AvaTax Address Validation Service | ||
2008 | 80 | valid_address = address_obj._validate_address(cr, uid, address, context=context) | ||
2009 | 81 | |||
2010 | 82 | if 'original_street' in fields: | ||
2011 | 83 | res.update({'original_street': address['street']}) | ||
2012 | 84 | if 'original_street2' in fields: | ||
2013 | 85 | res.update({'original_street2': address['street2']}) | ||
2014 | 86 | if 'original_city' in fields: | ||
2015 | 87 | res.update({'original_city': address['city']}) | ||
2016 | 88 | if 'original_state' in fields: | ||
2017 | 89 | res.update({'original_state': address_obj.get_state_code(cr, uid, address['state_id'], context=context)}) | ||
2018 | 90 | if 'original_zip' in fields: | ||
2019 | 91 | res.update({'original_zip': address['zip']}) | ||
2020 | 92 | if 'original_country' in fields: | ||
2021 | 93 | res.update({'original_country': address_obj.get_country_code(cr, uid, address['country_id'], context=context)}) | ||
2022 | 94 | if 'street' in fields: | ||
2023 | 95 | res.update({'street': valid_address.Line1}) | ||
2024 | 96 | if 'street2' in fields: | ||
2025 | 97 | res.update({'street2': valid_address.Line2}) | ||
2026 | 98 | if 'city' in fields: | ||
2027 | 99 | res.update({'city': valid_address.City}) | ||
2028 | 100 | if 'state' in fields: | ||
2029 | 101 | res.update({'state': valid_address.Region}) | ||
2030 | 102 | if 'zip' in fields: | ||
2031 | 103 | res.update({'zip': valid_address.PostalCode}) | ||
2032 | 104 | if 'country' in fields: | ||
2033 | 105 | res.update({'country': valid_address.Country}) | ||
2034 | 106 | if 'latitude' in fields: | ||
2035 | 107 | res.update({'latitude': valid_address.Latitude}) | ||
2036 | 108 | if 'longitude' in fields: | ||
2037 | 109 | res.update({'longitude': valid_address.Longitude}) | ||
2038 | 110 | return res | ||
2039 | 111 | |||
2040 | 112 | def accept_valid_address(self, cr, uid, ids, context=None): | ||
2041 | 113 | """ Updates the existing address with the valid address returned by the service. """ | ||
2042 | 114 | |||
2043 | 115 | valid_address = self.read(cr, uid, ids, context=context)[0] | ||
2044 | 116 | if context.get('active_id', False): | ||
2045 | 117 | address_obj = self.pool.get('res.partner.address') | ||
2046 | 118 | address_result = { | ||
2047 | 119 | 'street': valid_address['street'], | ||
2048 | 120 | 'street2': valid_address['street2'], | ||
2049 | 121 | 'city': valid_address['city'], | ||
2050 | 122 | 'state_id': address_obj.get_state_id(cr, uid, valid_address['state'], context=context), | ||
2051 | 123 | 'zip': valid_address['zip'], | ||
2052 | 124 | 'country_id': address_obj.get_country_id(cr, uid, valid_address['country'], context=context), | ||
2053 | 125 | 'latitude': valid_address['latitude'], | ||
2054 | 126 | 'longitude': valid_address['longitude'], | ||
2055 | 127 | 'date_validation': time.strftime('%Y-%m-%d'), | ||
2056 | 128 | 'validation_method': 'avatax' | ||
2057 | 129 | } | ||
2058 | 130 | address_obj.write(cr, uid, [context['active_id']], address_result, context=context) | ||
2059 | 131 | return {'type': 'ir.actions.act_window_close'} | ||
2060 | 132 | |||
2061 | 133 | account_salestax_avatax_address_validate() | ||
2062 | 134 | |||
2063 | 135 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
2064 | 0 | 136 | ||
2065 | === added file 'account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml' | |||
2066 | --- account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml 1970-01-01 00:00:00 +0000 | |||
2067 | +++ account_salestax_avatax/wizard/account_salestax_avatax_address_validate.xml 2011-09-22 18:37:20 +0000 | |||
2068 | @@ -0,0 +1,53 @@ | |||
2069 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
2070 | 2 | <openerp> | ||
2071 | 3 | <data> | ||
2072 | 4 | |||
2073 | 5 | <!-- Partner Address Validate --> | ||
2074 | 6 | |||
2075 | 7 | <record id="view_account_salestax_avatax_address_validate" model="ir.ui.view"> | ||
2076 | 8 | <field name="name">Address Validatation</field> | ||
2077 | 9 | <field name="model">account.salestax.avatax.address.validate</field> | ||
2078 | 10 | <field name="type">form</field> | ||
2079 | 11 | <field name="arch" type="xml"> | ||
2080 | 12 | <form string="Address Validation"> | ||
2081 | 13 | <group colspan="4" col="8"> | ||
2082 | 14 | <group colspan="1"> | ||
2083 | 15 | <separator string="Original Address" colspan="4"/> | ||
2084 | 16 | <field name="original_street" colspan="4" width="220"/> | ||
2085 | 17 | <field name="original_street2" colspan="4" width="220"/> | ||
2086 | 18 | <field name="original_city" colspan="4" width="220"/> | ||
2087 | 19 | <field name="original_state" colspan="4" width="220"/> | ||
2088 | 20 | <field name="original_zip" colspan="4" width="220"/> | ||
2089 | 21 | <field name="original_country" colspan="4" width="220"/> | ||
2090 | 22 | </group> | ||
2091 | 23 | <separator orientation="vertical" rowspan="10"/> | ||
2092 | 24 | <group colspan="6"> | ||
2093 | 25 | <separator string="Validated Address" colspan="4"/> | ||
2094 | 26 | <field name="street" colspan="4" width="220"/> | ||
2095 | 27 | <field name="street2" colspan="4"/> | ||
2096 | 28 | <field name="city" colspan="4"/> | ||
2097 | 29 | <field name="state" colspan="4"/> | ||
2098 | 30 | <field name="zip" colspan="4"/> | ||
2099 | 31 | <field name="country" colspan="4"/> | ||
2100 | 32 | </group> | ||
2101 | 33 | <group colspan="8" col="8"> | ||
2102 | 34 | <button special="cancel" string="Cancel" icon="gtk-cancel"/> | ||
2103 | 35 | <button name="accept_valid_address" type="object" icon="gtk-ok" string="_Accept"/> | ||
2104 | 36 | </group> | ||
2105 | 37 | </group> | ||
2106 | 38 | </form> | ||
2107 | 39 | </field> | ||
2108 | 40 | </record> | ||
2109 | 41 | |||
2110 | 42 | <record id="action_account_salestax_avatax_address_validate" model="ir.actions.act_window"> | ||
2111 | 43 | <field name="name">Address Validation</field> | ||
2112 | 44 | <field name="type">ir.actions.act_window</field> | ||
2113 | 45 | <field name="res_model">account.salestax.avatax.address.validate</field> | ||
2114 | 46 | <field name="view_type">form</field> | ||
2115 | 47 | <field name="view_mode">form</field> | ||
2116 | 48 | <field name="view_id" ref="view_account_salestax_avatax_address_validate"/> | ||
2117 | 49 | <field name="target">new</field> | ||
2118 | 50 | </record> | ||
2119 | 51 | |||
2120 | 52 | </data> | ||
2121 | 53 | </openerp> | ||
2122 | 0 | \ No newline at end of file | 54 | \ No newline at end of file |
2123 | 1 | 55 | ||
2124 | === added file 'account_salestax_avatax/wizard/account_salestax_avatax_ping.py' | |||
2125 | --- account_salestax_avatax/wizard/account_salestax_avatax_ping.py 1970-01-01 00:00:00 +0000 | |||
2126 | +++ account_salestax_avatax/wizard/account_salestax_avatax_ping.py 2011-09-22 18:37:20 +0000 | |||
2127 | @@ -0,0 +1,58 @@ | |||
2128 | 1 | # -*- coding: utf-8 -*- | ||
2129 | 2 | ############################################################################## | ||
2130 | 3 | # | ||
2131 | 4 | # OpenERP, Open Source Management Solution | ||
2132 | 5 | # Copyright (C) 2011 NovaPoint Group LLC (<http://www.novapointgroup.com>) | ||
2133 | 6 | # Copyright (C) 2004-2010 OpenERP SA (<http://www.openerp.com>) | ||
2134 | 7 | # | ||
2135 | 8 | # This program is free software: you can redistribute it and/or modify | ||
2136 | 9 | # it under the terms of the GNU Affero General Public License as | ||
2137 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
2138 | 11 | # License, or (at your option) any later version. | ||
2139 | 12 | # | ||
2140 | 13 | # This program is distributed in the hope that it will be useful, | ||
2141 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2142 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2143 | 16 | # GNU Affero General Public License for more details. | ||
2144 | 17 | # | ||
2145 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
2146 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2147 | 20 | # | ||
2148 | 21 | ############################################################################## | ||
2149 | 22 | |||
2150 | 23 | from osv import osv, fields | ||
2151 | 24 | from tools.translate import _ | ||
2152 | 25 | |||
2153 | 26 | from account_salestax_avatax.suds_client import AvaTaxService | ||
2154 | 27 | |||
2155 | 28 | class account_salestax_avatax_ping(osv.osv_memory): | ||
2156 | 29 | _name = 'account.salestax.avatax.ping' | ||
2157 | 30 | _description = 'Ping Service' | ||
2158 | 31 | _columns = { | ||
2159 | 32 | 'name': fields.char('Name', size=64) | ||
2160 | 33 | } | ||
2161 | 34 | |||
2162 | 35 | def default_get(self, cr, uid, fields_list, context=None): | ||
2163 | 36 | res = super(account_salestax_avatax_ping, self).default_get(cr, uid, fields_list, context) | ||
2164 | 37 | return self.ping(cr, uid, context=context) | ||
2165 | 38 | |||
2166 | 39 | def ping(self, cr, uid, context=None): | ||
2167 | 40 | """ Call the Avatax's Ping Service to test the connection. """ | ||
2168 | 41 | |||
2169 | 42 | if context is None: | ||
2170 | 43 | context = {} | ||
2171 | 44 | |||
2172 | 45 | if context.get('active_id', False): | ||
2173 | 46 | avatax_pool = self.pool.get('account.salestax.avatax') | ||
2174 | 47 | avatax_config = avatax_pool.browse(cr, uid, context['active_id'], context=context) | ||
2175 | 48 | avapoint = AvaTaxService(avatax_config.account_number, avatax_config.license_key, | ||
2176 | 49 | avatax_config.service_url, avatax_config.request_timeout, avatax_config.logging) | ||
2177 | 50 | taxSvc = avapoint.create_tax_service().taxSvc # Create 'tax' service for Ping and is_authorized calls | ||
2178 | 51 | avapoint.ping() | ||
2179 | 52 | result = avapoint.is_authorized() | ||
2180 | 53 | avatax_pool.write(cr, uid, avatax_config.id, {'date_expiration': result.Expires}) | ||
2181 | 54 | return {'type': 'ir.actions.act_window_close'} | ||
2182 | 55 | |||
2183 | 56 | account_salestax_avatax_ping() | ||
2184 | 57 | |||
2185 | 58 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: | ||
2186 | 0 | 59 | ||
2187 | === added file 'account_salestax_avatax/wizard/account_salestax_avatax_ping.xml' | |||
2188 | --- account_salestax_avatax/wizard/account_salestax_avatax_ping.xml 1970-01-01 00:00:00 +0000 | |||
2189 | +++ account_salestax_avatax/wizard/account_salestax_avatax_ping.xml 2011-09-22 18:37:20 +0000 | |||
2190 | @@ -0,0 +1,37 @@ | |||
2191 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
2192 | 2 | <openerp> | ||
2193 | 3 | <data> | ||
2194 | 4 | |||
2195 | 5 | <!-- Ping AvaTax Tax Service --> | ||
2196 | 6 | |||
2197 | 7 | <record id="view_account_salestax_avatax_ping" model="ir.ui.view"> | ||
2198 | 8 | <field name="name">Test Connection</field> | ||
2199 | 9 | <field name="model">account.salestax.avatax.ping</field> | ||
2200 | 10 | <field name="type">form</field> | ||
2201 | 11 | <field name="arch" type="xml"> | ||
2202 | 12 | <form string="Test Connection"> | ||
2203 | 13 | <group height="70" width="130"> | ||
2204 | 14 | <label align="0.5" string="Test Connection Successful!" colspan="4"/> | ||
2205 | 15 | <newline/> | ||
2206 | 16 | <group colspan="2" col="4"> | ||
2207 | 17 | <label string=""/> | ||
2208 | 18 | <button icon="gtk-ok" special="cancel" string="_OK"/> | ||
2209 | 19 | </group> | ||
2210 | 20 | </group> | ||
2211 | 21 | </form> | ||
2212 | 22 | </field> | ||
2213 | 23 | </record> | ||
2214 | 24 | |||
2215 | 25 | <record id="action_account_salestax_avatax_ping" model="ir.actions.act_window"> | ||
2216 | 26 | <field name="name">Test Connection</field> | ||
2217 | 27 | <field name="type">ir.actions.act_window</field> | ||
2218 | 28 | <field name="res_model">account.salestax.avatax.ping</field> | ||
2219 | 29 | <field name="view_type">form</field> | ||
2220 | 30 | <field name="view_mode">form</field> | ||
2221 | 31 | <field name="view_id" ref="view_account_salestax_avatax_ping"/> | ||
2222 | 32 | <field name="context">{'record_id': active_id}</field> | ||
2223 | 33 | <field name="target">new</field> | ||
2224 | 34 | </record> | ||
2225 | 35 | |||
2226 | 36 | </data> | ||
2227 | 37 | </openerp> | ||
2228 | 0 | \ No newline at end of file | 38 | \ No newline at end of file |