Browse Source

:Dec 30 [FIX] Bug Fixed 'dynamic_accounts_report'

18.0
Risvana Cybro 2 days ago
parent
commit
b653547137
  1. 2
      dynamic_accounts_report/__manifest__.py
  2. 5
      dynamic_accounts_report/doc/RELEASE_NOTES.md
  3. 125
      dynamic_accounts_report/models/account_trial_balance.py
  4. 2
      dynamic_accounts_report/report/financial_reports_views.xml
  5. 38
      dynamic_accounts_report/report/trial_balance.xml
  6. 97
      dynamic_accounts_report/static/src/js/trial_balance.js
  7. 117
      dynamic_accounts_report/static/src/xml/trial_balance_view.xml

2
dynamic_accounts_report/__manifest__.py

@ -21,7 +21,7 @@
################################################################################
{
'name': 'Odoo18 Dynamic Accounting Reports',
'version': '18.0.1.3.0',
'version': '18.0.1.3.1',
'category': 'Accounting',
'summary': "Odoo 18 Accounting Financial Reports,Dynamic Accounting Reports, Dynamic Financial Reports,Dynamic Report Odoo18, Odoo18,Financial Reports, Odoo18 Accounting,Accounting, Odoo Apps",
'description': "This module creates dynamic Accounting General Ledger, Trial"

5
dynamic_accounts_report/doc/RELEASE_NOTES.md

@ -39,3 +39,8 @@
#### Version 18.0.1.3.0
#### UPDT
- Commit for adding partner, partner tag, and account filter in the Partner Ledger report.
#### 22.12.2025
#### Version 18.0.1.3.1
#### UPDT
- Added Total to the Trial Balance and report. Fixed the filter issue and the date range RPC error.

125
dynamic_accounts_report/models/account_trial_balance.py

@ -22,7 +22,8 @@
import calendar
import io
import json
from datetime import datetime
import logging
from datetime import datetime, timedelta
import xlsxwriter
from odoo import api, fields, models
from odoo.tools.date_utils import get_month, get_fiscal_year, \
@ -90,7 +91,11 @@ class AccountTrialBalance(models.TransientModel):
'journal_ids': self.env['account.journal'].search_read([], [
'name'])
}
return move_line_list, journal
# Calculate totals
totals = self._calculate_totals(move_line_list, None)
return move_line_list, journal, totals
@api.model
def get_filter_values(self, start_date, end_date, comparison_number,
@ -130,6 +135,12 @@ class AccountTrialBalance(models.TransientModel):
account_ids = self.env['account.move.line'].search([]).mapped(
'account_id')
move_line_list = []
# Handle empty dates that can occur with language changes
if not start_date or not end_date:
today = fields.Date.today()
start_date = get_month(today)[0].strftime('%Y-%m-%d')
end_date = get_month(today)[1].strftime('%Y-%m-%d')
start_date_first = \
get_fiscal_year(datetime.strptime(start_date, "%Y-%m-%d").date())[
0] if comparison_type == 'year' else datetime.strptime(
@ -313,7 +324,92 @@ class AccountTrialBalance(models.TransientModel):
f"dynamic_total_credit_{eval(comparison_number) + 1 - i}",
0.0)
move_line_list.append(data)
return move_line_list
# Calculate totals
totals = self._calculate_totals(move_line_list, comparison_number)
return move_line_list, totals
def _calculate_totals(self, move_line_list, comparison_number):
"""
Calculate totals for all monetary fields in the trial balance.
:param move_line_list: List of account data dictionaries
:param comparison_number: Number of comparison periods
:return: Dictionary containing all totals
"""
totals = {
'initial_total_debit': 0.0,
'initial_total_credit': 0.0,
'total_debit': 0.0,
'total_credit': 0.0,
'end_total_debit': 0.0,
'end_total_credit': 0.0
}
# Add dynamic period totals if comparison is enabled
if comparison_number:
for i in range(1, eval(comparison_number) + 1):
totals[f'dynamic_total_debit_{i}'] = 0.0
totals[f'dynamic_total_credit_{i}'] = 0.0
# Helper function to convert value to float
def to_float(value):
if isinstance(value, str):
# Remove commas and convert to float
try:
return float(value.replace(',', ''))
except ValueError:
return 0.0
return float(value) if value is not None else 0.0
# Sum all values
for account_data in move_line_list:
totals['initial_total_debit'] += to_float(account_data.get('initial_total_debit', 0))
totals['initial_total_credit'] += to_float(account_data.get('initial_total_credit', 0))
totals['total_debit'] += to_float(account_data.get('total_debit', 0))
totals['total_credit'] += to_float(account_data.get('total_credit', 0))
totals['end_total_debit'] += to_float(account_data.get('end_total_debit', 0))
totals['end_total_credit'] += to_float(account_data.get('end_total_credit', 0))
if comparison_number:
for i in range(1, eval(comparison_number) + 1):
totals[f'dynamic_total_debit_{i}'] += to_float(account_data.get(f'dynamic_total_debit_{i}', 0))
totals[f'dynamic_total_credit_{i}'] += to_float(account_data.get(f'dynamic_total_credit_{i}', 0))
# Format totals as strings with commas and 2 decimal places
for key in totals:
totals[key] = "{:,.2f}".format(totals[key])
return totals
def _get_report_values(self, docids, data=None):
"""
Get the report values for the trial balance PDF report.
This method is called by the Odoo report engine.
"""
if not data:
data = {}
# Extract data from the data dictionary passed from JavaScript
report_data = data.get('data', [])
totals = data.get('totals', {})
date_viewed = data.get('date_viewed', [])
filters = data.get('filters', {})
apply_comparison = data.get('apply_comparison', False)
comparison_number_range = data.get('comparison_number_range', [])
title = data.get('title', 'Trial Balance')
report_name = data.get('report_name', 'Trial Balance')
return {
'data': report_data,
'totals': totals,
'date_viewed': date_viewed,
'filters': filters,
'apply_comparison': apply_comparison,
'comparison_number_range': comparison_number_range,
'title': title,
'report_name': report_name,
}
@api.model
def get_month_name(self, date):
@ -446,6 +542,29 @@ class AccountTrialBalance(models.TransientModel):
sheet.write(row, col + j + 3,
move_line['end_total_credit'], txt_name)
row += 1
# Add totals row
if 'totals' in data and data['totals']:
totals = data['totals']
# Create bold format for totals
totals_format = workbook.add_format(
{'bold': True, 'bg_color': '#E0E0E0', 'border': 1})
sheet.write(row, col, 'Total', totals_format)
sheet.write(row, col + 1, totals.get('initial_total_debit', '0.00'),
totals_format)
sheet.write(row, col + 2, totals.get('initial_total_credit', '0.00'), totals_format)
j = 3
if data['apply_comparison']:
number_of_periods = data['comparison_number_range']
for num in number_of_periods:
sheet.write(row, col + j, totals.get(f'dynamic_total_debit_{num}', '0.00'), totals_format)
sheet.write(row, col + j + 1, totals.get(f'dynamic_total_credit_{num}', '0.00'), totals_format)
j += 2
sheet.write(row, col + j, totals.get('total_debit', '0.00'), totals_format)
sheet.write(row, col + j + 1, totals.get('total_credit', '0.00'), totals_format)
sheet.write(row, col + j + 2, totals.get('end_total_debit', '0.00'), totals_format)
sheet.write(row, col + j + 3, totals.get('end_total_credit', '0.00'), totals_format)
workbook.close()
output.seek(0)
response.stream.write(output.read())

2
dynamic_accounts_report/report/financial_reports_views.xml

@ -63,6 +63,8 @@
<field name="report_type">qweb-pdf</field>
<field name="report_name">dynamic_accounts_report.trial_balance</field>
<field name="report_file">dynamic_accounts_report.trial_balance</field>
<field name="binding_model_id" ref="model_account_trial_balance"/>
<field name="binding_type">report</field>
</record>
<record id="action_print_tax_report" model="ir.actions.report">
<field name="name">Tax Report</field>

38
dynamic_accounts_report/report/trial_balance.xml

@ -162,6 +162,44 @@
</th>
</tr>
</t>
<!-- Totals Row -->
<t t-if="totals">
<tr class="border-bottom"
style="border-spacing: 0 10px;color:#000;font-weight:bold;">
<th colspan="6">Total</th>
<th style="text-align:center;">
<t t-esc="totals.get('initial_total_debit', '0.00')"/>
</th>
<th style="text-align:center;">
<t t-esc="totals.get('initial_total_credit', '0.00')"/>
</th>
<t t-if="apply_comparison == true">
<t t-set="number_of_periods"
t-value="comparison_number_range"/>
<t t-foreach="number_of_periods"
t-as="nb" t-key="nb">
<th style="text-align:center;">
<t t-esc="totals.get('dynamic_total_debit_' + nb, '0.00')"/>
</th>
<th style="text-align:center;">
<t t-esc="totals.get('dynamic_total_credit_' + nb, '0.00')"/>
</th>
</t>
</t>
<th style="text-align:center;">
<t t-esc="totals.get('total_debit', '0.00')"/>
</th>
<th style="text-align:center;">
<t t-esc="totals.get('total_credit', '0.00')"/>
</th>
<th style="text-align:center;">
<t t-esc="totals.get('end_total_debit', '0.00')"/>
</th>
<th style="text-align:center;">
<t t-esc="totals.get('end_total_credit', '0.00')"/>
</th>
</tr>
</t>
</t>
</tbody>
</table>

97
dynamic_accounts_report/static/src/js/trial_balance.js

@ -24,7 +24,7 @@ class TrialBalance extends owl.Component {
move_line: null,
default_report: true,
data: null,
total: null,
totals: null,
journals: null,
accounts: null,
selected_analytic: [],
@ -38,6 +38,8 @@ class TrialBalance extends owl.Component {
date_viewed: [],
comparison_number: null,
options: null,
has_filters: false,
filter_version: 0, // Add this to force re-render
method: {
'accural': true
},
@ -46,7 +48,8 @@ class TrialBalance extends owl.Component {
}
async load_data() {
/**
* Loads the data for the trial balance report.
* Asynchronously loads initial data for the trial balance report.
* Fetches default trial balance data and populates the component state.
*/
let move_line_list = []
let move_lines_total = ''
@ -57,12 +60,17 @@ class TrialBalance extends owl.Component {
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", []);
let result = await self.orm.call("account.trial.balance", "view_report", []);
self.state.data = result[0];
self.state.totals = result[2];
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())
self.state.journals = self.state.data[1]['journal_ids']
self.state.accounts = self.state.data[0]
self.state.journals = result[1]['journal_ids']
self.state.accounts = self.state.data
// Reset to default report state when loading initial data
self.state.default_report = true;
self.state.has_filters = false; // Reset filters flag
$.each(self.state.data, function (index, value) {
self.state.journals = value.journal_ids
})
@ -73,13 +81,8 @@ class TrialBalance extends owl.Component {
}
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.
* Asynchronously applies filters to the trial balance report.
* Fetches data based on selected filters and updates the component state.
* 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.
@ -89,10 +92,17 @@ class TrialBalance extends owl.Component {
* @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.
*/
// Set filter flags for ANY filter operation (including date changes)
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1; // Force re-render
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)
@ -129,6 +139,10 @@ class TrialBalance extends owl.Component {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
// Set filter flags for date range selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} 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')
@ -140,17 +154,25 @@ class TrialBalance extends owl.Component {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
// Set filter flags for date range selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} 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.comparison_type = this.state.date_type
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
// Set filter flags for date range selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} 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')
@ -158,10 +180,15 @@ class TrialBalance extends owl.Component {
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
console.log("amrutha test")
this.state.date_range = {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
// Set filter flags for date range selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} 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')
@ -173,6 +200,10 @@ class TrialBalance extends owl.Component {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
// Set filter flags for date range selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} 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')
@ -184,6 +215,10 @@ class TrialBalance extends owl.Component {
start_date: this.start_date.el.value,
end_date: this.end_date.el.value
};
// Set filter flags for date range selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} 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))
@ -193,6 +228,10 @@ class TrialBalance extends owl.Component {
this.state.selected_journal_list = updatedList
val.target.classList.remove("selected-filter");
}
// Set filter flags for journal selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
} else if (val && val.target.attributes["data-value"].value === 'draft') {
if (val.target.classList.contains("selected-filter")) {
const { draft, ...updatedAccount } = this.state.options;
@ -205,18 +244,22 @@ class TrialBalance extends owl.Component {
};
val.target.classList.add("selected-filter");
}
// Set filter flags for draft selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
}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");
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 {
this.state.method = {
...this.state.method,
'cash': true
};
val.target.classList.add("selected-filter");
var current_year = new Date(today).getFullYear();
var month = new Date(today).getMonth()
}
// Set filter flags for cash-basis selection
this.state.has_filters = true;
this.state.default_report = false;
this.state.filter_version += 1;
}
}
if (this.state.apply_comparison == true) {
@ -243,7 +286,8 @@ class TrialBalance extends owl.Component {
}
}
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,]);
this.state.default_report = false
this.state.totals = this.state.data[1];
this.state.data = this.state.data[0];
var date_viewed = []
if (date_viewed.length !== 0) {
this.state.date_viewed = date_viewed.reverse()
@ -297,7 +341,7 @@ class TrialBalance extends owl.Component {
}
sumByKey(data, key) {
if (!Array.isArray(data)) return 0;
return data.reduce((acc, item) => {
const total = data.reduce((acc, item) => {
let raw = item[key];
if (typeof raw === 'string') {
raw = raw.replace(/,/g, ''); // remove commas
@ -305,6 +349,8 @@ class TrialBalance extends owl.Component {
const val = parseFloat(raw);
return acc + (isNaN(val) ? 0 : val);
}, 0);
// Format with commas and 2 decimal places
return total.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
get comparison_number_range() {
/**
@ -373,6 +419,7 @@ class TrialBalance extends owl.Component {
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);
@ -386,6 +433,7 @@ class TrialBalance extends owl.Component {
'report_file': 'dynamic_accounts_report.trial_balance',
'data': {
'data': this.normalizeReportData(self.state.data),
'totals': self.state.totals,
'date_viewed': data_viewed,
'filters': this.filter(),
'apply_comparison': self.state.apply_comparison,
@ -470,6 +518,7 @@ class TrialBalance extends owl.Component {
var action_title = self.props.action.display_name;
var datas = {
'data': this.normalizeReportData(self.state.data),
'totals': self.state.totals,
'date_viewed': self.state.date_viewed,
'filters': this.filter(),
'apply_comparison': self.state.apply_comparison,

117
dynamic_accounts_report/static/src/xml/trial_balance_view.xml

@ -384,6 +384,48 @@
</th>
</tr>
</t>
<!-- Totals Row - Show for filtered data when data exists -->
<t t-if="state.data and state.data.length > 0">
<tr class="border-bottom"
style="border-spacing: 0 10px;color:#000;font-weight:bold;background-color:#f8f9fa;">
<th colspan="6">Total</th>
<!-- Initial Balance Totals -->
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'initial_total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'initial_total_credit')"/>
</th>
<!-- Dynamic Period Totals (when comparison is applied) -->
<t t-if="state.apply_comparison == true">
<t t-set="number_of_periods"
t-value="comparison_number_range"/>
<t t-foreach="number_of_periods"
t-as="num" t-key="num">
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'dynamic_total_debit_' + num)"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'dynamic_total_credit_' + num)"/>
</th>
</t>
</t>
<!-- Current Period Totals -->
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'total_credit')"/>
</th>
<!-- End Balance Totals -->
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'end_total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'end_total_credit')"/>
</th>
</tr>
</t>
</t>
</t>
<t t-if="this.state.default_report == true">
@ -462,41 +504,48 @@
</th>
</tr>
</t>
<tr class="border-bottom"
style="border-spacing: 0 10px;color:#000">
<th colspan="6">Total</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'initial_total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'initial_total_credit')"/>
</th>
<t t-if="state.apply_comparison == true">
<t t-set="number_of_periods"
t-value="comparison_number_range"/>
<t t-foreach="number_of_periods"
t-as="nb" t-key="nb">
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'dynamic_total_debit_' + nb)"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'dynamic_total_credit_' + nb)"/>
</th>
<!-- Totals Row - Show for all cases when data exists -->
<t t-if="state.data and state.data.length > 0">
<tr class="border-bottom"
style="border-spacing: 0 10px;color:#000;font-weight:bold;background-color:#f8f9fa;">
<th colspan="6">Total</th>
<!-- Initial Balance Totals -->
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'initial_total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'initial_total_credit')"/>
</th>
<!-- Dynamic Period Totals (when comparison is applied) -->
<t t-if="state.apply_comparison == true">
<t t-set="number_of_periods"
t-value="comparison_number_range"/>
<t t-foreach="number_of_periods"
t-as="num" t-key="num">
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'dynamic_total_debit_' + num)"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'dynamic_total_credit_' + num)"/>
</th>
</t>
</t>
</t>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'total_credit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'end_total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.accounts, 'end_total_credit')"/>
</th>
</tr>
<!-- Current Period Totals -->
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'total_credit')"/>
</th>
<!-- End Balance Totals -->
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'end_total_debit')"/>
</th>
<th style="text-align:center;">
<t t-esc="sumByKey(state.data, 'end_total_credit')"/>
</th>
</tr>
</t>
</t>
</t>
</tbody>

Loading…
Cancel
Save