diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py index 8748a03ff34..78e10cc7a20 100644 --- a/addons/account/report/account_aged_partner_balance.py +++ b/addons/account/report/account_aged_partner_balance.py @@ -17,17 +17,28 @@ class ReportAgedPartnerBalance(models.AbstractModel): # Do an invoice and a payment and unreconcile. The amount will be nullified # By default, the partner wouldn't appear in this report. # The context key allow it to appear + # In case of a period_length of 30 days as of 2019-02-08, we want the following periods: + # Name Stop Start + # 1 - 30 : 2019-02-07 - 2019-01-09 + # 31 - 60 : 2019-01-08 - 2018-12-10 + # 61 - 90 : 2018-12-09 - 2018-11-10 + # 91 - 120 : 2018-11-09 - 2018-10-11 + # +120 : 2018-10-10 ctx = self._context periods = {} start = datetime.strptime(date_from, "%Y-%m-%d") - relativedelta(days=1) for i in range(5)[::-1]: stop = start - relativedelta(days=period_length) + period_name = str((5-(i+1)) * period_length + 1) + '-' + str((5-i) * period_length) + period_stop = (start - relativedelta(days=1)).strftime('%Y-%m-%d') + if i == 0: + period_name = '+' + str(4 * period_length) periods[str(i)] = { - 'name': (i!=0 and (str((5-(i+1)) * period_length) + '-' + str((5-i) * period_length)) or ('+'+str(4 * period_length))), - 'stop': start.strftime('%Y-%m-%d'), + 'name': period_name, + 'stop': period_stop, 'start': (i!=0 and stop.strftime('%Y-%m-%d') or False), } - start = stop - relativedelta(days=1) + start = stop res = [] total = [] diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index 03d5d9ac8a3..91b567730b7 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -44,6 +44,7 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, { var defs = [this._super.apply(this, arguments)]; this.time = Date.now(); this.$progress = this.$('.progress'); + this.clickStatementName = this._initialState.bank_statement_id ? true : false; if (this._initialState.bank_statement_id) { var def = this.model.makeRecord("account.bank.statement", [{ diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml index 8345b94ac58..d352e2b8fa4 100644 --- a/addons/account/views/account_invoice_view.xml +++ b/addons/account/views/account_invoice_view.xml @@ -72,6 +72,7 @@ + diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index 1ebb1835507..3902d2e5737 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -423,8 +423,10 @@ - - + + + + diff --git a/addons/board/static/src/js/board_view.js b/addons/board/static/src/js/board_view.js index b2d57903a3d..60abe892045 100644 --- a/addons/board/static/src/js/board_view.js +++ b/addons/board/static/src/js/board_view.js @@ -9,6 +9,8 @@ var Domain = require('web.Domain'); var FormController = require('web.FormController'); var FormRenderer = require('web.FormRenderer'); var FormView = require('web.FormView'); +var pyUtils = require('web.pyeval'); // do not forwardport this to 12.0 +var session = require('web.session'); var viewRegistry = require('web.view_registry'); var _t = core._t; @@ -236,7 +238,6 @@ var BoardRenderer = FormRenderer.extend({ */ _createController: function (params) { var self = this; - var context = params.context.eval(); return this._rpc({ route: '/web/action/load', params: {action_id: params.actionID} @@ -246,18 +247,24 @@ var BoardRenderer = FormRenderer.extend({ // the action does not exist anymore return $.when(); } + // tz and lang are saved in the custom view + // override the language to take the current one + var rawContext = new Context(params.context, action.context, {lang: session.user_context.lang}); + var context = pyUtils.eval('context', rawContext); + var domain = params.domain || pyUtils.eval('domain', action.domain || '[]', action.context); + var viewType = params.viewType || action.views[0][1]; var view = _.find(action.views, function (descr) { - return descr[1] === params.viewType; - }) || [false, params.viewType]; + return descr[1] === viewType; + }) || [false, viewType]; return self.loadViews(action.res_model, context, [view]) .then(function (viewsInfo) { - var viewInfo = viewsInfo[params.viewType]; - var View = viewRegistry.get(params.viewType); + var viewInfo = viewsInfo[viewType]; + var View = viewRegistry.get(viewType); var view = new View(viewInfo, { action: action, context: context, - domain: params.domain, - groupBy: context.group_by, + domain: domain, + groupBy: context.group_by || [], modelName: action.res_model, hasSelectors: false, }); diff --git a/addons/board/static/tests/dashboard_tests.js b/addons/board/static/tests/dashboard_tests.js index e777b7b36b7..2293e1c87e6 100644 --- a/addons/board/static/tests/dashboard_tests.js +++ b/addons/board/static/tests/dashboard_tests.js @@ -6,6 +6,7 @@ var BoardView = require('board.BoardView'); var ListController = require('web.ListController'); var testUtils = require('web.test_utils'); var ListRenderer = require('web.ListRenderer'); +var pyeval = require('web.pyeval'); var createActionManager = testUtils.createActionManager; var createView = testUtils.createView; @@ -191,6 +192,57 @@ QUnit.test('basic functionality, with one sub action', function (assert) { form.destroy(); }); +QUnit.test('can render an action without view_mode attribute', function (assert) { + // The view_mode attribute is automatically set to the 'action' nodes when + // the action is added to the dashboard using the 'Add to dashboard' button + // in the searchview. However, other dashboard views can be written by hand + // (see openacademy tutorial), and in this case, we don't want hardcode + // action's params (like context or domain), as the dashboard can directly + // retrieve them from the action. Same applies for the view_type, as the + // first view of the action can be used, by default. + assert.expect(2); + + var form = createView({ + View: BoardView, + model: 'board', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + archs: { + 'partner,4,list': + '', + }, + mockRPC: function (route, args) { + if (route === '/board/static/src/img/layout_1-1-1.png') { + return $.when(); + } + if (route === '/web/action/load') { + return $.when({ + context: '{"b": 2}', + domain: '[["foo", "=", "yop"]]', + res_model: 'partner', + views: [[4, 'list'], [false, 'form']], + }); + } + if (route === '/web/dataset/search_read') { + assert.deepEqual(args.domain, [['foo', '=', 'yop']], + "should use the domain of the action"); + } + return this._super.apply(this, arguments); + }, + }); + + assert.strictEqual(form.$('.oe_action:contains(ABC) .o_list_view').length, 1, + "the list view (first view of action) should have been rendered correctly"); + + form.destroy(); +}); + QUnit.test('can sort a sub list', function (assert) { assert.expect(2); @@ -691,4 +743,44 @@ QUnit.test('save to dashboard actions with flag keepSearchView', function (asser actionManager.destroy(); }); +QUnit.test("Views should be loaded in the user's language", function (assert) { + assert.expect(2); + var form = createView({ + View: BoardView, + model: 'board', + data: this.data, + session: {user_context: {lang: 'fr_FR'}}, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + if (args.method === 'load_views') { + assert.deepEqual(pyeval.eval('context', args.kwargs.context), {lang: 'fr_FR'}, + 'The views should be loaded with the correct context'); + } + if (route === "/web/dataset/search_read") { + assert.deepEqual(args.context, {lang: 'fr_FR'}, + 'The data should be loaded with the correct context'); + } + if (route === '/web/action/load') { + return $.when({ + res_model: 'partner', + views: [[4, 'list']], + }); + } + return this._super.apply(this, arguments); + }, + archs: { + 'partner,4,list': + '', + }, + }); + + form.destroy(); +}); + }); diff --git a/addons/calendar/i18n/fr.po b/addons/calendar/i18n/fr.po index 13fdc967623..0da3f13c630 100644 --- a/addons/calendar/i18n/fr.po +++ b/addons/calendar/i18n/fr.po @@ -302,73 +302,87 @@ msgid "" " \n" "" msgstr "" -"
\"${user.company_id.name}\"



  • " +"\n" +"
    \n" +"% set colors = {'needsAction': 'grey', 'accepted': 'green', 'tentative': '#FFFF00', 'declined': 'red'}\n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +" \"${user.company_id.name}\"\n" +" \n" +"

    \n" +" Accepter\n" +" Refuser\n" +" Voir\n" +"

    \n" +"
    \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +"

    Invitation à l'événement ${object.event_id.name}

    \n" +"

    \n" +" Cher/chère ${object.common_name},
    \n" +" ${object.event_id.user_id.partner_id.name} vous a invité(e) à la réunion ${object.event_id.name} de ${object.event_id.user_id.company_id.name}.

    \n" +" \n" +" \n" +" \n" +"
    \n" +"
    \n" +" ${object.event_id.get_interval('dayname', tz=object.partner_id.tz if not object.event_id.allday else None)}\n" +"
    \n" +"
    \n" +" ${object.event_id.get_interval('day', tz=object.partner_id.tz if not object.event_id.allday else None)}\n" +"
    \n" +"
    ${object.event_id.get_interval('month', tz=object.partner_id.tz if not object.event_id.allday else None)}
    \n" +"
    ${not object.event_id.allday and object.event_id.get_interval('time', tz=object.partner_id.tz) or ''}
    \n" +"
    \n" +" \n" +"

    Détails de l'événement

    \n" +"
      \n" +" % if object.event_id.location:\n" +"
    • Lieu : ${object.event_id.location}\n" +" (Afficher la carte)\n" +"
    • \n" +" % endif\n" +" % if object.event_id.description :\n" +"
    • Description : ${object.event_id.description}
    • \n" +" % endif\n" +" % if not object.event_id.allday and object.event_id.duration\n" +"
    • Durée : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))}
    • \n" +" % endif\n" +"
    • Participants\n" +"
        \n" +" % for attendee in object.event_id.attendee_ids:\n" +"
      • \n" +"
        \n" +" % if attendee.common_name != object.common_name:\n" +" ${attendee.common_name}\n" +" % else:\n" +" Vous\n" +" % endif\n" +"
      • \n" +" % endfor\n" +"
    • \n" +"
    \n" +"
    \n" +"
    \n" +"
    " #. module: calendar #: model:mail.template,body_html:calendar.calendar_template_meeting_reminder @@ -456,73 +470,88 @@ msgid "" " \n" "" msgstr "" -"
    \"${user.company_id.name}\"



  • " +"\n" +"
    \n" +"% set colors = {'needsAction': 'grey', 'accepted': 'green', 'tentative': '#FFFF00', 'declined': 'red'}\n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +" \"${user.company_id.name}\"\n" +" \n" +"

    \n" +" Accepter\n" +" Refuser\n" +" Voir\n" +"

    \n" +"
    \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +"

    Rappel pour l'événement ${object.event_id.name}

    \n" +"

    \n" +" Cher/chère ${object.common_name},
    \n" +" Il s'agit d'un rappel pour l'événement ci-dessous :\n" +"

    \n" +" \n" +" \n" +" \n" +"
    \n" +"
    \n" +" ${object.event_id.get_interval('dayname', tz=object.partner_id.tz if not object.event_id.allday else None)}\n" +"
    \n" +"
    \n" +" ${object.event_id.get_interval('day', tz=object.partner_id.tz if not object.event_id.allday else None)}\n" +"
    \n" +"
    ${object.event_id.get_interval('month', tz=object.partner_id.tz if not object.event_id.allday else None)}
    \n" +"
    ${not object.event_id.allday and object.event_id.get_interval('time', tz=object.partner_id.tz) or ''}
    \n" +"
    \n" +" \n" +"

    Détails de l'événement

    \n" +"
      \n" +" % if object.event_id.location:\n" +"
    • Lieu : ${object.event_id.location}\n" +" (Afficher la carte)\n" +"
    • \n" +" % endif\n" +" % if object.event_id.description :\n" +"
    • Description : ${object.event_id.description}
    • \n" +" % endif\n" +" % if not object.event_id.allday and object.event_id.duration\n" +"
    • Durée : ${('%dH%02d' % (object.event_id.duration,(object.event_id.duration*60)%60))}
    • \n" +" % endif\n" +"
    • Participants\n" +"
        \n" +" % for attendee in object.event_id.attendee_ids:\n" +"
      • \n" +"
        \n" +" % if attendee.common_name != object.common_name:\n" +" ${attendee.common_name}\n" +" % else:\n" +" Vous\n" +" % endif\n" +"
      • \n" +" % endfor\n" +"
    • \n" +"
    \n" +"
    \n" +"
    \n" +"
    " #. module: calendar #: model:mail.template,subject:calendar.calendar_template_meeting_reminder diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 13c33451b67..d33781a84f1 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -66,7 +66,7 @@ - +