From 0915663545d4ba29d8d2a6e2860b90a85a89fd0f Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 22 Apr 2013 13:24:38 +0200 Subject: [PATCH 1/9] [FIX] note: notes without user's stage are added to the first user's stage (sort by sequence) bzr revid: chm@openerp.com-20130422112438-0f1s6we5ggq3t6xn --- addons/note/note.py | 23 +++++++++++++++-------- addons/note/note_data.xml | 6 ++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/addons/note/note.py b/addons/note/note.py index 2d95b15107a..90c2cb9bc54 100644 --- a/addons/note/note.py +++ b/addons/note/note.py @@ -141,12 +141,19 @@ class note_note(osv.osv): #note without user's stage nb_notes_ws = self.search(cr,uid, domain+[('stage_ids', 'not in', current_stage_ids)], context=context, count=True) if nb_notes_ws: - result += [{ #notes for unknown stage and if stage_ids is not empty - '__context': {'group_by': groupby[1:]}, - '__domain': domain + [('stage_ids', 'not in', current_stage_ids)], - 'stage_id': (0, 'Unknown'), - 'stage_id_count':nb_notes_ws - }] + # add note to the first column if it's the first stage + dom_not_in = ('stage_ids', 'not in', current_stage_ids) + if result and result[0]['stage_id'][0] == current_stage_ids[0]: + dom_in = result[0]['__domain'].pop() + result[0]['__domain'] = domain + ['|', dom_in, dom_not_in] + else: + # add the first stage column + result = [{ + '__context': {'group_by': groupby[1:]}, + '__domain': domain + [dom_not_in], + 'stage_id': (current_stage_ids[0], stage_name[current_stage_ids[0]]), + 'stage_id_count':nb_notes_ws + }] + result else: # if stage_ids is empty @@ -156,7 +163,7 @@ class note_note(osv.osv): result = [{ #notes for unknown stage '__context': {'group_by': groupby[1:]}, '__domain': domain, - 'stage_id': (0, 'Unknown'), + 'stage_id': False, 'stage_id_count':nb_notes_ws }] else: @@ -187,7 +194,7 @@ class res_users(osv.Model): model_id = data_obj.get_object_reference(cr, uid, 'base', 'group_user') #Employee Group group_id = model_id and model_id[1] or False if group_id in [x.id for x in user.groups_id]: - for note_xml_id in ['note_stage_01','note_stage_02','note_stage_03','note_stage_04']: + for note_xml_id in ['note_stage_00','note_stage_01','note_stage_02','note_stage_03','note_stage_04']: data_id = data_obj._get_id(cr, uid, 'note', note_xml_id) stage_id = data_obj.browse(cr, uid, data_id, context=context).res_id note_obj.copy(cr, uid, stage_id, default = { diff --git a/addons/note/note_data.xml b/addons/note/note_data.xml index 592292788b3..376b8e9053f 100644 --- a/addons/note/note_data.xml +++ b/addons/note/note_data.xml @@ -2,6 +2,12 @@ + + New + + + + Today 1 From e5736828f9eaa9de22360b52f6c31267cc2ee892 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 23 Apr 2013 14:41:51 +0200 Subject: [PATCH 2/9] [REVERT] revert commit fme@openerp.com-20130418171750-7oldgiewo1eewxk7: do not break stable API !!! bzr revid: chs@openerp.com-20130423124151-h025b891xp77flg3 --- addons/web/controllers/main.py | 24 +- addons/web/http.py | 60 +--- addons/web/tests/__init__.py | 3 +- addons/web/tests/test_dispatch.py | 373 ------------------------- addons/web_diagram/controllers/main.py | 7 +- 5 files changed, 22 insertions(+), 445 deletions(-) delete mode 100644 addons/web/tests/test_dispatch.py diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 47498752c24..01126694059 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- + import ast import base64 import csv import glob import itertools +import logging import operator import datetime import hashlib @@ -28,6 +30,7 @@ except ImportError: xlwt = None import openerp +import openerp.modules.registry from openerp.tools.translate import _ from .. import http @@ -1436,8 +1439,7 @@ class Action(openerpweb.Controller): else: return False - -class Export(openerpweb.Controller): +class Export(View): _cp_path = "/web/export" @openerpweb.jsonrequest @@ -1578,13 +1580,7 @@ class Export(openerpweb.Controller): (prefix + '/' + k, prefix_string + '/' + v) for k, v in self.fields_info(req, model, export_fields).iteritems()) - -class ExportFormat(object): - """ - Superclass for export formats, should probably be an abc and have a way to - generate _cp_path from fmt but it's a pain to deal with conflicts with - ControllerType - """ + #noinspection PyPropertyDefinition @property def content_type(self): """ Provides the format's content type """ @@ -1625,14 +1621,14 @@ class ExportFormat(object): else: columns_headers = [val['label'].strip() for val in fields] + return req.make_response(self.from_data(columns_headers, import_data), headers=[('Content-Disposition', content_disposition(self.filename(model), req)), ('Content-Type', self.content_type)], cookies={'fileToken': int(token)}) - -class CSVExport(ExportFormat, http.Controller): +class CSVExport(Export): _cp_path = '/web/export/csv' fmt = {'tag': 'csv', 'label': 'CSV'} @@ -1667,8 +1663,7 @@ class CSVExport(ExportFormat, http.Controller): fp.close() return data - -class ExcelExport(ExportFormat, http.Controller): +class ExcelExport(Export): _cp_path = '/web/export/xls' fmt = { 'tag': 'xls', @@ -1707,8 +1702,7 @@ class ExcelExport(ExportFormat, http.Controller): fp.close() return data - -class Reports(openerpweb.Controller): +class Reports(View): _cp_path = "/web/report" POLLING_DELAY = 0.25 TYPES_MAPPING = { diff --git a/addons/web/http.py b/addons/web/http.py index 7f99d27bd04..d12cdfb13a1 100644 --- a/addons/web/http.py +++ b/addons/web/http.py @@ -355,64 +355,18 @@ def httprequest(f): #---------------------------------------------------------- addons_module = {} addons_manifest = {} -controllers_class = {} +controllers_class = [] +controllers_object = {} controllers_path = {} class ControllerType(type): def __init__(cls, name, bases, attrs): super(ControllerType, cls).__init__(name, bases, attrs) - # Only for root "Controller" - if bases == (object,): - assert name == 'Controller' - return - - path = attrs.get('_cp_path') - if Controller in bases: - assert path, "Controller subclass %s missing a _cp_path" % name - else: - parent_paths = set(base._cp_path for base in bases - if issubclass(base, Controller)) - assert len(parent_paths) == 1,\ - "%s inheriting from multiple controllers is not supported" % ( - name) - [parent_path] = parent_paths - [parent] = [ - controller for controller in controllers_class.itervalues() - if controller._cp_path == parent_path] - - # inherit from a Controller subclass - if path: - # if extending in place with same URL, ignore URL - if parent_path == path: - _logger.warn( - "Controller %s extending %s in-place should not " - "explicitly specify URL", name, parent) - return - _logger.warn("Re-exposing %s at %s.\n" - "\tThis usage is unsupported.", - parent.__name__, - attrs['_cp_path']) - - if path: - assert path not in controllers_class,\ - "Trying to expose %s at the same URL as %s" % ( - name, controllers_class[path]) - controllers_class[path] = cls - + controllers_class.append(("%s.%s" % (cls.__module__, cls.__name__), cls)) class Controller(object): __metaclass__ = ControllerType - def __new__(cls, *args, **kwargs): - subclasses = [c for c in cls.__subclasses__() - if c._cp_path == cls._cp_path] - if subclasses: - name = "%s (+%s)" % ( - cls.__name__, - '+'.join(sub.__name__ for sub in subclasses)) - cls = type(name, tuple(reversed(subclasses)), {}) - return object.__new__(cls) - #---------------------------------------------------------- # Session context manager #---------------------------------------------------------- @@ -612,8 +566,12 @@ class Root(object): addons_manifest[module] = manifest self.statics['/%s/static' % module] = path_static - for c in controllers_class.itervalues(): - controllers_path[c._cp_path] = c() + for k, v in controllers_class: + if k not in controllers_object: + o = v() + controllers_object[k] = o + if hasattr(o, '_cp_path'): + controllers_path[o._cp_path] = o app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics) self.dispatch = DisableCacheMiddleware(app) diff --git a/addons/web/tests/__init__.py b/addons/web/tests/__init__.py index 2734bd45ddb..d7abb8e5a8c 100644 --- a/addons/web/tests/__init__.py +++ b/addons/web/tests/__init__.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- -from . import test_dataset, test_menu, test_serving_base, test_js, test_dispatch +from . import test_dataset, test_menu, test_serving_base, test_js fast_suite = [] checks = [ test_dataset, test_menu, test_serving_base, - test_dispatch, ] diff --git a/addons/web/tests/test_dispatch.py b/addons/web/tests/test_dispatch.py deleted file mode 100644 index b532cebac44..00000000000 --- a/addons/web/tests/test_dispatch.py +++ /dev/null @@ -1,373 +0,0 @@ -# -*- coding: utf-8 -*- -import contextlib -import json -import logging -import logging.handlers -import types -import unittest2 - -from .. import http -import werkzeug.test - - -def setUpModule(): - """ - Force load_addons once to import all the crap we don't care for as this - thing is full of side-effects - """ - http.Root().load_addons() - -class DispatchCleanup(unittest2.TestCase): - """ - Cleans up controllers registries in the web client so it's possible to - test controllers registration and dispatching in isolation. - """ - def setUp(self): - self.classes = http.controllers_class - self.paths = http.controllers_path - - http.controllers_class = {} - http.controllers_path = {} - - def tearDown(self): - http.controllers_path = self.paths - http.controllers_class = self.classes - - -def jsonrpc_body(params=None): - """ - Builds and dumps the body of a JSONRPC request with params ``params`` - """ - return json.dumps({ - 'jsonrpc': '2.0', - 'method': 'call', - 'id': None, - 'params': params or {}, - }) - - -def jsonrpc_response(result=None): - """ - Builds a JSONRPC response (as a Python dict) with result ``result`` - """ - return { - u'jsonrpc': u'2.0', - u'id': None, - u'result': result, - } - - -class TestHandler(logging.handlers.BufferingHandler): - def __init__(self): - logging.handlers.BufferingHandler.__init__(self, 0) - - def shouldFlush(self, record): - return False - -@contextlib.contextmanager -def capture_logging(logger, level=logging.DEBUG): - logger = logging.getLogger(logger) - old_level = logger.level - old_handlers = logger.handlers - old_propagate = logger.propagate - - test_handler = TestHandler() - logger.handlers = [test_handler] - logger.setLevel(level) - logger.propagate = False - - try: - yield test_handler - finally: - logger.propagate = old_propagate - logger.setLevel(old_level) - logger.handlers = old_handlers - - -class TestDispatching(DispatchCleanup): - def setUp(self): - super(TestDispatching, self).setUp() - self.app = http.Root() - self.client = werkzeug.test.Client(self.app) - - def test_not_exposed(self): - class CatController(http.Controller): - _cp_path = '/cat' - - def index(self): - return 'Blessid iz da feline' - - self.app.load_addons() - - body, status, headers = self.client.get('/cat') - self.assertEqual('404 NOT FOUND', status) - - def test_basic_http(self): - class CatController(http.Controller): - _cp_path = '/cat' - - @http.httprequest - def index(self, req): - return 'no walk in counsil of wickid,' - - self.app.load_addons() - - body, status, headers = self.client.get('/cat') - self.assertEqual('200 OK', status) - self.assertEqual('no walk in counsil of wickid,', ''.join(body)) - - def test_basic_jsonrpc(self): - class CatController(http.Controller): - _cp_path = '/cat' - - @http.jsonrequest - def index(self, req): - return 'no place paws in path of da sinnerz,' - self.app.load_addons() - - body, status, headers = self.client.post('/cat', data=jsonrpc_body()) - - self.assertEqual('200 OK', status) - self.assertEqual( - jsonrpc_response('no place paws in path of da sinnerz,'), - json.loads(''.join(body))) - - -class TestSubclassing(DispatchCleanup): - def setUp(self): - super(TestSubclassing, self).setUp() - self.app = http.Root() - self.client = werkzeug.test.Client(self.app) - - def test_add_method(self): - class CatController(http.Controller): - _cp_path = '/cat' - - @http.httprequest - def index(self, req): - return 'no sit and purr with da mockerz.' - - class CeilingController(CatController): - @http.httprequest - def lol(self, req): - return 'But der delightz in lawz of Ceiling Cat,' - - self.app.load_addons() - - body, status, headers = self.client.get('/cat') - self.assertEqual('200 OK', status) - self.assertEqual('no sit and purr with da mockerz.', ''.join(body)) - body, status, headers = self.client.get('/cat/lol') - self.assertEqual('200 OK', status) - self.assertEqual('But der delightz in lawz of Ceiling Cat,', - ''.join(body)) - - def test_override_method(self): - class CatController(http.Controller): - _cp_path = '/cat' - - @http.httprequest - def index(self, req): - return 'an ponderz' - - class CeilingController(CatController): - @http.httprequest - def index(self, req): - return '%s much.' % super(CeilingController, self).index(req) - - self.app.load_addons() - - body, status, headers = self.client.get('/cat') - self.assertEqual('200 OK', status) - self.assertEqual('an ponderz much.', ''.join(body)) - - def test_make_invisible(self): - class CatController(http.Controller): - _cp_path = '/cat' - - @http.httprequest - def index(self, req): - return 'Tehy liek treez bai teh waterz,' - - class CeilingController(CatController): - def index(self, req): - return super(CeilingController, self).index(req) - - self.app.load_addons() - - body, status, headers = self.client.get('/cat') - self.assertEqual('404 NOT FOUND', status) - - def test_make_json_invisible(self): - class CatController(http.Controller): - _cp_path = '/cat' - - @http.jsonrequest - def index(self, req): - return 'Tehy liek treez bai teh waterz,' - - class CeilingController(CatController): - def index(self, req): - return super(CeilingController, self).index(req) - - self.app.load_addons() - - body, status, headers = self.client.post('/cat') - self.assertEqual('404 NOT FOUND', status) - - def test_extends(self): - """ - When subclassing an existing Controller new classes are "merged" into - the base one - """ - class A(http.Controller): - _cp_path = '/foo' - @http.httprequest - def index(self, req): - return '1' - - class B(A): - @http.httprequest - def index(self, req): - return "%s 2" % super(B, self).index(req) - - class C(A): - @http.httprequest - def index(self, req): - return "%s 3" % super(C, self).index(req) - - self.app.load_addons() - - body, status, headers = self.client.get('/foo') - self.assertEqual('200 OK', status) - self.assertEqual('1 2 3', ''.join(body)) - - def test_extends_same_path(self): - """ - When subclassing an existing Controller and specifying the same - _cp_path as the parent, ??? - """ - class A(http.Controller): - _cp_path = '/foo' - @http.httprequest - def index(self, req): - return '1' - - class B(A): - _cp_path = '/foo' - @http.httprequest - def index(self, req): - return '2' - - self.app.load_addons() - - body, status, headers = self.client.get('/foo') - self.assertEqual('200 OK', status) - self.assertEqual('2', ''.join(body)) - - def test_re_expose(self): - """ - An existing Controller should not be extended with a new cp_path - (re-exposing somewhere else) - """ - class CatController(http.Controller): - _cp_path = '/cat' - - @http.httprequest - def index(self, req): - return '[%s]' % self.speak() - - def speak(self): - return 'Yu ordered cheezburgerz,' - - with capture_logging('openerp.addons.web.http') as handler: - class DogController(CatController): - _cp_path = '/dog' - - def speak(self): - return 'Woof woof woof woof' - - [record] = handler.buffer - self.assertEqual(logging.WARN, record.levelno) - self.assertEqual("Re-exposing CatController at /dog.\n" - "\tThis usage is unsupported.", - record.getMessage()) - - def test_fail_redefine(self): - """ - An existing Controller can't be overwritten by a new one on the same - path (? or should this generate a warning and still work as if it was - an extend?) - """ - class FooController(http.Controller): - _cp_path = '/foo' - - with self.assertRaises(AssertionError): - class BarController(http.Controller): - _cp_path = '/foo' - - def test_fail_no_path(self): - """ - A Controller must have a path (and thus be exposed) - """ - with self.assertRaises(AssertionError): - class FooController(http.Controller): - pass - - def test_mixin(self): - """ - Can mix "normal" python classes into a controller directly - """ - class Mixin(object): - @http.httprequest - def index(self, req): - return 'ok' - - class FooController(http.Controller, Mixin): - _cp_path = '/foo' - - class BarContoller(Mixin, http.Controller): - _cp_path = '/bar' - - self.app.load_addons() - - body, status, headers = self.client.get('/foo') - self.assertEqual('200 OK', status) - self.assertEqual('ok', ''.join(body)) - - body, status, headers = self.client.get('/bar') - self.assertEqual('200 OK', status) - self.assertEqual('ok', ''.join(body)) - - def test_mixin_extend(self): - """ - Can mix "normal" python class into a controller by extension - """ - class FooController(http.Controller): - _cp_path = '/foo' - - class M1(object): - @http.httprequest - def m1(self, req): - return 'ok 1' - - class M2(object): - @http.httprequest - def m2(self, req): - return 'ok 2' - - class AddM1(FooController, M1): - pass - - class AddM2(M2, FooController): - pass - - self.app.load_addons() - - body, status, headers = self.client.get('/foo/m1') - self.assertEqual('200 OK', status) - self.assertEqual('ok 1', ''.join(body)) - - body, status, headers = self.client.get('/foo/m2') - self.assertEqual('200 OK', status) - self.assertEqual('ok 2', ''.join(body)) diff --git a/addons/web_diagram/controllers/main.py b/addons/web_diagram/controllers/main.py index 2efaab7183d..c598a7e4652 100644 --- a/addons/web_diagram/controllers/main.py +++ b/addons/web_diagram/controllers/main.py @@ -1,10 +1,9 @@ -from openerp.addons.web.http import Controller, jsonrequest +import openerp - -class Diagram(Controller): +class DiagramView(openerp.addons.web.controllers.main.View): _cp_path = "/web_diagram/diagram" - @jsonrequest + @openerp.addons.web.http.jsonrequest def get_diagram_info(self, req, id, model, node, connector, src_node, des_node, label, **kw): From 2e4aafa7dc26981066df7f7fa648241e6d13fd5e Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 23 Apr 2013 15:06:56 +0200 Subject: [PATCH 3/9] [FIX] res.partner: proper store trigger for display name field That was a stupid oversight, sorry! bzr revid: odo@openerp.com-20130423130656-celrtr352yht19ro --- addons/account_report_company/account_report_company.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account_report_company/account_report_company.py b/addons/account_report_company/account_report_company.py index 0c102fcc4e1..eacee60e13c 100644 --- a/addons/account_report_company/account_report_company.py +++ b/addons/account_report_company/account_report_company.py @@ -30,7 +30,7 @@ class res_partner(osv.Model): _display_name_store_triggers = { 'res.partner': (lambda self,cr,uid,ids,context=None: self.search(cr, uid, [('id','child_of',ids)]), - ['parent_id', 'is_company'], 10) + ['parent_id', 'is_company', 'name'], 10) } # indirection to avoid passing a copy of the overridable method when declaring the function field From 6d9ae761cdc5b5b4599c0fbe1a63265f930ff8b9 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 23 Apr 2013 14:28:20 +0200 Subject: [PATCH 4/9] [FIX] hr_timesheet: return default values for journal and product only if the uid has the read access on it (multi-company) thanks to a new method on ir.model.data: check_object_reference() bzr revid: qdp-launchpad@openerp.com-20130423122820-9gf2i4iic5bqr8g6 --- addons/hr_timesheet/hr_timesheet.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index 6a538e5d09c..461507f9e41 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -37,8 +37,8 @@ class hr_employee(osv.osv): def _getAnalyticJournal(self, cr, uid, context=None): md = self.pool.get('ir.model.data') try: - result = md.get_object_reference(cr, uid, 'hr_timesheet', 'analytic_journal') - return result[1] + dummy, res_id = md.check_object_reference(cr, uid, 'hr_timesheet', 'analytic_journal') + return res_id except ValueError: pass return False @@ -46,8 +46,8 @@ class hr_employee(osv.osv): def _getEmployeeProduct(self, cr, uid, context=None): md = self.pool.get('ir.model.data') try: - result = md.get_object_reference(cr, uid, 'product', 'product_product_consultant') - return result[1] + dummy, res_id = md.check_object_reference(cr, uid, 'product', 'product_product_consultant') + return res_id except ValueError: pass return False From 7e462604747f4f9bd8668732b9cdae72d8613fd2 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 23 Apr 2013 14:57:31 +0200 Subject: [PATCH 5/9] [FIX] account: modified the constraint on account.move.reconcile in order to allow the reconciliation to be made on different partners if it doesn't concern a payable or receivable account bzr revid: qdp-launchpad@openerp.com-20130423125731-dblhpx7zz4q9x464 --- addons/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index 9cc6023375e..58c4cf447ef 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1680,7 +1680,7 @@ class account_move_reconcile(osv.osv): elif reconcile.line_partial_ids: first_partner = reconcile.line_partial_ids[0].partner_id.id move_lines = reconcile.line_partial_ids - if any([line.partner_id.id != first_partner for line in move_lines]): + if any([(line.account_id.type in ('receivable', 'payable') and line.partner_id.id != first_partner) for line in move_lines]): return False return True From 2bc4260c545dca24a1ea4f92dd52ac548a577b0e Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 23 Apr 2013 15:50:28 +0200 Subject: [PATCH 6/9] [FIX] account_voucher: fix in amount_currency computation when the rate is specified on the voucher and the voucher is in the company currency bzr revid: qdp-launchpad@openerp.com-20130423135028-70887smme8mxv0tn --- addons/account_voucher/account_voucher.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index dd8bb157067..9e0e7e5b167 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1167,8 +1167,13 @@ class account_voucher(osv.osv): amount_currency = sign * (line.amount) elif line.move_line_id.currency_id.id == voucher_brw.payment_rate_currency_id.id: # if the rate is specified on the voucher, we must use it - voucher_rate = currency_obj.browse(cr, uid, voucher_currency, context=ctx).rate - amount_currency = (move_line['debit'] - move_line['credit']) * voucher_brw.payment_rate * voucher_rate + payment_rate = voucher_brw.payment_rate + if voucher_currency != company_currency: + #if the voucher currency is not the company currency, we need to consider the rate of the line's currency + voucher_rate = currency_obj.browse(cr, uid, voucher_currency, context=ctx).rate + payment_rate = voucher_rate * payment_rate + amount_currency = (move_line['debit'] - move_line['credit']) * payment_rate + else: # otherwise we use the rates of the system (giving the voucher date in the context) amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx) From e13dc082e5f58f1bbaed3625e53ff1afb422562a Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 23 Apr 2013 16:49:48 +0200 Subject: [PATCH 7/9] [REV] revert revision 9056 that was deleting a whole wizard. The patch is good but not applicable as it in the v7 bzr revid: qdp-launchpad@openerp.com-20130423144948-unekni1ztjwv71fb --- .../account_analytic_analysis.py | 32 +++------- .../account_analytic_analysis_view.xml | 4 +- addons/hr_timesheet_invoice/__openerp__.py | 1 + .../test/test_hr_timesheet_invoice.yml | 17 ++++- .../test_hr_timesheet_invoice_no_prod_tax.yml | 17 ++++- .../hr_timesheet_invoice/wizard/__init__.py | 1 + .../hr_timesheet_final_invoice_create.py | 63 +++++++++++++++++++ ...hr_timesheet_invoice_create_final_view.xml | 52 +++++++++++++++ 8 files changed, 160 insertions(+), 27 deletions(-) create mode 100644 addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py create mode 100644 addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create_final_view.xml diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 89e39c71660..a187f9452f5 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -206,14 +206,17 @@ class account_analytic_account(osv.osv): return res if child_ids: - #Search all invoice lines not in cancelled state that refer to this analytic account - inv_line_obj = self.pool.get("account.invoice.line") - inv_lines = inv_line_obj.search(cr, uid, ['&', ('account_analytic_id', 'in', child_ids), ('invoice_id.state', '!=', 'cancel')], context=context) - for line in inv_line_obj.browse(cr, uid, inv_lines, context=context): - res[line.account_analytic_id.id] += line.price_subtotal + 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 account_analytic_journal.type = 'sale' \ + GROUP BY account_analytic_line.account_id", (child_ids,)) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) for acc in self.browse(cr, uid, res.keys(), context=context): res[acc.id] = res[acc.id] - (acc.timesheet_ca_invoiced or 0.0) - res_final = res return res_final @@ -540,23 +543,6 @@ class account_analytic_account(osv.osv): pass return result - - def hr_to_invoice_timesheets(self, cr, uid, ids, context=None): - domain = [('invoice_id','=',False),('to_invoice','!=',False), ('journal_id.type', '=', 'general'), ('account_id', 'in', ids)] - names = [record.name for record in self.browse(cr, uid, ids, context=context)] - name = _('Timesheets to Invoice of %s') % ','.join(names) - return { - 'type': 'ir.actions.act_window', - 'name': name, - 'view_type': 'form', - 'view_mode': 'tree,form', - 'domain' : domain, - 'res_model': 'account.analytic.line', - 'nodestroy': True, - } - - - class account_analytic_account_summary_user(osv.osv): _name = "account_analytic_analysis.summary.user" _description = "Hours Summary by User" diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml index c70a2493be9..8d7a89e4622 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml @@ -98,8 +98,8 @@ -