You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

529 lines
26 KiB

/** @odoo-module */
const { Component } = owl;
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { useRef, useState } from "@odoo/owl";
import { BlockUI } from "@web/core/ui/block_ui";
import { download } from "@web/core/network/download";
const actionRegistry = registry.category("actions");
const today = luxon.DateTime.now();
let monthNamesShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
class TrialBalance extends owl.Component {
async setup() {
super.setup(...arguments);
this.initial_render = true;
this.orm = useService('orm');
this.action = useService('action');
this.tbody = useRef('tbody');
this.end_date = useRef('date_to');
this.start_date = useRef('date_from');
this.period = useRef('periods');
this.period_year = useRef('period_year');
this.unfoldButton = useRef('unfoldButton');
this.state = useState({
move_line: null,
data: null,
total: null,
journals: null,
selected_analytic: [],
analytic_account: null,
selected_journal_list: [],
selected_analytic_account_rec: [],
date_range: 'month',
date_type: 'month',
apply_comparison: false,
comparison_type: null,
date_viewed: [],
comparison_number: null,
options: null,
method: {
'accural': true
},
});
this.load_data(self.initial_render = true);
}
async load_data() {
/**
* Loads the data for the trial balance report.
*/
let move_line_list = []
let move_lines_total = ''
var self = this;
var action_title = self.props.action.display_name;
try {
var self = this;
var today = new Date();
var startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
var endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0);
self.state.data = await self.orm.call("account.trial.balance", "view_report", []);
self.start_date.el.value = startOfMonth.getFullYear() + '-' + String(startOfMonth.getMonth() + 1).padStart(2, '0') + '-' + String(startOfMonth.getDate()).padStart(2, '0');
self.end_date.el.value = endOfMonth.getFullYear() + '-' + String(endOfMonth.getMonth() + 1).padStart(2, '0') + '-' + String(endOfMonth.getDate()).padStart(2, '0');
self.state.date_viewed.push(monthNamesShort[today.getMonth()] + ' ' + today.getFullYear())
$.each(self.state.data, function (index, value) {
self.state.journals = value.journal_ids
})
}
catch (el) {
window.location.href;
}
}
async applyFilter(val, ev, is_delete) {
/**
* Asynchronously applies filters and loads data for the trial balance report.
* Modifies state variables based on the selected filter options.
* Updates 'move_line_list' and 'move_lines_total'.
* Sets the start and end date inputs based on selected filter options.
* Appends the selected date ranges to 'state.date_viewed'.
* Updates 'state.journals' based on the selected journal filter.
* Retrieves data using the 'account.trial.balance' API with the selected filter values.
* Updates 'state.data' with the retrieved data.
* Handles the comparison logic and updates 'state.date_viewed' accordingly.
* Reverses the order of 'state.date_viewed' if data exists.
*
* @param {any} val - The selected filter value or event.
* @param {Event} ev - The event object triggered by the filter.
* @param {boolean} is_delete - Flag indicating if the filter is being deleted.
* @returns {Promise<void>} Resolves when the filter is applied and data is loaded.
*/
if (ev && ev.target && ev.target.attributes["data-value"] && ev.target.attributes["data-value"].value == 'no comparison') {
const lastIndex = this.state.date_viewed.length - 1;
this.state.date_viewed.splice(0, lastIndex);
}
if (ev) {
if (ev.input && ev.input.attributes.placeholder.value == 'Account' && !is_delete) {
this.state.selected_analytic.push(val[0].id)
this.state.selected_analytic_account_rec.push(val[0])
} else if (is_delete) {
let index = this.state.selected_analytic_account_rec.indexOf(val)
this.state.selected_analytic_account_rec.splice(index, 1)
this.state.selected_analytic = this.state.selected_analytic_account_rec.map((rec) => rec.id)
}
}
else {
if (val && val.target.name === 'start_date') {
this.state.date_viewed = []
this.state.date_viewed.push('From' + ' ' + this.formatDate(this.start_date.el.value) + ' ' + 'To' + ' ' + this.formatDate(this.end_date.el.value))
this.state.date_range = {
...this.state.date_range,
start_date: val.target.value
};
} else if (val && val.target.name === 'end_date') {
this.state.date_viewed = []
this.state.date_viewed.push('From' + ' ' + this.formatDate(this.start_date.el.value) + 'To' + ' ' + this.formatDate(this.end_date.el.value))
this.state.date_range = {
...this.state.date_range,
end_date: val.target.value
};
} else if (val && val.target.attributes["data-value"].value == 'month') {
this.start_date.el.value = today.startOf('month').toFormat('yyyy-MM-dd')
this.end_date.el.value = today.endOf('month').toFormat('yyyy-MM-dd')
this.state.date_viewed = []
this.state.date_viewed.push(today.monthShort + ' ' + today.c.year)
this.state.date_type = val.target.attributes["data-value"].value
this.state.comparison_type = this.state.date_type
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
} else if (val && val.target.attributes["data-value"].value == 'year') {
this.start_date.el.value = today.startOf('year').toFormat('yyyy-MM-dd')
this.end_date.el.value = today.endOf('year').toFormat('yyyy-MM-dd')
this.state.date_viewed = []
this.state.date_viewed.push(today.c.year)
this.state.date_type = val.target.attributes["data-value"].value
this.state.comparison_type = this.state.date_type
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
} else if (val && val.target.attributes["data-value"].value == 'quarter') {
this.start_date.el.value = today.startOf('quarter').toFormat('yyyy-MM-dd')
this.end_date.el.value = today.endOf('quarter').toFormat('yyyy-MM-dd')
this.state.date_viewed = []
this.state.date_viewed.push('Q' + ' ' + today.quarter)
this.state.comparison_type = this.state.date_type
this.state.date_type = val.target.attributes["data-value"].value
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
} else if (val && val.target.attributes["data-value"].value == 'last-month') {
this.start_date.el.value = today.startOf('month').minus({ days: 1 }).startOf('month').toFormat('yyyy-MM-dd')
this.end_date.el.value = today.startOf('month').minus({ days: 1 }).toFormat('yyyy-MM-dd')
this.state.date_viewed = []
this.state.date_viewed.push(today.startOf('month').minus({ days: 1 }).monthShort + ' ' + today.startOf('month').minus({ days: 1 }).c.year)
this.state.date_type = 'month'
this.state.comparison_type = this.state.date_type
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
} else if (val && val.target.attributes["data-value"].value == 'last-year') {
this.start_date.el.value = today.startOf('year').minus({ days: 1 }).startOf('year').toFormat('yyyy-MM-dd')
this.end_date.el.value = today.startOf('year').minus({ days: 1 }).toFormat('yyyy-MM-dd')
this.state.date_viewed = []
this.state.date_viewed.push(today.startOf('year').minus({ days: 1 }).c.year)
this.state.date_type = 'year'
this.state.comparison_type = this.state.date_type
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
} else if (val && val.target.attributes["data-value"].value == 'last-quarter') {
this.start_date.el.value = today.startOf('quarter').minus({ days: 1 }).startOf('quarter').toFormat('yyyy-MM-dd')
this.end_date.el.value = today.startOf('quarter').minus({ days: 1 }).toFormat('yyyy-MM-dd')
this.state.date_viewed = []
this.state.date_viewed.push('Q' + ' ' + today.startOf('quarter').minus({ days: 1 }).quarter)
this.state.date_type = 'quarter'
this.state.comparison_type = this.state.date_type
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
} else if (val && val.target.attributes["data-value"].value == 'journal') {
if (!val.target.classList.contains("selected-filter")) {
this.state.selected_journal_list.push(parseInt(val.target.attributes["data-id"].value, 10))
val.target.classList.add("selected-filter");
} else {
const updatedList = this.state.selected_journal_list.filter(item => item !== parseInt(val.target.attributes["data-id"].value, 10));
this.state.selected_journal_list = updatedList
val.target.classList.remove("selected-filter");
}
} else if (val && val.target.attributes["data-value"].value === 'draft') {
if (val.target.classList.contains("selected-filter")) {
const { draft, ...updatedAccount } = this.state.options;
this.state.options = updatedAccount;
val.target.classList.remove("selected-filter");
} else {
this.state.options = {
...this.state.options,
'draft': true
};
val.target.classList.add("selected-filter");
}
}else if (val.target.attributes["data-value"].value === 'cash-basis') {
if (val.target.classList.contains("selected-filter")) {
const { cash, ...updatedAccount } = this.state.method;
this.state.method = updatedAccount;
val.target.classList.remove("selected-filter");
} else {
this.state.method = {
...this.state.method,
'cash': true
};
val.target.classList.add("selected-filter");
}
}
}
if (this.state.apply_comparison == true) {
if (this.state.comparison_type == 'year') {
this.state.date_viewed = []
if (this.start_date.el.value) {
var current_year = new Date(this.start_date.el.value).getFullYear();
var month = new Date(this.start_date.el.value).getMonth();
} else {
var current_year = new Date(today).getFullYear();
var month = new Date(today).getMonth()
}
this.state.comparison_number = this.period_year.el.value
for (var i = this.state.comparison_number; i >= 0; i--) {
var date = monthNamesShort[month] + ' ' + (current_year - i);
this.state.date_viewed.push(date);
}
} else if (this.state.comparison_type == 'month') {
this.state.date_viewed = []
this.state.comparison_number = this.period.el.value
} else if (this.state.comparison_type == 'quarter') {
this.state.date_viewed = []
this.state.comparison_number = this.period.el.value
}
}
this.state.data = await this.orm.call("account.trial.balance", "get_filter_values", [this.start_date.el.value, this.end_date.el.value, this.state.comparison_number, this.state.comparison_type, this.state.selected_journal_list, this.state.selected_analytic, this.state.options,this.state.method,]);
var date_viewed = []
this.state.data.forEach((value, index) => {
if (index == 'journal_ids') {
this.state.journals = value
}
if (value.dynamic_date_num) {
let iterable = Array.isArray(value.dynamic_date_num) ? value.dynamic_date_num
: Object.values(value.dynamic_date_num);
for (const date_num of iterable) {
if (!date_viewed.includes(date_num)) {
date_viewed.push(date_num);
}
}
}
})
if (date_viewed.length !== 0) {
this.state.date_viewed = date_viewed.reverse()
}
}
onPeriodChange(ev) {
/**
* Event handler for period change.
* Updates the value of 'period_year' element based on the event target value.
*
* @param {Event} ev - Event object triggered by the period change.
* @returns {void} No explicit return value.
*/
this.period_year.el.value = ev.target.value
}
onPeriodYearChange(ev) {
/**
* Event handler for period year change.
* Updates the value of 'period' element based on the event target value.
*
* @param {Event} ev - Event object triggered by the period year change.
* @returns {void} No explicit return value.
*/
this.period.el.value = ev.target.value
}
applyComparisonPeriod(ev) {
/**
* Applies a comparison period and triggers data filtering.
* Sets 'apply_comparison' flag to true and updates 'comparison_type'.
* Invokes 'applyFilter' method to apply filters and load data.
*
* @param {Event} ev - Event object triggering the comparison period application.
* @returns {void} No explicit return value.
*/
this.state.apply_comparison = true
this.state.comparison_type = this.state.date_type
this.applyFilter(null, ev)
}
applyComparisonYear(ev) {
/**
* Applies a comparison year and triggers data filtering.
* Sets 'apply_comparison' flag to true and updates 'comparison_type' to 'year'.
* Invokes 'applyFilter' method to apply filters and load data.
*
* @param {Event} ev - Event object triggering the comparison year application.
* @returns {void} No explicit return value.
*/
this.state.apply_comparison = true
this.state.comparison_type = 'year'
this.applyFilter(null, ev)
}
sumByKey(data, key) {
/**
* Calculates the sum of values in an array of objects by a specified key.
*
* @param {Array} data - Array of objects containing numeric values.
* @param {string} key - The key to access the numeric value in each object.
* @returns {number} The sum of the numeric values.
*/
return data.reduce((acc, item) => acc + (item[key] || 0), 0);
}
get comparison_number_range() {
/**
* Generates an array of numbers representing a comparison number range.
* The range includes numbers from 1 up to the 'comparison_number' value in the state.
*
* @returns {Array} An array of numbers representing the comparison number range.
*/
const range = [];
for (let i = 1; i <= this.state.comparison_number; i++) {
range.push(i);
}
return range;
}
async applyComparison(ev) {
/**
* Disables comparison mode, resets comparison settings, and triggers data filtering.
* Sets 'apply_comparison' flag to false and clears 'comparison_type' and 'comparison_number'.
* Removes all date view entries except the current month/year.
* Invokes 'applyFilter' method to apply filters and load data.
*
* @param {Event} ev - Event object triggering the comparison mode removal.
* @returns {void} No explicit return value.
*/
this.state.apply_comparison = false
this.state.comparison_type = null
this.state.comparison_number = null
const lastIndex = this.state.date_viewed.length - 1;
this.state.date_viewed.splice(0, lastIndex);
this.applyFilter(null, ev)
}
getDomain() {
return [];
}
async printPdf(ev) {
/**
* Asynchronously generates and prints a PDF report.
* Triggers an action to generate a PDF report based on the current state and settings.
*
* @param {Event} ev - Event object triggering the PDF report generation.
* @returns {Promise} A promise that resolves after the PDF report action is triggered.
*/
ev.preventDefault();
var self = this;
var action_title = self.props.action.display_name;
let comparison_number_range = self.comparison_number_range
let data_viewed = self.state.date_viewed
if (self.state.apply_comparison) {
if (self.comparison_number_range.length > 10) {
comparison_number_range = self.comparison_number_range.slice(-10);
data_viewed = self.state.date_viewed.slice(-11);
}
}
return self.action.doAction({
'type': 'ir.actions.report',
'report_type': 'qweb-pdf',
'report_name': 'dynamic_accounts_report.trial_balance',
'report_file': 'dynamic_accounts_report.trial_balance',
'data': {
'data': self.state.data,
'date_viewed': data_viewed,
'filters': this.filter(),
'apply_comparison': self.state.apply_comparison,
'comparison_number_range': comparison_number_range,
'title': action_title,
'report_name': self.props.action.display_name
},
'display_name': self.props.action.display_name,
});
}
filter() {
var self=this;
let startDate, endDate;
let startYear, startMonth, startDay, endYear, endMonth, endDay;
if (self.state.date_range){
const today = new Date();
if (self.state.date_range === 'year') {
startDate = new Date(today.getFullYear(), 0, 1);
endDate = new Date(today.getFullYear(), 11, 31);
} else if (self.state.date_range === 'quarter') {
const currentQuarter = Math.floor(today.getMonth() / 3);
startDate = new Date(today.getFullYear(), currentQuarter * 3, 1);
endDate = new Date(today.getFullYear(), (currentQuarter + 1) * 3, 0);
} else if (self.state.date_range === 'month') {
startDate = new Date(today.getFullYear(), today.getMonth(), 1);
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0);
} else if (self.state.date_range === 'last-month') {
startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1);
endDate = new Date(today.getFullYear(), today.getMonth(), 0);
} else if (self.state.date_range === 'last-year') {
startDate = new Date(today.getFullYear() - 1, 0, 1);
endDate = new Date(today.getFullYear() - 1, 11, 31);
} else if (self.state.date_range === 'last-quarter') {
const lastQuarter = Math.floor((today.getMonth() - 3) / 3);
startDate = new Date(today.getFullYear(), lastQuarter * 3, 1);
endDate = new Date(today.getFullYear(), (lastQuarter + 1) * 3, 0);
}
// Get the date components for start and end dates
if (startDate) {
startYear = startDate.getFullYear();
startMonth = startDate.getMonth() + 1;
startDay = startDate.getDate();
}
if (endDate) {
endYear = endDate.getFullYear();
endMonth = endDate.getMonth() + 1;
endDay = endDate.getDate();
}
}
const selectedJournalIDs = Object.values(self.state.selected_journal_list);
const selectedJournalNames = selectedJournalIDs.map((journalID) => {
const journal = self.state.journals[journalID];
return journal ? journal.name : ''; // Return the name if journal exists, otherwise an empty string
});
let filters = {
'journal': selectedJournalNames,
'account': self.state.selected_analytic_account_rec,
'options': self.state.options,
'comparison_type': self.state.comparison_type,
'comparison_number_range': self.state.comparison_number,
'start_date': null,
'end_date': null,
};
// Check if start and end dates are available before adding them to the filters object
if (startYear !== undefined && startMonth !== undefined && startDay !== undefined &&
endYear !== undefined && endMonth !== undefined && endDay !== undefined) {
filters['start_date'] = `${startYear}-${startMonth < 10 ? '0' : ''}${startMonth}-${startDay < 10 ? '0' : ''}${startDay}`;
filters['end_date'] = `${endYear}-${endMonth < 10 ? '0' : ''}${endMonth}-${endDay < 10 ? '0' : ''}${endDay}`;
}
return filters
}
async print_xlsx() {
/**
* Asynchronously generates and downloads an XLSX report.
* Triggers an action to generate an XLSX report based on the current state and settings,
* and initiates the download of the generated XLSX file.
*
* @returns {void} No explicit return value.
*/
var self = this;
var action_title = self.props.action.display_name;
var datas = {
'data': self.state.data,
'date_viewed': self.state.date_viewed,
'filters': this.filter(),
'apply_comparison': self.state.apply_comparison,
'comparison_number_range': self.comparison_number_range,
'title': action_title,
'report_name': self.props.action.display_name
}
var action = {
'data': {
'model': 'account.trial.balance',
'data': JSON.stringify(datas),
'output_format': 'xlsx',
'report_action': self.props.action.xml_id,
'report_name': action_title,
},
};
BlockUI;
await download({
url: '/xlsx_report',
data: action.data,
complete: () => unblockUI,
error: (error) => self.call('crash_manager', 'rpc_error', error),
});
}
async show_gl(ev) {
/**
* Shows the General Ledger view by triggering an action.
*
* @param {Event} ev - The event object triggered by the action.
* @returns {Promise} - A promise that resolves to the result of the action.
*/
return this.action.doAction({
type: 'ir.actions.client',
name: 'General Ledger',
tag: 'gen_l',
});
}
formatDate(dateString) {
/**
* Formats a date string in "YYYY-MM-DD" format to "DD/MM/YYYY" format.
*
* @param {string} dateString - The date string to be formatted.
* @returns {string} The formatted date in "DD/MM/YYYY" format.
*/
const date = new Date(dateString);
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear();
return `${day}/${month}/${year}`;
}
gotoJournalItem(ev) {
/**
* Navigates to the journal items list view based on the selected event target.
*
* @param {Event} ev - The event object triggered by the action.
* @returns {Promise} - A promise that resolves to the result of the action.
*/
return this.action.doAction({
type: "ir.actions.act_window",
res_model: 'account.move.line',
name: "Journal Items",
views: [[false, "list"]],
domain: [["account_id", "=", parseInt(ev.target.attributes["data-id"].value, 10)]],
context: { group_by: ["account_id"] },
target: "current",
});
}
}
TrialBalance.template = 'trl_b_template_new';
actionRegistry.add("trl_b", TrialBalance);