diff --git a/partner_statement/README.rst b/partner_statement/README.rst index fd97708f..f269d4af 100644 --- a/partner_statement/README.rst +++ b/partner_statement/README.rst @@ -114,6 +114,7 @@ Contributors * Miquel Raïch * Graeme Gellatly * Lois Rilo +* Christopher Ormaza Maintainers ~~~~~~~~~~~ diff --git a/partner_statement/__manifest__.py b/partner_statement/__manifest__.py index 5dafa7e1..8cafadad 100644 --- a/partner_statement/__manifest__.py +++ b/partner_statement/__manifest__.py @@ -9,7 +9,7 @@ "author": "ForgeFlow, Odoo Community Association (OCA)", "website": "https://github.com/OCA/account-financial-reporting", "license": "AGPL-3", - "depends": ["account"], + "depends": ["account", "report_xlsx", "report_xlsx_helper"], "external_dependencies": {"python": ["dateutil"]}, "data": [ "security/ir.model.access.csv", diff --git a/partner_statement/report/__init__.py b/partner_statement/report/__init__.py index d1df1e8a..fd311a65 100644 --- a/partner_statement/report/__init__.py +++ b/partner_statement/report/__init__.py @@ -1,3 +1,5 @@ from . import report_statement_common from . import activity_statement from . import outstanding_statement +from . import activity_statement_xlsx +from . import outstanting_statement_xlsx diff --git a/partner_statement/report/activity_statement_xlsx.py b/partner_statement/report/activity_statement_xlsx.py new file mode 100644 index 00000000..fd6ba7fa --- /dev/null +++ b/partner_statement/report/activity_statement_xlsx.py @@ -0,0 +1,249 @@ +# Author: Christopher Ormaza +# Copyright 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, fields, models + + +class ActivityStatementXslx(models.AbstractModel): + _name = "report.p_s.report_activity_statement_xlsx" + _description = "Activity Statement XLSL Report" + _inherit = "report.report_xlsx.abstract" + + def _get_report_name(self, report, data=False): + company_id = data.get("company_id", False) + report_name = _("Activity Statement") + if company_id: + company = self.env["res.company"].browse(company_id) + suffix = " - {} - {}".format(company.name, company.currency_id.name) + report_name = report_name + suffix + return report_name + + def _write_currency_lines(self, row_pos, sheet, partner, currency, data): + partner_data = data.get("data", {}).get(partner.id, {}) + currency_data = partner_data.get("currencies", {}).get(currency.id) + account_type = data.get("account_type", False) + row_pos += 2 + statement_header = _( + "%sStatement between %s and %s in %s" + % ( + account_type == "payable" and _("Supplier ") or "", + partner_data.get("start"), + partner_data.get("end"), + currency.display_name, + ) + ) + sheet.merge_range( + row_pos, 0, row_pos, 6, statement_header, self.format_right_bold + ) + row_pos += 1 + sheet.write( + row_pos, 0, _("Reference Number"), self.format_theader_yellow_center + ) + sheet.write(row_pos, 1, _("Date"), self.format_theader_yellow_center) + sheet.merge_range( + row_pos, 2, row_pos, 4, _("Description"), self.format_theader_yellow_center + ) + sheet.write(row_pos, 5, _("Open Amount"), self.format_theader_yellow_center) + sheet.write(row_pos, 6, _("Balance"), self.format_theader_yellow_center) + row_pos += 1 + sheet.write(row_pos, 1, partner_data.get("start"), self.format_tcell_date_left) + sheet.merge_range( + row_pos, 2, row_pos, 4, _("Balance Forward"), self.format_tcell_left + ) + sheet.write( + row_pos, 6, currency_data.get("balance_forward"), self.current_money_format + ) + for line in currency_data.get("lines"): + row_pos += 1 + name_to_show = ( + line.get("name", "") == "/" or not line.get("name", "") + ) and line.get("ref", "") + if line.get("name", "") != "/": + if not line.get("ref", ""): + name_to_show = line.get("name", "") + else: + if (line.get("name", "") in line.get("ref", "")) or ( + line.get("name", "") == line.get("ref", "") + ): + name_to_show = line.get("name", "") + elif line.get("ref", "") not in line.get("name", ""): + name_to_show = line.get("ref", "") + sheet.write(row_pos, 0, line.get("move_id", ""), self.format_tcell_left) + sheet.write(row_pos, 1, line.get("date", ""), self.format_tcell_date_left) + sheet.merge_range( + row_pos, 2, row_pos, 4, name_to_show, self.format_distributed + ) + sheet.write(row_pos, 5, line.get("amount", ""), self.current_money_format) + sheet.write(row_pos, 6, line.get("balance", ""), self.current_money_format) + row_pos += 1 + sheet.write(row_pos, 1, partner_data.get("end"), self.format_tcell_date_left) + sheet.merge_range( + row_pos, 2, row_pos, 4, _("Ending Balance"), self.format_tcell_left + ) + sheet.write( + row_pos, 6, currency_data.get("amount_due"), self.current_money_format + ) + return row_pos + + def _write_currency_buckets(self, row_pos, sheet, partner, currency, data): + report_model = self.env["report.partner_statement.activity_statement"] + partner_data = data.get("data", {}).get(partner.id, {}) + currency_data = partner_data.get("currencies", {}).get(currency.id) + if currency_data.get("buckets"): + row_pos += 2 + buckets_header = _("Aging Report at %s in %s") % ( + partner_data.get("end"), + currency.display_name, + ) + sheet.merge_range( + row_pos, 0, row_pos, 6, buckets_header, self.format_right_bold + ) + buckets_data = currency_data.get("buckets") + buckets_labels = report_model._get_bucket_labels( + partner_data.get("end"), data.get("aging_type") + ) + row_pos += 1 + for i in range(len(buckets_labels)): + sheet.write( + row_pos, i, buckets_labels[i], self.format_theader_yellow_center + ) + row_pos += 1 + sheet.write( + row_pos, 0, buckets_data.get("current", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 1, buckets_data.get("b_1_30", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 2, buckets_data.get("b_30_60", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 3, buckets_data.get("b_60_90", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 4, buckets_data.get("b_90_120", 0.0), self.current_money_format + ) + sheet.write( + row_pos, + 5, + buckets_data.get("b_over_120", 0.0), + self.current_money_format, + ) + sheet.write( + row_pos, 6, buckets_data.get("balance", 0.0), self.current_money_format + ) + return row_pos + + def _size_columns(self, sheet): + for i in range(7): + sheet.set_column(0, i, 20) + + def generate_xlsx_report(self, workbook, data, objects): + report_model = self.env["report.partner_statement.activity_statement"] + self._define_formats(workbook) + self.format_distributed = workbook.add_format({"align": "vdistributed"}) + company_id = data.get("company_id", False) + if company_id: + company = self.env["res.company"].browse(company_id) + else: + company = self.env.user.company_id + data.update(report_model._get_report_values(data.get("partner_ids"), data)) + partners = self.env["res.partner"].browse(data.get("partner_ids")) + sheet = workbook.add_worksheet(_("Activity Statement")) + sheet.set_landscape() + row_pos = 0 + sheet.merge_range( + row_pos, + 0, + row_pos, + 6, + _("Statement of Account from %s" % (company.display_name)), + self.format_ws_title, + ) + row_pos += 1 + sheet.write(row_pos, 1, _("Date:"), self.format_theader_yellow_right) + sheet.write( + row_pos, + 2, + fields.Date.from_string(data.get("date_end")), + self.format_date_left, + ) + self._size_columns(sheet) + for partner in partners: + invoice_address = data.get( + "get_inv_addr", lambda x: self.env["res.partner"] + )(partner) + row_pos += 3 + sheet.write( + row_pos, 1, _("Statement to:"), self.format_theader_yellow_right + ) + sheet.merge_range( + row_pos, + 2, + row_pos, + 3, + invoice_address.display_name, + self.format_left, + ) + if invoice_address.vat: + sheet.write( + row_pos, + 4, + _("VAT:"), + self.format_theader_yellow_right, + ) + sheet.write( + row_pos, + 5, + invoice_address.vat, + self.format_left, + ) + row_pos += 1 + sheet.write( + row_pos, 1, _("Statement from:"), self.format_theader_yellow_right + ) + sheet.merge_range( + row_pos, + 2, + row_pos, + 3, + company.partner_id.display_name, + self.format_left, + ) + if company.vat: + sheet.write( + row_pos, + 4, + _("VAT:"), + self.format_theader_yellow_right, + ) + sheet.write( + row_pos, + 5, + company.vat, + self.format_left, + ) + partner_data = data.get("data", {}).get(partner.id) + currencies = partner_data.get("currencies", {}).keys() + if currencies: + row_pos += 1 + for currency_id in currencies: + currency = self.env["res.currency"].browse(currency_id) + if currency.position == "after": + money_string = "#,##0.%s " % ( + "0" * currency.decimal_places + ) + "[${}]".format(currency.symbol) + elif currency.position == "before": + money_string = "[${}]".format(currency.symbol) + " #,##0.%s" % ( + "0" * currency.decimal_places + ) + self.current_money_format = workbook.add_format( + {"align": "right", "num_format": money_string} + ) + row_pos = self._write_currency_lines( + row_pos, sheet, partner, currency, data + ) + row_pos = self._write_currency_buckets( + row_pos, sheet, partner, currency, data + ) diff --git a/partner_statement/report/outstanting_statement_xlsx.py b/partner_statement/report/outstanting_statement_xlsx.py new file mode 100644 index 00000000..c08a3922 --- /dev/null +++ b/partner_statement/report/outstanting_statement_xlsx.py @@ -0,0 +1,244 @@ +# Author: Christopher Ormaza +# Copyright 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, fields, models + + +class OutstandingStatementXslx(models.AbstractModel): + _name = "report.p_s.report_outstanding_statement_xlsx" + _description = "Outstanding Statement XLSL Report" + _inherit = "report.report_xlsx.abstract" + + def _get_report_name(self, report, data=False): + company_id = data.get("company_id", False) + report_name = _("Outstanding Statement") + if company_id: + company = self.env["res.company"].browse(company_id) + suffix = " - {} - {}".format(company.name, company.currency_id.name) + report_name = report_name + suffix + return report_name + + def _write_currency_lines(self, row_pos, sheet, partner, currency, data): + partner_data = data.get("data", {}).get(partner.id, {}) + currency_data = partner_data.get("currencies", {}).get(currency.id) + account_type = data.get("account_type", False) + row_pos += 2 + statement_header = _( + "%sStatement up to %s in %s" + % ( + account_type == "payable" and _("Supplier ") or "", + partner_data.get("end"), + currency.display_name, + ) + ) + sheet.merge_range( + row_pos, 0, row_pos, 6, statement_header, self.format_right_bold + ) + row_pos += 1 + sheet.write( + row_pos, 0, _("Reference Number"), self.format_theader_yellow_center + ) + sheet.write(row_pos, 1, _("Date"), self.format_theader_yellow_center) + sheet.write(row_pos, 2, _("Due Date"), self.format_theader_yellow_center) + sheet.write(row_pos, 3, _("Description"), self.format_theader_yellow_center) + sheet.write(row_pos, 4, _("Original"), self.format_theader_yellow_center) + sheet.write(row_pos, 5, _("Open Amount"), self.format_theader_yellow_center) + sheet.write(row_pos, 6, _("Balance"), self.format_theader_yellow_center) + for line in currency_data.get("lines"): + row_pos += 1 + name_to_show = ( + line.get("name", "") == "/" or not line.get("name", "") + ) and line.get("ref", "") + if line.get("name", "") != "/": + if not line.get("ref", ""): + name_to_show = line.get("name", "") + else: + if (line.get("name", "") in line.get("ref", "")) or ( + line.get("name", "") == line.get("ref", "") + ): + name_to_show = line.get("name", "") + elif line.get("ref", "") not in line.get("name", ""): + name_to_show = line.get("ref", "") + sheet.write(row_pos, 0, line.get("move_id", ""), self.format_tcell_left) + sheet.write(row_pos, 1, line.get("date", ""), self.format_tcell_date_left) + sheet.write( + row_pos, 2, line.get("date_maturity", ""), self.format_tcell_date_left + ) + sheet.write(row_pos, 3, name_to_show, self.format_distributed) + sheet.write(row_pos, 4, line.get("amount", ""), self.current_money_format) + sheet.write( + row_pos, 5, line.get("open_amount", ""), self.current_money_format + ) + sheet.write(row_pos, 6, line.get("balance", ""), self.current_money_format) + row_pos += 1 + sheet.write(row_pos, 1, partner_data.get("end"), self.format_tcell_date_left) + sheet.merge_range( + row_pos, 2, row_pos, 4, _("Ending Balance"), self.format_tcell_left + ) + sheet.write( + row_pos, 6, currency_data.get("amount_due"), self.current_money_format + ) + return row_pos + + def _write_currency_buckets(self, row_pos, sheet, partner, currency, data): + report_model = self.env["report.partner_statement.outstanding_statement"] + partner_data = data.get("data", {}).get(partner.id, {}) + currency_data = partner_data.get("currencies", {}).get(currency.id) + if currency_data.get("buckets"): + row_pos += 2 + buckets_header = _("Aging Report at %s in %s") % ( + partner_data.get("end"), + currency.display_name, + ) + sheet.merge_range( + row_pos, 0, row_pos, 6, buckets_header, self.format_right_bold + ) + buckets_data = currency_data.get("buckets") + buckets_labels = report_model._get_bucket_labels( + partner_data.get("end"), data.get("aging_type") + ) + row_pos += 1 + for i in range(len(buckets_labels)): + sheet.write( + row_pos, i, buckets_labels[i], self.format_theader_yellow_center + ) + row_pos += 1 + sheet.write( + row_pos, 0, buckets_data.get("current", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 1, buckets_data.get("b_1_30", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 2, buckets_data.get("b_30_60", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 3, buckets_data.get("b_60_90", 0.0), self.current_money_format + ) + sheet.write( + row_pos, 4, buckets_data.get("b_90_120", 0.0), self.current_money_format + ) + sheet.write( + row_pos, + 5, + buckets_data.get("b_over_120", 0.0), + self.current_money_format, + ) + sheet.write( + row_pos, 6, buckets_data.get("balance", 0.0), self.current_money_format + ) + return row_pos + + def _size_columns(self, sheet): + for i in range(7): + sheet.set_column(0, i, 20) + + def generate_xlsx_report(self, workbook, data, objects): + report_model = self.env["report.partner_statement.outstanding_statement"] + self._define_formats(workbook) + self.format_distributed = workbook.add_format({"align": "vdistributed"}) + company_id = data.get("company_id", False) + if company_id: + company = self.env["res.company"].browse(company_id) + else: + company = self.env.user.company_id + data.update(report_model._get_report_values(data.get("partner_ids"), data)) + partners = self.env["res.partner"].browse(data.get("partner_ids")) + sheet = workbook.add_worksheet(_("Outstanding Statement")) + sheet.set_landscape() + row_pos = 0 + sheet.merge_range( + row_pos, + 0, + row_pos, + 6, + _("Statement of Account from %s" % (company.display_name)), + self.format_ws_title, + ) + row_pos += 1 + sheet.write(row_pos, 1, _("Date:"), self.format_theader_yellow_right) + sheet.write( + row_pos, + 2, + fields.Date.from_string(data.get("date_end")), + self.format_date_left, + ) + self._size_columns(sheet) + for partner in partners: + invoice_address = data.get( + "get_inv_addr", lambda x: self.env["res.partner"] + )(partner) + row_pos += 3 + sheet.write( + row_pos, 1, _("Statement to:"), self.format_theader_yellow_right + ) + sheet.merge_range( + row_pos, + 2, + row_pos, + 3, + invoice_address.display_name, + self.format_left, + ) + if invoice_address.vat: + sheet.write( + row_pos, + 4, + _("VAT:"), + self.format_theader_yellow_right, + ) + sheet.write( + row_pos, + 5, + invoice_address.vat, + self.format_left, + ) + row_pos += 1 + sheet.write( + row_pos, 1, _("Statement from:"), self.format_theader_yellow_right + ) + sheet.merge_range( + row_pos, + 2, + row_pos, + 3, + company.partner_id.display_name, + self.format_left, + ) + if company.vat: + sheet.write( + row_pos, + 4, + _("VAT:"), + self.format_theader_yellow_right, + ) + sheet.write( + row_pos, + 5, + company.vat, + self.format_left, + ) + partner_data = data.get("data", {}).get(partner.id) + currencies = partner_data.get("currencies", {}).keys() + if currencies: + row_pos += 1 + for currency_id in currencies: + currency = self.env["res.currency"].browse(currency_id) + if currency.position == "after": + money_string = "#,##0.%s " % ( + "0" * currency.decimal_places + ) + "[${}]".format(currency.symbol) + elif currency.position == "before": + money_string = "[${}]".format(currency.symbol) + " #,##0.%s" % ( + "0" * currency.decimal_places + ) + self.current_money_format = workbook.add_format( + {"align": "right", "num_format": money_string} + ) + row_pos = self._write_currency_lines( + row_pos, sheet, partner, currency, data + ) + row_pos = self._write_currency_buckets( + row_pos, sheet, partner, currency, data + ) diff --git a/partner_statement/tests/test_activity_statement.py b/partner_statement/tests/test_activity_statement.py index 8482835a..ac052286 100644 --- a/partner_statement/tests/test_activity_statement.py +++ b/partner_statement/tests/test_activity_statement.py @@ -27,6 +27,7 @@ class TestActivityStatement(TransactionCase): self.statement_model = self.env["report.partner_statement.activity_statement"] self.wiz = self.env["activity.statement.wizard"] self.report_name = "partner_statement.activity_statement" + self.report_name_xlsx = "p_s.report_activity_statement_xlsx" self.report_title = "Activity Statement" self.today = fields.Date.context_today(self.wiz) @@ -65,6 +66,18 @@ class TestActivityStatement(TransactionCase): "There was an error and the PDF report was not generated.", ) + statement_xlsx = wiz_id.button_export_xlsx() + + self.assertDictContainsSubset( + { + "type": "ir.actions.report", + "report_name": self.report_name_xlsx, + "report_type": "xlsx", + }, + statement_xlsx, + "There was an error and the PDF report was not generated.", + ) + data = wiz_id._prepare_statement() docids = data["partner_ids"] report = self.statement_model._get_report_values(docids, data) diff --git a/partner_statement/tests/test_outstanding_statement.py b/partner_statement/tests/test_outstanding_statement.py index d747b228..7481e804 100644 --- a/partner_statement/tests/test_outstanding_statement.py +++ b/partner_statement/tests/test_outstanding_statement.py @@ -26,6 +26,7 @@ class TestOutstandingStatement(TransactionCase): ] self.wiz = self.env["outstanding.statement.wizard"] self.report_name = "partner_statement.outstanding_statement" + self.report_name_xlsx = "p_s.report_outstanding_statement_xlsx" self.report_title = "Outstanding Statement" def _create_user(self, login, groups, company): @@ -61,6 +62,18 @@ class TestOutstandingStatement(TransactionCase): "There was an error and the PDF report was not generated.", ) + statement_xlsx = wiz_id.button_export_xlsx() + + self.assertDictContainsSubset( + { + "type": "ir.actions.report", + "report_name": self.report_name_xlsx, + "report_type": "xlsx", + }, + statement_xlsx, + "There was an error and the PDF report was not generated.", + ) + data = wiz_id._prepare_statement() docids = data["partner_ids"] report = self.statement_model._get_report_values(docids, data) diff --git a/partner_statement/views/activity_statement.xml b/partner_statement/views/activity_statement.xml index 721b7856..2c900223 100644 --- a/partner_statement/views/activity_statement.xml +++ b/partner_statement/views/activity_statement.xml @@ -168,4 +168,19 @@ qweb-pdf partner_statement.activity_statement + + Activity Statement + res.partner + partner_statement.activity_statement + qweb-html + partner_statement.activity_statement + + + Activity Statement XLSX + res.partner + ir.actions.report + p_s.report_activity_statement_xlsx + xlsx + report_activity_statement + diff --git a/partner_statement/views/outstanding_statement.xml b/partner_statement/views/outstanding_statement.xml index 4fca43f4..041f0d91 100644 --- a/partner_statement/views/outstanding_statement.xml +++ b/partner_statement/views/outstanding_statement.xml @@ -164,4 +164,19 @@ qweb-pdf partner_statement.outstanding_statement + + Outstanding Statement + res.partner + partner_statement.outstanding_statement + qweb-html + partner_statement.outstanding_statement + + + Outstanding Statement XLSX + res.partner + ir.actions.report + p_s.report_outstanding_statement_xlsx + xlsx + report_outstanding_statement + diff --git a/partner_statement/wizard/activity_statement_wizard.py b/partner_statement/wizard/activity_statement_wizard.py index b764472c..5cfa3c88 100644 --- a/partner_statement/wizard/activity_statement_wizard.py +++ b/partner_statement/wizard/activity_statement_wizard.py @@ -29,14 +29,27 @@ class ActivityStatementWizard(models.TransientModel): else: self.date_start = self.date_end - relativedelta(days=30) - def _export(self): - """Export to PDF.""" - data = self._prepare_statement() - return self.env.ref( - "partner_statement.action_print_activity_statement" - ).report_action(self.ids, data=data) - def _prepare_statement(self): res = super()._prepare_statement() res.update({"date_start": self.date_start}) return res + + def _print_report(self, report_type): + self.ensure_one() + data = self._prepare_statement() + if report_type == "xlsx": + report_name = "p_s.report_activity_statement_xlsx" + else: + report_name = "partner_statement.activity_statement" + return ( + self.env["ir.actions.report"] + .search( + [("report_name", "=", report_name), ("report_type", "=", report_type)], + limit=1, + ) + .report_action(self, data=data) + ) + + def _export(self, report_type): + """Default export is PDF.""" + return self._print_report(report_type) diff --git a/partner_statement/wizard/outstanding_statement_wizard.py b/partner_statement/wizard/outstanding_statement_wizard.py index b9b8baef..0d9e0e12 100644 --- a/partner_statement/wizard/outstanding_statement_wizard.py +++ b/partner_statement/wizard/outstanding_statement_wizard.py @@ -11,9 +11,22 @@ class OutstandingStatementWizard(models.TransientModel): _inherit = "statement.common.wizard" _description = "Outstanding Statement Wizard" - def _export(self): - """Export to PDF.""" + def _print_report(self, report_type): + self.ensure_one() data = self._prepare_statement() - return self.env.ref( - "partner_statement.action_print_outstanding_statement" - ).report_action(self.ids, data=data) + if report_type == "xlsx": + report_name = "p_s.report_outstanding_statement_xlsx" + else: + report_name = "partner_statement.outstanding_statement" + return ( + self.env["ir.actions.report"] + .search( + [("report_name", "=", report_name), ("report_type", "=", report_type)], + limit=1, + ) + .report_action(self, data=data) + ) + + def _export(self, report_type): + """Default export is PDF.""" + return self._print_report(report_type) diff --git a/partner_statement/wizard/statement_common.py b/partner_statement/wizard/statement_common.py index df055700..769bc615 100644 --- a/partner_statement/wizard/statement_common.py +++ b/partner_statement/wizard/statement_common.py @@ -50,10 +50,6 @@ class StatementCommon(models.AbstractModel): else: self.date_end = fields.Date.context_today(self) - def button_export_pdf(self): - self.ensure_one() - return self._export() - def _prepare_statement(self): self.ensure_one() return { @@ -67,5 +63,17 @@ class StatementCommon(models.AbstractModel): "filter_negative_balances": self.filter_negative_balances, } - def _export(self): - raise NotImplementedError + def button_export_html(self): + self.ensure_one() + report_type = "qweb-html" + return self._export(report_type) + + def button_export_pdf(self): + self.ensure_one() + report_type = "qweb-pdf" + return self._export(report_type) + + def button_export_xlsx(self): + self.ensure_one() + report_type = "xlsx" + return self._export(report_type) diff --git a/partner_statement/wizard/statement_wizard.xml b/partner_statement/wizard/statement_wizard.xml index 84ec2762..c867c783 100644 --- a/partner_statement/wizard/statement_wizard.xml +++ b/partner_statement/wizard/statement_wizard.xml @@ -72,13 +72,25 @@