Merge lp:~jgrandguillaume-c2c/openobject-addons/multi-company-cost-price into lp:openobject-addons

Proposed by Joël Grand-Guillaume @ camptocamp
Status: Merged
Merged at revision: 3001
Proposed branch: lp:~jgrandguillaume-c2c/openobject-addons/multi-company-cost-price
Merge into: lp:openobject-addons
Diff against target: 1134 lines (+381/-161)
23 files modified
account/account_analytic_line.py (+64/-5)
account/project/project.py (+3/-7)
account/project/project_view.xml (+18/-8)
account_analytic_analysis/account_analytic_analysis.py (+22/-37)
analytic/project.py (+85/-61)
analytic_user_function/analytic_user_function.py (+10/-4)
hr_expense/hr_expense.py (+8/-2)
hr_expense/hr_expense_view.xml (+2/-2)
hr_timesheet/hr_timesheet.py (+3/-1)
hr_timesheet_invoice/hr_timesheet_invoice.py (+7/-6)
hr_timesheet_invoice/hr_timesheet_invoice_view.xml (+13/-1)
multi_company/__init__.py (+0/-1)
multi_company/__terp__.py (+1/-1)
product/__init__.py (+1/-1)
product/__terp__.py (+1/-0)
product/company.py (+53/-0)
product/company_view.xml (+18/-0)
product/pricelist.py (+1/-0)
product/product_data.xml (+8/-1)
project_timesheet/project_timesheet.py (+12/-3)
stock/stock.py (+34/-9)
stock/wizard/wizard_partial_move.py (+8/-5)
stock/wizard/wizard_partial_picking.py (+9/-6)
To merge this branch: bzr merge lp:~jgrandguillaume-c2c/openobject-addons/multi-company-cost-price
Reviewer Review Type Date Requested Status
Xavier (Open ERP) (community) Needs Fixing
Joël Grand-Guillaume @ camptocamp (community) Needs Resubmitting
Fabien (Open ERP) Disapprove
Stephane Wirtel (OpenERP) Pending
Grzegorz Grzelak (OpenGLOBE.pl) Pending
Christophe CHAUVET Pending
Raphaël Valyi - http://www.akretion.com Pending
Review via email: mp+17887@code.launchpad.net

This proposal supersedes a proposal from 2010-01-11.

To post a comment you must log in.
Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote : Posted in a previous version of this proposal

Hi !

This is the merge proposal from Camptocamp to improve multi-company into OpenERP regarding to product costs in manufacturing industry and in services company.

What has been done here:

 * Add price type on company as a property (with default value based on standard price)

 * Stock accounting
  * Use the price type currency and field for cost valuation
  * Into stock move for standard price
  * Into stock move for average price

 * Analytic accounting
  * Use the price type currency and field for cost valuation (including timesheet)
  * Add multi-currency on analytic lines (similar to financial accounting)
  * Allow to share the same product between company employees, with different cost for each one
  * Correct all "costs" indicators into analytic account to base them on the right currency (owner's company)

 * By default, nothing change for single company implementation (base the cost valuation on standard price)

As a result, we can now really share the same product between companies that doesn't have the same currency and/or same cost price.
We can also manage one field per company on the product form to store the cost for a given price type (and so for a given company).

Refer to the blueprint for more details:

https://blueprints.launchpad.net/openobject-addons/+spec/multi-cost-price-and-product-currency

Thanks in advance for your review.

Regards,

Joël

Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote : Posted in a previous version of this proposal

Hello, one thing that I can't understand is why you add a field in product and views called "standard_price_usd" by default?
Is that a typo?
If not, I think we shouldn't favor some country price by default, especially if there is just one like that. I agree that often you would need such field, but then I think you should rather add it at integration time, else is seems to me a bit arbitrary.
If that's a typo, please make sure you don't have such typo in your merge proposal.

Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote : Posted in a previous version of this proposal

Hi !

I agree with you Raphaël. I understood that the multi_company module is only an example of multi-company config... So I add an example here to allow people understand how it should work.

Otherwise, the rest is clean. If you think I'm wrong with that, I'll remove it from multi_company module and suggest the merge again.

Personally, I think it's good to have an example, but it's ok for me to remove it.

Let me know,

Regards,

Joël

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

Hi !

This is the merge proposal from Camptocamp to improve multi-company into OpenERP regarding to product costs in manufacturing industry and in services company.

What has been done here:

 * Add price type on company as a property (with default value based on standard price)

 * Stock accounting
  * Use the price type currency and field for cost valuation
  * Into stock move for standard price
  * Into stock move for average price

 * Analytic accounting
  * Use the price type currency and field for cost valuation (including timesheet)
  * Add multi-currency on analytic lines (similar to financial accounting)
  * Allow to share the same product between company employees, with different cost for each one
  * Correct all "costs" indicators into analytic account to base them on the right currency (owner's company)

 * By default, nothing change for single company implementation (base the cost valuation on standard price)

 * Factorise part of function field into analytic accounting

As a result, we can now really share the same product between companies that doesn't have the same currency and/or same cost price.
We can also manage one field per company on the product form to store the cost for a given price type (and so for a given company).

Refer to the blueprint for more details:

https://blueprints.launchpad.net/openobject-addons/+spec/multi-cost-price-and-product-currency

Thanks in advance for your review.

Regards,

Joël

P.S. The field standard_price_usd is an example, committed into an sample module to show how to configure this use case... It's not a typo errors...

Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

It covers interesting needs but can not be merged as it. Lot's of things have to be rewritten to be more generic.

review: Disapprove
Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

For example, this is not acceptable in the official, generic, version:

888 +class product_product(osv.osv):
889 + _inherit = "product.product"
891 + _columns = {
892 + 'standard_price_usd': fields.float('Cost Price USD', required=True, digits=(16, int(config['price_accuracy'])),
893 + help="Product's cost in USDfor accounting stock valuation."),
894 + }
896 +product_product()

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

Hi,

First, thanks for reviewing :)

Please, Raphaal already told me this the first time, but this module is (in my own understanding) a demo on how to setup multi-company !

That's why I write it the "standard_price_usd" in the code, to illustrate how we can handle this problematic !! I put it in the multi_company module as Syleam and you did to put some demo data for multi company, I followed you !!!

Please, have a look closer, I'm ready to invest more time on that. It's a need my customer strongly want. We need a solution.

Could we discuss the features in their-self ? Not the demo stuff, I remove them in the minute if you want !

Let me know your advices, and I will correct what's need to be done. This way everyone win : My customer, me and OpenERP !

Thanks,

Kind regards,

Joël

Revision history for this message
Stephane Wirtel (OpenERP) (stephane-openerp) wrote :

Can you remove this point from your code and add a new comment with your example in this bug report.

Thanks

2551. By Joël Grand-Guillaume @ camptocamp

[DEL] Remove the demo data in multi-company module as asked by Fabien

2552. By Joël Grand-Guillaume @ camptocamp

[DEL] Remove the multi-company view from multi-company

2553. By Joël Grand-Guillaume @ camptocamp

[MRG] Merge the last trunk in order to ease the merge from Tiny

2554. By Joël Grand-Guillaume @ camptocamp

[REF] Refactoring : moving the project.py from account to analytic module as it was done by Tiny

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

Hi,

Well it's done ! I just remove every example fields from demo data in multicompany module. I also merge the last trunk of the day for you, so it's easier for you to apply it.

If you don't agree with one of my new features, let me know. I really invest lots of time to design those new features with my customer. I'm expecting you to consider it seriouly : if you think to refuse it again, let me know how I could help to integrate it in the core.

I mean, this is my goal, I don't want a half solution. I want this in the core of OpenERP, whatever I have to change, as long as the needed feature are implemented.

Thanks for your time and consideration,

Joël

review: Needs Resubmitting
Revision history for this message
Fabien (Open ERP) (fp-tinyerp) wrote :

I checked the logic and it seems good. If someone can check the code and if it's right, you can merge.

Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

Fabien considers the business logic good so I have no issue with that, but on the style/tech front I have a few from a quick overview of the branch:

Inconsistency
=============

stock/wizard/wizard_partial_move.py and stock/wizard/wizard_partial_picking.py seem to have been modified in similar but not identical ways: in both cases `amount_unit` is computed through `product.price_get(pricetype.field, context)[product.id]` but while the latter uses `amount_unit` on the next line, the former uses `product.amount_unit` and never calls `amount_unit`. This might be a bug in either of them.

(I also note a high level of redundancy between these wizards (at least on this specific method), they might benefit from from de-duplication in the future, to avoid that kind of potential issues)

Commented code
==============

In a few places, you commented existing code or committed commented code. Please remove these instances, removed code can be found via the VCS's history, and I don't quite see the justification for added already commented code (if it's work in progress its place is in a non-merged branch, if it's something to add/complete, it should be in a bug on the tracker):

* hr_timesheet_invoice/hr_timesheet_invoice.py at revid:<email address hidden> (commented existing code)
* account_analytic_analysis/account_analytic_analysis.py at revid:<email address hidden> (added a commented method)

Style
=====

At revid:<email address hidden>, in account_analytic_analysis/account_analytic_analysis.py you perform a test using dict.has_key. In this case, please either use `'key' in dict` (`has_key` is deprecated using Python2.6 -3, and doesn't exist anymore in Python 3). Furthermore this specific instance would probably be simpler by using `dict.setdefault` or a `collections.defaultdict` instance.

As a side note, in a few places you edited code using old-style difference operators (<>). If you can (not in this branch, but in the future) don't hesitate swapping them for "modern" difference operators (!=).

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

Hi,

Thanks for those constructive comments !! I'll make those changes ASAP.

Little remark : those wizard are really fat, and I know that.. But I can't refactor everything at once right :) ?

I really appreciate you consider my work. I'll remember it !

Have a nice week-end,

Regards,

Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

> Little remark : those wizard are really fat, and I know that.. But I can't refactor everything at once right :) ?

Yeah, it was more of a tentative note for me (or anybody who reads this thing)

> I really appreciate you consider my work. I'll remember it !

Pleasure.

2555. By Joël Grand-Guillaume @ camptocamp

[FIX] According to Xavier (OpenERP) to fit OpenERP requierements : remove commented code, use != instead of <>, fix picking wizard.

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

Hi Xavier,

I change everything according to your comments. I hope this is fine for you now.

Summary:

- Change the stocks wizards (both with amount_unit)
- Remove commented code (hope I don't miss some...)
- Change <> for !=

Note, I don't find the ".has_key" you talked about. So, in case of trouble with this, feel free to rewrite that point for the merge.

Thanks in advance for merging this !

Regards,

Joël

Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

Merged, the _check_currency validator threw some error when trying to install all the demo data, but if there's a bug in that it'll probably be sniffed out during usage.

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

Hi,

Thanks a lot for that !

Best regards,

Joël

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'account/account_analytic_line.py'
2--- account/account_analytic_line.py 2010-01-12 09:18:39 +0000
3+++ account/account_analytic_line.py 2010-02-15 10:42:23 +0000
4@@ -25,10 +25,46 @@
5 from osv import osv
6 from tools.translate import _
7 import tools
8+from tools import config
9
10 class account_analytic_line(osv.osv):
11 _name = 'account.analytic.line'
12 _description = 'Analytic lines'
13+
14+ def _amount_currency(self, cr, uid, ids, field_name, arg, context={}):
15+ result = {}
16+ for rec in self.browse(cr, uid, ids, context):
17+ cmp_cur_id=rec.company_id.currency_id.id
18+ aa_cur_id=rec.account_id.currency_id.id
19+ # Always provide the amount in currency
20+ if cmp_cur_id != aa_cur_id:
21+ cur_obj = self.pool.get('res.currency')
22+ ctx = {}
23+ if rec.date and rec.amount:
24+ ctx['date'] = rec.date
25+ result[rec.id] = cur_obj.compute(cr, uid, rec.company_id.currency_id.id,
26+ rec.account_id.currency_id.id, rec.amount,
27+ context=ctx)
28+ else:
29+ result[rec.id]=rec.amount
30+ return result
31+
32+ def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}):
33+ result = {}
34+ for rec in self.browse(cr, uid, ids, context):
35+ # Always provide second currency
36+ result[rec.id] = (rec.account_id.currency_id.id,rec.account_id.currency_id.code)
37+ return result
38+
39+ def _get_account_line(self, cr, uid, ids, context={}):
40+ aac_ids = {}
41+ for acc in self.pool.get('account.analytic.account').browse(cr, uid, ids):
42+ aac_ids[acc.id] = True
43+ aal_ids = []
44+ if aac_ids:
45+ aal_ids = self.pool.get('account.analytic.line').search(cr, uid, [('account_id','in',aac_ids.keys())], context=context)
46+ return aal_ids
47+
48 _columns = {
49 'name' : fields.char('Description', size=256, required=True),
50 'date' : fields.date('Date', required=True),
51@@ -42,14 +78,27 @@
52 'journal_id' : fields.many2one('account.analytic.journal', 'Analytic Journal', required=True, ondelete='cascade', select=True),
53 'code' : fields.char('Code', size=8),
54 'user_id' : fields.many2one('res.users', 'User',),
55+ 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency',
56+ store={
57+ 'account.analytic.account': (_get_account_line, ['company_id'], 50),
58+ 'account.analytic.line': (lambda self,cr,uid,ids,c={}: ids, ['amount','unit_amount'],10),
59+ },
60+ help="The related account currency if not equal to the company one."),
61+ 'company_id': fields.many2one('res.company','Company',required=True),
62+ 'amount_currency': fields.function(_amount_currency, method=True, digits=(16, int(config['price_accuracy'])), string='Amount currency',
63+ store={
64+ 'account.analytic.account': (_get_account_line, ['company_id'], 50),
65+ 'account.analytic.line': (lambda self,cr,uid,ids,c={}: ids, ['amount','unit_amount'],10),
66+ },
67+ help="The amount expressed in the related account currency if not equal to the company one."),
68 'ref': fields.char('Reference', size=32),
69 }
70 _defaults = {
71 'date': lambda *a: time.strftime('%Y-%m-%d'),
72+ 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', c),
73 }
74 _order = 'date'
75
76-
77 def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
78 if context is None:
79 context = {}
80@@ -73,11 +122,15 @@
81 # (_check_company, 'You can not create analytic line that is not in the same company than the account line', ['account_id'])
82 ]
83
84- def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount,
85+ # Compute the cost based on the price type define into company
86+ # property_valuation_price_type property
87+ def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount,company_id,
88 unit=False, context=None):
89+ if context==None:
90+ context={}
91 uom_obj = self.pool.get('product.uom')
92 product_obj = self.pool.get('product.product')
93-# if unit_amount and prod_id:
94+ company_obj=self.pool.get('res.company')
95 if prod_id:
96 prod = product_obj.browse(cr, uid, prod_id)
97 a = prod.product_tmpl_id.property_account_expense.id
98@@ -88,8 +141,14 @@
99 _('There is no expense account defined ' \
100 'for this product: "%s" (id:%d)') % \
101 (prod.name, prod.id,))
102- amount = unit_amount * uom_obj._compute_price(cr, uid,
103- prod.uom_id.id, prod.standard_price, unit)
104+ if not company_id:
105+ company_id=company_obj._company_default_get(cr, uid, 'account.analytic.line', context)
106+
107+ # Compute based on pricetype
108+ pricetype=self.pool.get('product.price.type').browse(cr,uid,company_obj.browse(cr,uid,company_id).property_valuation_price_type.id)
109+ amount_unit=prod.price_get(pricetype.field, context)[prod.id]
110+
111+ amount=amount_unit*unit_amount or 1.0
112 return {'value': {
113 'amount': - round(amount, 2),
114 'general_account_id': a,
115
116=== modified file 'account/project/project.py'
117--- account/project/project.py 2010-02-05 15:09:27 +0000
118+++ account/project/project.py 2010-02-15 10:42:23 +0000
119@@ -1,8 +1,8 @@
120 # -*- coding: utf-8 -*-
121 ##############################################################################
122-#
123+#
124 # OpenERP, Open Source Management Solution
125-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
126+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
127 #
128 # This program is free software: you can redistribute it and/or modify
129 # it under the terms of the GNU Affero General Public License as
130@@ -15,7 +15,7 @@
131 # GNU Affero General Public License for more details.
132 #
133 # You should have received a copy of the GNU Affero General Public License
134-# along with this program. If not, see <http://www.gnu.org/licenses/>.
135+# along with this program. If not, see <http://www.gnu.org/licenses/>.
136 #
137 ##############################################################################
138
139@@ -25,10 +25,6 @@
140 from osv import fields
141 from osv import osv
142
143-#
144-# Object definition
145-#
146-
147 class account_analytic_journal(osv.osv):
148 _name = 'account.analytic.journal'
149 _columns = {
150
151=== modified file 'account/project/project_view.xml'
152--- account/project/project_view.xml 2010-01-19 07:35:47 +0000
153+++ account/project/project_view.xml 2010-02-15 10:42:23 +0000
154@@ -69,6 +69,7 @@
155 <field name="parent_id" on_change="on_change_parent(parent_id)"/>
156 <field name="company_id" select="2" widget="selection"/>
157 <field name="type" select="2"/>
158+ <field name="company_currency_id" select="2"/>
159 </group>
160 <notebook colspan="4">
161 <page string="Account Data">
162@@ -147,6 +148,9 @@
163 <field name="move_id" select="2"/>
164 <field name="unit_amount" select="2"/>
165 <field name="ref" select="2"/>
166+ <field name="currency_id" select="2"/>
167+ <field name="amount_currency" select="2"/>
168+ <field name="company_id" select="2"/>
169 <newline/>
170 <field name="product_id" select="2"/>
171 <field name="product_uom_id" select="2"/>
172@@ -159,16 +163,19 @@
173 <field name="type">tree</field>
174 <field name="arch" type="xml">
175 <tree editable="top" string="Analytic Entries">
176- <field name="date"/>
177+ <field name="date" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
178 <field name="name"/>
179- <field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, product_uom_id)" sum="Total quantity"/>
180- <field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, product_uom_id)"/>
181+ <field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)" sum="Total quantity"/>
182+ <field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
183 <field domain="[('type','=','normal')]" name="account_id"/>
184- <field invisible="True" name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, product_uom_id)"/>
185+ <field invisible="True" name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
186 <field name="amount" sum="Total amount"/>
187 <field name="general_account_id"/>
188 <field name="journal_id"/>
189 <field name="ref"/>
190+ <field name="currency_id" />
191+ <field name="amount_currency" />
192+ <field name="company_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
193 </tree>
194 </field>
195 </record>
196@@ -225,13 +232,16 @@
197 <form string="Project line">
198 <field name="name"/>
199 <field name="account_id"/>
200- <field name="date"/>
201+ <field name="date" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
202 <field name="journal_id"/>
203- <field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, product_uom_id)"/>
204- <field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, product_uom_id)"/>
205- <field name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, product_uom_id)"/>
206+ <field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
207+ <field name="product_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
208+ <field name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
209 <field invisible="True" name="general_account_id"/>
210 <field name="amount"/>
211+ <field name="currency_id" />
212+ <field name="amount_currency" />
213+ <field name="company_id" on_change="on_change_unit_amount(product_id, unit_amount, company_id, product_uom_id)"/>
214 </form>
215 </field>
216 </record>
217
218=== modified file 'account_analytic_analysis/account_analytic_analysis.py'
219--- account_analytic_analysis/account_analytic_analysis.py 2010-02-01 08:29:39 +0000
220+++ account_analytic_analysis/account_analytic_analysis.py 2010-02-15 10:42:23 +0000
221@@ -34,7 +34,8 @@
222 res = {}
223 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
224 if ids2:
225- cr.execute("select account_analytic_line.account_id, COALESCE(sum(amount),0.0) \
226+ acc_set = ",".join(map(str, ids2))
227+ cr.execute("select account_analytic_line.account_id, COALESCE(sum(amount_currency),0.0) \
228 from account_analytic_line \
229 join account_analytic_journal \
230 on account_analytic_line.journal_id = account_analytic_journal.id \
231@@ -43,15 +44,8 @@
232 group by account_analytic_line.account_id" ,(ids2,))
233 for account_id, sum in cr.fetchall():
234 res[account_id] = round(sum,2)
235- for obj_id in ids:
236- res.setdefault(obj_id, 0.0)
237- for child_id in self.search(cr, uid,
238- [('parent_id', 'child_of', [obj_id])]):
239- if child_id != obj_id:
240- res[obj_id] += res.get(child_id, 0.0)
241- for id in ids:
242- res[id] = round(res.get(id, 0.0),2)
243- return res
244+
245+ return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context)
246
247 def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context={}):
248 res = {}
249@@ -59,6 +53,10 @@
250 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
251 if ids2:
252 # Amount uninvoiced hours to invoice at sale price
253+ # Warnning
254+ # This computation doesn't take care of pricelist !
255+ # Just consider list_price
256+ acc_set = ",".join(map(str, ids2))
257 cr.execute("""SELECT account_analytic_account.id, \
258 COALESCE(sum (product_template.list_price * \
259 account_analytic_line.unit_amount * \
260@@ -83,17 +81,6 @@
261 for account_id, sum in cr.fetchall():
262 res[account_id] = round(sum,2)
263
264- # Expense amount and purchase invoice
265- #acc_set = ",".join(map(str, ids2))
266- #cr.execute ("select account_analytic_line.account_id, sum(amount) \
267- # from account_analytic_line \
268- # join account_analytic_journal \
269- # on account_analytic_line.journal_id = account_analytic_journal.id \
270- # where account_analytic_line.account_id IN (%s) \
271- # and account_analytic_journal.type = 'purchase' \
272- # GROUP BY account_analytic_line.account_id;"%acc_set)
273- #for account_id, sum in cr.fetchall():
274- # res2[account_id] = round(sum,2)
275 for obj_id in ids:
276 res.setdefault(obj_id, 0.0)
277 res2.setdefault(obj_id, 0.0)
278@@ -160,7 +147,9 @@
279 res = {}
280 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
281 if ids2:
282- cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount),0.0) \
283+ acc_set = ",".join(map(str, ids2))
284+ cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount_currency),0.0) \
285+
286 from account_analytic_line \
287 join account_analytic_journal \
288 on account_analytic_line.journal_id = account_analytic_journal.id \
289@@ -169,20 +158,16 @@
290 GROUP BY account_analytic_line.account_id""",(ids2,))
291 for account_id, sum in cr.fetchall():
292 res[account_id] = round(sum,2)
293- for obj_id in ids:
294- res.setdefault(obj_id, 0.0)
295- for child_id in self.search(cr, uid,
296- [('parent_id', 'child_of', [obj_id])]):
297- if child_id != obj_id:
298- res[obj_id] += res.get(child_id, 0.0)
299- for id in ids:
300- res[id] = round(res.get(id, 0.0),2)
301- return res
302-
303+ return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context)
304+
305+ # TODO Take care of pricelist and purchase !
306 def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}):
307 res = {}
308 res2 = {}
309 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
310+ # Warnning
311+ # This computation doesn't take care of pricelist !
312+ # Just consider list_price
313 if ids2:
314 cr.execute("""select account_analytic_line.account_id as account_id, \
315 COALESCE(sum((account_analytic_line.unit_amount * pt.list_price) \
316@@ -205,7 +190,7 @@
317 GROUP BY account_analytic_line.account_id""",(ids2,))
318 for account_id, sum in cr.fetchall():
319 res2[account_id] = round(sum,2)
320-
321+
322 for obj_id in ids:
323 res.setdefault(obj_id, 0.0)
324 res2.setdefault(obj_id, 0.0)
325@@ -214,7 +199,7 @@
326 if child_id != obj_id:
327 res[obj_id] += res.get(child_id, 0.0)
328 res[obj_id] += res2.get(child_id, 0.0)
329-
330+
331 # sum both result on account_id
332 for id in ids:
333 res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
334@@ -289,7 +274,7 @@
335 def _remaining_hours_calc(self, cr, uid, ids, name, arg, context={}):
336 res = {}
337 for account in self.browse(cr, uid, ids):
338- if account.quantity_max <> 0:
339+ if account.quantity_max != 0:
340 res[account.id] = account.quantity_max - account.hours_quantity
341 else:
342 res[account.id]=0.0
343@@ -323,7 +308,7 @@
344 for account in self.browse(cr, uid, ids):
345 if account.ca_invoiced == 0:
346 res[account.id]=0.0
347- elif account.total_cost <> 0.0:
348+ elif account.total_cost != 0.0:
349 res[account.id] = -(account.real_margin / account.total_cost) * 100
350 else:
351 res[account.id] = 0.0
352@@ -334,7 +319,7 @@
353 def _remaining_ca_calc(self, cr, uid, ids, name, arg, context={}):
354 res = {}
355 for account in self.browse(cr, uid, ids):
356- if account.amount_max <> 0:
357+ if account.amount_max != 0:
358 res[account.id] = account.amount_max - account.ca_invoiced
359 else:
360 res[account.id]=0.0
361
362=== modified file 'analytic/project.py'
363--- analytic/project.py 2010-02-05 15:09:27 +0000
364+++ analytic/project.py 2010-02-15 10:42:24 +0000
365@@ -33,65 +33,19 @@
366 _name = 'account.analytic.account'
367 _description = 'Analytic Accounts'
368
369- def _credit_calc(self, cr, uid, ids, name, arg, context={}):
370-
371- where_date = ''
372- if context.get('from_date',False):
373- where_date += " AND l.date >= '" + context['from_date'] + "'"
374- if context.get('to_date',False):
375- where_date += " AND l.date <= '" + context['to_date'] + "'"
376-
377- cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount<0 and a.id =ANY(%s) GROUP BY a.id",(ids,))
378- r = dict(cr.fetchall())
379- for i in ids:
380- r.setdefault(i,0.0)
381- return r
382-
383- def _debit_calc(self, cr, uid, ids, name, arg, context={}):
384-
385- where_date = ''
386- if context.get('from_date',False):
387- where_date += " AND l.date >= '" + context['from_date'] + "'"
388- if context.get('to_date',False):
389- where_date += " AND l.date <= '" + context['to_date'] + "'"
390-
391- cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount>0 and a.id =ANY(%s) GROUP BY a.id" ,(ids,))
392- r= dict(cr.fetchall())
393- for i in ids:
394- r.setdefault(i,0.0)
395- return r
396-
397- def _balance_calc(self, cr, uid, ids, name, arg, context={}):
398- res = {}
399- ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
400- for i in ids:
401- res.setdefault(i,0.0)
402- if not ids2:
403- return res
404-
405- where_date = ''
406- if context.get('from_date',False):
407- where_date += " AND l.date >= '" + context['from_date'] + "'"
408- if context.get('to_date',False):
409- where_date += " AND l.date <= '" + context['to_date'] + "'"
410-
411- cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE a.id =ANY(%s) GROUP BY a.id",(ids2,))
412-
413- for account_id, sum in cr.fetchall():
414- res[account_id] = sum
415-
416- cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id =ANY(%s)",(ids2,))
417-
418+ def _compute_currency_for_level_tree(self, cr, uid, ids, ids2, res, acc_set, context={}):
419+ # Handle multi-currency on each level of analytic account
420+ # This is a refactoring of _balance_calc computation
421+ cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id in (%s)" % acc_set)
422 currency= dict(cr.fetchall())
423-
424 res_currency= self.pool.get('res.currency')
425 for id in ids:
426 if id not in ids2:
427 continue
428 for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]):
429- if child <> id:
430+ if child != id:
431 res.setdefault(id, 0.0)
432- if currency[child]<>currency[id]:
433+ if currency[child]!=currency[id]:
434 res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context)
435 else:
436 res[id] += res.get(child, 0.0)
437@@ -104,24 +58,88 @@
438
439 return dict([(i, res[i]) for i in ids ])
440
441+
442+ def _credit_calc(self, cr, uid, ids, name, arg, context={}):
443+ res = {}
444+ ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
445+ acc_set = ",".join(map(str, ids2))
446+
447+ for i in ids:
448+ res.setdefault(i,0.0)
449+
450+ if not ids2:
451+ return res
452+
453+ where_date = ''
454+ if context.get('from_date',False):
455+ where_date += " AND l.date >= '" + context['from_date'] + "'"
456+ if context.get('to_date',False):
457+ where_date += " AND l.date <= '" + context['to_date'] + "'"
458+ cr.execute("SELECT a.id, COALESCE(SUM(l.amount_currency),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount_currency<0 and a.id =ANY(%s) GROUP BY a.id",(ids2,))
459+ r = dict(cr.fetchall())
460+ return self._compute_currency_for_level_tree(cr, uid, ids, ids2, r, acc_set, context)
461+
462+ def _debit_calc(self, cr, uid, ids, name, arg, context={}):
463+ res = {}
464+ ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
465+ acc_set = ",".join(map(str, ids2))
466+
467+ for i in ids:
468+ res.setdefault(i,0.0)
469+
470+ if not ids2:
471+ return res
472+
473+ where_date = ''
474+ if context.get('from_date',False):
475+ where_date += " AND l.date >= '" + context['from_date'] + "'"
476+ if context.get('to_date',False):
477+ where_date += " AND l.date <= '" + context['to_date'] + "'"
478+ cr.execute("SELECT a.id, COALESCE(SUM(l.amount_currency),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount_currency>0 and a.id =ANY(%s) GROUP BY a.id" ,(ids2,))
479+ r= dict(cr.fetchall())
480+ return self._compute_currency_for_level_tree(cr, uid, ids, ids2, r, acc_set, context)
481+
482+ def _balance_calc(self, cr, uid, ids, name, arg, context={}):
483+ res = {}
484+ ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
485+ acc_set = ",".join(map(str, ids2))
486+
487+ for i in ids:
488+ res.setdefault(i,0.0)
489+
490+ if not ids2:
491+ return res
492+
493+ where_date = ''
494+ if context.get('from_date',False):
495+ where_date += " AND l.date >= '" + context['from_date'] + "'"
496+ if context.get('to_date',False):
497+ where_date += " AND l.date <= '" + context['to_date'] + "'"
498+ cr.execute("SELECT a.id, COALESCE(SUM(l.amount_currency),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE a.id =ANY(%s) GROUP BY a.id",(ids2,))
499+
500+ for account_id, sum in cr.fetchall():
501+ res[account_id] = sum
502+
503+ return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context)
504+
505 def _quantity_calc(self, cr, uid, ids, name, arg, context={}):
506 #XXX must convert into one uom
507 res = {}
508 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
509 acc_set = ",".join(map(str, ids2))
510-
511+
512 for i in ids:
513 res.setdefault(i,0.0)
514-
515- if not acc_set:
516+
517+ if not ids2:
518 return res
519-
520+
521 where_date = ''
522 if context.get('from_date',False):
523 where_date += " AND l.date >= '" + context['from_date'] + "'"
524 if context.get('to_date',False):
525 where_date += " AND l.date <= '" + context['to_date'] + "'"
526-
527+
528 cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \
529 FROM account_analytic_account a \
530 LEFT JOIN account_analytic_line l ON (a.id = l.account_id ' + where_date + ') \
531@@ -134,7 +152,7 @@
532 if id not in ids2:
533 continue
534 for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]):
535- if child <> id:
536+ if child != id:
537 res.setdefault(id, 0.0)
538 res[id] += res.get(child, 0.0)
539 return dict([(i, res[i]) for i in ids])
540@@ -161,11 +179,15 @@
541 result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) or False
542 return result
543
544+ def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}):
545+ result=self._get_company_currency(cr, uid, ids, field_name, arg, context={})
546+ return result
547+
548 _columns = {
549 'name' : fields.char('Account Name', size=128, required=True),
550 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Full Account Name'),
551 'code' : fields.char('Account Code', size=24),
552-# 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the analytic account without removing it."),
553+ # 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the analytic account without removing it."),
554 'type': fields.selection([('view','View'), ('normal','Normal')], 'Account Type'),
555 'description' : fields.text('Description'),
556 'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
557@@ -190,6 +212,7 @@
558 \n* And finally when all the transactions are over, it can be in \'Close\' state. \
559 \n* The project can be in either if the states \'Template\' and \'Running\'.\n If it is template then we can make projects based on the template projects. If its in \'Running\' state it is a normal project.\
560 \n If it is to be reviewed then the state is \'Pending\'.\n When the project is completed the state is set to \'Done\'.'),
561+ 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True),
562 }
563
564 def _default_company(self, cr, uid, context={}):
565@@ -198,14 +221,14 @@
566 return user.company_id.id
567 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
568 _defaults = {
569-# 'active' : lambda *a : True,
570+ 'active' : lambda *a : True,
571 'type' : lambda *a : 'normal',
572 'company_id': _default_company,
573 'state' : lambda *a : 'open',
574 'user_id' : lambda self,cr,uid,ctx : uid,
575 'partner_id': lambda self,cr, uid, ctx: ctx.get('partner_id', False),
576 'contact_id': lambda self,cr, uid, ctx: ctx.get('contact_id', False),
577- 'date_start': lambda *a: time.strftime('%Y-%m-%d')
578+ 'date_start': lambda *a: time.strftime('%Y-%m-%d')
579 }
580
581 def check_recursion(self, cr, uid, ids, parent=None):
582@@ -260,3 +283,4 @@
583 return self.name_get(cr, uid, account, context=context)
584
585 account_analytic_account()
586+
587
588=== modified file 'analytic_user_function/analytic_user_function.py'
589--- analytic_user_function/analytic_user_function.py 2010-01-12 09:18:39 +0000
590+++ analytic_user_function/analytic_user_function.py 2010-02-15 10:42:25 +0000
591@@ -99,8 +99,11 @@
592 _('There is no expense account define ' \
593 'for this product: "%s" (id:%d)') % \
594 (r.product_id.name, r.product_id.id,))
595- amount = unit_amount * self.pool.get('product.uom')._compute_price(cr, uid,
596- r.product_id.uom_id.id, r.product_id.standard_price, False)
597+ # Compute based on pricetype
598+ amount_unit=self.on_change_unit_amount(cr, uid, ids,
599+ r.product_id.id, unit_amount, r.product_id.uom_id.id)['value']['amount']
600+
601+ amount = unit_amount * amount_unit
602 res ['value']['amount']= - round(amount, 2)
603 res ['value']['general_account_id']= a
604 return res
605@@ -132,8 +135,11 @@
606 _('There is no expense account define ' \
607 'for this product: "%s" (id:%d)') % \
608 (r.product_id.name, r.product_id.id,))
609- amount = unit_amount * r.product_id.uom_id._compute_price(cr, uid,
610- r.product_id.uom_id.id, r.product_id.standard_price, False)
611+ # Compute based on pricetype
612+ amount_unit=self.on_change_unit_amount(cr, uid, ids,
613+ r.product_id.id, unit_amount, r.product_id.uom_id.id)['value']['amount']
614+
615+ amount = unit_amount * amount_unit
616 res ['value']['amount']= - round(amount, 2)
617 res ['value']['general_account_id']= a
618 return res
619
620=== modified file 'hr_expense/hr_expense.py'
621--- hr_expense/hr_expense.py 2010-02-01 08:29:39 +0000
622+++ hr_expense/hr_expense.py 2010-02-15 10:42:25 +0000
623@@ -210,12 +210,18 @@
624 'date_value' : lambda *a: time.strftime('%Y-%m-%d'),
625 }
626 _order = "sequence"
627- def onchange_product_id(self, cr, uid, ids, product_id, uom_id, context={}):
628+ def onchange_product_id(self, cr, uid, ids, product_id, uom_id, employee_id, context={}):
629 v={}
630 if product_id:
631 product=self.pool.get('product.product').browse(cr,uid,product_id, context=context)
632 v['name']=product.name
633- v['unit_amount']=product.standard_price
634+
635+ # Compute based on pricetype of employee company
636+ pricetype_id = self.pool.get('hr.employee').browse(cr,uid,employee_id).user_id.company_id.property_valuation_price_type.id
637+ pricetype=self.pool.get('product.price.type').browse(cr,uid,pricetype_id)
638+ amount_unit=product.price_get(pricetype.field, context)[product.id]
639+
640+ v['unit_amount']=amount_unit
641 if not uom_id:
642 v['uom_id']=product.uom_id.id
643 return {'value':v}
644
645=== modified file 'hr_expense/hr_expense_view.xml'
646--- hr_expense/hr_expense_view.xml 2010-02-01 11:09:48 +0000
647+++ hr_expense/hr_expense_view.xml 2010-02-15 10:42:25 +0000
648@@ -73,8 +73,8 @@
649 <newline/>
650 <field colspan="4" name="line_ids" nolabel="1">
651 <form string="Expense Lines">
652- <field name="product_id" on_change="onchange_product_id(product_id, uom_id)" select="2"/>
653- <field name="uom_id" on_change="onchange_product_id(product_id, uom_id)" select="2"/>
654+ <field name="product_id" on_change="onchange_product_id(product_id, uom_id, parent.employee_id)" select="2"/>
655+ <field name="uom_id" on_change="onchange_product_id(product_id, uom_id, parent.employee_id)" select="2"/>
656 <field name="name" select="1"/>
657 <field name="date_value" select="1"/>
658 <field name="unit_quantity" select="2"/>
659
660=== modified file 'hr_timesheet/hr_timesheet.py'
661--- hr_timesheet/hr_timesheet.py 2010-01-12 09:18:39 +0000
662+++ hr_timesheet/hr_timesheet.py 2010-02-15 10:42:25 +0000
663@@ -57,7 +57,9 @@
664 res = {}
665 # if prod_id and unit_amount:
666 if prod_id:
667- res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,unit, context)
668+ # find company
669+ company_id=self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context)
670+ res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,company_id,unit, context)
671 return res
672
673 def _getEmployeeProduct(self, cr, uid, context):
674
675=== modified file 'hr_timesheet_invoice/hr_timesheet_invoice.py'
676--- hr_timesheet_invoice/hr_timesheet_invoice.py 2010-01-21 15:13:50 +0000
677+++ hr_timesheet_invoice/hr_timesheet_invoice.py 2010-02-15 10:42:25 +0000
678@@ -52,6 +52,7 @@
679 res[id] = round(res.get(id, 0.0),2)
680 return res
681
682+
683 _inherit = "account.analytic.account"
684 _columns = {
685 'pricelist_id' : fields.many2one('product.pricelist', 'Sale Pricelist'),
686@@ -77,7 +78,7 @@
687 }
688
689 def unlink(self, cursor, user, ids, context=None):
690- self._check(cursor, user, ids)
691+ # self._check(cursor, user, ids)
692 return super(account_analytic_line,self).unlink(cursor, user, ids,
693 context=context)
694
695@@ -90,11 +91,11 @@
696 select = ids
697 if isinstance(select, (int, long)):
698 select = [ids]
699- if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False:
700- for line in self.browse(cr, uid, select):
701- if line.invoice_id:
702- raise osv.except_osv(_('Error !'),
703- _('You can not modify an invoiced analytic line!'))
704+ if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False:
705+ for line in self.browse(cr, uid, select):
706+ if line.invoice_id:
707+ raise osv.except_osv(_('Error !'),
708+ _('You can not modify an invoiced analytic line!'))
709 return True
710
711 def copy(self, cursor, user, obj_id, default=None, context=None):
712
713=== modified file 'hr_timesheet_invoice/hr_timesheet_invoice_view.xml'
714--- hr_timesheet_invoice/hr_timesheet_invoice_view.xml 2010-01-09 14:30:02 +0000
715+++ hr_timesheet_invoice/hr_timesheet_invoice_view.xml 2010-02-15 10:42:25 +0000
716@@ -20,7 +20,19 @@
717 </field>
718 </field>
719 </record>
720-
721+
722+ <record id="view_account_analytic_account_form" model="ir.ui.view">
723+ <field name="name">account.analytic.account.form</field>
724+ <field name="model">account.analytic.account</field>
725+ <field name="inherit_id" ref="account.view_account_analytic_account_form"/>
726+ <field name="type">form</field>
727+ <field name="arch" type="xml">
728+ <field name="company_currency_id" select="2" position="replace">
729+ <field name="currency_id" select="2"/>
730+ </field>
731+ </field>
732+ </record>
733+
734 <record id="hr_timesheet_line_form" model="ir.ui.view">
735 <field name="name">hr.analytic.timesheet.form</field>
736 <field name="model">hr.analytic.timesheet</field>
737
738=== modified file 'multi_company/__init__.py'
739--- multi_company/__init__.py 2010-01-12 09:18:39 +0000
740+++ multi_company/__init__.py 2010-02-15 10:42:25 +0000
741@@ -18,6 +18,5 @@
742 # along with this program. If not, see <http://www.gnu.org/licenses/>.
743 #
744 ##############################################################################
745-
746 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
747
748
749=== modified file 'multi_company/__terp__.py'
750--- multi_company/__terp__.py 2010-01-12 09:18:39 +0000
751+++ multi_company/__terp__.py 2010-02-15 10:42:25 +0000
752@@ -32,7 +32,7 @@
753 'depends': [
754 'base',
755 'sale',
756- 'project'
757+ 'project',
758 ],
759 'init_xml': [],
760 'update_xml': [
761
762=== modified file 'product/__init__.py'
763--- product/__init__.py 2010-01-12 09:18:39 +0000
764+++ product/__init__.py 2010-02-15 10:42:25 +0000
765@@ -23,6 +23,6 @@
766 import report
767 import partner
768 import wizard
769-
770+import company
771 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
772
773
774=== modified file 'product/__terp__.py'
775--- product/__terp__.py 2010-01-12 09:18:39 +0000
776+++ product/__terp__.py 2010-02-15 10:42:25 +0000
777@@ -54,6 +54,7 @@
778 'product_view.xml',
779 'pricelist_view.xml',
780 'partner_view.xml',
781+ 'company_view.xml',
782 'product_wizard.xml',
783 'process/product_process.xml'
784 ],
785
786=== added file 'product/company.py'
787--- product/company.py 1970-01-01 00:00:00 +0000
788+++ product/company.py 2010-02-15 10:42:26 +0000
789@@ -0,0 +1,53 @@
790+# -*- coding: utf-8 -*-
791+##############################################################################
792+#
793+# OpenERP, Open Source Management Solution
794+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
795+#
796+# This program is free software: you can redistribute it and/or modify
797+# it under the terms of the GNU Affero General Public License as
798+# published by the Free Software Foundation, either version 3 of the
799+# License, or (at your option) any later version.
800+#
801+# This program is distributed in the hope that it will be useful,
802+# but WITHOUT ANY WARRANTY; without even the implied warranty of
803+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
804+# GNU Affero General Public License for more details.
805+#
806+# You should have received a copy of the GNU Affero General Public License
807+# along with this program. If not, see <http://www.gnu.org/licenses/>.
808+#
809+##############################################################################
810+
811+from osv import fields, osv
812+
813+
814+class res_company(osv.osv):
815+ _inherit = 'res.company'
816+ _columns = {
817+ 'property_valuation_price_type': fields.property(
818+ 'product.price.type',
819+ type='many2one',
820+ relation='product.price.type',
821+ domain=[],
822+ string="Valuation Price Type",
823+ method=True,
824+ view_load=True,
825+ help="The price type field in the selected price type will be used, instead of the default one, \
826+ for valuation of product in the current company"),
827+ }
828+
829+ def _check_currency(self, cr, uid, ids):
830+ for rec in self.browse(cr, uid, ids):
831+ if rec.currency_id.id <> rec.property_valuation_price_type.currency_id.id:
832+ return False
833+ return True
834+
835+ _constraints = [
836+ (_check_currency, 'Error! You can not chooes a pricetype in a different currency than your company (Not supported now).', ['property_valuation_price_type'])
837+ ]
838+
839+res_company()
840+
841+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
842+
843
844=== added file 'product/company_view.xml'
845--- product/company_view.xml 1970-01-01 00:00:00 +0000
846+++ product/company_view.xml 2010-02-15 10:42:26 +0000
847@@ -0,0 +1,18 @@
848+<openerp>
849+ <data>
850+
851+
852+ <record id="view_company_property_form" model="ir.ui.view">
853+ <field name="name">res.company.product.property.form.inherit</field>
854+ <field name="model">res.company</field>
855+ <field name="type">form</field>
856+ <field name="inherit_id" ref="base.view_company_form"/>
857+ <field name="arch" type="xml">
858+ <field name="currency_id" position="after">
859+ <field name="property_valuation_price_type"/>
860+ </field>
861+ </field>
862+ </record>
863+
864+ </data>
865+</openerp>
866
867=== modified file 'product/pricelist.py'
868--- product/pricelist.py 2010-02-01 08:29:39 +0000
869+++ product/pricelist.py 2010-02-15 10:42:26 +0000
870@@ -62,6 +62,7 @@
871 "active": lambda *args: True,
872 "currency_id": _get_currency
873 }
874+
875 price_type()
876
877 #----------------------------------------------------------
878
879=== modified file 'product/product_data.xml'
880--- product/product_data.xml 2009-09-24 10:46:21 +0000
881+++ product/product_data.xml 2010-02-15 10:42:26 +0000
882@@ -73,7 +73,6 @@
883
884
885
886-
887 <!--
888 Price list type
889 -->
890@@ -81,6 +80,7 @@
891 <field name="name">Sale Pricelist</field>
892 <field name="key">sale</field>
893 </record>
894+
895
896 <!--
897 Price list
898@@ -107,5 +107,12 @@
899 <field name="fields_id" search="[('model','=','res.partner'),('name','=','property_product_pricelist')]"/>
900 <field eval="'product.pricelist,'+str(ref('list0'))" name="value"/>
901 </record>
902+
903+ <record forcecreate="True" id="property_valuation_price_type" model="ir.property">
904+ <field name="name">property_valuation_price_type</field>
905+ <field name="fields_id" search="[('model','=','res.company'),('name','=','property_valuation_price_type')]"/>
906+ <field eval="'product.price.type,'+str(ref('standard_price'))" name="value"/>
907+ </record>
908+
909 </data>
910 </openerp>
911
912=== modified file 'project_timesheet/project_timesheet.py'
913--- project_timesheet/project_timesheet.py 2010-01-21 15:13:50 +0000
914+++ project_timesheet/project_timesheet.py 2010-02-15 10:42:26 +0000
915@@ -77,8 +77,12 @@
916 vals_line['journal_id'] = result['journal_id']
917 vals_line['amount'] = 00.0
918 timeline_id = obj.create(cr, uid, vals_line, {})
919-
920- vals_line['amount'] = (-1) * vals['hours']* ( obj.browse(cr,uid,timeline_id).product_id.standard_price or 0.0)
921+
922+ # Compute based on pricetype
923+ amount_unit=obj.on_change_unit_amount(cr, uid, line_id,
924+ vals_line['product_id'], vals_line['unit_amount'], unit, context)
925+
926+ vals_line['amount'] = (-1) * vals['hours']* (unit_amount or 0.0)
927 obj.write(cr, uid,[timeline_id], vals_line, {})
928 vals['hr_analytic_timesheet_id'] = timeline_id
929 return super(project_work,self).create(cr, uid, vals, *args, **kwargs)
930@@ -104,7 +108,12 @@
931 vals_line['date'] = vals['date'][:10]
932 if 'hours' in vals:
933 vals_line['unit_amount'] = vals['hours']
934- vals_line['amount'] = (-1) * vals['hours'] * (obj.browse(cr,uid,line_id).product_id.standard_price or 0.0)
935+
936+ # Compute based on pricetype
937+ amount_unit=obj.on_change_unit_amount(cr, uid, line_id,
938+ vals_line['product_id'], vals_line['unit_amount'], unit, context)
939+
940+ vals_line['amount'] = (-1) * vals['hours'] * (amount_unit or 0.0)
941 obj.write(cr, uid, [line_id], vals_line, {})
942
943 return super(project_work,self).write(cr, uid, ids, vals, context)
944
945=== modified file 'stock/stock.py'
946--- stock/stock.py 2010-02-09 08:31:46 +0000
947+++ stock/stock.py 2010-02-15 10:42:27 +0000
948@@ -110,11 +110,15 @@
949 cr.execute('select distinct product_id from stock_move where (location_id=%s) or (location_dest_id=%s)', (id, id))
950 result = cr.dictfetchall()
951 if result:
952+ # Choose the right filed standard_price to read
953+ # Take the user company
954+ price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id
955+ pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
956 for r in result:
957 c = (context or {}).copy()
958 c['location'] = id
959- product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read, 'standard_price'], context=c)
960- final_value += (product[field_to_read] * product['standard_price'])
961+ product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read, pricetype.field], context=c)
962+ final_value += (product[field_to_read] * product[pricetype.field])
963 return final_value
964
965 def _product_value(self, cr, uid, ids, field_names, arg, context={}):
966@@ -211,6 +215,10 @@
967 if context is None:
968 context = {}
969 product_obj = self.pool.get('product.product')
970+ # Take the user company and pricetype
971+ price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id
972+ pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
973+
974 if not product_ids:
975 product_ids = product_obj.search(cr, uid, [])
976
977@@ -241,10 +249,16 @@
978 continue
979 product = products_by_id[product_id]
980 quantity_total += qty[product_id]
981- price = qty[product_id] * product.standard_price
982+
983+ # Compute based on pricetype
984+ # Choose the right filed standard_price to read
985+ amount_unit=product.price_get(pricetype.field, context)[product.id]
986+ price = qty[product_id] * amount_unit
987+ # price = qty[product_id] * product.standard_price
988+
989 total_price += price
990 result['product'].append({
991- 'price': product.standard_price,
992+ 'price': amount_unit,
993 'prod_name': product.name,
994 'code': product.default_code, # used by lot_overview_all report!
995 'variants': product.variants or '',
996@@ -645,7 +659,11 @@
997 def _get_price_unit_invoice(self, cursor, user, move_line, type):
998 '''Return the price unit for the move line'''
999 if type in ('in_invoice', 'in_refund'):
1000- return move_line.product_id.standard_price
1001+ # Take the user company and pricetype
1002+ price_type_id=self.pool.get('res.users').browse(cr,users,users).company_id.property_valuation_price_type.id
1003+ pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
1004+ amount_unit=move_line.product_id.price_get(pricetype.field, context)[move_line.product_id.id]
1005+ return amount_unit
1006 else:
1007 return move_line.product_id.list_price
1008
1009@@ -1408,13 +1426,19 @@
1010 ref = move.picking_id and move.picking_id.name or False
1011 product_uom_obj = self.pool.get('product.uom')
1012 default_uom = move.product_id.uom_id.id
1013+ date = time.strftime('%Y-%m-%d')
1014 q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
1015 if move.product_id.cost_method == 'average' and move.price_unit:
1016 amount = q * move.price_unit
1017+ # Base computation on valuation price type
1018 else:
1019- amount = q * move.product_id.standard_price
1020-
1021- date = time.strftime('%Y-%m-%d')
1022+ company_id=move.company_id.id
1023+
1024+ pricetype=self.pool.get('product.price.type').browse(cr,uid,move.company_id.property_valuation_price_type.id)
1025+ amount_unit=move.product_id.price_get(pricetype.field, context)[move.product_id.id]
1026+ amount=amount_unit * q or 1.0
1027+ # amount = q * move.product_id.standard_price
1028+
1029 partner_id = False
1030 if move.picking_id:
1031 partner_id = move.picking_id.address_id and (move.picking_id.address_id.partner_id and move.picking_id.address_id.partner_id.id or False) or False
1032@@ -1492,7 +1516,8 @@
1033 move_line = []
1034 for line in inv.inventory_line_id:
1035 pid = line.product_id.id
1036- price = line.product_id.standard_price or 0.0
1037+
1038+ # price = line.product_id.standard_price or 0.0
1039 amount = self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid]
1040 change = line.product_qty - amount
1041 lot_id = line.prod_lot_id.id
1042
1043=== modified file 'stock/wizard/wizard_partial_move.py'
1044--- stock/wizard/wizard_partial_move.py 2010-01-12 09:18:39 +0000
1045+++ stock/wizard/wizard_partial_move.py 2010-02-15 10:42:27 +0000
1046@@ -55,7 +55,7 @@
1047
1048 for move in move_lines:
1049 quantity = move.product_qty
1050- if move.state <> 'assigned':
1051+ if move.state != 'assigned':
1052 quantity = 0
1053
1054 _moves_arch_lst.append('<field name="move%s" />' % (move.id,))
1055@@ -124,7 +124,8 @@
1056 currency = data['form']['currency%s' % move.id]
1057
1058 qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id)
1059-
1060+ pricetype=pool.get('product.price.type').browse(cr,uid,user.company_id.property_valuation_price_type.id)
1061+
1062 if (qty > 0):
1063 new_price = currency_obj.compute(cr, uid, currency,
1064 user.company_id.currency_id.id, price)
1065@@ -133,15 +134,17 @@
1066 if product.qty_available<=0:
1067 new_std_price = new_price
1068 else:
1069- new_std_price = ((product.standard_price * product.qty_available)\
1070+ # Get the standard price
1071+ amount_unit=product.price_get(pricetype.field, context)[product.id]
1072+ new_std_price = ((amount_unit * product.qty_available)\
1073 + (new_price * qty))/(product.qty_available + qty)
1074
1075 product_obj.write(cr, uid, [product.id],
1076- {'standard_price': new_std_price})
1077+ {pricetype.field: new_std_price})
1078 move_obj.write(cr, uid, move.id, {'price_unit': new_price})
1079
1080 for move in too_few:
1081- if data['form']['move%s' % move.id] <> 0:
1082+ if data['form']['move%s' % move.id] != 0:
1083 new_move = move_obj.copy(cr, uid, move.id,
1084 {
1085 'product_qty' : data['form']['move%s' % move.id],
1086
1087=== modified file 'stock/wizard/wizard_partial_picking.py'
1088--- stock/wizard/wizard_partial_picking.py 2010-01-21 15:13:50 +0000
1089+++ stock/wizard/wizard_partial_picking.py 2010-02-15 10:42:27 +0000
1090@@ -61,7 +61,7 @@
1091 if m.state in ('done', 'cancel'):
1092 continue
1093 quantity = m.product_qty
1094- if m.state<>'assigned':
1095+ if m.state!='assigned':
1096 quantity = 0
1097
1098 _moves_arch_lst.append('<field name="move%s" />' % (m.id,))
1099@@ -133,7 +133,7 @@
1100 currency = data['form']['currency%s' % move.id]
1101
1102 qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id)
1103-
1104+ pricetype=pool.get('product.price.type').browse(cr,uid,user.company_id.property_valuation_price_type.id)
1105 if (qty > 0):
1106 new_price = currency_obj.compute(cr, uid, currency,
1107 user.company_id.currency_id.id, price)
1108@@ -142,11 +142,14 @@
1109 if product.qty_available<=0:
1110 new_std_price = new_price
1111 else:
1112- new_std_price = ((product.standard_price * product.qty_available)\
1113+ # Get the standard price
1114+ amount_unit=product.price_get(pricetype.field, context)[product.id]
1115+ new_std_price = ((amount_unit * product.qty_available)\
1116 + (new_price * qty))/(product.qty_available + qty)
1117-
1118+
1119+ # Write the field according to price type field
1120 product_obj.write(cr, uid, [product.id],
1121- {'standard_price': new_std_price})
1122+ {pricetype.field: new_std_price})
1123 move_obj.write(cr, uid, [move.id], {'price_unit': new_price})
1124
1125 for move in too_few:
1126@@ -158,7 +161,7 @@
1127 'move_lines' : [],
1128 'state':'draft',
1129 })
1130- if data['form']['move%s' % move.id] <> 0:
1131+ if data['form']['move%s' % move.id] != 0:
1132 new_obj = move_obj.copy(cr, uid, move.id,
1133 {
1134 'product_qty' : data['form']['move%s' % move.id],

Subscribers

People subscribed via source and target branches

to all changes: