This commit is contained in:
gsinghpal
2026-03-13 12:38:28 -04:00
parent db4b9aa278
commit fc3c966484
2975 changed files with 1614 additions and 498 deletions

View File

@@ -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);

View File

@@ -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('Lets 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 dont 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('Lets 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('Lets go back to the dashboard.'),
run: "click",
},
]
});

View 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";
})
);
}