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
+
+
+
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 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
Traceability Report
-
-
-
- No operation made on this lot.
-
-
-
-
-
-
-
-