From ff19c0f96b8e0f64c381e2e075a0398e2401de01 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Thu, 17 Dec 2009 15:13:32 +0100 Subject: [PATCH 001/328] Account: [ADD] Field company_id on account.analytic.line [ADD] Function Field currency_id, amount_currency on account.analytic.line, on account.analytic.account currency_id [ADD] Function field currency_id on account.analytic.account = company currency [API] Change the on_change_unit_amount signature, adding date and company_id [IMP] Change the on_change_unit_amount funtcion to base cost price on valuation instead of standard price. Product: [ADD] Add a new property called property_valuation_pricelist into res.company. purpose of this pricelist is to compute the cost price for a given y. Hr_timesheet: [API] Change the on_change_unit_amount signature, adding date Hr_timesheet_invoice [IMP] Function field currency_id of account.analytic.account base computation pricelist currency instead of company currency Hr_timesheet_sheet: [IMP] Add support of new signature for on_change_unit_amount bzr revid: joel.grandguillaume@camptocamp.com-20091217141332-23u4v79m3pbst7u9 --- addons/account/account_analytic_line.py | 57 +++++++++++++++++-- addons/account/project/project.py | 5 ++ addons/account/project/project_view.xml | 26 ++++++--- addons/hr_timesheet/hr_timesheet.py | 8 ++- addons/hr_timesheet/hr_timesheet_view.xml | 12 ++-- .../hr_timesheet_invoice.py | 19 +++++-- .../hr_timesheet_invoice_view.xml | 14 ++++- .../hr_timesheet_sheet_view.xml | 8 +-- addons/product/__init__.py | 2 +- addons/product/__terp__.py | 1 + addons/product/company.py | 53 +++++++++++++++++ addons/product/company_view.xml | 18 ++++++ addons/product/product_data.xml | 28 ++++++++- 13 files changed, 216 insertions(+), 35 deletions(-) create mode 100644 addons/product/company.py create mode 100644 addons/product/company_view.xml diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 3e3266044b4..7b0723ca07f 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -24,10 +24,38 @@ import time from osv import fields from osv import osv from tools.translate import _ +import tools +from tools import config class account_analytic_line(osv.osv): _name = 'account.analytic.line' _description = 'Analytic lines' + + def _amount_currency(self, cr, uid, ids, field_name, arg, context={}): + result = {} + for rec in self.browse(cr, uid, ids, context): + cmp_cur_id=rec.company_id.currency_id.id + aa_cur_id=rec.account_id.currency_id.id + result[rec.id] = 0.0 + if cmp_cur_id <> aa_cur_id: + cur_obj = self.pool.get('res.currency') + ctx = {} + if rec.date and rec.amount: + ctx['date'] = rec.date + result[rec.id] = cur_obj.compute(cr, uid, rec.company_id.currency_id.id, + rec.account_id.currency_id.id, rec.amount, + context=ctx) + return result + + def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}): + result = {} + for rec in self.browse(cr, uid, ids, context): + if rec.company_id.currency_id.id <> rec.account_id.currency_id.id: + result[rec.id] = (rec.account_id.currency_id.id,rec.account_id.currency_id.code) + else: + result[rec.id] = False + return result + _columns = { 'name' : fields.char('Description', size=256, required=True), 'date' : fields.date('Date', required=True), @@ -42,13 +70,21 @@ class account_analytic_line(osv.osv): 'code' : fields.char('Code', size=8), 'user_id' : fields.many2one('res.users', 'User',), 'ref': fields.char('Ref.', size=32), + 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Currency', + store=True, help="The related account currency if not equal to the company one."), + 'company_id': fields.many2one('res.company','Company',required=True), + 'amount_currency': fields.function(_amount_currency, method=True, digits=(16, int(config['price_accuracy'])), string='Amount currency', + store=True, + help="The amount expressed in the related account currency if not equal to the company one.", + ), + } _defaults = { 'date': lambda *a: time.strftime('%Y-%m-%d'), + 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', c), } _order = 'date' - def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): if context is None: context = {} @@ -72,8 +108,10 @@ class account_analytic_line(osv.osv): # (_check_company, 'You can not create analytic line that is not in the same company than the account line', ['account_id']) ] - def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, - unit=False, context=None): + # Compute the cost based on the valuation pricelist define into company + # property_product_pricelist property + def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount,company_id, + date,unit=False, context=None): uom_obj = self.pool.get('product.uom') product_obj = self.pool.get('product.product') # if unit_amount and prod_id: @@ -87,8 +125,17 @@ class account_analytic_line(osv.osv): _('There is no expense account defined ' \ 'for this product: "%s" (id:%d)') % \ (prod.name, prod.id,)) - amount = unit_amount * uom_obj._compute_price(cr, uid, - prod.uom_id.id, prod.standard_price, unit) + if company_id: + company_obj = self.pool.get('res.company') + else: + company_id=self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context) + pricelist_id=company_obj.browse(cr,uid,company_id).property_valuation_pricelist.id + amount_unit = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], + prod_id, unit_amount or 1.0, None, { + 'uom': unit, + 'date': date, + })[pricelist_id] + amount=amount_unit*unit_amount or 1.0 return {'value': { 'amount': - round(amount, 2), 'general_account_id': a, diff --git a/addons/account/project/project.py b/addons/account/project/project.py index 51431ee1be1..d6d68eee76f 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -167,6 +167,10 @@ class account_analytic_account(osv.osv): result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) or False return result + def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}): + result=self._get_company_currency(cr, uid, ids, field_name, arg, context={}) + return result + _columns = { 'name' : fields.char('Account Name', size=64, required=True), 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Full Account Name'), @@ -194,6 +198,7 @@ class account_analytic_account(osv.osv): \n* If any associated partner is there, it can be in \'Open\' state.\ \n* If any pending balance is there it can be in \'Pending\'. \ \n* And finally when all the transactions are over, it can be in \'Close\' state.'), + 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True), } def _default_company(self, cr, uid, context={}): diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index accbbf52d53..ef4da006e49 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -50,6 +50,7 @@ + @@ -134,6 +135,9 @@ + + + @@ -146,16 +150,19 @@ tree - + - - + + - + + + + @@ -212,13 +219,16 @@
- + - - - + + + + + + diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index 652814b6e02..278b1d0357c 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -53,11 +53,13 @@ class hr_analytic_timesheet(osv.osv): return super(hr_analytic_timesheet, self).unlink(cr, uid, ids, context) - def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, unit, context={}): + def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, date, unit, context={}): res = {} # if prod_id and unit_amount: - if prod_id: - res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,unit, context) + if prod_id and date: + # find company + company_id=self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context) + res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,company_id,date,unit, context) return res def _getEmployeeProduct(self, cr, uid, context): diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml index 49ba1d602ad..ab07c4d1633 100644 --- a/addons/hr_timesheet/hr_timesheet_view.xml +++ b/addons/hr_timesheet/hr_timesheet_view.xml @@ -11,9 +11,9 @@ - - - + + + @@ -29,10 +29,10 @@ - + - - + + diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index 182d8843b8c..3236807c928 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -52,8 +52,15 @@ class account_analytic_account(osv.osv): res[id] = round(res.get(id, 0.0),2) return res + def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}): + result=super(account_analytic_account, self)._get_account_currency(cr, uid, ids, field_name, arg, context) + for rec in self.browse(cr, uid, ids, context): + result[rec.id] = rec.pricelist_id and (rec.pricelist_id.currency_id.id,rec.pricelist_id.currency_id.code) or result[rec.id] + return result + _inherit = "account.analytic.account" _columns = { + 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True), 'pricelist_id' : fields.many2one('product.pricelist', 'Sale Pricelist'), 'amount_max': fields.float('Max. Invoice Price'), 'amount_invoiced': fields.function(_invoiced_calc, method=True, string='Invoiced Amount', @@ -77,7 +84,7 @@ class account_analytic_line(osv.osv): } def unlink(self, cursor, user, ids, context=None): - self._check(cursor, user, ids) + # self._check(cursor, user, ids) return super(account_analytic_line,self).unlink(cursor, user, ids, context=context) @@ -90,11 +97,11 @@ class account_analytic_line(osv.osv): select = ids if isinstance(select, (int, long)): select = [ids] - if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False: - for line in self.browse(cr, uid, select): - if line.invoice_id: - raise osv.except_osv(_('Error !'), - _('You can not modify an invoiced analytic line!')) + if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False: + for line in self.browse(cr, uid, select): + if line.invoice_id: + raise osv.except_osv(_('Error !'), + _('You can not modify an invoiced analytic line!')) return True def copy(self, cursor, user, obj_id, default=None, context=None): diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml index 4ff5ac27c95..3ca932f6bcd 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml @@ -21,7 +21,19 @@ - + + + account.analytic.account.form + account.analytic.account + + form + + + + + + + hr.analytic.timesheet.form hr.analytic.timesheet diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index be40d8bacef..5452ccf790b 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -118,11 +118,11 @@ - + - + @@ -131,11 +131,11 @@ - + - + diff --git a/addons/product/__init__.py b/addons/product/__init__.py index 3f95fcb1900..da66b46efec 100644 --- a/addons/product/__init__.py +++ b/addons/product/__init__.py @@ -23,6 +23,6 @@ import pricelist import report import partner import wizard - +import company # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product/__terp__.py b/addons/product/__terp__.py index 3069a901577..183973a2275 100644 --- a/addons/product/__terp__.py +++ b/addons/product/__terp__.py @@ -54,6 +54,7 @@ 'product_view.xml', 'pricelist_view.xml', 'partner_view.xml', + 'company_view.xml', 'product_wizard.xml', 'process/product_process.xml' ], diff --git a/addons/product/company.py b/addons/product/company.py new file mode 100644 index 00000000000..32c4f3bccd2 --- /dev/null +++ b/addons/product/company.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from osv import fields, osv + + +class res_company(osv.osv): + _inherit = 'res.company' + _columns = { + 'property_valuation_pricelist': fields.property( + 'product.pricelist', + type='many2one', + relation='product.pricelist', + domain=[('type','=','valuation')], + string="Valuation Pricelist", + method=True, + view_load=True, + help="This pricelist will be used, instead of the default one, \ + for valuation to the current company"), + } + + def _check_currency(self, cr, uid, ids): + for rec in self.browse(cr, uid, ids): + if rec.currency_id.id <> rec.property_valuation_pricelist.currency_id.id: + return False + return True + + _constraints = [ + (_check_currency, 'Error! You can not chooes a pricelist in a different currency than your company.', ['property_valuation_pricelist']) + ] + +res_company() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/product/company_view.xml b/addons/product/company_view.xml new file mode 100644 index 00000000000..5ccd5982de4 --- /dev/null +++ b/addons/product/company_view.xml @@ -0,0 +1,18 @@ + + + + + + res.company.product.property.form.inherit + res.company + form + + + + + + + + + + diff --git a/addons/product/product_data.xml b/addons/product/product_data.xml index 2f000fdbfc6..452253f0c96 100644 --- a/addons/product/product_data.xml +++ b/addons/product/product_data.xml @@ -73,7 +73,6 @@ parameter) will see those record just disappear. - @@ -81,6 +80,11 @@ parameter) will see those record just disappear. Sale Pricelist sale + + + Valuation Pricelist + valuation + @@ -107,5 +126,12 @@ parameter) will see those record just disappear. + + + property_valuation_pricelist + + + + From d3c8dc2db40be0aa9cdc04ba60e1b9b6119e6810 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Thu, 17 Dec 2009 16:12:33 +0100 Subject: [PATCH 002/328] [IMP] Add computation of stock entries based on valuation priclist of company for accounting entries bzr revid: joel.grandguillaume@camptocamp.com-20091217151233-85fnrq17pt2o7w7n --- .../hr_timesheet_invoice/hr_timesheet_invoice.py | 12 ++++++------ addons/stock/stock.py | 14 +++++++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index 3236807c928..be0b8c4ccad 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -57,7 +57,7 @@ class account_analytic_account(osv.osv): for rec in self.browse(cr, uid, ids, context): result[rec.id] = rec.pricelist_id and (rec.pricelist_id.currency_id.id,rec.pricelist_id.currency_id.code) or result[rec.id] return result - + _inherit = "account.analytic.account" _columns = { 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True), @@ -97,11 +97,11 @@ class account_analytic_line(osv.osv): select = ids if isinstance(select, (int, long)): select = [ids] - if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False: - for line in self.browse(cr, uid, select): - if line.invoice_id: - raise osv.except_osv(_('Error !'), - _('You can not modify an invoiced analytic line!')) + if ( not vals.has_key('invoice_id')) or vals['invoice_id' ] == False: + for line in self.browse(cr, uid, select): + if line.invoice_id: + raise osv.except_osv(_('Error !'), + _('You can not modify an invoiced analytic line!')) return True def copy(self, cursor, user, obj_id, default=None, context=None): diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 8b5356a8573..01ed2c62fbe 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1321,13 +1321,21 @@ class stock_move(osv.osv): ref = move.picking_id and move.picking_id.name or False product_uom_obj = self.pool.get('product.uom') default_uom = move.product_id.uom_id.id + date = time.strftime('%Y-%m-%d') q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom) if move.product_id.cost_method == 'average' and move.price_unit: amount = q * move.price_unit + # Base computation on valuation pricelist else: - amount = q * move.product_id.standard_price - - date = time.strftime('%Y-%m-%d') + company_id=move.company_id.id + pricelist_id = self.pool.get('res.company').browse(cr,uid,company_id).property_valuation_pricelist.id + amount_unit = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], + move.product_id.id, q or 1.0, None, { + 'date': date, + })[pricelist_id] + amount=amount_unit * q or 1.0 + # amount = q * move.product_id.standard_price + partner_id = False if move.picking_id: 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 From a7c8200828c0a01fc6214067c182f816c093b74e Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Thu, 17 Dec 2009 17:13:33 +0100 Subject: [PATCH 003/328] [FIX] Change the computation of _total_cost_calc for account_analytic_analysis bzr revid: joel.grandguillaume@camptocamp.com-20091217161333-zickpw2wyjf7fzx5 --- .../account_analytic_analysis.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index bbd976f8c29..8876928caa6 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -171,9 +171,19 @@ class account_analytic_account(osv.osv): on account_analytic_line.journal_id = account_analytic_journal.id \ where account_analytic_line.account_id IN (%s) \ and amount<0 \ + and amount_currency=0.0 \ GROUP BY account_analytic_line.account_id"""%acc_set) for account_id, sum in cr.fetchall(): res[account_id] = round(sum,2) + cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount_currency),0.0) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and amount_currency<0 \ + GROUP BY account_analytic_line.account_id"""%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] += round(sum,2) for obj_id in ids: res.setdefault(obj_id, 0.0) for child_id in self.search(cr, uid, From 97d33b0da54e12295ea5f836b9ad26ff27eed9e7 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Thu, 17 Dec 2009 17:42:53 +0100 Subject: [PATCH 004/328] [IMP] Add trigger on analytic line, so currency and amount currency are recomputed if company change [REM] Remove the currency_id of analytic account to be computed based on sale pricelist, it's now the reported company currency_id [ADD] some demo to demo data provided by multicompany module bzr revid: joel.grandguillaume@camptocamp.com-20091217164253-3jwugrn14mkj21pe --- addons/account/account_analytic_line.py | 25 ++++++++++++++----- .../account_analytic_analysis.py | 5 +++- .../hr_timesheet_invoice.py | 12 ++++----- addons/multi_company/multi_company_demo.xml | 24 ++++++++++++++++++ 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 7b0723ca07f..e3332d15ca0 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -55,7 +55,16 @@ class account_analytic_line(osv.osv): else: result[rec.id] = False return result - + + def _get_account_line(self, cr, uid, ids, context={}): + aac_ids = {} + for acc in self.pool.get('account.analytic.account').browse(cr, uid, ids): + aac_ids[acc.id] = True + aal_ids = [] + if aac_ids: + aal_ids = self.pool.get('account.analytic.line').search(cr, uid, [('account_id','in',aac_ids.keys())], context=context) + return aal_ids + _columns = { 'name' : fields.char('Description', size=256, required=True), 'date' : fields.date('Date', required=True), @@ -70,13 +79,17 @@ class account_analytic_line(osv.osv): 'code' : fields.char('Code', size=8), 'user_id' : fields.many2one('res.users', 'User',), 'ref': fields.char('Ref.', size=32), - 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Currency', - store=True, help="The related account currency if not equal to the company one."), + 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', + store={ + 'account.analytic.account': (_get_account_line, ['company_id'], 50), + }, + help="The related account currency if not equal to the company one."), 'company_id': fields.many2one('res.company','Company',required=True), 'amount_currency': fields.function(_amount_currency, method=True, digits=(16, int(config['price_accuracy'])), string='Amount currency', - store=True, - help="The amount expressed in the related account currency if not equal to the company one.", - ), + store={ + 'account.analytic.account': (_get_account_line, ['company_id'], 50), + }, + help="The amount expressed in the related account currency if not equal to the company one.",), } _defaults = { diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 8876928caa6..425cde301d3 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -183,7 +183,10 @@ class account_analytic_account(osv.osv): and amount_currency<0 \ GROUP BY account_analytic_line.account_id"""%acc_set) for account_id, sum in cr.fetchall(): - res[account_id] += round(sum,2) + if res.has_key(account_id): + res[account_id] += round(sum,2) + else: + res[account_id] = round(sum,2) for obj_id in ids: res.setdefault(obj_id, 0.0) for child_id in self.search(cr, uid, diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index be0b8c4ccad..2a44dc4c5f8 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -52,15 +52,15 @@ class account_analytic_account(osv.osv): res[id] = round(res.get(id, 0.0),2) return res - def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}): - result=super(account_analytic_account, self)._get_account_currency(cr, uid, ids, field_name, arg, context) - for rec in self.browse(cr, uid, ids, context): - result[rec.id] = rec.pricelist_id and (rec.pricelist_id.currency_id.id,rec.pricelist_id.currency_id.code) or result[rec.id] - return result + # def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}): + # result=super(account_analytic_account, self)._get_account_currency(cr, uid, ids, field_name, arg, context) + # for rec in self.browse(cr, uid, ids, context): + # result[rec.id] = rec.pricelist_id and (rec.pricelist_id.currency_id.id,rec.pricelist_id.currency_id.code) or result[rec.id] + # return result _inherit = "account.analytic.account" _columns = { - 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True), + # 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True), 'pricelist_id' : fields.many2one('product.pricelist', 'Sale Pricelist'), 'amount_max': fields.float('Max. Invoice Price'), 'amount_invoiced': fields.function(_invoiced_calc, method=True, string='Invoiced Amount', diff --git a/addons/multi_company/multi_company_demo.xml b/addons/multi_company/multi_company_demo.xml index 7f18b7edd86..292cae0a0b5 100644 --- a/addons/multi_company/multi_company_demo.xml +++ b/addons/multi_company/multi_company_demo.xml @@ -1,6 +1,24 @@ + + + + + Valuation Pricelist USD + valuation + + + + + Default Valuation Pricelist Version USD + + + + + Default Valuation Pricelist Line USD + + OpenERP Corp. @@ -29,26 +47,32 @@ OpenERP Editor + OpenERP US + + OpenERP BE + OpenERP IN OpenERP IN + Odoo + From ee076f30df8f0a87b3ba461a2087ed49cafdef9a Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 08:52:56 +0100 Subject: [PATCH 005/328] [FIX] Correct typo into account in order to allow compilation bzr revid: joel.grandguillaume@camptocamp.com-20091223075256-k6tmy2wbc8delay7 --- addons/account/account_analytic_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 335cdd7f22e..4ae603a5438 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -88,7 +88,7 @@ class account_analytic_line(osv.osv): store={ 'account.analytic.account': (_get_account_line, ['company_id'], 50), }, - help="The amount expressed in the related account currency if not equal to the company one.",) + help="The amount expressed in the related account currency if not equal to the company one."), 'ref': fields.char('Reference', size=32), } _defaults = { From 497c1bed05c4f395944655d82a5bfc5a95866c67 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 09:51:37 +0100 Subject: [PATCH 006/328] [FIX] Correct debit, credit and balance on analytic account in order to give it in account currency [IMP] Always compute amount in currency, to allow debit,credit,balance computation bzr revid: joel.grandguillaume@camptocamp.com-20091223085137-pzuzatuknoluuhuh --- addons/account/account_analytic_line.py | 10 +++++----- addons/account/project/project.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 4ae603a5438..473a60f921b 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -36,7 +36,7 @@ class account_analytic_line(osv.osv): for rec in self.browse(cr, uid, ids, context): cmp_cur_id=rec.company_id.currency_id.id aa_cur_id=rec.account_id.currency_id.id - result[rec.id] = 0.0 + # Always provide the amount in currency if cmp_cur_id <> aa_cur_id: cur_obj = self.pool.get('res.currency') ctx = {} @@ -45,15 +45,15 @@ class account_analytic_line(osv.osv): result[rec.id] = cur_obj.compute(cr, uid, rec.company_id.currency_id.id, rec.account_id.currency_id.id, rec.amount, context=ctx) + else: + result[rec.id]=rec.amount return result def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}): result = {} for rec in self.browse(cr, uid, ids, context): - if rec.company_id.currency_id.id <> rec.account_id.currency_id.id: - result[rec.id] = (rec.account_id.currency_id.id,rec.account_id.currency_id.code) - else: - result[rec.id] = False + # Always provide second currency + result[rec.id] = (rec.account_id.currency_id.id,rec.account_id.currency_id.code) return result def _get_account_line(self, cr, uid, ids, context={}): diff --git a/addons/account/project/project.py b/addons/account/project/project.py index 469b3aab874..5fa9e8c96ff 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -42,7 +42,7 @@ class account_analytic_account(osv.osv): if context.get('to_date',False): where_date += " AND l.date <= '" + context['to_date'] + "'" - 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 %s) WHERE l.amount<0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) + 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 %s) WHERE l.amount_currency<0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) r = dict(cr.fetchall()) for i in ids: r.setdefault(i,0.0) @@ -58,7 +58,7 @@ class account_analytic_account(osv.osv): if context.get('to_date',False): where_date += " AND l.date <= '" + context['to_date'] + "'" - 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 %s) WHERE l.amount>0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) + 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 %s) WHERE l.amount_currency>0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) r= dict(cr.fetchall()) for i in ids: r.setdefault(i,0.0) @@ -81,7 +81,7 @@ class account_analytic_account(osv.osv): if context.get('to_date',False): where_date += " AND l.date <= '" + context['to_date'] + "'" - 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 %s) WHERE a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) + 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 %s) WHERE a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) for account_id, sum in cr.fetchall(): res[account_id] = sum From d8500896c936e10fdf157492aa2ed2787b47fca7 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 10:55:20 +0100 Subject: [PATCH 007/328] [IMP] Add trigger on account.analytic.line for currency change [IMP] Refactoring the _balance_calc in project.py [FIX] Use the new refactored funtion to compute indicator in right the currency according to each level of analytical account bzr revid: joel.grandguillaume@camptocamp.com-20091223095520-8gsqff2w7z0yrc7z --- addons/account/account_analytic_line.py | 14 +++-- addons/account/project/project.py | 52 ++++++++++--------- .../account_analytic_analysis.py | 43 +++------------ 3 files changed, 45 insertions(+), 64 deletions(-) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 473a60f921b..34a422579fe 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -81,14 +81,18 @@ class account_analytic_line(osv.osv): 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store={ 'account.analytic.account': (_get_account_line, ['company_id'], 50), + 'account.analytic.line': (lambda self,cr,uid,ids,c={}: ids, ['amount','unit_amount'],10), }, - help="The related account currency if not equal to the company one."), + # multi='all', + help="The related account currency if not equal to the company one."), 'company_id': fields.many2one('res.company','Company',required=True), 'amount_currency': fields.function(_amount_currency, method=True, digits=(16, int(config['price_accuracy'])), string='Amount currency', - store={ - 'account.analytic.account': (_get_account_line, ['company_id'], 50), - }, - help="The amount expressed in the related account currency if not equal to the company one."), + store={ + 'account.analytic.account': (_get_account_line, ['company_id'], 50), + 'account.analytic.line': (lambda self,cr,uid,ids,c={}: ids, ['amount','unit_amount'],10), + }, + # multi='all', + help="The amount expressed in the related account currency if not equal to the company one."), 'ref': fields.char('Reference', size=32), } _defaults = { diff --git a/addons/account/project/project.py b/addons/account/project/project.py index 5fa9e8c96ff..38d7e63ed30 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -33,6 +33,32 @@ class account_analytic_account(osv.osv): _name = 'account.analytic.account' _description = 'Analytic Accounts' + def _compute_currency_for_level_tree(self, cr, uid, ids, ids2, res, acc_set, context={}): + # Handle multi-currency on each level of analytic account + # This is a refactoring of _balance_calc computation + 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) + currency= dict(cr.fetchall()) + res_currency= self.pool.get('res.currency') + for id in ids: + if id not in ids2: + continue + for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): + if child <> id: + res.setdefault(id, 0.0) + if currency[child]<>currency[id]: + res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context) + else: + res[id] += res.get(child, 0.0) + + cur_obj = res_currency.browse(cr,uid,currency.values(),context) + cur_obj = dict([(o.id, o) for o in cur_obj]) + for id in ids: + if id in ids2: + res[id] = res_currency.round(cr,uid,cur_obj[currency[id]],res.get(id,0.0)) + + return dict([(i, res[i]) for i in ids ]) + + def _credit_calc(self, cr, uid, ids, name, arg, context={}): acc_set = ",".join(map(str, ids)) @@ -86,30 +112,8 @@ class account_analytic_account(osv.osv): for account_id, sum in cr.fetchall(): res[account_id] = sum - 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) - - currency= dict(cr.fetchall()) - - res_currency= self.pool.get('res.currency') - for id in ids: - if id not in ids2: - continue - for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): - if child <> id: - res.setdefault(id, 0.0) - if currency[child]<>currency[id]: - res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context) - else: - res[id] += res.get(child, 0.0) - - cur_obj = res_currency.browse(cr,uid,currency.values(),context) - cur_obj = dict([(o.id, o) for o in cur_obj]) - for id in ids: - if id in ids2: - res[id] = res_currency.round(cr,uid,cur_obj[currency[id]],res.get(id,0.0)) - - return dict([(i, res[i]) for i in ids ]) - + return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context) + def _quantity_calc(self, cr, uid, ids, name, arg, context={}): #XXX must convert into one uom res = {} diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 425cde301d3..e623b1c94f7 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -35,7 +35,7 @@ class account_analytic_account(osv.osv): ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) if ids2: acc_set = ",".join(map(str, ids2)) - cr.execute("select account_analytic_line.account_id, COALESCE(sum(amount),0.0) \ + cr.execute("select account_analytic_line.account_id, COALESCE(sum(amount_currency),0.0) \ from account_analytic_line \ join account_analytic_journal \ on account_analytic_line.journal_id = account_analytic_journal.id \ @@ -44,15 +44,8 @@ class account_analytic_account(osv.osv): group by account_analytic_line.account_id" % acc_set) for account_id, sum in cr.fetchall(): res[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res + + return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context) def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context={}): res = {} @@ -165,38 +158,18 @@ class account_analytic_account(osv.osv): ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) if ids2: acc_set = ",".join(map(str, ids2)) - cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount),0.0) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and amount<0 \ - and amount_currency=0.0 \ - GROUP BY account_analytic_line.account_id"""%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount_currency),0.0) \ from account_analytic_line \ join account_analytic_journal \ on account_analytic_line.journal_id = account_analytic_journal.id \ where account_analytic_line.account_id IN (%s) \ - and amount_currency<0 \ + and amount<0 \ + GROUP BY account_analytic_line.account_id"""%acc_set) for account_id, sum in cr.fetchall(): - if res.has_key(account_id): - res[account_id] += round(sum,2) - else: - res[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - + res[account_id] = round(sum,2) + return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context) + def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}): res = {} res2 = {} From 4a628b63d7f4cb180af0cff3d3ad57c0ad51cfb1 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 11:38:37 +0100 Subject: [PATCH 008/328] [COM] All indicators in account analytic analysis are now well computed, according to account currency, except theorical revenue and amount to invoice because they don't take care of pricelist neither account currency. bzr revid: joel.grandguillaume@camptocamp.com-20091223103837-etie8lovwb2twwbh --- .../account_analytic_analysis.py | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index e623b1c94f7..3178391b918 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -53,6 +53,9 @@ class account_analytic_account(osv.osv): ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) if ids2: # Amount uninvoiced hours to invoice at sale price + # Warnning + # This computation doesn't take care of pricelist ! + # Just consider list_price acc_set = ",".join(map(str, ids2)) cr.execute("""SELECT account_analytic_account.id, \ COALESCE(sum (product_template.list_price * \ @@ -89,6 +92,7 @@ class account_analytic_account(osv.osv): # GROUP BY account_analytic_line.account_id;"%acc_set) #for account_id, sum in cr.fetchall(): # res2[account_id] = round(sum,2) + for obj_id in ids: res.setdefault(obj_id, 0.0) res2.setdefault(obj_id, 0.0) @@ -169,11 +173,81 @@ class account_analytic_account(osv.osv): for account_id, sum in cr.fetchall(): res[account_id] = round(sum,2) return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context) + + # TODO Take care of pricelist and purchase ! + # def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}): + # res = {} + # res2 = {} + # date = time.strftime('%Y-%m-%d') + # ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + # # Take care of pricelist unit_price on all hours + # if ids2: + # acc_set = ",".join(map(str, ids2)) + # cr.execute("""select account_analytic_line.account_id as account_id, \ + # COALESCE(sum((account_analytic_line.unit_amount * pt.list_price) \ + # - (account_analytic_line.unit_amount * pt.list_price \ + # * hr.factor)),0.0) as somme,\ + # account_analytic_line.unit_amount as qty, + # a.pricelist_id as pricelist, + # account_analytic_line.product_id as product, + # a.partner_id as partner_id + # from account_analytic_line \ + # left join account_analytic_journal \ + # on (account_analytic_line.journal_id = account_analytic_journal.id) \ + # join product_product pp \ + # on (account_analytic_line.product_id = pp.id) \ + # join product_template pt \ + # on (pp.product_tmpl_id = pt.id) \ + # join account_analytic_account a \ + # on (a.id=account_analytic_line.account_id) \ + # join hr_timesheet_invoice_factor hr \ + # on (hr.id=a.to_invoice) \ + # where account_analytic_line.account_id IN (%s) \ + # and a.to_invoice IS NOT NULL \ + # and account_analytic_journal.type in ('purchase','general') + # GROUP BY account_analytic_line.account_id,"""%acc_set) + # # Compute unit amount with pricelist for given qty and product + # for account_id, sum, qty, pricelist,product, partner_id in cr.fetchall(): + # # If no product, no pricelist or no partner_id, no need to compute + # if not pricelist or not product or not partner_id: + # res2[account_id] = 0,0 + # else: + # unit_price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], + # product, qty or 1.0, partner_id, { + # 'date': date, + # })[pricelist] + # if unit_price is False: + # warning = { + # 'title': 'No valid pricelist line found !', + # 'message': + # "Couldn't find a pricelist line matching this product and quantity.\n" + # "You have to change either the product, the quantity or the pricelist." + # } + # else: + # res2[account_id] = round(unit_price,2) + # + # for obj_id in ids: + # res.setdefault(obj_id, 0.0) + # res2.setdefault(obj_id, 0.0) + # for child_id in self.search(cr, uid, + # [('parent_id', 'child_of', [obj_id])]): + # if child_id != obj_id: + # res[obj_id] += res.get(child_id, 0.0) + # res[obj_id] += res2.get(child_id, 0.0) + # + # # sum both result on account_id + # for id in ids: + # res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2) + # return res + def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}): res = {} res2 = {} ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + # Warnning + # This computation doesn't take care of pricelist ! + # Just consider list_price if ids2: acc_set = ",".join(map(str, ids2)) cr.execute("""select account_analytic_line.account_id as account_id, \ @@ -197,7 +271,7 @@ class account_analytic_account(osv.osv): GROUP BY account_analytic_line.account_id"""%acc_set) for account_id, sum in cr.fetchall(): res2[account_id] = round(sum,2) - + for obj_id in ids: res.setdefault(obj_id, 0.0) res2.setdefault(obj_id, 0.0) @@ -206,7 +280,7 @@ class account_analytic_account(osv.osv): if child_id != obj_id: res[obj_id] += res.get(child_id, 0.0) res[obj_id] += res2.get(child_id, 0.0) - + # sum both result on account_id for id in ids: res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2) From ffd766bd42301213e351d2b3a068ee3faf674211 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 16:28:06 +0100 Subject: [PATCH 009/328] [IMP] Change pricelist for price type into company property [IMP] Change all valuation according to price type instead of pricelist [IMP] Compute the aveerage price based on the define price type in the company, this allow company to have different cost price bzr revid: joel.grandguillaume@camptocamp.com-20091223152806-cacd3e8loi7tea53 --- addons/account/account_analytic_line.py | 27 +++++++++---------- addons/hr_timesheet/wizard/sign_in_out.py | 2 +- addons/multi_company/__init__.py | 1 + addons/multi_company/__terp__.py | 4 ++- addons/multi_company/multi_company_demo.xml | 20 +++++--------- addons/product/company.py | 18 ++++++------- addons/product/company_view.xml | 2 +- addons/product/pricelist.py | 1 + addons/product/product_data.xml | 27 +++---------------- addons/stock/stock.py | 11 ++++---- addons/stock/wizard/wizard_partial_picking.py | 11 +++++--- 11 files changed, 50 insertions(+), 74 deletions(-) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 34a422579fe..0abe79f01c6 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -83,7 +83,6 @@ class account_analytic_line(osv.osv): 'account.analytic.account': (_get_account_line, ['company_id'], 50), 'account.analytic.line': (lambda self,cr,uid,ids,c={}: ids, ['amount','unit_amount'],10), }, - # multi='all', help="The related account currency if not equal to the company one."), 'company_id': fields.many2one('res.company','Company',required=True), 'amount_currency': fields.function(_amount_currency, method=True, digits=(16, int(config['price_accuracy'])), string='Amount currency', @@ -91,7 +90,6 @@ class account_analytic_line(osv.osv): 'account.analytic.account': (_get_account_line, ['company_id'], 50), 'account.analytic.line': (lambda self,cr,uid,ids,c={}: ids, ['amount','unit_amount'],10), }, - # multi='all', help="The amount expressed in the related account currency if not equal to the company one."), 'ref': fields.char('Reference', size=32), } @@ -124,13 +122,15 @@ class account_analytic_line(osv.osv): # (_check_company, 'You can not create analytic line that is not in the same company than the account line', ['account_id']) ] - # Compute the cost based on the valuation pricelist define into company - # property_product_pricelist property + # Compute the cost based on the price type define into company + # property_valuation_price_type property def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount,company_id, date,unit=False, context=None): + if context==None: + context={} uom_obj = self.pool.get('product.uom') product_obj = self.pool.get('product.product') -# if unit_amount and prod_id: + company_obj=self.pool.get('res.company') if prod_id: prod = product_obj.browse(cr, uid, prod_id) a = prod.product_tmpl_id.property_account_expense.id @@ -141,16 +141,13 @@ class account_analytic_line(osv.osv): _('There is no expense account defined ' \ 'for this product: "%s" (id:%d)') % \ (prod.name, prod.id,)) - if company_id: - company_obj = self.pool.get('res.company') - else: - company_id=self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context) - pricelist_id=company_obj.browse(cr,uid,company_id).property_valuation_pricelist.id - amount_unit = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], - prod_id, unit_amount or 1.0, None, { - 'uom': unit, - 'date': date, - })[pricelist_id] + if not company_id: + company_id=company_obj._company_default_get(cr, uid, 'account.analytic.line', context) + + # Compute based on pricetype + pricetype=self.pool.get('product.price.type').browse(cr,uid,company_obj.browse(cr,uid,company_id).property_valuation_price_type.id) + amount_unit=prod.price_get(pricetype.field, context)[prod.id] + amount=amount_unit*unit_amount or 1.0 return {'value': { 'amount': - round(amount, 2), diff --git a/addons/hr_timesheet/wizard/sign_in_out.py b/addons/hr_timesheet/wizard/sign_in_out.py index 69be06a4b88..ac2f9d3d5c4 100644 --- a/addons/hr_timesheet/wizard/sign_in_out.py +++ b/addons/hr_timesheet/wizard/sign_in_out.py @@ -106,7 +106,7 @@ def _write(self, cr, uid, data, emp_id, context): res = timesheet_obj.default_get(cr, uid, ['product_id','product_uom_id']) if not res['product_uom_id']: raise wizard.except_wizard(_('UserError'), _('No cost unit defined for this employee !')) - up = timesheet_obj.on_change_unit_amount(cr, uid, False, res['product_id'], hour, res['product_uom_id'])['value'] + up = timesheet_obj.on_change_unit_amount(cr, uid, False, res['product_id'], hour, data['form']['date'], res['product_uom_id'])['value'] res['name'] = data['form']['info'] res['account_id'] = data['form']['account_id'] res['unit_amount'] = hour diff --git a/addons/multi_company/__init__.py b/addons/multi_company/__init__.py index 0d29405d526..537dbb62253 100644 --- a/addons/multi_company/__init__.py +++ b/addons/multi_company/__init__.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # ############################################################################## +import product # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/multi_company/__terp__.py b/addons/multi_company/__terp__.py index 83d6afa777f..d541c91cf4a 100644 --- a/addons/multi_company/__terp__.py +++ b/addons/multi_company/__terp__.py @@ -32,10 +32,12 @@ 'depends': [ 'base', 'sale', - 'project' + 'project', + 'product', ], 'init_xml': [], 'update_xml': [ + "multi_company_view.xml", #'security/ir.model.access.csv', ], 'demo_xml': [ diff --git a/addons/multi_company/multi_company_demo.xml b/addons/multi_company/multi_company_demo.xml index 292cae0a0b5..f550d133b28 100644 --- a/addons/multi_company/multi_company_demo.xml +++ b/addons/multi_company/multi_company_demo.xml @@ -3,21 +3,13 @@ - - - Valuation Pricelist USD - valuation + + + Cost Price USD + standard_price_usd - - - Default Valuation Pricelist Version USD - - - - - Default Valuation Pricelist Line USD - + @@ -54,7 +46,7 @@ OpenERP US - + diff --git a/addons/product/company.py b/addons/product/company.py index 32c4f3bccd2..f23d010b600 100644 --- a/addons/product/company.py +++ b/addons/product/company.py @@ -25,26 +25,26 @@ from osv import fields, osv class res_company(osv.osv): _inherit = 'res.company' _columns = { - 'property_valuation_pricelist': fields.property( - 'product.pricelist', + 'property_valuation_price_type': fields.property( + 'product.price.type', type='many2one', - relation='product.pricelist', - domain=[('type','=','valuation')], - string="Valuation Pricelist", + relation='product.price.type', + domain=[], + string="Valuation Price Type", method=True, view_load=True, - help="This pricelist will be used, instead of the default one, \ - for valuation to the current company"), + help="The price type field in the selected price type will be used, instead of the default one, \ + for valuation of product in the current company"), } def _check_currency(self, cr, uid, ids): for rec in self.browse(cr, uid, ids): - if rec.currency_id.id <> rec.property_valuation_pricelist.currency_id.id: + if rec.currency_id.id <> rec.property_valuation_price_type.currency_id.id: return False return True _constraints = [ - (_check_currency, 'Error! You can not chooes a pricelist in a different currency than your company.', ['property_valuation_pricelist']) + (_check_currency, 'Error! You can not chooes a pricetype in a different currency than your company (Not supported now).', ['property_valuation_price_type']) ] res_company() diff --git a/addons/product/company_view.xml b/addons/product/company_view.xml index 5ccd5982de4..eb0294e2daa 100644 --- a/addons/product/company_view.xml +++ b/addons/product/company_view.xml @@ -9,7 +9,7 @@ - + diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 0849ee85dcd..857cdc9b89b 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -62,6 +62,7 @@ class price_type(osv.osv): "active": lambda *args: True, "currency_id": _get_currency } + price_type() #---------------------------------------------------------- diff --git a/addons/product/product_data.xml b/addons/product/product_data.xml index 452253f0c96..8037453101e 100644 --- a/addons/product/product_data.xml +++ b/addons/product/product_data.xml @@ -81,10 +81,6 @@ parameter) will see those record just disappear. sale - - Valuation Pricelist - valuation - @@ -127,10 +108,10 @@ parameter) will see those record just disappear. - - property_valuation_pricelist - - + + property_valuation_price_type + + diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 00db75ce0f5..afe2ff47ee3 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1328,14 +1328,13 @@ class stock_move(osv.osv): q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom) if move.product_id.cost_method == 'average' and move.price_unit: amount = q * move.price_unit - # Base computation on valuation pricelist + # Base computation on valuation price type else: company_id=move.company_id.id - pricelist_id = self.pool.get('res.company').browse(cr,uid,company_id).property_valuation_pricelist.id - amount_unit = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], - move.product_id.id, q or 1.0, None, { - 'date': date, - })[pricelist_id] + + pricetype=self.pool.get('product.price.type').browse(cr,uid,move.company_id.property_valuation_price_type.id) + amount_unit=move.product_id.price_get(cr, uid, ids, pricetype.field, context) + amount=amount_unit * q or 1.0 # amount = q * move.product_id.standard_price diff --git a/addons/stock/wizard/wizard_partial_picking.py b/addons/stock/wizard/wizard_partial_picking.py index 416558735ff..52714f45eb6 100644 --- a/addons/stock/wizard/wizard_partial_picking.py +++ b/addons/stock/wizard/wizard_partial_picking.py @@ -131,7 +131,7 @@ def _do_split(self, cr, uid, data, context): currency = data['form']['currency%s' % move.id] qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id) - + pricetype=pool.get('product.price.type').browse(cr,uid,user.company_id.property_valuation_price_type.id) if (qty > 0): new_price = currency_obj.compute(cr, uid, currency, user.company_id.currency_id.id, price) @@ -140,11 +140,14 @@ def _do_split(self, cr, uid, data, context): if product.qty_available<=0: new_std_price = new_price else: - new_std_price = ((product.standard_price * product.qty_available)\ + # Get the standard price + amount_unit=product.price_get(pricetype.field, context)[product.id] + new_std_price = ((amount_unit * product.qty_available)\ + (new_price * qty))/(product.qty_available + qty) - + + # Write the field according to price type field product_obj.write(cr, uid, [product.id], - {'standard_price': new_std_price}) + {pricetype.field: new_std_price}) move_obj.write(cr, uid, [move.id], {'price_unit': new_price}) for move in too_few: From 0793892ca416afcb4b2e489d8eadcc8bfedd5580 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 16:28:37 +0100 Subject: [PATCH 010/328] [IMP] Change the demo data according to last modification bzr revid: joel.grandguillaume@camptocamp.com-20091223152837-b46zaj4iv1ua02jb --- addons/multi_company/multi_company_view.xml | 20 +++++++++++++ addons/multi_company/product.py | 33 +++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 addons/multi_company/multi_company_view.xml create mode 100644 addons/multi_company/product.py diff --git a/addons/multi_company/multi_company_view.xml b/addons/multi_company/multi_company_view.xml new file mode 100644 index 00000000000..96a4695f576 --- /dev/null +++ b/addons/multi_company/multi_company_view.xml @@ -0,0 +1,20 @@ + + + + + + product.normal.form + product.product + form + + + + + + + + + + + + diff --git a/addons/multi_company/product.py b/addons/multi_company/product.py new file mode 100644 index 00000000000..988ef7ee39e --- /dev/null +++ b/addons/multi_company/product.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from osv import fields, osv +from tools import config + +class product_product(osv.osv): + _inherit = "product.product" + + _columns = { + 'standard_price_usd': fields.float('Cost Price USD', required=True, digits=(16, int(config['price_accuracy'])), + help="Product's cost in USDfor accounting stock valuation."), + } + +product_product() \ No newline at end of file From 90ddcec2d73d2770455b1b6a2eac8cf3c023251c Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 17:34:53 +0100 Subject: [PATCH 011/328] [API] Remove date from onchange_unit_amount from analytic line, cause they are no more used as we base computation on pricetype [FIX] Start to replace standard_price in everymodule with the right field according to company property bzr revid: joel.grandguillaume@camptocamp.com-20091223163453-1a1y2pu9kmrzbep4 --- addons/account/account_analytic_line.py | 2 +- addons/account/project/project_view.xml | 20 ++++++------ addons/hr_expense/hr_expense.py | 10 ++++-- addons/hr_expense/hr_expense_view.xml | 4 +-- addons/hr_timesheet/hr_timesheet.py | 4 +-- addons/hr_timesheet/hr_timesheet_view.xml | 12 +++---- addons/hr_timesheet/wizard/sign_in_out.py | 2 +- .../hr_timesheet_sheet_view.xml | 8 ++--- addons/project_timesheet/project_timesheet.py | 7 +++- addons/stock/stock.py | 32 +++++++++++++++---- addons/stock/wizard/wizard_partial_move.py | 9 ++++-- 11 files changed, 72 insertions(+), 38 deletions(-) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 0abe79f01c6..20ec20f3f32 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -125,7 +125,7 @@ class account_analytic_line(osv.osv): # Compute the cost based on the price type define into company # property_valuation_price_type property def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount,company_id, - date,unit=False, context=None): + unit=False, context=None): if context==None: context={} uom_obj = self.pool.get('product.uom') diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index a730a172f09..dbbfc0cc9c3 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -163,19 +163,19 @@ tree - + - - + + - + - + @@ -232,16 +232,16 @@
- + - - - + + + - + diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 4c2ca16ce0c..5064eef8ff1 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -212,12 +212,18 @@ class hr_expense_line(osv.osv): 'date_value' : lambda *a: time.strftime('%Y-%m-%d'), } _order = "sequence" - def onchange_product_id(self, cr, uid, ids, product_id, uom_id, context={}): + def onchange_product_id(self, cr, uid, ids, product_id, uom_id, employee_id, context={}): v={} if product_id: product=self.pool.get('product.product').browse(cr,uid,product_id, context=context) v['name']=product.name - v['unit_amount']=product.standard_price + + # Compute based on pricetype of employee company + pricetype_id = self.pool.get('hr.employee').browse(cr,uid,employee_id).user_id.company_id.property_valuation_price_type.id + pricetype=self.pool.get('product.price.type').browse(cr,uid,pricetype_id) + amount_unit=product.price_get(pricetype.field, context)[product.id] + + v['unit_amount']=amount_unit if not uom_id: v['uom_id']=product.uom_id.id return {'value':v} diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index 184f95a4c3f..3d0e3dde899 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -58,8 +58,8 @@
- - + + diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index 1f4f8d2aadb..bf0e0d1426b 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -53,13 +53,13 @@ class hr_analytic_timesheet(osv.osv): return super(hr_analytic_timesheet, self).unlink(cr, uid, ids, context) - def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, date, unit, context={}): + def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, unit, context={}): res = {} # if prod_id and unit_amount: if prod_id and date: # find company company_id=self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context) - res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,company_id,date,unit, context) + res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,company_id,unit, context) return res def _getEmployeeProduct(self, cr, uid, context): diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml index 976bd64f5fb..0650889f182 100644 --- a/addons/hr_timesheet/hr_timesheet_view.xml +++ b/addons/hr_timesheet/hr_timesheet_view.xml @@ -11,9 +11,9 @@ - - - + + + @@ -29,10 +29,10 @@ - + - - + + diff --git a/addons/hr_timesheet/wizard/sign_in_out.py b/addons/hr_timesheet/wizard/sign_in_out.py index ac2f9d3d5c4..69be06a4b88 100644 --- a/addons/hr_timesheet/wizard/sign_in_out.py +++ b/addons/hr_timesheet/wizard/sign_in_out.py @@ -106,7 +106,7 @@ def _write(self, cr, uid, data, emp_id, context): res = timesheet_obj.default_get(cr, uid, ['product_id','product_uom_id']) if not res['product_uom_id']: raise wizard.except_wizard(_('UserError'), _('No cost unit defined for this employee !')) - up = timesheet_obj.on_change_unit_amount(cr, uid, False, res['product_id'], hour, data['form']['date'], res['product_uom_id'])['value'] + up = timesheet_obj.on_change_unit_amount(cr, uid, False, res['product_id'], hour, res['product_uom_id'])['value'] res['name'] = data['form']['info'] res['account_id'] = data['form']['account_id'] res['unit_amount'] = hour diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index a92c6575de9..e0603037b03 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -118,11 +118,11 @@ - + - + @@ -131,11 +131,11 @@ - + - + diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 03d463b8269..1dfda98b645 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -104,7 +104,12 @@ class project_work(osv.osv): vals_line['date'] = vals['date'][:10] if 'hours' in vals: vals_line['unit_amount'] = vals['hours'] - vals_line['amount'] = (-1) * vals['hours'] * (obj.browse(cr,uid,line_id).product_id.standard_price or 0.0) + + # Compute based on pricetype + amount_unit=obj.on_change_unit_amount(cr, uid, line_id, + vals_line['product_id'], vals_line['unit_amount'], unit, context) + + vals_line['amount'] = (-1) * vals['hours'] * (amount_unit or 0.0) obj.write(cr, uid, [line_id], vals_line, {}) return super(project_work,self).write(cr, uid, ids, vals, context) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index afe2ff47ee3..d8873a9e0c7 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -110,11 +110,15 @@ class stock_location(osv.osv): cr.execute('select distinct product_id from stock_move where (location_id=%s) or (location_dest_id=%s)', (id, id)) result = cr.dictfetchall() if result: + # Choose the right filed standard_price to read + # Take the user company + price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id + pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id) for r in result: c = (context or {}).copy() c['location'] = id - product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read, 'standard_price'], context=c) - final_value += (product[field_to_read] * product['standard_price']) + product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read, pricetype.field], context=c) + final_value += (product[field_to_read] * product[pricetype.field]) return final_value def _product_value(self, cr, uid, ids, field_names, arg, context={}): @@ -211,6 +215,10 @@ class stock_location(osv.osv): if context is None: context = {} product_obj = self.pool.get('product.product') + # Take the user company and pricetype + price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id + pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id) + if not product_ids: product_ids = product_obj.search(cr, uid, []) @@ -241,10 +249,16 @@ class stock_location(osv.osv): continue product = products_by_id[product_id] quantity_total += qty[product_id] - price = qty[product_id] * product.standard_price + + # Compute based on pricetype + # Choose the right filed standard_price to read + amount_unit=product.price_get(pricetype.field, context)[product.id] + price = qty[product_id] * amount_unit + # price = qty[product_id] * product.standard_price + total_price += price result['product'].append({ - 'price': product.standard_price, + 'price': amount_unit, 'prod_name': product.name, 'code': product.default_code, # used by lot_overview_all report! 'variants': product.variants or '', @@ -645,7 +659,12 @@ class stock_picking(osv.osv): def _get_price_unit_invoice(self, cursor, user, move_line, type): '''Return the price unit for the move line''' if type in ('in_invoice', 'in_refund'): - return move_line.product_id.standard_price + # Take the user company and pricetype + price_type_id=self.pool.get('res.users').browse(cr,users,users).company_id.property_valuation_price_type.id + pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id) + amount_unit=move_line.product_id.price_get(pricetype.field, context)[move_line.product_id.id] + return amount_unit + # return move_line.product_id.standard_price else: return move_line.product_id.list_price @@ -1411,7 +1430,8 @@ class stock_inventory(osv.osv): move_line = [] for line in inv.inventory_line_id: pid = line.product_id.id - price = line.product_id.standard_price or 0.0 + + # price = line.product_id.standard_price or 0.0 amount = self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid] change = line.product_qty - amount if change: diff --git a/addons/stock/wizard/wizard_partial_move.py b/addons/stock/wizard/wizard_partial_move.py index 66e426c1e18..e374c472789 100644 --- a/addons/stock/wizard/wizard_partial_move.py +++ b/addons/stock/wizard/wizard_partial_move.py @@ -124,7 +124,8 @@ def _do_split(self, cr, uid, data, context): currency = data['form']['currency%s' % move.id] qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id) - + pricetype=pool.get('product.price.type').browse(cr,uid,user.company_id.property_valuation_price_type.id) + if (qty > 0): new_price = currency_obj.compute(cr, uid, currency, user.company_id.currency_id.id, price) @@ -133,11 +134,13 @@ def _do_split(self, cr, uid, data, context): if product.qty_available<=0: new_std_price = new_price else: - new_std_price = ((product.standard_price * product.qty_available)\ + # Get the standard price + amount_unit=product.price_get(pricetype.field, context)[product.id] + new_std_price = ((product.amount_unit * product.qty_available)\ + (new_price * qty))/(product.qty_available + qty) product_obj.write(cr, uid, [product.id], - {'standard_price': new_std_price}) + {pricetype.field: new_std_price}) move_obj.write(cr, uid, move.id, {'price_unit': new_price}) for move in too_few: From b75d214b25cf7e7be63b4ad192b133d0456e9a14 Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 23 Dec 2009 18:14:09 +0100 Subject: [PATCH 012/328] [FIX] Analytic_user_function and project_timsheet corrected according to pricetype bzr revid: joel.grandguillaume@camptocamp.com-20091223171409-a0wfgy034f8o5yjw --- .../analytic_user_function.py | 14 ++++++++++---- addons/hr_timesheet/hr_timesheet.py | 2 +- addons/project_timesheet/project_timesheet.py | 8 ++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/addons/analytic_user_function/analytic_user_function.py b/addons/analytic_user_function/analytic_user_function.py index e2279bb891e..264c9fe43c0 100644 --- a/addons/analytic_user_function/analytic_user_function.py +++ b/addons/analytic_user_function/analytic_user_function.py @@ -99,8 +99,11 @@ class hr_analytic_timesheet(osv.osv): _('There is no expense account define ' \ 'for this product: "%s" (id:%d)') % \ (r.product_id.name, r.product_id.id,)) - amount = unit_amount * self.pool.get('product.uom')._compute_price(cr, uid, - r.product_id.uom_id.id, r.product_id.standard_price, False) + # Compute based on pricetype + amount_unit=self.on_change_unit_amount(cr, uid, ids, + r.product_id.id, unit_amount, r.product_id.uom_id.id)['value']['amount'] + + amount = unit_amount * amount_unit res ['value']['amount']= - round(amount, 2) res ['value']['general_account_id']= a return res @@ -132,8 +135,11 @@ class hr_analytic_timesheet(osv.osv): _('There is no expense account define ' \ 'for this product: "%s" (id:%d)') % \ (r.product_id.name, r.product_id.id,)) - amount = unit_amount * r.product_id.uom_id._compute_price(cr, uid, - r.product_id.uom_id.id, r.product_id.standard_price, False) + # Compute based on pricetype + amount_unit=self.on_change_unit_amount(cr, uid, ids, + r.product_id.id, unit_amount, r.product_id.uom_id.id)['value']['amount'] + + amount = unit_amount * amount_unit res ['value']['amount']= - round(amount, 2) res ['value']['general_account_id']= a return res diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index bf0e0d1426b..6082528379b 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -56,7 +56,7 @@ class hr_analytic_timesheet(osv.osv): def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, unit, context={}): res = {} # if prod_id and unit_amount: - if prod_id and date: + if prod_id: # find company company_id=self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context) res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,company_id,unit, context) diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 1dfda98b645..2152053f3de 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -77,8 +77,12 @@ class project_work(osv.osv): vals_line['journal_id'] = result['journal_id'] vals_line['amount'] = 00.0 timeline_id = obj.create(cr, uid, vals_line, {}) - - vals_line['amount'] = (-1) * vals['hours']* ( obj.browse(cr,uid,timeline_id).product_id.standard_price or 0.0) + + # Compute based on pricetype + amount_unit=obj.on_change_unit_amount(cr, uid, line_id, + vals_line['product_id'], vals_line['unit_amount'], unit, context) + + vals_line['amount'] = (-1) * vals['hours']* (unit_amount or 0.0) obj.write(cr, uid,[timeline_id], vals_line, {}) vals['hr_analytic_timesheet_id'] = timeline_id return super(project_work,self).create(cr, uid, vals, *args, **kwargs) From 9388c1b1f1e60139bcbee299df0359ab36da722b Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 6 Jan 2010 08:10:38 +0100 Subject: [PATCH 013/328] [FIX] Stock move valuation now use correctly price_get bzr revid: joel.grandguillaume@camptocamp.com-20100106071038-cp4xu2htyrkz7a3u --- addons/stock/stock.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index d8873a9e0c7..90d431d42f1 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1352,8 +1352,7 @@ class stock_move(osv.osv): company_id=move.company_id.id pricetype=self.pool.get('product.price.type').browse(cr,uid,move.company_id.property_valuation_price_type.id) - amount_unit=move.product_id.price_get(cr, uid, ids, pricetype.field, context) - + amount_unit=move.product_id.price_get(pricetype.field, context)[move.product_id.id] amount=amount_unit * q or 1.0 # amount = q * move.product_id.standard_price From c832258c146b8217d6c233076a2329f36336e95f Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Wed, 6 Jan 2010 08:40:27 +0100 Subject: [PATCH 014/328] [IMP] Compute debit and credit at each level in tree AA view bzr revid: joel.grandguillaume@camptocamp.com-20100106074027-ytdx87cjtwobvthv --- addons/account/project/project.py | 35 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/addons/account/project/project.py b/addons/account/project/project.py index 38d7e63ed30..0fa80d101f8 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -60,8 +60,16 @@ class account_analytic_account(osv.osv): def _credit_calc(self, cr, uid, ids, name, arg, context={}): - acc_set = ",".join(map(str, ids)) - + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + acc_set = ",".join(map(str, ids2)) + + for i in ids: + res.setdefault(i,0.0) + + if not acc_set: + return res + where_date = '' if context.get('from_date',False): where_date += " AND l.date >= '" + context['from_date'] + "'" @@ -70,13 +78,18 @@ class account_analytic_account(osv.osv): 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 %s) WHERE l.amount_currency<0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) r = dict(cr.fetchall()) - for i in ids: - r.setdefault(i,0.0) - return r - + return self._compute_currency_for_level_tree(cr, uid, ids, ids2, r, acc_set, context) + def _debit_calc(self, cr, uid, ids, name, arg, context={}): - - acc_set = ",".join(map(str, ids)) + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + acc_set = ",".join(map(str, ids2)) + + for i in ids: + res.setdefault(i,0.0) + + if not acc_set: + return res where_date = '' if context.get('from_date',False): @@ -86,10 +99,8 @@ class account_analytic_account(osv.osv): 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 %s) WHERE l.amount_currency>0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) r= dict(cr.fetchall()) - for i in ids: - r.setdefault(i,0.0) - return r - + return self._compute_currency_for_level_tree(cr, uid, ids, ids2, r, acc_set, context) + def _balance_calc(self, cr, uid, ids, name, arg, context={}): res = {} ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) From b14e30acd40488724d3ac2758a50e7da5b242229 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 18 Jan 2010 13:17:33 +0530 Subject: [PATCH 015/328] [FIX] Account : General Ledger report corrected for debit/credit if 0 lp bug: https://launchpad.net/bugs/507638 fixed bzr revid: jvo@tinyerp.com-20100118074733-50xckq59gj4kkykh --- addons/account/report/general_ledger.py | 2 +- addons/account/report/general_ledger_landscape.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/report/general_ledger.py b/addons/account/report/general_ledger.py index f8712ea4863..01ed8b7aec0 100644 --- a/addons/account/report/general_ledger.py +++ b/addons/account/report/general_ledger.py @@ -268,7 +268,7 @@ class general_ledger(rml_parse.rml_parse): else: sorttag = 'j.code' sql = """ - SELECT l.id, l.date, j.code,c.code AS currency_code,l.amount_currency,l.ref, l.name , l.debit, l.credit, l.period_id + SELECT l.id, l.date, j.code,c.code AS currency_code,l.amount_currency,l.ref, l.name , COALESCE(l.debit,0) as debit, COALESCE(l.credit,0) as credit, l.period_id FROM account_move_line as l LEFT JOIN res_currency c on (l.currency_id=c.id) JOIN account_journal j on (l.journal_id=j.id) diff --git a/addons/account/report/general_ledger_landscape.py b/addons/account/report/general_ledger_landscape.py index 0879a464f26..5f67f8ee96e 100644 --- a/addons/account/report/general_ledger_landscape.py +++ b/addons/account/report/general_ledger_landscape.py @@ -269,7 +269,7 @@ class general_ledger_landscape(rml_parse.rml_parse): else: sorttag = 'j.code' sql = """ - SELECT l.id, l.date, j.code,c.code AS currency_code,l.amount_currency,l.ref, l.name , l.debit, l.credit, l.period_id + SELECT l.id, l.date, j.code,c.code AS currency_code,l.amount_currency,l.ref, l.name , COALESCE(l.debit,0) as debit, COALESCE(l.credit,0) as credit, l.period_id FROM account_move_line as l LEFT JOIN res_currency c on (l.currency_id=c.id) JOIN account_journal j on (l.journal_id=j.id) From b3dd6bad347f11f2bebaeaa8d98f82eb43848c64 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 18 Jan 2010 14:07:37 +0530 Subject: [PATCH 016/328] [FIX/REF] MRP : Scheduler was having useless check on procure method lp bug: https://launchpad.net/bugs/507094 fixed bzr revid: jvo@tinyerp.com-20100118083737-7prft3usf1ctnns7 --- addons/mrp/schedulers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mrp/schedulers.py b/addons/mrp/schedulers.py index 22bca594c95..917fcef6ecd 100644 --- a/addons/mrp/schedulers.py +++ b/addons/mrp/schedulers.py @@ -61,7 +61,7 @@ class mrp_procurement(osv.osv): cr.execute('select id from mrp_procurement where state=%s and procure_method=%s order by date_planned limit 500 offset %s', ('confirmed', 'make_to_order', offset)) ids = map(lambda x: x[0], cr.fetchall()) for proc in procurement_obj.browse(cr, uid, ids): - if (maxdate.strftime('%Y-%m-%d')>=proc.date_planned) or (proc.procure_method=='make_to_order'): + if (maxdate.strftime('%Y-%m-%d')>=proc.date_planned): wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) else: offset += 1 @@ -84,7 +84,7 @@ class mrp_procurement(osv.osv): report_ids = [] ids = self.pool.get('mrp.procurement').search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_stock')], offset=offset) for proc in procurement_obj.browse(cr, uid, ids): - if ((maxdate).strftime('%Y-%m-%d') >= proc.date_planned) or (proc.procure_method=='make_to_order'): + if ((maxdate).strftime('%Y-%m-%d') >= proc.date_planned) : wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) report_ids.append(proc.id) else: From 28f42a58ec7337e5ac2506faf25bb518db30846d Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 18 Jan 2010 15:08:15 +0530 Subject: [PATCH 017/328] [FIX] If RML contains unicode characters and title in the same ,it would have failed. lp bug: https://launchpad.net/bugs/507429 fixed bzr revid: jvo@tinyerp.com-20100118093815-jkq0e0u09kx4x7hn --- bin/report/render/rml2pdf/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/report/render/rml2pdf/utils.py b/bin/report/render/rml2pdf/utils.py index bbccd23bb28..a5d141a6c10 100644 --- a/bin/report/render/rml2pdf/utils.py +++ b/bin/report/render/rml2pdf/utils.py @@ -112,6 +112,8 @@ def _process_text(self, txt): try: expr = sps.pop(0) txt = eval(expr,self.localcontext) + if txt and (isinstance(txt, unicode) or isinstance(txt, str)): + txt = self.localcontext.get('translate', lambda x:x)(txt) except Exception,e: tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR, @@ -119,7 +121,8 @@ def _process_text(self, txt): expr.encode('utf-8'))) if type(txt)==type('') or type(txt)==type(u''): txt2 = str2xml(txt) - result += tools.ustr(txt2) + result1 = tools.ustr(txt2) + result = result + str(result1) # To avoid concatenation of unicode and str elif (txt is not None) and (txt is not False): result += str(txt) return result From 3055b72703c431e3f9f6f6b9e3f847b2e4e216b6 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 18 Jan 2010 20:58:03 +0530 Subject: [PATCH 018/328] [FIX] Server Action : Email can be sent with non-English characters now bzr revid: jvo@tinyerp.com-20100118152803-d29cwcrh7b91zbd8 --- bin/addons/base/ir/ir_actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index 3047ceb7c8c..807fa9c266b 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -546,8 +546,8 @@ class actions_server(osv.osv): if not user: raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !")) - subject = self.merge_message(cr, uid, str(action.subject), action, context) - body = self.merge_message(cr, uid, str(action.message), action, context) + subject = self.merge_message(cr, uid, action.subject, action, context) + body = self.merge_message(cr, uid, action.message, action, context) if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True: logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address)) From d0a898c5c52681074df7751d862b7acc0aa42282 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 19 Jan 2010 14:05:28 +0530 Subject: [PATCH 019/328] [FIX] Menu : Search method corrected for the non-Admin user lp bug: https://launchpad.net/bugs/497364 fixed bzr revid: jvo@tinyerp.com-20100119083528-lmn47b0lvzhqisvl --- bin/addons/base/ir/ir_ui_menu.py | 34 ++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index 75a3550250e..98654c9d4e8 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -53,6 +53,7 @@ class ir_ui_menu(osv.osv): def __init__(self, *args, **kwargs): self._cache = {} + self._parents = [] r = super(ir_ui_menu, self).__init__(*args, **kwargs) self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache') return r @@ -64,20 +65,35 @@ class ir_ui_menu(osv.osv): def clear_cache(self): # radical but this doesn't frequently happen self._cache = {} + self._parents = [] def search(self, cr, uid, args, offset=0, limit=2000, order=None, context=None, count=False): if context is None: context = {} - ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1)) + + if uid !=1: + flag = True + for dom in args: + if dom[0] == 'parent_id': + flag = False + break + #Args should add the following domain in order to maintain the integrity of search. + #i.e. if search is performed on list view. + if flag: + args += [('parent_id','child_of',self._parents)] + + ids = osv.orm.orm.search(self, cr, uid, args, offset, uid==1 and limit or None, order, context=context, count=(count and uid==1)) if uid==1: + if limit and not count: + ids = ids[:int(limit)] return ids if not ids: if count: return 0 return [] - + modelaccess = self.pool.get('ir.model.access') user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id']) result = [] @@ -87,7 +103,7 @@ class ir_ui_menu(osv.osv): if key in self._cache: if self._cache[key]: result.append(menu.id) - continue + continue self._cache[key] = False if menu.groups_id: @@ -96,6 +112,8 @@ class ir_ui_menu(osv.osv): continue result.append(menu.id) self._cache[key] = True + if not menu.parent_id.id: + self._parents.append(menu.id) continue if menu.action: @@ -120,12 +138,20 @@ class ir_ui_menu(osv.osv): if not menu.child_id: # not displayed if there is no children continue + + if not menu.parent_id.id: +# #For List view display,this list keeps the track of parents + self._parents.append(menu.id) result.append(menu.id) - self._cache[key] = True + self._cache[key] = True if count: return len(result) + + if limit: + result = result[:int(limit)] + return result def _get_full_name(self, cr, uid, ids, name, args, context): From d961fd76daac5c66b843b0c4327a5822b5b16e46 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 19 Jan 2010 14:39:11 +0530 Subject: [PATCH 020/328] [FIX] Encoding problm corrected on report contents lp bug: https://launchpad.net/bugs/507429 fixed bzr revid: jvo@tinyerp.com-20100119090911-fj091etd2wi3nn1z --- bin/report/render/rml2pdf/utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bin/report/render/rml2pdf/utils.py b/bin/report/render/rml2pdf/utils.py index a5d141a6c10..72cd29f362c 100644 --- a/bin/report/render/rml2pdf/utils.py +++ b/bin/report/render/rml2pdf/utils.py @@ -107,13 +107,13 @@ def _process_text(self, txt): sps = _regex.split(txt) while sps: # This is a simple text to translate - result += self.localcontext.get('translate', lambda x:x)(sps.pop(0)) + result += tools.ustr(self.localcontext.get('translate', lambda x:x)(sps.pop(0))) if sps: try: expr = sps.pop(0) txt = eval(expr,self.localcontext) if txt and (isinstance(txt, unicode) or isinstance(txt, str)): - txt = self.localcontext.get('translate', lambda x:x)(txt) + txt = tools.ustr(self.localcontext.get('translate', lambda x:x)(txt)) except Exception,e: tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR, @@ -121,10 +121,9 @@ def _process_text(self, txt): expr.encode('utf-8'))) if type(txt)==type('') or type(txt)==type(u''): txt2 = str2xml(txt) - result1 = tools.ustr(txt2) - result = result + str(result1) # To avoid concatenation of unicode and str + result += tools.ustr(txt2) elif (txt is not None) and (txt is not False): - result += str(txt) + result += tools.ustr(txt) return result def text_get(node): From 6c3ae2de261925380c78eba906280b507f76427f Mon Sep 17 00:00:00 2001 From: "VRA(OpenERP)" <> Date: Tue, 19 Jan 2010 15:09:04 +0530 Subject: [PATCH 021/328] [FIX] Account : Wrong residual amount comes if company currency and invoice currency are different(provided no currency defined on journal) bzr revid: jvo@tinyerp.com-20100119093904-nsl7kjfnwo3gxsb7 --- addons/account/wizard/wizard_pay_invoice.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/account/wizard/wizard_pay_invoice.py b/addons/account/wizard/wizard_pay_invoice.py index 5dd98286339..64a2a32f604 100644 --- a/addons/account/wizard/wizard_pay_invoice.py +++ b/addons/account/wizard/wizard_pay_invoice.py @@ -66,6 +66,13 @@ def _pay_and_reconcile(self, cr, uid, data, context): currency_id = journal.currency.id # Put the paid amount in currency, and the currency, in the context if currency is different from company's currency context.update({'amount_currency':form['amount'],'currency_id':currency_id}) + + if invoice.company_id.currency_id.id<>invoice.currency_id.id: + ctx = {'date':data['form']['date']} + amount = cur_obj.compute(cr, uid, invoice.currency_id.id, invoice.company_id.currency_id.id, amount, context=ctx) + currency_id = invoice.currency_id.id + # Put the paid amount in currency, and the currency, in the context if currency is different from company's currency + context.update({'amount_currency':form['amount'],'currency_id':currency_id}) # Take the choosen date if form.has_key('comment'): From 3a748a1c6dceb08143d42ef979bda52a588dd567 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 19 Jan 2010 15:49:46 +0530 Subject: [PATCH 022/328] [FIX] Account_tax_include : Refund of invioce was not counting price_type. lp bug: https://launchpad.net/bugs/509496 fixed bzr revid: jvo@tinyerp.com-20100119101946-8vjzmajk5cqyuhpw --- addons/account_tax_include/invoice_tax_incl.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/addons/account_tax_include/invoice_tax_incl.py b/addons/account_tax_include/invoice_tax_incl.py index f05679c5791..a464580a2f2 100644 --- a/addons/account_tax_include/invoice_tax_incl.py +++ b/addons/account_tax_include/invoice_tax_incl.py @@ -37,6 +37,20 @@ class account_invoice(osv.osv): _defaults = { 'price_type': lambda *a: 'tax_excluded', } + + def refund(self, cr, uid, ids, date=None, period_id=None, description=None): + map_old_new = {} + refund_ids = [] + for old_inv_id in ids: + new_id = super(account_invoice,self).refund(cr, uid, ids, date=date, period_id=period_id, description=description) + refund_ids += new_id + map_old_new[old_inv_id] = new_id[0] + + for old_inv_id in map_old_new.keys(): + old_inv_record = self.read(cr, uid, [old_inv_id], ['price_type'])[0]['price_type'] + self.write(cr, uid, [map_old_new[old_inv_id]], {'price_type' : old_inv_record}) + return refund_ids + account_invoice() class account_invoice_line(osv.osv): From 172b9100d669d1d645ce273efec1b873a0928b17 Mon Sep 17 00:00:00 2001 From: "mra (Open ERP)" Date: Tue, 19 Jan 2010 16:25:21 +0530 Subject: [PATCH 023/328] [FIX] base_module_quality: add import statment for _ and give size to name field on save report wizard so that it can display long name also bzr revid: mra@tinyerp.com-20100119105521-47achfr9xu9ws7uo --- addons/base_module_quality/wizard/quality_save_report.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/base_module_quality/wizard/quality_save_report.py b/addons/base_module_quality/wizard/quality_save_report.py index 74e71c9f06d..bdfdd6edf50 100644 --- a/addons/base_module_quality/wizard/quality_save_report.py +++ b/addons/base_module_quality/wizard/quality_save_report.py @@ -26,6 +26,7 @@ import cStringIO import wizard from osv import osv import pooler +from tools.translate import _ form_rep = ''' @@ -36,7 +37,7 @@ form_rep = ''' fields_rep = { - 'name': {'string': 'File name', 'type': 'char', 'required': True, 'help': 'Save report as .html format'}, + 'name': {'string': 'File name', 'type': 'char', 'required': True, 'help': 'Save report as .html format', 'size':64}, 'module_file': {'string': 'Save report', 'type': 'binary', 'required': True}, } From 915f5b6db79579ac3f3c6a9d35c290b192f01ed3 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 19 Jan 2010 19:34:05 +0530 Subject: [PATCH 024/328] [FIX] Expression : Domain calculation of M2M/O2M corrected lp bug: https://launchpad.net/bugs/509629 fixed bzr revid: jvo@tinyerp.com-20100119140405-4qw4ovyyhcfqhytl --- bin/osv/expression.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 8c4bd589d44..5a43a17ac9b 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -187,15 +187,21 @@ class expression(object): if right: if isinstance(right, basestring): ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, context=context, limit=None)] - operator = 'in' + if ids2: + operator = 'in' else: if not isinstance(right,list): ids2 = [right] else: ids2 = right if not ids2: - call_null = True - operator = 'in' # operator changed because ids are directly related to main object + if operator in ['like','ilike','in','=']: + #no result found with given search criteria + call_null = False + self.__exp[i] = ('id','=',0) + else: + call_null = True + operator = 'in' # operator changed because ids are directly related to main object else: call_null = False o2m_op = 'in' @@ -230,15 +236,21 @@ class expression(object): if right: if isinstance(right, basestring): res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, context=context)] - operator = 'in' + if res_ids: + opeartor = 'in' else: if not isinstance(right, list): res_ids = [right] else: res_ids = right if not res_ids: - call_null_m2m = True - operator = 'in' # operator changed because ids are directly related to main object + if operator in ['like','ilike','in','=']: + #no result found with given search criteria + call_null_m2m = False + self.__exp[i] = ('id','=',0) + else: + call_null_m2m = True + operator = 'in' # operator changed because ids are directly related to main object else: call_null_m2m = False m2m_op = 'in' @@ -251,7 +263,7 @@ class expression(object): if operator in ['not like','not ilike','not in','<>','!=']: m2m_op = 'in' self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, [], operator, field._type) or [0]) - + elif field._type == 'many2one': if operator == 'child_of': if isinstance(right, basestring): From 0b1e1790c664dace2c3e3698aded34dc0da6acef Mon Sep 17 00:00:00 2001 From: Dukai Gabor <> Date: Tue, 19 Jan 2010 20:11:15 +0530 Subject: [PATCH 025/328] [FIX] Account: Better calculation of invoice amount lp bug: https://launchpad.net/bugs/496889 fixed bzr revid: jvo@tinyerp.com-20100119144115-bfmkskgefpf8bm9o --- addons/account/wizard/wizard_pay_invoice.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/account/wizard/wizard_pay_invoice.py b/addons/account/wizard/wizard_pay_invoice.py index 64a2a32f604..d12fe8852a3 100644 --- a/addons/account/wizard/wizard_pay_invoice.py +++ b/addons/account/wizard/wizard_pay_invoice.py @@ -99,7 +99,13 @@ def _wo_check(self, cr, uid, data, context): # => Ask to a write-off of the difference. This could happen even if both amount are equal, # because if the currency rate # Get the amount in company currency for the invoice (according to move lines) - inv_amount_company_currency=invoice.move_id.amount + inv_amount_company_currency = 0.0 + for aml in invoice.move_id.line_id: + if aml.account_id.id == invoice.account_id.id or aml.account_id.type in ('receivable', 'payable'): + inv_amount_company_currency += aml.debit + inv_amount_company_currency -= aml.credit + inv_amount_company_currency = abs(inv_amount_company_currency) + # Get the current amount paid in company currency if journal.currency and invoice.company_id.currency_id.id<>journal.currency.id: ctx = {'date':data['form']['date']} From 8fe0b562bc5cca14e029a0809aa1ed0e5b1245f7 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Wed, 20 Jan 2010 17:10:27 +0530 Subject: [PATCH 026/328] [REV] Reverted Raw commit for wizard of paying invoice bzr revid: jvo@tinyerp.com-20100120114027-4yultrzn5bz0qe2x --- addons/account/wizard/wizard_pay_invoice.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/account/wizard/wizard_pay_invoice.py b/addons/account/wizard/wizard_pay_invoice.py index d12fe8852a3..bd0bacadd93 100644 --- a/addons/account/wizard/wizard_pay_invoice.py +++ b/addons/account/wizard/wizard_pay_invoice.py @@ -99,12 +99,12 @@ def _wo_check(self, cr, uid, data, context): # => Ask to a write-off of the difference. This could happen even if both amount are equal, # because if the currency rate # Get the amount in company currency for the invoice (according to move lines) - inv_amount_company_currency = 0.0 - for aml in invoice.move_id.line_id: - if aml.account_id.id == invoice.account_id.id or aml.account_id.type in ('receivable', 'payable'): - inv_amount_company_currency += aml.debit - inv_amount_company_currency -= aml.credit - inv_amount_company_currency = abs(inv_amount_company_currency) + inv_amount_company_currency = invoice.move_id.amount +# for aml in invoice.move_id.line_id: +# if aml.account_id.id == invoice.account_id.id or aml.account_id.type in ('receivable', 'payable'): +# inv_amount_company_currency += aml.debit +# inv_amount_company_currency -= aml.credit +# inv_amount_company_currency = abs(inv_amount_company_currency) # Get the current amount paid in company currency if journal.currency and invoice.company_id.currency_id.id<>journal.currency.id: From 5303dc2e92e3ad7b057d8856c732c08884e8b9d1 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 20 Jan 2010 17:33:27 +0100 Subject: [PATCH 027/328] [FIX] correct threading problems in cache bzr revid: chs@tinyerp.com-20100120163327-nwfb0vs9cs3n3cl0 --- bin/tools/misc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/tools/misc.py b/bin/tools/misc.py index e3a96772b83..33f2c9ab93a 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -634,13 +634,13 @@ class cache(object): if *args and **kwargs are both empty, clear all the keys related to this database """ if not args and not kwargs: - keys_to_del = [key for key in self.cache if key[0][1] == dbname] + keys_to_del = [key for key in self.cache.keys() if key[0][1] == dbname] else: kwargs2 = self._unify_args(*args, **kwargs) - keys_to_del = [key for key, _ in self._generate_keys(dbname, kwargs2) if key in self.cache] + keys_to_del = [key for key, _ in self._generate_keys(dbname, kwargs2) if key in self.cache.keys()] for key in keys_to_del: - del self.cache[key] + self.cache.pop(key) @classmethod def clean_caches_for_db(cls, dbname): @@ -661,10 +661,10 @@ class cache(object): def cached_result(self2, cr, *args, **kwargs): if time.time()-int(self.timeout) > self.lasttime: self.lasttime = time.time() - t = time.time()-int(self.timeout) - old_keys = [key for key in self.cache if self.cache[key][1] < t] + t = time.time()-int(self.timeout) + old_keys = [key for key in self.cache.keys() if self.cache[key][1] < t] for key in old_keys: - del self.cache[key] + self.cache.pop(key) kwargs2 = self._unify_args(*args, **kwargs) From fecc3a360da24cd76b366626b4ef577bd7b032ab Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 20 Jan 2010 17:39:13 +0100 Subject: [PATCH 028/328] [FIX] remove the lock on database connections [ADD] exported method check_connectivity [REF] better logging of the Connection and ConnectionPool objects [REF] use template0 as template when create new database bzr revid: chs@tinyerp.com-20100120163913-5ftbs7e85lwqb1et --- bin/service/web_services.py | 146 ++++++++++++++++-------------------- bin/sql_db.py | 53 ++++--------- 2 files changed, 78 insertions(+), 121 deletions(-) diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 810171eb78e..ed3b06e637c 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -24,7 +24,6 @@ import base64 import logging import os import security -import string import thread import threading import time @@ -39,6 +38,8 @@ import release import sql_db import tools import locale +from cStringIO import StringIO + logging.basicConfig() class db(netsvc.Service): @@ -62,6 +63,15 @@ class db(netsvc.Service): self._pg_psw_env_var_is_set = False # on win32, pg_dump need the PGPASSWORD env var + def _create_empty_database(self, name): + db = sql_db.db_connect('template1') + cr = db.cursor() + try: + cr.autocommit(True) # avoid transaction block + cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "template0" """ % name) + finally: + cr.close() + def create(self, password, db_name, demo, lang, user_password='admin'): security.check_super(password) self.id_protect.acquire() @@ -71,24 +81,13 @@ class db(netsvc.Service): self.actions[id] = {'clean': False} - db = sql_db.db_connect('template1') - db.lock() - try: - cr = db.cursor() - try: - cr.autocommit(True) # avoid transaction block - cr.execute('CREATE DATABASE "%s" ENCODING \'unicode\'' % db_name) - finally: - cr.close() - finally: - db.release() + self._create_empty_database(db_name) class DBInitialize(object): def __call__(self, serv, id, db_name, demo, lang, user_password='admin'): cr = None try: serv.actions[id]['progress'] = 0 - clean = False cr = sql_db.db_connect(db_name).cursor() tools.init_db(cr) cr.commit() @@ -116,7 +115,6 @@ class db(netsvc.Service): except Exception, e: serv.actions[id]['clean'] = False serv.actions[id]['exception'] = e - from cStringIO import StringIO import traceback e_str = StringIO() traceback.print_exc(file=e_str) @@ -137,6 +135,7 @@ class db(netsvc.Service): def get_progress(self, password, id): security.check_super(password) + tools.debug((id, self.actions.keys())) if self.actions[id]['thread'].isAlive(): # return addons.init_progress[db_name] return (min(self.actions[id].get('progress', 0),0.95), []) @@ -144,11 +143,11 @@ class db(netsvc.Service): clean = self.actions[id]['clean'] if clean: users = self.actions[id]['users'] - del self.actions[id] + self.actions.pop(id) return (1.0, users) else: e = self.actions[id]['exception'] - del self.actions[id] + self.actions.pop(id) raise Exception, e def drop(self, password, db_name): @@ -157,24 +156,20 @@ class db(netsvc.Service): logger = netsvc.Logger() db = sql_db.db_connect('template1') - db.lock() + cr = db.cursor() + cr.autocommit(True) # avoid transaction block try: - cr = db.cursor() - cr.autocommit(True) # avoid transaction block try: - try: - cr.execute('DROP DATABASE "%s"' % db_name) - except Exception, e: - logger.notifyChannel("web-services", netsvc.LOG_ERROR, - 'DROP DB: %s failed:\n%s' % (db_name, e)) - raise Exception("Couldn't drop database %s: %s" % (db_name, e)) - else: - logger.notifyChannel("web-services", netsvc.LOG_INFO, - 'DROP DB: %s' % (db_name)) - finally: - cr.close() + cr.execute('DROP DATABASE "%s"' % db_name) + except Exception, e: + logger.notifyChannel("web-services", netsvc.LOG_ERROR, + 'DROP DB: %s failed:\n%s' % (db_name, e)) + raise Exception("Couldn't drop database %s: %s" % (db_name, e)) + else: + logger.notifyChannel("web-services", netsvc.LOG_INFO, + 'DROP DB: %s' % (db_name)) finally: - db.release() + cr.close() return True def _set_pg_psw_env_var(self): @@ -227,17 +222,7 @@ class db(netsvc.Service): 'RESTORE DB: %s already exists' % (db_name,)) raise Exception, "Database already exists" - db = sql_db.db_connect('template1') - db.lock() - try: - cr = db.cursor() - cr.autocommit(True) # avoid transaction block - try: - cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "template0" """ % db_name) - finally: - cr.close() - finally: - db.release() + self._create_empty_database(db_name) cmd = ['pg_restore', '--no-owner'] if tools.config['db_user']: @@ -276,27 +261,23 @@ class db(netsvc.Service): logger = netsvc.Logger() db = sql_db.db_connect('template1') - db.lock() + cr = db.cursor() try: - cr = db.cursor() try: - try: - cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name)) - except Exception, e: - logger.notifyChannel("web-services", netsvc.LOG_ERROR, - 'RENAME DB: %s -> %s failed:\n%s' % (old_name, new_name, e)) - raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e)) - else: - fs = os.path.join(tools.config['root_path'], 'filestore') - if os.path.exists(os.path.join(fs, old_name)): - os.rename(os.path.join(fs, old_name), os.path.join(fs, new_name)) + cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name)) + except Exception, e: + logger.notifyChannel("web-services", netsvc.LOG_ERROR, + 'RENAME DB: %s -> %s failed:\n%s' % (old_name, new_name, e)) + raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e)) + else: + fs = os.path.join(tools.config['root_path'], 'filestore') + if os.path.exists(os.path.join(fs, old_name)): + os.rename(os.path.join(fs, old_name), os.path.join(fs, new_name)) - logger.notifyChannel("web-services", netsvc.LOG_INFO, - 'RENAME DB: %s -> %s' % (old_name, new_name)) - finally: - cr.close() + logger.notifyChannel("web-services", netsvc.LOG_INFO, + 'RENAME DB: %s -> %s' % (old_name, new_name)) finally: - db.release() + cr.close() return True def db_exist(self, db_name): @@ -308,30 +289,26 @@ class db(netsvc.Service): raise Exception('AccessDenied') db = sql_db.db_connect('template1') - db.lock() + cr = db.cursor() try: - cr = db.cursor() try: - try: - db_user = tools.config["db_user"] - if not db_user and os.name == 'posix': - import pwd - db_user = pwd.getpwuid(os.getuid())[0] - if not db_user: - cr.execute("select decode(usename, 'escape') from pg_user where usesysid=(select datdba from pg_database where datname=%s)", (tools.config["db_name"],)) - res = cr.fetchone() - db_user = res and str(res[0]) - if db_user: - cr.execute("select decode(datname, 'escape') from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in ('template0', 'template1', 'postgres') order by datname", (db_user,)) - else: - cr.execute("select decode(datname, 'escape') from pg_database where datname not in('template0', 'template1','postgres') order by datname") - res = [str(name) for (name,) in cr.fetchall()] - except: - res = [] - finally: - cr.close() + db_user = tools.config["db_user"] + if not db_user and os.name == 'posix': + import pwd + db_user = pwd.getpwuid(os.getuid())[0] + if not db_user: + cr.execute("select decode(usename, 'escape') from pg_user where usesysid=(select datdba from pg_database where datname=%s)", (tools.config["db_name"],)) + res = cr.fetchone() + db_user = res and str(res[0]) + if db_user: + cr.execute("select decode(datname, 'escape') from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in ('template0', 'template1', 'postgres') order by datname", (db_user,)) + else: + cr.execute("select decode(datname, 'escape') from pg_database where datname not in('template0', 'template1','postgres') order by datname") + res = [str(name) for (name,) in cr.fetchall()] + except: + res = [] finally: - db.release() + cr.close() res.sort() return res @@ -366,7 +343,7 @@ class db(netsvc.Service): self.abortResponse(1, inst.name, 'warning', inst.value) except except_osv, inst: self.abortResponse(1, inst.name, inst.exc_type, inst.value) - except Exception, e: + except Exception: import traceback tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback)) l.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s) @@ -389,6 +366,7 @@ class common(netsvc.Service): self.exportMethod(self.get_migration_scripts) self.exportMethod(self.get_server_environment) self.exportMethod(self.login_message) + self.exportMethod(self.check_connectivity) def ir_set(self, db, uid, password, keys, args, name, value, replace=True, isobject=False): security.check(db, uid, password) @@ -512,7 +490,7 @@ GNU Public Licence. l.notifyChannel('migration', netsvc.LOG_ERROR, 'unable to read the module %s' % (module,)) raise - zip_contents = cStringIO.StringIO(base64_decoded) + zip_contents = StringIO(base64_decoded) zip_contents.seek(0) try: try: @@ -570,11 +548,13 @@ GNU Public Licence. %(platform.release(), platform.version(), platform.architecture()[0], os_lang, platform.python_version(),release.version,rev_id) return environment - def login_message(self): return tools.config.get('login_message', False) + def check_connectivity(self): + return bool(sql_db.db_connect('template1')) + common() class objects_proxy(netsvc.Service): diff --git a/bin/sql_db.py b/bin/sql_db.py index e39b40a1ee6..fec33b11284 100644 --- a/bin/sql_db.py +++ b/bin/sql_db.py @@ -119,7 +119,7 @@ class Cursor(object): if self.sql_log: now = mdt.now() - + try: params = params or None res = self._obj.execute(query, params) @@ -195,11 +195,11 @@ class Cursor(object): def autocommit(self, on): offlevel = [ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE][bool(self._serialized)] self._cnx.set_isolation_level([offlevel, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)]) - + @check def commit(self): return self._cnx.commit() - + @check def rollback(self): return self._cnx.rollback() @@ -228,16 +228,13 @@ class ConnectionPool(object): self._lock = threading.Lock() self._logger = netsvc.Logger() - def _log(self, msg): - #self._logger.notifyChannel('ConnectionPool', netsvc.LOG_INFO, msg) - pass def _debug(self, msg): - #self._logger.notifyChannel('ConnectionPool', netsvc.LOG_DEBUG, msg) - pass + self._logger.notifyChannel('ConnectionPool', netsvc.LOG_DEBUG, msg) + #pass @locked def borrow(self, dsn): - self._log('Borrow connection to %s' % (dsn,)) + self._debug('Borrow connection to %s' % (dsn,)) result = None for i, (cnx, used) in enumerate(self._connections): @@ -270,7 +267,7 @@ class ConnectionPool(object): @locked def give_back(self, connection): - self._log('Give back connection to %s' % (connection.dsn,)) + self._debug('Give back connection to %s' % (connection.dsn,)) for i, (cnx, used) in enumerate(self._connections): if cnx is connection: self._connections.pop(i) @@ -281,6 +278,7 @@ class ConnectionPool(object): @locked def close_all(self, dsn): + self._debug('Close all connections to %s' % (dsn,)) for i, (cnx, used) in tools.reverse_enumerate(self._connections): if dsn_are_equals(cnx.dsn, dsn): cnx.close() @@ -288,37 +286,17 @@ class ConnectionPool(object): class Connection(object): - __LOCKS = {} + def _debug(self, msg): + self._logger.notifyChannel('Connection', netsvc.LOG_DEBUG, msg) - def __init__(self, pool, dbname, unique=False): + def __init__(self, pool, dbname): self.dbname = dbname self._pool = pool - self._unique = unique - - def __enter__(self): - if self._unique: - self.lock() - return self - - def __exit__(self, exc_type, exc_value, traceback): - if self._unique: - self.release() - - def lock(self): - if self.dbname not in self.__LOCKS: - self.__LOCKS[self.dbname] = threading.Lock() - self.__LOCKS[self.dbname].acquire() - - def release(self): - close_db(self.dbname) - self.__LOCKS[self.dbname].release() + self._logger = netsvc.Logger() def cursor(self, serialized=False): - if self._unique: - lock = self.__LOCKS.get(self.dbname, None) - if not (lock and lock.locked()): - netsvc.Logger().notifyChannel('Connection', netsvc.LOG_WARNING, 'Unprotected connection to %s' % (self.dbname,)) - + cursor_type = serialized and 'serialized ' or '' + self._debug('create %scursor to "%s"' % (cursor_type, self.dbname,)) return Cursor(self._pool, self.dbname, serialized=serialized) def serialized_cursor(self): @@ -354,8 +332,7 @@ def dsn_are_equals(first, second): _Pool = ConnectionPool(int(tools.config['db_maxconn'])) def db_connect(db_name): - unique = db_name in ['template1', 'template0'] - return Connection(_Pool, db_name, unique) + return Connection(_Pool, db_name) def close_db(db_name): _Pool.close_all(dsn(db_name)) From db13ceee7aa2221b49dc8645871e12986ebe822f Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 20 Jan 2010 17:52:08 +0100 Subject: [PATCH 029/328] [FIX] revert previous patch about linked bug (lp:497364) lp bug: https://launchpad.net/bugs/497364 fixed bzr revid: chs@tinyerp.com-20100120165208-ppmd7tq9mqm1jtcu --- bin/addons/base/ir/ir_ui_menu.py | 34 ++++---------------------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index 98654c9d4e8..75a3550250e 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -53,7 +53,6 @@ class ir_ui_menu(osv.osv): def __init__(self, *args, **kwargs): self._cache = {} - self._parents = [] r = super(ir_ui_menu, self).__init__(*args, **kwargs) self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache') return r @@ -65,35 +64,20 @@ class ir_ui_menu(osv.osv): def clear_cache(self): # radical but this doesn't frequently happen self._cache = {} - self._parents = [] def search(self, cr, uid, args, offset=0, limit=2000, order=None, context=None, count=False): if context is None: context = {} - - if uid !=1: - flag = True - for dom in args: - if dom[0] == 'parent_id': - flag = False - break - #Args should add the following domain in order to maintain the integrity of search. - #i.e. if search is performed on list view. - if flag: - args += [('parent_id','child_of',self._parents)] - - ids = osv.orm.orm.search(self, cr, uid, args, offset, uid==1 and limit or None, order, context=context, count=(count and uid==1)) + ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, context=context, count=(count and uid==1)) if uid==1: - if limit and not count: - ids = ids[:int(limit)] return ids if not ids: if count: return 0 return [] - + modelaccess = self.pool.get('ir.model.access') user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id']) result = [] @@ -103,7 +87,7 @@ class ir_ui_menu(osv.osv): if key in self._cache: if self._cache[key]: result.append(menu.id) - continue + continue self._cache[key] = False if menu.groups_id: @@ -112,8 +96,6 @@ class ir_ui_menu(osv.osv): continue result.append(menu.id) self._cache[key] = True - if not menu.parent_id.id: - self._parents.append(menu.id) continue if menu.action: @@ -138,20 +120,12 @@ class ir_ui_menu(osv.osv): if not menu.child_id: # not displayed if there is no children continue - - if not menu.parent_id.id: -# #For List view display,this list keeps the track of parents - self._parents.append(menu.id) result.append(menu.id) - self._cache[key] = True + self._cache[key] = True if count: return len(result) - - if limit: - result = result[:int(limit)] - return result def _get_full_name(self, cr, uid, ids, name, args, context): From 491ec719d7801d3103e57105dc6c7135d9f7d23f Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 20 Jan 2010 18:19:31 +0100 Subject: [PATCH 030/328] [FIX] account: credit/debit/balance of consolidated accounts lp bug: https://launchpad.net/bugs/504824 fixed bzr revid: chs@tinyerp.com-20100120171931-8nta52yk7j7mlu3p --- addons/account/account.py | 65 ++++++++++++++++++------------- addons/account_voucher/account.py | 34 ++-------------- 2 files changed, 42 insertions(+), 57 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index b049ab5d087..4e29bb1901f 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -77,7 +77,10 @@ class account_payment_term_line(osv.osv): _columns = { 'name': fields.char('Line Name', size=32, required=True), 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"), - 'value': fields.selection([('procent', 'Percent'), ('balance', 'Balance'), ('fixed', 'Fixed Amount')], 'Value', required=True, help="""Example: 14 days 2%, 30 days net + 'value': fields.selection([('procent', 'Percent'), + ('balance', 'Balance'), + ('fixed', 'Fixed Amount')], 'Value', + required=True, help="""Example: 14 days 2%, 30 days net 1. Line 1: percent 0.02 14 days 2. Line 2: balance 30 days"""), @@ -205,43 +208,50 @@ class account_account(osv.osv): #compute for each account the balance/debit/credit from the move lines accounts = {} if ids2: - query = self.pool.get('account.move.line')._query_get(cr, uid, - context=context) + aml_query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + + wheres = [""] + if query: + wheres.append(query.strip()) + if aml_query: + wheres.append(aml_query.strip()) + query = " AND ".join(wheres) + cr.execute(("SELECT l.account_id as id, " +\ ' , '.join(map(lambda x: mapping[x], field_names)) + "FROM " \ "account_move_line l " \ "WHERE " \ "l.account_id IN (%s) " \ - "AND " + query + " " \ - "GROUP BY l.account_id") % (acc_set, )) + + query + + " GROUP BY l.account_id") % (acc_set, )) for res in cr.dictfetchall(): accounts[res['id']] = res - #for the asked accounts, get from the dictionnary 'accounts' the value of it + + # consolidate accounts with direct children + brs = list(self.browse(cr, uid, ids2, context=context)) + sums = {} + while brs: + current = brs[0] + can_compute = True + for child in current.child_id: + if child.id not in sums: + can_compute = False + try: + brs.insert(0, brs.pop(brs.index(child))) + except ValueError: + brs.insert(0, child) + if can_compute: + brs.pop(0) + for fn in field_names: + sums.setdefault(current.id, {})[fn] = accounts.get(current.id, {}).get(fn, 0.0) + if current.child_id: + sums[current.id][fn] += sum(sums[child.id][fn] for child in current.child_id) res = {} for id in ids: - res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context) - return res - - def _get_account_values(self, cr, uid, id, accounts, field_names, context={}): - res = {}.fromkeys(field_names, 0.0) - browse_rec = self.browse(cr, uid, id) - if browse_rec.type == 'consolidation': - ids2 = self.read(cr, uid, [browse_rec.id], ['child_consol_ids'], context)[0]['child_consol_ids'] - for t in self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]): - if t not in ids2 and t != browse_rec.id: - ids2.append(t) - for i in ids2: - tmp = self._get_account_values(cr, uid, i, accounts, field_names, context) - for a in field_names: - res[a] += tmp[a] - else: - ids2 = self.search(cr, uid, [('parent_id', 'child_of', [browse_rec.id])]) - for i in ids2: - for a in field_names: - res[a] += accounts.get(i, {}).get(a, 0.0) + res[id] = sums[id] return res def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): @@ -260,7 +270,8 @@ class account_account(osv.osv): if record.child_consol_ids: for acc in record.child_consol_ids: - result[record.id].append(acc.id) + if acc.id not in result[record.id]: + result[record.id].append(acc.id) return result diff --git a/addons/account_voucher/account.py b/addons/account_voucher/account.py index e0f0b5de62b..9c153a185b2 100644 --- a/addons/account_voucher/account.py +++ b/addons/account_voucher/account.py @@ -64,38 +64,12 @@ class account_account(osv.osv): 'type1':fields.selection([('dr','Debit'),('cr','Credit'),('none','None')], 'Dr/Cr',store=True), } - def compute_total(self, cr, uid, ids, yr_st_date, yr_end_date, st_date, end_date, field_names, context={}, query=''): - #compute the balance/debit/credit accordingly to the value of field_name for the given account ids - mapping = { - 'credit': "COALESCE(SUM(l.credit), 0) as credit ", - 'balance': "COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance ", - 'debit': "COALESCE(SUM(l.debit), 0) as debit ", - } - #get all the necessary accounts - ids2 = self._get_children_and_consol(cr, uid, ids, context) - acc_set = ",".join(map(str, ids2)) - #compute for each account the balance/debit/credit from the move lines + def compute_total(self, cr, uid, ids, yr_st_date, yr_end_date, st_date, end_date, field_names, context={}): if not (st_date >= yr_st_date and end_date <= yr_end_date): return {} - accounts = {} - if ids2: - query = self.pool.get('account.move.line')._query_get(cr, uid, - context=context) - cr.execute("SELECT l.account_id as id, " \ - + ' , '.join(map(lambda x: mapping[x], field_names.keys() )) + \ - "FROM account_move_line l " \ - "WHERE l.account_id IN ("+ acc_set +") " \ - "AND " + query + " " \ - " AND l.date >= "+"'"+ st_date +"'"+" AND l.date <= "+"'"+ end_date +""+"'"" " \ - "GROUP BY l.account_id ") - for res in cr.dictfetchall(): - accounts[res['id']] = res - #for the asked accounts, get from the dictionnary 'accounts' the value of it - res = {} - for id in ids: - res[id] = self._get_account_values(cr, uid, id, accounts, field_names, context) - return res - + query = "l.date >= '%s' AND l.date <= '%s'" (st_date, end_date) + return self.__compute(cr, uid, ids, field_names, context=context, query=query) + def create(self, cr, uid, vals, context={}): name=self.search(cr,uid,[('name','ilike',vals['name']),('company_id','=',vals['name'])]) if name: From da2907e790508998cd3a12f7eb4fcee957e96c70 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Thu, 21 Jan 2010 11:47:10 +0530 Subject: [PATCH 031/328] [FIX] Stock: Shipping Address has to be in.po/.pot lp bug: https://launchpad.net/bugs/481130 fixed bzr revid: jvo@tinyerp.com-20100121061710-ek0yr14qny0v7xs6 --- addons/stock/i18n/de_DE.po | 5 +++++ addons/stock/i18n/fr_FR.po | 5 +++++ addons/stock/i18n/stock.pot | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/addons/stock/i18n/de_DE.po b/addons/stock/i18n/de_DE.po index 63e206b61f0..6183bdfad26 100644 --- a/addons/stock/i18n/de_DE.po +++ b/addons/stock/i18n/de_DE.po @@ -687,6 +687,11 @@ msgstr "Erledigt am" msgid "Expected Shipping Date" msgstr "Erwartete Auslieferung" +#. module: stock +#: rml:stock.picking.list:0 +msgid "Shipping Address :" +msgstr "Lieferanschrift:" + #. module: stock #: view:stock.tracking:0 msgid "Tracking/Serial" diff --git a/addons/stock/i18n/fr_FR.po b/addons/stock/i18n/fr_FR.po index 5d5e1bcabaf..af09e959b34 100644 --- a/addons/stock/i18n/fr_FR.po +++ b/addons/stock/i18n/fr_FR.po @@ -712,6 +712,11 @@ msgstr "Date de fin" msgid "Expected Shipping Date" msgstr "Date d'envoi prévue" +#. module: stock +#: rml:stock.picking.list:0 +msgid "Shipping Address :" +msgstr "Adresse de livraison :" + #. module: stock #: view:stock.tracking:0 msgid "Tracking/Serial" diff --git a/addons/stock/i18n/stock.pot b/addons/stock/i18n/stock.pot index 4a38f1864bc..f72222b09c4 100644 --- a/addons/stock/i18n/stock.pot +++ b/addons/stock/i18n/stock.pot @@ -677,6 +677,11 @@ msgstr "" msgid "Expected Shipping Date" msgstr "" +#. module: stock +#: rml:stock.picking.list:0 +msgid "Shipping Address :" +msgstr "" + #. module: stock #: view:stock.tracking:0 msgid "Tracking/Serial" From 57cc12f1e87f05028d38149bf0ffc10fd154f806 Mon Sep 17 00:00:00 2001 From: "mra (Open ERP)" Date: Thu, 21 Jan 2010 12:16:27 +0530 Subject: [PATCH 032/328] [FIX] product: Now pricelist objects price get function will pass context to price_get method of product and compute method of currency object bzr revid: mra@tinyerp.com-20100121064627-kwxkds6q14c17nou --- addons/product/pricelist.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 4c8006d80f3..47da61efcdd 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved # $Id$ # @@ -84,12 +84,12 @@ class product_pricelist(osv.osv): pricelist_type_obj = self.pool.get('product.pricelist.type') pricelist_type_ids = pricelist_type_obj.search(cr, uid, [], order='name') pricelist_types = pricelist_type_obj.read(cr, uid, pricelist_type_ids, ['key','name'], context=context) - + res = [] - + for type in pricelist_types: res.append((type['key'],type['name'])) - + return res # cr.execute('select key,name from product_pricelist_type order by name') # return cr.fetchall() @@ -102,7 +102,7 @@ class product_pricelist(osv.osv): 'version_id': fields.one2many('product.pricelist.version', 'pricelist_id', 'Pricelist Versions'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True), } - + def name_get(self, cr, uid, ids, context={}): result= [] if not all(ids): @@ -111,7 +111,7 @@ class product_pricelist(osv.osv): name = pl.name + ' ('+ pl.currency_id.name + ')' result.append((pl.id,name)) return result - + def _get_currency(self, cr, uid, ctx): comp = self.pool.get('res.users').browse(cr, uid, uid).company_id @@ -211,7 +211,7 @@ class product_pricelist(osv.osv): elif res['base'] == -2: where = [] if partner: - where = [('name', '=', partner) ] + where = [('name', '=', partner) ] sinfo = supplierinfo_obj.search(cr, uid, [('product_id', '=', tmpl_id)] + where) price = 0.0 @@ -230,7 +230,7 @@ class product_pricelist(osv.osv): price = currency_obj.compute(cr, uid, price_type.currency_id.id, res['currency_id'], product_obj.price_get(cr, uid, [prod_id], - price_type.field)[prod_id], round=False) + price_type.field, context=context)[prod_id], round=False, context=context) price_limit = price @@ -245,12 +245,12 @@ class product_pricelist(osv.osv): # False means no valid line found ! But we may not raise an # exception here because it breaks the search price = False - result[id] = price + result[id] = price if context and ('uom' in context): product = product_obj.browse(cr, uid, prod_id) uom = product.uos_id or product.uom_id result[id] = self.pool.get('product.uom')._compute_price(cr, - uid, uom.id, result[id], context['uom']) + uid, uom.id, result[id], context['uom']) return result product_pricelist() @@ -332,16 +332,16 @@ class product_pricelist_item(osv.osv): 'sequence': lambda *a: 5, 'price_discount': lambda *a: 0, } - + def _check_recursion(self, cr, uid, ids): for obj_list in self.browse(cr, uid, ids): if obj_list.base == -1: main_pricelist = obj_list.price_version_id.pricelist_id.id other_pricelist = obj_list.base_pricelist_id.id if main_pricelist == other_pricelist: - return False + return False return True - + _columns = { 'name': fields.char('Rule Name', size=64, help="Explicit rule name for this pricelist line."), 'price_version_id': fields.many2one('product.pricelist.version', 'Price List Version', required=True, select=True), @@ -368,11 +368,11 @@ class product_pricelist_item(osv.osv): 'price_max_margin': fields.float('Max. Price Margin', digits=(16, int(config['price_accuracy']))), } - + _constraints = [ (_check_recursion, _('Error ! You cannot assign the Main Pricelist as Other Pricelist in PriceList Item!'), ['base_pricelist_id']) ] - + def product_id_change(self, cr, uid, ids, product_id, context={}): if not product_id: return {} From 1937e4387ddad6091e166bb443b78ad4b5dbffd5 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Thu, 21 Jan 2010 17:22:07 +0530 Subject: [PATCH 033/328] [FIX] Added Missing import statements of translations( from tools.translate import _) lp bug: https://launchpad.net/bugs/510217 fixed bzr revid: jvo@tinyerp.com-20100121115207-f8m3xx92d36hhqle --- addons/document_ics/document.py | 1 + addons/hr_attendance/wizard/print_attendance_error.py | 1 + addons/hr_timesheet_invoice/wizard/account_analytic_profit.py | 1 + addons/point_of_sale/wizard/wizard_pos_payment.py | 1 + addons/purchase_manual/wizard/wizard_purchase_line_invoice.py | 1 + addons/sale_crm/wizard/makesale.py | 1 + 6 files changed, 6 insertions(+) diff --git a/addons/document_ics/document.py b/addons/document_ics/document.py index 2d70f3f5770..2465f0aa507 100644 --- a/addons/document_ics/document.py +++ b/addons/document_ics/document.py @@ -29,6 +29,7 @@ import datetime import time import random import tools +from tools.translate import _ ICS_TAGS = { 'summary':'normal', diff --git a/addons/hr_attendance/wizard/print_attendance_error.py b/addons/hr_attendance/wizard/print_attendance_error.py index 242fc7f90aa..370588f1b14 100644 --- a/addons/hr_attendance/wizard/print_attendance_error.py +++ b/addons/hr_attendance/wizard/print_attendance_error.py @@ -23,6 +23,7 @@ import wizard import time import pooler +from tools.translate import _ _date_form = ''' diff --git a/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py b/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py index 9d1ac817baf..ddc956d9719 100644 --- a/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py +++ b/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py @@ -23,6 +23,7 @@ import wizard import datetime import pooler +from tools.translate import _ form=''' diff --git a/addons/point_of_sale/wizard/wizard_pos_payment.py b/addons/point_of_sale/wizard/wizard_pos_payment.py index e09e6daa87b..9e23b7f443e 100644 --- a/addons/point_of_sale/wizard/wizard_pos_payment.py +++ b/addons/point_of_sale/wizard/wizard_pos_payment.py @@ -24,6 +24,7 @@ import pooler import netsvc import wizard import time +from tools.translate import _ def _get_journal(self, cr, uid, context): diff --git a/addons/purchase_manual/wizard/wizard_purchase_line_invoice.py b/addons/purchase_manual/wizard/wizard_purchase_line_invoice.py index b1814455e98..838b5394668 100644 --- a/addons/purchase_manual/wizard/wizard_purchase_line_invoice.py +++ b/addons/purchase_manual/wizard/wizard_purchase_line_invoice.py @@ -25,6 +25,7 @@ import netsvc import ir import pooler from osv import osv +from tools.translate import _ invoice_form = """ diff --git a/addons/sale_crm/wizard/makesale.py b/addons/sale_crm/wizard/makesale.py index 97318ca6a08..f909f054e49 100644 --- a/addons/sale_crm/wizard/makesale.py +++ b/addons/sale_crm/wizard/makesale.py @@ -26,6 +26,7 @@ import wizard import netsvc import ir import pooler +from tools.translate import _ sale_form = """ From 0ca01aaf1285d61612ac69e5433c6727798edb43 Mon Sep 17 00:00:00 2001 From: "GPA(OpenERP)" <> Date: Thu, 21 Jan 2010 19:18:18 +0530 Subject: [PATCH 034/328] [FIX] Base_report_creator: Export of custom reports made working lp bug: https://launchpad.net/bugs/379581 fixed bzr revid: jvo@tinyerp.com-20100121134818-90uehv1qtbk3oqp1 --- addons/base_report_creator/base_report_creator.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/addons/base_report_creator/base_report_creator.py b/addons/base_report_creator/base_report_creator.py index 34f3b549dae..a546a65254d 100644 --- a/addons/base_report_creator/base_report_creator.py +++ b/addons/base_report_creator/base_report_creator.py @@ -55,6 +55,21 @@ class report_creator(osv.osv): # # Should request only used fields # + def export_data(self, cr, uid, ids, fields_to_export, context=None): + data_l = self.read(cr, uid, ids, ['sql_query'], context) + final_datas =[] + for i in data_l: + datas = [] + for key,value in i.items(): + if key not in fields_to_export: + continue + if isinstance(value,tuple): + datas.append(str(value[1])) + else: + datas.append(str(value)) + final_datas += [datas] + return {'datas':final_datas} + def fields_get(self, cr, user, fields=None, context=None): if (not context) or 'report_id' not in context: return super(report_creator, self).fields_get(cr, user, fields, context) From 5f34983f0bbf77945567a3be7aace0798b62e634 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Thu, 21 Jan 2010 20:28:07 +0530 Subject: [PATCH 035/328] [FIX] O2M should check existing relations for its concerned M2O lp bug: https://launchpad.net/bugs/456978 fixed bzr revid: jvo@tinyerp.com-20100121145807-ixlsuj3xakibd753 --- bin/osv/orm.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 3f247b9831a..77bc161da65 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -1765,6 +1765,12 @@ class orm(orm_template): if isinstance(f, fields.one2many): cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) + + if self.pool.get(f._obj): + if f._fields_id not in self.pool.get(f._obj)._columns.keys(): + if not self.pool.get(f._obj)._inherits or (f._fields_id not in self.pool.get(f._obj)._inherit_fields.keys()): + raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id,f._obj,)) + if cr.fetchone(): cr.execute("SELECT count(1) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id)) res = cr.fetchone()[0] From 22f41a324ce2d82e9e68aab171c0fcd76b849169 Mon Sep 17 00:00:00 2001 From: "Ferdinand@Chricar" <> Date: Fri, 22 Jan 2010 16:16:09 +0530 Subject: [PATCH 036/328] [FIX] Account: Sequence and main seq. should be different lp bug: https://launchpad.net/bugs/510604 fixed bzr revid: jvo@tinyerp.com-20100122104609-tj2d0txatblx08aq --- addons/account/sequence.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/account/sequence.py b/addons/account/sequence.py index 26f2e12f468..02af766a7d7 100644 --- a/addons/account/sequence.py +++ b/addons/account/sequence.py @@ -31,6 +31,11 @@ class ir_sequence_fiscalyear(osv.osv): "sequence_main_id": fields.many2one("ir.sequence", 'Main Sequence', required=True, ondelete='cascade'), "fiscalyear_id": fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, ondelete='cascade') } + + _sql_constraints = [ + ('main_id', 'CHECK (sequence_main_id != sequence_id)', 'Main Sequence must be different from current !'), + ] + ir_sequence_fiscalyear() class ir_sequence(osv.osv): From 7681deb834152087166b72896be7c64059da55c3 Mon Sep 17 00:00:00 2001 From: "mra (Open ERP)" Date: Fri, 22 Jan 2010 16:36:53 +0530 Subject: [PATCH 037/328] [FIX] account: set correct journal while opening customer and supplier invoices and refunds bzr revid: mra@tinyerp.com-20100122110653-lck2y7wkk5b8yyu7 --- addons/account/invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/account/invoice.py b/addons/account/invoice.py index c5d9b5b2eb7..0ddd831fa13 100644 --- a/addons/account/invoice.py +++ b/addons/account/invoice.py @@ -63,8 +63,9 @@ class account_invoice(osv.osv): context = {} type_inv = context.get('type', 'out_invoice') type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'} + refund_journal = {'out_invoice': False, 'in_invoice': False, 'out_refund': True, 'in_refund': True} journal_obj = self.pool.get('account.journal') - res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale'))], limit=1) + res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')), ('refund_journal', '=', refund_journal.get(type_inv, False))], limit=1) if res: return res[0] else: From f14e638fa6cb7fc8fdb65419b757b4d1f1ef69d4 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 22 Jan 2010 17:39:30 +0530 Subject: [PATCH 038/328] [FIX] Account : Onchange of product should affect price on supplier invoice line lp bug: https://launchpad.net/bugs/510692 fixed bzr revid: jvo@tinyerp.com-20100122120930-ppgd22tfug34nav3 --- addons/account/invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/invoice.py b/addons/account/invoice.py index 0ddd831fa13..cd8ba660ba7 100644 --- a/addons/account/invoice.py +++ b/addons/account/invoice.py @@ -1064,7 +1064,7 @@ class account_invoice_line(osv.osv): taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False) tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes) if type in ('in_invoice', 'in_refund'): - to_update = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context) + to_update = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit or res.standard_price, qty, address_invoice_id, product, partner_id, context=context) result.update(to_update) else: result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id}) From 2167877520c35973493e7e94506c7449f629f3a1 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 22 Jan 2010 18:50:35 +0530 Subject: [PATCH 039/328] [FIX] _inherits table may delete the record which is parental to other object but not required. bzr revid: jvo@tinyerp.com-20100122132035-yzquxa1g123im9jq --- bin/osv/orm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 77bc161da65..893b39909fc 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -2265,6 +2265,8 @@ class orm(orm_template): del r['id'] for record in res: + if not record[col]:# if the record is deleted from _inherits table? + continue record.update(res3[record[col]]) if col not in fields_to_read: del record[col] From 6ba1955ac1d0e009b238908d0f5be2adf8f44220 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 22 Jan 2010 19:47:46 +0530 Subject: [PATCH 040/328] [REF/FIX] Account : Minor corrections related to _unit_compute_inv() lp bug: https://launchpad.net/bugs/511198 fixed bzr revid: jvo@tinyerp.com-20100122141746-efo23pejj51a4nw5 --- addons/account/account.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index 4e29bb1901f..af9459082cd 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1478,8 +1478,8 @@ class account_tax(osv.osv): exec tax.python_compute_inv in localdict amount = localdict['result'] elif tax.type=='balance': - data['amount'] = cur_price_unit - reduce(lambda x,y: y.get('amount',0.0)+x, res, 0.0) - data['balance'] = cur_price_unit + amount = cur_price_unit - reduce(lambda x,y: y.get('amount',0.0)+x, res, 0.0) +# data['balance'] = cur_price_unit if tax.include_base_amount: From 20a4f9aa6627c0e2e70ae8c9ea45512a823f0cbc Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 22 Jan 2010 20:09:08 +0530 Subject: [PATCH 041/328] [FIX] Import should raise an uncaught exception if not identified lp bug: https://launchpad.net/bugs/510600 fixed bzr revid: jvo@tinyerp.com-20100122143908-7qxkxrmc5gcuwlq3 --- bin/osv/orm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 893b39909fc..feef5f00447 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -862,6 +862,8 @@ class orm_template(object): if isinstance(e, osv.orm.except_orm ): msg = _('Insertion Failed! ' + e[1]) return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' ) + #Raising Uncaught exception + raise for lang in translate: context2 = context.copy() context2['lang'] = lang From 1fa671cd45499f4b8447169b1c341bdef0603f69 Mon Sep 17 00:00:00 2001 From: "qdp (OpenERP)" <> Date: Mon, 25 Jan 2010 10:32:56 +0530 Subject: [PATCH 042/328] [IMP] event_project: change the string on event object all task to remaining task and put the domain on retro planning wizard(open only projects with state=template) bzr revid: mra@tinyerp.com-20100125050256-koxn20my54u7bnpd --- addons/event_project/event_view.xml | 3 +-- addons/event_project/wizard/project_wizard.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/event_project/event_view.xml b/addons/event_project/event_view.xml index 011eb01bb40..2c19f0023b2 100644 --- a/addons/event_project/event_view.xml +++ b/addons/event_project/event_view.xml @@ -25,9 +25,8 @@ form - - + diff --git a/addons/event_project/wizard/project_wizard.py b/addons/event_project/wizard/project_wizard.py index f8996c8fa35..228c5f3c47e 100644 --- a/addons/event_project/wizard/project_wizard.py +++ b/addons/event_project/wizard/project_wizard.py @@ -37,7 +37,7 @@ parameter_form = ''' ''' parameter_fields = { - 'project_id': {'string':'Project', 'type':'many2one', 'required':True, 'relation':'project.project', 'domain': [('active','<>',False)]}, + 'project_id': {'string':'Project', 'type':'many2one', 'required':True, 'relation':'project.project', 'domain': [('active','<>',False),('state','=','template')]}, } def _create_duplicate(self, cr, uid, data, context): From db12e88f2b48edab95e33399bfbd233039a9f56e Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 25 Jan 2010 14:22:56 +0530 Subject: [PATCH 043/328] [FIX] Account: Bank statement line,if has write-off entries,should pass write off account to move line bzr revid: jvo@tinyerp.com-20100125085256-jqxoqe1xlg8amx02 --- addons/account/account_bank_statement.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index f725a5477ba..2b0c72abb46 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -287,7 +287,14 @@ class account_bank_statement(osv.osv): torec += map(lambda x: x.id, move.reconcile_id.line_ids) #try: if abs(move.reconcile_amount-move.amount)<0.0001: - account_move_line_obj.reconcile(cr, uid, torec, 'statement', writeoff_period_id=st.period_id.id, writeoff_journal_id=st.journal_id.id, context=context) + + writeoff_acc_id = False + #There should only be one write-off account! + for entry in move.reconcile_id.line_new_ids: + writeoff_acc_id = entry.account_id.id + break + + account_move_line_obj.reconcile(cr, uid, torec, 'statement', writeoff_acc_id=writeoff_acc_id, writeoff_period_id=st.period_id.id, writeoff_journal_id=st.journal_id.id, context=context) else: account_move_line_obj.reconcile_partial(cr, uid, torec, 'statement', context) #except: From 470f5bf1258947b8fe1439f1143bc4d7c665e1c8 Mon Sep 17 00:00:00 2001 From: "Borja Lopez Soilan (Pexego)" <> Date: Mon, 25 Jan 2010 10:41:23 +0100 Subject: [PATCH 044/328] [FIX] Remove the useless cr.commit() in the get_id() method of the ir.sequence object lp bug: https://launchpad.net/bugs/462285 fixed bzr revid: stephane@openerp.com-20100125094123-kg19t7rebg1am1fr --- bin/addons/base/ir/ir_sequence.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py index f48ea0f25c4..dab7bf807d1 100644 --- a/bin/addons/base/ir/ir_sequence.py +++ b/bin/addons/base/ir/ir_sequence.py @@ -71,17 +71,14 @@ class ir_sequence(osv.osv): } def get_id(self, cr, uid, sequence_id, test='id=%s', context=None): - try: - cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+' AND active=%s FOR UPDATE', (sequence_id, True)) - res = cr.dictfetchone() - if res: - cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True)) - if res['number_next']: - return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) - else: - return self._process(res['prefix']) + self._process(res['suffix']) - finally: - cr.commit() + cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+' AND active=%s FOR UPDATE', (sequence_id, True)) + res = cr.dictfetchone() + if res: + cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True)) + if res['number_next']: + return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) + else: + return self._process(res['prefix']) + self._process(res['suffix']) return False def get(self, cr, uid, code): From cab8f8f0fb5d4fa0e7957d27942bdfd383ef399f Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 25 Jan 2010 18:04:53 +0530 Subject: [PATCH 045/328] [FIX] Related with O2M behavior corrected lp bug: https://launchpad.net/bugs/413121 fixed bzr revid: jvo@tinyerp.com-20100125123453-ahsnyt52zy8du9oc --- bin/osv/fields.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/osv/fields.py b/bin/osv/fields.py index c99fafe7af7..541b9e77e71 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -711,15 +711,18 @@ class related(function): field_detail = self._relations[i] relation = field_detail['object'] if not t_data[self.arg[i]]: + if self._type not in ('one2many', 'many2many'): + t_id = t_data['id'] t_data = False break if field_detail['type'] in ('one2many', 'many2many'): if self._type != "many2one": - t_id=t_data.id + t_id = t_data.id t_data = t_data[self.arg[i]][0] else: - t_id=t_data['id'] + t_id = t_data['id'] t_data = t_data[self.arg[i]] + if t_id: obj.pool.get(field_detail['object']).write(cr,uid,[t_id],{args[-1]:values}, context=context) From 4678c890a665b399e06491741c3eddc8527f9e6f Mon Sep 17 00:00:00 2001 From: "mra (Open ERP)" Date: Wed, 27 Jan 2010 12:10:35 +0530 Subject: [PATCH 046/328] [FIX] account: At the time of invoice cancel it will remove partial reconcile id from entry bzr revid: mra@tinyerp.com-20100127064035-oy6ygr97qeish73k --- addons/account/invoice.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/account/invoice.py b/addons/account/invoice.py index cd8ba660ba7..bd09445ee9f 100644 --- a/addons/account/invoice.py +++ b/addons/account/invoice.py @@ -749,7 +749,7 @@ class account_invoice(osv.osv): def action_cancel(self, cr, uid, ids, *args): account_move_obj = self.pool.get('account.move') - invoices = self.read(cr, uid, ids, ['move_id']) + invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids']) for i in invoices: if i['move_id']: account_move_obj.button_cancel(cr, uid, [i['move_id'][0]]) @@ -757,6 +757,8 @@ class account_invoice(osv.osv): # Note that the corresponding move_lines and move_reconciles # will be automatically deleted too account_move_obj.unlink(cr, uid, [i['move_id'][0]]) + if i['payment_ids']: + self.pool.get('account.move.line').write(cr, uid, i['payment_ids'], {'reconcile_partial_id': False}) self.write(cr, uid, ids, {'state':'cancel', 'move_id':False}) self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice') return True From 08e60b16bf9e4017250cff6b3f605da4122184cb Mon Sep 17 00:00:00 2001 From: Mantavya Gajjar Date: Wed, 27 Jan 2010 16:09:06 +0530 Subject: [PATCH 047/328] [FIX]: fix a prolem for the wiki content for the indexing bzr revid: mga@tinyerp.com-20100127103906-smwyml2b64vb2fih --- addons/wiki/wiki.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/wiki/wiki.py b/addons/wiki/wiki.py index 7f97f08bb14..ea4660cbb7c 100644 --- a/addons/wiki/wiki.py +++ b/addons/wiki/wiki.py @@ -69,7 +69,7 @@ class Wiki(osv.osv): _columns={ 'name':fields.char('Title', size=256, select=True, required=True), 'write_uid':fields.many2one('res.users',"Last Author"), - 'text_area':fields.text("Content", select=True), + 'text_area':fields.text("Content"), 'create_uid':fields.many2one('res.users','Author', select=True), 'create_date':fields.datetime("Created on", select=True), 'write_date':fields.datetime("Modification Date", select=True), @@ -143,7 +143,7 @@ class History(osv.osv): _order = 'id DESC' _columns={ 'create_date':fields.datetime("Date",select=True), - 'text_area':fields.text("Text area",select=True), + 'text_area':fields.text("Text area"), 'minor_edit':fields.boolean('This is a major edit ?',select=True), 'summary':fields.char('Summary',size=256, select=True), 'write_uid':fields.many2one('res.users',"Modify By", select=True), From 00064d587f4deedf05f2d56e698b374d48290692 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Wed, 27 Jan 2010 19:38:03 +0530 Subject: [PATCH 048/328] [REF] Typo corrected lp bug: https://launchpad.net/bugs/513240 fixed bzr revid: jvo@tinyerp.com-20100127140803-40961occk7uudyl6 --- addons/account_tax_include/invoice_tax_incl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account_tax_include/invoice_tax_incl.py b/addons/account_tax_include/invoice_tax_incl.py index a464580a2f2..94be2c2071b 100644 --- a/addons/account_tax_include/invoice_tax_incl.py +++ b/addons/account_tax_include/invoice_tax_incl.py @@ -100,8 +100,8 @@ class account_invoice_line(osv.osv): res[line.id]['price_subtotal'] = res[line.id]['price_subtotal'] - tax['amount'] res[line.id]['data'].append( tax) - res[line.id]['price_subtotal']= round(res[line.id]['price_subtotal'], int(config['price_accuracy'])) - res[line.id]['price_subtotal_incl']= round(res[line.id]['price_subtotal_incl'], int(config['price_accuracy'])) + res[line.id]['price_subtotal']= round(res[line.id]['price_subtotal'], int(config['price_accuracy'])) + res[line.id]['price_subtotal_incl']= round(res[line.id]['price_subtotal_incl'], int(config['price_accuracy'])) return res def _price_unit_default(self, cr, uid, context=None): From 656b1dd1be8900b7292955e1182158ad1a98dc74 Mon Sep 17 00:00:00 2001 From: "GPA(OpenERP)" <> Date: Wed, 27 Jan 2010 20:01:01 +0530 Subject: [PATCH 049/328] [FIX] Product :Uom conversion was made 2 times lp bug: https://launchpad.net/bugs/513230 fixed bzr revid: jvo@tinyerp.com-20100127143101-02ef025l4mv8nr85 --- addons/product/pricelist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 47da61efcdd..d10c7562692 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -230,7 +230,7 @@ class product_pricelist(osv.osv): price = currency_obj.compute(cr, uid, price_type.currency_id.id, res['currency_id'], product_obj.price_get(cr, uid, [prod_id], - price_type.field, context=context)[prod_id], round=False, context=context) + price_type.field)[prod_id], round=False, context=context) price_limit = price From 03c74daf840952df3300367d343aa6946a949337 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Wed, 27 Jan 2010 20:12:27 +0530 Subject: [PATCH 050/328] [FIX] Ir_actions : Unicode error corrected lp bug: https://launchpad.net/bugs/493548 fixed bzr revid: jvo@tinyerp.com-20100127144227-v8ktghs92y5j37by --- bin/addons/base/ir/ir_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index 807fa9c266b..ccb0e244ef5 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -481,7 +481,7 @@ class actions_server(osv.osv): result = eval(exp, {'object':obj, 'context': context,'time':time}) if result in (None, False): return str("--------") - return str(result) + return tools.ustr(result) com = re.compile('(\[\[.+?\]\])') message = com.sub(merge, keystr) From a2236246063d353820f5bb1e15c918e4bd4e4967 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Wed, 27 Jan 2010 20:19:10 +0530 Subject: [PATCH 051/328] [REF] Minor edit bzr revid: jvo@tinyerp.com-20100127144910-l7boiga2m3zvk1ke --- bin/service/web_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/service/web_services.py b/bin/service/web_services.py index ed3b06e637c..d99dc5df5d6 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -523,7 +523,7 @@ GNU Public Licence. try: rev_id = os.popen('bzr revision-info').read() except Exception,e: - rev_id = 'Exception: %s\n' % (tools.ustr(e)) + rev_id = 'Exception: %s\n' % (tools.ustr(e)) os_lang = '.'.join( [x for x in locale.getdefaultlocale() if x] ) if not os_lang: From 9eaac508977084afae06a39695e144ade18890ad Mon Sep 17 00:00:00 2001 From: "CHS,JVO" <> Date: Thu, 28 Jan 2010 10:41:35 +0530 Subject: [PATCH 052/328] [FIX] Behavior of server corrected if addons-path is wrong lp bug: https://launchpad.net/bugs/373176 fixed bzr revid: jvo@tinyerp.com-20100128051135-bpbbww23dcmpjd9g --- bin/addons/__init__.py | 2 +- bin/tools/config.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index 4364a5ed555..dfe081808cd 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -311,7 +311,7 @@ def upgrade_graph(graph, cr, module_list, force=None): mod_path = get_module_path(module) terp_file = get_module_resource(module, '__terp__.py') if not mod_path or not terp_file: - cr.execute("update ir_module_module set state=%s where name=%s", ('uninstallable', module)) + #Removed the query which was setting the module state uninstallable if module is not found continue if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'): diff --git a/bin/tools/config.py b/bin/tools/config.py index b8f90ec33d7..faa3168c993 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -327,6 +327,18 @@ class configmanager(object): res = os.path.abspath(os.path.expanduser(value)) if not os.path.exists(res): raise optparse.OptionValueError("option %s: no such directory: %r" % (opt, value)) + + contains_addons = False + for f in os.listdir(res): + if os.path.exists(res, f, '__init__.py') and \ + os.path.exists(res, f, '__terp__.py'): + contains_addons = True + break + + if not contains_addons: + raise optparse.OptionValueError("option %s: The addons-path %r does not seem to a be a valid Addons Directory!" % (opt, value)) + + setattr(parser.values, option.dest, res) def load(self): From 877cf0e75549f8dd6b75c4cf157aed7eeb550008 Mon Sep 17 00:00:00 2001 From: "CHS,JVO" <> Date: Thu, 28 Jan 2010 11:00:18 +0530 Subject: [PATCH 053/328] [FIX] Behavior of server corrected if addons-path is wrong lp bug: https://launchpad.net/bugs/373176 fixed bzr revid: jvo@tinyerp.com-20100128053018-vn583kr39usmuzfa --- bin/tools/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/tools/config.py b/bin/tools/config.py index faa3168c993..82bc93857ed 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -330,8 +330,9 @@ class configmanager(object): contains_addons = False for f in os.listdir(res): - if os.path.exists(res, f, '__init__.py') and \ - os.path.exists(res, f, '__terp__.py'): + modpath = os.path.join(res, f) + if os.path.isdir(modpath) and os.path.exists(os.path.join(modpath, '__init__.py')) and \ + os.path.exists(os.path.join(modpath, '__terp__.py')): contains_addons = True break From 9633423bccf28003a7f0785fbf7b67a96372355d Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 29 Jan 2010 10:38:46 +0100 Subject: [PATCH 054/328] [FIX] Disable the cache for the ir.rules lp bug: https://launchpad.net/bugs/372747 fixed bzr revid: stephane@openerp.com-20100129093846-b1hbz5as9p4olvv4 --- bin/addons/base/ir/ir_rule.py | 39 ------------------------------ bin/addons/base/res/res_company.py | 1 - bin/addons/base/res/res_user.py | 2 -- 3 files changed, 42 deletions(-) diff --git a/bin/addons/base/ir/ir_rule.py b/bin/addons/base/ir/ir_rule.py index 7e5a2d05155..0eea6a11369 100644 --- a/bin/addons/base/ir/ir_rule.py +++ b/bin/addons/base/ir/ir_rule.py @@ -43,24 +43,6 @@ class ir_rule_group(osv.osv): 'global': lambda *a: True, } - def unlink(self, cr, uid, ids, context=None): - res = super(ir_rule_group, self).unlink(cr, uid, ids, context=context) - # Restart the cache on the domain_get method of ir.rule - self.pool.get('ir.rule').domain_get.clear_cache(cr.dbname) - return res - - def create(self, cr, user, vals, context=None): - res = super(ir_rule_group, self).create(cr, user, vals, context=context) - # Restart the cache on the domain_get method of ir.rule - self.pool.get('ir.rule').domain_get.clear_cache(cr.dbname) - return res - - def write(self, cr, uid, ids, vals, context=None): - res = super(ir_rule_group, self).write(cr, uid, ids, vals, context=context) - # Restart the cache on the domain_get method of ir.rule - self.pool.get('ir.rule').domain_get.clear_cache(cr.dbname) - return res - ir_rule_group() @@ -210,27 +192,6 @@ class ir_rule(osv.osv): if query: query = '('+query+')' return query, val - domain_get = tools.cache()(domain_get) - - def unlink(self, cr, uid, ids, context=None): - res = super(ir_rule, self).unlink(cr, uid, ids, context=context) - # Restart the cache on the domain_get method of ir.rule - self.domain_get.clear_cache(cr.dbname) - return res - - def create(self, cr, user, vals, context=None): - res = super(ir_rule, self).create(cr, user, vals, context=context) - # Restart the cache on the domain_get method of ir.rule - self.domain_get.clear_cache(cr.dbname) - return res - - def write(self, cr, uid, ids, vals, context=None): - if not context: - context={} - res = super(ir_rule, self).write(cr, uid, ids, vals, context=context) - # Restart the cache on the domain_get method - self.domain_get.clear_cache(cr.dbname) - return res ir_rule() diff --git a/bin/addons/base/res/res_company.py b/bin/addons/base/res/res_company.py index 2158035abb3..87c81f95e09 100644 --- a/bin/addons/base/res/res_company.py +++ b/bin/addons/base/res/res_company.py @@ -82,7 +82,6 @@ class res_company(osv.osv): def write(self, cr, *args, **argv): self.cache_restart(cr) # Restart the cache on the company_get method - self.pool.get('ir.rule').domain_get.clear_cache(cr.dbname) return super(res_company, self).write(cr, *args, **argv) def _get_euro(self, cr, uid, context={}): diff --git a/bin/addons/base/res/res_user.py b/bin/addons/base/res/res_user.py index da36c0d7555..a3c3923d17c 100644 --- a/bin/addons/base/res/res_user.py +++ b/bin/addons/base/res/res_user.py @@ -48,7 +48,6 @@ class groups(osv.osv): _('The name of the group can not start with "-"')) res = super(groups, self).write(cr, uid, ids, vals, context=context) # Restart the cache on the company_get method - self.pool.get('ir.rule').domain_get.clear_cache(cr.dbname) self.pool.get('ir.model.access').call_cache_clearing_methods(cr) return res @@ -185,7 +184,6 @@ class users(osv.osv): res = super(users, self).write(cr, uid, ids, values, *args, **argv) self.company_get.clear_cache(cr.dbname) # Restart the cache on the company_get method - self.pool.get('ir.rule').domain_get.clear_cache(cr.dbname) self.pool.get('ir.model.access').call_cache_clearing_methods(cr) return res From ee33d85b03b66815a4bfc186a755e761f321af61 Mon Sep 17 00:00:00 2001 From: "YYA(Axelor)" <> Date: Fri, 29 Jan 2010 15:43:14 +0530 Subject: [PATCH 055/328] [FIX] Base_report_creator : ilike query generation problem corrected bzr revid: jvo@tinyerp.com-20100129101314-6nakxk4khllg5ff7 --- addons/base_report_creator/wizard/wiz_set_filter_fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/base_report_creator/wizard/wiz_set_filter_fields.py b/addons/base_report_creator/wizard/wiz_set_filter_fields.py index 851eed0dbb1..1975de53185 100644 --- a/addons/base_report_creator/wizard/wiz_set_filter_fields.py +++ b/addons/base_report_creator/wizard/wiz_set_filter_fields.py @@ -85,13 +85,13 @@ def set_field_operator(self,field_name,field_type,search_operator,search_value): field_search[2] = "("+','.join([str(x) for x in search_value])+")" else: field_search[1] = 'ilike' - field_search[2] = "'%"+str(search_value)+"%'" + field_search[2] = "'%%"+str(search_value)+"%%'" elif search_operator == 'not in': if field_type=='many2one': field_search[2] = "("+','.join([str(x) for x in search_value])+")" else: field_search[1] = 'not ilike' - field_search[2] = "'%"+str(search_value)+"%'" + field_search[2] = "'%%"+str(search_value)+"%%'" elif search_operator == '^': if field_type in char_type: field_search[1]='~' From 5a3cca40c9c9b6ef7546092be35d81f864279177 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 29 Jan 2010 17:00:35 +0530 Subject: [PATCH 056/328] [FIX] M2O : Unfair behavior of M2O for access denial records and normal ones(All records were hidden by access denied if one had failed to meet access rights). lp bug: https://launchpad.net/bugs/514231 fixed bzr revid: jvo@tinyerp.com-20100129113035-8zpefjjaj8snhndd --- bin/osv/fields.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/bin/osv/fields.py b/bin/osv/fields.py index 541b9e77e71..1bd31c507cc 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -306,14 +306,16 @@ class many2one(_column): obj = obj.pool.get(self._obj) # build a dictionary of the form {'id_of_distant_resource': name_of_distant_resource} from orm import except_orm - try: - names = dict(obj.name_get(cr, user, filter(None, res.values()), context)) - except except_orm: - names = {} - iids = filter(None, res.values()) - for iiid in iids: - names[iiid] = '// Access Denied //' + names = {} + for record in list(set(filter(None, res.values()))): + try: + record_name = dict(obj.name_get(cr, user, [record], context)) + except except_orm: + record_name = {} + record_name[record] = '// Access Denied //' + names.update(record_name) + for r in res.keys(): if res[r] and res[r] in names: res[r] = (res[r], names[res[r]]) From e5be0142c4e4a8caaf79687a87089a10e781f98c Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 29 Jan 2010 19:57:43 +0530 Subject: [PATCH 057/328] [FIX] Account : wizard_pay_invoice corrected (Check made to calculate only receivable,payable lines) lp bug: https://launchpad.net/bugs/496889 fixed bzr revid: jvo@tinyerp.com-20100129142743-gkslge17jcfmewar --- addons/account/wizard/wizard_pay_invoice.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/account/wizard/wizard_pay_invoice.py b/addons/account/wizard/wizard_pay_invoice.py index bd0bacadd93..60e5a38ea4e 100644 --- a/addons/account/wizard/wizard_pay_invoice.py +++ b/addons/account/wizard/wizard_pay_invoice.py @@ -99,12 +99,12 @@ def _wo_check(self, cr, uid, data, context): # => Ask to a write-off of the difference. This could happen even if both amount are equal, # because if the currency rate # Get the amount in company currency for the invoice (according to move lines) - inv_amount_company_currency = invoice.move_id.amount -# for aml in invoice.move_id.line_id: -# if aml.account_id.id == invoice.account_id.id or aml.account_id.type in ('receivable', 'payable'): -# inv_amount_company_currency += aml.debit -# inv_amount_company_currency -= aml.credit -# inv_amount_company_currency = abs(inv_amount_company_currency) + inv_amount_company_currency = 0 + for aml in invoice.move_id.line_id: + if aml.account_id.id == invoice.account_id.id or aml.account_id.type in ('receivable', 'payable'): + inv_amount_company_currency += aml.debit + inv_amount_company_currency -= aml.credit + inv_amount_company_currency = abs(inv_amount_company_currency) # Get the current amount paid in company currency if journal.currency and invoice.company_id.currency_id.id<>journal.currency.id: From b1643c7af602f7724e87867569ca047669d45d20 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Mon, 1 Feb 2010 08:58:52 +0100 Subject: [PATCH 058/328] [FIX] base_contact: search in "Contact's Jobs" now also search in firstname and lastname of contacts bzr revid: olt@tinyerp.com-20100201075852-eomf4z7an0rno10r --- addons/base_contact/base_contact.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/addons/base_contact/base_contact.py b/addons/base_contact/base_contact.py index ab42ba94571..ca4fb93bced 100644 --- a/addons/base_contact/base_contact.py +++ b/addons/base_contact/base_contact.py @@ -114,17 +114,29 @@ class res_partner_job(osv.osv): res = [] for r in self.browse(cr, uid, ids): funct = r.function_id and (", " + r.function_id.name) or "" - res.append((r.id, self.pool.get('res.partner.contact').name_get(cr, uid, [r.contact_id.id])[0][1] + funct )) + res.append((r.id, self.pool.get('res.partner.contact').name_get(cr, uid, [r.contact_id.id])[0][1] + funct)) return res - def search(self, cr, user, args, offset=0, limit=None, order=None, - context=None, count=False): + def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): + job_ids = [] for arg in args: - if arg[0]=='address_id': + if arg[0] == 'address_id': self._order = 'sequence_partner' - if arg[0]=='contact_id': + elif arg[0] == 'contact_id': self._order = 'sequence_contact' - return super(res_partner_job,self).search(cr, user, args, offset, limit, order, context, count) + elif arg[0] == 'name': + contact_obj = self.pool.get('res.partner.contact') + search_arg = ['|', ('first_name', 'ilike', arg[2]), ('name', 'ilike', arg[2])] + contact_ids = contact_obj.search(cr, user, search_arg, offset=offset, limit=limit, order=order, context=context, count=count) + contacts = contact_obj.browse(cr, user, contact_ids, context=context) + for contact in contacts: + job_ids.extend([item.id for item in contact.job_ids]) + + res = super(res_partner_job,self).search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count) + if job_ids: + res = list(set(res + job_ids)) + + return res _name = 'res.partner.job' _description ='Contact Partner Function' From 6ce9dec0138d246229f3cff7f7d7db5157ebb803 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 1 Feb 2010 14:17:36 +0530 Subject: [PATCH 059/328] [FIX] Analytic_user_function : Corrected the Wrong way call to the method of product.uom bzr revid: jvo@tinyerp.com-20100201084736-kj4if8506grjy8gz --- addons/analytic_user_function/analytic_user_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/analytic_user_function/analytic_user_function.py b/addons/analytic_user_function/analytic_user_function.py index 3c8972f4df9..16d0f01a7de 100644 --- a/addons/analytic_user_function/analytic_user_function.py +++ b/addons/analytic_user_function/analytic_user_function.py @@ -132,7 +132,7 @@ class hr_analytic_timesheet(osv.osv): _('There is no expense account define ' \ 'for this product: "%s" (id:%d)') % \ (r.product_id.name, r.product_id.id,)) - amount = unit_amount * r.product_id.uom_id._compute_price(cr, uid, + amount = unit_amount * self.pool.get('product.uom')._compute_price(cr, uid, r.product_id.uom_id.id, r.product_id.standard_price, False) res ['value']['amount']= - round(amount, 2) res ['value']['general_account_id']= a From abc44a50cbffdab7372e598f4a2f1fcf71b5e4f1 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 1 Feb 2010 17:16:05 +0530 Subject: [PATCH 060/328] [FIX] Custom fields should be removed from _columns too lp bug: https://launchpad.net/bugs/490125 fixed bzr revid: jvo@tinyerp.com-20100201114605-6klss5f5bqw5yb6o --- bin/addons/base/ir/ir_model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index 29bd130a400..8b2c52a33a4 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -235,10 +235,12 @@ class ir_model_fields(osv.osv): def unlink(self, cr, user, ids, context=None): for field in self.browse(cr, user, ids, context): if field.state <> 'manual': - raise except_orm(_('Error'), _("You can not remove the field '%s' !") %(field.name,)) + raise except_orm(_('Error'), _("You cannot remove the field '%s' !") %(field.name,)) # # MAY BE ADD A ALTER TABLE DROP ? # + #Removing _columns entry for that table + self.pool.get(field.model)._columns.pop(field.name,None) return super(ir_model_fields, self).unlink(cr, user, ids, context) def create(self, cr, user, vals, context=None): From 3c484bca12fb86149af0dd9725be9f33e3343586 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Mon, 1 Feb 2010 13:45:58 +0100 Subject: [PATCH 061/328] [FIX] sale_delivery_report: typo in french translation file bzr revid: olt@tinyerp.com-20100201124558-dt9204wz55om09uc --- addons/sale_delivery_report/i18n/fr_FR.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_delivery_report/i18n/fr_FR.po b/addons/sale_delivery_report/i18n/fr_FR.po index 557a16c5148..e7a0c701f41 100644 --- a/addons/sale_delivery_report/i18n/fr_FR.po +++ b/addons/sale_delivery_report/i18n/fr_FR.po @@ -39,7 +39,7 @@ msgstr "Désignation" #. module: sale_delivery_report #: rml:sale.shipping:0 msgid "Delivery Order :" -msgstr "Ordre de ivraison :" +msgstr "Ordre de livraison :" #. module: sale_delivery_report #: model:ir.actions.report.xml,name:sale_delivery_report.report_shipping From 9cdc40a633eed1a86a466d41e487b40c154f25cb Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 2 Feb 2010 12:52:52 +0530 Subject: [PATCH 062/328] [FIX] Ir_values : reading of default values correted lp bug: https://launchpad.net/bugs/447819 fixed bzr revid: jvo@tinyerp.com-20100202072252-p35d0mpagxm7n4qj --- bin/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index feef5f00447..ce3229c5e36 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -2104,7 +2104,7 @@ class orm(orm_template): fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2] if fld_def._type in ('many2one', 'one2one'): obj = self.pool.get(fld_def._obj) - if not obj.search(cr, uid, [('id', '=', field_value)]): + if not obj.search(cr, uid, [('id', '=', field_value or False)]): continue if fld_def._type in ('many2many'): obj = self.pool.get(fld_def._obj) From 0ca22830f1c94af7102dbd64dc9d59cc291239b7 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 2 Feb 2010 13:07:02 +0530 Subject: [PATCH 063/328] [REM] Account : Duplicated demo data removed lp bug: https://launchpad.net/bugs/515590 fixed bzr revid: jvo@tinyerp.com-20100202073702-x10zs4igvwbhkkjz --- addons/account/demo/account_minimal.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/addons/account/demo/account_minimal.xml b/addons/account/demo/account_minimal.xml index 8ab449af66a..9f15acb0449 100644 --- a/addons/account/demo/account_minimal.xml +++ b/addons/account/demo/account_minimal.xml @@ -62,7 +62,6 @@ your own accounts. Main Receivable x 40000 receivable - @@ -78,7 +77,6 @@ your own chart of account. Main Payable x 440000 payable - @@ -94,7 +92,6 @@ your own chart of account. Petty Cash x 570000 other - @@ -109,7 +106,6 @@ your own chart of account. Products Purchase x 600000 other - @@ -124,7 +120,6 @@ your own chart of account. Products Sales x 701000 other - From d0b7144c0fc46c0d50b9b80aee1208ff1fefb776 Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Tue, 2 Feb 2010 14:17:57 +0530 Subject: [PATCH 064/328] [IMP] crm_hr, crm_project: Removed repeating method and unused import statements. bzr revid: uco@tinyerp.co.in-20100202084757-0qvctmbcfknuqiw2 --- addons/crm_hr/crm_hr.py | 20 -------------------- addons/crm_project/crm_project_bug.py | 21 +-------------------- addons/crm_project/crm_project_feature.py | 22 ++-------------------- 3 files changed, 3 insertions(+), 60 deletions(-) diff --git a/addons/crm_hr/crm_hr.py b/addons/crm_hr/crm_hr.py index 7fe2f2c4878..b122cc6b9e9 100644 --- a/addons/crm_hr/crm_hr.py +++ b/addons/crm_hr/crm_hr.py @@ -19,22 +19,9 @@ # ############################################################################## -import time -import re -import os - -import mx.DateTime -import base64 - -from tools.translate import _ - -import tools from osv import fields,osv,orm -from osv.orm import except_orm - from crm import crm - class crm_job(osv.osv): _name = "crm.job" _description = "Job Cases" @@ -63,13 +50,6 @@ class crm_job(osv.osv): "the partner mentality in relation to our services.The scale has" \ "to be created with a factor for each level from 0 (Very dissatisfied) to 10 (Extremely satisfied)."), 'phonecall_id':fields.many2one ('crm.phonecall', 'Phonecall'), - - } - def onchange_categ_id(self, cr, uid, ids, categ, context={}): - if not categ: - return {'value':{}} - cat = self.pool.get('crm.case.categ').browse(cr, uid, categ, context).probability - return {'value':{'probability':cat}} crm_job() diff --git a/addons/crm_project/crm_project_bug.py b/addons/crm_project/crm_project_bug.py index 6e56e0e3746..3a7939b6484 100644 --- a/addons/crm_project/crm_project_bug.py +++ b/addons/crm_project/crm_project_bug.py @@ -19,22 +19,9 @@ # ############################################################################## -import time -import re -import os - -import mx.DateTime -import base64 - -from tools.translate import _ - -import tools from osv import fields,osv,orm -from osv.orm import except_orm - from crm import crm - class crm_project_bug(osv.osv): _name = "crm.project.bug" _description = "Project Bug Cases" @@ -72,13 +59,7 @@ class crm_project_bug(osv.osv): _defaults = { 'project_id':_get_project - } - def onchange_categ_id(self, cr, uid, ids, categ, context={}): - if not categ: - return {'value':{}} - cat = self.pool.get('crm.case.categ').browse(cr, uid, categ, context).probability - return {'value':{'probability':cat}} - + } crm_project_bug() diff --git a/addons/crm_project/crm_project_feature.py b/addons/crm_project/crm_project_feature.py index 77314ec32de..e80be383987 100644 --- a/addons/crm_project/crm_project_feature.py +++ b/addons/crm_project/crm_project_feature.py @@ -19,19 +19,7 @@ # ############################################################################## -import time -import re -import os - -import mx.DateTime -import base64 - -from tools.translate import _ - -import tools from osv import fields,osv,orm -from osv.orm import except_orm - from crm import crm class crm_project_future_request(osv.osv): @@ -70,13 +58,7 @@ class crm_project_future_request(osv.osv): _defaults = { 'project_id':_get_project - } - def onchange_categ_id(self, cr, uid, ids, categ, context={}): - if not categ: - return {'value':{}} - cat = self.pool.get('crm.case.categ').browse(cr, uid, categ, context).probability - return {'value':{'probability':cat}} - - + } + crm_project_future_request() From 0c14c9360c0731b67edd4ebe138d05ad99914958 Mon Sep 17 00:00:00 2001 From: "GPA(OpenERP)" <> Date: Tue, 2 Feb 2010 15:15:23 +0530 Subject: [PATCH 065/328] [FIX] Sale : Order lines should be not editable for non-draft SO. lp bug: https://launchpad.net/bugs/513983 fixed bzr revid: jvo@tinyerp.com-20100202094523-2a6l3i4o2q9147bt --- addons/sale/sale.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index ee47dea4a76..ad378f19f3c 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -734,31 +734,31 @@ class sale_order_line(osv.osv): _name = 'sale.order.line' _description = 'Sale Order line' _columns = { - 'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True), - 'name': fields.char('Description', size=256, required=True, select=True), + 'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}), + 'name': fields.char('Description', size=256, required=True, select=True, readonly=True, states={'draft':[('readonly',False)]}), 'sequence': fields.integer('Sequence'), - 'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation the the shipping of the products to the customer"), + 'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation the the shipping of the products to the customer", readonly=True, states={'draft':[('readonly',False)]}), 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True), 'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True), 'invoiced': fields.boolean('Invoiced', readonly=True), 'procurement_id': fields.many2one('mrp.procurement', 'Procurement'), - 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), + 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy'])), readonly=True, states={'draft':[('readonly',False)]}), 'price_net': fields.function(_amount_line_net, method=True, string='Net Price', digits=(16, int(config['price_accuracy']))), 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), - 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes'), - 'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procure Method', required=True), - 'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties'), + 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft':[('readonly',False)]}), + 'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procure Method', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft':[('readonly',False)]}), 'address_allotment_id': fields.many2one('res.partner.address', 'Allotment Partner'), - 'product_uom_qty': fields.float('Quantity (UoM)', digits=(16, 2), required=True), - 'product_uom': fields.many2one('product.uom', 'Product UoM', required=True), - 'product_uos_qty': fields.float('Quantity (UoS)'), + 'product_uom_qty': fields.float('Quantity (UoM)', digits=(16, 2), required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'product_uom': fields.many2one('product.uom', 'Product UoM', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'product_uos_qty': fields.float('Quantity (UoS)', readonly=True, states={'draft':[('readonly',False)]}), 'product_uos': fields.many2one('product.uom', 'Product UoS'), 'product_packaging': fields.many2one('product.packaging', 'Packaging'), 'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True), - 'discount': fields.float('Discount (%)', digits=(16, 2)), + 'discount': fields.float('Discount (%)', digits=(16, 2), readonly=True, states={'draft':[('readonly',False)]}), 'number_packages': fields.function(_number_packages, method=True, type='integer', string='Number Packages'), 'notes': fields.text('Notes'), - 'th_weight': fields.float('Weight'), + 'th_weight': fields.float('Weight', readonly=True, states={'draft':[('readonly',False)]}), 'state': fields.selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled'), ('exception', 'Exception')], 'Status', required=True, readonly=True), 'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', string='Customer') } From 5ac0fe688a7f18976ebd056346e1b8eebc055738 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Tue, 2 Feb 2010 11:22:12 +0100 Subject: [PATCH 066/328] [FIX] sale_delivery_report: production lot reference was missing bzr revid: olt@tinyerp.com-20100202102212-4fp3l3tr3mo5s5ka --- addons/sale_delivery_report/report/shipping.rml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale_delivery_report/report/shipping.rml b/addons/sale_delivery_report/report/shipping.rml index c827cced55a..f0fd5fa282a 100644 --- a/addons/sale_delivery_report/report/shipping.rml +++ b/addons/sale_delivery_report/report/shipping.rml @@ -201,7 +201,7 @@ [[line.product_id.code ]] [[ line.product_id and line.product_id.name or '']] - [[ (line.prodlot_id and line.prodlot_id.name) or ' ' ]] + [[ (line.prodlot_id and (line.prodlot_id.name + '/' + line.prodlot_id.ref)) or ' ' ]] [[ formatLang(line.product_qty) ]] From 9148fc2662f99d7578a9688919567b94ee89e42d Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Tue, 2 Feb 2010 20:34:54 +0530 Subject: [PATCH 067/328] [FIX] Search made working for function/related fields with store=True/{...} or fnct_search=method bzr revid: jvo@tinyerp.com-20100202150454-x9kne83yzkjciidw --- bin/osv/expression.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 5a43a17ac9b..5924b62bf20 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -149,21 +149,21 @@ class expression(object): continue - if field._properties: + if field._properties and ((not field.store) or field._fnct_search): + # this is a function field - if not field.store: - if not field._fnct_search: - # the function field doesn't provide a search function and doesn't store - # values in the database, so we must ignore it : we generate a dummy leaf - self.__exp[i] = self.__DUMMY_LEAF - else: - subexp = field.search(cr, uid, table, left, [self.__exp[i]], context=context) - # we assume that the expression is valid - # we create a dummy leaf for forcing the parsing of the resulting expression - self.__exp[i] = '&' - self.__exp.insert(i + 1, self.__DUMMY_LEAF) - for j, se in enumerate(subexp): - self.__exp.insert(i + 2 + j, se) + if not field._fnct_search: + # the function field doesn't provide a search function and doesn't store + # values in the database, so we must ignore it : we generate a dummy leaf + self.__exp[i] = self.__DUMMY_LEAF + else: + subexp = field.search(cr, uid, table, left, [self.__exp[i]], context=context) + # we assume that the expression is valid + # we create a dummy leaf for forcing the parsing of the resulting expression + self.__exp[i] = '&' + self.__exp.insert(i + 1, self.__DUMMY_LEAF) + for j, se in enumerate(subexp): + self.__exp.insert(i + 2 + j, se) # else, the value of the field is store in the database, so we search on it From fff40de35d2be86b30e254d08b253999299422ee Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 2 Feb 2010 17:40:13 +0100 Subject: [PATCH 068/328] [FIX] orm: bug when browsing _inherits object bzr revid: chs@tinyerp.com-20100202164013-7o85hcmmhqcg5fvz --- bin/osv/orm.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index ce3229c5e36..01364b669b1 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -214,7 +214,16 @@ class browse_record(object): else: ids2 = data[n] if ids2: - data[n] = browse_record(self._cr, self._uid, ids2, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) + # FIXME: this happen when a _inherits object + # overwrite a field of it parent. Need + # testing to be sure we got the right + # object and not the parent one. + if not isinstance(ids2, browse_record): + data[n] = browse_record(self._cr, + self._uid, ids2, obj, self._cache, + context=self._context, + list_class=self._list_class, + fields_process=self._fields_process) else: data[n] = browse_null() else: @@ -2225,7 +2234,7 @@ class orm(orm_template): return 'length("%s") as "%s"' % (f, f) return '"%s"' % (f,) fields_pre2 = map(convert_field, fields_pre) - order_by = self._parent_order or self._order + order_by = self._parent_order or self._order for i in range(0, len(ids), cr.IN_MAX): sub_ids = ids[i:i+cr.IN_MAX] if d1: From 2ac35cfca3e7e890be276d5561a0814964fbe08a Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 2 Feb 2010 22:37:52 +0100 Subject: [PATCH 069/328] [FIX] Properly cancel a draft purchase.order when unlinking it, so correct workflow signals can be propagated to parent workflows if there are any. Note: in trunk a different mechanism should be added to automatically propagate e.g. a subflow.delete signal whenever a record with a parent workflow is unlinked. This would allow adding an explicit transition from the subflow node in the parent flow for these kind of cases. lp bug: https://launchpad.net/bugs/507694 fixed bzr revid: odo@openerp.com-20100202213752-lffj73sbobyw6ygk --- addons/purchase/purchase.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index ad1c2aef951..cc300516dd0 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -217,6 +217,13 @@ class purchase_order(osv.osv): unlink_ids.append(s['id']) else: raise osv.except_osv(_('Invalid action !'), _('Cannot delete Purchase Order(s) which are in %s State!' % s['state'])) + + # TODO: temporary fix in 5.0, to remove in 5.2 when subflows support + # automatically sending subflow.delete upon deletion + wf_service = netsvc.LocalService("workflow") + for id in unlink_ids: + wf_service.trg_validate(uid, 'purchase.order', id, 'purchase_cancel', cr) + return super(purchase_order, self).unlink(cr, uid, unlink_ids, context=context) def button_dummy(self, cr, uid, ids, context={}): From 6242ee95887223e2ee2badf651f3acadebf48b75 Mon Sep 17 00:00:00 2001 From: "GPA(OpenERP)" <> Date: Wed, 3 Feb 2010 11:17:12 +0530 Subject: [PATCH 070/328] [FIX] POS : for Invoice created from POS,Consistency should be maintained as SO. lp bug: https://launchpad.net/bugs/516361 fixed bzr revid: jvo@tinyerp.com-20100203054712-ki6z2p6ozaipx58s --- addons/point_of_sale/pos.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/point_of_sale/pos.py b/addons/point_of_sale/pos.py index 88466970905..9d6ca51ac52 100644 --- a/addons/point_of_sale/pos.py +++ b/addons/point_of_sale/pos.py @@ -534,12 +534,15 @@ class pos_order(osv.osv): 'product_id': line.product_id.id, 'quantity': line.qty, } + inv_name = self.pool.get('product.product').name_get(cr, uid, [line.product_id.id], context=context)[0][1] + inv_line.update(inv_line_ref.product_id_change(cr, uid, [], line.product_id.id, line.product_id.uom_id.id, line.qty, partner_id = order.partner_id.id, fposition_id=order.partner_id.property_account_position.id)['value']) inv_line['price_unit'] = line.price_unit inv_line['discount'] = line.discount + inv_line['name'] = inv_name inv_line['invoice_line_tax_id'] = ('invoice_line_tax_id' in inv_line)\ and [(6, 0, inv_line['invoice_line_tax_id'])] or [] From c4851f039c96e0215fde839e16d5b5e0400a1ab4 Mon Sep 17 00:00:00 2001 From: "SBH(OpenERP)" <> Date: Wed, 3 Feb 2010 11:47:38 +0530 Subject: [PATCH 071/328] [FIX] Base_report_designer : Latin-1 encoding replaced by utf-8 lp bug: https://launchpad.net/bugs/516143 fixed bzr revid: jvo@tinyerp.com-20100203061738-96k32cbzej09feth --- .../wizard/tiny_sxw2rml/tiny_sxw2rml.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py b/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py index 0d0ced599cb..fb16d26cda4 100644 --- a/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py +++ b/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py @@ -202,7 +202,7 @@ class DomApi(DomApiGeneral): s.appendChild(temp) c = self.findChildrenByName(s,"style:properties") c = c[0] - dict = self.style_properties_dict[(s.getAttribute("style:name")).encode("latin-1")] or {} + dict = self.style_properties_dict[(s.getAttribute("style:name")).encode("utf-8")] or {} for attribute in dict.keys(): c.setAttribute(self.openOfficeStringUtf8(attribute),self.openOfficeStringUtf8(dict[attribute])) @@ -251,7 +251,7 @@ class DomApi(DomApiGeneral): Caution: in this dict the nodes from two dom apis are merged!""" for st in (self.styles_dom,self.content_dom): for s in st.getElementsByTagName("style:style"): - name = s.getAttribute("style:name").encode("latin-1") + name = s.getAttribute("style:name").encode("utf-8") self.style_dict[name] = s return True @@ -262,14 +262,14 @@ class DomApi(DomApiGeneral): res = {} if self.style_dict[style_name].hasAttribute("style:parent-style-name"): - parent = self.style_dict[style_name].getAttribute("style:parent-style-name").encode("latin-1") + parent = self.style_dict[style_name].getAttribute("style:parent-style-name").encode("utf-8") res = self.getStylePropertiesDict(parent) childs = self.style_dict[style_name].childNodes for c in childs: if c.nodeType == c.ELEMENT_NODE and c.nodeName.find("properties")>0 : for attr in c._attrs.keys(): - res[attr] = c.getAttribute(attr).encode("latin-1") + res[attr] = c.getAttribute(attr).encode("utf-8") return res class PyOpenOffice(object): From 636a14b993b8d57899761a32680d1709193c67c8 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Wed, 3 Feb 2010 17:08:23 +0530 Subject: [PATCH 072/328] [FIX] Wizard should raise exception to user when writte inside 'choice' type lp bug: https://launchpad.net/bugs/516469 fixed bzr revid: jvo@tinyerp.com-20100203113823-03l80sd5bbf1pa6x --- bin/wizard/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/wizard/__init__.py b/bin/wizard/__init__.py index 428a85537db..e447c136b49 100644 --- a/bin/wizard/__init__.py +++ b/bin/wizard/__init__.py @@ -149,6 +149,10 @@ class interface(netsvc.Service): res['arch'] = arch res['state'] = button_list + elif result_def['type'] == 'choice': + next_state = result_def['next_state'](self, cr, uid, data, context) + return self.execute_cr(cr, uid, data, next_state, context) + except Exception, e: if isinstance(e, except_wizard) \ or isinstance(e, except_osv) \ @@ -163,9 +167,6 @@ class interface(netsvc.Service): 'Exception in call: ' + tb_s) raise - if result_def['type'] == 'choice': - next_state = result_def['next_state'](self, cr, uid, data, context) - return self.execute_cr(cr, uid, data, next_state, context) return res def execute(self, db, uid, data, state='init', context=None): From 7f1589769b193a9e127e810c95abc47eb5868bf1 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Wed, 3 Feb 2010 12:43:14 +0100 Subject: [PATCH 073/328] [FIX] sale: onchange on partner_id now set the partner's dedicated salesman as the sale salesman bzr revid: olt@tinyerp.com-20100203114314-p48a6joztsu86r3y --- addons/sale/sale.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index ad378f19f3c..4cf3ab64af6 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -309,14 +309,26 @@ class sale_order(osv.osv): def onchange_partner_id(self, cr, uid, ids, part): if not part: return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False, 'partner_order_id': False, 'payment_term': False, 'fiscal_position': False}} + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['delivery', 'invoice', 'contact']) part = self.pool.get('res.partner').browse(cr, uid, part) pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False payment_term = part.property_payment_term and part.property_payment_term.id or False fiscal_position = part.property_account_position and part.property_account_position.id or False - val = {'partner_invoice_id': addr['invoice'], 'partner_order_id': addr['contact'], 'partner_shipping_id': addr['delivery'], 'payment_term': payment_term, 'fiscal_position': fiscal_position} + dedicated_salesman = part.user_id and part.user_id.id or False + + val = { + 'partner_invoice_id': addr['invoice'], + 'partner_order_id': addr['contact'], + 'partner_shipping_id': addr['delivery'], + 'payment_term': payment_term, + 'fiscal_position': fiscal_position, + 'user_id': dedicated_salesman, + } + if pricelist: val['pricelist_id'] = pricelist + return {'value': val} def shipping_policy_change(self, cr, uid, ids, policy, context={}): From 9d3a538658649c814005604e8f9e1895b99c0183 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Wed, 3 Feb 2010 20:06:42 +0530 Subject: [PATCH 074/328] [FIX] Allowed SQL reserved keywords to be used in _order,group by ,etc.[Make sure , your _order should look like _order='to' instead of to] lp bug: https://launchpad.net/bugs/407667 fixed bzr revid: jvo@tinyerp.com-20100203143642-l72g9tpo538mbpx6 --- bin/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 01364b669b1..f7246b93678 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -63,7 +63,7 @@ except ImportError: from tools.config import config -regex_order = re.compile('^([a-z0-9_]+( *desc| *asc)?( *, *|))+$', re.I) +regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I) def last_day_of_current_month(): import datetime From 4fbdd7b65e82c2cd9177189b2fe794933365e12a Mon Sep 17 00:00:00 2001 From: "GPA(OpenERP)" <> Date: Wed, 3 Feb 2010 20:40:00 +0530 Subject: [PATCH 075/328] [FIX] Services product included on BOM now creates a task on Project lp bug: https://launchpad.net/bugs/494245 fixed bzr revid: jvo@tinyerp.com-20100203151000-necggy6sjs967d0z --- addons/project_mrp/mrp.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/addons/project_mrp/mrp.py b/addons/project_mrp/mrp.py index 75a04f27872..b6aaf526720 100644 --- a/addons/project_mrp/mrp.py +++ b/addons/project_mrp/mrp.py @@ -21,6 +21,7 @@ ############################################################################## from osv import fields, osv, orm +import tools class mrp_procurement(osv.osv): _name = "mrp.procurement" @@ -38,10 +39,18 @@ class mrp_procurement(osv.osv): l = line if line.order_id.project_id: content+="\n\n"+line.order_id.project_id.complete_name - + + # Creating a project for task.Project is created from Procurement. + proj_name = tools.ustr(procurement.name) + proj_exist_id = self.pool.get('project.project').search(cr, uid, [('name','=',proj_name)], context=context) + if not proj_exist_id: + project_id = self.pool.get('project.project').create(cr, uid, {'name':proj_name}) + else: + project_id = proj_exist_id[0] + self.write(cr, uid, [procurement.id], {'state':'running'}) task_id = self.pool.get('project.task').create(cr, uid, { - 'name': (procurement.origin or procurement.product_id.name) +': '+(procurement.name or ''), + 'name': '%s:%s' %(procurement.product_id.name or procurement.origin, procurement.name or ''), 'date_deadline': procurement.date_planned, 'planned_hours': procurement.product_qty, 'remaining_hours': procurement.product_qty, @@ -51,8 +60,9 @@ class mrp_procurement(osv.osv): 'description': content, 'date_deadline': procurement.date_planned, 'state': 'draft', - 'partner_id': l and l.order_id.partner_id.id or False - }) + 'partner_id': l and l.order_id.partner_id.id or False, + 'project_id': project_id, + },context=context) return task_id mrp_procurement() From 3d76b8d4114b343ac7c4e4d8720504bba21741b9 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 3 Feb 2010 19:31:10 +0100 Subject: [PATCH 076/328] [FIX] avoid sql injection in sequences lp bug: https://launchpad.net/bugs/512682 fixed bzr revid: chs@tinyerp.com-20100203183110-bgqjoxtjjkvhhwq4 --- bin/addons/base/ir/ir_sequence.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py index dab7bf807d1..729ee6a9590 100644 --- a/bin/addons/base/ir/ir_sequence.py +++ b/bin/addons/base/ir/ir_sequence.py @@ -71,6 +71,8 @@ class ir_sequence(osv.osv): } def get_id(self, cr, uid, sequence_id, test='id=%s', context=None): + if test not in ('id=%s', 'code=%s'): + raise ValueError('invalid test') cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+' AND active=%s FOR UPDATE', (sequence_id, True)) res = cr.dictfetchone() if res: From 3ff98ce1d9f59ea7045865b619ef78cb19c7b933 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 3 Feb 2010 19:32:55 +0100 Subject: [PATCH 077/328] [FIX] avoid sql injection in sequences lp bug: https://launchpad.net/bugs/512682 fixed bzr revid: chs@tinyerp.com-20100203183255-21lh44tfloc01wqw --- addons/account/sequence.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/account/sequence.py b/addons/account/sequence.py index 02af766a7d7..8b38fe19690 100644 --- a/addons/account/sequence.py +++ b/addons/account/sequence.py @@ -44,6 +44,8 @@ class ir_sequence(osv.osv): 'fiscal_ids' : fields.one2many('account.sequence.fiscalyear', 'sequence_main_id', 'Sequences') } def get_id(self, cr, uid, sequence_id, test='id=%s', context={}): + if test not in ('id=%s', 'code=%s'): + raise ValueError('invalid test') cr.execute('select id from ir_sequence where '+test+' and active=%s', (sequence_id, True,)) res = cr.dictfetchone() if res: From b67c51749708ea411a780f062aae8e5e0032f2f1 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Thu, 4 Feb 2010 08:12:40 +0100 Subject: [PATCH 078/328] [FIX] product: pricelist: ondelete cascade to avoid errors when deleting pricelists and versions ('null value in column "price_version_id" violates not-null constraint') bzr revid: olt@tinyerp.com-20100204071240-gvxzu0hu1utz7jgy --- addons/product/pricelist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index d10c7562692..be413273ebd 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -261,7 +261,7 @@ class product_pricelist_version(osv.osv): _description = "Pricelist Version" _columns = { 'pricelist_id': fields.many2one('product.pricelist', 'Price List', - required=True, select=True), + required=True, select=True, ondelete='cascade'), 'name': fields.char('Name', size=64, required=True, translate=True), 'active': fields.boolean('Active', help="When a version is duplicated it is set to non active, so that the " \ @@ -344,7 +344,7 @@ class product_pricelist_item(osv.osv): _columns = { 'name': fields.char('Rule Name', size=64, help="Explicit rule name for this pricelist line."), - 'price_version_id': fields.many2one('product.pricelist.version', 'Price List Version', required=True, select=True), + 'price_version_id': fields.many2one('product.pricelist.version', 'Price List Version', required=True, select=True, ondelete='cascade'), 'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade', help="Set a template if this rule only apply to a template of product. Keep empty for all products"), 'product_id': fields.many2one('product.product', 'Product', ondelete='cascade', help="Set a product if this rule only apply to one product. Keep empty for all products"), 'categ_id': fields.many2one('product.category', 'Product Category', ondelete='cascade', help="Set a category of product if this rule only apply to products of a category and his childs. Keep empty for all products"), From 64fb43920c0a78d65263aa37b7d6c2d5460cc5fe Mon Sep 17 00:00:00 2001 From: "KSH(Axelor)" <> Date: Thu, 4 Feb 2010 12:55:19 +0530 Subject: [PATCH 079/328] [FIX] Graph : report corrected lp bug: https://launchpad.net/bugs/516044 fixed bzr revid: jvo@tinyerp.com-20100204072519-7r6yu5g3r8qxlhm4 --- bin/tools/graph.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/bin/tools/graph.py b/bin/tools/graph.py index d6a26deb3c2..029bc873e9d 100644 --- a/bin/tools/graph.py +++ b/bin/tools/graph.py @@ -763,16 +763,20 @@ if __name__=='__main__': import ImageDraw img = Image.new("RGB", (800, 600), "#ffffff") draw = ImageDraw.Draw(img) - - for name,node in g.result.items(): + + result = g.result_get() + node_res = {} + for node in nodes: + node_res[node] = result[node] + + for name,node in node_res.items(): + draw.arc( (int(node['y']-radius), int(node['x']-radius),int(node['y']+radius), int(node['x']+radius) ), 0, 360, (128,128,128)) draw.text( (int(node['y']), int(node['x'])), name, (128,128,128)) - for nodefrom in g.transitions: - for nodeto in g.transitions[nodefrom]: - draw.line( (int(g.result[nodefrom]['y']), int(g.result[nodefrom]['x']),int(g.result[nodeto]['y']),int(g.result[nodeto]['x'])),(128,128,128) ) - + for t in transitions: + draw.line( (int(node_res[t[0]]['y']), int(node_res[t[0]]['x']),int(node_res[t[1]]['y']),int(node_res[t[1]]['x'])),(128,128,128) ) img.save("graph.png", "PNG") From dcce6d645d1174bec97fb3e563a914fb7a01dfa7 Mon Sep 17 00:00:00 2001 From: "GPA(OpenERP)" <> Date: Thu, 4 Feb 2010 17:17:04 +0530 Subject: [PATCH 080/328] [FIX] Account : Bank statements should not be altered/removed when state is confirmed lp bug: https://launchpad.net/bugs/516746 fixed bzr revid: jvo@tinyerp.com-20100204114704-gu2q297pf6z9w6qc --- addons/account/account_bank_statement.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 2b0c72abb46..15732174f0a 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -107,7 +107,7 @@ class account_bank_statement(osv.osv): _name = "account.bank.statement" _description = "Bank Statement" _columns = { - 'name': fields.char('Name', size=64, required=True), + 'name': fields.char('Name', size=64, required=True, states={'confirm': [('readonly', True)]}), 'date': fields.date('Date', required=True, states={'confirm': [('readonly', True)]}), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, @@ -343,6 +343,17 @@ class account_bank_statement(osv.osv): context=context)[0] return {'value': {'balance_start': balance_start, 'currency': currency}} + def unlink(self, cr, uid, ids, context=None): + stat = self.read(cr, uid, ids, ['state']) + unlink_ids = [] + for t in stat: + if t['state'] in ('draft'): + unlink_ids.append(t['id']) + else: + raise osv.except_osv(_('Invalid action !'), _('Cannot delete bank statement which are already confirmed !')) + osv.osv.unlink(self, cr, uid, unlink_ids, context=context) + return True + account_bank_statement() From 388a073a8fcad34327e9ae4f2bf30a143b98cf24 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Thu, 4 Feb 2010 17:28:36 +0530 Subject: [PATCH 081/328] [REF/FIX] Account : Importing invoice for Bank statement corrected lp bug: https://launchpad.net/bugs/517018 fixed bzr revid: jvo@tinyerp.com-20100204115836-62scrc1l4fuxgu6e --- addons/account/wizard/wizard_statement_from_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/wizard/wizard_statement_from_invoice.py b/addons/account/wizard/wizard_statement_from_invoice.py index 2a8d05bf333..6c501445b59 100644 --- a/addons/account/wizard/wizard_statement_from_invoice.py +++ b/addons/account/wizard/wizard_statement_from_invoice.py @@ -71,7 +71,7 @@ def _search_invoices(obj, cr, uid, data, context): move_line_id = line_obj.search(cr, uid, args_move_line,context=context) if move_line_id: - repeated_move_line_ids.append(move_line_id) + repeated_move_line_ids += move_line_id journal_ids = data['form']['journal_id'][0][2] From 644fb6d0fac428a5396593817ab595075261b3ed Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Tue, 2 Feb 2010 19:17:02 +0530 Subject: [PATCH 083/328] [ADD] base_action_rule: Added new module which can be used to set action rules for any object. bzr revid: uco@tinyerp.co.in-20100202134702-33lb9937qdr257y7 --- addons/base_action_rule/__init__.py | 24 +++ addons/base_action_rule/__terp__.py | 39 ++++ addons/base_action_rule/base_action_rule.py | 187 ++++++++++++++++++ .../base_action_rule_data.xml | 15 ++ .../base_action_rule_view.xml | 179 +++++++++++++++++ .../security/ir.model.access.csv | 4 + 6 files changed, 448 insertions(+) create mode 100644 addons/base_action_rule/__init__.py create mode 100644 addons/base_action_rule/__terp__.py create mode 100644 addons/base_action_rule/base_action_rule.py create mode 100644 addons/base_action_rule/base_action_rule_data.xml create mode 100644 addons/base_action_rule/base_action_rule_view.xml create mode 100644 addons/base_action_rule/security/ir.model.access.csv diff --git a/addons/base_action_rule/__init__.py b/addons/base_action_rule/__init__.py new file mode 100644 index 00000000000..c14f1081f46 --- /dev/null +++ b/addons/base_action_rule/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import base_action_rule + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/base_action_rule/__terp__.py b/addons/base_action_rule/__terp__.py new file mode 100644 index 00000000000..13040a8d5f1 --- /dev/null +++ b/addons/base_action_rule/__terp__.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Action Rule', + 'version': '1.0', + 'category': 'Generic Modules/Others', + 'description': "This module allows to implement action rules for any object.", + 'author': 'Tiny', + 'website': 'http://www.openerp.com', + 'depends': ['base'], + 'init_xml': ['base_action_rule_data.xml'], + 'update_xml': [ + 'base_action_rule_view.xml', + 'security/ir.model.access.csv', + ], + 'demo_xml': [], + 'installable': True, + 'active': False, +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py new file mode 100644 index 00000000000..65012ac501b --- /dev/null +++ b/addons/base_action_rule/base_action_rule.py @@ -0,0 +1,187 @@ +import time +import mx.DateTime + +from osv import fields, osv, orm +from osv.orm import except_orm + +AVAILABLE_STATES = [ + ('draft','Draft'), + ('open','Open'), + ('cancel', 'Cancelled'), + ('done', 'Closed'), + ('pending','Pending') +] + +AVAILABLE_PRIORITIES = [ + ('5','Lowest'), + ('4','Low'), + ('3','Normal'), + ('2','High'), + ('1','Highest') +] + +class base_action_rule(osv.osv): + _name = 'base.action.rule' + _description = 'Action Rules' + _columns = { + 'name': fields.many2one('ir.model', 'Model', required=True), + 'max_level': fields.integer('Max Level'), + 'rule_lines': fields.one2many('base.action.rule.line','rule_id','Rule Lines'), + 'create_date': fields.datetime('Create Date', readonly=1) + } + + def _check(self, cr, uid, ids=False, context={}): + ''' + Function called by the scheduler to process models + ''' + ruleobj = self.pool.get('base.action.rule') + ids = ruleobj.search(cr, uid, []) + rules = ruleobj.browse(cr, uid, ids, context) + return ruleobj._action(cr, uid, rules, False, context=context) + + def _action(self, cr, uid, rules, state_to, scrit=None, context={}): + if not scrit: + scrit = [] + history = [] + history_obj = self.pool.get('base.action.rule.history') + if rules: + cr.execute('select * from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",rules)))) + history = cr.fetchall() + checkids = map(lambda x: x[0], history or []) + + if not len(history) or len(history) < len(rules): + for rule in rules: + if rule.id not in checkids: + lastDate = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') + history_obj.create(cr, uid, {'rule_id': rule.id, 'res_id': rule.name.id, 'date_action_last': lastDate}) + + for rule in rules: + for action in rule.rule_lines: + if action.trg_date_type=='create': + base = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='action_last': + for hist in history: + if hist[6]: + base = mx.DateTime.strptime(hist[6][:19], '%Y-%m-%d %H:%M:%S') + else: + base = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') + history_obj.write(cr, uid, [hist[0]], {'date_action_last': base}) + elif action.trg_date_type=='deadline' and rule.name.date_deadline: + base = mx.DateTime.strptime(rule.name.date_deadline, '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='date' and rule.name.date: + base = mx.DateTime.strptime(rule.name.date, '%Y-%m-%d %H:%M:%S') + + if base: + fnct = { + 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), + 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), + 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), + 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), + } + d = base + fnct[action.trg_date_range_type](action.trg_date_range) + dt = d.strftime('%Y-%m-%d %H:%M:%S') + for hist in history: + if not hist[8] or dt < hist[8]: + history_obj.write(cr, uid, [hist[0]], {'date_action_next': dt}, context) + return True + +base_action_rule() + +class base_action_rule_line(osv.osv): + _name = 'base.action.rule.line' + _description = 'Action Rule Lines' + _columns = { + 'name': fields.char('Rule Name',size=64, required=True), + 'rule_id': fields.many2one('base.action.rule','Rule'), + 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the case rule without removing it."), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of case rules."), + + 'trg_state_from': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'State', size=16), + 'trg_state_to': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Button Pressed', size=16), + + 'trg_date_type': fields.selection([ + ('none','None'), + ('create','Creation Date'), + ('action_last','Last Action Date'), + ('deadline','Deadline'), + ('date','Date'), + ], 'Trigger Date', size=16), + 'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date, specifies you can put a negative number " \ + "if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."), + 'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type'), + + + 'trg_user_id': fields.many2one('res.users', 'Responsible'), + + 'trg_partner_id': fields.many2one('res.partner', 'Partner'), + 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), + + 'trg_priority_from': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Minimum Priority'), + 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Maximim Priority'), + + + 'act_method': fields.char('Call Object Method', size=64), + 'act_state': fields.selection([('','')]+AVAILABLE_STATES, 'Set state to', size=16), + 'act_user_id': fields.many2one('res.users', 'Set responsible to'), + 'act_priority': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Set priority to'), + 'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people will receive a copy of the future communication between partner and users by email"), + + 'act_remind_partner': fields.boolean('Remind Partner', help="Check this if you want the rule to send a reminder by email to the partner."), + 'act_remind_user': fields.boolean('Remind responsible', help="Check this if you want the rule to send a reminder by email to the user."), + 'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the case be attached to the reminder email sent."), + + 'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if you want the rule to send an email to the responsible person."), + 'act_mail_to_partner': fields.boolean('Mail to partner',help="Check this if you want the rule to send an email to the partner."), + 'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this if you want the rule to mark CC(mail to any other person defined in actions)."), + 'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id of the persons whom mail is to be sent"), + 'act_mail_body': fields.text('Mail body',help="Content of mail"), + 'regex_name': fields.char('Regular Expression on Case Name', size=128), + 'regex_history': fields.char('Regular Expression on Case History', size=128), + 'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the action name." \ + "eg:on which object which ation to be taken on basis of which condition"), + } + + _defaults = { + 'active': lambda *a: 1, + 'trg_date_type': lambda *a: 'none', + 'trg_date_range_type': lambda *a: 'day', + 'act_mail_to_user': lambda *a: 0, + 'act_remind_partner': lambda *a: 0, + 'act_remind_user': lambda *a: 0, + 'act_mail_to_partner': lambda *a: 0, + 'act_mail_to_watchers': lambda *a: 0, + } + + _order = 'sequence' + + def _check_mail(self, cr, uid, ids, context=None): + emptycase = orm.browse_null() + for rule in self.browse(cr, uid, ids): + if rule.act_mail_body: + try: + obj = self.pool.get(rule.rule_id.name.model) + obj.format_mail(emptycase, rule.act_mail_body) + except (ValueError, KeyError, TypeError): + return False + return True + + _constraints = [ + (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), + ] + +base_action_rule_line() + +class base_action_rule_history(osv.osv): + _name = 'base.action.rule.history' + _description = 'Action Rule History' + _rec_name = 'rule_id' + _columns = { + 'rule_id': fields.many2one('base.action.rule','Rule', required=True, readonly=1), + 'res_id': fields.integer('Resource ID', readonly=1), + 'date_action_last': fields.datetime('Last Action', readonly=1), + 'date_action_next': fields.datetime('Next Action', readonly=1), + } + +base_action_rule_history() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_action_rule/base_action_rule_data.xml b/addons/base_action_rule/base_action_rule_data.xml new file mode 100644 index 00000000000..026553e2aae --- /dev/null +++ b/addons/base_action_rule/base_action_rule_data.xml @@ -0,0 +1,15 @@ + + + + + Check Rules + 4 + hours + -1 + + + + + + + \ No newline at end of file diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml new file mode 100644 index 00000000000..b8c762cbd63 --- /dev/null +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -0,0 +1,179 @@ + + + + + + + + base.action.rule.form + base.action.rule + form + +
+ + + + + +
+ + + base.action.rule.tree + base.action.rule + tree + + + + + + + + + + + Action Rules + base.action.rule + form + tree,form + + + + + + + base.action.rule.line.form + base.action.rule.line + form + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + base.action.rule.line.tree + base.action.rule.line + tree + + + + + + + + + Action Rule Lines + base.action.rule.line + form + tree,form + + + + + + + base.action.rule.history.form + base.action.rule.history + form + +
+ + + + + + +
+ + + base.action.rule.history.tree + base.action.rule.history + tree + + + + + + + + + + + + Action Rule History + base.action.rule.history + form + tree,form + + + + +
+
\ No newline at end of file diff --git a/addons/base_action_rule/security/ir.model.access.csv b/addons/base_action_rule/security/ir.model.access.csv new file mode 100644 index 00000000000..2f09e8fd656 --- /dev/null +++ b/addons/base_action_rule/security/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_base_action_rule","base.action.rule","model_base_action_rule",,1,1,1,1 +"access_base_action_rule_line","base.action.rule.line","model_base_action_rule_line",,1,1,1,1 +"access_base_action_rule_history","base.action.rule.history","model_base_action_rule_history",,1,1,1,1 \ No newline at end of file From 904c896e0c0f80512639103dffa5aca939206722 Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Wed, 3 Feb 2010 15:58:09 +0530 Subject: [PATCH 084/328] [IMP] base_action_rule, crm: Improved base_action_rule. Removed crm.case.rule from crm and added objects of base.actio.rule. bzr revid: uco@tinyerp.co.in-20100203102809-h622irvspyflvw0e --- addons/base_action_rule/base_action_rule.py | 132 +++-- .../base_action_rule_view.xml | 10 +- addons/crm/__terp__.py | 2 +- addons/crm/crm.py | 455 ++++++++---------- addons/crm/crm_data.xml | 13 +- addons/crm/crm_view.xml | 134 ++---- addons/crm/security/ir.model.access.csv | 2 - 7 files changed, 333 insertions(+), 415 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 65012ac501b..753a7c0365b 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -1,6 +1,7 @@ import time import mx.DateTime +import tools from osv import fields, osv, orm from osv.orm import except_orm @@ -44,45 +45,91 @@ class base_action_rule(osv.osv): scrit = [] history = [] history_obj = self.pool.get('base.action.rule.history') + cr.execute("select nextcall from ir_cron where model='base.action.rule'") + action_next = cr.fetchone()[0] if rules: cr.execute('select * from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",rules)))) history = cr.fetchall() checkids = map(lambda x: x[0], history or []) - if not len(history) or len(history) < len(rules): for rule in rules: if rule.id not in checkids: lastDate = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') - history_obj.create(cr, uid, {'rule_id': rule.id, 'res_id': rule.name.id, 'date_action_last': lastDate}) + history_obj.create(cr, uid, {'rule_id': rule.id, 'res_id': rule.name.id, 'date_action_last': lastDate, 'date_action_next': action_next}) for rule in rules: + obj = self.pool.get(rule.name.model) + rec_ids = obj.search(cr, uid, []) for action in rule.rule_lines: - if action.trg_date_type=='create': - base = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='action_last': - for hist in history: - if hist[6]: - base = mx.DateTime.strptime(hist[6][:19], '%Y-%m-%d %H:%M:%S') - else: - base = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') - history_obj.write(cr, uid, [hist[0]], {'date_action_last': base}) - elif action.trg_date_type=='deadline' and rule.name.date_deadline: - base = mx.DateTime.strptime(rule.name.date_deadline, '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='date' and rule.name.date: - base = mx.DateTime.strptime(rule.name.date, '%Y-%m-%d %H:%M:%S') - - if base: - fnct = { - 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), - 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), - 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), - 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), - } - d = base + fnct[action.trg_date_range_type](action.trg_date_range) - dt = d.strftime('%Y-%m-%d %H:%M:%S') - for hist in history: - if not hist[8] or dt < hist[8]: - history_obj.write(cr, uid, [hist[0]], {'date_action_next': dt}, context) + for data in obj.browse(cr, uid, rec_ids): + ok = True + ok = ok and (not action.trg_state_from or action.trg_state_from==data.state) + ok = ok and (not action.trg_state_to or action.trg_state_to==state_to) + ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==data.user_id.id) + ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==data.partner_id.id) + ok = ok and ( + not action.trg_partner_categ_id.id or + ( + data.partner_id.id and + (action.trg_partner_categ_id.id in map(lambda x: x.id, data.partner_id.category_id or [])) + ) + ) + ok = ok and (not action.trg_priority_from or action.trg_priority_from>=data.priority) + ok = ok and (not action.trg_priority_to or action.trg_priority_to<=data.priority) + + reg_name = action.regex_name + result_name = True + if reg_name: + ptrn = re.compile(str(reg_name)) + _result = ptrn.search(str(data.name)) + if not _result: + result_name = False + regex_n = not reg_name or result_name + ok = ok and regex_n + + if not ok: + continue + + base = False + if action.trg_date_type=='create': + base = mx.DateTime.strptime(data.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='action_last': + for hist in history: + if hist[6]: + base = hist[8] + else: + base = mx.DateTime.strptime(data.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='date' and data.date: + base = mx.DateTime.strptime(data.date, '%Y-%m-%d %H:%M:%S') + if base: + fnct = { + 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), + 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), + 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), + 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), + } + d = base + fnct[action.trg_date_range_type](action.trg_date_range) + dt = d.strftime('%Y-%m-%d %H:%M:%S') + for hist in history: + ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ + ((not hist[8]) or \ + (dt >= hist[8] and \ + hist[6] < hist[8])) + if not ok: + if not hist[8] or dt < hist[8]: + history_obj.write(cr, uid, [hist[0]], {'date_action_next': dt}, context) + + else: + ok = action.trg_date_type=='none' + + if ok: + if action.server_action_id: + context.update({'active_id':case.id,'active_ids':[case.id]}) + self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) + for hist in history: + if hist[6]: + base = hist[8] + history_obj.write(cr, uid, [hist[0]], {'date_action_last': base, 'date_action_next': action_next}) return True base_action_rule() @@ -103,7 +150,6 @@ class base_action_rule_line(osv.osv): ('none','None'), ('create','Creation Date'), ('action_last','Last Action Date'), - ('deadline','Deadline'), ('date','Date'), ], 'Trigger Date', size=16), 'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date, specifies you can put a negative number " \ @@ -135,10 +181,9 @@ class base_action_rule_line(osv.osv): 'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this if you want the rule to mark CC(mail to any other person defined in actions)."), 'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id of the persons whom mail is to be sent"), 'act_mail_body': fields.text('Mail body',help="Content of mail"), - 'regex_name': fields.char('Regular Expression on Case Name', size=128), - 'regex_history': fields.char('Regular Expression on Case History', size=128), + 'regex_name': fields.char('Regular Expression on Model Name', size=128), 'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the action name." \ - "eg:on which object which ation to be taken on basis of which condition"), + "eg:on which object which action to be taken on basis of which condition"), } _defaults = { @@ -154,13 +199,32 @@ class base_action_rule_line(osv.osv): _order = 'sequence' + def format_body(self, body): + return body and tools.ustr(body.encode('ascii', 'replace')) or '' + + def format_mail(self, case, body): + data = { + 'case_id': case.id, + 'case_subject': case.name, + 'case_date': case.date, + 'case_description': case.description, + + 'case_user': (case.user_id and case.user_id.name) or '/', + 'case_user_email': (case.user_id and case.user_id.address_id and case.user_id.address_id.email) or '/', + 'case_user_phone': (case.user_id and case.user_id.address_id and case.user_id.address_id.phone) or '/', + + 'email_from': case.email_from, + 'partner': (case.partner_id and case.partner_id.name) or '/', + 'partner_email': (case.partner_address_id and case.partner_address_id.email) or '/', + } + return self.format_body(body % data) + def _check_mail(self, cr, uid, ids, context=None): emptycase = orm.browse_null() for rule in self.browse(cr, uid, ids): if rule.act_mail_body: try: - obj = self.pool.get(rule.rule_id.name.model) - obj.format_mail(emptycase, rule.act_mail_body) + self.format_mail(emptycase, rule.act_mail_body) except (ValueError, KeyError, TypeError): return False return True diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index b8c762cbd63..441d2956151 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -57,10 +57,10 @@ - - + + - + @@ -73,10 +73,8 @@ - - -
diff --git a/addons/crm/__terp__.py b/addons/crm/__terp__.py index 5ef83f99cbe..87648ef8b74 100644 --- a/addons/crm/__terp__.py +++ b/addons/crm/__terp__.py @@ -44,7 +44,7 @@ The CRM module has a email gateway for the synchronisation interface between mails and Open ERP.""", 'author': 'Tiny', 'website': 'http://www.openerp.com', - 'depends': ['base', + 'depends': ['base', 'base_action_rule', 'caldav', 'process', 'mail_gateway', diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 32035d06973..82322d79e13 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -163,18 +163,185 @@ class crm_case_stage(osv.osv): crm_case_stage() +class base_action_rule(osv.osv): + _inherit = 'base.action.rule' + _description = 'Action Rules' + + def _check(self, cr, uid, ids=False, context={}): + ''' + Function called by the scheduler to process cases for date actions + Only works on not done and cancelled cases + ''' + obj = self.pool.get('base.action.rule') + ids2 = obj.search(cr, uid, [('name.model','=','crm.case')]) + cases = obj.browse(cr, uid, ids2, context) + return obj._action(cr, uid, cases, False, context=context) -class crm_case_rule(osv.osv): - _name = "crm.case.rule" - _description = "Case Rule" + def _action(self, cr, uid, cases, state_to, scrit=None, context={}): + if not scrit: + scrit = [] + history = [] + history_obj = self.pool.get('base.action.rule.history') + cr.execute("select nextcall from ir_cron where model='base.action.rule'") + action_next = cr.fetchone()[0] + action_ids = self.pool.get('base.action.rule').search(cr, uid, scrit) + level = MAX_LEVEL + if cases and action_ids: + cr.execute('select * from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",cases)))) + history = cr.fetchall() + checkids = map(lambda x: x[0], history or []) + + if not len(history) or len(history) < len(cases): + for case in cases: + if case.id not in checkids: + lastDate = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') + history_obj.create(cr, uid, {'rule_id': case.id, 'res_id': case.name.id, 'date_action_last': lastDate, 'date_action_next': action_next}) + caseobj = self.pool.get('crm.case') + case_ids = caseobj.search(cr, uid, [('state', 'not in', ('cancel','done'))]) + + while len(action_ids) and level: + newactions = [] + actions = self.pool.get('base.action.rule').browse(cr, uid, action_ids, context) + for case in cases: + for line in actions: + for action in line.rule_lines: + for cs in caseobj.browse(cr, uid, case_ids): + ok = True + ok = ok and (not action.trg_state_from or action.trg_state_from==cs.state) + ok = ok and (not action.trg_state_to or action.trg_state_to==state_to) + ok = ok and (not action.trg_section_id or action.trg_section_id.id==cs.section_id.id) + ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==cs.categ_id.id) + ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==cs.user_id.id) + ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==cs.partner_id.id) + ok = ok and (not action.trg_max_history or action.trg_max_history<=(len(cs.history_line)+1)) + ok = ok and ( + not action.trg_partner_categ_id.id or + ( + cs.partner_id.id and + (action.trg_partner_categ_id.id in map(lambda x: x.id, cs.partner_id.category_id or [])) + ) + ) + ok = ok and (not action.trg_priority_from or action.trg_priority_from>=case.priority) + ok = ok and (not action.trg_priority_to or action.trg_priority_to<=case.priority) + + reg_name = action.regex_name + result_name = True + if reg_name: + ptrn = re.compile(str(reg_name)) + _result = ptrn.search(str(cs.name)) + if not _result: + result_name = False + regex_n = not reg_name or result_name + ok = ok and regex_n + + reg_history = action.regex_history + result_history = True + if reg_history: + ptrn = re.compile(str(reg_history)) + if cs.history_line: + _result = ptrn.search(str(cs.history_line[0].description)) + if not _result: + result_history = False + regex_h = not reg_history or result_history + ok = ok and regex_h + + if not ok: + continue + + base = False + if action.trg_date_type=='create': + base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='action_last': + for hist in history: + if hist[6]: + base = mx.DateTime.strptime(hist[6], '%Y-%m-%d %H:%M:%S') + else: + base = mx.DateTime.strptime(cs.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='deadline' and cs.date_deadline: + base = mx.DateTime.strptime(cs.date_deadline, '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='date' and cs.date: + base = mx.DateTime.strptime(cs.date, '%Y-%m-%d %H:%M:%S') + if base: + fnct = { + 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), + 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), + 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), + 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), + } + d = base + fnct[action.trg_date_range_type](action.trg_date_range) + dt = d.strftime('%Y-%m-%d %H:%M:%S') + for hist in history: + ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ + ((not hist[8]) or \ + (dt >= hist[8] and \ + hist[6] < hist[8])) + if not ok: + if not hist[8] or dt < hist[8]: + history_obj.write(cr, uid, [hist[0]], {'date_action_next': dt}, context) + + else: + ok = action.trg_date_type=='none' + + if ok: + if action.server_action_id: + context.update({'active_id':cs.id,'active_ids':[cs.id]}) + self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) + write = {} + if action.act_state: + cs.state = action.act_state + write['state'] = action.act_state + if action.act_section_id: + cs.section_id = action.act_section_id + write['section_id'] = action.act_section_id.id + if action.act_user_id: + cs.user_id = action.act_user_id + write['user_id'] = action.act_user_id.id + if action.act_priority: + cs.priority = action.act_priority + write['priority'] = action.act_priority + if action.act_email_cc: + if '@' in (cs.email_cc or ''): + emails = cs.email_cc.split(",") + if action.act_email_cc not in emails:# and '<'+str(action.act_email_cc)+">" not in emails: + write['email_cc'] = cs.email_cc+','+action.act_email_cc + else: + write['email_cc'] = action.act_email_cc + caseobj.write(cr, uid, [cs.id], write, context) + if action.act_remind_user: + caseobj.remind_user(cr, uid, [cs.id], context, attach=action.act_remind_attach) + if action.act_remind_partner: + caseobj.remind_partner(cr, uid, [cs.id], context, attach=action.act_remind_attach) + if action.act_method: + getattr(caseobj, 'act_method')(cr, uid, [cs.id], action, context) + emails = [] + if action.act_mail_to_user: + if cs.user_id and cs.user_id.address_id: + emails.append(cs.user_id.address_id.email) + if action.act_mail_to_partner: + emails.append(cs.email_from) + if action.act_mail_to_watchers: + emails += (action.act_email_cc or '').split(',') + if action.act_mail_to_email: + emails += (action.act_mail_to_email or '').split(',') + emails = filter(None, emails) + if len(emails) and action.act_mail_body: + emails = list(set(emails)) + caseobj.email_send(cr, uid, cs, emails, action.act_mail_body) + break + action_ids = newactions + level -= 1 + return True + +base_action_rule() + +class base_action_rule_line(osv.osv): + _inherit = 'base.action.rule.line' _columns = { - 'name': fields.char('Rule Name',size=64, required=True), - 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the case rule without removing it."), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of case rules."), - - 'trg_state_from': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Case State', size=16), - 'trg_state_to': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Button Pressed', size=16), - + 'trg_section_id': fields.many2one('crm.case.section', 'Section'), + 'trg_max_history': fields.integer('Maximum Communication History'), + 'trg_categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',trg_section_id)]"), + 'regex_history' : fields.char('Regular Expression on Case History', size=128), + 'act_section_id': fields.many2one('crm.case.section', 'Set section to'), 'trg_date_type': fields.selection([ ('none','None'), ('create','Creation Date'), @@ -182,88 +349,9 @@ class crm_case_rule(osv.osv): ('deadline','Deadline'), ('date','Date'), ], 'Trigger Date', size=16), - 'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date, specifies you can put a negative number " \ - "if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."), - 'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type'), - - 'trg_section_id': fields.many2one('crm.case.section', 'Section'), + } - #'trg_categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',trg_section_id)]"), - 'trg_user_id': fields.many2one('res.users', 'Responsible'), - - 'trg_partner_id': fields.many2one('res.partner', 'Partner'), - 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), - - 'trg_priority_from': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Minimum Priority'), - 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Maximim Priority'), - 'trg_max_history': fields.integer('Maximum Communication History'), - - 'act_method': fields.char('Call Object Method', size=64), - 'act_state': fields.selection([('','')]+AVAILABLE_STATES, 'Set state to', size=16), - 'act_section_id': fields.many2one('crm.case.section', 'Set section to'), - 'act_user_id': fields.many2one('res.users', 'Set responsible to'), - 'act_priority': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Set priority to'), - 'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people will receive a copy of the future communication between partner and users by email"), - - 'act_remind_partner': fields.boolean('Remind Partner', help="Check this if you want the rule to send a reminder by email to the partner."), - 'act_remind_user': fields.boolean('Remind responsible', help="Check this if you want the rule to send a reminder by email to the user."), - 'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the case be attached to the reminder email sent."), - - 'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if you want the rule to send an email to the responsible person."), - 'act_mail_to_partner': fields.boolean('Mail to partner',help="Check this if you want the rule to send an email to the partner."), - 'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this if you want the rule to mark CC(mail to any other person defined in actions)."), - 'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id of the persons whom mail is to be sent"), - 'act_mail_body': fields.text('Mail body',help="Content of mail"), - 'regex_name' : fields.char('Regular Expression on Case Name', size=128), - 'regex_history' : fields.char('Regular Expression on Case History', size=128), - 'server_action_id' : fields.many2one('ir.actions.server','Server Action',help="Describes the action name." \ - "eg:on which object which ation to be taken on basis of which condition"), - } - _defaults = { - 'active': lambda *a: 1, - 'trg_date_type': lambda *a: 'none', - 'trg_date_range_type': lambda *a: 'day', - 'act_mail_to_user': lambda *a: 0, - 'act_remind_partner': lambda *a: 0, - 'act_remind_user': lambda *a: 0, - 'act_mail_to_partner': lambda *a: 0, - 'act_mail_to_watchers': lambda *a: 0, - } - _order = 'sequence' - - def _check(self, cr, uid, ids=False, context={}): - ''' - Function called by the scheduler to process cases for date actions - Only works on not done and cancelled cases - ''' - cr.execute('select * from crm_case \ - where (date_action_last<%s or date_action_last is null) \ - and (date_action_next<=%s or date_action_next is null) \ - and state not in (\'cancel\',\'done\')', - (time.strftime("%Y-%m-%d %H:%M:%S"), - time.strftime('%Y-%m-%d %H:%M:%S'))) - ids2 = map(lambda x: x[0], cr.fetchall() or []) - case_obj = self.pool.get('crm.case') - cases = case_obj.browse(cr, uid, ids2, context) - return case_obj._action(cr, uid, cases, False, context=context) - - - def _check_mail(self, cr, uid, ids, context=None): - caseobj = self.pool.get('crm.case') - emptycase = orm.browse_null() - for rule in self.browse(cr, uid, ids): - if rule.act_mail_body: - try: - caseobj.format_mail(emptycase, rule.act_mail_body) - except (ValueError, KeyError, TypeError): - return False - return True - - _constraints = [ - (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), - ] - -crm_case_rule() +base_action_rule_line() def _links_get(self, cr, uid, context={}): obj = self.pool.get('res.request.link') @@ -335,9 +423,6 @@ class crm_case(osv.osv): \nIf the case is in progress the state is set to \'Open\'.\ \nWhen the case is over, the state is set to \'Done\'.\ \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), - - 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), 'company_id': fields.many2one('res.company','Company'), } def _get_default_partner_address(self, cr, uid, context): @@ -427,164 +512,6 @@ class crm_case(osv.osv): value['email_from'] = case.email_from return {'value': value} - def _action(self, cr, uid, cases, state_to, scrit=None, context={}): - if not scrit: - scrit = [] - action_ids = self.pool.get('crm.case.rule').search(cr, uid, scrit) - level = MAX_LEVEL - while len(action_ids) and level: - newactions = [] - actions = self.pool.get('crm.case.rule').browse(cr, uid, action_ids, context) - for case in cases: - for action in actions: - ok = True - ok = ok and (not action.trg_state_from or action.trg_state_from==case.state) - ok = ok and (not action.trg_state_to or action.trg_state_to==state_to) - ok = ok and (not action.trg_section_id or action.trg_section_id.id==case.section_id.id) - ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==case.categ_id.id) - ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==case.user_id.id) - ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==case.partner_id.id) - ok = ok and (not action.trg_max_history or action.trg_max_history<=(len(case.history_line)+1)) - ok = ok and ( - not action.trg_partner_categ_id.id or - ( - case.partner_id.id and - (action.trg_partner_categ_id.id in map(lambda x: x.id, case.partner_id.category_id or [])) - ) - ) - ok = ok and (not action.trg_priority_from or action.trg_priority_from>=case.priority) - ok = ok and (not action.trg_priority_to or action.trg_priority_to<=case.priority) - - reg_name = action.regex_name - result_name = True - if reg_name: - ptrn = re.compile(str(reg_name)) - _result = ptrn.search(str(case.name)) - if not _result: - result_name = False - regex_n = not reg_name or result_name - ok = ok and regex_n - - reg_history = action.regex_history - result_history = True - if reg_history: - ptrn = re.compile(str(reg_history)) - if case.history_line: - _result = ptrn.search(str(case.history_line[0].description)) - if not _result: - result_history = False - regex_h = not reg_history or result_history - ok = ok and regex_h - - if not ok: - continue - - base = False - if action.trg_date_type=='create': - base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='action_last': - if case.date_action_last: - base = mx.DateTime.strptime(case.date_action_last, '%Y-%m-%d %H:%M:%S') - else: - base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='deadline' and case.date_deadline: - base = mx.DateTime.strptime(case.date_deadline, '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='date' and case.date: - base = mx.DateTime.strptime(case.date, '%Y-%m-%d %H:%M:%S') - if base: - fnct = { - 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), - 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), - 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), - 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), - } - d = base + fnct[action.trg_date_range_type](action.trg_date_range) - dt = d.strftime('%Y-%m-%d %H:%M:%S') - ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ - ((not case.date_action_next) or \ - (dt >= case.date_action_next and \ - case.date_action_last < case.date_action_next)) - if not ok: - if not case.date_action_next or dt < case.date_action_next: - case.date_action_next = dt - self.write(cr, uid, [case.id], {'date_action_next': dt}, context) - - else: - ok = action.trg_date_type=='none' - - if ok: - if action.server_action_id: - context.update({'active_id':case.id,'active_ids':[case.id]}) - self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) - write = {} - if action.act_state: - case.state = action.act_state - write['state'] = action.act_state - if action.act_section_id: - case.section_id = action.act_section_id - write['section_id'] = action.act_section_id.id - if action.act_user_id: - case.user_id = action.act_user_id - write['user_id'] = action.act_user_id.id - if action.act_priority: - case.priority = action.act_priority - write['priority'] = action.act_priority - if action.act_email_cc: - if '@' in (case.email_cc or ''): - emails = case.email_cc.split(",") - if action.act_email_cc not in emails:# and '<'+str(action.act_email_cc)+">" not in emails: - write['email_cc'] = case.email_cc+','+action.act_email_cc - else: - write['email_cc'] = action.act_email_cc - write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S') - self.write(cr, uid, [case.id], write, context) - caseobj = self.pool.get('crm.case') - if action.act_remind_user: - caseobj.remind_user(cr, uid, [case.id], context, attach=action.act_remind_attach) - if action.act_remind_partner: - caseobj.remind_partner(cr, uid, [case.id], context, attach=action.act_remind_attach) - if action.act_method: - getattr(caseobj, 'act_method')(cr, uid, [case.id], action, context) - emails = [] - if action.act_mail_to_user: - if case.user_id and case.user_id.address_id: - emails.append(case.user_id.address_id.email) - if action.act_mail_to_partner: - emails.append(case.email_from) - if action.act_mail_to_watchers: - emails += (action.act_email_cc or '').split(',') - if action.act_mail_to_email: - emails += (action.act_mail_to_email or '').split(',') - emails = filter(None, emails) - if len(emails) and action.act_mail_body: - emails = list(set(emails)) - self.email_send(cr, uid, case, emails, action.act_mail_body) - break - action_ids = newactions - level -= 1 - return True - - def format_body(self, body): - return body and tools.ustr(body.encode('ascii', 'replace')) or '' - - def format_mail(self, case, body): - data = { - 'case_id': case.id, - 'case_subject': case.name, - 'case_date': case.date, - 'case_description': case.description, - - 'case_user': (case.user_id and case.user_id.name) or '/', - 'case_user_email': (case.user_id and case.user_id.address_id and case.user_id.address_id.email) or '/', - 'case_user_phone': (case.user_id and case.user_id.address_id and case.user_id.address_id.phone) or '/', - - 'email_from': case.email_from, - 'partner': (case.partner_id and case.partner_id.name) or '/', - 'partner_email': (case.partner_address_id and case.partner_address_id.email) or '/', - } - return self.format_body(body % data) - - def __history(self, cr, uid, cases, keyword, history=False, email=False, details=None, context={}): model_obj = self.pool.get('ir.model') for case in cases: @@ -612,7 +539,8 @@ class crm_case(osv.osv): res = super(crm_case, self).create(cr, uid, *args, **argv) cases = self.browse(cr, uid, [res]) cases[0].state # to fill the browse record cache - self._action(cr,uid, cases, 'draft') + obj = self.pool.get('base.action.rule') + obj._action(cr,uid, cases, 'draft') return res def remind_partner(self, cr, uid, ids, context={}, attach=False): @@ -745,7 +673,8 @@ class crm_case(osv.osv): # # We use the cache of cases to keep the old case state # - self._action(cr,uid, cases, 'done') + obj = self.pool.get('base.action.rule') + obj._action(cr,uid, cases, 'done') return True def case_escalate(self, cr, uid, ids, *args): @@ -761,7 +690,8 @@ class crm_case(osv.osv): self.write(cr, uid, ids, data) cases = self.browse(cr, uid, ids) self.__history(cr, uid, cases, _('Escalate')) - self._action(cr, uid, cases, 'escalate') + obj = self.pool.get('base.action.rule') + obj._action(cr, uid, cases, 'escalate') return True @@ -773,7 +703,8 @@ class crm_case(osv.osv): if not case.user_id: data['user_id'] = uid self.write(cr, uid, ids, data) - self._action(cr,uid, cases, 'open') + obj = self.pool.get('base.action.rule') + obj._action(cr,uid, cases, 'open') return True @@ -782,7 +713,8 @@ class crm_case(osv.osv): cases[0].state # to fill the browse record cache self.__history(cr, uid, cases, _('Cancel')) self.write(cr, uid, ids, {'state':'cancel', 'active':True}) - self._action(cr,uid, cases, 'cancel') + obj = self.pool.get('base.action.rule') + obj._action(cr,uid, cases, 'cancel') return True def case_pending(self, cr, uid, ids, *args): @@ -790,7 +722,8 @@ class crm_case(osv.osv): cases[0].state # to fill the browse record cache self.__history(cr, uid, cases, _('Pending')) self.write(cr, uid, ids, {'state':'pending', 'active':True}) - self._action(cr,uid, cases, 'pending') + obj = self.pool.get('base.action.rule') + obj._action(cr,uid, cases, 'pending') return True def case_reset(self, cr, uid, ids, *args): @@ -798,7 +731,8 @@ class crm_case(osv.osv): cases[0].state # to fill the browse record cache self.__history(cr, uid, cases, _('Draft')) self.write(cr, uid, ids, {'state':'draft', 'active':True}) - self._action(cr, uid, cases, 'draft') + obj = self.pool.get('base.action.rule') + obj._action(cr, uid, cases, 'draft') return True crm_case() @@ -909,6 +843,3 @@ class users(osv.osv): 'context_section_id': fields.selection(_section_get, 'Sales Section'), } users() - - - diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml index 8bc30fbd26e..5a0ca1a2256 100644 --- a/addons/crm/crm_data.xml +++ b/addons/crm/crm_data.xml @@ -59,17 +59,6 @@ - - - Check cases rules - 4 - hours - -1 - - - - - - + diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index 1c9a7aa2bfd..98055bf78e7 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -133,105 +133,45 @@ - - crm.case.rule.form - crm.case.rule + + base.action.rule.line.form.inherit + base.action.rule.line + form -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - crm.case.rule.tree - crm.case.rule - tree + + + + + + + + + + base.action.rule.line.form2.inherit + base.action.rule.line + + form - - - - - - - - Rules - crm.case.rule - form - - - - + + + + + +
+ + + base.action.rule.line.form3.inherit + base.action.rule.line + + form + + + + + + + Add CC crm.email.add.cc @@ -377,8 +317,6 @@ - -
diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv index 7339832b083..48d09804d1b 100644 --- a/addons/crm/security/ir.model.access.csv +++ b/addons/crm/security/ir.model.access.csv @@ -3,7 +3,6 @@ "access_crm_segmentation_line","crm.segmentation.line","model_crm_segmentation_line","crm.group_crm_manager",1,1,1,1 "access_crm_case_section","crm.case.section","model_crm_case_section","crm.group_crm_user",1,0,0,0 "access_crm_case_categ","crm.case.categ","model_crm_case_categ","crm.group_crm_user",1,0,0,0 -"access_crm_case_rule","crm.case.rule","model_crm_case_rule","crm.group_crm_user",1,0,0,0 "access_crm_case_manger","crm.case manager","model_crm_case","crm.group_crm_user",1,1,1,1 "access_crm_case","crm.case","model_crm_case","crm.group_crm_manager",1,1,1,1 "access_crm_claim","crm.claim","model_crm_claim","crm.group_crm_manager",1,1,1,1 @@ -17,7 +16,6 @@ "access_crm_case_history","crm.case.history","model_crm_case_history","crm.group_crm_user",1,1,1,1 "access_crm_case_section_manager","crm.case.section.manager","model_crm_case_section","crm.group_crm_manager",1,1,1,1 "access_crm_case_categ_manager","crm.case.categ.manager","model_crm_case_categ","crm.group_crm_manager",1,1,1,1 -"access_crm_case_rule_manager","crm.case.rule.manager","model_crm_case_rule","crm.group_crm_manager",1,1,1,1 "access_crm_case_log_manager","crm.case.log manager","model_crm_case_log","crm.group_crm_manager",1,1,1,1 "access_crm_case_history_manager","crm.case.history manager","model_crm_case_history","crm.group_crm_manager",1,1,1,1 "access_crm_email_add_cc_manager","crm.email.add.cc","model_crm_email_add_cc","crm.group_crm_manager",1,1,1,1 From 4073fd445fc080db8ca35ae8dc8abc839af3ce11 Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Wed, 3 Feb 2010 16:58:55 +0530 Subject: [PATCH 085/328] [IMP] crm: Removed date_action_last field from all views of all objects related to crm.case. bzr revid: uco@tinyerp.co.in-20100203112855-gq150a2sd516kqui --- addons/crm/crm.py | 37 ++++++++++++++++++++++------ addons/crm/crm_claims_view.xml | 2 -- addons/crm/crm_fund_view.xml | 2 -- addons/crm/crm_helpdesk_view.xml | 2 -- addons/crm/crm_opportunity_view.xml | 2 -- addons/crm_hr/crm_hr_view.xml | 2 -- addons/crm_project/crm_bugs_view.xml | 2 -- addons/event/event_view.xml | 2 -- 8 files changed, 29 insertions(+), 22 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 82322d79e13..f63ef65ab5b 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -540,7 +540,10 @@ class crm_case(osv.osv): cases = self.browse(cr, uid, [res]) cases[0].state # to fill the browse record cache obj = self.pool.get('base.action.rule') - obj._action(cr,uid, cases, 'draft') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr,uid, rules, 'draft') return res def remind_partner(self, cr, uid, ids, context={}, attach=False): @@ -551,7 +554,7 @@ class crm_case(osv.osv): destination=True): for case in self.browse(cr, uid, ids): if not case.section_id.reply_to: - raise osv.except_osv(_('Error!'),("Reply TO is not specified in Section")) + raise osv.except_osv(_('Error!'),("Reply To is not specified in Section")) if not case.email_from: raise osv.except_osv(_('Error!'),("Partner Email is not specified in Case")) if case.section_id.reply_to and case.email_from: @@ -674,7 +677,10 @@ class crm_case(osv.osv): # We use the cache of cases to keep the old case state # obj = self.pool.get('base.action.rule') - obj._action(cr,uid, cases, 'done') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr,uid, rules, 'done') return True def case_escalate(self, cr, uid, ids, *args): @@ -691,7 +697,10 @@ class crm_case(osv.osv): cases = self.browse(cr, uid, ids) self.__history(cr, uid, cases, _('Escalate')) obj = self.pool.get('base.action.rule') - obj._action(cr, uid, cases, 'escalate') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr, uid, rules, 'escalate') return True @@ -704,7 +713,10 @@ class crm_case(osv.osv): data['user_id'] = uid self.write(cr, uid, ids, data) obj = self.pool.get('base.action.rule') - obj._action(cr,uid, cases, 'open') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr,uid, rules, 'open') return True @@ -714,7 +726,10 @@ class crm_case(osv.osv): self.__history(cr, uid, cases, _('Cancel')) self.write(cr, uid, ids, {'state':'cancel', 'active':True}) obj = self.pool.get('base.action.rule') - obj._action(cr,uid, cases, 'cancel') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr,uid, rules, 'cancel') return True def case_pending(self, cr, uid, ids, *args): @@ -723,7 +738,10 @@ class crm_case(osv.osv): self.__history(cr, uid, cases, _('Pending')) self.write(cr, uid, ids, {'state':'pending', 'active':True}) obj = self.pool.get('base.action.rule') - obj._action(cr,uid, cases, 'pending') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr,uid, rules, 'pending') return True def case_reset(self, cr, uid, ids, *args): @@ -732,7 +750,10 @@ class crm_case(osv.osv): self.__history(cr, uid, cases, _('Draft')) self.write(cr, uid, ids, {'state':'draft', 'active':True}) obj = self.pool.get('base.action.rule') - obj._action(cr, uid, cases, 'draft') + objids = obj.search(cr, uid, [('name.model','=','crm.case')]) + if len(objids): + rules = obj.browse(cr, uid, objids) + obj._action(cr, uid, rules, 'draft') return True crm_case() diff --git a/addons/crm/crm_claims_view.xml b/addons/crm/crm_claims_view.xml index 5f7e9b07fdc..3b04559d2bb 100644 --- a/addons/crm/crm_claims_view.xml +++ b/addons/crm/crm_claims_view.xml @@ -98,8 +98,6 @@ - - diff --git a/addons/crm/crm_fund_view.xml b/addons/crm/crm_fund_view.xml index 96b0ac51ad9..e117d677f8c 100644 --- a/addons/crm/crm_fund_view.xml +++ b/addons/crm/crm_fund_view.xml @@ -102,8 +102,6 @@ - - diff --git a/addons/crm/crm_helpdesk_view.xml b/addons/crm/crm_helpdesk_view.xml index 1f0877946c7..ccf51e2975a 100644 --- a/addons/crm/crm_helpdesk_view.xml +++ b/addons/crm/crm_helpdesk_view.xml @@ -66,8 +66,6 @@ - - diff --git a/addons/crm/crm_opportunity_view.xml b/addons/crm/crm_opportunity_view.xml index 0268ffc0b3c..9a2a4ad2271 100644 --- a/addons/crm/crm_opportunity_view.xml +++ b/addons/crm/crm_opportunity_view.xml @@ -91,8 +91,6 @@ - - diff --git a/addons/crm_hr/crm_hr_view.xml b/addons/crm_hr/crm_hr_view.xml index f4a46ec9621..9771a25309c 100644 --- a/addons/crm_hr/crm_hr_view.xml +++ b/addons/crm_hr/crm_hr_view.xml @@ -127,8 +127,6 @@ - - diff --git a/addons/crm_project/crm_bugs_view.xml b/addons/crm_project/crm_bugs_view.xml index 0d84723f11e..3ff37426770 100644 --- a/addons/crm_project/crm_bugs_view.xml +++ b/addons/crm_project/crm_bugs_view.xml @@ -80,8 +80,6 @@ - - diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 03110015779..28c8d5e3500 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -216,8 +216,6 @@ - - From 243e3d2fe0686972917e760a88b7199a9ce8854b Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Wed, 3 Feb 2010 18:14:10 +0530 Subject: [PATCH 086/328] [IMP] base_action_rule, crm: Improved scheduling methods. bzr revid: uco@tinyerp.co.in-20100203124410-h0pvp8oeayoz7of5 --- addons/base_action_rule/base_action_rule.py | 21 +++++++++++---------- addons/crm/crm.py | 18 +++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 753a7c0365b..e70de1100c2 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -48,9 +48,10 @@ class base_action_rule(osv.osv): cr.execute("select nextcall from ir_cron where model='base.action.rule'") action_next = cr.fetchone()[0] if rules: - cr.execute('select * from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",rules)))) + cr.execute('select id, rule_id, res_id, date_action_last, date_action_next' \ + ' from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",rules)))) history = cr.fetchall() - checkids = map(lambda x: x[0], history or []) + checkids = map(lambda x: x[1], history or []) if not len(history) or len(history) < len(rules): for rule in rules: if rule.id not in checkids: @@ -95,8 +96,8 @@ class base_action_rule(osv.osv): base = mx.DateTime.strptime(data.create_date[:19], '%Y-%m-%d %H:%M:%S') elif action.trg_date_type=='action_last': for hist in history: - if hist[6]: - base = hist[8] + if hist[3]: + base = hist[4] else: base = mx.DateTime.strptime(data.create_date[:19], '%Y-%m-%d %H:%M:%S') elif action.trg_date_type=='date' and data.date: @@ -112,11 +113,11 @@ class base_action_rule(osv.osv): dt = d.strftime('%Y-%m-%d %H:%M:%S') for hist in history: ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ - ((not hist[8]) or \ - (dt >= hist[8] and \ - hist[6] < hist[8])) + ((not hist[4]) or \ + (dt >= hist[4] and \ + hist[3] < hist[4])) if not ok: - if not hist[8] or dt < hist[8]: + if not hist[4] or dt < hist[4]: history_obj.write(cr, uid, [hist[0]], {'date_action_next': dt}, context) else: @@ -127,8 +128,8 @@ class base_action_rule(osv.osv): context.update({'active_id':case.id,'active_ids':[case.id]}) self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) for hist in history: - if hist[6]: - base = hist[8] + if hist[3]: + base = hist[4] history_obj.write(cr, uid, [hist[0]], {'date_action_last': base, 'date_action_next': action_next}) return True diff --git a/addons/crm/crm.py b/addons/crm/crm.py index f63ef65ab5b..2bb35500a76 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -187,10 +187,10 @@ class base_action_rule(osv.osv): action_ids = self.pool.get('base.action.rule').search(cr, uid, scrit) level = MAX_LEVEL if cases and action_ids: - cr.execute('select * from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",cases)))) + cr.execute('select id, rule_id, res_id, date_action_last, date_action_next' \ + ' from base_action_rule_history where rule_id in (%s)' %(','.join(map(lambda x: "'"+str(x.id)+"'",cases)))) history = cr.fetchall() - checkids = map(lambda x: x[0], history or []) - + checkids = map(lambda x: x[1], history or []) if not len(history) or len(history) < len(cases): for case in cases: if case.id not in checkids: @@ -253,8 +253,8 @@ class base_action_rule(osv.osv): base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') elif action.trg_date_type=='action_last': for hist in history: - if hist[6]: - base = mx.DateTime.strptime(hist[6], '%Y-%m-%d %H:%M:%S') + if hist[3]: + base = mx.DateTime.strptime(hist[3], '%Y-%m-%d %H:%M:%S') else: base = mx.DateTime.strptime(cs.create_date[:19], '%Y-%m-%d %H:%M:%S') elif action.trg_date_type=='deadline' and cs.date_deadline: @@ -272,11 +272,11 @@ class base_action_rule(osv.osv): dt = d.strftime('%Y-%m-%d %H:%M:%S') for hist in history: ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ - ((not hist[8]) or \ - (dt >= hist[8] and \ - hist[6] < hist[8])) + ((not hist[4]) or \ + (dt >= hist[4] and \ + hist[3] < hist[4])) if not ok: - if not hist[8] or dt < hist[8]: + if not hist[4] or dt < hist[4]: history_obj.write(cr, uid, [hist[0]], {'date_action_next': dt}, context) else: From 309ca5e6f202d4dd1fad97a77fb5fe5cd9051df4 Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Thu, 4 Feb 2010 11:06:04 +0530 Subject: [PATCH 088/328] [IMP] crm, base_action_rule : Small Change in methods. bzr revid: uco@tinyerp.co.in-20100204053604-ae7yhrlrb7s7c4w1 --- addons/base_action_rule/base_action_rule.py | 4 ++-- addons/crm/crm.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index e70de1100c2..bcc0bbea976 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -125,7 +125,7 @@ class base_action_rule(osv.osv): if ok: if action.server_action_id: - context.update({'active_id':case.id,'active_ids':[case.id]}) + context.update({'active_id': data.id,'active_ids': [data.id]}) self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) for hist in history: if hist[3]: @@ -164,7 +164,7 @@ class base_action_rule_line(osv.osv): 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), 'trg_priority_from': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Minimum Priority'), - 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Maximim Priority'), + 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Maximum Priority'), 'act_method': fields.char('Call Object Method', size=64), diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 16c63d8dde1..916f8cec750 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -198,7 +198,7 @@ class base_action_rule(osv.osv): history_obj.create(cr, uid, {'rule_id': case.id, 'res_id': case.name.id, 'date_action_last': lastDate, 'date_action_next': action_next}) caseobj = self.pool.get('crm.case') case_ids = caseobj.search(cr, uid, [('state', 'not in', ('cancel','done'))]) - + while len(action_ids) and level: newactions = [] actions = self.pool.get('base.action.rule').browse(cr, uid, action_ids, context) @@ -221,8 +221,6 @@ class base_action_rule(osv.osv): (action.trg_partner_categ_id.id in map(lambda x: x.id, cs.partner_id.category_id or [])) ) ) - ok = ok and (not action.trg_priority_from or action.trg_priority_from>=case.priority) - ok = ok and (not action.trg_priority_to or action.trg_priority_to<=case.priority) reg_name = action.regex_name result_name = True @@ -284,7 +282,7 @@ class base_action_rule(osv.osv): if ok: if action.server_action_id: - context.update({'active_id':cs.id,'active_ids':[cs.id]}) + context.update({'active_id': cs.id,'active_ids': [cs.id]}) self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) write = {} if action.act_state: From 529d44a80ed4e3246cd990979e116af3c32ce462 Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Thu, 4 Feb 2010 12:56:45 +0530 Subject: [PATCH 089/328] [IMP] crm, base_action_rule : Improved methods. bzr revid: uco@tinyerp.co.in-20100204072645-6nvg0b3j11f1hyxe --- addons/base_action_rule/base_action_rule.py | 40 ++++++++++++++++++++- addons/crm/crm.py | 5 ++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index bcc0bbea976..69b84af04ad 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -1,5 +1,6 @@ import time import mx.DateTime +import re import tools from osv import fields, osv, orm @@ -57,7 +58,6 @@ class base_action_rule(osv.osv): if rule.id not in checkids: lastDate = mx.DateTime.strptime(rule.create_date[:19], '%Y-%m-%d %H:%M:%S') history_obj.create(cr, uid, {'rule_id': rule.id, 'res_id': rule.name.id, 'date_action_last': lastDate, 'date_action_next': action_next}) - for rule in rules: obj = self.pool.get(rule.name.model) rec_ids = obj.search(cr, uid, []) @@ -127,6 +127,44 @@ class base_action_rule(osv.osv): if action.server_action_id: context.update({'active_id': data.id,'active_ids': [data.id]}) self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context) + write = {} + if action.act_state: + data.state = action.act_state + write['state'] = action.act_state + if action.act_user_id: + data.user_id = action.act_user_id + write['user_id'] = action.act_user_id.id + if action.act_priority: + data.priority = action.act_priority + write['priority'] = action.act_priority + if action.act_email_cc: + if '@' in (data.email_cc or ''): + emails = data.email_cc.split(",") + if action.act_email_cc not in emails:# and '<'+str(action.act_email_cc)+">" not in emails: + write['email_cc'] = data.email_cc+','+action.act_email_cc + else: + write['email_cc'] = action.act_email_cc + obj.write(cr, uid, [data.id], write, context) + if action.act_remind_user: + obj.remind_user(cr, uid, [data.id], context, attach=action.act_remind_attach) + if action.act_remind_partner: + obj.remind_partner(cr, uid, [data.id], context, attach=action.act_remind_attach) + if action.act_method: + getattr(caseobj, 'act_method')(cr, uid, [data.id], action, context) + emails = [] + if action.act_mail_to_user: + if data.user_id and data.user_id.address_id: + emails.append(data.user_id.address_id.email) + if action.act_mail_to_partner: + emails.append(data.email_from) + if action.act_mail_to_watchers: + emails += (action.act_email_cc or '').split(',') + if action.act_mail_to_email: + emails += (action.act_mail_to_email or '').split(',') + emails = filter(None, emails) + if len(emails) and action.act_mail_body: + emails = list(set(emails)) + obj.email_send(cr, uid, data, emails, action.act_mail_body) for hist in history: if hist[3]: base = hist[4] diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 916f8cec750..aade125d08c 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -325,7 +325,10 @@ class base_action_rule(osv.osv): if len(emails) and action.act_mail_body: emails = list(set(emails)) caseobj.email_send(cr, uid, cs, emails, action.act_mail_body) - break + for hist in history: + if hist[3]: + base = hist[4] + history_obj.write(cr, uid, [hist[0]], {'date_action_last': base, 'date_action_next': action_next}) action_ids = newactions level -= 1 return True From e81c40c7e7de4684fdfc2e2dfd9546750faa095b Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Thu, 4 Feb 2010 17:46:56 +0530 Subject: [PATCH 090/328] [FIX] default_FIELD from context should not overwrite modified FIELD value while creating record lp bug: https://launchpad.net/bugs/516964 fixed bzr revid: jvo@tinyerp.com-20100204121656-pzh1opx6kbrywscg --- bin/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index f7246b93678..d1ea04f01d8 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -2140,7 +2140,7 @@ class orm(orm_template): field_value = field_value2 value[field] = field_value for key in context or {}: - if key.startswith('default_'): + if key.startswith('default_') and (key[8:] in fields_list): value[key[8:]] = context[key] return value From 6d599c6ccacd0e726e8a4f18711c6b64c5e8465c Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 4 Feb 2010 18:24:36 +0100 Subject: [PATCH 091/328] [FIX] allow to create menu executing ir.actions.server [IMP] pyflackes bzr revid: chs@tinyerp.com-20100204172436-icchmn39spmew41g --- bin/addons/base/ir/ir_ui_menu.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index 75a3550250e..9fa06450110 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -21,7 +21,6 @@ ############################################################################## from osv import fields, osv -from osv.orm import browse_null, browse_record import re import tools @@ -106,7 +105,7 @@ class ir_ui_menu(osv.osv): if data: model_field = { 'ir.actions.act_window': 'res_model', 'ir.actions.report.custom': 'model', - 'ir.actions.report.xml': 'model', + 'ir.actions.report.xml': 'model', 'ir.actions.wizard': 'model', 'ir.actions.server': 'model_id', } @@ -168,8 +167,8 @@ class ir_ui_menu(osv.osv): ('res_id', '=', id), ]) for iv in ir_values_obj.browse(cr, uid, ids): - new_id = ir_values_obj.copy(cr, uid, iv.id, - default={'res_id': res}, context=context) + ir_values_obj.copy(cr, uid, iv.id, default={'res_id': res}, + context=context) return res def _action(self, cursor, user, ids, name, arg, context=None): @@ -242,6 +241,7 @@ class ir_ui_menu(osv.osv): ('ir.actions.act_window', 'ir.actions.act_window'), ('ir.actions.wizard', 'ir.actions.wizard'), ('ir.actions.url', 'ir.actions.url'), + ('ir.actions.server', 'ir.actions.server'), ]), } _defaults = { From 054cc4aa1fcbe18019dc7215d7200f6bc18327c9 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 4 Feb 2010 18:26:10 +0100 Subject: [PATCH 092/328] [IMP] osv: log (in debug) access errors [IMP] web_service: remove log (debug) when creating database bzr revid: chs@tinyerp.com-20100204172610-hm6ofa8cf2letwlj --- bin/osv/osv.py | 17 ++++++++--------- bin/service/web_services.py | 1 - 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/bin/osv/osv.py b/bin/osv/osv.py index 388bdeeb5cb..7fb004c7fd4 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -29,18 +29,16 @@ import netsvc import pooler import copy import sys +import traceback from psycopg2 import IntegrityError -from netsvc import Logger, LOG_ERROR -from tools.misc import UpdateableDict +from tools.func import wraps -from tools.translate import _ module_list = [] module_class_list = {} class_pool = {} - class except_osv(Exception): def __init__(self, name, value, exc_type='warning'): self.name = name @@ -49,7 +47,6 @@ class except_osv(Exception): self.args = (exc_type, name) -from tools.func import wraps class osv_pool(netsvc.Service): def check(f): @@ -60,19 +57,20 @@ class osv_pool(netsvc.Service): raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.') return f(self, dbname, *args, **kwargs) except orm.except_orm, inst: + if inst.name == 'AccessError': + tb_s = "AccessError\n" + "".join(traceback.format_exception(*sys.exc_info())) + self.logger.notifyChannel('web-services', netsvc.LOG_DEBUG, tb_s) self.abortResponse(1, inst.name, 'warning', inst.value) except except_osv, inst: self.abortResponse(1, inst.name, inst.exc_type, inst.value) except IntegrityError, inst: for key in self._sql_error.keys(): if key in inst[0]: - self.abortResponse(1, _('Constraint Error'), 'warning', _(self._sql_error[key])) + self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key]) self.abortResponse(1, 'Integrity Error', 'warning', inst[0]) except Exception, e: - import traceback, sys tb_s = "".join(traceback.format_exception(*sys.exc_info())) - logger = Logger() - logger.notifyChannel('web-services', LOG_ERROR, tb_s) + self.logger.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s) raise return wrapper @@ -87,6 +85,7 @@ class osv_pool(netsvc.Service): self._store_function = {} self._init = True self._init_parent = {} + self.logger = netsvc.Logger() netsvc.Service.__init__(self, 'object_proxy', audience='') self.exportMethod(self.obj_list) self.exportMethod(self.exec_workflow) diff --git a/bin/service/web_services.py b/bin/service/web_services.py index d99dc5df5d6..d8110fccc8b 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -135,7 +135,6 @@ class db(netsvc.Service): def get_progress(self, password, id): security.check_super(password) - tools.debug((id, self.actions.keys())) if self.actions[id]['thread'].isAlive(): # return addons.init_progress[db_name] return (min(self.actions[id].get('progress', 0),0.95), []) From 1c2210064c2fd410890c46e4587262ee6fa5fd4f Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 4 Feb 2010 18:28:00 +0100 Subject: [PATCH 093/328] [IMP] sql_db: better logging bzr revid: chs@tinyerp.com-20100204172800-oy6uavhn13osk9x1 --- bin/sql_db.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/bin/sql_db.py b/bin/sql_db.py index fec33b11284..9398a4206df 100644 --- a/bin/sql_db.py +++ b/bin/sql_db.py @@ -55,6 +55,7 @@ from tools.func import wraps from datetime import datetime as mdt from datetime import timedelta import threading +from inspect import stack import re re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$'); @@ -91,10 +92,7 @@ class Cursor(object): self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor) self.__closed = False # real initialisation value self.autocommit(False) - - if tools.config['log_level'] in (netsvc.LOG_DEBUG, netsvc.LOG_DEBUG_RPC): - from inspect import stack - self.__caller = tuple(stack()[2][1:3]) + self.__caller = tuple(stack()[2][1:3]) def __del__(self): if not self.__closed: @@ -103,10 +101,9 @@ class Cursor(object): # but the database connection is not put back into the connection # pool, preventing some operation on the database like dropping it. # This can also lead to a server overload. - if tools.config['log_level'] in (netsvc.LOG_DEBUG, netsvc.LOG_DEBUG_RPC): - msg = "Cursor not closed explicitly\n" \ - "Cursor was created at %s:%s" % self.__caller - log(msg, netsvc.LOG_WARNING) + msg = "Cursor not closed explicitly\n" \ + "Cursor was created at %s:%s" % self.__caller + log(msg, netsvc.LOG_WARNING) self.close() @check @@ -228,9 +225,14 @@ class ConnectionPool(object): self._lock = threading.Lock() self._logger = netsvc.Logger() + def __repr__(self): + used = len([1 for c, u in self._connections[:] if u]) + count = len(self._connections) + return "ConnectionPool(used=%d/count=%d/max=%d)" % (used, count, self._maxconn) + def _debug(self, msg): + self._logger.notifyChannel('ConnectionPool', netsvc.LOG_DEBUG, repr(self)) self._logger.notifyChannel('ConnectionPool', netsvc.LOG_DEBUG, msg) - #pass @locked def borrow(self, dsn): @@ -250,7 +252,7 @@ class ConnectionPool(object): return result if len(self._connections) >= self._maxconn: - # try to remove the older connection not used + # try to remove the oldest connection not used for i, (cnx, used) in enumerate(self._connections): if not used: self._debug('Removing old connection at index %d: %s' % (i, cnx.dsn)) @@ -258,7 +260,7 @@ class ConnectionPool(object): break else: # note: this code is called only if the for loop has completed (no break) - raise PoolError('Connection Pool Full') + raise PoolError('The Connection Pool Is Full') self._debug('Create new connection') result = psycopg2.connect(dsn=dsn) From c7b588ec599e824e7809e5290b34e3236d9bc60d Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Fri, 5 Feb 2010 09:32:08 +0100 Subject: [PATCH 094/328] [FIX] base_contact: search for a contact in a contact job now searches in firstname and lastname bzr revid: olt@tinyerp.com-20100205083208-1hhoonlnwmhaas5p --- addons/base_contact/base_contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_contact/base_contact.py b/addons/base_contact/base_contact.py index ca4fb93bced..b3065d808d5 100644 --- a/addons/base_contact/base_contact.py +++ b/addons/base_contact/base_contact.py @@ -124,7 +124,7 @@ class res_partner_job(osv.osv): self._order = 'sequence_partner' elif arg[0] == 'contact_id': self._order = 'sequence_contact' - elif arg[0] == 'name': + contact_obj = self.pool.get('res.partner.contact') search_arg = ['|', ('first_name', 'ilike', arg[2]), ('name', 'ilike', arg[2])] contact_ids = contact_obj.search(cr, user, search_arg, offset=offset, limit=limit, order=order, context=context, count=count) From 47c3b5519ac57520641f0cb9501d0abb3ddc4326 Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Fri, 5 Feb 2010 09:56:30 +0100 Subject: [PATCH 095/328] [FIX] sale: onchange on partner_id now set the partner's dedicated salesman as the sale salesman or the user if there is no dedicated salesman on that partner bzr revid: olt@tinyerp.com-20100205085630-pn0smp1ras32gmf9 --- addons/sale/sale.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 4cf3ab64af6..36cd10a4311 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -315,7 +315,7 @@ class sale_order(osv.osv): pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False payment_term = part.property_payment_term and part.property_payment_term.id or False fiscal_position = part.property_account_position and part.property_account_position.id or False - dedicated_salesman = part.user_id and part.user_id.id or False + dedicated_salesman = part.user_id and part.user_id.id or uid val = { 'partner_invoice_id': addr['invoice'], From 98c0c350443f94f2e610594c870cff2cdd5e73cf Mon Sep 17 00:00:00 2001 From: "olt@tinyerp.com" <> Date: Fri, 5 Feb 2010 10:34:57 +0100 Subject: [PATCH 096/328] [FIX] sale_crm: CRM->Opportunities: 'convert to quote' now sets the salesman from the partner (or the user if there is none) bzr revid: olt@tinyerp.com-20100205093457-ssc4z2bz7q8tak33 --- addons/sale_crm/wizard/makesale.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/sale_crm/wizard/makesale.py b/addons/sale_crm/wizard/makesale.py index f909f054e49..c05cb85ebba 100644 --- a/addons/sale_crm/wizard/makesale.py +++ b/addons/sale_crm/wizard/makesale.py @@ -92,7 +92,7 @@ class make_sale(wizard.interface): if False in partner_addr.values(): raise wizard.except_wizard(_('Data Insufficient!'),_('Customer has no addresses defined!')) - + vals = { 'origin': 'CRM:%s' % str(case.id), 'picking_policy': data['form']['picking_policy'], @@ -104,8 +104,13 @@ class make_sale(wizard.interface): 'partner_shipping_id': partner_addr['delivery'], 'order_policy': 'manual', 'date_order': now(), - 'fiscal_position': fpos + 'fiscal_position': fpos, } + + if partner_id: + partner = partner_obj.browse(cr, uid, partner_id, context=context) + vals['user_id'] = partner.user_id and partner.user_id.id or uid + if data['form']['analytic_account']: vals['project_id'] = data['form']['analytic_account'] new_id = sale_obj.create(cr, uid, vals) From 3c9f4f2fac422162a29f5114b7b257a09f7bc80e Mon Sep 17 00:00:00 2001 From: "VRA(OpenERP)" <> Date: Fri, 5 Feb 2010 17:05:25 +0530 Subject: [PATCH 097/328] [FIX] Sale/Purchase : Worfklow instance had to ve removed while setting record's workflow back to draft state lp bug: https://launchpad.net/bugs/401801 fixed bzr revid: jvo@tinyerp.com-20100205113525-5l4pb805w19s6c9q --- addons/purchase/purchase.py | 2 ++ addons/sale/sale.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index cc300516dd0..8f58dc32c10 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -300,6 +300,8 @@ class purchase_order(osv.osv): self.write(cr, uid, ids, {'state':'draft','shipped':0}) wf_service = netsvc.LocalService("workflow") for p_id in ids: + # Deleting the existing instance of workflow for PO + wf_service.trg_delete(uid, 'purchase.order', p_id, cr) wf_service.trg_create(uid, 'purchase.order', p_id, cr) return True diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 36cd10a4311..263c5f3c233 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -303,6 +303,8 @@ class sale_order(osv.osv): self.pool.get('sale.order.line').write(cr, uid, line_ids, {'invoiced': False, 'state': 'draft', 'invoice_lines': [(6, 0, [])]}) wf_service = netsvc.LocalService("workflow") for inv_id in ids: + # Deleting the existing instance of workflow for SO + wf_service.trg_delete(uid, 'sale.order', inv_id, cr) wf_service.trg_create(uid, 'sale.order', inv_id, cr) return True From ab557cc1e6b57666880f4361b39584c8b3f9ea0e Mon Sep 17 00:00:00 2001 From: "ACH,VRA" <> Date: Mon, 8 Feb 2010 12:56:55 +0530 Subject: [PATCH 098/328] [IMP] MRP : BOM structure report should follow hierarchy. bzr revid: jvo@tinyerp.com-20100208072655-f7nndzqb00tg7pm9 --- addons/mrp/report/__init__.py | 1 + addons/mrp/report/bom_structure.py | 64 +++++++++++++++++++++++++++++ addons/mrp/report/bom_structure.rml | 12 +++--- 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 addons/mrp/report/bom_structure.py diff --git a/addons/mrp/report/__init__.py b/addons/mrp/report/__init__.py index 4456190cfcb..8b47ad3527e 100644 --- a/addons/mrp/report/__init__.py +++ b/addons/mrp/report/__init__.py @@ -22,6 +22,7 @@ import price import workcenter_load import order +import bom_structure # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/report/bom_structure.py b/addons/mrp/report/bom_structure.py new file mode 100644 index 00000000000..25d179b6901 --- /dev/null +++ b/addons/mrp/report/bom_structure.py @@ -0,0 +1,64 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved +# $Id$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + +import time +from report import report_sxw +from osv import osv +import pooler + +class bom_structure(report_sxw.rml_parse): + def __init__(self, cr, uid, name, context): + super(bom_structure, self).__init__(cr, uid, name, context=context) + self.localcontext.update({ + 'time': time, + 'get_children':self.get_children, + }) + + def get_children(self, object, level=0): + result = [] + + def _get_rec(object,level): + for l in object: + res = {} + res['name'] = l.name + res['pname'] = l.product_id.name + res['pqty'] = l.product_qty + res['uname'] = l.product_uom.name + res['code'] = l.code + res['level'] = level + result.append(res) + if l.child_complete_ids: + if level<6: + level += 1 + _get_rec(l.child_complete_ids,level) + if level>0 and level<6: + level -= 1 + return result + + children = _get_rec(object,level) + + return children + +report_sxw.report_sxw('report.bom.structure','mrp.bom','mrp/report/bom_structure.rml',parser=bom_structure) + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/mrp/report/bom_structure.rml b/addons/mrp/report/bom_structure.rml index 7601e490eec..527c26d1581 100644 --- a/addons/mrp/report/bom_structure.rml +++ b/addons/mrp/report/bom_structure.rml @@ -112,20 +112,20 @@
- [[ repeatIn(o.bom_lines, 'l') ]] + [[ repeatIn(get_children(o.bom_lines), 'l') ]] - - [[ l.name ]] + [[ '... '*(l['level']) ]]-[[ l['name'] ]] - [[ l.product_id.name ]] + [[ l['pname'] ]] - [[ l.product_qty ]] [[ l.product_uom.name ]] + [[ l['pqty'] ]] [[ l['uname'] ]] - [[ l.code ]] + [[ l['code'] ]] @@ -135,4 +135,4 @@
- + \ No newline at end of file From 3b392b6b1f8383be610f2e403b8fd381379073c5 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 8 Feb 2010 14:11:23 +0530 Subject: [PATCH 099/328] [FIX] Project_timesheet: Product_uom supplied to Analytic entries lp bug: https://launchpad.net/bugs/516636 fixed bzr revid: jvo@tinyerp.com-20100208084123-swfjodhlqxh92c3z --- addons/project_timesheet/project_timesheet.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 903970cff21..724e36d41ba 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -57,6 +57,7 @@ class project_work(osv.osv): res['product_id'] = emp.product_id.id res['journal_id'] = emp.journal_id.id res['general_account_id'] = a + res['product_uom_id'] = emp.product_id.uom_id.id return res def create(self, cr, uid, vals, *args, **kwargs): @@ -77,6 +78,7 @@ class project_work(osv.osv): vals_line['general_account_id'] = result['general_account_id'] vals_line['journal_id'] = result['journal_id'] vals_line['amount'] = 00.0 + vals_line['product_uom_id'] = result['product_uom_id'] timeline_id = obj.create(cr, uid, vals_line, {}) vals_line['amount'] = (-1) * vals['hours'] * obj.browse(cr, uid, timeline_id).product_id.standard_price @@ -101,6 +103,7 @@ class project_work(osv.osv): vals_line['product_id'] = result['product_id'] vals_line['general_account_id'] = result['general_account_id'] vals_line['journal_id'] = result['journal_id'] + vals_line['product_uom_id'] = result['product_uom_id'] if 'date' in vals: vals_line['date'] = vals['date'][:10] if 'hours' in vals: From 3e298de7b027795e81581ea7cf8d6dc69ef76304 Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 8 Feb 2010 14:22:12 +0530 Subject: [PATCH 100/328] [IMP] Groups : Better duplication terminology. lp bug: https://launchpad.net/bugs/516969 fixed bzr revid: jvo@tinyerp.com-20100208085212-g4bggomq0nw2ijo9 --- bin/addons/base/res/res_user.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/addons/base/res/res_user.py b/bin/addons/base/res/res_user.py index a3c3923d17c..f62e9822c94 100644 --- a/bin/addons/base/res/res_user.py +++ b/bin/addons/base/res/res_user.py @@ -40,7 +40,12 @@ class groups(osv.osv): _sql_constraints = [ ('name_uniq', 'unique (name)', 'The name of the group must be unique !') ] - + + def copy(self, cr, uid, id, default=None, context={}): + group_name = self.read(cr, uid, [id], ['name'])[0]['name'] + default.update({'name': group_name +' (copy)'}) + return super(groups, self).copy(cr, uid, id, default, context) + def write(self, cr, uid, ids, vals, context=None): if 'name' in vals: if vals['name'].startswith('-'): From cc0b6a77061338ec8eb4a79ec379fe3945f4a3c7 Mon Sep 17 00:00:00 2001 From: "uco (OpenERP)" Date: Mon, 8 Feb 2010 16:04:14 +0530 Subject: [PATCH 101/328] [IMP] base_action_rule,crm: Added button for activating/deactivating rules. Added remind_user function for general purpose. Changes in crm according to action rules. bzr revid: uco@tinyerp.co.in-20100208103414-a40idgngbwuqjuui --- addons/base_action_rule/base_action_rule.py | 101 ++++++++++++++++-- .../base_action_rule_view.xml | 19 +++- addons/crm/crm.py | 51 +-------- 3 files changed, 110 insertions(+), 61 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 68988e4eb8a..0d9b2f295be 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -5,6 +5,7 @@ import re import tools from osv import fields, osv, orm from osv.orm import except_orm +from tools.translate import _ AVAILABLE_STATES = [ ('draft','Draft'), @@ -25,13 +26,95 @@ AVAILABLE_PRIORITIES = [ class base_action_rule(osv.osv): _name = 'base.action.rule' _description = 'Action Rules' + + def _get_max_level(self, cr, uid, ids, field_name, arg, context=None): + res = {} + for check in self.browse(cr, uid, ids): + if check.rule_lines and len(check.rule_lines) < 15: + res[check.id] = len(check.rule_lines) + elif len(check.rule_lines) > 15: + raise osv.except_osv(_('Error !'), _('Max Level exceeded.')) + return res + _columns = { - 'name': fields.many2one('ir.model', 'Model', required=True), - 'max_level': fields.integer('Max Level'), - 'rule_lines': fields.one2many('base.action.rule.line','rule_id','Rule Lines'), - 'create_date': fields.datetime('Create Date', readonly=1) + 'name': fields.many2one('ir.model', 'Model', required=True, states={'activate': [('readonly', True)]}), + 'max_level': fields.function(_get_max_level, method=True, string='Max Level', + type='integer', store=True, help='Specifies maximum rule lines can be entered.'), + 'rule_lines': fields.one2many('base.action.rule.line','rule_id','Rule Lines', states={'activate': [('readonly', True)]}), + 'create_date': fields.datetime('Create Date', readonly=1), + 'state': fields.selection([('draft','Draft'),('activate','Activated'),('deactivate','Deactivated')],'State',readonly=1) } + _defaults = { + 'state': lambda *a: 'draft', + } + + def button_activate_rule(self, cr, uid, ids, context=None): + check = self.browse(cr, uid, ids[0]).rule_lines + if not check: + raise osv.except_osv(_('Error !'), _('Rule Lines are empty ! Cannot activate the Rule.')) + cronobj = self.pool.get('ir.cron') + cronids = cronobj.search(cr,uid,[('model','=','base.action.rule'),('active','=',False)]) + if cronids: + cronobj.write(cr, uid, cronids, {'active': True}) + self.write(cr, uid, ids, {'state': 'activate'}) + return True + + def button_deactivate_rule(self, cr, uid, ids, context=None): + cronobj = self.pool.get('ir.cron') + cronids = cronobj.search(cr,uid,[('model','=','base.action.rule'),('active','=',True)]) + if cronids: + cronobj.write(cr, uid, cronids, {'active': False}) + self.write(cr, uid, ids, {'state': 'deactivate'}) + return True + + def remind_partner(self, cr, uid, ids, context={}, attach=False): + return self.remind_user(cr, uid, ids, context, attach, + destination=False) + + def remind_user(self, cr, uid, ids, context={}, attach=False, destination=True): + ruleline_obj = self.pool.get('base.action.rule.line') + for rule in self.browse(cr, uid, ids): + for action in rule.rule_lines: + if not action.act_remind_user: + raise osv.except_osv(_('Warning!'), ("Remind Responsible should be active.")) + if action.trg_user_id and action.trg_user_id.address_id and not action.trg_user_id.address_id.email: + raise osv.except_osv(_('Error!'), ("User Email is not specified.")) + if action.trg_user_id and action.trg_user_id.address_id and action.trg_user_id.address_id.email: + src = action.trg_user_id.address_id.email + dest = action.act_reply_to + body = action.act_mail_body + if not destination: + src, dest = dest, src + if action.trg_user_id.signature: + body += '\n\n%s' % (action.trg_user_id.signature or '') + dest = [dest] + + attach_to_send = None + + if attach: + attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', rule.name.model), ('res_id', '=', rule.name.id)]) + attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname','datas']) + attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send) + + # Send an email + flag = tools.email_send( + src, + dest, + "Reminder: [%s] %s" % (str(rule.name.id), rule.name.model, ), + ruleline_obj.format_body(body), + reply_to=action.act_reply_to, + openobject_id=str(rule.name.id), + attach=attach_to_send + ) + if flag: + raise except_orm(_('Email!'), + _("Email Successfully Sent by %s") % action.trg_user_id.name) + else: + raise except_orm(_('Email!'), + _("Email is not sent Successfully for %s") % action.trg_user_id.name) + return True + def _check(self, cr, uid, ids=False, context={}): ''' Function called by the scheduler to process models @@ -146,11 +229,9 @@ class base_action_rule(osv.osv): write['email_cc'] = action.act_email_cc obj.write(cr, uid, [data.id], write, context) if action.act_remind_user: - obj.remind_user(cr, uid, [data.id], context, attach=action.act_remind_attach) + self.remind_user(cr, uid, [rule.id], context, attach=action.act_remind_attach) if action.act_remind_partner: - obj.remind_partner(cr, uid, [data.id], context, attach=action.act_remind_attach) - if action.act_method: - getattr(caseobj, 'act_method')(cr, uid, [data.id], action, context) + self.remind_partner(cr, uid, [rule.id], context, attach=action.act_remind_attach) emails = [] if action.act_mail_to_user: if data.user_id and data.user_id.address_id: @@ -203,6 +284,7 @@ class base_action_rule_line(osv.osv): 'trg_priority_from': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Minimum Priority'), 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Maximum Priority'), + 'act_method': fields.char('Call Object Method', size=64), 'act_state': fields.selection([('','')]+AVAILABLE_STATES, 'Set state to', size=16), 'act_user_id': fields.many2one('res.users', 'Set responsible to'), 'act_priority': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Set priority to'), @@ -210,6 +292,7 @@ class base_action_rule_line(osv.osv): 'act_remind_partner': fields.boolean('Remind Partner', help="Check this if you want the rule to send a reminder by email to the partner."), 'act_remind_user': fields.boolean('Remind responsible', help="Check this if you want the rule to send a reminder by email to the user."), + 'act_reply_to': fields.char('Reply-To', size=64), 'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the case be attached to the reminder email sent."), 'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if you want the rule to send an email to the responsible person."), @@ -274,9 +357,9 @@ base_action_rule_line() class base_action_rule_history(osv.osv): _name = 'base.action.rule.history' _description = 'Action Rule History' - _rec_name = 'rule_id' _columns = { 'rule_id': fields.many2one('base.action.rule','Rule', required=True, readonly=1), + 'name': fields.related('rule_id', 'name', type='many2one', relation='ir.model', string='Model', readonly=1), 'res_id': fields.integer('Resource ID', readonly=1), 'date_action_last': fields.datetime('Last Action', readonly=1), 'date_action_next': fields.datetime('Next Action', readonly=1), diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index 441d2956151..20a8641cbcb 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -14,7 +14,13 @@ - + + + + +