[IMP] purchase(_*): convert order view to OWL
Task-2918791 X-original-commit: b2457fbc45aa8a2714c92ea48333fa8108acf405 Part-of: odoo/odoo#101352
This commit is contained in:
@@ -132,7 +132,7 @@
|
||||
<field name="name">purchase.order.form</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Purchase Order" class="o_purchase_order" js_class="legacy_form">
|
||||
<form string="Purchase Order" class="o_purchase_order">
|
||||
<header>
|
||||
<button name="action_rfq_send" states="draft" string="Send by Email" type="object" context="{'send_rfq':True}" class="oe_highlight" data-hotkey="g"/>
|
||||
<button name="print_quotation" string="Print RFQ" type="object" states="draft" class="oe_highlight" groups="base.group_user" data-hotkey="k"/>
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
'web.assets_backend': [
|
||||
'purchase_product_matrix/static/src/**/*',
|
||||
],
|
||||
'web.qunit_suite_tests': [
|
||||
'purchase_product_matrix/static/tests/section_and_note_widget_tests.js',
|
||||
],
|
||||
'web.assets_tests': [
|
||||
'purchase_product_matrix/static/tests/tours/**/*',
|
||||
],
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
odoo.define('purchase.product_matrix_configurator', function (require) {
|
||||
|
||||
var relationalFields = require('web.relational_fields');
|
||||
var FieldsRegistry = require('web.field_registry');
|
||||
var core = require('web.core');
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
* The purchase.product_matrix_configurator widget is a widget extending FieldMany2One
|
||||
* It triggers the opening of the matrix edition when the product has multiple variants.
|
||||
*
|
||||
*
|
||||
* !!! WARNING !!!
|
||||
*
|
||||
* This widget is only designed for Purchase Order Lines.
|
||||
* !!! It should only be used on a product_template field !!!
|
||||
*/
|
||||
var MatrixConfiguratorWidget = relationalFields.FieldMany2One.extend({
|
||||
events: _.extend({}, relationalFields.FieldMany2One.prototype.events, {
|
||||
'click .o_edit_product_configuration': '_onEditProductConfiguration'
|
||||
}),
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_render: function () {
|
||||
this._super.apply(this, arguments);
|
||||
if (this.mode === 'edit' && this.value &&
|
||||
(this._isConfigurableProduct())) {
|
||||
this._addProductLinkButton();
|
||||
this._addConfigurationEditButton();
|
||||
} else if (this.mode === 'edit' && this.value) {
|
||||
this._addProductLinkButton();
|
||||
} else {
|
||||
this.$('.o_edit_product_configuration').hide();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add button linking to product_id/product_template_id form.
|
||||
*/
|
||||
_addProductLinkButton: function () {
|
||||
if (this.$('.o_external_button').length === 0) {
|
||||
var $productLinkButton = $('<button>', {
|
||||
type: 'button',
|
||||
class: 'fa fa-external-link btn btn-secondary o_external_button',
|
||||
tabindex: '-1',
|
||||
draggable: false,
|
||||
'aria-label': _t('External Link'),
|
||||
title: _t('External Link')
|
||||
});
|
||||
|
||||
var $inputDropdown = this.$('.o_input_dropdown');
|
||||
$inputDropdown.after($productLinkButton);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If current product is configurable,
|
||||
* Show edit button (in Edit Mode) after the product/product_template
|
||||
*/
|
||||
_addConfigurationEditButton: function () {
|
||||
var $inputDropdown = this.$('.o_input_dropdown');
|
||||
|
||||
if ($inputDropdown.length !== 0 &&
|
||||
this.$('.o_edit_product_configuration').length === 0) {
|
||||
var $editConfigurationButton = $('<button>', {
|
||||
type: 'button',
|
||||
class: 'fa fa-pencil btn btn-secondary o_edit_product_configuration',
|
||||
tabindex: '-1',
|
||||
draggable: false,
|
||||
'aria-label': _t('Edit Configuration'),
|
||||
title: _t('Edit Configuration')
|
||||
});
|
||||
|
||||
$inputDropdown.after($editConfigurationButton);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hook to override with _onEditProductConfiguration
|
||||
* to know if edit pencil button has to be put next to the field
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_isConfigurableProduct: function () {
|
||||
return this.recordData.is_configurable_product;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override catching changes on product_id or product_template_id.
|
||||
* Calls _onTemplateChange in case of product_template change.
|
||||
* Calls _onProductChange in case of product change.
|
||||
* Shouldn't be overridden by product configurators
|
||||
* or only to setup some data for further computation
|
||||
* before calling super.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
reset: async function (record, ev) {
|
||||
await this._super(...arguments);
|
||||
if (ev && ev.target === this && ev.data.changes && ev.data.changes.product_template_id && record.data.product_template_id.data.id) {
|
||||
this._onTemplateChange(record.data.product_template_id.data.id, ev.data.dataPointID);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hook for product_template based configurators
|
||||
* (product configurator, matrix, ...).
|
||||
*
|
||||
* @param {integer} productTemplateId
|
||||
* @param {String} dataPointID
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onTemplateChange: function (productTemplateId, dataPointId) {
|
||||
var self = this;
|
||||
this._rpc({
|
||||
model: 'product.template',
|
||||
method: 'get_single_product_variant',
|
||||
args: [
|
||||
productTemplateId
|
||||
]
|
||||
}).then(function (result) {
|
||||
if (result.product_id) {
|
||||
self.trigger_up('field_changed', {
|
||||
dataPointID: dataPointId,
|
||||
changes: {
|
||||
product_id: {id: result.product_id},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
self._openMatrix(productTemplateId, dataPointId, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Hook for editing a configured line.
|
||||
* The button triggering this function is only shown in Edit mode,
|
||||
* when _isConfigurableProduct is True.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onEditProductConfiguration: function () {
|
||||
if (this.recordData.is_configurable_product) {
|
||||
this._openMatrix(this.recordData.product_template_id.data.id, this.dataPointID, true);
|
||||
}
|
||||
},
|
||||
|
||||
_openMatrix: function (productTemplateId, dataPointId, edit) {
|
||||
var attribs = edit ? this._getPTAVS() : [];
|
||||
this.trigger_up('open_matrix', {
|
||||
product_template_id: productTemplateId,
|
||||
model: 'purchase.order',
|
||||
dataPointId: dataPointId,
|
||||
edit: edit,
|
||||
editedCellAttributes: attribs,
|
||||
// used to focus the cell representing the line on which the pencil was clicked.
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the list of attribute ids (product.template.attribute.value)
|
||||
* from the current POLine.
|
||||
*/
|
||||
_getPTAVS: function () {
|
||||
var PTAVSIDS = [];
|
||||
_.each(this.recordData.product_no_variant_attribute_value_ids.res_ids, function (id) {
|
||||
PTAVSIDS.push(id);
|
||||
});
|
||||
_.each(this.recordData.product_template_attribute_value_ids.res_ids, function (id) {
|
||||
PTAVSIDS.push(id);
|
||||
});
|
||||
return PTAVSIDS.sort(function (a, b) {return a - b;});
|
||||
}
|
||||
});
|
||||
|
||||
FieldsRegistry.add('matrix_configurator', MatrixConfiguratorWidget);
|
||||
|
||||
return MatrixConfiguratorWidget;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import Dialog from 'web.Dialog';
|
||||
import { qweb } from "web.core";
|
||||
import { registry } from '@web/core/registry';
|
||||
import { Many2OneField } from '@web/views/fields/many2one/many2one_field';
|
||||
import { formatMonetary } from "@web/views/fields/formatters";
|
||||
|
||||
const { onWillUpdateProps } = owl;
|
||||
|
||||
|
||||
export class PurchaseOrderLineProductField extends Many2OneField {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
onWillUpdateProps(async (nextProps) => {
|
||||
if (nextProps.record.mode === 'edit' && nextProps.value) {
|
||||
if (
|
||||
!this.props.value ||
|
||||
this.props.value[0] != nextProps.value[0]
|
||||
) {
|
||||
// Field was updated if line was open in edit mode,
|
||||
// field is not emptied,
|
||||
// new value is different than existing value.
|
||||
|
||||
this._onProductTemplateUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get configurationButtonHelp() {
|
||||
return this.env._t("Edit Configuration");
|
||||
}
|
||||
get isConfigurableTemplate() {
|
||||
return this.props.record.data.is_configurable_product;
|
||||
}
|
||||
|
||||
async _onProductTemplateUpdate() {
|
||||
const result = await this.orm.call(
|
||||
'product.template',
|
||||
'get_single_product_variant',
|
||||
[this.props.record.data.product_template_id[0]],
|
||||
);
|
||||
if(result && result.product_id) {
|
||||
if (this.props.record.data.product_id != result.product_id.id) {
|
||||
this.props.record.update({
|
||||
// TODO right name get (same problem as configurator)
|
||||
product_id: [result.product_id, 'whatever'],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this._openGridConfigurator(false);
|
||||
}
|
||||
}
|
||||
|
||||
onEditConfiguration() {
|
||||
if (this.props.record.data.is_configurable_product) {
|
||||
this._openGridConfigurator(true);
|
||||
}
|
||||
}
|
||||
|
||||
async _openGridConfigurator(edit) {
|
||||
const PurchaseOrderRecord = this.props.record.model.root;
|
||||
|
||||
// fetch matrix information from server;
|
||||
await PurchaseOrderRecord.update({
|
||||
grid_product_tmpl_id: this.props.record.data.product_template_id,
|
||||
});
|
||||
|
||||
let updatedLineAttributes = [];
|
||||
if (edit) {
|
||||
// provide attributes of edited line to automatically focus on matching cell in the matrix
|
||||
for (let ptnvav of this.props.record.data.product_no_variant_attribute_value_ids.records) {
|
||||
updatedLineAttributes.push(ptnvav.data.id);
|
||||
}
|
||||
for (let ptav of this.props.record.data.product_template_attribute_value_ids.records) {
|
||||
updatedLineAttributes.push(ptav.data.id);
|
||||
}
|
||||
updatedLineAttributes.sort((a, b) => { return a - b; });
|
||||
}
|
||||
|
||||
this._openMatrixConfigurator(
|
||||
PurchaseOrderRecord.data.grid,
|
||||
this.props.record.data.product_template_id[0],
|
||||
updatedLineAttributes,
|
||||
);
|
||||
|
||||
if (!edit) {
|
||||
// remove new line used to open the matrix
|
||||
PurchaseOrderRecord.data.order_line.removeRecord(this.props.record);
|
||||
}
|
||||
}
|
||||
|
||||
_openMatrixConfigurator(jsonInfo, productTemplateId, editedCellAttributes) {
|
||||
const infos = JSON.parse(jsonInfo);
|
||||
const saleOrderRecord = this.props.record.model.root;
|
||||
const MatrixDialog = new Dialog(this, {
|
||||
title: this.env._t('Choose Product Variants'),
|
||||
size: 'extra-large', // adapt size depending on matrix size?
|
||||
$content: $(qweb.render(
|
||||
'product_matrix.matrix', {
|
||||
header: infos.header,
|
||||
rows: infos.matrix,
|
||||
format({price, currency_id}) {
|
||||
if (!price) { return ""; }
|
||||
return formatMonetary(price, {
|
||||
currencyId: currency_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
)),
|
||||
buttons: [
|
||||
{text: this.env._t('Confirm'), classes: 'btn-primary', close: true, click: function (result) {
|
||||
const $inputs = this.$('.o_matrix_input');
|
||||
var matrixChanges = [];
|
||||
_.each($inputs, function (matrixInput) {
|
||||
if (matrixInput.value && matrixInput.value !== matrixInput.attributes.value.nodeValue) {
|
||||
matrixChanges.push({
|
||||
qty: parseFloat(matrixInput.value),
|
||||
ptav_ids: matrixInput.attributes.ptav_ids.nodeValue.split(",").map(function (id) {
|
||||
return parseInt(id);
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
if (matrixChanges.length > 0) {
|
||||
// NB: server also removes current line opening the matrix
|
||||
saleOrderRecord.update({
|
||||
grid: JSON.stringify({changes: matrixChanges, product_template_id: productTemplateId}),
|
||||
grid_update: true // to say that the changes to grid have to be applied to the SO.
|
||||
});
|
||||
}
|
||||
}},
|
||||
{text: this.env._t('Close'), close: true},
|
||||
],
|
||||
}).open();
|
||||
|
||||
MatrixDialog.opened(function () {
|
||||
MatrixDialog.$content.closest('.o_dialog_container').removeClass('d-none');
|
||||
if (editedCellAttributes.length > 0) {
|
||||
const str = editedCellAttributes.toString();
|
||||
MatrixDialog.$content.find('.o_matrix_input').filter((k, v) => v.attributes.ptav_ids.nodeValue === str)[0].focus();
|
||||
} else {
|
||||
MatrixDialog.$content.find('.o_matrix_input:first()').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PurchaseOrderLineProductField.template = "purchase.PurchaseProductField";
|
||||
|
||||
registry.category("fields").add("pol_product_many2one", PurchaseOrderLineProductField);
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="purchase.PurchaseProductField" t-inherit="web.Many2OneField" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//t[@t-if='hasExternalButton']" position="before">
|
||||
<t t-if="isConfigurableTemplate">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary fa fa-pencil"
|
||||
tabindex="-1"
|
||||
draggable="false"
|
||||
t-att-aria-label="configurationButtonHelp"
|
||||
t-att-data-tooltip="configurationButtonHelp"
|
||||
t-on-click="onEditConfiguration"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,260 +0,0 @@
|
||||
odoo.define('purchase_product_matrix.section_and_note_widget_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var FormView = require('web.FormView');
|
||||
var testUtils = require('web.test_utils');
|
||||
var createView = testUtils.createView;
|
||||
|
||||
function getGrid(product) {
|
||||
return JSON.stringify({
|
||||
header: [{name: product.name}, {name: "M"}, {name: "L"}],
|
||||
matrix: [[
|
||||
{name: "Men"},
|
||||
{ptav_ids: [10, 13], qty: 0, is_possible_combination: true},
|
||||
{ptav_ids: [11, 13], qty: 0, is_possible_combination: true},
|
||||
], [
|
||||
{name: "Women"},
|
||||
{ptav_ids: [10, 14], qty: 0, is_possible_combination: true},
|
||||
{ptav_ids: [11, 14], qty: 0, is_possible_combination: true},
|
||||
]],
|
||||
});
|
||||
}
|
||||
|
||||
QUnit.module('section_and_note: purchase_product_matrix', {
|
||||
beforeEach: function () {
|
||||
this.data = {
|
||||
purchase_order: {
|
||||
fields: {
|
||||
order_line_ids: {
|
||||
string: "Lines",
|
||||
type: 'one2many',
|
||||
relation: 'order_line',
|
||||
relation_field: 'order_id',
|
||||
},
|
||||
grid: {string: "Grid", type: 'char'},
|
||||
grid_product_tmpl_id: {string: "Grid Product", type: 'many2one', relation: 'product'},
|
||||
},
|
||||
onchanges: {
|
||||
grid_product_tmpl_id: (obj) => {
|
||||
const product = this.data.product.records.find((p) => {
|
||||
return p.id === obj.grid_product_tmpl_id;
|
||||
});
|
||||
obj.grid = product ? getGrid(product) : false;
|
||||
},
|
||||
grid: () => {},
|
||||
},
|
||||
},
|
||||
order_line: {
|
||||
fields: {
|
||||
order_id: {string: "Invoice", type: 'many2one', relation: 'invoice'},
|
||||
product_template_id: {string: "Product", type: 'many2one', relation: 'product'},
|
||||
},
|
||||
},
|
||||
product: {
|
||||
fields: {
|
||||
name: {string: "Name", type: 'char'},
|
||||
},
|
||||
records: [
|
||||
{id: 1, name: 'A configurable product'},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
}, function () {
|
||||
QUnit.test('can configure a product with the matrix', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
var form = await createView({
|
||||
View: FormView,
|
||||
model: 'purchase_order',
|
||||
data: this.data,
|
||||
arch: `<form>
|
||||
<field name="grid" invisible="1"/>
|
||||
<field name="grid_product_tmpl_id" invisible="1"/>
|
||||
<field name="order_line_ids" widget="section_and_note_one2many">
|
||||
<tree editable="bottom">
|
||||
<field name="product_template_id" widget="matrix_configurator"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'onchange' && args.args[2] === 'grid') {
|
||||
// should trigger an onchange on the grid field and let the
|
||||
// business logic create rows according to the matrix content
|
||||
assert.deepEqual(args.args[1].grid, JSON.stringify({
|
||||
changes: [{qty: 2, ptav_ids: [10, 13]}, {qty: 3, ptav_ids: [11, 14]}],
|
||||
product_template_id: 1,
|
||||
}));
|
||||
}
|
||||
if (args.method === 'get_single_product_variant') {
|
||||
assert.strictEqual(args.args[0], 1);
|
||||
return Promise.resolve({mode: 'matrix'});
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
await testUtils.dom.click('.o_field_x2many_list_row_add a');
|
||||
await testUtils.fields.many2one.searchAndClickItem("product_template_id", {item: 'configurable'});
|
||||
|
||||
assert.containsOnce(document.body, '.modal .o_product_variant_matrix');
|
||||
const $matrix = $('.modal .o_product_variant_matrix');
|
||||
assert.strictEqual($matrix.text().replace(/[\n\r\s\u00a0]+/g, ' '),
|
||||
' A configurable product M L Men Women ');
|
||||
|
||||
// select 2 M-Men and 3 L-Women
|
||||
await testUtils.fields.editInput($matrix.find('.o_matrix_input[ptav_ids="10,13"]'), '2');
|
||||
await testUtils.fields.editInput($matrix.find('.o_matrix_input[ptav_ids="11,14"]'), '3');
|
||||
await testUtils.dom.click($('.modal .modal-footer .btn-primary'));
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('can open the matrix twice with 2 different products', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
this.data.product.records.push({ id: 101, name: "Product A" });
|
||||
this.data.product.records.push({ id: 102, name: "Product B" });
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'purchase_order',
|
||||
data: this.data,
|
||||
arch: `<form>
|
||||
<field name="grid" invisible="1"/>
|
||||
<field name="grid_product_tmpl_id" invisible="1"/>
|
||||
<field name="order_line_ids" widget="section_and_note_one2many">
|
||||
<tree editable="bottom">
|
||||
<field name="product_template_id" widget="matrix_configurator"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'onchange' && args.args[2] === 'grid') {
|
||||
// should trigger an onchange on the grid field and let the
|
||||
// business logic create rows according to the matrix content
|
||||
assert.deepEqual(args.args[1].grid, JSON.stringify({
|
||||
changes: [{qty: 2, ptav_ids: [10, 13]}, {qty: 3, ptav_ids: [11, 14]}],
|
||||
product_template_id: 102,
|
||||
}));
|
||||
}
|
||||
if (args.method === 'get_single_product_variant') {
|
||||
return Promise.resolve({mode: 'matrix'});
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
// open the matrix with "Product A" and close it
|
||||
await testUtils.dom.click('.o_field_x2many_list_row_add a');
|
||||
await testUtils.fields.many2one.searchAndClickItem("product_template_id", {item: 'Product A'});
|
||||
|
||||
assert.containsOnce(document.body, '.modal .o_product_variant_matrix');
|
||||
let $matrix = $('.modal .o_product_variant_matrix');
|
||||
assert.strictEqual($matrix.text().replace(/[\n\r\s\u00a0]+/g, ' '),
|
||||
' Product A M L Men Women ');
|
||||
|
||||
await testUtils.dom.click($('.modal .modal-footer .btn-secondary')); // close
|
||||
|
||||
// re-open the matrix with "Product B"
|
||||
await testUtils.dom.click('.o_field_x2many_list_row_add a');
|
||||
await testUtils.fields.many2one.searchAndClickItem("product_template_id", {item: 'Product B'});
|
||||
|
||||
assert.containsOnce(document.body, '.modal .o_product_variant_matrix');
|
||||
$matrix = $('.modal .o_product_variant_matrix');
|
||||
assert.strictEqual($matrix.text().replace(/[\n\r\s\u00a0]+/g, ' '),
|
||||
' Product B M L Men Women ');
|
||||
|
||||
// select 2 M-Men and 3 L-Women
|
||||
await testUtils.fields.editInput($matrix.find('.o_matrix_input[ptav_ids="10,13"]'), '2');
|
||||
await testUtils.fields.editInput($matrix.find('.o_matrix_input[ptav_ids="11,14"]'), '3');
|
||||
await testUtils.dom.click($('.modal .modal-footer .btn-primary'));
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('_onTemplateChange is executed after product template quick create', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
let created_product_template;
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'purchase_order',
|
||||
data: this.data,
|
||||
arch: `<form>
|
||||
<field name="order_line_ids" widget="section_and_note_one2many">
|
||||
<tree editable="bottom">
|
||||
<field name="product_template_id" widget="matrix_configurator"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
async mockRPC(route, args) {
|
||||
if (route === '/web/dataset/call_kw/product.template/get_single_product_variant') {
|
||||
assert.strictEqual(args.args[0], created_product_template[0]);
|
||||
}
|
||||
|
||||
const result = await this._super(...arguments);
|
||||
if (args.method === 'name_create') {
|
||||
created_product_template = result;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
});
|
||||
|
||||
await testUtils.dom.click('.o_field_x2many_list_row_add a');
|
||||
await testUtils.fields.many2one.searchAndClickItem("product_template_id", {search: 'new product'});
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('drag and drop rows containing matrix_configurator many2one', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
this.data.order_line.fields.sequence = {string: "Sequence", type: 'number'};
|
||||
this.data.order_line.fields.order_id.relation = 'purchase_order';
|
||||
this.data.purchase_order.records = [
|
||||
{id: 1, order_line_ids: [1, 2]}
|
||||
];
|
||||
this.data.order_line.records = [
|
||||
{id: 1, sequence: 4, product_template_id: 1, order_id: 1},
|
||||
{id: 2, sequence: 14, product_template_id: 2, order_id: 1},
|
||||
];
|
||||
this.data.product.records.push(
|
||||
{id: 1, name: "Chair"},
|
||||
{id: 2, name: "Table"}
|
||||
);
|
||||
|
||||
const form = await createView({
|
||||
View: FormView,
|
||||
model: 'purchase_order',
|
||||
data: this.data,
|
||||
arch: `<form>
|
||||
<field name="order_line_ids" widget="section_and_note_one2many">
|
||||
<tree editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_template_id" widget="matrix_configurator"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
res_id: 1,
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
||||
assert.containsN(form, '.o_data_row', 2);
|
||||
assert.strictEqual(form.$('.o_data_row').text(), 'ChairTable');
|
||||
assert.containsN(form, '.o_data_row .o_row_handle', 2);
|
||||
|
||||
// move first row below second
|
||||
const $firstHandle = form.$('.o_data_row:nth(0) .o_row_handle');
|
||||
const $secondHandle = form.$('.o_data_row:nth(1) .o_row_handle');
|
||||
await testUtils.dom.dragAndDrop($firstHandle, $secondHandle, { position: 'bottom' });
|
||||
|
||||
assert.strictEqual(form.$('.o_data_row').text(), 'TableChair');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,21 +11,19 @@ tour.register('purchase_matrix_tour', {
|
||||
}, {
|
||||
trigger: ".o_list_button_add",
|
||||
extra_trigger: ".o_purchase_order"
|
||||
}, {
|
||||
trigger: '.o_required_modifier[name=partner_id] input',
|
||||
run: 'text Agrolait',
|
||||
}, {
|
||||
trigger: '.ui-menu-item > a:contains("Agrolait")',
|
||||
auto: true,
|
||||
}, {
|
||||
trigger: "a:contains('Add a product')"
|
||||
}, {
|
||||
trigger: 'div[name="product_template_id"] input',
|
||||
run: function () {
|
||||
var $input = $('div[name="product_template_id"] input');
|
||||
$input.click();
|
||||
$input.val('Matrix');
|
||||
var keyDownEvent = jQuery.Event("keydown");
|
||||
keyDownEvent.which = 42;
|
||||
$input.trigger(keyDownEvent);
|
||||
}
|
||||
run: "text Matrix",
|
||||
}, {
|
||||
trigger: 'ul.ui-autocomplete a:contains("Matrix")',
|
||||
run: 'click'
|
||||
}, {
|
||||
trigger: '.o_product_variant_matrix',
|
||||
run: function () {
|
||||
@@ -35,29 +33,16 @@ tour.register('purchase_matrix_tour', {
|
||||
}, {
|
||||
trigger: 'span:contains("Confirm")',
|
||||
run: 'click'
|
||||
}, {
|
||||
trigger: ".o_form_editable .o_field_many2one[name='partner_id'] input",
|
||||
extra_trigger: ".o_purchase_order",
|
||||
run: 'text Agrolait'
|
||||
}, {
|
||||
trigger: ".ui-menu-item > a",
|
||||
auto: true,
|
||||
in_modal: false,
|
||||
}, {
|
||||
trigger: '.o_form_button_save:contains("Save")',
|
||||
run: 'click' // SAVE Sales Order.
|
||||
},
|
||||
// Open the matrix through the pencil button next to the product in line edit mode.
|
||||
{
|
||||
trigger: '.o_form_button_edit:contains("Edit")',
|
||||
run: 'click' // Edit Sales Order.
|
||||
}, {
|
||||
trigger: 'span:contains("Matrix (PAV11, PAV22, PAV31)\nPA4: PAV41")',
|
||||
extra_trigger: '.o_form_editable',
|
||||
run: 'click'
|
||||
extra_trigger: '.o_form_status_indicator_buttons_hidden', // wait for save to be finished
|
||||
}, {
|
||||
trigger: '.o_edit_product_configuration',
|
||||
run: 'click' // edit the matrix
|
||||
trigger: '[name=product_template_id] button.fa-pencil', // edit the matrix
|
||||
}, {
|
||||
trigger: '.o_product_variant_matrix',
|
||||
run: function () {
|
||||
@@ -71,29 +56,18 @@ tour.register('purchase_matrix_tour', {
|
||||
trigger: '.o_form_button_save:contains("Save")',
|
||||
extra_trigger: '.o_field_cell.o_data_cell.o_list_number:contains("4.00")',
|
||||
run: 'click' // SAVE Sales Order, after matrix has been applied (extra_trigger).
|
||||
}, {
|
||||
trigger: '.o_form_button_edit:contains("Edit")',
|
||||
run: 'click' // Edit Sales Order.
|
||||
},
|
||||
// Ensures the matrix is opened with the values, when adding the same product.
|
||||
{
|
||||
trigger: "a:contains('Add a product')",
|
||||
extra_trigger: '.o_form_editable',
|
||||
trigger: 'a:contains("Add a product")',
|
||||
extra_trigger: '.o_form_status_indicator_buttons_hidden',
|
||||
}, {
|
||||
trigger: 'div[name="product_template_id"] input',
|
||||
run: function () {
|
||||
var $input = $('div[name="product_template_id"] input');
|
||||
$input.click();
|
||||
$input.val('Matrix');
|
||||
var keyDownEvent = jQuery.Event("keydown");
|
||||
keyDownEvent.which = 42;
|
||||
$input.trigger(keyDownEvent);
|
||||
}
|
||||
run: 'text Matrix',
|
||||
}, {
|
||||
trigger: 'ul.ui-autocomplete a:contains("Matrix")',
|
||||
run: 'click'
|
||||
}, {
|
||||
trigger: "input[value='4']",
|
||||
trigger: 'input[value="4"]',
|
||||
run: function () {
|
||||
// update some values of the matrix
|
||||
$("input[value='4']").slice(0, 4).val(8.2);
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- TODO ensure barcode working with matrix (configurator mobile task ?) -->
|
||||
|
||||
<record id="purchase_order_form_matrix" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form.inherit.matrix</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree/field[@name='product_id']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//tree/field[@name='product_id']" position="after">
|
||||
<field name="product_template_id"
|
||||
string="Product"
|
||||
attrs="{
|
||||
'readonly': [('state', 'in', ('purchase', 'to approve','done', 'cancel'))],
|
||||
'required': [('display_type', '=', False)],
|
||||
}"
|
||||
options="{'no_open': True}"
|
||||
context="{'partner_id': parent.partner_id}"
|
||||
widget="matrix_configurator"/>
|
||||
<field name="product_template_attribute_value_ids" invisible="1" />
|
||||
<field name="product_no_variant_attribute_value_ids" invisible="1" />
|
||||
<field name="is_configurable_product" invisible="1" />
|
||||
</xpath>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="grid" invisible="1"/>
|
||||
<field name="grid_product_tmpl_id" invisible="1"/>
|
||||
<field name="grid_update" invisible="1"/>
|
||||
</field>
|
||||
<xpath expr="//group[@name='other_info']" position="inside">
|
||||
<field name="report_grids" groups="base.group_no_one"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="purchase_order_form_matrix" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form.inherit.matrix</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//tree/field[@name='product_id']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//tree/field[@name='product_id']" position="after">
|
||||
<field name="product_template_id"
|
||||
string="Product"
|
||||
attrs="{
|
||||
'readonly': [('state', 'in', ('purchase', 'to approve','done', 'cancel'))],
|
||||
'required': [('display_type', '=', False)],
|
||||
}"
|
||||
options="{'no_open': True}"
|
||||
context="{'partner_id': parent.partner_id}"
|
||||
widget="pol_product_many2one"/>
|
||||
<field name="product_template_attribute_value_ids" invisible="1"/>
|
||||
<field name="product_no_variant_attribute_value_ids" invisible="1"/>
|
||||
<field name="is_configurable_product" invisible="1"/>
|
||||
</xpath>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="grid" invisible="1"/>
|
||||
<field name="grid_product_tmpl_id" invisible="1"/>
|
||||
<field name="grid_update" invisible="1"/>
|
||||
</field>
|
||||
<group name="other_info" position="inside">
|
||||
<field name="report_grids" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user