Merge lp:~borjals/openobject-addons/extra-6.0-bugfix-603100 into lp:openobject-addons/extra-trunk

Proposed by Borja López Soilán (NeoPolus)
Status: Merged
Merged at revision: 4743
Proposed branch: lp:~borjals/openobject-addons/extra-6.0-bugfix-603100
Merge into: lp:openobject-addons/extra-trunk
Diff against target: 652 lines (+424/-114)
7 files modified
purchase_tax_include/__init__.py (+7/-1)
purchase_tax_include/__openerp__.py (+36/-0)
purchase_tax_include/__terp__.py (+0/-35)
purchase_tax_include/i18n/es.po (+89/-0)
purchase_tax_include/i18n/purchase_tax_include.pot (+40/-3)
purchase_tax_include/purchase_tax_incl.py (+212/-47)
purchase_tax_include/purchase_tax_incl.xml (+40/-28)
To merge this branch: bzr merge lp:~borjals/openobject-addons/extra-6.0-bugfix-603100
Reviewer Review Type Date Requested Status
Joël Grand-Guillaume @ camptocamp Approve
OpenERP Core Team Pending
Review via email: mp+29566@code.launchpad.net

Description of the change

Complete rewrite of the purchase_tax_include module to address multiple bugs.

- Solved "column 'amount_tax' of relation 'purchase_order' does not exist"
  error when saving a purchase order (bug 603100).
  The previous fields redefinitions where incompatible with the purchase
  module (comming from the 5.0 addons) since Dec 2008.

- Now correct 'amount_untaxed' and 'amount_total' are shown on the
  purchase order totals.

- Now 'price_subtotal_incl' column is properly displayed on the
  purchase order lines list of the purchase form.

- When the order is invoiced, the created invoice did not use the
  price type of the order, so the amounts didn't match; fixed.

- When a picking is invoiced, the created invoice did not use the
  price type of the original orders; fixed.

To post a comment you must log in.
Revision history for this message
Numérigraphe (numerigraphe) wrote :

I'm not a core team reviewer, but please allow me to share a few comments on the form:
 - you should add a proper default context in the methods
 - maybe you shouldn't bump the version number to 2.0 unless you provide a migration script (then the server can apply it automatically).
 - would you care to write a few YAML test to prove it works?
I hope you don't mind my intervention.
Lionel

Revision history for this message
Borja López Soilán (NeoPolus) (borjals) wrote :

Thanks for the comments Numérigraphe :) There is always room for improvement.

- I didn't want to change the methods signature, cause it might break things.
  For example the original _amount_line method of purchase.order.line is defined like this:
    class purchase_order_line(osv.osv):
       def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
  As you can see, the context (unknown_dict - WTF? who gave it this name?) is not optional.

- Migration script? The changes affect only to function fields (and new documents) so there is no data to migrate :) Also, I seriously doubt anybody was using this module on 5.0 (it has been broken since 2008!).

- I hadn't thought about YAML tests, seems like a good idea (it is a pity that 5.0 does not support YAML tests), but it will take some time.

Revision history for this message
Borja López Soilán (NeoPolus) (borjals) wrote :

Ok, this has been waiting for a month and only Numérigraphe (thanks!) reviewed the merge proposal. As in its current state purchase_tax_include is not usable at all, I think I'll just merge it tomorrow.

Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote :

Hi Borja,

Just installed and check some little cases with your improvements/fixes. It seems good to me.

Thanks you for your work.

Regards,

Joël

review: Approve
Revision history for this message
Borja López Soilán (NeoPolus) (borjals) wrote :

Thanks Joël :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'purchase_tax_include/__init__.py' (properties changed: +x to -x)
2--- purchase_tax_include/__init__.py 2010-07-06 05:10:58 +0000
3+++ purchase_tax_include/__init__.py 2010-07-09 15:01:00 +0000
4@@ -1,3 +1,4 @@
5+# -*- encoding: utf-8 -*-
6 ##############################################################################
7 #
8 # OpenERP, Open Source Management Solution
9@@ -18,6 +19,11 @@
10 #
11 ##############################################################################
12
13+__authors__ = [
14+ "OpenERP S.A.",
15+ "Borja López Soilán (Pexego) <borjals@pexego.es>"
16+]
17+
18 import purchase_tax_incl
19-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
20+
21
22
23=== added file 'purchase_tax_include/__openerp__.py'
24--- purchase_tax_include/__openerp__.py 1970-01-01 00:00:00 +0000
25+++ purchase_tax_include/__openerp__.py 2010-07-09 15:01:00 +0000
26@@ -0,0 +1,36 @@
27+# -*- encoding: utf-8 -*-
28+##############################################################################
29+#
30+# OpenERP, Open Source Management Solution
31+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
32+#
33+# This program is free software: you can redistribute it and/or modify
34+# it under the terms of the GNU Affero General Public License as
35+# published by the Free Software Foundation, either version 3 of the
36+# License, or (at your option) any later version.
37+#
38+# This program is distributed in the hope that it will be useful,
39+# but WITHOUT ANY WARRANTY; without even the implied warranty of
40+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41+# GNU Affero General Public License for more details.
42+#
43+# You should have received a copy of the GNU Affero General Public License
44+# along with this program. If not, see <http://www.gnu.org/licenses/>.
45+#
46+##############################################################################
47+{
48+ "name" : "Purchases with taxes included",
49+ "version" : "2.0",
50+ "depends" : ["purchase","account_tax_include"],
51+ "author" : "Tiny",
52+ "website" : "http://www.openerp.com",
53+ "category" : "Generic Modules/Sales & Purchases",
54+ "description": "This module allows you to use purchase order with prices including or excluding taxes.",
55+ "init_xml" : [ ],
56+ "demo_xml" : [ ],
57+ "update_xml" : [ 'purchase_tax_incl.xml' ],
58+ "active": False,
59+ "installable": True
60+}
61+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
62+
63
64=== removed file 'purchase_tax_include/__terp__.py'
65--- purchase_tax_include/__terp__.py 2010-07-06 05:10:58 +0000
66+++ purchase_tax_include/__terp__.py 1970-01-01 00:00:00 +0000
67@@ -1,35 +0,0 @@
68-##############################################################################
69-#
70-# OpenERP, Open Source Management Solution
71-# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
72-#
73-# This program is free software: you can redistribute it and/or modify
74-# it under the terms of the GNU Affero General Public License as
75-# published by the Free Software Foundation, either version 3 of the
76-# License, or (at your option) any later version.
77-#
78-# This program is distributed in the hope that it will be useful,
79-# but WITHOUT ANY WARRANTY; without even the implied warranty of
80-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
81-# GNU Affero General Public License for more details.
82-#
83-# You should have received a copy of the GNU Affero General Public License
84-# along with this program. If not, see <http://www.gnu.org/licenses/>.
85-#
86-##############################################################################
87-{
88- "name" : "Purchases with taxes included",
89- "version" : "1.0",
90- "depends" : ["purchase","account_tax_include"],
91- "author" : "Tiny",
92- "website" : "http://www.openerp.com",
93- "category" : "Generic Modules/Sales & Purchases",
94- "description": "This module allows you to use purchase order with prices including or excluding taxes.",
95- "init_xml" : [ ],
96- "demo_xml" : [ ],
97- "update_xml" : [ 'purchase_tax_incl.xml' ],
98- "active": False,
99- "installable": True
100-}
101-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
102-
103
104=== added file 'purchase_tax_include/i18n/es.po'
105--- purchase_tax_include/i18n/es.po 1970-01-01 00:00:00 +0000
106+++ purchase_tax_include/i18n/es.po 2010-07-09 15:01:00 +0000
107@@ -0,0 +1,89 @@
108+# Translation of OpenERP Server.
109+# This file contains the translation of the following modules:
110+# * purchase_tax_include
111+#
112+msgid ""
113+msgstr ""
114+"Project-Id-Version: OpenERP Server 6.0dev\n"
115+"Report-Msgid-Bugs-To: support@openerp.com\n"
116+"POT-Creation-Date: 2010-07-09 11:24:53+0000\n"
117+"PO-Revision-Date: 2010-07-09 13:32+0100\n"
118+"Last-Translator: Borja López Soilán (Pexego) <borjals@pexego.es>\n"
119+"Language-Team: \n"
120+"MIME-Version: 1.0\n"
121+"Content-Type: text/plain; charset=UTF-8\n"
122+"Content-Transfer-Encoding: 8bit\n"
123+"Plural-Forms: \n"
124+
125+#. module: purchase_tax_include
126+#: constraint:ir.ui.view:0
127+msgid "Invalid XML for View Architecture!"
128+msgstr "¡XML inválido para la definición de la vista!"
129+
130+#. module: purchase_tax_include
131+#: constraint:ir.model:0
132+msgid "The Object name must start with x_ and not contain any special character !"
133+msgstr "¡El nombre del objeto debe empezar con x_ y no contener ningún carácter especial!"
134+
135+#. module: purchase_tax_include
136+#: code:addons/purchase_tax_include/purchase_tax_incl.py:0
137+#, python-format
138+msgid "You can't mix tax included and tax excluded purchases in one invoice!"
139+msgstr "¡No puede mezclar compras con impuestos incluidos y excluidos en una factura!"
140+
141+#. module: purchase_tax_include
142+#: field:purchase.order,price_type:0
143+msgid "Price method"
144+msgstr "Método de precio"
145+
146+#. module: purchase_tax_include
147+#: model:ir.module.module,shortdesc:purchase_tax_include.module_meta_information
148+msgid "Purchases with taxes included"
149+msgstr "Compras con impuestos incluidos"
150+
151+#. module: purchase_tax_include
152+#: selection:purchase.order,price_type:0
153+msgid "Tax included"
154+msgstr "Impuestos incluidos"
155+
156+#. module: purchase_tax_include
157+#: model:ir.model,name:purchase_tax_include.model_purchase_order
158+msgid "Purchase order"
159+msgstr "Pedido de compra"
160+
161+#. module: purchase_tax_include
162+#: code:addons/purchase_tax_include/purchase_tax_incl.py:0
163+#, python-format
164+msgid "Error!"
165+msgstr "¡Error!"
166+
167+#. module: purchase_tax_include
168+#: selection:purchase.order,price_type:0
169+msgid "Tax excluded"
170+msgstr "Impuestos excluidos"
171+
172+#. module: purchase_tax_include
173+#: model:ir.model,name:purchase_tax_include.model_purchase_order_line
174+msgid "Purchase Order lines"
175+msgstr "Líneas del pedido de compra"
176+
177+#. module: purchase_tax_include
178+#: model:ir.module.module,description:purchase_tax_include.module_meta_information
179+msgid "This module allows you to use purchase order with prices including or excluding taxes."
180+msgstr "Este módulo le permite usar órdenes de compra con precios con impuestos incluídos o excluidos."
181+
182+#. module: purchase_tax_include
183+#: model:ir.model,name:purchase_tax_include.model_stock_picking
184+msgid "Picking List"
185+msgstr "Albarán"
186+
187+#. module: purchase_tax_include
188+#: field:purchase.order.line,price_subtotal:0
189+msgid "Subtotal w/o tax"
190+msgstr "Subtotal sin imp."
191+
192+#. module: purchase_tax_include
193+#: field:purchase.order.line,price_subtotal_incl:0
194+msgid "Subtotal"
195+msgstr "Subtotal"
196+
197
198=== modified file 'purchase_tax_include/i18n/fr_BE.po' (properties changed: +x to -x)
199=== modified file 'purchase_tax_include/i18n/purchase_tax_include.pot' (properties changed: +x to -x)
200--- purchase_tax_include/i18n/purchase_tax_include.pot 2010-07-06 05:10:58 +0000
201+++ purchase_tax_include/i18n/purchase_tax_include.pot 2010-07-09 15:01:00 +0000
202@@ -4,10 +4,10 @@
203 #
204 msgid ""
205 msgstr ""
206-"Project-Id-Version: OpenERP Server 5.0.6\n"
207+"Project-Id-Version: OpenERP Server 6.0dev\n"
208 "Report-Msgid-Bugs-To: support@openerp.com\n"
209-"POT-Creation-Date: 2009-11-25 14:07:16+0000\n"
210-"PO-Revision-Date: 2009-11-25 14:07:16+0000\n"
211+"POT-Creation-Date: 2010-07-09 11:22:17+0000\n"
212+"PO-Revision-Date: 2010-07-09 11:22:17+0000\n"
213 "Last-Translator: <>\n"
214 "Language-Team: \n"
215 "MIME-Version: 1.0\n"
216@@ -21,6 +21,17 @@
217 msgstr ""
218
219 #. module: purchase_tax_include
220+#: constraint:ir.model:0
221+msgid "The Object name must start with x_ and not contain any special character !"
222+msgstr ""
223+
224+#. module: purchase_tax_include
225+#: code:addons/purchase_tax_include/purchase_tax_incl.py:0
226+#, python-format
227+msgid "You can't mix tax included and tax excluded purchases in one invoice!"
228+msgstr ""
229+
230+#. module: purchase_tax_include
231 #: field:purchase.order,price_type:0
232 msgid "Price method"
233 msgstr ""
234@@ -36,16 +47,42 @@
235 msgstr ""
236
237 #. module: purchase_tax_include
238+#: model:ir.model,name:purchase_tax_include.model_purchase_order
239+msgid "Purchase order"
240+msgstr ""
241+
242+#. module: purchase_tax_include
243+#: code:addons/purchase_tax_include/purchase_tax_incl.py:0
244+#, python-format
245+msgid "Error!"
246+msgstr ""
247+
248+#. module: purchase_tax_include
249 #: selection:purchase.order,price_type:0
250 msgid "Tax excluded"
251 msgstr ""
252
253 #. module: purchase_tax_include
254+#: model:ir.model,name:purchase_tax_include.model_purchase_order_line
255+msgid "Purchase Order lines"
256+msgstr ""
257+
258+#. module: purchase_tax_include
259 #: model:ir.module.module,description:purchase_tax_include.module_meta_information
260 msgid "This module allows you to use purchase order with prices including or excluding taxes."
261 msgstr ""
262
263 #. module: purchase_tax_include
264+#: model:ir.model,name:purchase_tax_include.model_stock_picking
265+msgid "Picking List"
266+msgstr ""
267+
268+#. module: purchase_tax_include
269+#: field:purchase.order.line,price_subtotal:0
270+msgid "Subtotal w/o tax"
271+msgstr ""
272+
273+#. module: purchase_tax_include
274 #: field:purchase.order.line,price_subtotal_incl:0
275 msgid "Subtotal"
276 msgstr ""
277
278=== modified file 'purchase_tax_include/purchase_tax_incl.py' (properties changed: +x to -x)
279--- purchase_tax_include/purchase_tax_incl.py 2010-07-06 05:10:58 +0000
280+++ purchase_tax_include/purchase_tax_incl.py 2010-07-09 15:01:00 +0000
281@@ -1,3 +1,4 @@
282+# -*- encoding: utf-8 -*-
283 ##############################################################################
284 #
285 # OpenERP, Open Source Management Solution
286@@ -18,80 +19,244 @@
287 #
288 ##############################################################################
289
290-import time
291-import netsvc
292+__authors__ = [
293+ "OpenERP S.A.",
294+ "Borja López Soilán (Pexego) <borjals@pexego.es>"
295+]
296+
297 from osv import fields, osv
298-import ir
299+import decimal_precision as dp
300
301 class purchase_order(osv.osv):
302+ """
303+ Extends the purchase order to allow using "tax included" prices.
304+ """
305 _inherit = "purchase.order"
306- def _amount_tax(self, cr, uid, ids, field_name, arg, context):
307+
308+ def _amount_all(self, cr, uid, ids, field_name, arg, context):
309+ """
310+ Overwrites/extends the amounts calculation to allow tax included prices.
311+ """
312 res = {}
313 cur_obj=self.pool.get('res.currency')
314 for order in self.browse(cr, uid, ids):
315- val = 0.0
316- cur=order.pricelist_id.currency_id
317- for line in order.order_line:
318- if order.price_type=='tax_included':
319- ttt = self.pool.get('account.tax').compute_inv(cr, uid, line.taxes_id, line.price_unit, line.product_qty, order.partner_address_id.id, line.product_id, order.partner_id)
320- else:
321- ttt = self.pool.get('account.tax').compute(cr, uid, line.taxes_id, line.price_unit, line.product_qty, order.partner_address_id.id, line.product_id, order.partner_id)
322- for c in ttt:
323- val += cur_obj.round(cr, uid, cur, c['amount'])
324- res[order.id]=cur_obj.round(cr, uid, cur, val)
325+ if order.price_type == 'tax_included':
326+ #
327+ # Use the tax included calculation
328+ #
329+ res[order.id] = {
330+ 'amount_untaxed': 0.0,
331+ 'amount_tax': 0.0,
332+ 'amount_total': 0.0,
333+ }
334+ val = val1 = 0.0
335+ cur=order.pricelist_id.currency_id
336+ for line in order.order_line:
337+ for c in self.pool.get('account.tax').compute_inv(cr, uid, line.taxes_id, line.price_unit, line.product_qty, order.partner_address_id.id, line.product_id, order.partner_id):
338+ val+= c['amount']
339+ val1 += line.price_subtotal
340+ res[order.id]['amount_tax']=cur_obj.round(cr, uid, cur, val)
341+ res[order.id]['amount_untaxed']=cur_obj.round(cr, uid, cur, val1)
342+ res[order.id]['amount_total']=res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
343+ else:
344+ #
345+ # Use the default calculation
346+ #
347+ res = super(purchase_order, self)._amount_all(cr, uid, ids, field_name, arg, context)
348 return res
349+
350+
351+ def _get_order(self, cr, uid, ids, context={}):
352+ """
353+ Returns the orders that must be updated when some order lines change.
354+ """
355+ result = {}
356+ for line in self.pool.get('purchase.order.line').browse(cr, uid, ids, context=context):
357+ result[line.order_id.id] = True
358+ return result.keys()
359+
360 _columns = {
361 'price_type': fields.selection([
362 ('tax_included','Tax included'),
363 ('tax_excluded','Tax excluded')
364 ], 'Price method', required=True),
365- 'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'),
366+ 'amount_untaxed': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Purchase Price'), string='Untaxed Amount',
367+ store={
368+ 'purchase.order': (lambda self, cr, uid, ids, c={}: ids, ['price_type'], 20),
369+ 'purchase.order.line': (_get_order, None, 10),
370+ }, multi="sums"),
371+ 'amount_tax': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Purchase Price'), string='Taxes',
372+ store={
373+ 'purchase.order': (lambda self, cr, uid, ids, c={}: ids, ['price_type'], 20),
374+ 'purchase.order.line': (_get_order, None, 10),
375+ }, multi="sums"),
376+ 'amount_total': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Purchase Price'), string='Total',
377+ store={
378+ 'purchase.order': (lambda self, cr, uid, ids, c={}: ids, ['price_type'], 20),
379+ 'purchase.order.line': (_get_order, None, 10),
380+ }, multi="sums"),
381 }
382+
383 _defaults = {
384 'price_type': lambda *a: 'tax_excluded',
385 }
386+
387 def _inv_get(self, cr, uid, order, context={}):
388+ """
389+ Returns the columns as a dictionary.
390+ """
391 return {
392 'price_type': order.price_type
393 }
394+
395+
396+ def action_invoice_create(self, cr, uid, ids, *args):
397+ """
398+ Extend the invoice creation action to set the price type if needed.
399+ """
400+ #
401+ # Count how many orders have tax included prices.
402+ #
403+ tax_included_count = 0
404+ orders = self.browse(cr, uid, ids)
405+ for order in orders:
406+ if order.price_type == 'tax_included':
407+ tax_included_count += 1
408+
409+ if tax_included_count:
410+ if len(orders) == tax_included_count:
411+ #
412+ # Every order has tax include prices, we must create the invoice
413+ # and (afterwards) set the price type and recalculate.
414+ #
415+ invoice_id = super(purchase_order, self).action_invoice_create(cr, uid, ids, args)
416+ self.pool.get('account.invoice').write(cr, uid, [invoice_id], { 'price_type': 'tax_included' })
417+ self.pool.get('account.invoice').button_compute(cr, uid, [invoice_id], {'type': 'in_invoice'}, set_total=True)
418+ else:
419+ # We have no current way of creating an invoice mixing
420+ # tax included and tax excluded prices, so we just fail:
421+ raise osv.except_osv(_('Error!'), _("You can't mix tax included and tax excluded purchases in one invoice!"))
422+ else:
423+ # All the invoices are 'tax excluded', that's the default value
424+ # so we just let the default method create the invoice.
425+ invoice_id = super(purchase_order, self).action_invoice_create(cr, uid, ids, args)
426+
427+ return invoice_id
428+
429+
430 purchase_order()
431
432 class purchase_order_line(osv.osv):
433+ """
434+ Extends the purchase order lines to alter the calculation when
435+ tax includes prices are used.
436+ """
437 _inherit = 'purchase.order.line'
438+
439 def _amount_line(self, cr, uid, ids, name, arg, context):
440- res = {}
441- cur_obj=self.pool.get('res.currency')
442- tax_obj = self.pool.get('account.tax')
443- res = super(purchase_order_line, self)._amount_line(cr, uid, ids, name, arg, context)
444- res2 = res.copy()
445- for line in self.browse(cr, uid, ids):
446- if line.order_id.price_type == 'tax_included':
447- if line.product_id:
448- for tax in tax_obj.compute_inv(cr, uid, line.product_id.supplier_taxes_id, res[line.id]/line.product_qty, line.product_qty):
449- res[line.id] = res[line.id] - tax['amount']
450- else:
451- for tax in tax_obj.compute_inv(cr, uid, line.taxes_id, res[line.id]/line.product_qty, line.product_qty):
452- res[line.id] = res[line.id] - tax['amount']
453- if name == 'price_subtotal_incl' and line.order_id.price_type == 'tax_included':
454- if line.product_id:
455- prod_taxe_ids = [ t.id for t in line.product_id.supplier_taxes_id ]
456- prod_taxe_ids.sort()
457- line_taxe_ids = [ t.id for t in line.taxes_id ]
458- line_taxe_ids.sort()
459- if line.product_id and prod_taxe_ids == line_taxe_ids:
460- res[line.id] = res2[line.id]
461- elif not line.product_id:
462- res[line.id] = res2[line.id]
463- else:
464- for tax in tax_obj.compute(cr, uid, line.taxes_id, res[line.id]/line.product_qty, line.product_qty):
465- res[line.id] = res[line.id] + tax['amount']
466- cur = line.order_id.pricelist_id.currency_id
467- res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
468- return res
469+ """
470+ Calculate the subtotal for the line without taxes.
471+ """
472+ # Use the original method to calculate the amounts:
473+ res = super(purchase_order_line, self)._amount_line(cr, uid, ids, 'price_subtotal', arg, context)
474+ # Check if we are using 'tax_included' prices:
475+ if ids and self.browse(cr, uid, ids[0]).order_id.price_type == 'tax_included':
476+ #
477+ # Tax included => Remove the taxes from the line amounts.
478+ #
479+ cur_facade=self.pool.get('res.currency')
480+ tax_facade = self.pool.get('account.tax')
481+ for line in self.browse(cr, uid, ids):
482+ for tax in tax_facade.compute_inv(cr, uid, line.taxes_id, res[line.id]/line.product_qty, line.product_qty):
483+ res[line.id] = res[line.id] - tax['amount']
484+ cur = line.order_id.pricelist_id.currency_id
485+ res[line.id] = cur_facade.round(cr, uid, cur, res[line.id])
486+ return res
487+
488+ def _amount_line_incl(self, cr, uid, ids, name, arg, context):
489+ """
490+ Calculate the subtotal for the line with taxes.
491+ """
492+ # Use the original method to calculate the amounts:
493+ res = super(purchase_order_line, self)._amount_line(cr, uid, ids, 'price_subtotal', arg, context)
494+ # Check if we *aren't* using 'tax_included' prices on the subtotal:
495+ if ids and self.browse(cr, uid, ids[0]).order_id.price_type != 'tax_included':
496+ #
497+ # Tax excluded on the subtotal => Add taxes here from the line amounts.
498+ #
499+ cur_facade=self.pool.get('res.currency')
500+ tax_facade = self.pool.get('account.tax')
501+ for line in self.browse(cr, uid, ids):
502+ for tax in tax_facade.compute(cr, uid, line.taxes_id, res[line.id]/line.product_qty, line.product_qty):
503+ res[line.id] = res[line.id] + tax['amount']
504+ cur = line.order_id.pricelist_id.currency_id
505+ res[line.id] = cur_facade.round(cr, uid, cur, res[line.id])
506+ return res
507+
508+
509 _columns = {
510- 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o tax'),
511- 'price_subtotal_incl': fields.function(_amount_line, method=True, string='Subtotal'),
512+ 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o tax', digits_compute=dp.get_precision('Purchase Price')),
513+ 'price_subtotal_incl': fields.function(_amount_line_incl, method=True, string='Subtotal', digits_compute=dp.get_precision('Purchase Price')),
514 }
515 purchase_order_line()
516-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
517+
518+class stock_picking(osv.osv):
519+ """
520+ Extends the stock pickings to manage the creation of invoices with
521+ tax included prices when the original order had tax included prices.
522+ """
523+ _inherit = 'stock.picking'
524+ _description = "Picking list"
525+
526+ def action_invoice_create(self, cr, uid, ids, journal_id=False,
527+ group=False, type='out_invoice', context=None):
528+ """
529+ Extend the invoice creation action to set the price type if needed.
530+ """
531+ #
532+ # Count how many pickings have tax included prices.
533+ #
534+ tax_included_count = 0
535+ lines_count = 0
536+ pickings = self.browse(cr, uid, ids, context=context)
537+ for picking in pickings:
538+ #
539+ # We must find the orders associated with this picking and check
540+ # if they have tax included prices.
541+ # As picking lines may come from different orders (even if it is
542+ # not the usual), we must check it line by line.
543+ #
544+ for move in picking.move_lines:
545+ if move.purchase_line_id and move.purchase_line_id.order_id:
546+ lines_count += 1
547+ if move.purchase_line_id.order_id.price_type == 'tax_included':
548+ tax_included_count += 1
549+
550+ if tax_included_count:
551+ if lines_count == tax_included_count:
552+ #
553+ # Every order has tax include prices, we must create the invoice
554+ # and (afterwards) set the price type and recalculate.
555+ #
556+ invoices_map = super(stock_picking, self).action_invoice_create(cr,
557+ uid, ids, journal_id=journal_id,
558+ group=group, type=type,
559+ context=context)
560+ invoice_ids = list(set(invoices_map.values()))
561+ self.pool.get('account.invoice').write(cr, uid, invoice_ids, { 'price_type': 'tax_included' })
562+ self.pool.get('account.invoice').button_compute(cr, uid, invoice_ids, {'type': 'in_invoice'}, set_total=True)
563+ else:
564+ # We have no current way of creating an invoice mixing
565+ # tax included and tax excluded prices, so we just fail:
566+ raise osv.except_osv(_('Error!'), _("You can't mix tax included and tax excluded purchases in one invoice!"))
567+ else:
568+ # All the invoices are 'tax excluded', that's the default value
569+ # so we just let the default method create the invoice.
570+ invoices_map = super(stock_picking, self).action_invoice_create(cr,
571+ uid, ids, journal_id=journal_id,
572+ group=group, type=type,
573+ context=context)
574+ return invoices_map
575+
576+stock_picking()
577
578
579=== modified file 'purchase_tax_include/purchase_tax_incl.xml' (properties changed: +x to -x)
580--- purchase_tax_include/purchase_tax_incl.xml 2010-07-06 05:10:58 +0000
581+++ purchase_tax_include/purchase_tax_incl.xml 2010-07-09 15:01:00 +0000
582@@ -1,31 +1,43 @@
583-<?xml version="1.0"?>
584+<?xml version="1.0" encoding="utf-8"?>
585 <openerp>
586-<data>
587-
588- <record model="ir.ui.view" id="account_tax_view_price">
589- <field name="name">purchase.order.exlcuded.view.form</field>
590- <field name="type">form</field>
591- <field name="model">purchase.order</field>
592- <field name="inherit_id" ref="purchase.purchase_order_form" />
593- <field name="arch" type="xml">
594- <field name="origin" position="after">
595- <field name="price_type"/>
596- </field>
597- </field>
598- </record>
599-
600- <record model="ir.ui.view" id="account_tax_view_price_subtotal_incl">
601- <field name="name">purchase.order.line.tree</field>
602- <field name="type">tree</field>
603- <field name="model">purchase.order.line</field>
604- <field name="inherit_id" ref="purchase.purchase_order_line_tree" />
605- <field name="arch" type="xml">
606- <field name="price_subtotal" position="after">
607- <field name="price_subtotal_incl"/>
608- </field>
609- </field>
610- </record>
611-
612-</data>
613+ <data>
614+
615+ <record model="ir.ui.view" id="account_tax_view_price">
616+ <field name="name">purchase.order.form.add_price_type</field>
617+ <field name="type">form</field>
618+ <field name="model">purchase.order</field>
619+ <field name="inherit_id" ref="purchase.purchase_order_form" />
620+ <field name="arch" type="xml">
621+ <field name="shipped" position="after">
622+ <field name="price_type"/>
623+ </field>
624+ </field>
625+ </record>
626+
627+ <record model="ir.ui.view" id="account_tax_view_price_subtotal_incl">
628+ <field name="name">purchase.order.line.tree.add_price_subtotal_incl</field>
629+ <field name="type">tree</field>
630+ <field name="model">purchase.order.line</field>
631+ <field name="inherit_id" ref="purchase.purchase_order_line_tree" />
632+ <field name="arch" type="xml">
633+ <field name="price_subtotal" position="after">
634+ <field name="price_subtotal_incl"/>
635+ </field>
636+ </field>
637+ </record>
638+
639+ <record model="ir.ui.view" id="account_tax_view_price_subtotal_incl">
640+ <field name="name">purchase.order.form.add_price_subtotal_incl_to_lines</field>
641+ <field name="type">form</field>
642+ <field name="model">purchase.order</field>
643+ <field name="inherit_id" ref="purchase.purchase_order_form" />
644+ <field name="arch" type="xml">
645+ <xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
646+ <field name="price_subtotal_incl"/>
647+ </xpath>
648+ </field>
649+ </record>
650+
651+ </data>
652 </openerp>
653

Subscribers

People subscribed via source and target branches