Initial commit

This commit is contained in:
gsinghpal
2026-02-22 01:22:18 -05:00
commit 5200d5baf0
2394 changed files with 386834 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
import { accountTourSteps } from "@account/js/tours/account";
patch(accountTourSteps, {
onboarding() {
return [
{
trigger: ".o_widget_account_onboarding .fa-circle",
},
{
trigger: "a[data-method=action_open_step_fiscal_year]",
content: _t("Set Periods"),
run: "click",
},
{
trigger: "button[name=action_save_onboarding_fiscal_year]",
content: _t("Save Fiscal Year end"),
run: "click",
},
];
},
newInvoice() {
return [
{
trigger: ".o_widget_account_onboarding .fa-check-circle",
},
{
trigger: "button[name=action_create_new]",
content: _t("Now, we'll create your first invoice (accountant)"),
run: "click",
},
];
},
});

View File

@@ -0,0 +1,61 @@
/** @odoo-module */
import { accountTourSteps } from "@account/js/tours/account";
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("account_merge_wizard_tour", {
url: "/odoo",
steps: () => [
...accountTourSteps.goToAccountMenu("Go to Accounting"),
{
content: "Go to Configuration",
trigger: 'span:contains("Configuration")',
run: "click",
},
{
content: "Go to Chart of Accounts",
trigger: 'a:contains("Chart of Accounts")',
run: "click",
},
{
trigger: '.o_breadcrumb .text-truncate:contains("Chart of Accounts")',
},
{
content: "Select accounts",
trigger: "thead .o_list_record_selector",
run: "click",
},
{
content: "Check that exactly 4 accounts are present and selected",
trigger: ".o_list_selection_box:contains(4):contains(selected)",
},
{
content: "Open Actions menu",
trigger: ".o_cp_action_menus .dropdown-toggle",
run: "click",
},
{
content: "Open Merge accounts wizard",
trigger: 'span:contains("Merge accounts")',
run: "click",
},
{
content: "Group by name",
trigger: 'div[name="is_group_by_name"] input',
run: "click",
},
{
content: "Wait for content to be updated",
trigger: 'td:contains("Current Assets (Current Assets)")',
},
{
content: "Merge accounts",
trigger: 'button:not([disabled]) span:contains("Merge")',
run: "click",
},
{
content: "Check that there are now exactly 2 accounts",
trigger: "tr.o_data_row:contains(2)",
},
],
});

View File

@@ -0,0 +1,262 @@
/** @odoo-module **/
import { Asserts } from "./asserts";
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("account_reports", {
url: "/odoo/action-fusion_accounting.action_account_report_bs",
steps: () => [
//--------------------------------------------------------------------------------------------------------------
// Foldable
//--------------------------------------------------------------------------------------------------------------
{
content: "Initial foldable",
trigger: ".o_content",
run: () => {
Asserts.DOMContainsNumber("tbody > tr:not(.d-none):not(.empty)", 28);
// Since the total line is not displayed (folded), the amount should be on the line
Asserts.isEqual(
document.querySelector("tr:nth-child(4) td:nth-child(2)").textContent,
"75.00"
);
},
},
{
content: "Click to unfold line",
trigger: "tr:nth-child(4) td:first()",
run: "click",
},
{
content: "Line is unfolded",
trigger: "tr:nth-child(5) .name:contains('101401')",
run: () => {
Asserts.DOMContainsNumber("tbody > tr:not(.d-none):not(.empty)", 30);
// Since the total line is displayed (unfolded), the amount should not be on the line
Asserts.isEqual(
document.querySelector("tr:nth-child(4) td:nth-child(2)").textContent,
""
);
},
},
{
content: "Click to fold line",
trigger: "tr:nth-child(4) td:first()",
run: "click",
},
{
content: "Line is folded",
trigger: ".o_content",
run: () => {
Asserts.DOMContainsNumber("tbody > tr:not(.d-none):not(.empty)", 28);
},
},
//--------------------------------------------------------------------------------------------------------------
// Sortable
//--------------------------------------------------------------------------------------------------------------
{
content: "Unfold first line",
trigger: "tr:nth-child(4) td:first()",
run: "click",
},
{
content: "Unfold second line",
trigger: "tr:nth-child(7) td:first()",
run: "click",
},
{
content: "Unfold third line",
trigger: "tr:nth-child(10) td:first()",
run: "click",
},
{
content: "Extra Trigger step",
trigger: "tr:nth-child(12):not(.d-none) .name:contains('101404')",
},
{
content: "Initial sortable",
trigger: ".o_content",
run: () => {
// Bank and Cash Accounts
Asserts.isEqual(
document.querySelector("tr:nth-child(5) td:nth-child(2)").textContent,
"75.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(6) td:nth-child(2)").textContent,
"75.00"
);
// Receivables
Asserts.isEqual(
document.querySelector("tr:nth-child(8) td:nth-child(2)").textContent,
"25.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(9) td:nth-child(2)").textContent,
"25.00"
);
// Current Assets
Asserts.isEqual(
document.querySelector("tr:nth-child(11) td:nth-child(2)").textContent,
"100.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(12) td:nth-child(2)").textContent,
"50.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(13) td:nth-child(2)").textContent,
"150.00"
);
},
},
{
content: "Click sort",
trigger: "th .btn_sortable",
run: "click",
},
{
trigger: "tr:nth-child(11) td:nth-child(2):contains('50.00')",
},
{
content: "Unfold not previously unfolded line",
trigger: "tr:nth-child(22):contains('Current Liabilities') td:first()",
run: "click",
},
{
content: "Line is unfolded",
trigger: "tr:nth-child(23) .name:contains('251000')",
run: "click",
},
{
content: "Sortable (asc)",
trigger: "tr:nth-child(11) td:nth-child(2):contains('50.00')",
run: () => {
// Bank and Cash Accounts
Asserts.isEqual(
document.querySelector("tr:nth-child(5) td:nth-child(2)").textContent,
"75.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(6) td:nth-child(2)").textContent,
"75.00"
);
// Receivables
Asserts.isEqual(
document.querySelector("tr:nth-child(8) td:nth-child(2)").textContent,
"25.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(9) td:nth-child(2)").textContent,
"25.00"
);
// Current Assets
Asserts.isEqual(
document.querySelector("tr:nth-child(11) td:nth-child(2)").textContent,
"50.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(12) td:nth-child(2)").textContent,
"100.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(13) td:nth-child(2)").textContent,
"150.00"
);
},
},
{
content: "Click sort",
trigger: "th .btn_sortable",
run: "click",
},
{
content: "Sortable (desc)",
trigger: "tr:nth-child(11) td:nth-child(2):contains('100.00')",
run: () => {
// Bank and Cash Accounts
Asserts.isEqual(
document.querySelector("tr:nth-child(5) td:nth-child(2)").textContent,
"75.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(6) td:nth-child(2)").textContent,
"75.00"
);
// Receivables
Asserts.isEqual(
document.querySelector("tr:nth-child(8) td:nth-child(2)").textContent,
"25.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(9) td:nth-child(2)").textContent,
"25.00"
);
// Current Assets
Asserts.isEqual(
document.querySelector("tr:nth-child(11) td:nth-child(2)").textContent,
"100.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(12) td:nth-child(2)").textContent,
"50.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(13) td:nth-child(2)").textContent,
"150.00"
);
},
},
{
content: "Click sort",
trigger: "th .btn_sortable",
run: "click",
},
{
content: "Sortable (reset)",
trigger: "tr:nth-child(5) td:nth-child(2):contains('75.00')",
run: () => {
// Bank and Cash Accounts
Asserts.isEqual(
document.querySelector("tr:nth-child(5) td:nth-child(2)").textContent,
"75.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(6) td:nth-child(2)").textContent,
"75.00"
);
// Receivables
Asserts.isEqual(
document.querySelector("tr:nth-child(8) td:nth-child(2)").textContent,
"25.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(9) td:nth-child(2)").textContent,
"25.00"
);
// Current Assets
Asserts.isEqual(
document.querySelector("tr:nth-child(11) td:nth-child(2)").textContent,
"100.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(12) td:nth-child(2)").textContent,
"50.00"
);
Asserts.isEqual(
document.querySelector("tr:nth-child(13) td:nth-child(2)").textContent,
"150.00"
);
},
},
],
});

View File

@@ -0,0 +1,94 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add('account_reports_rounding_unit', {
url: '/odoo/action-fusion_accounting.action_account_report_bs',
steps: () => [
{
content: 'Test the value of `Receivables` line in decimals',
trigger: '.line_name:contains("Receivables") + .line_cell:contains("1,150,000.00")',
run: "click",
},
// Units
{
content: "Open amounts rounding dropdown",
trigger: "#filter_rounding_unit button",
run: 'click',
},
{
content: "Select the units filter",
trigger: ".dropdown-item:contains('In $')",
run: 'click',
},
{
trigger:
'.line_name:contains("Receivables") + .line_cell:not(:contains("1,150,000.00"))',
},
{
content: 'test the value of `Receivables` line in units',
// We wait for the value to change.
// We check the new value.
trigger: '.line_name:contains("Receivables") + .line_cell:contains("1,150,000")',
run: "click",
},
// Thousands
{
content: "Open amounts rounding dropdown",
trigger: "#filter_rounding_unit button",
run: 'click',
},
{
content: "Select the thousands filter",
trigger: ".dropdown-item:contains('In K$')",
run: 'click',
},
{
trigger: '.line_name:contains("Receivables") + .line_cell:not(:contains("1,150,000"))',
},
{
content: 'test the value of `Receivables` line in thousands',
// We wait for the value to change.
// We check the new value.
trigger: '.line_name:contains("Receivables") + .line_cell:contains("1,150")',
run: "click",
},
// Millions
{
content: "Open amounts rounding dropdown",
trigger: "#filter_rounding_unit button",
run: 'click',
},
{
content: "Select the millions filter",
trigger: ".dropdown-item:contains('In M$')",
run: 'click',
},
{
trigger: '.line_name:contains("Receivables") + .line_cell:not(:contains("1,150"))',
},
{
content: 'test the value of `Receivables` line in millions',
// We wait for the value to change.
// We check the new value.
trigger: '.line_name:contains("Receivables") + .line_cell:contains("1")',
run: "click",
},
// Decimals
{
content: "Open amounts rounding dropdown",
trigger: "#filter_rounding_unit button",
run: 'click',
},
{
content: "Select the decimals filter",
trigger: ".dropdown-item:contains('In .$')",
run: 'click',
},
{
content: 'test the value of `Receivables` line in millions',
trigger: '.line_name:contains("Receivables") + .line_cell:contains("1,150,000.00")',
run: () => null,
},
]
});

View File

@@ -0,0 +1,28 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("account_reports_analytic_filters", {
url: "/odoo/action-fusion_accounting.action_account_report_general_ledger",
steps: () => [
{
content: "click analytic filters",
trigger: ".filter_analytic button",
run: "click",
},
{
content: "insert text in the searchbar",
trigger: ".o_multi_record_selector input",
run: "edit Time",
},
{
content: "click on the item",
trigger: '.o-autocomplete--dropdown-item:contains("Time Off")',
run: "click",
},
{
content: "Check the label of the badge",
trigger: '.dropdown-menu .o_tag_badge_text:contains("Time Off")',
},
],
});

View File

@@ -0,0 +1,298 @@
/** @odoo-module **/
import { Asserts } from "./asserts";
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("account_reports_annotations", {
url: "/odoo/action-fusion_accounting.action_account_report_bs",
steps: () => [
//--------------------------------------------------------------------------------------------------------------
// Annotations
//--------------------------------------------------------------------------------------------------------------
// Test the initial status of annotations - There are 2 annotations to display
{
content: "Initial annotations",
trigger: ".o_content",
run: () => {
Asserts.DOMContainsNone(".annotations");
},
},
{
content: "Unfold first line",
trigger: "tr:nth-child(4) td:first()",
run: "click",
},
{
content: "Unfold second line",
trigger: "tr:nth-child(7) td:first()",
run: "click",
},
{
content: "Unfold third line",
trigger: "tr:nth-child(10) td:first()",
run: "click",
},
{
content: "Extra Trigger step",
trigger: "tr:nth-child(12):not(.d-none) .name:contains('101404')",
},
{
content: "Check there are two lines annotated initially",
trigger: ".o_content",
run: () => {
const annotations = document.querySelectorAll(".btn_annotation .fa-commenting");
// Check the number of annotated lines
Asserts.isEqual(annotations.length, 2);
// Check the annotations buttons are on the right lines
Asserts.isTrue(
annotations[0] ===
document.querySelector("tr:nth-child(5)").querySelector(".fa-commenting")
);
Asserts.isTrue(
annotations[1] ===
document.querySelector("tr:nth-child(12)").querySelector(".fa-commenting")
);
},
},
// Test that we can add a new annotation
{
content: "Click to show caret option",
trigger: "tr:nth-child(8) .dropdown-toggle",
run: "click",
},
{
content: "Caret option is displayed",
trigger: "tr:nth-child(8)",
run: () => {
Asserts.hasClass("tr:nth-child(8) .o-dropdown", "show");
},
},
{
content: "Click on annotate",
trigger: ".o-dropdown--menu .dropdown-item:last-of-type:contains('Annotate')",
run: "click",
},
{
content: "Add text to annotate",
trigger: "textarea",
run: "edit Annotation 121000",
},
{
content: "Submit annotation by blurring",
trigger: "textarea",
run: function () {
const annotation = this.anchor;
annotation.dispatchEvent(new InputEvent("input"));
annotation.dispatchEvent(new Event("change"));
},
},
{
content: "Wait for annotation created",
trigger: "tr:nth-child(8) .btn_annotation .fa-commenting",
},
{
content: "Close annotation",
trigger: ".o_content",
run: "click",
},
{
content: "Check there are now three lines annotated",
trigger: ".o_content",
run: () => {
const annotations = document.querySelectorAll(".btn_annotation .fa-commenting");
// Check the number of annotated lines
Asserts.isEqual(annotations.length, 3);
// Check the annotations buttons are on the right lines
Asserts.isTrue(
annotations[0] ===
document.querySelector("tr:nth-child(5)").querySelector(".fa-commenting")
);
Asserts.isTrue(
annotations[1] ===
document.querySelector("tr:nth-child(8)").querySelector(".fa-commenting")
);
Asserts.isTrue(
annotations[2] ===
document.querySelector("tr:nth-child(12)").querySelector(".fa-commenting")
);
},
},
// Test that we can edit an annotation
{
content: "Open second annotated line annotation popover",
trigger: "tr:nth-child(8) .btn_annotation",
run: "click",
},
{
content: "Annotate contains previous text value",
trigger: "textarea",
run: () => {
Asserts.isEqual(document.querySelector("textarea").value, "Annotation 121000");
},
},
{
content: "Add text to annotate",
trigger: "textarea",
run: "edit Annotation 121000 edited",
},
{
content: "Annotation is edited",
trigger: "tr:nth-child(8) .btn_annotation",
run: () => {
Asserts.isEqual(
document.querySelector(".annotation_popover_autoresize_textarea").value,
"Annotation 121000 edited"
);
},
},
// Test that we can delete an annotation by clicking the trash icon
{
content: "Click on trash can",
trigger: ".btn_annotation_delete",
run: "click",
},
{
content: "Check there are now only two lines annotated",
trigger: "tr:nth-child(8):not(:has(.fa-commenting))",
run: () => {
const annotations = document.querySelectorAll(".btn_annotation .fa-commenting");
// Check the number of annotated lines
Asserts.isEqual(annotations.length, 2);
// Check the annotations buttons are on the right lines
Asserts.isTrue(
annotations[0] ===
document.querySelector("tr:nth-child(5)").querySelector(".fa-commenting")
);
Asserts.isTrue(
annotations[1] ===
document.querySelector("tr:nth-child(12)").querySelector(".fa-commenting")
);
},
},
// Test that we can add an annotation by clicking on the "New" button inside the popover
{
content: "Open an annotated line annotation popover",
trigger: "tr:nth-child(12) .btn_annotation",
run: "click",
},
{
content: "Click on the 'New' button",
trigger: ".annotation_popover_line .oe_link",
run: "click",
},
{
content: "Add text to annotate",
trigger: "textarea:last()",
run: "edit Annotation 101404 bis",
},
{
content: "Submit annotation by blurring",
trigger: "textarea:last()",
run: function () {
const annotation = this.anchor;
annotation.dispatchEvent(new InputEvent("input"));
annotation.dispatchEvent(new Event("change"));
},
},
// Check the current state of the annotations
{
content: "Check there are two annotated lines to end the test",
trigger: ".o_content",
run: () => {
const annotations = document.querySelectorAll(".btn_annotation .fa-commenting");
// Check there is only one annotated line
Asserts.isEqual(annotations.length, 2);
// Check the annotation buttons are on the right lines
Asserts.isTrue(
annotations[0] ===
document.querySelector("tr:nth-child(5)").querySelector(".fa-commenting")
);
Asserts.isTrue(
annotations[1] ===
document.querySelector("tr:nth-child(12)").querySelector(".fa-commenting")
);
},
},
//--------------------------------------------------------------------------------------------------------------
// Annotations dates
//--------------------------------------------------------------------------------------------------------------
{
content:
"Remove first annotation to only have one annotation on line 12 (required setup step)",
trigger: ".annotation_popover tr:nth-child(2) .btn_annotation_delete",
run: "click",
},
{
content: "Verify that we still have one element",
trigger: ".annotation_popover tr:nth-child(3):contains('Add a line')",
},
{
content: "Modify the date of an annotation to a further period",
trigger: ".annotation_popover tr:nth-child(2) input",
run: "edit 01/01/2100",
},
{
content: "Modify the date of an annotation to a further period",
trigger: ".annotation_popover tr:nth-child(2) input",
run: function () {
// Since the t-on-change of the input is not triggered by the run: "edit" action,
// we need to dispatch the event manually requiring a function.
const input = this.anchor;
input.dispatchEvent(new InputEvent("input"));
input.dispatchEvent(new Event("change", { bubbles: true }));
},
},
{
content: "Check that there is no annotation anymore on line 12",
trigger: "tr:nth-child(12):not(:has(.fa-commenting))",
},
{
content: "change date filter",
trigger: "#filter_date button",
run: "click",
},
{
content: "Open specific date button",
trigger: ".dropdown-menu div.dropdown-item",
run: "click",
},
{
content: "Go to 15 January 2100",
trigger: ".o_datetime_input",
run: "edit 01/15/2100",
},
{
content: "Apply filter by closing the dropdown",
trigger: "#filter_date .btn:first()",
run: "click",
},
{
content: "wait refresh",
trigger: `#filter_date button:not(:contains(${new Date().getFullYear()}))`,
},
{
content: "Check there is one annotation on line 12",
trigger: "tr:nth-child(12):has(.fa-commenting)",
run: () => {
const annotations = document.querySelectorAll(".btn_annotation .fa-commenting");
// Check there is only one annotated line
Asserts.isEqual(annotations.length, 1);
// Check the annotation buttons are on the right lines
Asserts.isTrue(
annotations[0] ===
document.querySelector("tr:nth-child(12)").querySelector(".fa-commenting")
);
}
},
]
});

View File

@@ -0,0 +1,107 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add('account_reports_hide_0_lines', {
url: '/odoo/action-fusion_accounting.action_account_report_bs',
steps: () => [
{
content: 'test if the Bank and Cash line is present (but the value is 0)',
trigger: '.line_name:contains("Bank and Cash Accounts")',
run: "click",
},
{
content: 'test if the Current Year Unallocated Earnings line is present (but the value is 0)',
trigger: '.line_name:contains("Current Year Unallocated Earnings")',
run: "click",
},
{
content: 'test if the Unallocated Earnings line is present (but value is different from 0 and so should be there after the hide_0_lines',
trigger: '.line_name:contains("Unallocated Earnings")',
run: "click",
},
{
content: "Open options selector",
trigger: "#filter_extra_options button",
run: 'click',
},
{
content: "Select the hide line at 0 option",
trigger: ".dropdown-item:contains('Hide lines at 0')",
run: 'click',
},
{
content: 'test if the Unallocated Earnings line is still present',
trigger: '.line_name:contains("Unallocated Earnings")',
run: "click",
},
{
content: 'test if the Bank and Cash line is not present',
trigger: '.line_name:not(:contains("Bank and Cash Accounts"))',
run: "click",
},
{
content: 'test if the Current Year Unallocated Earnings line is not present',
trigger: '.line_name:not(:contains("Current Year Unallocated Earnings"))',
run: "click",
},
{
content: "Click again to open the options selector",
trigger: "#filter_extra_options button",
run: 'click',
},
{
content: "Select the hide lines at 0 option again",
trigger: ".dropdown-item:contains('Hide lines at 0')",
run: 'click',
},
{
content: 'test again if the Bank and Cash line is present (but the value is 0)',
trigger: '.line_name:contains("Bank and Cash Accounts")',
run: () => null,
},
]
});
registry.category("web_tour.tours").add('account_reports_hide_0_lines_with_string_columns', {
url: '/odoo/action-fusion_accounting.action_account_report_general_ledger',
steps: () => [
{
content: "Check if the 211000 Account Payable line is present (but the value is 0)",
trigger: ".name:contains('211000 Account Payable')",
run: "click",
},
{
content: "Check if the MISC item line is present with string values set up, but all amounts are at 0",
trigger: ".name:contains('Coucou les biloutes')",
},
{
content: "Open options selector",
trigger: "#filter_extra_options button",
run: 'click',
},
{
content: "Select the hide line at 0 option",
trigger: ".dropdown-item:contains('Hide lines at 0')",
run: 'click',
},
{
content: "Check if the MISC item line is hidden",
trigger: ":not(:visible):contains('Coucou les biloutes')",
},
{
content: "Click again to open the options selector",
trigger: "#filter_extra_options button",
run: 'click',
},
{
content: "Select the hide lines at 0 option again",
trigger: ".dropdown-item:contains('Hide lines at 0')",
run: 'click',
},
{
content: "Check again if the MISC item line is present (but the value is 0)",
trigger: ".name:contains('Coucou les biloutes')",
},
]
});

View File

@@ -0,0 +1,43 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add('account_reports_search', {
url: '/odoo/action-fusion_accounting.action_account_report_general_ledger',
steps: () => [
{
content: "click search",
trigger: '.o_searchview_input',
run: 'click',
},
{
content: 'insert text in the searchbar',
trigger: '.o_searchview_input',
run: "edit 40",
},
{
content: 'test if the product sale line is present',
trigger: '.line_name:contains("400000 Product Sales")',
run: "click",
},
{
content: "click search",
trigger: '.o_searchview_input',
run: 'click',
},
{
content: 'insert text in the search bar',
trigger: '.o_searchview_input',
run: "edit Account",
},
{
content: 'test if the receivable line is present',
trigger: '.line_name:contains("121000 Account Receivable")',
run: 'click',
},
{
content: 'check that the product sale line is not present',
trigger: '.line_name:not(:contains("400000 Product Sales"))',
},
]
});

View File

@@ -0,0 +1,129 @@
/** @odoo-module **/
const { DateTime } = luxon;
import { Asserts } from "./asserts";
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add('account_reports_sections', {
url: "/odoo/action-fusion_accounting.action_account_report_gt",
steps: () => [
{
content: "Open variant selector",
trigger: "#filter_variant button",
run: 'click',
},
{
content: "Select the test variant using sections",
trigger: ".dropdown-item:contains('Test Sections')",
run: 'click',
},
{
content: "Check the lines of section 1 are displayed",
trigger: ".line_name:contains('Section 1 line')",
},
{
content: "Check the columns of section 1 are displayed",
trigger: "#table_header th:last():contains('Column 1')",
},
{
content: "Check the export buttons belong to the composite report",
trigger: ".btn:contains('composite_report_custom_button')",
},
{
content: "Check the filters displayed belong to section 1 (journals filter is not enabled on section 2, nor the composite report)",
trigger: "#filter_journal",
},
{
content: "Check the date chosen by default",
trigger: "#filter_date",
run: (actionHelper) => {
// Generic tax report opens on the previous period and in this case the period is one month.
// And since we are using the generic tax report, we need to go back one month.
const previousMonth = DateTime.now().minus({months: 1});
Asserts.isTrue(actionHelper.anchor.getElementsByTagName('button')[0].innerText.includes(previousMonth.year));
},
},
{
content: "Switch to section 2",
trigger: "#section_selector .btn:contains('Section 2')",
run: 'click',
},
{
content: "Check the lines of section 2 are displayed",
trigger: ".line_name:contains('Section 2 line')",
},
{
content: "Check the columns of section 2 are displayed",
trigger: "#table_header th:last():contains('Column 2')",
},
{
content: "Check the export buttons belong to the composite report",
trigger: ".btn:contains('composite_report_custom_button')",
},
{
content: "Check the filters displayed belong to section 2 (comparison filter is not enabled on section 1, nor the composite report)",
trigger: "#filter_comparison",
},
{
content: "Open date switcher",
trigger: "#filter_date button",
run: 'click',
},
{
content: "Select another date in the future",
trigger: ".dropdown-menu span.dropdown-item:nth-child(3) .btn_next_date",
run: 'click'
},
{
content: "Apply filter by closing the dropdown for the future date",
trigger: "#filter_date .btn:first()",
run: "click",
},
{
content: "Check that the date has changed",
trigger: `#filter_date button:not(:contains(${ DateTime.now().minus({months: 1}).year }))`, // We need to remove one month for the case where we are in january. It will impact the year.
run: (actionHelper) => {
const nextYear = DateTime.now().plus({years: 1}).year;
Asserts.isTrue(actionHelper.anchor.innerText.includes(nextYear));
},
},
{
content: "Open date switcher",
trigger: "#filter_date button",
run: 'click',
},
{
content: "Select another date first time",
trigger: ".dropdown-menu span.dropdown-item:nth-child(3) .btn_previous_date",
run: 'click'
},
{
trigger: `.dropdown-menu span.dropdown-item:nth-child(3) time:contains(${ DateTime.now().year})`,
},
{
content: "Select another date second time",
trigger: ".dropdown-menu span.dropdown-item:nth-child(3) .btn_previous_date",
run: 'click'
},
{
trigger: `.dropdown-menu span.dropdown-item:nth-child(3) time:contains(${ DateTime.now().minus({years: 1}).year })`,
},
{
content: "Apply filter by closing the dropdown",
trigger: "#filter_date .btn:first()",
run: "click",
},
{
content: "Check that the date has changed",
trigger: `#filter_date button:contains(${ DateTime.now().minus({years: 1}).year })`,
},
{
content: "Switch back to section 1",
trigger: "#section_selector .btn:contains('Section 1')",
run: 'click',
},
]
});

View File

@@ -0,0 +1,82 @@
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("account_reports_widgets", {
url: "/odoo/action-fusion_accounting.action_account_report_pl",
steps: () => [
{
content: "change date filter",
trigger: "#filter_date button",
run: "click",
},
{
content: "Select another date in the future",
trigger: ".dropdown-menu span.dropdown-item:nth-child(3) .btn_next_date",
run: 'click'
},
{
content: "Apply filter by closing the dropdown",
trigger: "#filter_date .btn:first()",
run: "click",
},
{
content: "wait refresh",
trigger: `#filter_date button:not(:contains(${ new Date().getFullYear() }))`,
},
{
content: "change date filter for the second time",
trigger: "#filter_date button",
run: "click",
},
{
content: "Select another date in the past first time",
trigger: ".dropdown-menu span.dropdown-item:nth-child(3) .btn_previous_date",
run: 'click'
},
{
trigger: `.dropdown-menu span.dropdown-item:nth-child(3) time:contains(${new Date().getFullYear()})`,
},
{
content: "Select another date in the past second time",
trigger: ".dropdown-menu span.dropdown-item:nth-child(3) .btn_previous_date",
run: 'click'
},
{
trigger: `.dropdown-menu span.dropdown-item:nth-child(3) time:contains(${
new Date().getFullYear() - 1
})`,
},
{
content: "Apply filter by closing the dropdown",
trigger: "#filter_date .btn:first()",
run: "click",
},
{
content: "wait refresh",
trigger: `#filter_date button:contains(${ new Date().getFullYear() - 1 })`,
},
{
content: "change comparison filter",
trigger: "#filter_comparison .btn:first()",
run: "click",
},
{
content: "change comparison filter",
trigger: ".dropdown-item.period:first()",
run: "click",
},
{
content: "Apply filter by closing the dropdown",
trigger: "#filter_comparison .btn:first()",
run: "click",
},
{
content: "wait refresh, report should have 4 columns",
trigger: "th + th + th + th",
},
{
content: "export xlsx",
trigger: "button:contains('XLSX')",
run: "click",
},
],
});

View File

@@ -0,0 +1,10 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { accountTourSteps } from "@account/js/tours/account";
import { stepUtils } from "@web_tour/tour_service/tour_utils";
patch(accountTourSteps, {
goToAccountMenu(description="Open Accounting Menu") {
return stepUtils.goToAppSteps('fusion_accounting.menu_accounting', description);
}
});

View File

@@ -0,0 +1,115 @@
/** @odoo-module **/
//----------------------------------------------------------------------------------------------------------------------
// This class provides some helpers function to do assertions on tours
//----------------------------------------------------------------------------------------------------------------------
export class Asserts {
//------------------------------------------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------------------------------------------
// Gets the number of 'selector' element inside 'target' element
static getCount(target, selector) {
return document.querySelector(target).querySelectorAll(selector).length;
}
// Gets the number of 'selector' element inside DOM
static getDOMCount(selector) {
return document.querySelectorAll(selector).length;
}
static check(condition, success, error) {
condition ? Asserts.success(success) : Asserts.error(error);
}
static success(message) {
return console.info(`SUCCESS: ${message}`);
}
static error(message) {
throw new Error(`FAIL: ${message}`);
}
//------------------------------------------------------------------------------------------------------------------
// Asserts
//------------------------------------------------------------------------------------------------------------------
static isTrue(actual) {
Asserts.check(actual, `${actual} is true`, `${actual} is not true`);
}
static isFalse(actual) {
Asserts.check(!actual, `${actual} is false`, `${actual} is not false`);
}
// Assert that 'actual' and 'expected' are equal
static isEqual(actual, expected) {
Asserts.check(
(actual == expected),
`${actual} is equal to expected ${expected}`,
`${actual} is not equal to expected ${expected}`
);
}
// Asserts that 'actual' and 'expected' are strictly equal
static isStrictEqual(actual, expected) {
Asserts.check(
(actual === expected),
`${actual} is strictly equal to expected ${expected}`,
`${actual} is not strictly equal to expected ${expected}`
);
}
// Assert that 'target' element contains at least one 'selector' element
static contains(target, selector) {
const count = Asserts.getCount(target, selector);
Asserts.check(
(count > 0),
`There is at least one ${selector} in ${target}`,
`There should be at least one ${selector} in ${target} but there is ${count}`
);
}
// Asserts there is no 'selector' element in 'target' element
static containsNone(target, selector) {
const count = Asserts.getCount(target, selector);
Asserts.check(
(count === 0),
`There is no ${selector} in ${target}`,
`There should be no ${selector} in ${target} but there is ${count}`
);
}
// Asserts that 'target' element contains 'number' of 'selector' elements
static containsNumber(target, selector, number) {
const count = Asserts.getCount(target, selector);
Asserts.check(
(count === number),
`There is the correct number (${number}) of ${selector} in ${target}`,
`There should be at ${number} ${selector} in ${target} but there is ${count}`
);
}
// Asserts that DOM contains at least one 'selector' element
static DOMContains(selector) {
const count = Asserts.getDOMCount(selector);
Asserts.check(
(count > 0),
`There is at least one ${selector} in the DOM`,
`There should be at least one ${selector} in the DOM but there is ${count}`
);
}
// Asserts there is no 'selector' element in the DOM
static DOMContainsNone(selector) {
const count = Asserts.getDOMCount(selector);
Asserts.check(
(count === 0),
`There is no ${selector} in the DOM`,
`There should be 0 ${selector} in the DOM but there is ${count}`
);
}
// Asserts that DOM contains 'number' of 'selector' element
static DOMContainsNumber(selector, number) {
const count = Asserts.getDOMCount(selector);
Asserts.check(
(Asserts.getDOMCount(selector) === number),
`There is the correct number (${number}) of ${selector} in the DOM`,
`There should be ${number} ${selector} in the DOM but there is ${count}`
);
}
// Asserts that 'selector' element has class 'classname'
static hasClass(selector, classname) {
Asserts.check(
document.querySelector(selector).classList.contains(classname),
`${selector} has class ${classname}`,
`${selector} should have class ${classname} but hasn't`
);
}
}

View File

@@ -0,0 +1,91 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { stepUtils } from "@web_tour/tour_service/tour_utils";
import { accountTourSteps } from "@account/js/tours/account";
registry.category("web_tour.tours").add("fusion_accounting_bank_rec_widget_rainbowman_reset", {
url: "/odoo",
steps: () => [
stepUtils.showAppsMenuItem(),
...accountTourSteps.goToAccountMenu("Open the accounting module"),
// Open the widget. The first line should be selected by default.
{
trigger: ".o_breadcrumb",
},
{
content: "Open the bank reconciliation widget",
trigger: "button.btn-secondary[name='action_open_reconcile']",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line1')",
},
{
content: "'line1' should be selected and form mounted",
trigger: ".o_bank_rec_selected_st_line:contains('line1')",
},
// Rainbowman gets reset
{
content: "Mount invoice 2 for line 1",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table td[name='move_id']:contains('INV/2019/00002')",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00002')",
},
{
content: "Validate line1",
trigger: "button:contains('Validate')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
},
{
content: "No records brings rainbows",
trigger: "div.o_kanban_view .o_searchview_input",
run: "fill thisShouldNotReturnAnyRecords",
},
{
content: "Select the Journal Entry search option from the dropdown",
trigger: ".o_searchview_autocomplete li:contains(Journal Entry)",
run: "click",
},
{
trigger: ".o_reward_rainbow_man:contains('You reconciled 1 transaction in')",
},
{
content: "Remove the filter while rainbow man is on screen",
trigger: ".o_kanban_view .o_searchview_facet:nth-child(3) .o_facet_remove",
run: "click",
},
{
trigger: ".o_bank_rec_st_line:contains('line2')",
},
{
content: "Search for no results again",
trigger: "div.o_kanban_view .o_searchview_input",
run: "fill thisShouldNotReturnAnyRecords",
},
{
content: "Select the Journal Entry search option from the dropdown",
trigger: ".o_searchview_autocomplete li:contains(Journal Entry)",
run: "click",
},
{
content: "No content helper is displayed instead of rainbowman",
trigger: ".o_view_nocontent_smiling_face",
},
// End
...stepUtils.toggleHomeMenu(),
...accountTourSteps.goToAccountMenu("Reset back to accounting module"),
{
content: "check that we're back on the dashboard",
trigger: 'a:contains("Customer Invoices")',
},
],
});

View File

@@ -0,0 +1,95 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { stepUtils } from "@web_tour/tour_service/tour_utils";
import { accountTourSteps } from "@account/js/tours/account";
registry.category("web_tour.tours").add('fusion_accounting_bank_rec_widget_statements',
{
url: '/odoo',
steps: () => [
stepUtils.showAppsMenuItem(),
...accountTourSteps.goToAccountMenu("Open the accounting module"),
{
trigger: ".o_breadcrumb",
},
{
content: "Open the bank reconciliation widget",
trigger: "button.btn-secondary[name='action_open_reconcile']",
run: "click",
},
{
content: "Statement button",
trigger:
".o_bank_rec_st_line:eq(2) a.oe_kanban_action:contains('Statement'):not(:visible)",
run: "click",
},
{
trigger: ".modal-dialog:contains('Create Statement')",
},
{
content: "Save the statement with proposed values",
trigger: ".o_form_button_save",
run: "click",
},
{
content: "Click the Valid Statement with $ 1,000.00 that is visible in Kanban",
trigger: "span[name='kanban-subline-clickable-amount']:contains('$ 1,000.00')",
run: "click",
},
{
content: "Modify the end balance",
trigger: "input[id='balance_end_real_0']",
run: "edit 100 && click body",
},
{
trigger: ".alert-warning:contains('The running balance')",
},
{
content: "Dialog displays warning, save anyway",
trigger: ".breadcrumb-item.o_back_button:nth-of-type(2)",
run: "click",
},
{
trigger: ".btn-link:contains('$ 2,100.00')",
},
{
content: "Click the red statement, after checking the balance",
trigger: "span[name='kanban-subline-clickable-amount']:contains('$ 100.00')",
run: "click",
},
{
content: "Back in the form view",
trigger: ".alert-warning:contains('The running balance')",
},
{
content: "Click on Action",
trigger: ".o_cp_action_menus button",
run: "click",
},
{
content: "Click on Delete",
trigger: ".o-dropdown--menu span:contains('Delete')",
run: "click",
},
{
content: "Confirm Deletion",
trigger: ".btn-primary:contains('Delete')",
run: "click",
},
{
trigger: ".o_kanban_renderer:not(:has(.kanban-statement))",
},
{
content: "balance displays $3000.00 and no statement",
trigger: ".btn-link:contains('$ 3,000')",
},
// End
...stepUtils.toggleHomeMenu(),
...accountTourSteps.goToAccountMenu("Reset back to accounting module"),
{
content: "check that we're back on the dashboard",
trigger: 'a:contains("Customer Invoices")',
}
]
});

View File

@@ -0,0 +1,983 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { stepUtils } from "@web_tour/tour_service/tour_utils";
import { patch } from "@web/core/utils/patch";
import { accountTourSteps } from "@account/js/tours/account";
patch(accountTourSteps, {
bankRecUiReportSteps() {
return [
{
trigger: ".o_bank_rec_selected_st_line:contains('line1')",
},
{
content: "balance is 2100",
trigger: ".btn-link:contains('$ 2,100.00')",
},
];
},
});
patch(accountTourSteps, {
bankRecUiReportSteps() {
return [
{
trigger: ".o_bank_rec_selected_st_line:contains('line1')",
},
{
content: "balance is 2100",
trigger: ".btn-link:contains('$ 2,100.00')",
run: "click",
},
{
trigger: "span:contains('General Ledger')",
},
{
content: "Breadcrumb back to Bank Reconciliation from the report",
trigger: ".breadcrumb-item a:contains('Bank Reconciliation')",
run: "click",
},
];
},
});
registry.category("web_tour.tours").add("fusion_accounting_bank_rec_widget_ui", {
url: "/odoo",
steps: () => [
stepUtils.showAppsMenuItem(),
...accountTourSteps.goToAccountMenu("Open the accounting module"),
// Open the widget. The first line should be selected by default.
{
trigger: ".o_breadcrumb",
},
{
content: "Open the bank reconciliation widget",
trigger: "button.btn-secondary[name='action_open_reconcile']",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line1')",
},
{
content: "'line1' should be selected and form mounted",
trigger: ".o_bank_rec_selected_st_line:contains('line1')",
},
// Select line2. It should remain selected when returning using the breadcrumbs.
{
trigger: ".o_bank_rec_st_line:contains('line3')",
},
{
content: "select 'line2'",
trigger: ".o_bank_rec_st_line:contains('line2')",
run: "click",
},
{
content: "'line2' should be selected",
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
},
{
content: "View an invoice",
trigger: "button.btn-secondary[name='action_open_business_doc']:eq(1)",
run: "click",
},
{
trigger: ".o_breadcrumb .active:contains('INV/2019/00001')",
},
{
content: "Breadcrumb back to Bank Reconciliation from INV/2019/00001",
trigger: ".breadcrumb-item:contains('Bank Reconciliation')",
run: "click",
},
{
trigger: ".o_bank_rec_st_line:contains('line1')",
},
{
content: "'line2' should be selected after returning",
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
{
content: "'line2' form mounted",
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
run: "click",
},
// Keep AML search, and prepared entry (line_ids) when changing tabs, using breadcrumbs, and view switcher
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr:nth-child(2) td[name='move_id']:contains('INV/2019/00001')",
},
{
content: "AMLs list has both invoices",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr:nth-child(1) td[name='move_id']:contains('INV/2019/00002')",
},
{
trigger: "a.active[name='amls_tab']",
},
{
content: "Search for INV/2019/00001",
trigger: "div.bank_rec_widget_form_amls_list_anchor .o_searchview_input",
run: "edit INV/2019/00001",
},
{
content: "Select the Journal Entry search option from the dropdown",
trigger: ".o_searchview_autocomplete li:contains(Journal Entry)",
run: "click",
},
{
content: "AMLs list only displays one invoice",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr:nth-child(1) td[name='move_id']:contains('INV/2019/00001')",
},
{
content: "Liquidity line displays debit '$ 1,000.00'",
trigger:
"div[name='line_ids'] table.o_list_table tr.o_bank_rec_liquidity_line td[field='debit']:contains('$ 1,000.00')",
},
{
content: "Select the liquidity line",
trigger: "tr.o_bank_rec_liquidity_line td[field='debit']",
run: "click",
},
{
content: "Modify the liquidity line amount",
trigger: "div[name='amount_currency'] input",
run: "edit 100.00 && click body",
},
{
content: "Liquidity line displays debit '$ 100.00'",
trigger:
"div[name='line_ids'] table.o_list_table tr.o_bank_rec_liquidity_line td[field='debit']:contains('$ 100.00')",
},
{
trigger: "div[name='partner_id'] input",
},
{
content: "Select 'amls_tab'",
trigger: "a[name='amls_tab']",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor .o_searchview_facet:nth-child(1) .o_facet_value:contains('INV/2019/00001')",
},
{
content: "AMLs list contains the search facet, and one invoice - select it",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr:nth-child(1) td[name='move_id']:contains('INV/2019/00001')",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
{
content: "Check INV/2019/00001 is well marked as selected",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
{
content: "View an invoice",
trigger: "button.btn-secondary[name='action_open_business_doc']:nth-child(1)",
run: "click",
},
{
trigger: ".o_breadcrumb .active:contains('INV/2019/00001')",
},
{
content: "Breadcrumb back to Bank Reconciliation from INV/2019/00001",
trigger: ".breadcrumb-item:contains('Bank Reconciliation')",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor .o_searchview_facet:nth-child(1) .o_facet_value:contains('INV/2019/00001')",
},
{
content: "Check INV/2019/00001 is selected and still contains the search facet",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
// Search should remove some lines, select the first unmatched record, and persist when returning with breadcrumbs
{
trigger: "a.active[name='amls_tab']",
},
{
content: "Search for line2",
trigger: "div.o_kanban_view .o_searchview_input",
run: "fill line2",
},
{
content: "Select the Transaction search option from the dropdown",
trigger: ".o_searchview_autocomplete li:contains(Transaction)",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
{
content: "'line2' should be selected",
trigger: ".o_bank_rec_st_line:last():contains('line2')",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor .o_searchview_facet:nth-child(1) .o_facet_value:contains('INV/2019/00001')",
},
{
content:
"Nothing has changed: INV/2019/00001 is selected and still contains the search facet",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
{
trigger: ".o_switch_view.o_kanban.active",
},
{
content: "Switch to list view",
trigger: ".o_switch_view.o_list",
run: "click",
},
{
trigger: ".o_switch_view.o_list.active",
},
{
content: "Switch back to kanban",
trigger: ".o_switch_view.o_kanban",
run: "click",
},
{
content: "Remove the kanban filter for line2",
trigger: ".o_kanban_view .o_searchview_facet:nth-child(3) .o_facet_remove",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor .o_searchview_facet:nth-child(1) .o_facet_value:contains('INV/2019/00001')",
},
{
content:
"Nothing has changed: INV/2019/00001 is still selected and contains the search facet",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
// AML Search Facet is removed, and line_ids reset when changing line
{
trigger: ".o_bank_rec_st_line:contains('line3')",
},
{
content: "selecting 'line1' should reset the AML search filter ",
trigger: ".o_bank_rec_st_line:contains('line1')",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line1')",
},
{
content: "select 'line2' again",
trigger: ".o_bank_rec_st_line:contains('line2')",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
{
content: "Bank Suspense Account is back",
trigger: "div[name='line_ids'] .o_bank_rec_auto_balance_line",
},
{
content: "AML Search Filter has been reset",
trigger: ".o_list_view .o_searchview_input_container:not(:has(.o_searchview_facet))",
},
// Test statement line selection when using the pager
{
content: "Click Pager",
trigger: ".o_pager_value:first()",
run: "click",
},
{
content: "Change pager to display lines 1-2",
trigger: "input.o_pager_value",
run: "edit 1-2 && click body",
},
{
trigger: ".o_pager_value:contains('1-2')",
},
{
content: "Last St Line is line2",
trigger: ".o_bank_rec_st_line:last():contains('line2')",
},
{
content: "Page Next",
trigger: ".o_pager_next:first():not(:disabled)",
run: "click",
},
{
trigger: ".o_pager_value:contains('3-3')",
},
{
content: "Statement line3 is selected",
trigger: ".o_bank_rec_selected_st_line:contains('line3')",
},
{
content: "Page to beginning",
trigger: ".o_pager_next:first()",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line1')",
},
{
content: "Statement line1 is selected",
trigger: ".o_bank_rec_selected_st_line:contains('line1')",
},
// HTML buttons
{
content: "Mount an invoice",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table td[name='move_id']:contains('INV/2019/00003')",
run: "click",
},
{
trigger:
"div[name='line_ids']:has(.text-decoration-line-through:contains('$ 2,000.00'))",
},
{
content: "Select the mounted invoice line and check the strikethrough value",
trigger:
"div[name='line_ids'] tr.o_data_row:last() td[field='name']:contains('INV/2019/00003')",
run: "click",
},
{
trigger: "a.active[name='manual_operations_tab']",
},
{
content: "Fully Paid button",
trigger: "button[name='action_apply_line_suggestion']:contains('fully paid')",
run: "click",
},
{
content: "Check the remainder",
trigger:
"div[name='line_ids'] tr.o_data_row:contains('Suspense') td[field='debit']:contains('$ 1,000.00')",
},
{
content: "Partial Payment",
trigger: "button[name='action_apply_line_suggestion']:contains('partial payment')",
run: "click",
},
{
trigger: "button[name='action_apply_line_suggestion']:contains('fully paid')",
},
{
content: "View Invoice 0003",
trigger: "button[name='action_redirect_to_move']",
run: "click",
},
{
trigger: ".o_breadcrumb .active:contains('INV/2019/00003')",
},
{
content: "Breadcrumb back to Bank Reconciliation from INV/2019/00003",
trigger: ".breadcrumb-item:contains('Bank Reconciliation')",
run: "click",
},
{
content: "Select the mounted invoice line INV/2019/00003",
trigger:
"div[name='line_ids'] tr.o_data_row:last() td[field='name']:contains('INV/2019/00003')",
run: "click",
},
// Match Existing entries tab is activated when line is removed
{
trigger: "a.active[name='manual_operations_tab']",
},
{
content: "Remove the invoice",
trigger: ".o_list_record_remove .fa-trash-o",
run: "click",
},
{
content: "amls_tab is activated",
trigger: "a.active[name='amls_tab']",
},
{
content: "Activate Manual Operations to add manual entries",
trigger: "a[name='manual_operations_tab']",
run: "click",
},
{
content: "add manual entry 1",
trigger: "div[name='amount_currency'] input",
run: "edit -600.0 && click body",
},
{
content: "mount the remaining opening balance line",
trigger:
"div[name='line_ids'] tr.o_data_row:contains('Suspense') td[field='credit']:contains('$ 400.00')",
run: "click",
},
{
trigger: "div[name='amount_currency'] input:value('-400.00'):focus-within",
},
{
content: "Remove the manual entry",
trigger: ".o_list_record_remove .fa-trash-o",
run: "click",
},
{
trigger:
"div[name='line_ids'] tr.o_data_row:contains('Suspense') td[field='credit']:contains('$ 1,000.00')",
},
{
content: "amls_tab is activated and auto balancing line is 1000",
trigger: "a.active[name='amls_tab']",
},
{
content: "Mount another invoice",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table td[name='move_id']:contains('INV/2019/00001')",
run: "click",
},
// After validating, line1 should disappear & line2 should be selected (due to filters)
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
{
content: "Validate line1",
trigger: "button:contains('Validate')",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
{
content: "The 'line2' is the first kanban record and is selected",
trigger: ".o_bank_rec_st_line:first():contains('line2')",
},
// Test Reset, "Matched" badge and double-click
{
content: "Remove the kanban filter for 'Not Matched'",
trigger: ".o_kanban_view .o_searchview_facet:nth-child(2) .o_facet_remove",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
{
content: "The 'line1' is the first kanban record with line2 selected",
trigger: ".o_bank_rec_st_line:first():contains('line1')",
},
{
content: "Mount invoice 2 for line 2",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table td[name='move_id']:contains('INV/2019/00002')",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00002')",
},
{
content: "Validate line2 with double click",
trigger: "button:contains('Validate')",
run: "dblclick",
},
{
trigger: ".o_bank_rec_st_line:contains('line2') .badge.text-bg-success",
},
{
content: "Click Pager again after line2 is matched",
trigger: ".o_pager_value:first()",
run: "click",
},
{
content: "Change pager to display lines 1-3",
trigger: "input.o_pager_value",
run: "edit 1-3 && click body",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line3')",
},
{
content: "manually select line2 again by clicking it's matched icon",
trigger: ".badge.text-bg-success:last()",
run: "click",
},
{
trigger:
"div[name='line_ids']:not(:has(.fa-trash-o)) td[field='name']:contains('line2')",
},
{
content: "Reset line2",
trigger: "button:contains('Reset')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line2'):not(:has(div.badge))",
},
{
content: "amls_tab is activated while still on line2 which doesn't contain a badge",
trigger: ".o_notebook a.active[name='amls_tab']",
},
// Test view_switcher
{
trigger: ".o_switch_view.o_kanban.active",
},
{
content: "Switch to list view",
trigger: ".o_switch_view.o_list",
run: "click",
},
{
trigger: ".btn-secondary:contains('View')",
},
{
content: "Select the first Match Button (line2)",
trigger: ".btn-secondary:contains('Match')",
run: "click",
},
{
trigger: ".o_bank_rec_st_line:last():contains('line2')",
},
{
content: "Last St Line is line2",
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
run: "click",
},
{
content: "Button To Check will reconcile since partner is saved on line2",
trigger: ".btn-secondary:contains('To Check')",
run: "click",
},
{
trigger:
".o_bank_rec_selected_st_line:contains('line2'):has(div.badge[title='Matched'] i):has(span.badge:contains('To check'))",
},
{
content: "both badges are visible, trash icon is not, manual operation tab is active",
trigger:
"div[name='line_ids']:not(:has(.fa-trash-o))+.o_notebook a.active[name='manual_operations_tab']",
},
{
trigger: ".o_switch_view.o_kanban.active",
},
{
content: "Switch to list view",
trigger: ".o_switch_view.o_list",
run: "click",
},
{
trigger: ".o_switch_view.o_list.active",
},
{
content: "Remove the line filter",
trigger: ".o_searchview_facet:contains('0002') .o_facet_remove",
run: "click",
},
{
trigger: ".o_data_row:contains('line2'):has(.btn-secondary:contains('View'))",
},
{
content: "Select the first Match Button (line3)",
trigger: ".btn-secondary:contains('Match')",
run: "click",
},
{
trigger: ".o_bank_rec_stats_buttons",
},
{
content: "Open search bar menu",
trigger: ".o_searchview_dropdown_toggler:eq(0)",
run: "click",
},
// Test Reco Model
{
trigger: ".o-dropdown--menu.o_search_bar_menu",
},
{
content: "Choose a filter",
trigger: ".o_search_bar_menu .dropdown-item:first()",
run: "click",
},
{
trigger: ".o-dropdown--menu",
},
{
content: "Not Matched Filter",
trigger: ".dropdown-item:contains('Not Matched')",
run: "click",
},
{
trigger: ".o_switch_view.o_kanban.active",
},
{
content: "reco model dropdown",
trigger: ".bank_rec_reco_model_dropdown i",
run: "click",
},
{
trigger: ".o-dropdown--menu",
},
{
content: "create model",
trigger: ".dropdown-item:contains('Create model')",
run: "click",
},
{
content: "model name",
trigger: "input#name_0",
run: "edit Bank Fees",
},
{
content: "add an account",
trigger: "a:contains('Add a line')",
run: "click",
},
{
content: "search for bank fees account",
trigger: "[name='account_id'] input",
run: "edit Bank Fees",
},
{
trigger: ".o-autocomplete--dropdown-menu",
},
{
content: "select the bank fees account",
trigger: ".o-autocomplete--dropdown-item:contains('Bank Fees')",
run: "click",
},
{
trigger: ".o_breadcrumb .active > span:contains('New')",
},
{
content: "Breadcrumb back to Bank Reconciliation from the model",
trigger: ".breadcrumb-item:contains('Bank Reconciliation')",
run: "click",
},
{
content: "Choose Bank Fees Model",
trigger: ".recon_model_button:contains('Bank Fees')",
run: "click",
},
{
content: "Validate line3",
trigger: "button:contains('Validate').btn-primary",
run: "dblclick",
},
{
trigger: ".o_reward_rainbow_man",
},
{
content:
"Remove the kanbans 'not matched' filter to reset all lines - use the rainbow man button",
trigger: "p.btn-primary:contains('All Transactions')",
run: "click",
},
{
trigger:
".o_kanban_view .o_searchview:first() .o_searchview_facet:last():contains('Bank')",
},
{
content: "Wait for search model change and line3 to appear",
trigger: ".o_bank_rec_st_line:last():contains('line3')",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
},
{
content: "'line2' should be selected, reset it",
trigger: "button:contains('Reset')",
run: "click",
},
{
trigger: ".o_bank_rec_st_line:contains('line2'):not(:has(div.badge))",
},
{
content: "select matched 'line3'",
trigger: ".o_bank_rec_st_line:contains('line3')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line3')",
},
{
content: "'line3' should be selected, reset it",
trigger: "button:contains('Reset')",
run: "click",
},
{
trigger: ".o_bank_rec_st_line:contains('line3'):not(:has(div.badge))",
},
{
content: "select matched 'line1'",
trigger: ".o_bank_rec_st_line:contains('line1')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line1')",
},
{
content: "'line1' should be selected, reset it",
trigger: "button:contains('Reset')",
run: "click",
},
{
trigger: ".o_bank_rec_stats_buttons",
},
{
content: "Open search bar menu",
trigger: ".o_searchview_dropdown_toggler:eq(0)",
run: "click",
},
{
trigger: "button:contains('Validate')",
},
{
content: "Filter Menu",
trigger: ".o_search_bar_menu .dropdown-item:first()",
run: "click",
},
{
trigger: ".o-dropdown--menu",
},
{
content: "Activate the Not Matched filter",
trigger: ".dropdown-item:contains('Not Matched')",
run: "click",
},
{
trigger: ".o_searchview_facet:contains('Not Matched')",
},
{
content: "Close the Filter Menu",
trigger: ".o_searchview_dropdown_toggler:eq(0)",
run: "click",
},
{
trigger: ".o_searchview_facet:contains('Not Matched')",
},
{
content: "select 'line2'",
trigger: ".o_bank_rec_st_line:contains('line2')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
},
{
content: "Validate 'line2' again",
trigger: "button:contains('Validate')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line3')",
},
{
content: "'line3' should be selected now",
trigger: ".o_bank_rec_selected_st_line:contains('line3')",
},
// Test the Balance when changing journal and liquidity line
...stepUtils.toggleHomeMenu(),
...accountTourSteps.goToAccountMenu("Reset back to accounting module"),
{
trigger: ".o_breadcrumb",
},
{
content: "Open the bank reconciliation widget for Bank2",
trigger: "button.btn-secondary[name='action_open_reconcile']:last()",
run: "click",
},
{
content: "Remove the kanbans 'not matched' filter",
trigger: ".o_kanban_view .o_searchview_facet:nth-child(2) .o_facet_remove",
run: "click",
},
{
content: "Remove the kanban 'journal' filter",
trigger: ".o_kanban_view .o_searchview_facet:nth-child(1) .o_facet_remove",
run: "click",
},
{
content: "select 'line1' from another journal",
trigger: ".o_bank_rec_st_line:contains('line1')",
run: "click",
},
...accountTourSteps.bankRecUiReportSteps(),
{
content: "select 'line4' from this journal",
trigger: ".o_bank_rec_st_line:contains('line4')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line4')",
},
{
content: "balance is $222.22",
trigger: ".btn-link:contains('$ 222.22')",
},
{
content: "Select the liquidity line",
trigger: "tr.o_bank_rec_liquidity_line td[field='debit']",
run: "click",
},
{
trigger: "div[name='amount_currency'] input:focus-within",
},
{
content: "Modify the liquidity line amount",
trigger: "div[name='amount_currency'] input",
run: "edit -333.33 && click body",
},
{
trigger: ".btn-link:contains('$ -333.33')",
},
{
content: "balance displays $-333.33",
trigger: ".btn-link:contains('$ -333.33')",
},
{
content: "Modify the label",
trigger: "div[name='name'] input",
run: "edit Spontaneous Combustion && click body",
},
{
content: "statement line displays combustion and $-333.33",
trigger: ".o_bank_rec_selected_st_line:contains('Combustion'):contains('$ -333.33')",
},
// Test that changing the balance in the list view updates the right side of the kanban view
// (including reapplying matching rules)
{
content: "select matched 'line2'",
trigger: ".o_bank_rec_st_line:contains('line2')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line2')",
},
{
content: "'line2' should be selected, reset it",
trigger: "button:contains('Reset')",
run: "click",
},
{
trigger: ".o_bank_rec_selected_st_line:contains('line2'):not(:has(div.badge))",
},
{
content: "Liquidity line displays debit '$ 100.00'",
trigger:
"div[name='line_ids'] table.o_list_table tr.o_bank_rec_liquidity_line td[field='debit']:contains('$ 100.00')",
},
{
trigger: ".o_switch_view.o_kanban.active",
},
{
content: "Switch to list view",
trigger: ".o_switch_view.o_list",
run: "click",
},
{
content: "Click amount field of 'line2'; Selects the row",
trigger: "table.o_list_table tr.o_data_row:contains('line2') td[name='amount']",
run: "click",
},
{
content: "Set balance of 'line2' (selected row) to 500.00",
trigger: "table.o_list_table tr.o_data_row.o_selected_row td[name='amount'] input",
run: "edit 500.00 && click body",
},
{
trigger: ".o_switch_view.o_list.active",
},
{
content: "Switch back to kanban",
trigger: ".o_switch_view.o_kanban",
run: "click",
},
{
content: "'line2' is still selected",
trigger: ".o_bank_rec_st_line:contains('line2')",
},
{
content: "Liquidity line displays debit '$ 500.00'",
trigger:
"div[name='line_ids'] table.o_list_table tr.o_bank_rec_liquidity_line td[field='debit']:contains('$ 500.00')",
},
{
content:
"'INV/2019/00001' has been selected as matching existing entry by matching rules",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='name']:contains('INV/2019/00001')",
},
// End
...stepUtils.toggleHomeMenu(),
...accountTourSteps.goToAccountMenu("Reset back to accounting module"),
{
content: "check that we're back on the dashboard",
trigger: 'a:contains("Customer Invoices")',
},
],
});
registry.category("web_tour.tours").add('fusion_accounting_bank_rec_widget_reconciliation_button',
{
url: '/odoo',
steps: () => [
stepUtils.showAppsMenuItem(),
...accountTourSteps.goToAccountMenu("Open the accounting module"),
{
content: "Open the bank reconciliation widget",
trigger: "button.btn-secondary[name='action_open_reconcile']",
run: "click",
},
{
content: "Remove suggested line, if present",
trigger: ".o_list_record_remove",
run() {
const button = document.querySelector('.fa-trash-o');
if(button) {
button.click();
}
}
},
{
content: "Wait for deletion",
trigger: ".o_data_row:contains('Open balance')",
},
{
content: "Select reconciliation model creating a new move",
trigger: ".recon_model_button:contains('test reconcile')",
run: "click",
},
{
content: "Confirm move created through reconciliation model writeoff button",
trigger: "button[name=action_post]",
run: "click",
},
{
trigger: ".o_breadcrumb",
},
{
content: "Breadcrumb back to Bank Reconciliation from created move",
trigger: ".breadcrumb-item:contains('Bank Reconciliation')",
run: "click",
},
{
content: "Validate created move added as a line in reco widget",
trigger: "button:contains('Validate')",
run: "click",
},
// End
...stepUtils.toggleHomeMenu(),
...accountTourSteps.goToAccountMenu("Reset back to accounting module"),
{
content: "check that we're back on the dashboard",
trigger: 'a:contains("Customer Invoices")',
},
],
});

View File

@@ -0,0 +1,236 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { stepUtils } from "@web_tour/tour_service/tour_utils";
import { accountTourSteps } from "@account/js/tours/account";
registry.category("web_tour.tours").add("fusion_accounting_bank_rec_widget", {
url: "/odoo",
steps: () => [
stepUtils.showAppsMenuItem(),
...accountTourSteps.goToAccountMenu("Open the accounting module"),
// Open the widget. The first line should be selected by default.
{
trigger: ".o_breadcrumb",
},
{
content: "Open the bank reconciliation widget",
trigger: "button.btn-secondary[name='action_open_reconcile']",
run: "click",
},
{
trigger: "div[name='line_ids']",
},
{
content: "The 'line1' should be selected by default",
trigger: "div[name='line_ids'] td[field='name']:contains('line1')",
},
// Test 1: Check the loading of lazy notebook tabs.
// Check 'amls_tab' (active by default).
{
trigger: "div.bank_rec_widget_form_amls_list_anchor table.o_list_table",
},
{
content: "The 'amls_tab' should be active and the inner list view loaded",
trigger: "a.active[name='amls_tab']",
},
// Check 'discuss_tab'.
{
trigger: "a.active[name='amls_tab']",
},
{
content: "Click on the 'discuss_tab'",
trigger: "a[name='discuss_tab']",
run: "click",
},
{
trigger: "a.active[name='discuss_tab']",
},
{
content: "The 'discuss_tab' should be active and the chatter loaded",
trigger: "div.bank_rec_widget_form_discuss_anchor div.o-mail-Chatter",
},
// Check 'manual_operations_tab'.
{
trigger: "tr.o_bank_rec_auto_balance_line",
},
{
content: "Click on the 'auto_balance' to make the 'manual_operations_tab' visible",
trigger: "tr.o_bank_rec_auto_balance_line td[field='name']",
run: "click",
},
{
content: "The 'manual_operations_tab' should be active",
trigger: "a.active[name='manual_operations_tab']",
},
{
content: "The 'name' field should be focus automatically",
trigger: "div.o_notebook div[name='name'] input:focus",
},
{
trigger: "tr.o_bank_rec_auto_balance_line",
},
{
content: "Click on the 'credit' field to change the focus from 'name' to 'amount_currency'",
trigger: "tr.o_bank_rec_auto_balance_line td[field='credit']",
run: "click",
},
{
content: "Wait to avoid non-deterministic errors on the next step",
trigger: "tr.o_bank_rec_auto_balance_line td[field='credit']",
},
{
content: "The 'balance' field should be focus now",
trigger: "div.o_notebook div[name='amount_currency'] input:focus",
},
// Test 2: Test validation + auto select the next line.
{
trigger: "a.active[name='manual_operations_tab']",
},
{
content: "Click on the 'amls_tab'",
trigger: "a[name='amls_tab']",
run: "click",
},
{
trigger: "a.active[name='amls_tab']",
},
{
content: "Mount INV/2019/00002",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table td[name='move_id']:contains('INV/2019/00002')",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00002')",
},
{
content: "Check INV/2019/00002 is well marked as selected",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00002')",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00002')",
},
{
content: "Remove INV/2019/00002",
trigger: "tr td.o_list_record_remove button",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr:not(.o_rec_widget_list_selected_item) td[name='move_id']:contains('INV/2019/00002')",
},
{
content: "Mount INV/2019/00001",
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table td[name='move_id']:contains('INV/2019/00001')",
run: "click",
},
{
trigger:
"div.bank_rec_widget_form_amls_list_anchor table.o_list_table tr.o_rec_widget_list_selected_item td[name='move_id']:contains('INV/2019/00001')",
},
{
content: "Validate",
trigger: "button:contains('Validate')",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
{
content: "The 'line2' is the next not already reconciled line",
trigger: "div[name='line_ids'] td[field='name']:contains('line2')",
},
// Test 3: Test manual operations tab.
{
content: "Click on 'credit'",
trigger: "div[name='line_ids'] td[field='credit']:last",
run: "click",
},
{
content:
"The 'manual_operations_tab' should be active now and the auto_balance line mounted in edit",
trigger: "a.active[name='manual_operations_tab']",
},
{
content: "The last line should be selected",
trigger: "div[name='line_ids'] tr.o_bank_rec_selected_line",
},
{
content: "Search for 'partner_a'",
trigger: "div[name='partner_id'] input",
run: "edit partner_a",
},
{
trigger: ".ui-autocomplete .o_m2o_dropdown_option a:contains('Create')",
},
{
content: "Select 'partner_a'",
trigger: ".ui-autocomplete:visible li:contains('partner_a')",
run: "click",
},
{
trigger:
"tr:not(.o_bank_rec_auto_balance_line) td[field='partner_id']:contains('partner_a')",
},
{
content: "Select the payable account",
trigger: "button:contains('Payable')",
run: "click",
},
{
trigger:
"tr:not(.o_bank_rec_auto_balance_line) td[field='account_id']:contains('Payable')",
},
{
content: "Enter a tax",
trigger: "div[name='tax_ids'] input",
run: "edit 15",
},
{
trigger: ".ui-autocomplete",
},
{
content: "Select 'Tax 15% (Sales)'",
trigger: ".ui-autocomplete:visible li:contains('Sales')",
run: "click",
},
{
content: "Tax column appears in list of lines",
trigger: "div[name='line_ids'] td[field='tax_ids']",
},
{
content: "Wait to avoid non-deterministic errors on the next step",
trigger: "div[name='line_ids'] td:contains('Tax Received')",
},
{
trigger: "button.btn-primary:contains('Validate')",
},
{
content: "Validate",
trigger: "button:contains('Validate')",
run: "click",
},
{
trigger: "div[name='line_ids'] td[field='name']:contains('line3')",
},
{
content: "The 'line3' is the next not already reconciled line",
trigger: "div[name='line_ids'] td[field='name']:contains('line3')",
},
...stepUtils.toggleHomeMenu(),
...accountTourSteps.goToAccountMenu("Reset back to accounting module"),
{
content: "check that we're back on the dashboard",
trigger: 'a:contains("Customer Invoices")',
},
],
});