diff --git a/addons/stock/report/stock_traceability.py b/addons/stock/report/stock_traceability.py index d28b94757e2..eb1aba84c8b 100644 --- a/addons/stock/report/stock_traceability.py +++ b/addons/stock/report/stock_traceability.py @@ -231,17 +231,13 @@ class MrpStockReport(models.TransientModel): specific_paperformat_args={'data-report-margin-top': 17, 'data-report-header-spacing': 12} ) - def _get_html(self): - result = {} - rcontext = {} + def _get_main_lines(self): context = dict(self.env.context) - rcontext['lines'] = self.with_context(context).get_lines() - result['html'] = self.env['ir.qweb']._render('stock.report_stock_inventory', rcontext) - return result + return self.with_context(context).get_lines() @api.model - def get_html(self, given_context=None): + def get_main_lines(self, given_context=None): res = self.search([('create_uid', '=', self.env.uid)], limit=1) if not res: - return self.create({}).with_context(given_context)._get_html() - return res.with_context(given_context)._get_html() + return self.create({}).with_context(given_context)._get_main_lines() + return res.with_context(given_context)._get_main_lines() diff --git a/addons/stock/static/src/client_actions/stock_traceability_report_backend.js b/addons/stock/static/src/client_actions/stock_traceability_report_backend.js new file mode 100644 index 00000000000..d86818eac2f --- /dev/null +++ b/addons/stock/static/src/client_actions/stock_traceability_report_backend.js @@ -0,0 +1,145 @@ +/** @odoo-module **/ + +import { Component, onWillStart, useState } from "@odoo/owl"; +import { download } from "@web/core/network/download"; +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { Layout } from "@web/search/layout"; +import { useSetupAction } from "@web/webclient/actions/action_hook"; + +function processLine(line) { + return { ...line, lines: [], isFolded: true }; +} + +function extractPrintData(lines) { + const data = []; + for (const line of lines) { + const { id, model_id, model, unfoldable, level } = line; + data.push({ + id: id, + model_id: model_id, + model_name: model, + unfoldable, + level: level || 1, + }); + if (!line.isFolded) { + data.push(...extractPrintData(line.lines)); + } + } + return data; +} + +export class TraceabilityReport extends Component { + static template = "stock.TraceabilityReport"; + static components = { Layout }; + + setup() { + this.actionService = useService("action"); + this.orm = useService("orm"); + this.user = useService("user"); + + onWillStart(this.onWillStart); + useSetupAction({ + getLocalState: () => ({ + lines: [...this.state.lines], + }), + }); + + this.state = useState({ + lines: this.props.state?.lines || [], + }); + + const { active_id, active_model, auto_unfold, context, lot_name, ttype, url } = + this.props.action.context; + this.controllerUrl = url; + + this.context = context || {}; + Object.assign(this.context, { + active_id: active_id || this.props.action.params.active_id, + auto_unfold: auto_unfold || false, + model: active_model || false, + lot_name: lot_name || false, + ttype: ttype || false, + }); + + this.display = { + controlPanel: { "top-right": false, "bottom-right": false }, + searchPanel: false, + }; + } + + async onWillStart() { + if (!this.state.lines.length) { + const mainLines = await this.orm.call("stock.traceability.report", "get_main_lines", [ + this.context, + ]); + this.state.lines = mainLines.map(processLine); + } + } + + onClickBoundLink(line) { + this.actionService.doAction({ + type: "ir.actions.act_window", + res_model: line.res_model, + res_id: line.res_id, + views: [[false, "form"]], + target: "current", + }); + } + + onCLickOpenLot(line) { + this.actionService.doAction({ + type: "ir.actions.client", + tag: "stock_report_generic", + name: line.lot_name !== undefined && line.lot_name.toString(), + context: { + active_id: line.lot_id, + active_model: "stock.lot", + url: "/stock/output_format/stock?active_id=:active_id&active_model=:active_model", + }, + }); + } + + onClickUpDownStream(line) { + this.actionService.doAction({ + type: "ir.actions.client", + tag: "stock_report_generic", + name: this.env._t("Traceability Report"), + context: { + active_id: line.model_id, + active_model: line.model, + auto_unfold: true, + lot_name: line.lot_name !== undefined && line.lot_name, + url: "/stock/output_format/stock/active_id", + }, + }); + } + + onClickPrint() { + const data = JSON.stringify(extractPrintData(this.state.lines)); + const url = this.controllerUrl + .replace(":active_id", this.context.active_id) + .replace(":active_model", this.context.model) + .replace("output_format", "pdf"); + + download({ + data: { data }, + url, + }); + } + + async toggleLine(line) { + line.isFolded = !line.isFolded; + if (!line.lines.length) { + line.lines = ( + await this.orm.call("stock.traceability.report", "get_lines", [line.id], { + model_id: line.model_id, + model_name: line.model, + level: line.level + 30 || 1, + }) + ).map(processLine); + } + } +} + +registry.category("actions").add("stock_report_generic", TraceabilityReport); diff --git a/addons/stock/static/src/client_actions/stock_traceability_report_backend.xml b/addons/stock/static/src/client_actions/stock_traceability_report_backend.xml new file mode 100644 index 00000000000..415c4cdf16f --- /dev/null +++ b/addons/stock/static/src/client_actions/stock_traceability_report_backend.xml @@ -0,0 +1,82 @@ + + + + +
+ + + + + +
+ +

Traceability Report

+
+ + + + + + + + + + + + + + + + + + +
ReferenceProductDateLot/Serial #FromToQuantity
+
+
+

No operation made on this lot.

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/addons/stock/static/src/js/stock_traceability_report_backend.js b/addons/stock/static/src/js/stock_traceability_report_backend.js deleted file mode 100644 index 105e02a5aec..00000000000 --- a/addons/stock/static/src/js/stock_traceability_report_backend.js +++ /dev/null @@ -1,111 +0,0 @@ -odoo.define('stock.stock_report_generic', function (require) { -'use strict'; - -var AbstractAction = require('web.AbstractAction'); -var core = require('web.core'); -var session = require('web.session'); -var ReportWidget = require('stock.ReportWidget'); -var framework = require('web.framework'); - -var QWeb = core.qweb; - -var stock_report_generic = AbstractAction.extend({ - hasControlPanel: true, - - // Stores all the parameters of the action. - init: function(parent, action) { - this._super.apply(this, arguments); - this.actionManager = parent; - this.given_context = Object.assign({}, session.user_context); - this.controller_url = action.context.url; - if (action.context.context) { - this.given_context = action.context.context; - } - this.given_context.active_id = action.context.active_id || action.params.active_id; - this.given_context.model = action.context.active_model || false; - this.given_context.ttype = action.context.ttype || false; - this.given_context.auto_unfold = action.context.auto_unfold || false; - this.given_context.lot_name = action.context.lot_name || false; - }, - willStart: function() { - return Promise.all([this._super.apply(this, arguments), this.get_html()]); - }, - set_html: function() { - var self = this; - var def = Promise.resolve(); - if (!this.report_widget) { - this.report_widget = new ReportWidget(this, this.given_context); - def = this.report_widget.appendTo(this.$('.o_content')); - } - return def.then(function () { - self.report_widget.$el.html(self.html); - self.report_widget.$el.find('.o_report_heading').html('

Traceability Report

'); - if (self.given_context.auto_unfold) { - _.each(self.$el.find('.fa-caret-right'), function (line) { - self.report_widget.autounfold(line, self.given_context.lot_name); - }); - } - }); - }, - start: async function() { - const props = this.getControlPanelProps(); - this.controlPanelProps.cp_content = props; - await this._super(...arguments); - this.set_html(); - }, - getControlPanelProps: function() { - return { $buttons: this.$buttons } - }, - // Fetches the html and is previous report.context if any, else create it - get_html: async function() { - const { html } = await this._rpc({ - args: [this.given_context], - method: 'get_html', - model: 'stock.traceability.report', - }); - this.html = html; - this.renderButtons(); - }, - // Updates the control panel and render the elements that have yet to be rendered - update_cp: function() { - if (!this.$buttons) { - this.renderButtons(); - } - this.controlPanelProps.cp_content = { $buttons: this.$buttons }; - return this.updateControlPanel(); - }, - renderButtons: function() { - var self = this; - this.$buttons = $(QWeb.render("stockReports.buttons", {})); - // pdf output - this.$buttons.bind('click', function () { - var $element = $(self.$el[0]).find('.o_stock_reports_table tbody tr'); - var dict = []; - - $element.each(function( index ) { - var $el = $($element[index]); - dict.push({ - 'id': $el.data('id'), - 'model_id': $el.data('model_id'), - 'model_name': $el.data('model'), - 'unfoldable': $el.data('unfold'), - 'level': $el.find('td:first').data('level') || 1 - }); - }); - framework.blockUI(); - var url_data = self.controller_url.replace(':active_id', self.given_context.active_id); - url_data = url_data.replace(':active_model', self.given_context.model); - session.get_file({ - url: url_data.replace('output_format', 'pdf'), - data: {data: JSON.stringify(dict)}, - complete: framework.unblockUI, - error: (error) => self.call('crash_manager', 'rpc_error', error), - }); - }); - return this.$buttons; - }, -}); - -core.action_registry.add("stock_report_generic", stock_report_generic); -return stock_report_generic; -}); diff --git a/addons/stock/static/src/js/stock_traceability_report_widgets.js b/addons/stock/static/src/js/stock_traceability_report_widgets.js deleted file mode 100644 index 66921b97d15..00000000000 --- a/addons/stock/static/src/js/stock_traceability_report_widgets.js +++ /dev/null @@ -1,131 +0,0 @@ -odoo.define('stock.ReportWidget', function (require) { -'use strict'; - -var core = require('web.core'); -var Widget = require('web.Widget'); - -var QWeb = core.qweb; - -var _t = core._t; - -var ReportWidget = Widget.extend({ - events: { - 'click span.o_stock_reports_foldable': 'fold', - 'click span.o_stock_reports_unfoldable': 'unfold', - 'click .o_stock_reports_web_action': 'boundLink', - 'click .o_stock_reports_stream': 'updownStream', - 'click .o_stock_report_lot_action': 'actionOpenLot' - }, - init: function(parent) { - this._super.apply(this, arguments); - }, - start: function() { - QWeb.add_template("/stock/static/src/xml/stock_traceability_report_line.xml"); - return this._super.apply(this, arguments); - }, - boundLink: function(e) { - e.preventDefault(); - return this.do_action({ - type: 'ir.actions.act_window', - res_model: $(e.target).data('res-model'), - res_id: $(e.target).data('active-id'), - views: [[false, 'form']], - target: 'current' - }); - }, - actionOpenLot: function(e) { - e.preventDefault(); - var $el = $(e.target).parents('tr'); - this.do_action({ - type: 'ir.actions.client', - tag: 'stock_report_generic', - name: $el.data('lot_name') !== undefined && $el.data('lot_name').toString(), - context: { - active_id : $el.data('lot_id'), - active_model : 'stock.lot', - url: '/stock/output_format/stock?active_id=:active_id&active_model=:active_model', - }, - }); - }, - updownStream: function(e) { - var $el = $(e.target).parents('tr'); - this.do_action({ - type: "ir.actions.client", - tag: 'stock_report_generic', - name: _t("Traceability Report"), - context: { - active_id : $el.data('model_id'), - active_model : $el.data('model'), - auto_unfold: true, - lot_name: $el.data('lot_name') !== undefined && $el.data('lot_name').toString(), - url: '/stock/output_format/stock/active_id' - }, - }); - }, - removeLine: function(element) { - var self = this; - var el, $el; - var rec_id = element.data('id'); - var $stockEl = element.nextAll('tr[data-parent_id=' + rec_id + ']') - for (el in $stockEl) { - $el = $($stockEl[el]).find(".o_stock_reports_domain_line_0, .o_stock_reports_domain_line_1"); - if ($el.length === 0) { - break; - } - else { - var $nextEls = $($el[0]).parents("tr"); - self.removeLine($nextEls); - $nextEls.remove(); - } - $el.remove(); - } - return true; - }, - fold: function(e) { - this.removeLine($(e.target).parents('tr')); - var active_id = $(e.target).parents('tr').find('td.o_stock_reports_foldable').data('id'); - $(e.target).parents('tr').find('td.o_stock_reports_foldable').attr('class', 'o_stock_reports_unfoldable ' + active_id); // Change the class, rendering, and remove line from model - $(e.target).parents('tr').find('span.o_stock_reports_foldable').replaceWith(QWeb.render("unfoldable", {lineId: active_id})); - $(e.target).parents('tr').toggleClass('o_stock_reports_unfolded'); - }, - autounfold: function(target, lot_name) { - var self = this; - var $CurretElement; - $CurretElement = $(target).parents('tr').find('td.o_stock_reports_unfoldable'); - var active_id = $CurretElement.data('id'); - var active_model_name = $CurretElement.data('model'); - var active_model_id = $CurretElement.data('model_id'); - var row_level = $CurretElement.data('level'); - var $cursor = $(target).parents('tr'); - this._rpc({ - model: 'stock.traceability.report', - method: 'get_lines', - args: [parseInt(active_id, 10)], - kwargs: { - 'model_id': active_model_id, - 'model_name': active_model_name, - 'level': parseInt(row_level) + 30 || 1 - }, - }) - .then(function (lines) {// After loading the line - _.each(lines, function (line) { // Render each line - $cursor.after(QWeb.render("report_mrp_line", {l: line})); - $cursor = $cursor.next(); - if ($cursor && line.unfoldable && line.lot_name == lot_name) { - self.autounfold($cursor.find(".fa-caret-right"), lot_name); - } - }); - }); - $CurretElement.attr('class', 'o_stock_reports_foldable ' + active_id); // Change the class, and rendering of the unfolded line - $(target).parents('tr').find('span.o_stock_reports_unfoldable').replaceWith(QWeb.render("foldable", {lineId: active_id})); - $(target).parents('tr').toggleClass('o_stock_reports_unfolded'); - }, - unfold: function(e) { - this.autounfold($(e.target)); - }, - -}); - -return ReportWidget; - -}); diff --git a/addons/stock/static/src/xml/stock_traceability_report_backend.xml b/addons/stock/static/src/xml/stock_traceability_report_backend.xml deleted file mode 100644 index e0a5c606bbe..00000000000 --- a/addons/stock/static/src/xml/stock_traceability_report_backend.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - diff --git a/addons/stock/static/src/xml/stock_traceability_report_line.xml b/addons/stock/static/src/xml/stock_traceability_report_line.xml deleted file mode 100644 index 11504054a37..00000000000 --- a/addons/stock/static/src/xml/stock_traceability_report_line.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - diff --git a/addons/stock/static/tests/stock_traceability_report_backend_tests.js b/addons/stock/static/tests/stock_traceability_report_backend_tests.js index def4bb68295..2410db256f2 100644 --- a/addons/stock/static/tests/stock_traceability_report_backend_tests.js +++ b/addons/stock/static/tests/stock_traceability_report_backend_tests.js @@ -1,174 +1,47 @@ -odoo.define('stock.stock_traceability_report_backend_tests', function (require) { - "use strict"; +/** @odoo-module **/ - const LegacyControlPanel = require('web.ControlPanel'); - const { ControlPanel } = require("@web/search/control_panel/control_panel"); - const dom = require('web.dom'); - const StockReportGeneric = require('stock.stock_report_generic'); - const testUtils = require('web.test_utils'); +import { getFixture } from "@web/../tests/helpers/utils"; +import { createWebClient, doAction } from "@web/../tests/webclient/helpers"; - const { dom: domUtils } = testUtils; - const { - destroy, - getFixture, - legacyExtraNextTick, - patchWithCleanup, - } = require("@web/../tests/helpers/utils"); - const { createWebClient, doAction } = require('@web/../tests/webclient/helpers'); +let serverData; +let target; - const { onMounted, onWillUnmount } = owl; - - /** - * Helper function to instantiate a stock report action. - * @param {Object} params - * @param {Object} params.action - * @param {boolean} [params.debug] - * @returns {Promise} - */ - async function createStockReportAction(params) { - const parent = await testUtils.createParent(params); - const report = new StockReportGeneric(parent, params.action); - const target = testUtils.prepareTarget(params.debug); - - const _destroy = report.destroy; - report.destroy = function () { - report.destroy = _destroy; - parent.destroy(); - }; - const fragment = document.createDocumentFragment(); - await report.appendTo(fragment); - dom.prepend(target, fragment, { - callbacks: [{ widget: report }], - in_DOM: true, - }); - // Wait for the ReportWidget to be appended - await testUtils.nextTick(); - - return report; - } - - QUnit.module('Stock', {}, function () { - QUnit.module('Traceability report'); - - QUnit.test("Rendering with no lines", async function (assert) { - assert.expect(1); - - const template = ` -
-
- -

No operation made on this lot.

-
-
-
`; - const report = await createStockReportAction({ - action: { +QUnit.module("Stock", {}, function (hooks) { + hooks.beforeEach(() => { + serverData = { + actions: { + 42: { + id: 42, + name: "Stock report", + tag: "stock_report_generic", + type: "ir.actions.client", context: {}, params: {}, }, - data: { - 'stock.traceability.report': { - fields: {}, - get_html: () => ({ html: template }), - }, - }, - }); + }, + }; - // HTML content is nested in a div inside of the content - assert.strictEqual(report.el.querySelector('.o_content > div').innerHTML, template, - "Displayed template should match"); + target = getFixture(); + }); - report.destroy(); + QUnit.module("Traceability report"); + + QUnit.test("Rendering with no lines", async function (assert) { + assert.expect(1); + + const webClient = await createWebClient({ + serverData, + mockRPC: async function (route) { + if (route === "/web/dataset/call_kw/stock.traceability.report/get_main_lines") { + return []; + } + }, }); - QUnit.test("mounted is called once when returning on 'Stock report' from breadcrumb", async assert => { - // This test can be removed as soon as we don't mix legacy and owl layers anymore. - assert.expect(7); - - let mountCount = 0; - - patchWithCleanup(ControlPanel.prototype, { - setup() { - this._super(); - onMounted(() => { - mountCount = mountCount + 1; - this.__uniqueId = mountCount; - assert.step(`mounted ${this.__uniqueId}`); - }); - onWillUnmount(() => { - assert.step(`willUnmount ${this.__uniqueId}`); - }); - }, - }); - - patchWithCleanup(LegacyControlPanel.prototype, { - setup() { - this._super(); - onMounted(() => { - mountCount = mountCount + 1; - this.__uniqueId = mountCount; - assert.step(`mounted ${this.__uniqueId} (legacy)`); - }); - onWillUnmount(() => { - assert.step(`willUnmount ${this.__uniqueId} (legacy)`); - }); - }, - }); - const serverData = { - models: { - partner: { - fields: { - display_name: { string: "Displayed name", type: "char" }, - }, - records: [ - {id: 1, display_name: "Genda Swami"}, - ], - }, - }, - views: { - 'partner,false,form': '
', - 'partner,false,search': '', - }, - actions: { - 42: { - id: 42, - name: "Stock report", - tag: 'stock_report_generic', - type: 'ir.actions.client', - context: {}, - params: {}, - }, - }, - }; - - const target = getFixture(); - const webClient = await createWebClient({ - serverData, - mockRPC: function (route) { - if (route === '/web/dataset/call_kw/stock.traceability.report/get_html') { - return Promise.resolve({ - html: 'Go to form view', - }); - } - }, - }); - - await doAction(webClient, 42); - await domUtils.click(target.querySelector('.o_stock_reports_web_action')); - await legacyExtraNextTick(); - await domUtils.click(target.querySelector('.breadcrumb-item')); - await legacyExtraNextTick(); - - destroy(webClient); - - assert.verifySteps([ - 'mounted 1 (legacy)', - 'willUnmount 1 (legacy)', - 'mounted 2', - 'willUnmount 2', - 'mounted 3 (legacy)', - 'willUnmount 3 (legacy)', - ]); - }); + await doAction(webClient, 42); + assert.strictEqual( + target.querySelector(".o_stock_reports_page").textContent, + "No operation made on this lot." + ); }); }); diff --git a/addons/stock/views/report_stock_traceability.xml b/addons/stock/views/report_stock_traceability.xml index d60047d5f37..46bb46e368a 100644 --- a/addons/stock/views/report_stock_traceability.xml +++ b/addons/stock/views/report_stock_traceability.xml @@ -1,80 +1,6 @@ - - - -