Merge lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing into lp:stock-logistic-warehouse

Proposed by Lorenzo Battistini
Status: Merged
Merged at revision: 32
Proposed branch: lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing
Merge into: lp:stock-logistic-warehouse
Diff against target: 1166 lines (+1103/-0)
12 files modified
stock_lot_valuation/AUTHORS.txt (+1/-0)
stock_lot_valuation/__init__.py (+23/-0)
stock_lot_valuation/__openerp__.py (+52/-0)
stock_lot_valuation/i18n/stock_lot_valuation.pot (+205/-0)
stock_lot_valuation/product.py (+32/-0)
stock_lot_valuation/product_view.xml (+15/-0)
stock_lot_valuation/stock.py (+358/-0)
stock_lot_valuation/stock_view.xml (+22/-0)
stock_lot_valuation/test/stock.yml (+230/-0)
stock_lot_valuation/wizard/__init__.py (+21/-0)
stock_lot_valuation/wizard/stock_change_standard_price.py (+110/-0)
stock_lot_valuation/wizard/stock_change_standard_price_view.xml (+34/-0)
To merge this branch: bzr merge lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing
Reviewer Review Type Date Requested Status
Joao Alfredo Gama Batista code review. no test Approve
Maxime Chambreuil (http://www.savoirfairelinux.com) code review Needs Fixing
Joël Grand-Guillaume @ camptocamp code review, no tests Approve
Guewen Baconnier @ Camptocamp code review, nitpicking, no test Needs Fixing
Review via email: mp+164766@code.launchpad.net

Description of the change

Stock valuation (standard or average price, ...) based on lots.
This module extends standard stock valuation (based on products). Valuing lots allows to have different costs for different lots of the same product.

Usage
-----
Set the 'Lot valuation' flag on product form (used for real time valuation).
As for products, lots have 'cost' and 'costing method' fields. Also, a 'Change Standard Price' wizard is available

To post a comment you must log in.
Revision history for this message
Eric Caudal - www.elico-corp.com (elicoidal) wrote :

I think this is a first step to start getting FIFO cost calculation on
product
Eric CAUDAL

Eric Caudal
/CEO/
--
*Elico Corporation, Shanghai branch
/OpenERP Premium Certified Training Partner/ *
Cell: + 86 186 2136 1670
Office: + 86 21 6211 8017/27/37
Skype: elico.corp
<email address hidden> <mailto:<email address hidden>>
http://www.elico-corp.com

Elico Corp
On 05/21/2013 12:35 AM, Lorenzo Battistini - Agile BG wrote:
> Lorenzo Battistini - Agile BG has proposed merging lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing into lp:stock-logistic-warehouse.
>
> Requested reviews:
> Stock and Logistic Core Editors (stock-logistic-core-editors)
>
> For more details, see:
> https://code.launchpad.net/~elbati/stock-logistic-warehouse/adding_stock_lot_costing/+merge/164766
>
> Stock valuation (standard or average price, ...) based on lots.
> This module extends standard stock valuation (based on products). Valuing lots allows to have different costs for different lots of the same product.
>
> Usage
> -----
> Set the 'Lot valuation' flag on product form (used for real time valuation).
> As for products, lots have 'cost' and 'costing method' fields. Also, a 'Change Standard Price' wizard is available

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Well done!

Seems good to me, some nitpickings:

Most are styling and pep8 issues, but the most annoying ones are the missing propagations of the context in some calls.

l.183
Could be worse to cut the long help:

            help="Standard Price: The cost price is manually updated "
                 "at the end of a specific period. \nAverage Price: "
                 "The cost price is recomputed at each incoming shipment."),

l.195-196 (applies for other places in the code too)
pep8: indentation, see http://www.python.org/dev/peps/pep-0008/#indentation

l.199-202
The expression is hard to read.
I think I would prefer a version with if / elif (or maybe an inline ... if ... else ... expression but it would probably be too complex here)

l.224 pep8: missing spaces around =

l.227,241 pep8: should not be on the same line and the line is too long

l.229 propagation of the context is missing, space after commas

l.250 prefer the form `... if ... else ...` than `... and ... or ...`

l.253-256 (and some other places) the \ is useless here as it is enclosed in ()

l.257-260, l.279-290, l.305-318: propagation of context is missing (+ pep8 indentation)

l.360 and lot of calls under: propagation of the context is missing

l. 554,560,561, ... : s/incomming/incoming/

pep8: in some places, a space is missing after comma or colon.

I will be pleased to merge your proposal once fixed.

Congrats for the tests ;-)

review: Needs Fixing (code review, nitpicking, no test)
Revision history for this message
Lorenzo Battistini (elbati) wrote :

On 06/18/2013 09:53 AM, Guewen Baconnier @ Camptocamp wrote:
> Review: Needs Fixing code review, nitpicking, no test

Thank you Guewen, I did the changes.

52. By Lorenzo Battistini

[fix] lines lenght, indentation, style

53. By Lorenzo Battistini

[fix] The expression is hard to read

54. By Lorenzo Battistini

[fix] propagation of the context is missing, space after commas

55. By Lorenzo Battistini

[fix] prefer the form `... if ... else ...` than `... and ... or ...`

56. By Lorenzo Battistini

[fix] propagation of context is missing

57. By Lorenzo Battistini

[fix] propagation of the context is missing

58. By Lorenzo Battistini

[fix] incomming/incoming

59. By Lorenzo Battistini

[fix] space after comma

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

LGTM

review: Approve (code review, no tests)
Revision history for this message
Joao Alfredo Gama Batista (joao-gama) wrote :

Hi Lorenzo,

Just a small comment:

l.746: You forgot to change the copyright owner.

The rest is ok. Thanks for your contribution!

review: Needs Fixing (code review, no tests)
Revision history for this message
Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903) wrote :

Thanks Lorenzo.

Can you please add the translation file and run flake8 on your code ?

review: Needs Fixing (code review)
60. By Lorenzo Battistini

[FIX] copyright

Revision history for this message
Lorenzo Battistini (elbati) wrote :

On 10/17/2013 04:09 PM, Joao Alfredo Gama Batista wrote:
> Review: Needs Fixing code review, no tests
>
> Hi Lorenzo,
>
> Just a small comment:
>
> l.746: You forgot to change the copyright owner.

Added our copyright.
I keep the Tiny one because I copied that file from stock module.

61. By Lorenzo Battistini

[IMP] PEP8

62. By Lorenzo Battistini

[FIX] osv.except_osv

63. By Lorenzo Battistini

[IMP] PEP8

64. By Lorenzo Battistini

[ADD] pot file

Revision history for this message
Lorenzo Battistini (elbati) wrote :

On 10/17/2013 04:56 PM, Maxime Chambreuil
(http://www.savoirfairelinux.com) wrote:
> Can you please add the translation file and run flake8 on your code ?

Added stock_lot_valuation.pot and improved PEP8.
Thanks

Revision history for this message
Joao Alfredo Gama Batista (joao-gama) wrote :

LGTM.

review: Approve (code review. no test)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'stock_lot_valuation'
2=== added file 'stock_lot_valuation/AUTHORS.txt'
3--- stock_lot_valuation/AUTHORS.txt 1970-01-01 00:00:00 +0000
4+++ stock_lot_valuation/AUTHORS.txt 2013-10-18 16:18:27 +0000
5@@ -0,0 +1,1 @@
6+Lorenzo Battistini <lorenzo.battistini@agilebg.com>
7
8=== added file 'stock_lot_valuation/__init__.py'
9--- stock_lot_valuation/__init__.py 1970-01-01 00:00:00 +0000
10+++ stock_lot_valuation/__init__.py 2013-10-18 16:18:27 +0000
11@@ -0,0 +1,23 @@
12+# -*- coding: utf-8 -*-
13+##############################################################################
14+#
15+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
16+#
17+# This program is free software: you can redistribute it and/or modify
18+# it under the terms of the GNU Affero General Public License as published
19+# by the Free Software Foundation, either version 3 of the License, or
20+# (at your option) any later version.
21+#
22+# This program is distributed in the hope that it will be useful,
23+# but WITHOUT ANY WARRANTY; without even the implied warranty of
24+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25+# GNU Affero General Public License for more details.
26+#
27+# You should have received a copy of the GNU Affero General Public License
28+# along with this program. If not, see <http://www.gnu.org/licenses/>.
29+#
30+##############################################################################
31+
32+import product
33+import stock
34+import wizard
35
36=== added file 'stock_lot_valuation/__openerp__.py'
37--- stock_lot_valuation/__openerp__.py 1970-01-01 00:00:00 +0000
38+++ stock_lot_valuation/__openerp__.py 2013-10-18 16:18:27 +0000
39@@ -0,0 +1,52 @@
40+# -*- coding: utf-8 -*-
41+##############################################################################
42+#
43+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
44+#
45+# This program is free software: you can redistribute it and/or modify
46+# it under the terms of the GNU Affero General Public License as published
47+# by the Free Software Foundation, either version 3 of the License, or
48+# (at your option) any later version.
49+#
50+# This program is distributed in the hope that it will be useful,
51+# but WITHOUT ANY WARRANTY; without even the implied warranty of
52+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53+# GNU Affero General Public License for more details.
54+#
55+# You should have received a copy of the GNU Affero General Public License
56+# along with this program. If not, see <http://www.gnu.org/licenses/>.
57+#
58+##############################################################################
59+
60+{
61+ 'name': "Lot Valuation",
62+ 'version': '0.1',
63+ 'category': 'Warehouse Management',
64+ 'description': """
65+Stock valuation (standard or average price, ...) based on lots.
66+This module extends standard stock valuation (based on products).
67+Valuing lots allows to have different costs for different lots of the same
68+product.
69+
70+Usage
71+-----
72+Set the 'Lot valuation' flag on product form (used for real time valuation).
73+As for products, lots have 'cost' and 'costing method' fields. Also, a
74+'Change Standard Price' wizard is available.
75+""",
76+ 'author': 'Agile Business Group',
77+ 'website': 'http://www.agilebg.com',
78+ 'license': 'AGPL-3',
79+ "depends": ['stock'],
80+ "data": [
81+ "wizard/stock_change_standard_price_view.xml",
82+ "product_view.xml",
83+ "stock_view.xml",
84+ ],
85+ "demo": [],
86+ 'test': [
87+ 'test/stock.yml', # TODO cover user interface operations
88+ ],
89+ "active": False,
90+ "installable": True
91+}
92
93=== added directory 'stock_lot_valuation/i18n'
94=== added file 'stock_lot_valuation/i18n/stock_lot_valuation.pot'
95--- stock_lot_valuation/i18n/stock_lot_valuation.pot 1970-01-01 00:00:00 +0000
96+++ stock_lot_valuation/i18n/stock_lot_valuation.pot 2013-10-18 16:18:27 +0000
97@@ -0,0 +1,205 @@
98+# Translation of OpenERP Server.
99+# This file contains the translation of the following modules:
100+# * stock_lot_valuation
101+#
102+msgid ""
103+msgstr ""
104+"Project-Id-Version: OpenERP Server 7.0\n"
105+"Report-Msgid-Bugs-To: \n"
106+"POT-Creation-Date: 2013-10-18 16:17+0000\n"
107+"PO-Revision-Date: 2013-10-18 16:17+0000\n"
108+"Last-Translator: <>\n"
109+"Language-Team: \n"
110+"MIME-Version: 1.0\n"
111+"Content-Type: text/plain; charset=UTF-8\n"
112+"Content-Transfer-Encoding: \n"
113+"Plural-Forms: \n"
114+
115+#. module: stock_lot_valuation
116+#: selection:stock.production.lot,cost_method:0
117+msgid "Average Price"
118+msgstr ""
119+
120+#. module: stock_lot_valuation
121+#: field:lot.change.standard.price,stock_journal:0
122+msgid "Stock journal"
123+msgstr ""
124+
125+#. module: stock_lot_valuation
126+#: field:lot.change.standard.price,enable_stock_in_out_acc:0
127+msgid "Enable Related Account"
128+msgstr ""
129+
130+#. module: stock_lot_valuation
131+#: model:ir.model,name:stock_lot_valuation.model_stock_picking
132+msgid "Picking List"
133+msgstr ""
134+
135+#. module: stock_lot_valuation
136+#: code:addons/stock_lot_valuation/stock.py:132
137+#, python-format
138+msgid "Please specify company in Location."
139+msgstr ""
140+
141+#. module: stock_lot_valuation
142+#: code:addons/stock_lot_valuation/stock.py:145
143+#, python-format
144+msgid "Please define journal on the product category: '%s' (id: %d)."
145+msgstr ""
146+
147+#. module: stock_lot_valuation
148+#: model:ir.model,name:stock_lot_valuation.model_product_product
149+msgid "Product"
150+msgstr ""
151+
152+#. module: stock_lot_valuation
153+#: help:stock.production.lot,standard_price:0
154+msgid "Cost price (in company currency) of the lot used for standard stock valuation in accounting."
155+msgstr ""
156+
157+#. module: stock_lot_valuation
158+#: field:lot.change.standard.price,new_price:0
159+msgid "Price"
160+msgstr ""
161+
162+#. module: stock_lot_valuation
163+#: model:ir.model,name:stock_lot_valuation.model_stock_production_lot
164+msgid "Serial Number"
165+msgstr ""
166+
167+#. module: stock_lot_valuation
168+#: code:addons/stock_lot_valuation/stock.py:123
169+#, python-format
170+msgid "No difference between standard price! and new price"
171+msgstr ""
172+
173+#. module: stock_lot_valuation
174+#: selection:stock.production.lot,cost_method:0
175+msgid "Standard Price"
176+msgstr ""
177+
178+#. module: stock_lot_valuation
179+#: model:ir.model,name:stock_lot_valuation.model_stock_partial_picking
180+msgid "Partial Picking Processing Wizard"
181+msgstr ""
182+
183+#. module: stock_lot_valuation
184+#: view:lot.change.standard.price:0
185+#: view:stock.production.lot:0
186+msgid "Cost Price"
187+msgstr ""
188+
189+#. module: stock_lot_valuation
190+#: help:lot.change.standard.price,new_price:0
191+msgid "If cost price is increased, stock variation account will be debited and stock output account will be credited with the value = (difference of amount * quantity available).\n"
192+"If cost price is decreased, stock variation account will be creadited and stock input account will be debited."
193+msgstr ""
194+
195+#. module: stock_lot_valuation
196+#: field:product.product,lot_valuation:0
197+msgid "Lot valuation"
198+msgstr ""
199+
200+#. module: stock_lot_valuation
201+#: help:stock.production.lot,cost_method:0
202+msgid "Standard Price: The cost price is manually updated at the end of a specific period. \n"
203+"Average Price: The cost price is recomputed at each incoming shipment."
204+msgstr ""
205+
206+#. module: stock_lot_valuation
207+#: view:stock.production.lot:0
208+msgid "update"
209+msgstr ""
210+
211+#. module: stock_lot_valuation
212+#: code:addons/stock_lot_valuation/stock.py:100
213+#: code:addons/stock_lot_valuation/stock.py:122
214+#: code:addons/stock_lot_valuation/stock.py:131
215+#: code:addons/stock_lot_valuation/stock.py:144
216+#: code:addons/stock_lot_valuation/stock.py:167
217+#: code:addons/stock_lot_valuation/stock.py:196
218+#, python-format
219+msgid "Error!"
220+msgstr ""
221+
222+#. module: stock_lot_valuation
223+#: field:stock.production.lot,cost_method:0
224+msgid "Costing Method"
225+msgstr ""
226+
227+#. module: stock_lot_valuation
228+#: code:addons/stock_lot_valuation/wizard/stock_change_standard_price.py:100
229+#, python-format
230+msgid "Active ID is not set in Context."
231+msgstr ""
232+
233+#. module: stock_lot_valuation
234+#: view:lot.change.standard.price:0
235+msgid "_Apply"
236+msgstr ""
237+
238+#. module: stock_lot_valuation
239+#: view:lot.change.standard.price:0
240+msgid "Change Price"
241+msgstr ""
242+
243+#. module: stock_lot_valuation
244+#: field:lot.change.standard.price,stock_account_output:0
245+msgid "Stock Output Account"
246+msgstr ""
247+
248+#. module: stock_lot_valuation
249+#: help:product.product,lot_valuation:0
250+msgid "Use lot valuation instead of product valuation"
251+msgstr ""
252+
253+#. module: stock_lot_valuation
254+#: code:addons/stock_lot_valuation/stock.py:197
255+#, python-format
256+msgid "Please define stock output account for this product: '%s' (id: %d)."
257+msgstr ""
258+
259+#. module: stock_lot_valuation
260+#: model:ir.model,name:stock_lot_valuation.model_stock_move
261+msgid "Stock Move"
262+msgstr ""
263+
264+#. module: stock_lot_valuation
265+#: view:lot.change.standard.price:0
266+msgid "or"
267+msgstr ""
268+
269+#. module: stock_lot_valuation
270+#: field:stock.production.lot,standard_price:0
271+msgid "Cost"
272+msgstr ""
273+
274+#. module: stock_lot_valuation
275+#: field:lot.change.standard.price,stock_account_input:0
276+msgid "Stock Input Account"
277+msgstr ""
278+
279+#. module: stock_lot_valuation
280+#: model:ir.actions.act_window,name:stock_lot_valuation.action_view_change_standard_price
281+#: model:ir.model,name:stock_lot_valuation.model_lot_change_standard_price
282+#: view:lot.change.standard.price:0
283+msgid "Change Standard Price"
284+msgstr ""
285+
286+#. module: stock_lot_valuation
287+#: code:addons/stock_lot_valuation/stock.py:168
288+#, python-format
289+msgid "Please define stock input account for this product: '%s' (id: %d)."
290+msgstr ""
291+
292+#. module: stock_lot_valuation
293+#: view:lot.change.standard.price:0
294+msgid "Cancel"
295+msgstr ""
296+
297+#. module: stock_lot_valuation
298+#: code:addons/stock_lot_valuation/stock.py:101
299+#, python-format
300+msgid "Specify valuation Account for Product Category: %s."
301+msgstr ""
302+
303
304=== added file 'stock_lot_valuation/product.py'
305--- stock_lot_valuation/product.py 1970-01-01 00:00:00 +0000
306+++ stock_lot_valuation/product.py 2013-10-18 16:18:27 +0000
307@@ -0,0 +1,32 @@
308+# -*- coding: utf-8 -*-
309+##############################################################################
310+#
311+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
312+#
313+# This program is free software: you can redistribute it and/or modify
314+# it under the terms of the GNU Affero General Public License as published
315+# by the Free Software Foundation, either version 3 of the License, or
316+# (at your option) any later version.
317+#
318+# This program is distributed in the hope that it will be useful,
319+# but WITHOUT ANY WARRANTY; without even the implied warranty of
320+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
321+# GNU Affero General Public License for more details.
322+#
323+# You should have received a copy of the GNU Affero General Public License
324+# along with this program. If not, see <http://www.gnu.org/licenses/>.
325+#
326+##############################################################################
327+
328+from openerp.osv import fields, orm
329+from openerp.tools.translate import _
330+
331+
332+class product_product(orm.Model):
333+ _inherit = "product.product"
334+
335+ _columns = {
336+ 'lot_valuation': fields.boolean(
337+ 'Lot valuation',
338+ help="Use lot valuation instead of product valuation"),
339+ }
340
341=== added file 'stock_lot_valuation/product_view.xml'
342--- stock_lot_valuation/product_view.xml 1970-01-01 00:00:00 +0000
343+++ stock_lot_valuation/product_view.xml 2013-10-18 16:18:27 +0000
344@@ -0,0 +1,15 @@
345+<?xml version="1.0" encoding="utf-8"?>
346+<openerp>
347+<data>
348+ <record id="product_normal_form_view" model="ir.ui.view">
349+ <field name="name">product_normal_form_view</field>
350+ <field name="model">product.product</field>
351+ <field name="inherit_id" ref="stock.view_normal_property_acc_form"></field>
352+ <field name="arch" type="xml">
353+ <field name="valuation" position="after">
354+ <field name="lot_valuation"></field>
355+ </field>
356+ </field>
357+ </record>
358+</data>
359+</openerp>
360
361=== added file 'stock_lot_valuation/stock.py'
362--- stock_lot_valuation/stock.py 1970-01-01 00:00:00 +0000
363+++ stock_lot_valuation/stock.py 2013-10-18 16:18:27 +0000
364@@ -0,0 +1,358 @@
365+# -*- coding: utf-8 -*-
366+##############################################################################
367+#
368+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
369+#
370+# This program is free software: you can redistribute it and/or modify
371+# it under the terms of the GNU Affero General Public License as published
372+# by the Free Software Foundation, either version 3 of the License, or
373+# (at your option) any later version.
374+#
375+# This program is distributed in the hope that it will be useful,
376+# but WITHOUT ANY WARRANTY; without even the implied warranty of
377+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
378+# GNU Affero General Public License for more details.
379+#
380+# You should have received a copy of the GNU Affero General Public License
381+# along with this program. If not, see <http://www.gnu.org/licenses/>.
382+#
383+##############################################################################
384+
385+from openerp.osv import fields, orm
386+from openerp.tools.translate import _
387+import openerp.addons.decimal_precision as dp
388+
389+
390+class stock_production_lot(orm.Model):
391+ _inherit = "stock.production.lot"
392+
393+ _columns = {
394+ 'standard_price': fields.float(
395+ 'Cost', digits_compute=dp.get_precision('Lot Price'),
396+ help="Cost price (in company currency) of the lot used for "
397+ "standard stock valuation in accounting.",
398+ groups="base.group_user"
399+ ),
400+ 'cost_method': fields.selection(
401+ [
402+ ('standard', 'Standard Price'),
403+ ('average', 'Average Price')
404+ ], 'Costing Method',
405+ help="Standard Price: The cost price is manually updated at the "
406+ "end of a specific period. \nAverage Price: The cost price is "
407+ "recomputed at each incoming shipment."
408+ ),
409+ }
410+
411+ def price_get(self, cr, uid, ids, context=None):
412+ if context is None:
413+ context = {}
414+ res = {}
415+ product_uom_obj = self.pool.get('product.uom')
416+ for lot in self.browse(cr, uid, ids, context=context):
417+ res[lot.id] = lot['standard_price'] or 0.0
418+ if 'uom' in context:
419+ uom = lot.product_id.uom_id or lot.product_id.uos_id
420+ res[lot.id] = product_uom_obj._compute_price(
421+ cr, uid,
422+ uom.id, res[lot.id], context['uom'])
423+ # Convert from price_type currency to asked one
424+ if 'currency_id' in context:
425+ currency_id = False
426+ if lot.company_id and lot.company_id.currency_id:
427+ currency_id = lot.company_id.currency_id.id
428+ elif (
429+ lot.product_id.company_id
430+ and lot.product_id.company_id.currency_id
431+ ):
432+ currency_id = lot.product_id.company_id.currency_id.id
433+ if currency_id:
434+ res[lot.id] = self.pool.get('res.currency').compute(
435+ cr, uid,
436+ currency_id,
437+ context['currency_id'], res[lot.id], context=context)
438+ return res
439+
440+ def do_change_standard_price(self, cr, uid, ids, datas, context=None):
441+ """ Changes the Standard Price of Lot and creates an account move
442+ accordingly.
443+ @param datas : dict. contain default datas like new_price,
444+ stock_output_account, stock_input_account, stock_journal
445+ @param context: A standard dictionary
446+ """
447+ location_obj = self.pool.get('stock.location')
448+ move_obj = self.pool.get('account.move')
449+ move_line_obj = self.pool.get('account.move.line')
450+ if context is None:
451+ context = {}
452+
453+ new_price = datas.get('new_price', 0.0)
454+ stock_output_acc = datas.get('stock_output_account', False)
455+ stock_input_acc = datas.get('stock_input_account', False)
456+ journal_id = datas.get('stock_journal', False)
457+ lot_obj = self.browse(cr, uid, ids, context=context)[0]
458+ account_valuation = (
459+ lot_obj.product_id.categ_id.property_stock_valuation_account_id)
460+ account_valuation_id = (
461+ account_valuation and account_valuation.id or False)
462+ if not account_valuation_id:
463+ raise orm.except_orm(
464+ _('Error!'),
465+ _('Specify valuation Account for Product Category: %s.')
466+ % (lot_obj.product_id.categ_id.name))
467+ move_ids = []
468+ loc_ids = location_obj.search(
469+ cr, uid, [('usage', '=', 'internal')],
470+ context=context)
471+ for rec_id in ids:
472+ for location in location_obj.browse(
473+ cr, uid, loc_ids, context=context
474+ ):
475+ c = context.copy()
476+ c.update({
477+ 'location_id': location.id,
478+ 'compute_child': False
479+ })
480+
481+ lot = self.browse(cr, uid, rec_id, context=c)
482+ qty = lot.stock_available
483+ diff = lot.standard_price - new_price
484+ if not diff:
485+ raise orm.except_orm(
486+ _('Error!'),
487+ _("No difference between standard price!"
488+ " and new price"))
489+ if qty:
490+ company_id = (
491+ location.company_id and location.company_id.id or False
492+ )
493+ if not company_id:
494+ raise orm.except_orm(
495+ _('Error!'),
496+ _('Please specify company in Location.'))
497+ #
498+ # Accounting Entries
499+ #
500+ product = lot.product_id
501+ if (
502+ not journal_id
503+ and product.categ_id.property_stock_journal
504+ ):
505+ journal_id = product.categ_id.property_stock_journal.id
506+ if not journal_id:
507+ raise orm.except_orm(
508+ _('Error!'),
509+ _("Please define journal "
510+ "on the product category: '%s' (id: %d).") %
511+ (product.categ_id.name, product.categ_id.id,))
512+ move_id = move_obj.create(cr, uid, {
513+ 'journal_id': journal_id,
514+ 'company_id': company_id
515+ }, context=context)
516+
517+ move_ids.append(move_id)
518+
519+ if diff > 0:
520+ if not stock_input_acc:
521+ stock_input_acc = (
522+ product.property_stock_account_input.id
523+ )
524+ if not stock_input_acc:
525+ stock_input_acc = (
526+ product.categ_id.
527+ property_stock_account_input_categ.id
528+ )
529+ if not stock_input_acc:
530+ raise orm.except_orm(
531+ _('Error!'),
532+ _("Please define stock input account "
533+ "for this product: '%s' (id: %d).") %
534+ (product.name, product.id,))
535+ amount_diff = qty * diff
536+ move_line_obj.create(cr, uid, {
537+ 'name': product.name,
538+ 'account_id': stock_input_acc,
539+ 'debit': amount_diff,
540+ 'move_id': move_id,
541+ }, context=context)
542+ move_line_obj.create(cr, uid, {
543+ 'name': product.categ_id.name,
544+ 'account_id': account_valuation_id,
545+ 'credit': amount_diff,
546+ 'move_id': move_id
547+ }, context=context)
548+ elif diff < 0:
549+ if not stock_output_acc:
550+ stock_output_acc = (
551+ product.property_stock_account_output.id
552+ )
553+ if not stock_output_acc:
554+ stock_output_acc = (
555+ product.categ_id.
556+ property_stock_account_output_categ.id
557+ )
558+ if not stock_output_acc:
559+ raise orm.except_orm(
560+ _('Error!'),
561+ _("Please define stock output account "
562+ "for this product: '%s' (id: %d).") %
563+ (product.name, product.id,))
564+ amount_diff = qty * -diff
565+ move_line_obj.create(cr, uid, {
566+ 'name': product.name,
567+ 'account_id': stock_output_acc,
568+ 'credit': amount_diff,
569+ 'move_id': move_id
570+ }, context=context)
571+ move_line_obj.create(cr, uid, {
572+ 'name': product.categ_id.name,
573+ 'account_id': account_valuation_id,
574+ 'debit': amount_diff,
575+ 'move_id': move_id
576+ }, context=context)
577+
578+ self.write(cr, uid, rec_id, {'standard_price': new_price})
579+
580+ return move_ids
581+
582+
583+class stock_move(orm.Model):
584+ _inherit = "stock.move"
585+
586+ def _get_reference_accounting_values_for_valuation(
587+ self, cr, uid, move, context=None
588+ ):
589+ res = super(
590+ stock_move, self)._get_reference_accounting_values_for_valuation(
591+ cr, uid, move, context=context)
592+ if move.product_id.lot_valuation and move.prodlot_id:
593+ product_uom_obj = self.pool.get('product.uom')
594+ qty = product_uom_obj._compute_qty(
595+ cr, uid, move.product_uom.id,
596+ move.product_qty, move.product_id.uom_id.id)
597+ if context is None:
598+ context = {}
599+ currency_ctx = dict(
600+ context, currency_id=move.company_id.currency_id.id)
601+ amount_unit = move.prodlot_id.price_get(
602+ context=currency_ctx)[move.prodlot_id.id]
603+ reference_amount = amount_unit * qty
604+ new_res = (reference_amount, move.company_id.currency_id.id)
605+ res = new_res
606+ return res
607+
608+ def do_partial(self, cr, uid, ids, partial_datas, context=None):
609+ if context is None:
610+ context = {}
611+ pick_obj = self.pool.get('stock.picking')
612+ for move in self.browse(cr, uid, ids, context=context):
613+ pick_obj.write_lot(cr, uid, move, partial_datas, context=context)
614+ res = super(stock_move, self).do_partial(
615+ cr, uid, ids, partial_datas, context=context)
616+ return res
617+
618+
619+class stock_picking(orm.Model):
620+ _inherit = "stock.picking"
621+
622+ def compute_price(self, cr, uid, partial_datas, move, context=None):
623+ if context is None:
624+ context = {}
625+ lot_obj = self.pool.get('stock.production.lot')
626+ uom_obj = self.pool.get('product.uom')
627+ move_obj = self.pool.get('stock.move')
628+ currency_obj = self.pool.get('res.currency')
629+ partial_data = partial_datas.get('move%s' % (move.id), {})
630+ product_uom = partial_data.get('product_uom', False)
631+ product_qty = partial_data.get('product_qty', 0.0)
632+ product_currency = partial_data.get('product_currency', False)
633+ product_price = partial_data.get('product_price', 0.0)
634+
635+ lot = lot_obj.browse(cr, uid, move.prodlot_id.id, context=context)
636+ product = lot.product_id
637+ move_currency_id = move.company_id.currency_id.id
638+ context['currency_id'] = move_currency_id
639+ qty = uom_obj._compute_qty(
640+ cr, uid, product_uom, product_qty, product.uom_id.id)
641+ if qty > 0:
642+ new_price = currency_obj.compute(
643+ cr, uid, product_currency,
644+ move_currency_id, product_price)
645+ new_price = uom_obj._compute_price(
646+ cr, uid, product_uom, new_price,
647+ product.uom_id.id)
648+ if lot.stock_available <= 0:
649+ new_std_price = new_price
650+ else:
651+ # Get the standard price
652+ amount_unit = lot.price_get(context=context)[lot.id]
653+ new_std_price = (
654+ ((amount_unit * lot.stock_available)
655+ + (new_price * qty)) / (lot.stock_available + qty)
656+ )
657+
658+ lot_obj.write(
659+ cr, uid, [lot.id], {'standard_price': new_std_price},
660+ context=context
661+ )
662+
663+ # Record the values that were chosen in the wizard, so they can be
664+ # used for inventory valuation if real-time valuation is enabled.
665+ move_obj.write(cr, uid, [move.id], {
666+ 'price_unit': product_price,
667+ 'price_currency_id': product_currency
668+ }, context=context)
669+
670+ def write_lot(self, cr, uid, move, partial_datas, context=None):
671+ lot_obj = self.pool.get('stock.production.lot')
672+ currency_obj = self.pool.get('res.currency')
673+ uom_obj = self.pool.get('product.uom')
674+ if partial_datas.get('move%s' % (move.id)):
675+ partial_data = partial_datas.get('move%s' % (move.id), {})
676+ product_price = partial_data.get('product_price', 0.0)
677+ product_currency = partial_data.get('product_currency', False)
678+ product_uom = partial_data.get('product_uom', False)
679+ if partial_data.get('prodlot_id'):
680+ lot = lot_obj.browse(
681+ cr, uid, partial_data['prodlot_id'], context)
682+ product = lot.product_id
683+ if (
684+ move.product_id.lot_valuation and (
685+ move.picking_id.type == 'in'
686+ ) and (lot.cost_method == 'average')
687+ ):
688+ self.compute_price(
689+ cr, uid, partial_datas, move, context=context)
690+ if (
691+ move.product_id.lot_valuation and product_price
692+ and not lot.standard_price
693+ ):
694+ new_price = currency_obj.compute(
695+ cr, uid, product_currency,
696+ move.company_id.currency_id.id, product_price)
697+ new_price = uom_obj._compute_price(
698+ cr, uid, product_uom, new_price,
699+ product.uom_id.id)
700+ lot.write({'standard_price': new_price})
701+
702+ def do_partial(self, cr, uid, ids, partial_datas, context=None):
703+ if context is None:
704+ context = {}
705+ for pick in self.browse(cr, uid, ids, context=context):
706+ for move in pick.move_lines:
707+ self.write_lot(cr, uid, move, partial_datas, context=context)
708+ res = super(stock_picking, self).do_partial(
709+ cr, uid, ids, partial_datas, context=context)
710+ return res
711+
712+
713+class stock_partial_picking(orm.TransientModel):
714+ _inherit = "stock.partial.picking"
715+
716+ def _product_cost_for_average_update(self, cr, uid, move):
717+ res = super(
718+ stock_partial_picking, self
719+ )._product_cost_for_average_update(cr, uid, move)
720+ if move.prodlot_id and move.product_id.lot_valuation:
721+ res['cost'] = move.prodlot_id.standard_price
722+ return res
723
724=== added file 'stock_lot_valuation/stock_view.xml'
725--- stock_lot_valuation/stock_view.xml 1970-01-01 00:00:00 +0000
726+++ stock_lot_valuation/stock_view.xml 2013-10-18 16:18:27 +0000
727@@ -0,0 +1,22 @@
728+<?xml version="1.0" encoding="utf-8"?>
729+<openerp>
730+<data>
731+ <record id="view_production_lot_form" model="ir.ui.view">
732+ <field name="name">view_production_lot_form</field>
733+ <field name="model">stock.production.lot</field>
734+ <field name="inherit_id" ref="stock.view_production_lot_form"></field>
735+ <field name="arch" type="xml">
736+ <field name="stock_available" position="after" version="7.0">
737+ <field name="cost_method"></field>
738+ <label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/>
739+ <div groups="base.group_user">
740+ <field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}" nolabel="1"/>
741+ <button name="%(action_view_change_standard_price)d" string="update"
742+ type="action" attrs="{'invisible':[('cost_method','&lt;&gt;','average')]}"
743+ class="oe_link" groups="product.group_costing_method"/>
744+ </div>
745+ </field>
746+ </field>
747+ </record>
748+</data>
749+</openerp>
750
751=== added directory 'stock_lot_valuation/test'
752=== added file 'stock_lot_valuation/test/stock.yml'
753--- stock_lot_valuation/test/stock.yml 1970-01-01 00:00:00 +0000
754+++ stock_lot_valuation/test/stock.yml 2013-10-18 16:18:27 +0000
755@@ -0,0 +1,230 @@
756+-
757+ !record {model: stock.location, id: location_refrigerator}:
758+ name: Refrigerator
759+ usage: internal
760+-
761+ !record {model: stock.location, id: location_delivery_counter}:
762+ name: Delivery Counter
763+ usage: internal
764+-
765+ !record {model: stock.location, id: location_refrigerator_small}:
766+ name: Small Refrigerator
767+ usage: internal
768+ location_id: location_refrigerator
769+-
770+ !record {model: stock.location, id: location_opening}:
771+ name: opening
772+ usage: inventory
773+-
774+ !record {model: stock.location, id: location_convenience_shop}:
775+ name: Convenient Store
776+ usage: supplier
777+-
778+ !record {model: stock.warehouse, id: warehouse_icecream}:
779+ name: Ice Cream Shop
780+ lot_input_id: location_refrigerator
781+ lot_stock_id: location_refrigerator
782+ lot_output_id: location_delivery_counter
783+-
784+ !record {model: product.product, id: product_icecream}:
785+ default_code: 001
786+ name: Ice Cream
787+ type: product
788+ categ_id: product.product_category_1
789+ list_price: 100.0
790+ standard_price: 70.0
791+ uom_id: product.product_uom_kgm
792+ uom_po_id: product.product_uom_kgm
793+ procure_method: make_to_stock
794+ property_stock_inventory: location_opening
795+ valuation: real_time
796+ cost_method: average
797+ property_stock_account_input: account.o_expense
798+ property_stock_account_output: account.o_income
799+ description: Ice cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
800+ lot_valuation: True
801+
802+-
803+ !record {model: stock.production.lot, id: lot_icecream_0}:
804+ name: Lot0 for Ice cream
805+ product_id: product_icecream
806+ cost_method: average
807+ standard_price: 70.0
808+-
809+ !record {model: stock.production.lot, id: lot_icecream_1}:
810+ name: Lot1 for Ice cream
811+ product_id: product_icecream
812+-
813+ !record {model: stock.inventory, id: stock_inventory_icecream}:
814+ name: Inventory for icecream
815+-
816+ !record {model: stock.inventory.line, id: stock_inventory_line_icecream_lot0}:
817+ product_id: product_icecream
818+ product_uom: product.product_uom_kgm
819+ inventory_id: stock_inventory_icecream
820+ product_qty: 50.0
821+ prod_lot_id: lot_icecream_0
822+ location_id: location_refrigerator
823+-
824+ !record {model: stock.inventory.line, id: stock_inventory_line_icecream_lot1}:
825+ product_id: product_icecream
826+ product_uom: product.product_uom_kgm
827+ inventory_id: stock_inventory_icecream
828+ product_qty: 40.0
829+ prod_lot_id: lot_icecream_1
830+ location_id: location_refrigerator
831+
832+-
833+ !record {model: stock.picking, id: outgoing_shipment}:
834+ type: out
835+ location_dest_id: location_delivery_counter
836+-
837+ !record {model: stock.move, id: outgoing_shipment_icecream}:
838+ picking_id: outgoing_shipment
839+ product_id: product_icecream
840+ product_uom: product.product_uom_kgm
841+ product_qty: 130.0
842+ location_id: location_refrigerator
843+ location_dest_id: location_delivery_counter
844+ prodlot_id: lot_icecream_0
845+-
846+ !record {model: stock.picking, id: incoming_shipment}:
847+ type: in
848+ invoice_state: 2binvoiced
849+ partner_id: base.res_partner_address_9
850+ location_dest_id: location_refrigerator
851+-
852+ !record {model: stock.move, id: incoming_shipment_icecream}:
853+ picking_id: incoming_shipment
854+ product_id: product_icecream
855+ product_uom: product.product_uom_kgm
856+ product_qty: 50.0
857+ location_id: location_convenience_shop
858+ location_dest_id: location_refrigerator
859+ prodlot_id: lot_icecream_0
860+
861+-
862+ I update the price of the Ice-cream.
863+-
864+ !python {model: stock.change.standard.price}: |
865+ context.update({'active_model':'product.product', 'active_id': ref('product_icecream'), 'active_ids':[ref('product_icecream')]})
866+-
867+ !record {model: stock.change.standard.price, id: change_price}:
868+ new_price: 120
869+-
870+ !python {model: stock.change.standard.price}: |
871+ self.change_price(cr, uid, [ref('change_price')], context=context)
872+-
873+ I check price of Ice-cream after update price.
874+-
875+ !python {model: product.product}: |
876+ product = self.browse(cr, uid, ref('product_icecream'), context=context)
877+ assert product.standard_price == 120, "Price is not updated."
878+
879+-
880+ I confirm physical inventory of Ice-cream which are came in different lots.
881+-
882+ !python {model: stock.inventory}: |
883+ self.action_confirm(cr, uid, [ref('stock_inventory_icecream')], context=context)
884+-
885+ I check move details after confirmed physical inventory.
886+-
887+ !python {model: stock.inventory}: |
888+ inventory = self.browse(cr, uid, ref('stock_inventory_icecream'), context=context)
889+ assert len(inventory.move_ids) == len(inventory.inventory_line_id), "moves are not correspond."
890+ for move_line in inventory.move_ids:
891+ for line in inventory.inventory_line_id:
892+ if move_line.product_id.id == line.product_id.id and move_line.prodlot_id.id == line.prod_lot_id.id:
893+ location_id = line.product_id.property_stock_inventory.id
894+ assert move_line.product_qty == line.product_qty, "Qty is not correspond."
895+ assert move_line.product_uom.id == line.product_uom.id, "UOM is not correspond."
896+ assert move_line.date == inventory.date, "Date is not correspond."
897+ assert move_line.location_id.id == location_id, "Source location is not correspond."
898+ assert move_line.location_dest_id.id == line.location_id.id, "Destination location is not correspond."
899+ assert move_line.state == 'confirmed', "Move is not confirmed."
900+
901+-
902+ Now I check vitual stock of Ice-cream after confirmed physical inventory.
903+-
904+ !python {model: product.product}: |
905+ product = self.browse(cr, uid, ref('product_icecream'), context=context)
906+ assert product.virtual_available == 90, "Vitual stock is not updated."
907+
908+-
909+ I complete physical inventory of Ice-cream.
910+-
911+ !python {model: stock.inventory}: |
912+ self.action_done(cr, uid, [ref('stock_inventory_icecream')], context=context)
913+ balance = self.pool.get('account.account').browse(cr, uid, ref('account.stk')).balance
914+ assert balance == 3500.0, "Purchased Stocks balance is %s, not 3500 (50*70)" % balance
915+-
916+ I update the price of the Ice-cream lot.
917+-
918+ !python {model: lot.change.standard.price}: |
919+ context.update({'active_model':'stock.production.lot', 'active_id': ref('lot_icecream_0'), 'active_ids':[ref('lot_icecream_0')]})
920+-
921+ !record {model: lot.change.standard.price, id: change_price}:
922+ new_price: 120
923+-
924+ !python {model: lot.change.standard.price}: |
925+ self.change_price(cr, uid, [ref('change_price')], context=context)
926+-
927+ I check price of Ice-cream lot after update price.
928+-
929+ !python {model: stock.production.lot}: |
930+ lot = self.browse(cr, uid, ref('lot_icecream_0'), context=context)
931+ assert lot.standard_price == 120, "Price is not updated."
932+ balance = self.pool.get('account.account').browse(cr, uid, ref('account.stk')).balance
933+ assert balance == 6000.0, "Purchased Stocks balance is %s, not 6000 (old balance + (lot.standard_price - new_price) * lot.stock_available)" % balance
934+
935+-
936+ I confirm outgoing shipment of 130 kgm Ice-cream.
937+-
938+ !workflow {model: stock.picking, action: button_confirm, ref: outgoing_shipment}
939+-
940+ I check shipment details after confirmed.
941+-
942+ !python {model: stock.picking}: |
943+ shipment = self.browse(cr, uid, ref("outgoing_shipment"))
944+ assert shipment.state == "confirmed", "Shipment should be confirmed."
945+ for move_line in shipment.move_lines:
946+ assert move_line.state == "confirmed", "Move should be confirmed."
947+
948+-
949+ I confirm incoming shipment of 50 kgm Ice-cream.
950+-
951+ !workflow {model: stock.picking, action: button_confirm, ref: incoming_shipment}
952+-
953+ I receive 40kgm Ice-cream so I make backorder of incoming shipment for 40 kgm.
954+-
955+ !python {model: stock.partial.picking}: |
956+ context.update({'active_model': 'stock.picking', 'active_id': ref('incoming_shipment'), 'active_ids': [ref('incoming_shipment')]})
957+-
958+ !record {model: stock.partial.picking, id: partial_incoming}:
959+ move_ids:
960+ - quantity: 40
961+ product_id: product_icecream
962+ product_uom: product.product_uom_kgm
963+ move_id: incoming_shipment_icecream
964+ location_id: location_convenience_shop
965+ location_dest_id: location_refrigerator
966+ prodlot_id: lot_icecream_0
967+ cost: 100
968+-
969+ !python {model: stock.partial.picking }: |
970+ self.do_partial(cr, uid, [ref('partial_incoming')], context=context)
971+-
972+ I check backorder shipment after received partial shipment.
973+-
974+ !python {model: stock.picking}: |
975+ lot = self.pool.get('stock.production.lot').browse(cr, uid, ref('lot_icecream_0'), context=context)
976+ assert lot.standard_price == 111.11, "Price is not updated to 111.11 (((120*50)+(100*40))/(50+40))"
977+ shipment = self.browse(cr, uid, ref("incoming_shipment"))
978+ backorder = shipment.backorder_id
979+ assert backorder, "Backorder should be created after partial shipment."
980+ assert backorder.state == 'done', "Backorder should be close after received."
981+ for move_line in backorder.move_lines:
982+ assert move_line.product_qty == 40, "Qty in backorder does not correspond."
983+ assert move_line.state == 'done', "Move line of backorder should be closed."
984+ balance = self.pool.get('account.account').browse(cr, uid, ref('account.stk')).balance
985+ assert balance == 10444.4, "Purchased Stocks balance is %s, not 10444.4 (old balance + 111.11×40)" % balance
986
987=== added directory 'stock_lot_valuation/wizard'
988=== added file 'stock_lot_valuation/wizard/__init__.py'
989--- stock_lot_valuation/wizard/__init__.py 1970-01-01 00:00:00 +0000
990+++ stock_lot_valuation/wizard/__init__.py 2013-10-18 16:18:27 +0000
991@@ -0,0 +1,21 @@
992+# -*- coding: utf-8 -*-
993+##############################################################################
994+#
995+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
996+#
997+# This program is free software: you can redistribute it and/or modify
998+# it under the terms of the GNU Affero General Public License as published
999+# by the Free Software Foundation, either version 3 of the License, or
1000+# (at your option) any later version.
1001+#
1002+# This program is distributed in the hope that it will be useful,
1003+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1004+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1005+# GNU Affero General Public License for more details.
1006+#
1007+# You should have received a copy of the GNU Affero General Public License
1008+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1009+#
1010+##############################################################################
1011+
1012+import stock_change_standard_price
1013
1014=== added file 'stock_lot_valuation/wizard/stock_change_standard_price.py'
1015--- stock_lot_valuation/wizard/stock_change_standard_price.py 1970-01-01 00:00:00 +0000
1016+++ stock_lot_valuation/wizard/stock_change_standard_price.py 2013-10-18 16:18:27 +0000
1017@@ -0,0 +1,110 @@
1018+# -*- coding: utf-8 -*-
1019+##############################################################################
1020+#
1021+# OpenERP, Open Source Management Solution
1022+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
1023+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
1024+#
1025+# This program is free software: you can redistribute it and/or modify
1026+# it under the terms of the GNU Affero General Public License as
1027+# published by the Free Software Foundation, either version 3 of the
1028+# License, or (at your option) any later version.
1029+#
1030+# This program is distributed in the hope that it will be useful,
1031+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1032+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1033+# GNU Affero General Public License for more details.
1034+#
1035+# You should have received a copy of the GNU Affero General Public License
1036+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1037+#
1038+##############################################################################
1039+
1040+from openerp.osv import fields, orm
1041+from openerp.tools.translate import _
1042+import openerp.addons.decimal_precision as dp
1043+
1044+
1045+class change_standard_price(orm.TransientModel):
1046+ _name = "lot.change.standard.price"
1047+ _description = "Change Standard Price"
1048+ _columns = {
1049+ 'new_price': fields.float(
1050+ 'Price', required=True,
1051+ digits_compute=dp.get_precision('Account'),
1052+ help="If cost price is increased, stock variation account will be "
1053+ "debited and stock output account will be credited with the "
1054+ "value = (difference of amount * quantity available).\n"
1055+ "If cost price is decreased, stock variation account will be "
1056+ "creadited and stock input account will be debited."
1057+ ),
1058+ 'stock_account_input': fields.many2one(
1059+ 'account.account', 'Stock Input Account'),
1060+ 'stock_account_output': fields.many2one(
1061+ 'account.account', 'Stock Output Account'),
1062+ 'stock_journal': fields.many2one(
1063+ 'account.journal', 'Stock journal', required=True),
1064+ 'enable_stock_in_out_acc': fields.boolean('Enable Related Account',),
1065+ }
1066+
1067+ def default_get(self, cr, uid, fields, context=None):
1068+ """ To get default values for the object.
1069+ @param self: The object pointer.
1070+ @param cr: A database cursor
1071+ @param uid: ID of the user currently logged in
1072+ @param fields: List of fields for which we want default values
1073+ @param context: A standard dictionary
1074+ @return: A dictionary which of fields with values.
1075+ """
1076+ if context is None:
1077+ context = {}
1078+ lot_pool = self.pool.get('stock.production.lot')
1079+ product_pool = self.pool.get('product.product')
1080+ lot_obj = lot_pool.browse(cr, uid, context.get('active_id', False))
1081+ res = super(change_standard_price, self).default_get(
1082+ cr, uid, fields, context=context)
1083+
1084+ accounts = product_pool.get_product_accounts(
1085+ cr, uid, lot_obj.product_id.id, context={})
1086+
1087+ price = lot_obj.standard_price
1088+
1089+ if 'new_price' in fields:
1090+ res.update({'new_price': price})
1091+ if 'stock_account_input' in fields:
1092+ res.update(
1093+ {'stock_account_input': accounts['stock_account_input']})
1094+ if 'stock_account_output' in fields:
1095+ res.update(
1096+ {'stock_account_output': accounts['stock_account_output']})
1097+ if 'stock_journal' in fields:
1098+ res.update({'stock_journal': accounts['stock_journal']})
1099+ if 'enable_stock_in_out_acc' in fields:
1100+ res.update({'enable_stock_in_out_acc': True})
1101+
1102+ return res
1103+
1104+ def change_price(self, cr, uid, ids, context=None):
1105+ """ Changes the Standard Price of Product.
1106+ And creates an account move accordingly.
1107+ @param self: The object pointer.
1108+ @param cr: A database cursor
1109+ @param uid: ID of the user currently logged in
1110+ @param ids: List of IDs selected
1111+ @param context: A standard dictionary
1112+ @return:
1113+ """
1114+ if context is None:
1115+ context = {}
1116+ rec_id = context and context.get('active_id', False)
1117+ assert rec_id, _('Active ID is not set in Context.')
1118+ lot_pool = self.pool.get('stock.production.lot')
1119+ res = self.browse(cr, uid, ids, context=context)
1120+ datas = {
1121+ 'new_price': res[0].new_price,
1122+ 'stock_output_account': res[0].stock_account_output.id,
1123+ 'stock_input_account': res[0].stock_account_input.id,
1124+ 'stock_journal': res[0].stock_journal.id
1125+ }
1126+ lot_pool.do_change_standard_price(cr, uid, [rec_id], datas, context)
1127+ return {'type': 'ir.actions.act_window_close'}
1128
1129=== added file 'stock_lot_valuation/wizard/stock_change_standard_price_view.xml'
1130--- stock_lot_valuation/wizard/stock_change_standard_price_view.xml 1970-01-01 00:00:00 +0000
1131+++ stock_lot_valuation/wizard/stock_change_standard_price_view.xml 2013-10-18 16:18:27 +0000
1132@@ -0,0 +1,34 @@
1133+<?xml version="1.0" encoding="utf-8"?>
1134+<openerp>
1135+ <data>
1136+ <record id="view_change_standard_price" model="ir.ui.view">
1137+ <field name="name">Change Standard Price</field>
1138+ <field name="model">lot.change.standard.price</field>
1139+ <field name="arch" type="xml">
1140+ <form string="Change Standard Price" version="7.0">
1141+ <separator string="Change Price"/>
1142+ <group>
1143+ <field name="new_price" string="Cost Price"/>
1144+ </group>
1145+ <footer>
1146+ <button name="change_price" string="_Apply" type="object" class="oe_highlight"/>
1147+ or
1148+ <button string="Cancel" class="oe_link" special="cancel" />
1149+ </footer>
1150+ </form>
1151+ </field>
1152+ </record>
1153+
1154+ <record id="action_view_change_standard_price" model="ir.actions.act_window">
1155+ <field name="name">Change Standard Price</field>
1156+ <field name="type">ir.actions.act_window</field>
1157+ <field name="res_model">lot.change.standard.price</field>
1158+ <field name="view_type">form</field>
1159+ <field name="view_mode">form</field>
1160+ <field name="view_id" ref="view_change_standard_price"/>
1161+ <field name="target">new</field>
1162+ </record>
1163+
1164+ </data>
1165+</openerp>
1166+

Subscribers

People subscribed via source and target branches