changes
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// Fusion Accounting - Account Report Download Action
|
||||
// Copyright (C) 2026 Nexa Systems Inc.
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { download } from "@web/core/network/download";
|
||||
|
||||
/**
|
||||
* Executes a file download for account report exports (PDF, XLSX, etc.).
|
||||
* Registered as a client action handler for report download requests.
|
||||
*/
|
||||
async function executeAccountReportDownload({ env, action }) {
|
||||
env.services.ui.block();
|
||||
|
||||
const url = "/fusion_accounting";
|
||||
const data = action.data;
|
||||
|
||||
try {
|
||||
await download({ url, data });
|
||||
env.services.action.doAction({type: 'ir.actions.act_window_close'});
|
||||
} catch (e) {
|
||||
if (e.exceptionName === 'AccountReportFileDownloadException') {
|
||||
const reportOptions = JSON.parse(data.options);
|
||||
const reportAction = await env.services.orm.call(
|
||||
'account.report',
|
||||
'open_account_report_file_download_error_wizard',
|
||||
[reportOptions.report_id, e.data.arguments[0], e.data.arguments[1]],
|
||||
);
|
||||
env.services.action.doAction(reportAction);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
env.services.ui.unblock();
|
||||
}
|
||||
}
|
||||
|
||||
registry
|
||||
.category("action_handlers")
|
||||
.add('ir_actions_account_report_download', executeAccountReportDownload);
|
||||
@@ -0,0 +1,84 @@
|
||||
// Fusion Accounting - Guided Tour
|
||||
// Copyright (C) 2026 Nexa Systems Inc.
|
||||
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { markup } from "@odoo/owl";
|
||||
import { accountTourSteps } from "@account/js/tours/account";
|
||||
import { stepUtils } from "@web_tour/tour_service/tour_utils";
|
||||
|
||||
patch(accountTourSteps, {
|
||||
newInvoice() {
|
||||
return [
|
||||
{
|
||||
trigger: "button[name=action_create_new]",
|
||||
content: _t("Now, we'll create your first invoice (accountant)"),
|
||||
run: "click",
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
patch(accountTourSteps, {
|
||||
goToAccountMenu(description="Open Accounting Menu") {
|
||||
return stepUtils.goToAppSteps('fusion_accounting.menu_accounting', description);
|
||||
},
|
||||
});
|
||||
|
||||
registry.category("web_tour.tours").add('fusion_accounting_tour', {
|
||||
url: "/odoo",
|
||||
steps: () => [
|
||||
...accountTourSteps.goToAccountMenu('Let’s automate your bills, bank transactions and accounting processes.'),
|
||||
// The tour will stop here if there is at least 1 vendor bill in the database.
|
||||
// While not ideal, it is ok, since that means the user obviously knows how to create a vendor bill...
|
||||
{
|
||||
trigger: 'a[name="action_create_vendor_bill"]',
|
||||
content: markup(_t('Create your first vendor bill.<br/><br/><i>Tip: If you don’t have one on hand, use our sample bill.</i>')),
|
||||
tooltipPosition: 'bottom',
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: 'button.btn-primary[name="action_post"]',
|
||||
content: _t('After the data extraction, check and validate the bill. If no vendor has been found, add one before validating.'),
|
||||
tooltipPosition: 'bottom',
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: '.dropdown-item[data-menu-xmlid="account.menu_board_journal_1"]',
|
||||
content: _t('Let’s go back to the dashboard.'),
|
||||
tooltipPosition: 'bottom',
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: 'a[name="open_action"] span:contains(bank)',
|
||||
content: _t('Connect your bank and get your latest transactions.'),
|
||||
tooltipPosition: 'bottom',
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: 'button.o-kanban-button-new',
|
||||
content: _t('Create a new transaction.'),
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: "div[name=amount] div input[id=amount_0]",
|
||||
content: _t("Set an amount."),
|
||||
tooltipPosition: "bottom",
|
||||
run: "edit -19250.00",
|
||||
}, {
|
||||
trigger: "div[name=payment_ref] input[id=payment_ref_0]",
|
||||
content: _t("Set the payment reference."),
|
||||
tooltipPosition: "bottom",
|
||||
run: "edit Payment Deco Adict",
|
||||
}, {
|
||||
trigger: "button.o_kanban_edit",
|
||||
content: _t("Confirm the transaction."),
|
||||
tooltipPosition: "bottom",
|
||||
run: "click",
|
||||
}, {
|
||||
trigger: '.o_bank_rec_st_line:not(.o_bank_rec_selected_st_line)',
|
||||
content: _t('Click on a fetched bank transaction to start the reconciliation process.'),
|
||||
run: "click",
|
||||
}, {
|
||||
isActive: ['auto'],
|
||||
trigger: '.dropdown-item[data-menu-xmlid="account.menu_board_journal_1"]',
|
||||
content: _t('Let’s go back to the dashboard.'),
|
||||
run: "click",
|
||||
},
|
||||
]
|
||||
});
|
||||
78
Work in Progress/fusion_accounting/static/src/js/util.js
Normal file
78
Work in Progress/fusion_accounting/static/src/js/util.js
Normal file
@@ -0,0 +1,78 @@
|
||||
// Fusion Accounting - Report Utility Functions
|
||||
// Copyright (C) 2026 Nexa Systems Inc.
|
||||
|
||||
const LINE_ID_HIERARCHY_DELIMITER = "|";
|
||||
const LINE_ID_GROUP_DELIMITER = "~";
|
||||
|
||||
/**
|
||||
* Constructs a hierarchical line identifier string from an array of
|
||||
* [markup, model, value] tuples, using delimiters for serialization.
|
||||
* @param {Array} current - Array of [markup, model, value] tuples
|
||||
* @returns {string} Serialized line ID
|
||||
*/
|
||||
export function buildLineId(current) {
|
||||
const convertNull = (value) => {
|
||||
if (value === null || value === undefined || value === false) {
|
||||
return "";
|
||||
} else if (typeof value === "object") {
|
||||
// We're using the replaceAll to follow the python
|
||||
// behavior where we're stringify a dict.
|
||||
return JSON.stringify(value).replaceAll('":', '": ');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
return current
|
||||
.map(([markup, model, value]) => {
|
||||
const lineValues = [convertNull(markup), convertNull(model), convertNull(value)];
|
||||
return lineValues.join(LINE_ID_GROUP_DELIMITER);
|
||||
})
|
||||
.join(LINE_ID_HIERARCHY_DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a serialized line ID string back into an array of
|
||||
* [markup, model, value] tuples.
|
||||
* @param {string} lineID - The serialized line ID string
|
||||
* @param {boolean} markupAsString - If true, keeps markup as raw string
|
||||
* @returns {Array} Array of [markup, model, value] tuples
|
||||
*/
|
||||
export function parseLineId(lineID, markupAsString = false) {
|
||||
const parseMarkup = (markup) => {
|
||||
if (!markup) {
|
||||
return markup;
|
||||
}
|
||||
try {
|
||||
const result = JSON.parse(markup);
|
||||
return typeof result === "object" ? result : markup;
|
||||
} catch {
|
||||
return markup;
|
||||
}
|
||||
};
|
||||
|
||||
if (!lineID) {
|
||||
return [];
|
||||
}
|
||||
return lineID.split(LINE_ID_HIERARCHY_DELIMITER).map((key) => {
|
||||
const [markup, model, value] = key.split(LINE_ID_GROUP_DELIMITER);
|
||||
return [
|
||||
(markupAsString ? markup : parseMarkup(markup)) || null,
|
||||
model || null,
|
||||
model && value ? parseInt(value) : value || null,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes tax grouping segments from a line ID, since tax grouping
|
||||
* is not relevant for annotations and other per-line operations.
|
||||
* @param {string} lineId - The line ID to filter
|
||||
* @returns {string} Line ID with tax group entries removed
|
||||
*/
|
||||
export function removeTaxGroupingFromLineId(lineId) {
|
||||
return buildLineId(
|
||||
parseLineId(lineId, true).filter(([markup, model, value]) => {
|
||||
return model !== "account.group";
|
||||
})
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user