mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-27 10:52:05 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -0,0 +1,381 @@
|
|||
import { defineMailModels } from '@mail/../tests/mail_test_helpers';
|
||||
import { expect, test } from '@odoo/hoot';
|
||||
import { queryAllTexts } from '@odoo/hoot-dom';
|
||||
import {
|
||||
clickSave,
|
||||
contains,
|
||||
defineModels,
|
||||
fields,
|
||||
mountView,
|
||||
onRpc,
|
||||
} from '@web/../tests/web_test_helpers';
|
||||
import { saleModels } from '@sale/../tests/sale_test_helpers';
|
||||
|
||||
class SaleOrderLine extends saleModels.SaleOrderLine {
|
||||
// for skipping tax setup required for prices computation to run correctly
|
||||
price_unit = fields.Float({ default: 3.00 });
|
||||
price_total = fields.Float({ default: 3.00 });
|
||||
price_subtotal = fields.Float({ default: 3.50 });
|
||||
product_uom_qty = fields.Float({ default: 1.00 });
|
||||
|
||||
_records = [
|
||||
{ id: 1, name: "r1", sequence: 1 },
|
||||
{ id: 2, name: "r2", sequence: 2 },
|
||||
{
|
||||
id: 3,
|
||||
name: "Sec1",
|
||||
sequence: 3,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
price_subtotal: 0,
|
||||
collapse_prices: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Sec2",
|
||||
sequence: 4,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
price_subtotal: 0,
|
||||
collapse_composition: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Sec3",
|
||||
sequence: 5,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
price_subtotal: 0,
|
||||
},
|
||||
{ id: 6, name: "Sec3-r1", sequence: 6 },
|
||||
{ id: 7, name: "Sec3-r2", sequence: 7 },
|
||||
{
|
||||
id: 8,
|
||||
name: "Sec3-sub1",
|
||||
sequence: 8,
|
||||
display_type: 'line_subsection',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
},
|
||||
{ id: 9, name: "Sec3-sub1-r1", sequence: 9 },
|
||||
{
|
||||
id: 10,
|
||||
name: "Sec3-sub2",
|
||||
sequence: 10,
|
||||
display_type: 'line_subsection',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
},
|
||||
{ id: 11, name: "Sec3-sub2-r1", sequence: 11 },
|
||||
{
|
||||
id: 12,
|
||||
name: "Sec4",
|
||||
sequence: 12,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
price_subtotal: 0
|
||||
},
|
||||
{ id: 13, name: "Sec4-r1", sequence: 13 },
|
||||
{
|
||||
id: 14,
|
||||
name: "Sec4-sub1",
|
||||
sequence: 14,
|
||||
display_type: 'line_subsection',
|
||||
product_uom_qty: 0,
|
||||
price_unit: 0,
|
||||
price_total: 0,
|
||||
collapse_composition: true,
|
||||
collapse_prices: true,
|
||||
},
|
||||
{ id: 15, name: "Sec4-sub1-r1", sequence: 15 },
|
||||
];
|
||||
}
|
||||
|
||||
class SaleOrder extends saleModels.SaleOrder {
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Optional Sections Sale order",
|
||||
order_line: SaleOrderLine._records.map(record => record.id),
|
||||
},
|
||||
];
|
||||
_views = {
|
||||
form: `
|
||||
<form>
|
||||
<field
|
||||
name="order_line"
|
||||
widget="sol_o2m"
|
||||
options="{'subsections': True, 'hide_composition': True, 'hide_prices': True}"
|
||||
>
|
||||
<list editable="bottom">
|
||||
<control>
|
||||
<create name="add_line_control" string="Add a line"/>
|
||||
<create name="add_section_control" string="Add a section" context="{'default_display_type': 'line_section'}"/>
|
||||
<create name="add_note_control" string="Add a note" context="{'default_display_type': 'line_note'}"/>
|
||||
</control>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="price_total"/>
|
||||
<field name="price_subtotal"/>
|
||||
<field name="display_type" column_invisible="1"/>
|
||||
<field name="collapse_composition" column_invisible="1"/>
|
||||
<field name="collapse_prices" column_invisible="1"/>
|
||||
<field name="is_optional" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</form>
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
defineModels({ SaleOrderLine, SaleOrder });
|
||||
defineMailModels();
|
||||
|
||||
const EXPECTED_LINE_RECORDS = [
|
||||
"r1",
|
||||
"r2",
|
||||
"Sec1",
|
||||
"Sec2",
|
||||
"Sec3",
|
||||
"Sec3-r1",
|
||||
"Sec3-r2",
|
||||
"Sec3-sub1",
|
||||
"Sec3-sub1-r1",
|
||||
"Sec3-sub2",
|
||||
"Sec3-sub2-r1",
|
||||
"Sec4",
|
||||
"Sec4-r1",
|
||||
"Sec4-sub1",
|
||||
"Sec4-sub1-r1",
|
||||
];
|
||||
|
||||
test("Can't mark section hidden if optional and vice versa", async () => {
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec1) .o_list_section_options button').click();
|
||||
expect('.o-dropdown-item:contains(Set Optional)').toHaveClass('disabled', {
|
||||
message: "Section with hidden prices can't be optional"
|
||||
});
|
||||
|
||||
await contains('.o_data_row:contains(Sec2) .o_list_section_options button').click();
|
||||
expect('.o-dropdown-item:contains(Set Optional)').toHaveClass('disabled', {
|
||||
message: "Hidden section can't be optional"
|
||||
});
|
||||
})
|
||||
|
||||
test("Setting section optional should reset some fields", async () => {
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
expect(args[1]).toEqual(
|
||||
{
|
||||
order_line: [
|
||||
[1, 10, { collapse_composition: false, collapse_prices: false }],
|
||||
[1, 8, { collapse_composition: false, collapse_prices: false }],
|
||||
[1, 5, { is_optional: true }],
|
||||
[1, 6, { product_uom_qty: 0, price_total: 0, price_subtotal: 0 }],
|
||||
[1, 7, { product_uom_qty: 0, price_total: 0, price_subtotal: 0 }],
|
||||
[1, 9, { product_uom_qty: 0, price_total: 0, price_subtotal: 0 }],
|
||||
[1, 11, { product_uom_qty: 0, price_total: 0, price_subtotal: 0 }],
|
||||
],
|
||||
},
|
||||
{ message: "Subsections reset collapse_* fields' value and product lines reset qty/price when section becomes optional" }
|
||||
);
|
||||
});
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec3-sub2) .o_list_section_options button').click();
|
||||
await contains('.o-dropdown-item:contains(Hide Composition)').click();
|
||||
|
||||
await contains('.o_data_row:contains(Sec3-sub1) .o_list_section_options button').click();
|
||||
await contains('.o-dropdown-item:contains(Hide Prices)').click();
|
||||
|
||||
await contains('.o_data_row:contains(Sec3) .o_list_section_options button').click();
|
||||
await contains('.o-dropdown-item:contains(Set Optional)').click();
|
||||
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
||||
test("Unsetting optional section should reset some fields", async () => {
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3').is_optional = true;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-r1').product_uom_qty = 0;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-r2').product_uom_qty = 0;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-sub1-r1').product_uom_qty = 0;
|
||||
// This line should not be reset
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-sub2-r1').product_uom_qty = 5;
|
||||
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
expect(args[1]).toEqual(
|
||||
{
|
||||
order_line: [
|
||||
[1, 5, { is_optional: false }],
|
||||
[1, 6, { product_uom_qty: 1 }],
|
||||
[1, 7, { product_uom_qty: 1 }],
|
||||
[1, 9, { product_uom_qty: 1 }],
|
||||
[1, 11, { product_uom_qty: 5 }],
|
||||
],
|
||||
},
|
||||
{ message: "The subsections should reset products lines with 0 quantity with 1 as soon as section becomes non optional" }
|
||||
);
|
||||
});
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
expect('.o_data_row:contains(Sec3-r1)').toHaveClass('text-primary', {
|
||||
message: "Line under optional section should be text-primary"
|
||||
});
|
||||
expect('.o_data_row:contains(Sec3-sub1)').toHaveClass('text-primary', {
|
||||
message: "Subsection under optional section should be text-primary"
|
||||
});
|
||||
expect('.o_data_row:contains(Sec3-sub1-r1)').toHaveClass('text-primary', {
|
||||
message: "Line under subsection(which is under optional section) should be text-primary"
|
||||
});
|
||||
|
||||
await contains('.o_data_row:contains(Sec3) .o_list_section_options button').click();
|
||||
await contains('.o-dropdown-item:contains(Unset Optional)').click();
|
||||
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
||||
test("drag and drop regular line inside optional section resets some fields", async () => {
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3').is_optional = true;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-sub2-r1').product_uom_qty = 0;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-sub1-r1').product_uom_qty = 1;
|
||||
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
|
||||
expect(args[1].order_line.find(commands => commands[1] === 13)[2].product_uom_qty).toEqual( // Sec4-r1
|
||||
0,
|
||||
{ message: "Drag and drop inside optional section should reset product_uom_qty to 0" },
|
||||
);
|
||||
expect(args[1].order_line.find(commands => commands[1] === 11)[2].product_uom_qty).toEqual( // Sec3-sub2-r1
|
||||
1,
|
||||
{ message: "Drag and drop line with 0 quantity outside optional section should reset product_uom_qty to 1" },
|
||||
);
|
||||
expect(args[1].order_line.find(commands => commands[1] === 9)?.[2].product_uom_qty).toEqual( // Sec3-sub1-r1
|
||||
undefined,
|
||||
{ message: "Drag and drop line with non-zero quantity outside optional section shouldn't reset product_uom_qty" }
|
||||
);
|
||||
})
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec4-r1):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec3-sub2):first');
|
||||
await contains('.o_data_row:contains(Sec3-sub2-r1):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec4-sub1):first');
|
||||
await contains('.o_data_row:contains(Sec3-sub1-r1):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec4-sub1):first');
|
||||
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
||||
test("Moving Optional Sections to include some lines should set quantity to 0", async () => {
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec4').is_optional = true;
|
||||
// keep sec4-r1's quantity 1 so that we can check that it doesn't reset
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec4-sub1-r1').product_uom_qty = 0;
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
|
||||
expect(args[1].order_line.find(commands => commands[1] === 7)[2].product_uom_qty).toEqual( // Sec3-r2
|
||||
0,
|
||||
{ message: "New lines added to an optional section should have product_uom_qty set to 0" },
|
||||
);
|
||||
expect(args[1].order_line.find(commands => commands[1] === 9)[2].product_uom_qty).toEqual( // Sec3-sub1-r1
|
||||
0,
|
||||
{ message: "New lines added to a subsection of an optional section should also have product_uom_qty set to 0" },
|
||||
);
|
||||
expect(args[1].order_line.find(commands => commands[1] === 13)?.[2].product_uom_qty).toEqual( // Sec4-r1
|
||||
undefined,
|
||||
{ message: "Existing optional lines should keep their current product_uom_qty" }
|
||||
);
|
||||
});
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec4):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec3-r2):first');
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
||||
test("Moving Optional Sections to exclude some lines should set quantity to 1", async () => {
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3').is_optional = true;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-r1').product_uom_qty = 0;
|
||||
SaleOrderLine._records.find(record => record.name === 'Sec3-sub1-r1').product_uom_qty = 0;
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
|
||||
expect(args[1].order_line.find(command => command[1] === 6)[2].product_uom_qty).toEqual( // Sec3-r1
|
||||
1,
|
||||
{ message: "Non-optional lines should reset product_uom_qty to 1 when it was previously 0." },
|
||||
);
|
||||
expect(args[1].order_line.find(command => command[1] === 7)?.[2].product_uom_qty).toEqual( // Sec3-r2
|
||||
undefined,
|
||||
{ message: "Non-optional lines should keep their existing product_uom_qty when it was already non-zero." },
|
||||
);
|
||||
expect(args[1].order_line.find(command => command[1] === 9)[2].product_uom_qty).toEqual( // Sec3-sub1-r1
|
||||
1,
|
||||
{ message: "Lines moved out of an optional subsection should reset product_uom_qty to 1 when it was 0." },
|
||||
);
|
||||
expect(args[1].order_line.find(command => command[1] === 11)?.[2].product_uom_qty).toEqual( // Sec3-sub2-r1
|
||||
undefined,
|
||||
{ message: "Lines moved out of an optional subsection should keep their existing product_uom_qty when it was already non-zero." },
|
||||
);
|
||||
});
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec3):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec4):first');
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
import { defineMailModels } from '@mail/../tests/mail_test_helpers';
|
||||
import { expect, test } from '@odoo/hoot';
|
||||
import { queryAllTexts } from '@odoo/hoot-dom';
|
||||
import {
|
||||
clickSave,
|
||||
contains,
|
||||
defineModels,
|
||||
fields,
|
||||
models,
|
||||
mountView,
|
||||
onRpc,
|
||||
} from '@web/../tests/web_test_helpers';
|
||||
|
||||
class SaleOrderTemplateLine extends models.ServerModel {
|
||||
_name = 'sale.order.template.line';
|
||||
|
||||
product_uom_qty = fields.Float({ default: 1.00 });
|
||||
_records = [
|
||||
{ id: 1, name: "r1", sequence: 1 },
|
||||
{ id: 2, name: "r2", sequence: 2 },
|
||||
{
|
||||
id: 3,
|
||||
name: "Sec1",
|
||||
sequence: 3,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Sec2",
|
||||
sequence: 4,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Sec3",
|
||||
sequence: 5,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{ id: 6, name: "Sec3-r1", sequence: 6 },
|
||||
{ id: 7, name: "Sec3-r2", sequence: 7 },
|
||||
{
|
||||
id: 8,
|
||||
name: "Sec3-sub1",
|
||||
sequence: 8,
|
||||
display_type: 'line_subsection',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{ id: 9, name: "Sec3-sub1-r1", sequence: 9 },
|
||||
{
|
||||
id: 10,
|
||||
name: "Sec3-sub2",
|
||||
sequence: 10,
|
||||
display_type: 'line_subsection',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{ id: 11, name: "Sec3-sub2-r1", sequence: 11 },
|
||||
{
|
||||
id: 12,
|
||||
name: "Sec4",
|
||||
sequence: 12,
|
||||
display_type: 'line_section',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{ id: 13, name: "Sec4-r1", sequence: 13 },
|
||||
{
|
||||
id: 14,
|
||||
name: "Sec4-sub1",
|
||||
sequence: 14,
|
||||
display_type: 'line_subsection',
|
||||
product_uom_qty: 0,
|
||||
},
|
||||
{ id: 15, name: "Sec4-sub1-r1", sequence: 15 },
|
||||
];
|
||||
}
|
||||
|
||||
class SaleOrderTemplate extends models.ServerModel {
|
||||
_name = 'sale.order.template';
|
||||
_records = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Optional Sections Sale order template",
|
||||
sale_order_template_line_ids: SaleOrderTemplateLine._records.map(record => record.id),
|
||||
},
|
||||
];
|
||||
_views = {
|
||||
form: `
|
||||
<form>
|
||||
<field
|
||||
name="sale_order_template_line_ids"
|
||||
widget="so_template_line_o2m"
|
||||
options="{'subsections': True}"
|
||||
>
|
||||
<list editable="bottom">
|
||||
<control>
|
||||
<create name="add_line_control" string="Add a line"/>
|
||||
<create name="add_section_control" string="Add a section" context="{'default_display_type': 'line_section'}"/>
|
||||
<create name="add_note_control" string="Add a note" context="{'default_display_type': 'line_note'}"/>
|
||||
</control>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="display_type" column_invisible="1"/>
|
||||
<field name="is_optional" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</form>
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
defineModels({ SaleOrderTemplateLine, SaleOrderTemplate });
|
||||
defineMailModels();
|
||||
|
||||
const EXPECTED_LINE_RECORDS = [
|
||||
"r1",
|
||||
"r2",
|
||||
"Sec1",
|
||||
"Sec2",
|
||||
"Sec3",
|
||||
"Sec3-r1",
|
||||
"Sec3-r2",
|
||||
"Sec3-sub1",
|
||||
"Sec3-sub1-r1",
|
||||
"Sec3-sub2",
|
||||
"Sec3-sub2-r1",
|
||||
"Sec4",
|
||||
"Sec4-r1",
|
||||
"Sec4-sub1",
|
||||
"Sec4-sub1-r1",
|
||||
];
|
||||
|
||||
test("drag and drop regular template lines inside optional section resets some fields", async () => {
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec3').is_optional = true;
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec3-sub2-r1').product_uom_qty = 0;
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec3-sub1-r1').product_uom_qty = 1;
|
||||
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
commands => commands[1] === 13 // Sec4-r1
|
||||
)[2].product_uom_qty
|
||||
).toEqual(0, {
|
||||
message: "Drag and drop inside optional section should reset product_uom_qty to 0"
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
commands => commands[1] === 11 // Sec3-sub2-r1
|
||||
)[2].product_uom_qty
|
||||
).toEqual(1, {
|
||||
message: "Drag and drop line with 0 quantity outside optional section should reset product_uom_qty to 1"
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
commands => commands[1] === 9 // Sec3-sub1-r1
|
||||
)?.[2].product_uom_qty
|
||||
).toEqual(undefined, {
|
||||
message: "Drag and drop line with non-zero quantity outside optional section shouldn't reset product_uom_qty"
|
||||
});
|
||||
})
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order.template',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec4-r1):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec3-sub2):first');
|
||||
await contains('.o_data_row:contains(Sec3-sub2-r1):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec4-sub1):first');
|
||||
await contains('.o_data_row:contains(Sec3-sub1-r1):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec4-sub1):first');
|
||||
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
||||
test("Moving Optional Sections to include some template lines should set quantity to 0", async () => {
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec4').is_optional = true;
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec4-sub1-r1').product_uom_qty = 0;
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
commands => commands[1] === 7 // Sec3-r2
|
||||
)[2].product_uom_qty
|
||||
).toEqual(0, {
|
||||
message: "New lines added to an optional section should have product_uom_qty set to 0",
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
commands => commands[1] === 9 // Sec3-sub1-r1
|
||||
)[2].product_uom_qty
|
||||
).toEqual(0, {
|
||||
message: "New lines added to a subsection of an optional section should also have product_uom_qty set to 0",
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
commands => commands[1] === 13 // Sec4-r1
|
||||
)?.[2].product_uom_qty
|
||||
).toEqual(undefined, {
|
||||
message: "Existing optional lines should keep their current product_uom_qty",
|
||||
});
|
||||
});
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order.template',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec4):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec3-r2):first');
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
||||
test("Moving Optional Sections to exclude some template lines should set quantity to 1", async () => {
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec3').is_optional = true;
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec3-r1').product_uom_qty = 0;
|
||||
SaleOrderTemplateLine._records.find(record => record.name === 'Sec3-sub1-r1').product_uom_qty = 0;
|
||||
onRpc('web_save', ({ args }) => {
|
||||
expect.step('web_save');
|
||||
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
command => command[1] === 6 // Sec3-r1
|
||||
)[2].product_uom_qty
|
||||
).toEqual(1, {
|
||||
message: "Non-optional lines should reset product_uom_qty to 1 when it was previously 0.",
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
command => command[1] === 7 // Sec3-r2
|
||||
)?.[2].product_uom_qty
|
||||
).toEqual(undefined, {
|
||||
message: "Non-optional lines should keep their existing product_uom_qty when it was already non-zero.",
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
command => command[1] === 9 // Sec3-sub1-r1
|
||||
)[2].product_uom_qty
|
||||
).toEqual(1, {
|
||||
message: "Lines moved out of an optional subsection should reset product_uom_qty to 1 when it was 0.",
|
||||
});
|
||||
expect(
|
||||
args[1].sale_order_template_line_ids.find(
|
||||
command => command[1] === 11 // Sec3-sub2-r1
|
||||
)?.[2].product_uom_qty
|
||||
).toEqual(undefined, {
|
||||
message: "Lines moved out of an optional subsection should keep their existing product_uom_qty when it was already non-zero.",
|
||||
});
|
||||
});
|
||||
|
||||
await mountView({
|
||||
type: 'form',
|
||||
resModel: 'sale.order.template',
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
expect(queryAllTexts('.o_data_row .o_list_text')).toEqual(EXPECTED_LINE_RECORDS);
|
||||
|
||||
await contains('.o_data_row:contains(Sec3):first .o_row_handle').dragAndDrop('.o_data_row:contains(Sec4):first');
|
||||
await clickSave();
|
||||
await expect.verifySteps(['web_save']);
|
||||
})
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { registry } from "@web/core/registry";
|
||||
import { stepUtils } from "@web_tour/tour_utils";
|
||||
import tourUtils from "@sale/js/tours/tour_utils";
|
||||
|
||||
registry.category("web_tour.tours").add("test_basic_sale_flow_with_minimal_access_rights", {
|
||||
steps: () => [
|
||||
...stepUtils.goToAppSteps("sale.sale_menu_root", "Open the sales app"),
|
||||
{
|
||||
content: "Check that at least one quotation is present in the view",
|
||||
trigger: ".o_sale_onboarding_list_view .o_data_row",
|
||||
},
|
||||
...tourUtils.createNewSalesOrder(),
|
||||
...tourUtils.selectCustomer("partner_a"),
|
||||
...tourUtils.addProduct("Test Product"),
|
||||
tourUtils.checkSOLDescriptionContains("Test Product"),
|
||||
{
|
||||
trigger: "button[name=action_confirm]",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".o_statusbar_status .o_arrow_button_current:contains(Sales Order)",
|
||||
},
|
||||
{
|
||||
trigger: "button[id=create_invoice]",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
trigger: ".modal-content button[id=create_invoice_open]",
|
||||
run: "click",
|
||||
},
|
||||
{
|
||||
content: "Check that we are in the invoice form view",
|
||||
trigger: ".o_statusbar_status:contains(Posted) .o_arrow_button_current:contains(Draft)",
|
||||
},
|
||||
{
|
||||
content: "Check that the invoice is linked to the sale order",
|
||||
trigger: "button[name=action_view_source_sale_orders] .o_stat_value:contains(1)",
|
||||
},
|
||||
],
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue