Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions addons/spreadsheet_account/i18n/spreadsheet_account.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0\n"
"Project-Id-Version: Odoo Server 18.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-26 08:56+0000\n"
"PO-Revision-Date: 2024-09-26 08:56+0000\n"
"POT-Creation-Date: 2025-01-16 15:16+0000\n"
"PO-Revision-Date: 2025-01-16 15:16+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
Expand All @@ -34,6 +34,12 @@ msgstr ""
msgid "Account"
msgstr ""

#. module: spreadsheet_account
#. odoo-python
#: code:addons/spreadsheet_account/models/account.py:0
msgid "Cell Audit"
msgstr ""

#. module: spreadsheet_account
#: model:ir.model,name:spreadsheet_account.model_res_company
msgid "Companies"
Expand All @@ -58,9 +64,21 @@ msgid "Get the total debit for the specified account(s) and period."
msgstr ""

#. module: spreadsheet_account
#. odoo-python
#: code:addons/spreadsheet_account/models/account.py:0
msgid "Journal items for account prefix %s"
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/accounting_functions.js:0
msgid "Offset applied to the years."
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/accounting_functions.js:0
msgid "Return the partner balance for the specified account(s) and period"
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/accounting_functions.js:0
msgid "Return the residual amount for the specified account(s) and period"
msgstr ""

#. module: spreadsheet_account
Expand Down Expand Up @@ -95,6 +113,12 @@ msgstr ""
msgid "Set to TRUE to include unposted entries."
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/plugins/accounting_plugin.js:0
msgid "The balance for given partners could not be computed."
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/plugins/accounting_plugin.js:0
Expand Down Expand Up @@ -133,6 +157,12 @@ msgstr ""
msgid "The day from which to extract the fiscal year start."
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/accounting_functions.js:0
msgid "The partner ids (separated by a comma)."
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/accounting_functions.js:0
Expand All @@ -148,5 +178,13 @@ msgstr ""
#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/accounting_functions.js:0
msgid "Year offset applied to date_range."
msgid ""
"The prefix of the accounts. If none provided, all receivable and payable "
"accounts will be used."
msgstr ""

#. module: spreadsheet_account
#. odoo-javascript
#: code:addons/spreadsheet_account/static/src/plugins/accounting_plugin.js:0
msgid "The residual amount for given accounts could not be computed."
msgstr ""
75 changes: 70 additions & 5 deletions addons/spreadsheet_account/models/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@ def _get_date_period_boundaries(self, date_period, company):
start, _ = date_utils.get_fiscal_year(end, fiscal_day, fiscal_month)
return start, end

def _build_spreadsheet_formula_domain(self, formula_params):
def _build_spreadsheet_formula_domain(self, formula_params, default_accounts=False):
codes = [code for code in formula_params["codes"] if code]

default_domain = expression.FALSE_DOMAIN
if not codes:
return expression.FALSE_DOMAIN
if not default_accounts:
return default_domain
default_domain = [('account_type', 'in', ['liability_payable', 'asset_receivable'])]

company_id = formula_params["company_id"] or self.env.company.id
company = self.env["res.company"].browse(company_id)
start, end = self._get_date_period_boundaries(
Expand All @@ -67,7 +72,8 @@ def _build_spreadsheet_formula_domain(self, formula_params):
]
for code in codes
)
account_ids = self.env["account.account"].with_company(company_id).search(code_domain).ids
account_domain = expression.OR([code_domain, default_domain])
account_ids = self.env["account.account"].with_company(company_id).search(account_domain).ids
code_domain = [("account_id", "in", account_ids)]
period_domain = expression.OR([balance_domain, pnl_domain])
domain = expression.AND([code_domain, period_domain, [("company_id", "=", company_id)]])
Expand All @@ -79,19 +85,24 @@ def _build_spreadsheet_formula_domain(self, formula_params):
domain = expression.AND(
[domain, [("move_id.state", "=", "posted")]]
)
partner_ids = [int(partner_id) for partner_id in formula_params.get('partner_ids', []) if partner_id]
if partner_ids:
domain = expression.AND(
[domain, [("partner_id", "in", partner_ids)]]
)
return domain

@api.model
def spreadsheet_move_line_action(self, args):
domain = self._build_spreadsheet_formula_domain(args)
domain = self._build_spreadsheet_formula_domain(args, default_accounts=True)
return {
"type": "ir.actions.act_window",
"res_model": "account.move.line",
"view_mode": "list",
"views": [[False, "list"]],
"target": "current",
"domain": domain,
"name": _("Journal items for account prefix %s", ", ".join(args["codes"])),
"name": _("Cell Audit"),
}

@api.model
Expand All @@ -118,6 +129,60 @@ def spreadsheet_fetch_debit_credit(self, args_list):

return results

@api.model
def spreadsheet_fetch_residual_amount(self, args_list):
"""Fetch data for ODOO.RESUDUAL formulas
The input list looks like this:
[{
date_range: {
range_type: "year"
year: int
},
company_id: int
codes: str[]
include_unposted: bool
}]
"""
results = []
for args in args_list:
company_id = args["company_id"] or self.env.company.id
domain = self._build_spreadsheet_formula_domain(args, default_accounts=True)
MoveLines = self.env["account.move.line"].with_company(company_id)
[(amount_residual,)] = MoveLines._read_group(domain, aggregates=['amount_residual:sum'])
results.append({'amount_residual': amount_residual or 0})

return results

@api.model
def spreadsheet_fetch_partner_balance(self, args_list):
"""Fetch data for ODOO.PARTNER.BALANCE formulas
The input list looks like this:
[{
date_range: {
range_type: "year"
year: int
},
company_id: int
codes: str[]
include_unposted: bool
partner_ids: int[]
}]
"""
results = []
for args in args_list:
partner_ids = [partner_id for partner_id in args.get('partner_ids', []) if partner_id]
if not partner_ids:
results.append({'balance': 0})
continue

company_id = args["company_id"] or self.env.company.id
domain = self._build_spreadsheet_formula_domain(args, default_accounts=True)
MoveLines = self.env["account.move.line"].with_company(company_id)
[(balance,)] = MoveLines._read_group(domain, aggregates=['balance:sum'])
results.append({'balance': balance or 0})

return results

@api.model
def get_account_group(self, account_types):
data = self._read_group(
Expand Down
110 changes: 106 additions & 4 deletions addons/spreadsheet_account/static/src/accounting_functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,20 +140,43 @@ export function parseAccountingDate(dateRange, locale) {
}
}

const YEAR_OFFSET_ARG = arg("offset (number, default=0)", _t("Offset applied to the years."))
const COMPANY_ARG = arg("company_id (number, optional)", _t("The company to target (Advanced)."))
const POSTED_ARG = arg(
"include_unposted (boolean, default=FALSE)",
_t("Set to TRUE to include unposted entries.")
)

const ODOO_FIN_ARGS = () => [
arg("account_codes (string)", _t("The prefix of the accounts.")),
arg(
"date_range (string, date)",
_t(`The date range. Supported formats are "21/12/2022", "Q1/2022", "12/2022", and "2022".`)
),
arg("offset (number, default=0)", _t("Year offset applied to date_range.")),
arg("company_id (number, optional)", _t("The company to target (Advanced).")),
YEAR_OFFSET_ARG,
COMPANY_ARG,
POSTED_ARG,
];

const ODOO_RESIDUAL_ARGS = () => [
arg(
"account_codes (string, optional)",
_t("The prefix of the accounts. If none provided, all receivable and payable accounts will be used.")
),
arg(
"include_unposted (boolean, default=FALSE)",
_t("Set to TRUE to include unposted entries.")
"date_range (string, date, optional)",
_t(`The date range. Supported formats are "21/12/2022", "Q1/2022", "12/2022", and "2022".`)
),
YEAR_OFFSET_ARG,
COMPANY_ARG,
POSTED_ARG,
];

const ODOO_PARTNER_BALANCE_ARGS = () => {
const partner_arg = arg("partner_ids (string)", _t("The partner ids (separated by a comma)."));
return [partner_arg, ...ODOO_RESIDUAL_ARGS()];
}

functionRegistry.add("ODOO.CREDIT", {
description: _t("Get the total credit for the specified account(s) and period."),
args: ODOO_FIN_ARGS(),
Expand Down Expand Up @@ -335,3 +358,82 @@ functionRegistry.add("ODOO.ACCOUNT.GROUP", {
return accountTypes.join(",");
},
});

functionRegistry.add("ODOO.RESIDUAL", {
description: _t("Return the residual amount for the specified account(s) and period"),
args: ODOO_RESIDUAL_ARGS(),
category: "Odoo",
returns: ["NUMBER"],
compute: function (
accountCodes,
dateRange,
offset = { value: 0 },
companyId = { value: null },
includeUnposted = { value: false }
) {
const _accountCodes = toString(accountCodes)
.split(",")
.map((code) => code.trim())
.sort();
const _offset = toNumber(offset, this.locale);
if ( !dateRange?.value ) {
dateRange = { value: new Date().getFullYear() }
}
const _dateRange = parseAccountingDate(dateRange, this.locale);
const _companyId = toNumber(companyId, this.locale);
const _includeUnposted = toBoolean(includeUnposted);
return {
value: this.getters.getAccountResidual(
_accountCodes,
_dateRange,
_offset,
_companyId,
_includeUnposted
),
format: this.getters.getCompanyCurrencyFormat(_companyId) || "#,##0.00",
};
},
})

functionRegistry.add("ODOO.PARTNER.BALANCE", {
description: _t("Return the partner balance for the specified account(s) and period"),
args: ODOO_PARTNER_BALANCE_ARGS(),
category: "Odoo",
returns: ["NUMBER"],
compute: function (
partnerIds,
accountCodes,
dateRange,
offset = { value: 0 },
companyId = { value: null },
includeUnposted = { value: false }
) {
const _partnerIds = toString(partnerIds)
.split(",")
.map((partnerId) => toNumber(partnerId, this.locale))
.sort();
const _accountCodes = toString(accountCodes)
.split(",")
.map((code) => code.trim())
.sort();
const _offset = toNumber(offset, this.locale);

if ( !dateRange?.value ) {
dateRange = { value: new Date().getFullYear() }
}
const _dateRange = parseAccountingDate(dateRange, this.locale);
const _companyId = toNumber(companyId, this.locale);
const _includeUnposted = toBoolean(includeUnposted);
return {
value: this.getters.getAccountPartnerData(
_accountCodes,
_dateRange,
_offset,
_companyId,
_includeUnposted,
_partnerIds
),
format: this.getters.getCompanyCurrencyFormat(_companyId) || "#,##0.00",
};
},
})
39 changes: 32 additions & 7 deletions addons/spreadsheet_account/static/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,31 @@ cellMenuRegistry.add("move_lines_see_records", {
const position = env.model.getters.getActivePosition();
const sheetId = position.sheetId;
const cell = env.model.getters.getCell(position);
const { args } = getFirstAccountFunction(cell.compiledFormula.tokens);
let [codes, date_range, offset, companyId, includeUnposted] = args
.map(astToFormula)
.map((arg) => env.model.getters.evaluateFormulaResult(sheetId, arg));
codes = toString(codes?.value).split(",");
const func = getFirstAccountFunction(cell.compiledFormula.tokens);
let codes, partner_ids = "";
let date_range, offset, companyId, includeUnposted = false;
const parsed_args = func.args.map(astToFormula).map(
(arg) => env.model.getters.evaluateFormulaResult(sheetId, arg)
);
if ( func.functionName === "ODOO.PARTNER.BALANCE" ) {
[partner_ids, codes, date_range, offset, companyId, includeUnposted] = parsed_args;
} else {
[codes, date_range, offset, companyId, includeUnposted] = parsed_args;
}
if ( codes?.value && !isEvaluationError(codes.value) ) {
codes = toString(codes?.value).split(",").map((code) => code.trim());
} else {
codes = [];
}
const locale = env.model.getters.getLocale();
const dateRange = parseAccountingDate(date_range, locale);
let dateRange;
if ( date_range?.value && !isEvaluationError(date_range.value) ) {
dateRange = parseAccountingDate(date_range, locale);
} else {
if ( ["ODOO.PARTNER.BALANCE", "ODOO.RESIDUAL"].includes(func.functionName) ) {
dateRange = parseAccountingDate({ value: new Date().getFullYear() }, locale);
}
}
offset = parseInt(offset?.value) || 0;
dateRange.year += offset || 0;
companyId = parseInt(companyId?.value) || null;
Expand All @@ -35,11 +53,18 @@ cellMenuRegistry.add("move_lines_see_records", {
} catch {
includeUnposted = false;
}
const partnerIds = toString(partner_ids).split(",").map((code) => code.trim());

let param;
if ( func.functionName === "ODOO.PARTNER.BALANCE" ) {
param = [camelToSnakeObject({ dateRange, companyId, codes, includeUnposted, partnerIds })]
} else {
param = [camelToSnakeObject({ dateRange, companyId, codes, includeUnposted })]
}
const action = await env.services.orm.call(
"account.account",
"spreadsheet_move_line_action",
[camelToSnakeObject({ dateRange, companyId, codes, includeUnposted })]
param
);
await env.services.action.doAction(action);
},
Expand Down
Loading