diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 17:14:58 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 17:14:58 +0700 |
| commit | 1ca3b3df3421961caec3b747a364071c80f5c7da (patch) | |
| tree | 6778a1f0f3f9b4c6e26d6d87ccde16e24da6c9d6 /base_accounting_kit/report | |
| parent | b57188be371d36d96caac4b8d65a40745c0e972c (diff) | |
initial commit
Diffstat (limited to 'base_accounting_kit/report')
30 files changed, 4178 insertions, 0 deletions
diff --git a/base_accounting_kit/report/__init__.py b/base_accounting_kit/report/__init__.py new file mode 100644 index 0000000..e96b59f --- /dev/null +++ b/base_accounting_kit/report/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# +from . import general_ledger_report +from . import account_report_common_account +from . import report_partner_ledger +from . import report_tax +from . import report_trial_balance +from . import report_aged_partner +from . import report_journal_audit +from . import report_financial +from . import cash_flow_report +from . import account_bank_book +from . import account_cash_book +from . import account_day_book +from . import account_asset_report +from . import multiple_invoice_report diff --git a/base_accounting_kit/report/account_asset_report.py b/base_accounting_kit/report/account_asset_report.py new file mode 100644 index 0000000..7917958 --- /dev/null +++ b/base_accounting_kit/report/account_asset_report.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, tools + + +class AssetAssetReport(models.Model): + _name = "asset.asset.report" + _description = "Assets Analysis" + _auto = False + + name = fields.Char(string='Year', required=False, readonly=True) + date = fields.Date(readonly=True) + depreciation_date = fields.Date(string='Depreciation Date', readonly=True) + asset_id = fields.Many2one('account.asset.asset', string='Asset', readonly=True) + asset_category_id = fields.Many2one('account.asset.category', string='Asset category', readonly=True) + partner_id = fields.Many2one('res.partner', string='Partner', readonly=True) + state = fields.Selection([('draft', 'Draft'), ('open', 'Running'), ('close', 'Close')], string='Status', readonly=True) + depreciation_value = fields.Float(string='Amount of Depreciation Lines', readonly=True) + installment_value = fields.Float(string='Amount of Installment Lines', readonly=True) + move_check = fields.Boolean(string='Posted', readonly=True) + installment_nbr = fields.Integer(string='# of Installment Lines', readonly=True) + depreciation_nbr = fields.Integer(string='# of Depreciation Lines', readonly=True) + gross_value = fields.Float(string='Gross Amount', readonly=True) + posted_value = fields.Float(string='Posted Amount', readonly=True) + unposted_value = fields.Float(string='Unposted Amount', readonly=True) + company_id = fields.Many2one('res.company', string='Company', readonly=True) + + def init(self): + tools.drop_view_if_exists(self._cr, 'asset_asset_report') + self._cr.execute(""" + create or replace view asset_asset_report as ( + select + min(dl.id) as id, + dl.name as name, + dl.depreciation_date as depreciation_date, + a.date as date, + (CASE WHEN dlmin.id = min(dl.id) + THEN a.value + ELSE 0 + END) as gross_value, + dl.amount as depreciation_value, + dl.amount as installment_value, + (CASE WHEN dl.move_check + THEN dl.amount + ELSE 0 + END) as posted_value, + (CASE WHEN NOT dl.move_check + THEN dl.amount + ELSE 0 + END) as unposted_value, + dl.asset_id as asset_id, + dl.move_check as move_check, + a.category_id as asset_category_id, + a.partner_id as partner_id, + a.state as state, + count(dl.*) as installment_nbr, + count(dl.*) as depreciation_nbr, + a.company_id as company_id + from account_asset_depreciation_line dl + left join account_asset_asset a on (dl.asset_id=a.id) + left join (select min(d.id) as id,ac.id as ac_id from account_asset_depreciation_line as d inner join account_asset_asset as ac ON (ac.id=d.asset_id) group by ac_id) as dlmin on dlmin.ac_id=a.id + where a.active is true + group by + dl.amount,dl.asset_id,dl.depreciation_date,dl.name, + a.date, dl.move_check, a.state, a.category_id, a.partner_id, a.company_id, + a.value, a.id, a.salvage_value, dlmin.id + )""") diff --git a/base_accounting_kit/report/account_asset_report_views.xml b/base_accounting_kit/report/account_asset_report_views.xml new file mode 100644 index 0000000..b9cbea4 --- /dev/null +++ b/base_accounting_kit/report/account_asset_report_views.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + + <record model="ir.ui.view" id="action_account_asset_report_pivot"> + <field name="name">asset.asset.report.pivot</field> + <field name="model">asset.asset.report</field> + <field name="arch" type="xml"> + <pivot string="Assets Analysis" disable_linking="True"> + <field name="asset_category_id" type="row"/> + <field name="gross_value" type="measure"/> + <field name="unposted_value" type="measure"/> + </pivot> + </field> + </record> + <record model="ir.ui.view" id="action_account_asset_report_graph"> + <field name="name">asset.asset.report.graph</field> + <field name="model">asset.asset.report</field> + <field name="arch" type="xml"> + <graph string="Assets Analysis"> + <field name="asset_category_id" type="row"/> + <field name="gross_value" type="measure"/> + <field name="unposted_value" type="measure"/> + </graph> + </field> + </record> + + <record id="view_asset_asset_report_search" model="ir.ui.view"> + <field name="name">asset.asset.report.search</field> + <field name="model">asset.asset.report</field> + <field name="arch" type="xml"> + <search string="Assets Analysis"> + <field name="date"/> + <field name="depreciation_date"/> + <filter string="Draft" name="draft" domain="[('state','=','draft')]" help="Assets in draft state"/> + <filter string="Running" name="running" domain="[('state','=','open')]" help="Assets in running state"/> + <filter string="Not archived" name="only_active" domain="[('asset_id.active','=', True)]"/> + <separator/> + <filter string="Posted" name="posted" domain="[('move_check','=',True)]" help="Posted depreciation lines" context="{'unposted_value_visible': 0}"/> + <field name="asset_id"/> + <field name="asset_category_id"/> + <group expand="0" string="Extended Filters..."> + <field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/> + <field name="company_id" groups="base.group_multi_company"/> + </group> + <group expand="1" string="Group By"> + <filter string="Asset" name="asset" context="{'group_by':'asset_id'}"/> + <filter string="Asset Category" name="asset_category" context="{'group_by':'asset_category_id'}"/> + <filter string="Company" name="company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> + <separator/> + <filter string="Purchase Month" name="purchase_month" help="Date of asset purchase" + context="{'group_by':'date:month'}"/> + <filter string="Depreciation Month" name="deprecation_month" help="Date of depreciation" + context="{'group_by':'depreciation_date:month'}"/> + </group> + </search> + </field> + </record> + + <record model="ir.actions.act_window" id="action_asset_asset_report"> + <field name="name">Assets Analysis</field> + <field name="res_model">asset.asset.report</field> + <field name="view_mode">graph,pivot</field> + <field name="search_view_id" ref="view_asset_asset_report_search"/> + <field name="domain">[('asset_category_id.type', '=', 'purchase')]</field> + <field name="context">{'search_default_only_active': 1}</field> + <field name="help" type="html"> + <p> + From this report, you can have an overview on all depreciations. The + search bar can also be used to personalize your assets depreciation reporting. + </p> + </field> + </record> + + <menuitem name="Assets" action="action_asset_asset_report" + id="menu_action_asset_asset_report" + parent="account.account_reports_management_menu" sequence="21"/> +</odoo> diff --git a/base_accounting_kit/report/account_bank_book.py b/base_accounting_kit/report/account_bank_book.py new file mode 100644 index 0000000..7c050c7 --- /dev/null +++ b/base_accounting_kit/report/account_bank_book.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# +from datetime import time + +from odoo import models, api, _ +from odoo.exceptions import UserError + + +class ReportBankBook(models.AbstractModel): + _name = 'report.base_accounting_kit.report_bank_book' + _description = 'Bank Book Report' + + def _get_account_move_entry(self, accounts, init_balance, sortby, + display_account): + cr = self.env.cr + move_line = self.env['account.move.line'] + move_lines = {x: [] for x in accounts.ids} + + # Prepare initial sql query and Get the initial move lines + if init_balance: + init_tables, init_where_clause, init_where_params = move_line.with_context( + date_from=self.env.context.get('date_from'), date_to=False, + initial_bal=True)._query_get() + init_wheres = [""] + if init_where_clause.strip(): + init_wheres.append(init_where_clause.strip()) + init_filters = " AND ".join(init_wheres) + filters = init_filters.replace('account_move_line__move_id', + 'm').replace('account_move_line', + 'l') + sql = ("""SELECT 0 AS lid, l.account_id AS account_id, \ + '' AS ldate, '' AS lcode, 0.0 AS amount_currency, \ + '' AS lref, 'Initial Balance' AS lname, \ + COALESCE(SUM(l.debit),0.0) AS debit, \ + COALESCE(SUM(l.credit),0.0) AS credit, \ + COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance, \ + '' AS lpartner_id,\ + '' AS move_name, '' AS mmove_id, '' AS currency_code,\ + NULL AS currency_id,\ + '' AS invoice_id, '' AS invoice_type, '' AS invoice_number,\ + '' AS partner_name\ + FROM account_move_line l\ + LEFT JOIN account_move m ON (l.move_id=m.id)\ + LEFT JOIN res_currency c ON (l.currency_id=c.id)\ + LEFT JOIN res_partner p ON (l.partner_id=p.id)\ + JOIN account_journal j ON (l.journal_id=j.id)\ + WHERE l.account_id IN %s""" + filters + ' GROUP BY l.account_id') + params = (tuple(accounts.ids),) + tuple(init_where_params) + cr.execute(sql, params) + for row in cr.dictfetchall(): + move_lines[row.pop('account_id')].append(row) + sql_sort = 'l.date, l.move_id' + if sortby == 'sort_journal_partner': + sql_sort = 'j.code, p.name, l.move_id' + + # Prepare sql query base on selected parameters from wizard + tables, where_clause, where_params = move_line._query_get() + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + filters = filters.replace('account_move_line__move_id', 'm').replace( + 'account_move_line', 'l') + + # Get move lines base on sql query and Calculate the total + # balance of move lines + sql = ('''SELECT l.id AS lid, l.account_id \ + AS account_id, l.date AS ldate, j.code AS lcode,\ + l.currency_id, l.amount_currency, l.ref AS lref, l.name AS lname,\ + COALESCE(l.debit,0) AS debit, \ + COALESCE(l.credit,0) AS credit, \ + COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,\ + m.name AS move_name, c.symbol AS \ + currency_code, p.name AS partner_name\ + FROM account_move_line l\ + JOIN account_move m ON (l.move_id=m.id)\ + LEFT JOIN res_currency c ON (l.currency_id=c.id)\ + LEFT JOIN res_partner p ON (l.partner_id=p.id)\ + JOIN account_journal j ON (l.journal_id=j.id)\ + JOIN account_account acc ON (l.account_id = acc.id) \ + WHERE l.account_id IN %s ''' + filters + ''' GROUP BY \ + l.id, l.account_id, l.date, j.code, l.currency_id, \ + l.amount_currency, l.ref, l.name, m.name, \ + c.symbol, p.name ORDER BY ''' + sql_sort) + params = (tuple(accounts.ids),) + tuple(where_params) + cr.execute(sql, params) + + for row in cr.dictfetchall(): + balance = 0 + for line in move_lines.get(row['account_id']): + balance += line['debit'] - line['credit'] + row['balance'] += balance + move_lines[row.pop('account_id')].append(row) + + # Calculate the debit, credit and balance for Accounts + account_res = [] + for account in accounts: + currency = account.currency_id and \ + account.currency_id or account.company_id.currency_id + res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance']) + res['code'] = account.code + res['name'] = account.name + res['move_lines'] = move_lines[account.id] + for line in res.get('move_lines'): + res['debit'] += line['debit'] + res['credit'] += line['credit'] + res['balance'] = line['balance'] + if display_account == 'all': + account_res.append(res) + if display_account == 'movement' and res.get('move_lines'): + account_res.append(res) + if display_account == 'not_zero' and not currency.is_zero( + res['balance']): + account_res.append(res) + + return account_res + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get('active_model'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse(self.env.context.get('active_ids', [])) + init_balance = data['form'].get('initial_balance', True) + sortby = data['form'].get('sortby', 'sort_date') + display_account = 'movement' + codes = [] + if data['form'].get('journal_ids', False): + codes = [journal.code for journal in + self.env['account.journal'].search( + [('id', 'in', data['form']['journal_ids'])])] + account_ids = data['form']['account_ids'] + accounts = self.env['account.account'].search( + [('id', 'in', account_ids)]) + if not accounts: + journals = self.env['account.journal'].search([('type', '=', 'bank')]) + accounts = [] + for journal in journals: + accounts.append(journal.payment_credit_account_id.id) + accounts = self.env['account.account'].search([('id', 'in', accounts)]) + + accounts_res = self.with_context(data['form'].get('used_context', {}))._get_account_move_entry( + accounts, + init_balance, + sortby, + display_account) + return { + 'doc_ids': docids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'Accounts': accounts_res, + 'print_journal': codes, + } diff --git a/base_accounting_kit/report/account_bank_book_view.xml b/base_accounting_kit/report/account_bank_book_view.xml new file mode 100644 index 0000000..b4410ae --- /dev/null +++ b/base_accounting_kit/report/account_bank_book_view.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_bank_book"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + + <t t-call="web.internal_layout"> + <div class="page"> + <h2><span t-esc="env.company.name"/>: Bank Book Report + </h2> + + <div class="row"> + <div class="col-xs-4" style="width:40%;"> + <strong>Journals:</strong> + <p t-esc="', '.join([ lt or '' for lt in print_journal ])"/> + </div> + <div class="col-xs-4" style="width:30%;"> + <strong>Display Account</strong> + <p> + <span t-if="data['display_account'] == 'all'">All accounts'</span> + <span t-if="data['display_account'] == 'movement'">With movements</span> + <span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span> + </p> + </div> + <div class="col-xs-4" style="width:30%;"> + <strong>Target Moves:</strong> + <p t-if="data['target_move'] == 'all'">All Entries</p> + <p t-if="data['target_move'] == 'posted'">All Posted Entries</p> + </div> + </div> + <br/> + <div class="row"> + <div style="width:70%;"> + <strong>Sorted By:</strong> + <p t-if="data['sortby'] == 'sort_date'">Date</p> + <p t-if="data['sortby'] == 'sort_journal_partner'">Journal and Partner</p> + </div> + <div style="width:30%;"> + <t t-if="data['date_from']"> + <strong>Date from :</strong> + <span t-esc="data['date_from']"/> + <br/> + </t> + <t t-if="data['date_to']"> + <strong>Date to :</strong> + <span t-esc="data['date_to']"/> + </t> + </div> + </div> + <br/> + <table class="table table-condensed"> + <thead> + <tr class="text-center"> + <th>Date</th> + <th>JRNL</th> + <th>Partner</th> + <th>Ref</th> + <th>Move</th> + <th>Entry Label</th> + <th>Debit</th> + <th>Credit</th> + <th>Balance</th> + <th groups="base.group_multi_currency">Currency</th> + </tr> + </thead> + <tbody> + <t t-foreach="Accounts" t-as="account"> + <tr style="font-weight: bold;"> + <td colspan="6"> + <span style="color: white;" t-esc="'..'"/> + <span t-esc="account['code']"/> + <span t-esc="account['name']"/> + </td> + <td class="text-right"> + <span t-esc="account['debit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['balance']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td groups="base.group_multi_currency"/> + </tr> + <tr t-foreach="account['move_lines']" t-as="line"> + <td> + <span t-esc="line['ldate']"/> + </td> + <td> + <span t-esc="line['lcode']"/> + </td> + <td> + <span t-esc="line['partner_name']"/> + </td> + <td> + <span t-if="line['lref']" t-esc="line['lref']"/> + </td> + <td> + <span t-esc="line['move_name']"/> + </td> + <td> + <span t-esc="line['lname']"/> + </td> + <td class="text-right"> + <span t-esc="line['debit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['balance']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td t-if="line['amount_currency']" class="text-right" groups="base.group_multi_currency"> + <span t-esc="line['amount_currency'] if line['amount_currency'] > 0.00 else ''"/> + <span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/> + </td> + </tr> + </t> + </tbody> + </table> + </div> + </t> + </t> + </template> +</odoo> diff --git a/base_accounting_kit/report/account_cash_book.py b/base_accounting_kit/report/account_cash_book.py new file mode 100644 index 0000000..2c89c52 --- /dev/null +++ b/base_accounting_kit/report/account_cash_book.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# +from datetime import time + +from odoo import models, api, _ +from odoo.exceptions import UserError + + +class ReportCashBook(models.AbstractModel): + _name = 'report.base_accounting_kit.report_cash_book' + _description = 'Cash Book Report' + + def _get_account_move_entry(self, accounts, init_balance, sortby, + display_account): + + cr = self.env.cr + move_line = self.env['account.move.line'] + move_lines = {x: [] for x in accounts.ids} + + # Prepare initial sql query and Get the initial move lines + if init_balance: + init_tables, init_where_clause, init_where_params = move_line.with_context( + date_from=self.env.context.get('date_from'), date_to=False, + initial_bal=True)._query_get() + init_wheres = [""] + if init_where_clause.strip(): + init_wheres.append(init_where_clause.strip()) + init_filters = " AND ".join(init_wheres) + filters = init_filters.replace('account_move_line__move_id', + 'm').replace('account_move_line', + 'l') + sql = ("""SELECT 0 AS lid, l.account_id AS account_id, '' AS ldate, '' AS lcode, 0.0 AS amount_currency, '' AS lref, 'Initial Balance' AS lname, COALESCE(SUM(l.debit),0.0) AS debit, COALESCE(SUM(l.credit),0.0) AS credit, COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance, '' AS lpartner_id,\ + '' AS move_name, '' AS mmove_id, '' AS currency_code,\ + NULL AS currency_id,\ + '' AS invoice_id, '' AS invoice_type, '' AS invoice_number,\ + '' AS partner_name\ + FROM account_move_line l\ + LEFT JOIN account_move m ON (l.move_id=m.id)\ + LEFT JOIN res_currency c ON (l.currency_id=c.id)\ + LEFT JOIN res_partner p ON (l.partner_id=p.id)\ + JOIN account_journal j ON (l.journal_id=j.id)\ + WHERE l.account_id IN %s""" + filters + ' GROUP BY l.account_id') + params = (tuple(accounts.ids),) + tuple(init_where_params) + cr.execute(sql, params) + for row in cr.dictfetchall(): + move_lines[row.pop('account_id')].append(row) + sql_sort = 'l.date, l.move_id' + if sortby == 'sort_journal_partner': + sql_sort = 'j.code, p.name, l.move_id' + + # Prepare sql query base on selected parameters from wizard + tables, where_clause, where_params = move_line._query_get() + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + filters = filters.replace('account_move_line__move_id', 'm').replace( + 'account_move_line', 'l') + if not accounts: + journals = self.env['account.journal'].search([('type', '=', 'cash')]) + accounts = [] + for journal in journals: + accounts.append(journal.payment_credit_account_id.id) + accounts = self.env['account.account'].search([('id','in',accounts)]) + + # Get move lines base on sql query and Calculate the total balance of move lines + sql = ('''SELECT l.id AS lid, l.account_id AS account_id, l.date AS ldate, j.code AS lcode, l.currency_id, l.amount_currency, l.ref AS lref, l.name AS lname, COALESCE(l.debit,0) AS debit, COALESCE(l.credit,0) AS credit, COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,\ + m.name AS move_name, c.symbol AS currency_code, p.name AS partner_name\ + FROM account_move_line l\ + JOIN account_move m ON (l.move_id=m.id)\ + LEFT JOIN res_currency c ON (l.currency_id=c.id)\ + LEFT JOIN res_partner p ON (l.partner_id=p.id)\ + JOIN account_journal j ON (l.journal_id=j.id)\ + JOIN account_account acc ON (l.account_id = acc.id) \ + WHERE l.account_id IN %s ''' + filters + ''' GROUP BY l.id, l.account_id, l.date, j.code, l.currency_id, l.amount_currency, l.ref, l.name, m.name, c.symbol, p.name ORDER BY ''' + sql_sort) + params = (tuple(accounts.ids),) + tuple(where_params) + cr.execute(sql, params) + + for row in cr.dictfetchall(): + balance = 0 + for line in move_lines.get(row['account_id']): + balance += line['debit'] - line['credit'] + row['balance'] += balance + move_lines[row.pop('account_id')].append(row) + + # Calculate the debit, credit and balance for Accounts + account_res = [] + for account in accounts: + currency = account.currency_id and account.currency_id or account.company_id.currency_id + res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance']) + res['code'] = account.code + res['name'] = account.name + res['move_lines'] = move_lines[account.id] + for line in res.get('move_lines'): + res['debit'] += line['debit'] + res['credit'] += line['credit'] + res['balance'] = line['balance'] + if display_account == 'all': + account_res.append(res) + if display_account == 'movement' and res.get('move_lines'): + account_res.append(res) + if display_account == 'not_zero' and not currency.is_zero( + res['balance']): + account_res.append(res) + + return account_res + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get('active_model'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse( + self.env.context.get('active_ids', [])) + init_balance = data['form'].get('initial_balance', True) + sortby = data['form'].get('sortby', 'sort_date') + display_account = 'movement' + codes = [] + if data['form'].get('journal_ids', False): + codes = [journal.code for journal in + self.env['account.journal'].search( + [('id', 'in', data['form']['journal_ids'])])] + account_ids = data['form']['account_ids'] + accounts = self.env['account.account'].search( + [('id', 'in', account_ids)]) + if not accounts: + journals = self.env['account.journal'].search([('type', '=', 'cash')]) + accounts = [] + for journal in journals: + accounts.append(journal.payment_credit_account_id.id) + accounts = self.env['account.account'].search([('id', 'in', accounts)]) + accounts_res = self.with_context( + data['form'].get('used_context', {}))._get_account_move_entry( + accounts, + init_balance, + sortby, + display_account) + return { + 'doc_ids': docids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'Accounts': accounts_res, + 'print_journal': codes, + } diff --git a/base_accounting_kit/report/account_cash_book_view.xml b/base_accounting_kit/report/account_cash_book_view.xml new file mode 100644 index 0000000..e26ae7e --- /dev/null +++ b/base_accounting_kit/report/account_cash_book_view.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + +<template id="report_cash_book"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + + <t t-call="web.internal_layout"> + <div class="page"> + <h2><span t-esc="env.company.name"/>: Cash Book Report</h2> + + <div class="row"> + <div class="col-xs-4" style="width:40%;"> + <strong>Journals:</strong> + <p t-esc="', '.join([ lt or '' for lt in print_journal ])"/> + </div> + <div class="col-xs-4" style="width:30%;"> + <strong>Display Account</strong> + <p> + <span t-if="data['display_account'] == 'all'">All accounts'</span> + <span t-if="data['display_account'] == 'movement'">With movements</span> + <span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span> + </p> + </div> + <div class="col-xs-4" style="width:30%;"> + <strong>Target Moves:</strong> + <p t-if="data['target_move'] == 'all'">All Entries</p> + <p t-if="data['target_move'] == 'posted'">All Posted Entries</p> + </div> + </div> + <br/> + <div class="row"> + <div style="width:70%;"> + <strong>Sorted By:</strong> + <p t-if="data['sortby'] == 'sort_date'">Date</p> + <p t-if="data['sortby'] == 'sort_journal_partner'">Journal and Partner</p> + </div> + <div style="width:30%;"> + <t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t> + <t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t> + </div> + </div> + <br/> + <table class="table table-condensed"> + <thead> + <tr class="text-center"> + <th>Date</th> + <th>JRNL</th> + <th>Partner</th> + <th>Ref</th> + <th>Move</th> + <th>Entry Label</th> + <th>Debit</th> + <th>Credit</th> + <th>Balance</th> + <th groups="base.group_multi_currency">Currency</th> + </tr> + </thead> + <tbody> + <t t-foreach="Accounts" t-as="account"> + <tr style="font-weight: bold;"> + <td colspan="6"> + <span style="color: white;" t-esc="'..'"/> + <span t-esc="account['code']"/> + <span t-esc="account['name']"/> + </td> + <td class="text-right"> + <span t-esc="account['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['credit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['balance']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td groups="base.group_multi_currency"/> + </tr> + <tr t-foreach="account['move_lines']" t-as="line"> + <td><span t-esc="line['ldate']"/></td> + <td><span t-esc="line['lcode']"/></td> + <td><span t-esc="line['partner_name']"/></td> + <td><span t-if="line['lref']" t-esc="line['lref']"/></td> + <td><span t-esc="line['move_name']"/></td> + <td><span t-esc="line['lname']"/></td> + <td class="text-right"> + <span t-esc="line['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['credit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['balance']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td t-if="line['amount_currency']" class="text-right" groups="base.group_multi_currency"> + <span t-esc="line['amount_currency'] if line['amount_currency'] > 0.00 else ''"/> + <span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/> + </td> + </tr> + </t> + </tbody> + </table> + </div> + </t> + </t> +</template> +</odoo> diff --git a/base_accounting_kit/report/account_day_book.py b/base_accounting_kit/report/account_day_book.py new file mode 100644 index 0000000..fcc7e8b --- /dev/null +++ b/base_accounting_kit/report/account_day_book.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# +import time +from datetime import timedelta, datetime + +from odoo import models, api, _ +from odoo.exceptions import UserError + + +class DayBookPdfReport(models.AbstractModel): + _name = 'report.base_accounting_kit.day_book_report_template' + _description = 'Day Book Report' + + def _get_account_move_entry(self, accounts, form_data, pass_date): + cr = self.env.cr + move_line = self.env['account.move.line'] + tables, where_clause, where_params = move_line._query_get() + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + if form_data['target_move'] == 'posted': + target_move = "AND m.state = 'posted'" + else: + target_move = '' + sql = (''' + SELECT l.id AS lid, acc.name as accname, l.account_id AS account_id, l.date AS ldate, j.code AS lcode, l.currency_id, + l.amount_currency, l.ref AS lref, l.name AS lname, COALESCE(l.debit,0) AS debit, COALESCE(l.credit,0) AS credit, + COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance, + m.name AS move_name, c.symbol AS currency_code, p.name AS partner_name + FROM account_move_line l + JOIN account_move m ON (l.move_id=m.id) + LEFT JOIN res_currency c ON (l.currency_id=c.id) + LEFT JOIN res_partner p ON (l.partner_id=p.id) + JOIN account_journal j ON (l.journal_id=j.id) + JOIN account_account acc ON (l.account_id = acc.id) + WHERE l.account_id IN %s AND l.journal_id IN %s ''' + target_move + ''' AND l.date = %s + GROUP BY l.id, l.account_id, l.date, + j.code, l.currency_id, l.amount_currency, l.ref, l.name, m.name, c.symbol, p.name , acc.name + ORDER BY l.date DESC + ''') + params = ( + tuple(accounts.ids), tuple(form_data['journal_ids']), pass_date) + cr.execute(sql, params) + data = cr.dictfetchall() + res = {} + debit = credit = balance = 0.00 + for line in data: + debit += line['debit'] + credit += line['credit'] + balance += line['balance'] + res['debit'] = debit + res['credit'] = credit + res['balance'] = balance + res['lines'] = data + return res + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get('active_model'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse( + self.env.context.get('active_ids', [])) + form_data = data['form'] + codes = [] + if data['form'].get('journal_ids', False): + codes = [journal.code for journal in + self.env['account.journal'].search( + [('id', 'in', data['form']['journal_ids'])])] + active_acc = data['form']['account_ids'] + accounts = self.env['account.account'].search( + [('id', 'in', active_acc)]) if data['form']['account_ids'] else \ + self.env['account.account'].search([]) + + date_start = datetime.strptime(form_data['date_from'], + '%Y-%m-%d').date() + date_end = datetime.strptime(form_data['date_to'], '%Y-%m-%d').date() + days = date_end - date_start + dates = [] + record = [] + for i in range(days.days + 1): + dates.append(date_start + timedelta(days=i)) + for head in dates: + pass_date = str(head) + accounts_res = self.with_context( + data['form'].get('used_context', {}))._get_account_move_entry( + accounts, form_data, pass_date) + if accounts_res['lines']: + record.append({ + 'date': head, + 'debit': accounts_res['debit'], + 'credit': accounts_res['credit'], + 'balance': accounts_res['balance'], + 'child_lines': accounts_res['lines'] + }) + return { + 'doc_ids': docids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'Accounts': record, + 'print_journal': codes, + } diff --git a/base_accounting_kit/report/account_day_book_view.xml b/base_accounting_kit/report/account_day_book_view.xml new file mode 100644 index 0000000..8d80aad --- /dev/null +++ b/base_accounting_kit/report/account_day_book_view.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="day_book_report_template"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <t t-call="web.internal_layout"> + <div class="page"> + <h2><span t-esc="env.company.name"/>: Day Book Report + </h2> + <div class="row mt32" style="margin-bottom:3%;"> + <div class="col-7"> + <strong>Journals:</strong> + <p t-esc="', '.join([ lt or '' for lt in print_journal ])"/> + </div> + <div class="col-2"> + <strong>Target Moves:</strong> + <p t-if="data['target_move'] == 'all'">All Entries</p> + <p t-if="data['target_move'] == 'posted'">All Posted Entries</p> + </div> + <div class="col-3"> + <t t-if="data['date_from']"> + <strong>Date from :</strong> + <span t-esc="data['date_from']"/> + <br/> + </t> + <t t-if="data['date_to']"> + <strong>Date to :</strong> + <span t-esc="data['date_to']"/> + </t> + </div> + </div> + + <table class="table table-condensed"> + <thead> + <tr class="text-center"> + <th>Date</th> + <th>JRNL</th> + <th>Partner</th> + <th>Ref</th> + <th>Move</th> + <th>Entry Label</th> + <th>Debit</th> + <th>Credit</th> + <th>Balance</th> + <th groups="base.group_multi_currency">Currency</th> + </tr> + </thead> + <tbody> + <t t-foreach="Accounts" t-as="account"> + <tr style="font-weight: bold;background: #ededed;"> + <td colspan="6"> + <span style="color: white;" t-esc="'..'"/> + <span t-esc="account['date']"/> + </td> + <td class="text-right"> + <span t-esc="account['debit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['balance']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td groups="base.group_multi_currency"/> + </tr> + <tr t-foreach="account['child_lines']" t-as="line"> + <td> + <span t-esc="line['ldate']"/> + </td> + <td> + <span t-esc="line['lcode']"/> + </td> + <td> + <span t-esc="line['partner_name']"/> + </td> + <td> + <span t-if="line['lref']" t-esc="line['lref']"/> + </td> + <td> + <span t-esc="line['move_name']"/> + </td> + <td> + <span t-esc="line['lname']"/> + </td> + <td class="text-right"> + <span t-esc="line['debit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['balance']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td t-if="line['amount_currency']" class="text-right" groups="base.group_multi_currency"> + <span t-esc="line['amount_currency'] if line['amount_currency'] > 0.00 else ''"/> + <span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/> + </td> + </tr> + </t> + </tbody> + </table> + </div> + </t> + </t> + </template> +</odoo> + diff --git a/base_accounting_kit/report/account_report_common_account.py b/base_accounting_kit/report/account_report_common_account.py new file mode 100644 index 0000000..1c359e2 --- /dev/null +++ b/base_accounting_kit/report/account_report_common_account.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +from odoo import api, fields, models + + +class AccountCommonAccountReport(models.TransientModel): + _name = 'account.common.account.report' + _description = 'Account Common Account Report' + _inherit = "account.common.report" + + display_account = fields.Selection( + [('all', 'All'), ('movement', 'With movements'), + ('not_zero', 'With balance is not equal to 0')], + string='Display Accounts', required=True, default='movement') + + def pre_print_report(self, data): + data['form'].update(self.read(['display_account'])[0]) + return data diff --git a/base_accounting_kit/report/cash_flow_report.py b/base_accounting_kit/report/cash_flow_report.py new file mode 100644 index 0000000..f4af93f --- /dev/null +++ b/base_accounting_kit/report/cash_flow_report.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +import time + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportFinancial(models.AbstractModel): + _name = 'report.base_accounting_kit.report_cash_flow' + _description = 'Cash Flow Report' + + def _compute_account_balance(self, accounts): + mapping = { + 'balance': "COALESCE(SUM(debit),0) - COALESCE(SUM(credit), 0) as balance", + 'debit': "COALESCE(SUM(debit), 0) as debit", + 'credit': "COALESCE(SUM(credit), 0) as credit", + } + + res = {} + for account in accounts: + res[account.id] = dict.fromkeys(mapping, 0.0) + if accounts: + tables, where_clause, where_params = self.env[ + 'account.move.line']._query_get() + tables = tables.replace('"', '') if tables else "account_move_line" + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + request = "SELECT account_id as id, " + ', '.join( + mapping.values()) + \ + " FROM " + tables + \ + " WHERE account_id IN %s " \ + + filters + \ + " GROUP BY account_id" + params = (tuple(accounts._ids),) + tuple(where_params) + self.env.cr.execute(request, params) + for row in self.env.cr.dictfetchall(): + res[row['id']] = row + return res + + def _compute_report_balance(self, reports): + + res = {} + fields = ['credit', 'debit', 'balance'] + for report in reports: + if report.id in res: + continue + res[report.id] = dict((fn, 0.0) for fn in fields) + if report.type == 'accounts': + # it's the sum of credit or debit + res2 = self._compute_report_balance(report.parent_id) + for key, value in res2.items(): + cash_in_operation = self.env.ref( + 'base_accounting_kit.cash_in_from_operation0') + cash_out_operation = self.env.ref( + 'base_accounting_kit.cash_out_operation1') + cash_in_financial = self.env.ref( + 'base_accounting_kit.cash_in_financial0') + cash_out_financial = self.env.ref( + 'base_accounting_kit.cash_out_financial1') + cash_in_investing = self.env.ref( + 'base_accounting_kit.cash_in_investing0') + cash_out_investing = self.env.ref( + 'base_accounting_kit.cash_out_investing1') + if report == cash_in_operation or report == cash_in_financial or report == cash_in_investing: + res[report.id]['debit'] += value['debit'] + res[report.id]['balance'] += value['debit'] + elif report == cash_out_operation or report == cash_out_financial or report == cash_out_investing: + res[report.id]['credit'] += value['credit'] + res[report.id]['balance'] += -(value['credit']) + elif report.type == 'account_type': + # it's the sum the leaf accounts with such an account type + accounts = self.env['account.account'].search( + [('user_type_id', 'in', report.account_type_ids.ids)]) + res[report.id]['account'] = self._compute_account_balance( + accounts) + for value in res[report.id]['account'].values(): + for field in fields: + res[report.id][field] += value.get(field) + elif report.type == 'account_report' and report.account_report_id: + # it's the amount of the linked + res[report.id]['account'] = self._compute_account_balance( + report.account_ids) + for value in res[report.id]['account'].values(): + for field in fields: + res[report.id][field] += value.get(field) + + elif report.type == 'sum': + # it's the sum of the linked accounts + res[report.id]['account'] = self._compute_account_balance( + report.account_ids) + for values in res[report.id]['account'].values(): + for field in fields: + res[report.id][field] += values.get(field) + return res + + def get_account_lines(self, data): + lines = [] + account_report = self.env['account.financial.report'].search( + [('id', '=', data['account_report_id'][0])]) + child_reports = account_report._get_children_by_order() + res = self.with_context( + data.get('used_context'))._compute_report_balance(child_reports) + if data['enable_filter']: + comparison_res = self.with_context( + data.get('comparison_context'))._compute_report_balance( + child_reports) + for report_id, value in comparison_res.items(): + res[report_id]['comp_bal'] = value['balance'] + report_acc = res[report_id].get('account') + if report_acc: + for account_id, val in comparison_res[report_id].get( + 'account').items(): + report_acc[account_id]['comp_bal'] = val['balance'] + + for report in child_reports: + vals = { + 'name': report.name, + 'balance': res[report.id]['balance'] * int(report.sign), + 'type': 'report', + 'level': bool(report.style_overwrite) and int( + report.style_overwrite) or report.level, + 'account_type': report.type or False, + # used to underline the financial report balances + } + if data['debit_credit']: + vals['debit'] = res[report.id]['debit'] + vals['credit'] = res[report.id]['credit'] + + if data['enable_filter']: + vals['balance_cmp'] = res[report.id]['comp_bal'] * int( + report.sign) + + lines.append(vals) + if report.display_detail == 'no_detail': + # the rest of the loop is used to display the details of the financial report, so it's not needed here. + continue + if res[report.id].get('account'): + # if res[report.id].get('debit'): + sub_lines = [] + for account_id, value in res[report.id]['account'].items(): + # if there are accounts to display, we add them to the lines with a level equals to their level in + # the COA + 1 (to avoid having them with a too low level that would conflicts with the level of data + # financial reports for Assets, liabilities...) + flag = False + account = self.env['account.account'].browse(account_id) + + vals = { + 'name': account.code + ' ' + account.name, + 'balance': value['balance'] * int(report.sign) or 0.0, + 'type': 'account', + 'level': report.display_detail == 'detail_with_hierarchy' and 4, + 'account_type': account.internal_type, + } + if data['debit_credit']: + vals['debit'] = value['debit'] + vals['credit'] = value['credit'] + if not account.company_id.currency_id.is_zero( + vals[ + 'debit']) or not account.company_id.currency_id.is_zero( + vals['credit']): + flag = True + if not account.company_id.currency_id.is_zero( + vals['balance']): + flag = True + if data['enable_filter']: + vals['balance_cmp'] = value['comp_bal'] * int( + report.sign) + if not account.company_id.currency_id.is_zero( + vals['balance_cmp']): + flag = True + if flag: + sub_lines.append(vals) + lines += sorted(sub_lines, + key=lambda sub_line: sub_line['name']) + return lines + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get( + 'active_model') or not self.env.context.get('active_id'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse(self.env.context.get('active_id')) + report_lines = self.get_account_lines(data.get('form')) + return { + 'doc_ids': self.ids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'get_account_lines': report_lines, + } diff --git a/base_accounting_kit/report/cash_flow_report.xml b/base_accounting_kit/report/cash_flow_report.xml new file mode 100644 index 0000000..839767d --- /dev/null +++ b/base_accounting_kit/report/cash_flow_report.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_cash_flow"> + <t t-call="web.html_container"> + <t t-foreach="docs" t-as="o"> + <t t-call="web.internal_layout"> + <div class="page"> + <h2 t-esc="data['account_report_id'][1]"/> + + <div class="row mt32 mb32"> + <div class="col-4"> + <strong>Target Moves:</strong> + <p> + <span t-if="data['target_move'] == 'all'">All Entries</span> + <span t-if="data['target_move'] == 'posted'">All Posted Entries</span> + </p> + </div> + <div class="col-4"> + <p> + <strong>Date from :</strong> + <span t-esc="data['date_from']"/> + <br/> + <strong>Date to :</strong> + <span t-esc="data['date_to']"/> + </p> + </div> + </div> + <table class="table table-sm table-reports"> + <thead> + <tr> + <th> + <strong>Name</strong> + </th> + <th class="text-right" t-if="data['debit_credit']"> + <strong>Debit</strong> + </th> + <th class="text-right" t-if="data['debit_credit']"> + <strong>Credit</strong> + </th> + <th class="text-right"> + <strong>Balance</strong> + </th> + <th class="text-right" t-if="data['enable_filter']"> + <strong t-esc="data['label_filter']"/> + </th> + </tr> + </thead> + <tbody> + <tr t-foreach="get_account_lines" t-as="a"> + <t t-if="a['level'] != 0"> + <t t-if="a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: normal;'"/> + </t> + <t t-if="not a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: bold;'"/> + </t> + <td> + <span style="color: white;" t-esc="'..' * a.get('level', 0)"/> + <span t-att-style="style" t-esc="a.get('name')"/> + </td> + <td t-if="data['debit_credit']" class="text-right" style="white-space: text-nowrap;"> + <span t-att-style="style" t-esc="a.get('debit')" t-if="data['debit_credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td t-if="data['debit_credit']" class="text-right" style="white-space: text-nowrap;"> + <span t-att-style="style" t-esc="a.get('credit')" + t-if="data['debit_credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right" style="white-space: text-nowrap;"> + <span t-att-style="style" t-esc="a.get('balance')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="a.get('balance_cmp')" + t-if="data['enable_filter']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </t> + </tr> + </tbody> + </table> + </div> + </t> + </t> + </t> + </template> +</odoo> diff --git a/base_accounting_kit/report/general_ledger_report.py b/base_accounting_kit/report/general_ledger_report.py new file mode 100644 index 0000000..f4e4b4e --- /dev/null +++ b/base_accounting_kit/report/general_ledger_report.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +import time + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportGeneralLedger(models.AbstractModel): + _name = 'report.base_accounting_kit.report_general_ledger' + _description = 'General Ledger Report' + + def _get_account_move_entry(self, accounts, init_balance, sortby, + display_account): + """ + :param: + accounts: the recordset of accounts + init_balance: boolean value of initial_balance + sortby: sorting by date or partner and journal + display_account: type of account(receivable, payable and both) + + Returns a dictionary of accounts with following key and value { + 'code': account code, + 'name': account name, + 'debit': sum of total debit amount, + 'credit': sum of total credit amount, + 'balance': total balance, + 'amount_currency': sum of amount_currency, + 'move_lines': list of move line + } + """ + cr = self.env.cr + MoveLine = self.env['account.move.line'] + move_lines = {x: [] for x in accounts.ids} + + # Prepare initial sql query and Get the initial move lines + if init_balance: + init_tables, init_where_clause, init_where_params = MoveLine.with_context( + date_from=self.env.context.get('date_from'), date_to=False, + initial_bal=True)._query_get() + init_wheres = [""] + if init_where_clause.strip(): + init_wheres.append(init_where_clause.strip()) + init_filters = " AND ".join(init_wheres) + filters = init_filters.replace('account_move_line__move_id', + 'm').replace('account_move_line', + 'l') + sql = ("""SELECT 0 AS lid, l.account_id AS account_id, '' AS ldate, '' AS lcode, 0.0 AS amount_currency, '' AS lref, 'Initial Balance' AS lname, COALESCE(SUM(l.debit),0.0) AS debit, COALESCE(SUM(l.credit),0.0) AS credit, COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance, '' AS lpartner_id,\ + '' AS move_name, '' AS mmove_id, '' AS currency_code,\ + NULL AS currency_id,\ + '' AS invoice_id, '' AS invoice_type, '' AS invoice_number,\ + '' AS partner_name\ + FROM account_move_line l\ + LEFT JOIN account_move m ON (l.move_id=m.id)\ + LEFT JOIN res_currency c ON (l.currency_id=c.id)\ + LEFT JOIN res_partner p ON (l.partner_id=p.id)\ + LEFT JOIN account_move i ON (m.id =i.id)\ + JOIN account_journal j ON (l.journal_id=j.id)\ + WHERE l.account_id IN %s""" + filters + ' GROUP BY l.account_id') + params = (tuple(accounts.ids),) + tuple(init_where_params) + cr.execute(sql, params) + for row in cr.dictfetchall(): + move_lines[row.pop('account_id')].append(row) + + sql_sort = 'l.date, l.move_id' + if sortby == 'sort_journal_partner': + sql_sort = 'j.code, p.name, l.move_id' + + # Prepare sql query base on selected parameters from wizard + tables, where_clause, where_params = MoveLine._query_get() + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + filters = filters.replace('account_move_line__move_id', 'm').replace( + 'account_move_line', 'l') + + # Get move lines base on sql query and Calculate the total balance of move lines + sql = ('''SELECT l.id AS lid, l.account_id AS account_id, l.date AS ldate, j.code AS lcode, l.currency_id, l.amount_currency, l.ref AS lref, l.name AS lname, COALESCE(l.debit,0) AS debit, COALESCE(l.credit,0) AS credit, COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,\ + m.name AS move_name, c.symbol AS currency_code, p.name AS partner_name\ + FROM account_move_line l\ + JOIN account_move m ON (l.move_id=m.id)\ + LEFT JOIN res_currency c ON (l.currency_id=c.id)\ + LEFT JOIN res_partner p ON (l.partner_id=p.id)\ + JOIN account_journal j ON (l.journal_id=j.id)\ + JOIN account_account acc ON (l.account_id = acc.id) \ + WHERE l.account_id IN %s ''' + filters + ''' GROUP BY l.id, l.account_id, l.date, j.code, l.currency_id, l.amount_currency, l.ref, l.name, m.name, c.symbol, p.name ORDER BY ''' + sql_sort) + params = (tuple(accounts.ids),) + tuple(where_params) + cr.execute(sql, params) + + for row in cr.dictfetchall(): + balance = 0 + for line in move_lines.get(row['account_id']): + balance += line['debit'] - line['credit'] + row['balance'] += balance + move_lines[row.pop('account_id')].append(row) + + # Calculate the debit, credit and balance for Accounts + account_res = [] + for account in accounts: + currency = account.currency_id and account.currency_id or account.company_id.currency_id + res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance']) + res['code'] = account.code + res['name'] = account.name + res['move_lines'] = move_lines[account.id] + for line in res.get('move_lines'): + res['debit'] += line['debit'] + res['credit'] += line['credit'] + res['balance'] = line['balance'] + if display_account == 'all': + account_res.append(res) + if display_account == 'movement' and res.get('move_lines'): + account_res.append(res) + if display_account == 'not_zero' and not currency.is_zero( + res['balance']): + account_res.append(res) + + return account_res + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get('active_model'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse( + self.env.context.get('active_ids', [])) + + init_balance = data['form'].get('initial_balance', True) + sortby = data['form'].get('sortby', 'sort_date') + display_account = data['form']['display_account'] + codes = [] + if data['form'].get('journal_ids', False): + codes = [journal.code for journal in + self.env['account.journal'].search( + [('id', 'in', data['form']['journal_ids'])])] + + accounts = docs if model == 'account.account' else self.env[ + 'account.account'].search([]) + accounts_res = self.with_context( + data['form'].get('used_context', {}))._get_account_move_entry( + accounts, init_balance, sortby, display_account) + return { + 'doc_ids': docids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'Accounts': accounts_res, + 'print_journal': codes, + } diff --git a/base_accounting_kit/report/general_ledger_report.xml b/base_accounting_kit/report/general_ledger_report.xml new file mode 100644 index 0000000..ef78eba --- /dev/null +++ b/base_accounting_kit/report/general_ledger_report.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_general_ledger"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <t t-call="web.internal_layout"> + <div class="page"> + <h2><span t-esc="env.company.name"/>: General ledger</h2> + + <div class="row mt32"> + <div class="col-4"> + <strong>Journals:</strong> + <p t-esc="', '.join([ lt or '' for lt in print_journal ])"/> + </div> + <div class="col-4"> + <strong>Display Account</strong> + <p> + <span t-if="data['display_account'] == 'all'">All accounts'</span> + <span t-if="data['display_account'] == 'movement'">With movements</span> + <span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span> + </p> + </div> + <div class="col-4"> + <strong>Target Moves:</strong> + <p t-if="data['target_move'] == 'all'">All Entries</p> + <p t-if="data['target_move'] == 'posted'">All Posted Entries</p> + </div> + </div> + <div class="row mb32"> + <div class="col-4"> + <strong>Sorted By:</strong> + <p t-if="data['sortby'] == 'sort_date'">Date</p> + <p t-if="data['sortby'] == 'sort_journal_partner'">Journal and Partner</p> + </div> + <div class="col-4"> + <t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t> + <t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t> + </div> + </div> + + <table class="table table-sm table-reports"> + <thead> + <tr class="text-center"> + <th>Date</th> + <th>JRNL</th> + <th>Partner</th> + <th>Ref</th> + <th>Move</th> + <th>Entry Label</th> + <th>Debit</th> + <th>Credit</th> + <th>Balance</th> + <th groups="base.group_multi_currency">Currency</th> + </tr> + </thead> + <tbody> + <t t-foreach="Accounts" t-as="account"> + <tr style="font-weight: bold;"> + <td colspan="6"> + <span style="color: white;" t-esc="'..'"/> + <span t-esc="account['code']"/> + <span t-esc="account['name']"/> + </td> + <td class="text-right"> + <span t-esc="account['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['credit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="account['balance']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td groups="base.group_multi_currency"/> + </tr> + <tr t-foreach="account['move_lines']" t-as="line"> + <td><span t-esc="line['ldate']"/></td> + <td><span t-esc="line['lcode']"/></td> + <td><span t-esc="line['partner_name']"/></td> + <td><span t-if="line['lref']" t-esc="line['lref']"/></td> + <td><span t-esc="line['move_name']"/></td> + <td><span t-esc="line['lname']"/></td> + <td class="text-right"> + <span t-esc="line['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['credit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['balance']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <t t-if="line['amount_currency']"> + <td class="text-right" groups="base.group_multi_currency"> + <span t-esc="line['amount_currency'] if line['amount_currency'] > 0.00 else ''"/> + <span t-esc="line['currency_code'] if line['amount_currency'] > 0.00 else ''"/> + </td> + </t> + </tr> + </t> + </tbody> + </table> + </div> + </t> + </t> + </template> +</odoo> diff --git a/base_accounting_kit/report/multiple_invoice_layouts.xml b/base_accounting_kit/report/multiple_invoice_layouts.xml new file mode 100644 index 0000000..b88c2b9 --- /dev/null +++ b/base_accounting_kit/report/multiple_invoice_layouts.xml @@ -0,0 +1,552 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + + <template id="base_accounting_kit.standard"> + <div t-attf-class="header o_company_#{company.id}_layout" t-att-style="report_header_style"> + <div class="row"> + <div class="col-3 mb4"> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" style="max-height: 45px;" alt="Logo"/> +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div class="row"> + <div t-if="txt_align == 'left'" class="text-left"> + <span t-esc="mi.copy_name" style="font-size: 20px; padding-left:25px; white-space:nowrap;"/> + </div> + <div t-if="txt_align == 'center'" class="text-center"> + <span t-esc="mi.copy_name" style="font-size: 20px; + margin-left:340px; margin-right:340px; white-space:nowrap;"/> + </div> + </div> + + </t> + </t> + </div> + <div class="col-9 text-right" style="margin-top:22px;" t-field="company.report_header" name="moto"/> +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div t-if="txt_align == 'right'" class="col-9 text-right"> + <span t-esc="mi.copy_name" style="font-size: 20px;"/> + </div> + </t> + </t> + + </div> + <div t-if="company.logo or company.report_header" class="row zero_min_height"> + <div class="col-12"> + <div style="border-bottom: 1px solid black;"/> + </div> + </div> + <div class="row"> + <div class="col-6" name="company_address"> + <div t-field="company.partner_id" + t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' + /> + </div> + </div> +<!--Watermark--> + <t t-if="mi_type =='watermark'"> + <div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);"> + <t t-esc="mi.copy_name"/> + </div> + </t> + </div> + + <div t-attf-class="article o_report_layout_standard o_company_#{company.id}_layout" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id" t-att-data-oe-lang="o and o.env.context.get('lang')"> + <div class="pt-5"> + <!-- This div ensures that the address is not cropped by the header. --> + <t t-call="web.address_layout"/> + </div> + <t t-raw="0"/> + </div> + + <div t-attf-class="footer o_standard_footer o_company_#{company.id}_layout"> + <div class="text-center" style="border-top: 1px solid black;"> + <ul class="list-inline mb4"> + <!-- using the list-inline-item class from bootstrap causes weird behaviours in pdf report + adding d-inline class fixes the problem--> + <li t-if="company.phone" class="list-inline-item d-inline"><span class="o_force_ltr" t-field="company.phone"/></li> + <li t-if="company.email" class="list-inline-item d-inline"><span t-field="company.email"/></li> + <li t-if="company.website" class="list-inline-item d-inline"><span t-field="company.website"/></li> + <li t-if="company.vat" class="list-inline-item d-inline"><t t-esc="company.country_id.vat_label or 'Tax ID'"/>: <span t-field="company.vat"/></li> + +<!--Footer--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'footer'"> + <div t-if="txt_align == 'right'" class="text-right"> + <span t-esc="mi.copy_name" style="font-size: 15px;"/> + </div> + <div t-if="txt_align == 'left'" class="text-left"> + <span t-esc="mi.copy_name" style="font-size: 15px;"/> + </div> + <div t-if="txt_align == 'center'" class="text-center;"> + <span t-esc="mi.copy_name" style="font-size: 15px;"/> + </div> + + </t> + </t> + + </ul> + + <div name="financial_infos"> + <span t-field="company.report_footer"/> + </div> + + <div t-if="report_type == 'pdf'" class="text-muted"> + Page: <span class="page"/> / <span class="topage"/> + </div> + </div> + </div> + </template> + + <template id="base_accounting_kit.boxed"> + <div t-attf-class="header o_company_#{company.id}_layout" t-att-style="report_header_style"> + <div class="o_boxed_header"> + <div class="row mb8"> + <div class="col-6"> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" alt="Logo"/> +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div t-if="txt_align == 'left'"> + <span t-esc="mi.copy_name" style="font-size: 25px; white-space:nowrap;"/> + </div> + <div t-if="txt_align == 'center'" class="text-align: center"> + <span t-esc="mi.copy_name" style="font-size: 25px; + margin-left:340px; margin-right:340px; white-space:nowrap;"/> + </div> + </t> + </t> + + </div> + + + <div class="col-6 text-right mb4"> + <h4 class="mt0" t-field="company.report_header"/> + <div name="company_address" class="float-right mb4"> + <span class="company_address" t-field="company.partner_id" + t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'/> +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div t-if="txt_align == 'right'" class="float-right mb4"> + <span t-esc="mi.copy_name" style="font-size: 25px;"/> + </div> + + </t> + </t> + <br/> + </div> + + </div> + + </div> + + </div> +<!--Watermark--> + <t t-if="mi_type =='watermark'"> + <div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);"> + <t t-esc="mi.copy_name"/> + </div> + </t> + </div> + + <div t-attf-class="article o_report_layout_boxed o_company_#{company.id}_layout" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id" t-att-data-oe-lang="o and o.env.context.get('lang')"> + <div class="pt-5"> + <!-- This div ensures that the address is not cropped by the header. --> + <t t-call="web.address_layout"/> + </div> + <t t-raw="0"/> + </div> + + <div t-attf-class="footer o_boxed_footer o_company_#{company.id}_layout"> + <div class="text-center"> + <ul class="list-inline"> + <li t-if="company.phone" class="list-inline-item"><span class="o_force_ltr" t-field="company.phone"/></li> + <li t-if="company.email" class="list-inline-item"><span t-field="company.email"/></li> + <li t-if="company.website" class="list-inline-item"><span t-field="company.website"/></li> + <li t-if="company.vat" class="list-inline-item"><t t-esc="company.country_id.vat_label or 'Tax ID'"/>: <span t-field="company.vat"/></li> + </ul> + <div t-field="company.report_footer"/> +<!--Footer--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'footer'"> + <div t-if="txt_align == 'right'" class="text-right"> + <span t-esc="mi.copy_name" style="font-size: 20px;"/> + </div> + <div t-if="txt_align == 'left'" class="text-left"> + <span t-esc="mi.copy_name" style="font-size: 20px;"/> + </div> + <div t-if="txt_align == 'center'" class="text-center;"> + <span t-esc="mi.copy_name" style="font-size: 20px;"/> + </div> + + </t> + </t> + + + <div t-if="report_type == 'pdf'"> + Page: <span class="page"/> / <span class="topage"/> + </div> + </div> + </div> + + </template> + + <template id="base_accounting_kit.clean"> + <div t-attf-class="header o_company_#{company.id}_layout" t-att-style="report_header_style"> + <div class="o_clean_header"> + <div class="row"> + <div class="col-6"> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" alt="Logo"/> +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div t-if="txt_align == 'left'"> + <br/> + <span t-esc="mi.copy_name" style="font-size: 20px; padding-left:25px; white-space:nowrap;"/> + </div> + <div t-if="txt_align == 'center'" class="text-align: center"> + <br/> + <span t-esc="mi.copy_name" style="font-size: 20px; + margin-left:280px; margin-right:280px; white-space:nowrap;"/> + </div> + </t> + </t> + </div> + <div class="col-5 offset-1" name="company_address"> + <ul class="list-unstyled"> + <strong><li t-if="company.name"><span t-field="company.name"/></li></strong> + <li t-if="company.vat"><t t-esc="company.country_id.vat_label or 'Tax ID'"/>: <span t-field="company.vat"/></li> + <li t-if="company.phone">Tel: <span class="o_force_ltr" t-field="company.phone"/></li> + <li t-if="company.email"><span t-field="company.email"/></li> + <li t-if="company.website"><span t-field="company.website"/></li> +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div t-if="txt_align == 'right'"> + <span t-esc="mi.copy_name" style="font-size: 20px;"/> + </div> + + </t> + </t> + + </ul> + + </div> + </div> + </div> +<!--Watermark--> + <t t-if="mi_type =='watermark'"> + <div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);"> + <t t-esc="mi.copy_name"/> + </div> + </t> + </div> + + <div t-attf-class="article o_report_layout_clean o_company_#{company.id}_layout" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id" t-att-data-oe-lang="o and o.env.context.get('lang')"> + <t t-call="web.address_layout"/> + <t t-raw="0"/> + </div> + + <div t-attf-class="footer o_clean_footer o_company_#{company.id}_layout"> + <div class="row mt8"> +<!--Footer--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'footer'"> + <div t-if="txt_align == 'left'" class="text-left"> + <span t-esc="mi.copy_name" style="font-size: 18px; padding-left:25px; white-space:nowrap;"/> + </div> + </t> + </t> + <div class="col-3"> + <span t-field="company.report_footer"/> + </div> + + <div class="col-4 text-right"> + <span class="company_address" t-field="company.partner_id" + t-options='{"widget": "contact", "fields": ["address"], "no_marker": true}'/> + </div> + <div class="col-4"> + <h4 class="mt0 mb0 text-uppercase" t-field="company.report_header"/> +<!--Footer--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'footer'"> + <div t-if="txt_align == 'right'" class="text-right"> + <span t-esc="mi.copy_name" style="font-size: 18px;"/> + </div> + + <div t-if="txt_align == 'center'" class="text-center;"> + <span t-esc="mi.copy_name" style="font-size: 18px;"/> + </div> + + </t> + </t> + + </div> + + <div class="col-1"> + <ul t-if="report_type == 'pdf'" class="list-inline pagenumber float-right text-center"> + <li class="list-inline-item"><strong><span class="page"/></strong></li> + </ul> + </div> + </div> + </div> + </template> + + <template id="base_accounting_kit.background"> + <div t-attf-class="o_company_#{company.id}_layout header" t-att-style="report_header_style"> + <div class="o_background_header"> + <div class="float-right"> + <h3 class="mt0 text-right" t-field="company.report_header"/> + </div> + <img t-if="company.logo" t-att-src="image_data_uri(company.logo)" class="float-left" alt="Logo"/> + <div class="float-left company_address"> + <div> + <strong t-field="company.partner_id.name"/> + </div> + <span t-field="company.partner_id" + t-options='{"widget": "contact", "fields": ["address"], "no_marker": true}'/> + </div> + +<!--Header--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'header'"> + <div t-if="txt_align == 'right'" class="text-right" style="position: relative; top: 50px;"> + <span t-esc="mi.copy_name" style="font-size: 20px;"/> + </div> + <div t-if="txt_align == 'center'" class="text-center"> + <br/> + <span t-esc="mi.copy_name" style="font-size: 20px; + margin-left:280px; margin-right:280px; white-space:nowrap;"/> + </div> + <div t-if="txt_align == 'left'" class="text-left" style="position: fixed; top: 70px; left:20px;"> + <br/> + <span t-esc="mi.copy_name" style="font-size: 20px; white-space:nowrap;"/> + </div> + + </t> + </t> + + <div class="clearfix mb8"/> + </div> +<!--Watermark--> + <t t-if="mi_type =='watermark'"> + <div style="opacity:0.15; font-size:100px; width:85%; text-align:center;top:500px; right:100px; position: fixed; z-index:99; -webkit-transform: rotate(-30deg);"> + <t t-esc="mi.copy_name"/> + </div> + </t> + </div> + + <div t-attf-class="o_company_#{company.id}_layout article o_report_layout_background" t-att-data-oe-model="o and o._name" t-att-data-oe-id="o and o.id" t-att-data-oe-lang="o and o.env.context.get('lang')"> + <t t-call="web.address_layout"/> + <t t-raw="0"/> + </div> + + <div t-attf-class="o_company_#{company.id}_layout footer o_background_footer"> + <div class="text-center"> + <ul class="list-inline"> + <li t-if="company.phone" class="list-inline-item"><i class="fa fa-phone" role="img" aria-label="Phone" title="Phone"/> <span class="o_force_ltr" t-field="company.phone"/></li> + <li t-if="company.email" class="list-inline-item"><i class="fa fa-at" role="img" aria-label="Email" title="Email"/> <span t-field="company.email"/></li> + <li t-if="company.website" class="list-inline-item"><i class="fa fa-globe" role="img" aria-label="Website" title="Website"/> <span t-field="company.website"/></li> + <li t-if="company.vat" class="list-inline-item"><i class="fa fa-building-o" role="img" aria-label="Fiscal number"/><t t-esc="company.country_id.vat_label or 'Tax ID'"/>: <span t-field="company.vat"/></li> + </ul> + <div t-field="company.report_footer"/> + +<!--Footer--> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'footer'"> + <div t-if="txt_align == 'right'" class="text-right"> + <span t-esc="mi.copy_name" style="font-size: 15px;"/> + </div> + <div t-if="txt_align == 'left'" class="text-left"> + <span t-esc="mi.copy_name" style="font-size: 15px;"/> + </div> + <div t-if="txt_align == 'center'" class="text-center;"> + <span t-esc="mi.copy_name" style="font-size: 15px;"/> + </div> + + </t> + </t> + <div t-if="report_type == 'pdf'" class="text-muted"> + Page: + <span class="page"/> + of + <span class="topage"/> + </div> + </div> + </div> + </template> + + + <template id="multiple_invoice_wizard_preview"> + <t t-call="web.html_preview_container"> + <t t-call="base_accounting_kit.new_external_layout"> + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'body'"> + <div t-if="body_txt_position == 'tr'" style="font-size:25px; text-align:right;"> + <span>Sample Name</span> + </div> + <div t-if="body_txt_position == 'tl'" style="font-size:25px; text-align:left;"> + <span>Sample Name</span> + </div> + + </t> + </t> + <div class="pt-5"> + <div class="address row"> + <div name="address" class="col-md-5 ml-auto"> + <address> + <address class="mb-0" itemscope="itemscope" + itemtype="http://schema.org/Organization"> + <div> + <span itemprop="name">Deco Addict</span> + </div> + <div itemprop="address" itemscope="itemscope" + itemtype="http://schema.org/PostalAddress"> + <div class="d-flex align-items-baseline"> + <span class="w-100 o_force_ltr" itemprop="streetAddress">77 Santa Barbara + Rd<br/>Pleasant Hill CA 94523<br/>United States</span> + </div> + </div> + </address> + </address> + </div> + </div> + </div> + <div class="page"> + <h2> + <span>Invoice</span> + <span>INV/2020/07/0003</span> + </h2> + <div id="informations" class="row mt32 mb32"> + <div class="col-auto mw-100 mb-2" name="invoice_date"> + <strong>Invoice Date:</strong> + <p class="m-0">07/08/2020</p> + </div> + <div class="col-auto mw-100 mb-2" name="due_date"> + <strong>Due Date:</strong> + <p class="m-0">08/07/2020</p> + </div> + </div> + <table class="table table-sm o_main_table" name="invoice_line_table"> + <thead> + <tr> + <th name="th_description" class="text-left"><span>Description</span></th> + <th name="th_quantity" class="text-right"><span>Quantity</span></th> + <th name="th_priceunit" class="text-right d-none d-md-table-cell"><span>Unit Price</span></th> + <th name="th_taxes" class="text-left d-none d-md-table-cell"><span>Taxes</span></th> + <th name="th_subtotal" class="text-right"> + <span>Amount</span> + </th> + </tr> + </thead> + <tbody class="invoice_tbody"> + <tr> + <td name="account_invoice_line_name"><span>[FURN_8999] Three-Seat Sofa<br/> + Three Seater Sofa with Lounger in Steel Grey Colour</span></td> + <td class="text-right"> + <span>5.000</span> + </td> + <td class="text-right d-none d-md-table-cell"> + <span class="text-nowrap">1,500.00</span> + </td> + <td class="text-left d-none d-md-table-cell"> + <span id="line_tax_ids">15.00%</span> + </td> + <td class="text-right o_price_total"> + <span class="text-nowrap">$ <span class="oe_currency_value">7,500.00</span></span> + </td> + </tr> + <tr> + <td name="account_invoice_line_name"><span>[FURN_8220] Four Person Desk<br/> + Four person modern office workstation</span></td> + <td class="text-right"> + <span>5.000</span> + </td> + <td class="text-right d-none d-md-table-cell"> + <span class="text-nowrap">23,500.00</span> + </td> + <td class="text-left d-none d-md-table-cell"> + <span id="line_tax_ids">15.00%</span> + </td> + <td class="text-right o_price_total"> + <span class="text-nowrap">$ <span class="oe_currency_value">117,500.00</span></span> + </td> + </tr> + </tbody> + </table> + <div class="clearfix"> + <div id="total" class="row"> + <div class="col-sm-7 col-md-6 ml-auto"> + <table class="table table-sm" style="page-break-inside: avoid;"> + <tbody><tr class="border-black o_subtotal" style=""> + <td><strong>Subtotal</strong></td> + <td class="text-right"> + <span>$ <span class="oe_currency_value">125,000.00</span></span> + </td> + </tr> + <tr style=""> + <td><span class="text-nowrap">Tax 15%</span></td> + <td class="text-right o_price_total"> + <span class="text-nowrap">$ 18,750.00</span> + </td> + </tr> + <tr class="border-black o_total"> + <td><strong>Total</strong></td> + <td class="text-right"> + <span class="text-nowrap">$ <span class="oe_currency_value"> + 143,750.00</span></span> + </td> + </tr> + </tbody></table> + </div> + </div> + </div> + <p> + Please use the following communication for your payment : <b><span> + INV/2020/07/0003</span></b> + </p> + <p name="payment_term"> + <span>Payment terms: 300 Days</span> + </p> + + <t t-if="mi_type == 'text'"> + <t t-if="txt_position == 'body'"> + <div t-if="body_txt_position == 'br'" style="font-size:25px; text-align:right;"> + <span>Sample Name</span> + </div> + <div t-if="body_txt_position == 'bl'" style="font-size:25px; text-align:left;"> + <span>Sample Name</span> + </div> + + </t> + </t> + </div> + </t> + </t> + </template> + + <template id="new_external_layout"> + <t t-if="not o" t-set="o" t-value="doc"/> + + <t t-if="not company"> + <!-- Multicompany --> + <t t-if="company_id"> + <t t-set="company" t-value="company_id"/> + </t> + <t t-elif="o and 'company_id' in o"> + <t t-set="company" t-value="o.company_id.sudo()"/> + </t> + <t t-else="else"> + <t t-set="company" t-value="res_company"/> + </t> + </t> + + <t t-if="layout" t-call="{{layout}}"><t t-raw="0"/></t> + <t t-else="else" t-call="base_accounting_kit.standard"><t t-raw="0"/></t> + + </template> + +</odoo>
\ No newline at end of file diff --git a/base_accounting_kit/report/multiple_invoice_report.py b/base_accounting_kit/report/multiple_invoice_report.py new file mode 100644 index 0000000..aa18558 --- /dev/null +++ b/base_accounting_kit/report/multiple_invoice_report.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +from odoo import models, api + + +class ReportInvoiceMultiple(models.AbstractModel): + _name = 'report.base_accounting_kit.report_multiple_invoice' + _inherit = 'report.account.report_invoice' + + @api.model + def _get_report_values(self, docids, data=None): + rslt = super()._get_report_values(docids, data) + + inv = rslt['docs'] + layout = inv.journal_id.company_id.external_report_layout_id.key + + if layout == 'web.external_layout_boxed': + new_layout = 'base_accounting_kit.boxed' + + elif layout == 'web.external_layout_clean': + new_layout = 'base_accounting_kit.clean' + + elif layout == 'web.external_layout_background': + new_layout = 'base_accounting_kit.background' + + else: + new_layout = 'base_accounting_kit.standard' + + rslt['mi_type'] = inv.journal_id.multiple_invoice_type + rslt['mi_ids'] = inv.journal_id.multiple_invoice_ids + rslt['txt_position'] = inv.journal_id.text_position + rslt['body_txt_position'] = inv.journal_id.body_text_position + rslt['txt_align'] = inv.journal_id.text_align + rslt['layout'] = new_layout + + rslt['report_type'] = data.get('report_type') if data else '' + return rslt diff --git a/base_accounting_kit/report/multiple_invoice_report.xml b/base_accounting_kit/report/multiple_invoice_report.xml new file mode 100644 index 0000000..e582752 --- /dev/null +++ b/base_accounting_kit/report/multiple_invoice_report.xml @@ -0,0 +1,260 @@ +<odoo> + + <template id="report_multiple_invoice_new"> + + <t t-call="base_accounting_kit.new_external_layout"> + <t t-set="o" t-value="o.with_context(lang=lang)" /> + + <t t-set="address"> + <address t-field="o.partner_id" t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' /> + <div t-if="o.partner_id.vat" class="mt16"> + <t t-if="o.company_id.country_id.vat_label" t-esc="o.company_id.country_id.vat_label" id="inv_tax_id_label"/> + <t t-else="">Tax ID</t>: <span t-field="o.partner_id.vat"/></div> + </t> + <div class="page"> + + <t t-set="txt_style" t-value="'font-size:25px; text-align:center;top:0px; left:15px; position:absolute; z-index:99;'"/> + <t t-if="body_txt_position == 'tr'"> + <t t-set="txt_style" t-value="'font-size:25px; text-align:center;top:0px; right:15px; position:absolute; z-index:99;'"/> + </t> + <t t-if="body_txt_position == 'br'"> + <t t-set="txt_style" t-value="'font-size:25px; text-align:right;'"/> + </t> + <t t-if="body_txt_position == 'bl'"> + <t t-set="txt_style" t-value="'font-size:25px; text-align:left;'"/> + </t> + + <h2> + <span t-if="o.move_type == 'out_invoice' and o.state == 'posted'">Invoice</span> + <span t-if="o.move_type == 'out_invoice' and o.state == 'draft'">Draft Invoice</span> + <span t-if="o.move_type == 'out_invoice' and o.state == 'cancel'">Cancelled Invoice</span> + <span t-if="o.move_type == 'out_refund'">Credit Note</span> + <span t-if="o.move_type == 'in_refund'">Vendor Credit Note</span> + <span t-if="o.move_type == 'in_invoice'">Vendor Bill</span> + <span t-if="o.name != '/'" t-field="o.name"/> + </h2> + + <div id="informations" class="row mt32 mb32"> + <div class="col-auto col-3 mw-100 mb-2" t-if="o.invoice_date" name="invoice_date"> + <strong>Invoice Date:</strong> + <p class="m-0" t-field="o.invoice_date"/> + </div> + <div class="col-auto col-3 mw-100 mb-2" t-if="o.invoice_date_due and o.move_type == 'out_invoice' and o.state == 'posted'" name="due_date"> + <strong>Due Date:</strong> + <p class="m-0" t-field="o.invoice_date_due"/> + </div> + <div class="col-auto col-3 mw-100 mb-2" t-if="o.invoice_origin" name="origin"> + <strong>Source:</strong> + <p class="m-0" t-field="o.invoice_origin"/> + </div> + <div class="col-auto col-3 mw-100 mb-2" t-if="o.partner_id.ref" name="customer_code"> + <strong>Customer Code:</strong> + <p class="m-0" t-field="o.partner_id.ref"/> + </div> + <div class="col-auto col-3 mw-100 mb-2" t-if="o.ref" name="reference"> + <strong>Reference:</strong> + <p class="m-0" t-field="o.ref"/> + </div> + </div> + + <t t-set="display_discount" t-value="any(l.discount for l in o.invoice_line_ids)"/> + + <table class="table table-sm o_main_table" name="invoice_line_table"> + <thead> + <tr> + <th name="th_description" class="text-left"><span>Description</span></th> + <th name="th_quantity" class="text-right"><span>Quantity</span></th> + <th name="th_priceunit" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Unit Price</span></th> + <th name="th_price_unit" t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"> + <span>Disc.%</span> + </th> + <th name="th_taxes" t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Taxes</span></th> + <th name="th_subtotal" class="text-right"> + <span groups="account.group_show_line_subtotals_tax_excluded">Amount</span> + <span groups="account.group_show_line_subtotals_tax_included">Total Price</span> + </th> + </tr> + </thead> + <tbody class="invoice_tbody"> + <t t-set="current_subtotal" t-value="0"/> + <t t-set="lines" t-value="o.invoice_line_ids.sorted(key=lambda l: (-l.sequence, l.date, l.move_name, -l.id), reverse=True)"/> + + <t t-foreach="lines" t-as="line"> + <t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/> + <t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/> + + <tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''"> + <t t-if="not line.display_type" name="account_invoice_line_accountable"> + <td name="account_invoice_line_name"><span t-field="line.name" t-options="{'widget': 'text'}"/></td> + <td class="text-right"> + <span t-field="line.quantity"/> + <span t-field="line.product_uom_id" groups="uom.group_uom"/> + </td> + <td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"> + <span class="text-nowrap" t-field="line.price_unit"/> + </td> + <td t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"> + <span class="text-nowrap" t-field="line.discount"/> + </td> + <td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"> + <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_ids))" id="line_tax_ids"/> + </td> + <td class="text-right o_price_total"> + <span class="text-nowrap" t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/> + <span class="text-nowrap" t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/> + </td> + </t> + <t t-if="line.display_type == 'line_section'"> + <td colspan="99"> + <span t-field="line.name" t-options="{'widget': 'text'}"/> + </td> + <t t-set="current_section" t-value="line"/> + <t t-set="current_subtotal" t-value="0"/> + </t> + <t t-if="line.display_type == 'line_note'"> + <td colspan="99"> + <span t-field="line.name" t-options="{'widget': 'text'}"/> + </td> + </t> + </tr> + + <t t-if="current_section and (line_last or lines[line_index+1].display_type == 'line_section')"> + <tr class="is-subtotal text-right"> + <td colspan="99"> + <strong class="mr16">Subtotal</strong> + <span + t-esc="current_subtotal" + t-options='{"widget": "monetary", "display_currency": o.currency_id}' + /> + </td> + </tr> + </t> + </t> + </tbody> + </table> + + <div class="clearfix"> + <div id="total" class="row"> + <div t-attf-class="#{'col-6' if report_type != 'html' else 'col-sm-7 col-md-6'} ml-auto"> + <table class="table table-sm" style="page-break-inside: avoid;"> + <tr class="border-black o_subtotal" style=""> + <td><strong>Subtotal</strong></td> + <td class="text-right"> + <span t-field="o.amount_untaxed"/> + </td> + </tr> + <t t-foreach="o.amount_by_group" t-as="amount_by_group"> + <tr style=""> + <t t-if="len(o.line_ids.filtered(lambda line: line.tax_line_id)) in [0, 1] and o.amount_untaxed == amount_by_group[2]"> + <td><span class="text-nowrap" t-esc="amount_by_group[0]"/></td> + <td class="text-right o_price_total"> + <span class="text-nowrap" t-esc="amount_by_group[3]" /> + </td> + </t> + <t t-else=""> + <td> + <span t-esc="amount_by_group[0]"/> + <span class="text-nowrap"> on + <t t-esc="amount_by_group[4]"/> + </span> + </td> + <td class="text-right o_price_total"> + <span class="text-nowrap" t-esc="amount_by_group[3]"/> + </td> + </t> + </tr> + </t> + <tr class="border-black o_total"> + <td><strong>Total</strong></td> + <td class="text-right"> + <span class="text-nowrap" t-field="o.amount_total"/> + </td> + </tr> + <t t-if="print_with_payments"> + <t t-if="o.payment_state != 'invoicing_legacy'"> + <t t-set="payments_vals" t-value="o.sudo()._get_reconciled_info_JSON_values()"/> + <t t-foreach="payments_vals" t-as="payment_vals"> + <tr> + <td> + <i class="oe_form_field text-right oe_payment_label">Paid on <t t-esc="payment_vals['date']" t-options='{"widget": "date"}'/></i> + </td> + <td class="text-right"> + <span t-esc="payment_vals['amount']" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/> + </td> + </tr> + </t> + <t t-if="len(payments_vals) > 0"> + <tr class="border-black"> + <td><strong>Amount Due</strong></td> + <td class="text-right"> + <span t-field="o.amount_residual"/> + </td> + </tr> + </t> + </t> + </t> + </table> + </div> + </div> + </div> + <p t-if="o.move_type in ('out_invoice', 'in_refund') and o.payment_reference" name="payment_communication"> + Please use the following communication for your payment : <b><span t-field="o.payment_reference"/></b> + </p> + + <p t-if="o.invoice_payment_term_id" name="payment_term"> + <span t-field="o.invoice_payment_term_id.note"/> + </p> + + <p t-if="o.narration" name="comment"> + <span t-field="o.narration"/> + </p> + <p t-if="o.fiscal_position_id.note" name="note"> + <span t-field="o.fiscal_position_id.note"/> + </p> + <p t-if="o.invoice_incoterm_id" name="incoterm"> + <strong>Incoterm: </strong><span t-field="o.invoice_incoterm_id.code"/> - <span t-field="o.invoice_incoterm_id.name"/> + </p> + <div id="qrcode" t-if="o.display_qr_code"> + <p t-if="qr_code_urls.get(o.id)"> + <strong class="text-center">Scan me with your banking app.</strong><br/><br/> + <img class="border border-dark rounded" t-att-src="qr_code_urls[o.id]"/> + </p> + </div> + + <t t-if="mi_type == 'text'"> + <div t-if="txt_position == 'body'" t-att-style="txt_style"> + <span t-esc="mi.copy_name" /> + </div> + </t> + + </div> + </t> + </template> + + <template id="report_multiple_invoice"> + <t t-call="web.html_container"> + <t t-foreach="docs" t-as="o"> + <t t-set="lang" t-value="o.invoice_user_id.sudo().lang if o.move_type in ('in_invoice', 'in_refund') else o.partner_id.lang"/> + <t t-set="print_with_payments" t-value="True"/> + <t t-if="o._get_name_invoice_report() == 'account.report_invoice_document'" + t-call="account.report_invoice_document" t-lang="lang"/> + <t t-foreach="mi_ids" t-as="mi"> + <t t-call="base_accounting_kit.report_multiple_invoice_new" t-lang="lang"/> + </t> + + </t> + </t> + </template> + + + <record id="report_multiple_invoice_copies" model="ir.actions.report"> + <field name="name">Multiple Invoice Copies</field> + <field name="model">account.move</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_multiple_invoice</field> + <field name="report_file">base_accounting_kit.report_multiple_invoice</field> + <field name="binding_model_id" ref="account.model_account_move"/> + <field name="binding_type">report</field> + </record> + +</odoo>
\ No newline at end of file diff --git a/base_accounting_kit/report/report.xml b/base_accounting_kit/report/report.xml new file mode 100644 index 0000000..330b4f8 --- /dev/null +++ b/base_accounting_kit/report/report.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <!-- # Financial report --> + <record id="financial_report_pdf" model="ir.actions.report"> + <field name="name">Financial reports</field> + <field name="model">financial.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_financial</field> + <field name="report_file">base_accounting_kit.report_financial</field> + </record> + <!-- # General ledger report --> + <record id="action_report_general_ledger" model="ir.actions.report"> + <field name="name">General Ledger</field> + <field name="model">account.report.general.ledger</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_general_ledger</field> + <field name="report_file">base_accounting_kit.report_general_ledger</field> + </record> + <!-- # Partner ledger report --> + <record id="action_report_partnerledger" model="ir.actions.report"> + <field name="name">Partner Ledger</field> + <field name="model">account.report.partner.ledger</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_partnerledger</field> + <field name="report_file">base_accounting_kit.report_partnerledger</field> + </record> + <!-- # Ageing report --> + <record id="action_report_aged_partner_balance" model="ir.actions.report"> + <field name="name">Aged Partner Balance</field> + <field name="model">res.partner</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_agedpartnerbalance</field> + <field name="report_file">base_accounting_kit.report_agedpartnerbalance</field> + </record> + <!-- # Journal audit report --> + <record id="action_report_journal" model="ir.actions.report"> + <field name="name">Journals Audit</field> + <field name="model">account.common.journal.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_journal_audit</field> + <field name="report_file">base_accounting_kit.report_journal_audit</field> + </record> + <!-- # Tax report --> + <record id="action_report_account_tax" model="ir.actions.report"> + <field name="name">Tax Report</field> + <field name="model">kit.account.tax.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_tax</field> + <field name="report_file">base_accounting_kit.report_tax</field> + </record> + <!-- # Trial balance report --> + <record id="action_report_trial_balance" model="ir.actions.report"> + <field name="name">Trial Balance</field> + <field name="model">account.balance.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_trial_balance</field> + <field name="report_file">base_accounting_kit.report_trial_balance</field> + </record> + <!-- # CAsh flow statements --> + <record id="action_report_cash_flow" model="ir.actions.report"> + <field name="name">Cash Flow Statement</field> + <field name="model">account.financial.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_cash_flow</field> + <field name="report_file">base_accounting_kit.report_cash_flow</field> + </record> + <!-- # Accounting Bank Book Report --> + <record id="action_report_bank_book" model="ir.actions.report"> + <field name="name">Bank Book Report</field> + <field name="model">account.bank.book.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_bank_book</field> + <field name="report_file">base_accounting_kit.report_bank_book</field> + <field name="attachment_use">False</field> + </record> + + <!-- # Accounting Cash Book Report --> + <record id="action_report_cash_book" model="ir.actions.report"> + <field name="name">Cash Book Report</field> + <field name="model">account.cash.book.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.report_cash_book</field> + <field name="report_file">base_accounting_kit.report_cash_book</field> + <field name="attachment_use">False</field> + </record> + + <!-- # Accounting Day Book Report --> + <record id="day_book_pdf_report" model="ir.actions.report"> + <field name="name">Day Book PDF Report</field> + <field name="model">account.day.book.report</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">base_accounting_kit.day_book_report_template</field> + <field name="report_file">base_accounting_kit.day_book_report_template</field> + <field name="attachment_use">True</field> + </record> +</odoo>
\ No newline at end of file diff --git a/base_accounting_kit/report/report_aged_partner.py b/base_accounting_kit/report/report_aged_partner.py new file mode 100644 index 0000000..f946793 --- /dev/null +++ b/base_accounting_kit/report/report_aged_partner.py @@ -0,0 +1,303 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +import time +from datetime import datetime + +from dateutil.relativedelta import relativedelta + +from odoo import api, models, _ +from odoo.exceptions import UserError +from odoo.tools import float_is_zero + + +class ReportAgedPartnerBalance(models.AbstractModel): + _name = 'report.base_accounting_kit.report_agedpartnerbalance' + _description = 'Aged Partner Balance Report' + + def _get_partner_move_lines(self, account_type, date_from, target_move, + period_length): + # This method can receive the context key 'include_nullified_amount' {Boolean} + # Do an invoice and a payment and unreconcile. The amount will be nullified + # By default, the partner wouldn't appear in this report. + # The context key allow it to appear + # In case of a period_length of 30 days as of 2019-02-08, we want the following periods: + # Name Stop Start + # 1 - 30 : 2019-02-07 - 2019-01-09 + # 31 - 60 : 2019-01-08 - 2018-12-10 + # 61 - 90 : 2018-12-09 - 2018-11-10 + # 91 - 120 : 2018-11-09 - 2018-10-11 + # +120 : 2018-10-10 + periods = {} + start = datetime.strptime(date_from, "%Y-%m-%d") + date_from = datetime.strptime(date_from, "%Y-%m-%d").date() + for i in range(5)[::-1]: + stop = start - relativedelta(days=period_length) + period_name = str((5 - (i + 1)) * period_length + 1) + '-' + str( + (5 - i) * period_length) + period_stop = (start - relativedelta(days=1)).strftime('%Y-%m-%d') + if i == 0: + period_name = '+' + str(4 * period_length) + periods[str(i)] = { + 'name': period_name, + 'stop': period_stop, + 'start': (i != 0 and stop.strftime('%Y-%m-%d') or False), + } + start = stop + + res = [] + total = [] + cr = self.env.cr + user_company = self.env.company + user_currency = user_company.currency_id + ResCurrency = self.env['res.currency'].with_context(date=date_from) + company_ids = self._context.get('company_ids') or [user_company.id] + move_state = ['draft', 'posted'] + if target_move == 'posted': + move_state = ['posted'] + arg_list = (tuple(move_state), tuple(account_type)) + # build the reconciliation clause to see what partner needs to be printed + reconciliation_clause = '(l.reconciled IS FALSE)' + cr.execute( + 'SELECT debit_move_id, credit_move_id FROM account_partial_reconcile where max_date > %s', + (date_from,)) + reconciled_after_date = [] + for row in cr.fetchall(): + reconciled_after_date += [row[0], row[1]] + if reconciled_after_date: + reconciliation_clause = '(l.reconciled IS FALSE OR l.id IN %s)' + arg_list += (tuple(reconciled_after_date),) + arg_list += (date_from, tuple(company_ids)) + query = ''' + SELECT DISTINCT l.partner_id, UPPER(res_partner.name) + FROM account_move_line AS l left join res_partner on l.partner_id = res_partner.id, account_account, account_move am + WHERE (l.account_id = account_account.id) + AND (l.move_id = am.id) + AND (am.state IN %s) + AND (account_account.internal_type IN %s) + AND ''' + reconciliation_clause + ''' + AND (l.date <= %s) + AND l.company_id IN %s + ORDER BY UPPER(res_partner.name)''' + cr.execute(query, arg_list) + + partners = cr.dictfetchall() + # put a total of 0 + for i in range(7): + total.append(0) + + # Build a string like (1,2,3) for easy use in SQL query + partner_ids = [partner['partner_id'] for partner in partners if + partner['partner_id']] + lines = dict( + (partner['partner_id'] or False, []) for partner in partners) + if not partner_ids: + return [], [], {} + + # This dictionary will store the not due amount of all partners + undue_amounts = {} + query = '''SELECT l.id + FROM account_move_line AS l, account_account, account_move am + WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) + AND (am.state IN %s) + AND (account_account.internal_type IN %s) + AND (COALESCE(l.date_maturity,l.date) >= %s)\ + AND ((l.partner_id IN %s) OR (l.partner_id IS NULL)) + AND (l.date <= %s) + AND l.company_id IN %s''' + cr.execute(query, ( + tuple(move_state), tuple(account_type), date_from, + tuple(partner_ids), date_from, tuple(company_ids))) + aml_ids = cr.fetchall() + aml_ids = aml_ids and [x[0] for x in aml_ids] or [] + for line in self.env['account.move.line'].browse(aml_ids): + partner_id = line.partner_id.id or False + if partner_id not in undue_amounts: + undue_amounts[partner_id] = 0.0 + line_amount = ResCurrency._compute(line.company_id.currency_id, + user_currency, line.balance) + if user_currency.is_zero(line_amount): + continue + for partial_line in line.matched_debit_ids: + if partial_line.max_date <= date_from: + line_amount += ResCurrency._compute( + partial_line.company_id.currency_id, user_currency, + partial_line.amount) + for partial_line in line.matched_credit_ids: + if partial_line.max_date <= date_from: + line_amount -= ResCurrency._compute( + partial_line.company_id.currency_id, user_currency, + partial_line.amount) + if not self.env.company.currency_id.is_zero(line_amount): + undue_amounts[partner_id] += line_amount + lines[partner_id].append({ + 'line': line, + 'amount': line_amount, + 'period': 6, + }) + + # Use one query per period and store results in history (a list variable) + # Each history will contain: history[1] = {'<partner_id>': <partner_debit-credit>} + history = [] + for i in range(5): + args_list = ( + tuple(move_state), tuple(account_type), tuple(partner_ids),) + dates_query = '(COALESCE(l.date_maturity,l.date)' + + if periods[str(i)]['start'] and periods[str(i)]['stop']: + dates_query += ' BETWEEN %s AND %s)' + args_list += ( + periods[str(i)]['start'], periods[str(i)]['stop']) + elif periods[str(i)]['start']: + dates_query += ' >= %s)' + args_list += (periods[str(i)]['start'],) + else: + dates_query += ' <= %s)' + args_list += (periods[str(i)]['stop'],) + args_list += (date_from, tuple(company_ids)) + + query = '''SELECT l.id + FROM account_move_line AS l, account_account, account_move am + WHERE (l.account_id = account_account.id) AND (l.move_id = am.id) + AND (am.state IN %s) + AND (account_account.internal_type IN %s) + AND ((l.partner_id IN %s) OR (l.partner_id IS NULL)) + AND ''' + dates_query + ''' + AND (l.date <= %s) + AND l.company_id IN %s''' + cr.execute(query, args_list) + partners_amount = {} + aml_ids = cr.fetchall() + aml_ids = aml_ids and [x[0] for x in aml_ids] or [] + for line in self.env['account.move.line'].browse(aml_ids): + partner_id = line.partner_id.id or False + if partner_id not in partners_amount: + partners_amount[partner_id] = 0.0 + line_amount = ResCurrency._compute(line.company_id.currency_id, + user_currency, line.balance) + if user_currency.is_zero(line_amount): + continue + for partial_line in line.matched_debit_ids: + if partial_line.max_date <= date_from: + line_amount += ResCurrency._compute( + partial_line.company_id.currency_id, user_currency, + partial_line.amount) + for partial_line in line.matched_credit_ids: + if partial_line.max_date <= date_from: + line_amount -= ResCurrency._compute( + partial_line.company_id.currency_id, user_currency, + partial_line.amount) + + if not self.env.company.currency_id.is_zero( + line_amount): + partners_amount[partner_id] += line_amount + lines[partner_id].append({ + 'line': line, + 'amount': line_amount, + 'period': i + 1, + }) + history.append(partners_amount) + + for partner in partners: + if partner['partner_id'] is None: + partner['partner_id'] = False + at_least_one_amount = False + values = {} + undue_amt = 0.0 + if partner[ + 'partner_id'] in undue_amounts: # Making sure this partner actually was found by the query + undue_amt = undue_amounts[partner['partner_id']] + + total[6] = total[6] + undue_amt + values['direction'] = undue_amt + if not float_is_zero(values['direction'], + precision_rounding=self.env.company.currency_id.rounding): + at_least_one_amount = True + + for i in range(5): + during = False + if partner['partner_id'] in history[i]: + during = [history[i][partner['partner_id']]] + # Adding counter + total[(i)] = total[(i)] + (during and during[0] or 0) + values[str(i)] = during and during[0] or 0.0 + if not float_is_zero(values[str(i)], + precision_rounding=self.env.company.currency_id.rounding): + at_least_one_amount = True + values['total'] = sum( + [values['direction']] + [values[str(i)] for i in range(5)]) + ## Add for total + total[(i + 1)] += values['total'] + values['partner_id'] = partner['partner_id'] + if partner['partner_id']: + browsed_partner = self.env['res.partner'].browse( + partner['partner_id']) + values['name'] = browsed_partner.name and len( + browsed_partner.name) >= 45 and browsed_partner.name[ + 0:40] + '...' or browsed_partner.name + values['trust'] = browsed_partner.trust + else: + values['name'] = _('Unknown Partner') + values['trust'] = False + + if at_least_one_amount or ( + self._context.get('include_nullified_amount') and lines[ + partner['partner_id']]): + res.append(values) + + return res, total, lines + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get( + 'active_model') or not self.env.context.get('active_id'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + total = [] + model = self.env.context.get('active_model') + docs = self.env[model].browse(self.env.context.get('active_id')) + + target_move = data['form'].get('target_move', 'all') + date_from = data['form'].get('date_from', time.strftime('%Y-%m-%d')) + + if data['form']['result_selection'] == 'customer': + account_type = ['receivable'] + elif data['form']['result_selection'] == 'supplier': + account_type = ['payable'] + else: + account_type = ['payable', 'receivable'] + + movelines, total, dummy = self._get_partner_move_lines(account_type, + date_from, + target_move, + data['form'][ + 'period_length']) + return { + 'doc_ids': self.ids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'get_partner_lines': movelines, + 'get_direction': total, + } diff --git a/base_accounting_kit/report/report_aged_partner.xml b/base_accounting_kit/report/report_aged_partner.xml new file mode 100644 index 0000000..d212692 --- /dev/null +++ b/base_accounting_kit/report/report_aged_partner.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_agedpartnerbalance"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <t t-call="web.internal_layout"> + <div class="page"> + <h2>Aged Partner Balance</h2> + + <div class="row mt32"> + <div class="col-3"> + <strong>Start Date:</strong> + <p t-esc="data['date_from']"/> + </div> + <div class="col-3"> + <strong>Period Length (days)</strong> + <p t-esc="data['period_length']"/> + </div> + </div> + <div class="row mb32"> + <div class="col-3"> + <strong>Partner's:</strong> + <p> + <span t-if="data['result_selection'] == 'customer'">Receivable Accounts</span> + <span t-if="data['result_selection'] == 'supplier'">Payable Accounts</span> + <span t-if="data['result_selection'] == 'customer_supplier'">Receivable and Payable Accounts</span> + </p> + </div> + <div class="col-3"> + <strong>Target Moves:</strong> + <p> + <span t-if="data['target_move'] == 'all'">All Entries</span> + <span t-if="data['target_move'] == 'posted'">All Posted Entries</span> + </p> + </div> + </div> + + <table class="table table-sm table-reports"> + <thead> + <tr> + <th>Partners</th> + <th class="text-right"> + <span>Not due</span> + </th> + <th class="text-right"><span t-esc="data['4']['name']"/></th> + <th class="text-right"><span t-esc="data['3']['name']"/></th> + <th class="text-right"><span t-esc="data['2']['name']"/></th> + <th class="text-right"><span t-esc="data['1']['name']"/></th> + <th class="text-right"><span t-esc="data['0']['name']"/></th> + <th class="text-right">Total</th> + </tr> + <tr t-if="get_partner_lines"> + <th>Account Total</th> + <th class="text-right"><span t-esc="get_direction[6]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + <th class="text-right"><span t-esc="get_direction[4]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + <th class="text-right"><span t-esc="get_direction[3]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + <th class="text-right"><span t-esc="get_direction[2]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + <th class="text-right"><span t-esc="get_direction[1]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + <th class="text-right"><span t-esc="get_direction[0]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + <th class="text-right"><span t-esc="get_direction[5]" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/></th> + </tr> + </thead> + <tbody> + <tr t-foreach="get_partner_lines" t-as="partner"> + <td> + <span t-esc="partner['name']"/> + </td> + <td class="text-right"> + <span t-esc="partner['direction']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="partner['4']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="partner['3']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="partner['2']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="partner['1']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="partner['0']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="partner['total']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + </tbody> + </table> + </div> + </t> + </t> + </template> +</odoo> diff --git a/base_accounting_kit/report/report_financial.py b/base_accounting_kit/report/report_financial.py new file mode 100644 index 0000000..bdf5801 --- /dev/null +++ b/base_accounting_kit/report/report_financial.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# +from odoo import api, fields, models + + +# --------------------------------------------------------- +# Account Financial Report +# --------------------------------------------------------- + + +class AccountFinancialReport(models.Model): + _name = "account.financial.report" + _description = "Account Report" + _rec_name = 'name' + + @api.depends('parent_id', 'parent_id.level') + def _get_level(self): + """Returns a dictionary with key=the ID of a record and + value = the level of this + record in the tree structure.""" + for report in self: + level = 0 + if report.parent_id: + level = report.parent_id.level + 1 + report.level = level + + def _get_children_by_order(self): + """returns a recordset of all the children computed recursively, + and sorted by sequence. Ready for the printing""" + res = self + children = self.search([('parent_id', 'in', self.ids)], + order='sequence ASC') + if children: + for child in children: + res += child._get_children_by_order() + return res + + name = fields.Char('Report Name', required=True, translate=True) + parent_id = fields.Many2one('account.financial.report', 'Parent') + children_ids = fields.One2many( + 'account.financial.report', + 'parent_id', + 'Account Report') + sequence = fields.Integer('Sequence') + level = fields.Integer(compute='_get_level', string='Level', store=True) + type = fields.Selection( + [('sum', 'View'), + ('accounts', 'Accounts'), + ('account_type', 'Account Type'), + ('account_report', 'Report Value')], + 'Type', + default='sum') + account_ids = fields.Many2many( + 'account.account', + 'account_account_financial_report', + 'report_line_id', + 'account_id', + 'Accounts') + account_report_id = fields.Many2one( + 'account.financial.report', + 'Report Value') + account_type_ids = fields.Many2many( + 'account.account.type', + 'account_account_financial_report_type', + 'report_id', 'account_type_id', + 'Account Types') + sign = fields.Selection( + [("-1", 'Reverse balance sign'), ("1", 'Preserve balance sign')], + 'Sign on Reports', required=True, default="1", + help='For accounts that are typically more' + ' debited than credited and that you' + ' would like to print as negative' + ' amounts in your reports, you should' + ' reverse the sign of the balance;' + ' e.g.: Expense account. The same applies' + ' for accounts that are typically more' + ' credited than debited and that you would' + ' like to print as positive amounts in' + ' your reports; e.g.: Income account.') + display_detail = fields.Selection( + [('no_detail', 'No detail'), + ('detail_flat', 'Display children flat'), + ('detail_with_hierarchy', 'Display children with hierarchy')], + 'Display details', + default='detail_flat') + style_overwrite = fields.Selection( + [('0', 'Automatic formatting'), + ('1', 'Main Title 1 (bold, underlined)'), + ('2', 'Title 2 (bold)'), + ('3', 'Title 3 (bold, smaller)'), + ('4', 'Normal Text'), + ('5', 'Italic Text (smaller)'), + ('6', 'Smallest Text')], + 'Financial Report Style', + default='0', + help="You can set up here the format you want this" + " record to be displayed. If you leave the" + " automatic formatting, it will be computed" + " based on the financial reports hierarchy " + "(auto-computed field 'level').") diff --git a/base_accounting_kit/report/report_financial.xml b/base_accounting_kit/report/report_financial.xml new file mode 100644 index 0000000..1ca5812 --- /dev/null +++ b/base_accounting_kit/report/report_financial.xml @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <template id="report_financial"> + <t t-call="web.html_container"> + <t t-call="web.internal_layout"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <div class="page"> + <h2 t-esc="data['form']['account_report_id'][1]"/> + + <div class="row mt32 mb32"> + <div class="col-4"> + <strong>Target Moves:</strong> + <p> + <span t-if="data['form']['target_move'] == 'all'">All Entries</span> + <span t-if="data['form']['target_move'] == 'posted'">All Posted Entries</span> + </p> + </div> + <div class="col-4"> + <p> + <t t-if="data['form']['date_from']"> + <strong>Date from :</strong> + <span t-esc="data['form']['date_from']"/> + <br/> + </t> + <t t-if="data['form']['date_to']"> + <strong>Date to :</strong> + <span t-esc="data['form']['date_to']"/> + </t> + </p> + </div> + </div> + + <table class="table table-sm table-reports" t-if="data['form']['debit_credit'] == 1"> + <thead> + <tr> + <th>Name</th> + <th class="text-right">Debit</th> + <th class="text-right">Credit</th> + <th class="text-right">Balance</th> + </tr> + </thead> + <tbody> + <tr t-foreach="report_lines" t-as="a"> + <t t-if="a['level'] != 0"> + <t t-if="a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: normal;'"/> + </t> + <t t-if="not a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: bold;'"/> + </t> + + <td> + <span style="color: white;" t-esc="'..' * a.get('level', 0)"/> + <span t-att-style="style" t-esc="a.get('name')"/> + </td> + <td class="text-right" style="white-space: text-nowrap;"> + <span t-att-style="style" t-esc="a.get('debit')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right" style="white-space: text-nowrap;"> + <span t-att-style="style" t-esc="a.get('credit')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right" style="white-space: text-nowrap;"> + <span t-att-style="style" t-esc="a.get('balance')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </t> + </tr> + </tbody> + </table> + + <table class="table table-sm table-reports" + t-if="not data['form']['enable_filter'] and not data['form']['debit_credit']"> + <thead> + <tr> + <th>Name</th> + <th class="text-right">Balance</th> + </tr> + </thead> + <tbody> + <tr t-foreach="report_lines" t-as="a"> + <t t-if="a['level'] != 0"> + <t t-if="a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: normal;'"/> + </t> + <t t-if="not a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: bold;'"/> + </t> + + <td> + <span style="color: white;" t-esc="'..' * a.get('level', 0)"/> + <span t-att-style="style" t-esc="a.get('name')"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="a.get('balance')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </t> + </tr> + </tbody> + </table> + + <table class="table table-sm table-reports" + t-if="data['form']['enable_filter'] == 1 and not data['form']['debit_credit']"> + <thead> + <tr> + <th>Name</th> + <th class="text-right">Balance</th> + <th class="text-right"> +<!-- <span t-esc="data['form']['label_filter']"/>--> + <span>Comp</span> + </th> + </tr> + </thead> + <tbody> + <tr t-foreach="report_lines" t-as="a"> + <t t-if="a['level'] != 0"> + <t t-if="a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: normal;'"/> + </t> + <t t-if="not a.get('level') > 3"> + <t t-set="style" t-value="'font-weight: bold;'"/> + </t> + <td> + <span style="color: white;" t-esc="'..'"/> + <span t-att-style="style" t-esc="a.get('name')"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="a.get('balance')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="a.get('balance_cmp')"/> + </td> + </t> + </tr> + </tbody> + </table> + </div> + </t> + </t> + </template> +</odoo>
\ No newline at end of file diff --git a/base_accounting_kit/report/report_journal_audit.py b/base_accounting_kit/report/report_journal_audit.py new file mode 100644 index 0000000..ea5ab3f --- /dev/null +++ b/base_accounting_kit/report/report_journal_audit.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +import time + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportJournal(models.AbstractModel): + _name = 'report.base_accounting_kit.report_journal_audit' + _description = 'Journal Report' + + def lines(self, target_move, journal_ids, sort_selection, data): + if isinstance(journal_ids, int): + journal_ids = [journal_ids] + move_state = ['draft', 'posted'] + if target_move == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_ids)] + query_get_clause[2] + query = 'SELECT "account_move_line".id FROM ' + query_get_clause[ + 0] + ', account_move am, account_account acc WHERE "account_move_line".account_id = acc.id AND "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + \ + query_get_clause[1] + ' ORDER BY ' + if sort_selection == 'date': + query += '"account_move_line".date' + else: + query += 'am.name' + query += ', "account_move_line".move_id, acc.code' + self.env.cr.execute(query, tuple(params)) + ids = (x[0] for x in self.env.cr.fetchall()) + return self.env['account.move.line'].browse(ids) + + def _sum_debit(self, data, journal_id): + move_state = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[ + 2] + self.env.cr.execute('SELECT SUM(debit) FROM ' + query_get_clause[ + 0] + ', account_move am ' + 'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + + query_get_clause[1] + ' ', + tuple(params)) + return self.env.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, data, journal_id): + move_state = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[ + 2] + self.env.cr.execute('SELECT SUM(credit) FROM ' + query_get_clause[ + 0] + ', account_move am ' + 'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + + query_get_clause[1] + ' ', + tuple(params)) + return self.env.cr.fetchone()[0] or 0.0 + + def _get_taxes(self, data, journal_id): + move_state = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[ + 2] + query = """ + SELECT rel.account_tax_id, SUM("account_move_line".balance) AS base_amount + FROM account_move_line_account_tax_rel rel, """ + query_get_clause[ + 0] + """ + LEFT JOIN account_move am ON "account_move_line".move_id = am.id + WHERE "account_move_line".id = rel.account_move_line_id + AND am.state IN %s + AND "account_move_line".journal_id IN %s + AND """ + query_get_clause[1] + """ + GROUP BY rel.account_tax_id""" + self.env.cr.execute(query, tuple(params)) + ids = [] + base_amounts = {} + for row in self.env.cr.fetchall(): + ids.append(row[0]) + base_amounts[row[0]] = row[1] + + res = {} + for tax in self.env['account.tax'].browse(ids): + self.env.cr.execute( + 'SELECT sum(debit - credit) FROM ' + query_get_clause[ + 0] + ', account_move am ' + 'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + + query_get_clause[1] + ' AND tax_line_id = %s', + tuple(params + [tax.id])) + res[tax] = { + 'base_amount': base_amounts[tax.id], + 'tax_amount': self.env.cr.fetchone()[0] or 0.0, + } + if journal_id.type == 'sale': + # sales operation are credits + res[tax]['base_amount'] = res[tax]['base_amount'] * -1 + res[tax]['tax_amount'] = res[tax]['tax_amount'] * -1 + return res + + def _get_query_get_clause(self, data): + return self.env['account.move.line'].with_context( + data['form'].get('used_context', {}))._query_get() + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + target_move = data['form'].get('target_move', 'all') + sort_selection = data['form'].get('sort_selection', 'date') + + res = {} + for journal in data['form']['journal_ids']: + res[journal] = self.with_context( + data['form'].get('used_context', {})).lines(target_move, + journal, + sort_selection, + data) + return { + 'doc_ids': data['form']['journal_ids'], + 'doc_model': self.env['account.journal'], + 'data': data, + 'docs': self.env['account.journal'].browse( + data['form']['journal_ids']), + 'time': time, + 'lines': res, + 'sum_credit': self._sum_credit, + 'sum_debit': self._sum_debit, + 'get_taxes': self._get_taxes, + } diff --git a/base_accounting_kit/report/report_journal_audit.xml b/base_accounting_kit/report/report_journal_audit.xml new file mode 100644 index 0000000..0ab38ac --- /dev/null +++ b/base_accounting_kit/report/report_journal_audit.xml @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <template id="report_journal_audit"> + <t t-call="web.html_container"> + <t t-call="web.internal_layout"> + <t t-foreach="docs" t-as="o"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <div class="page"> + <span t-esc="context_timestamp(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M')"/> + <h2> + <t t-esc="o.name"/> + Journal + </h2> + + <div class="row mt32"> + + <div class="col-3"> + <strong>Company:</strong> + <p t-esc="env.company.name"/> + </div> + + <div class="col-3"> + <strong>Journal:</strong> + <p t-esc="o.name"/> + </div> + + <div class="col-3"> + <strong>Entries Sorted By:</strong> + <p t-if="data['form'].get('sort_selection') != 'l.date'">Journal Entry Number</p> + <p t-if="data['form'].get('sort_selection') == 'l.date'">Date</p> + </div> + + <div class="col-3"> + <strong>Target Moves:</strong> + <p t-if="data['form']['target_move'] == 'all'">All Entries</p> + <p t-if="data['form']['target_move'] == 'posted'">All Posted Entries</p> + </div> + + </div> + + <table class="table table-sm"> + <thead> + <tr> + <th>Move</th> + <th>Date</th> + <th>Account</th> + <th>Partner</th> + <th>Label</th> + <th>Debit</th> + <th>Credit</th> + <th t-if="data['form']['amount_currency']">Currency</th> + </tr> + </thead> + + <tbody> + <tr t-foreach="lines[o.id]" t-as="aml"> + <td> + <span t-esc="aml.move_id.name != '/' and aml.move_id.name or ('*'+str(aml.move_id.id))"/> + </td> + <td> + <span t-field="aml.date"/> + </td> + <td> + <span t-field="aml.account_id.code"/> + </td> + <td> + <span t-esc="aml.sudo().partner_id and aml.sudo().partner_id.name and aml.sudo().partner_id.name[:23] or ''"/> + </td> + <td> + <span t-esc="aml.name and aml.name[:35]"/> + </td> + <td> + <span t-esc="aml.debit" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td> + <span t-esc="aml.credit" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td t-if="data['form']['amount_currency'] and aml.amount_currency"> + <span t-esc="aml.amount_currency" + t-options="{'widget': 'monetary', 'display_currency': aml.currency_id}"/> + </td> + </tr> + </tbody> + </table> + + <div class="row"> + <div class="col-4 pull-right"> + <table class="table table-sm"> + <tr> + <td> + <strong>Total</strong> + </td> + <td> + <span t-esc="sum_debit(data, o)" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td> + <span t-esc="sum_credit(data, o)" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + </table> + </div> + </div> + + <div class="row"> + <div class="col-4"> + <table class="table table-sm table-reports"> + <thead> + <tr> + <th colspan="3">Tax Declaration</th> + </tr> + <tr> + <th>Name</th> + <th>Base Amount</th> + <th>Tax Amount</th> + </tr> + </thead> + <tbody> + <t t-set="taxes" t-value="get_taxes(data, o)"/> + <tr t-foreach="taxes" t-as="tax"> + <td> + <span t-esc="tax.name"/> + </td> + <td> + <span t-esc="taxes[tax]['base_amount']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td> + <span t-esc="taxes[tax]['tax_amount']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + </tbody> + </table> + </div> + </div> + <p style="page-break-after: always;"/> + </div> + </t> + </t> + </t> + </template> + </data> +</odoo> diff --git a/base_accounting_kit/report/report_partner_ledger.py b/base_accounting_kit/report/report_partner_ledger.py new file mode 100644 index 0000000..6e8951a --- /dev/null +++ b/base_accounting_kit/report/report_partner_ledger.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +import time + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportPartnerLedger(models.AbstractModel): + _name = 'report.base_accounting_kit.report_partnerledger' + _description = 'Partner Ledger Report' + + def _lines(self, data, partner): + full_account = [] + currency = self.env['res.currency'] + query_get_data = self.env['account.move.line'].with_context( + data['form'].get('used_context', {}))._query_get() + reconcile_clause = "" if data['form'][ + 'reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL ' + params = [partner.id, tuple(data['computed']['move_state']), + tuple(data['computed']['account_ids'])] + \ + query_get_data[2] + query = """ + SELECT "account_move_line".id, "account_move_line".date, j.code, acc.code as a_code, acc.name as a_name, "account_move_line".ref, m.name as move_name, "account_move_line".name, "account_move_line".debit, "account_move_line".credit, "account_move_line".amount_currency,"account_move_line".currency_id, c.symbol AS currency_code + FROM """ + query_get_data[0] + """ + LEFT JOIN account_journal j ON ("account_move_line".journal_id = j.id) + LEFT JOIN account_account acc ON ("account_move_line".account_id = acc.id) + LEFT JOIN res_currency c ON ("account_move_line".currency_id=c.id) + LEFT JOIN account_move m ON (m.id="account_move_line".move_id) + WHERE "account_move_line".partner_id = %s + AND m.state IN %s + AND "account_move_line".account_id IN %s AND """ + \ + query_get_data[1] + reconcile_clause + """ + ORDER BY "account_move_line".date""" + self.env.cr.execute(query, tuple(params)) + res = self.env.cr.dictfetchall() + sum = 0.0 + lang_code = self.env.context.get('lang') or 'en_US' + lang = self.env['res.lang'] + lang_id = lang._lang_get(lang_code) + date_format = lang_id.date_format + for r in res: + r['date'] = r['date'] + r['displayed_name'] = '-'.join( + r[field_name] for field_name in ('move_name', 'ref', 'name') + if r[field_name] not in (None, '', '/') + ) + sum += r['debit'] - r['credit'] + r['progress'] = sum + r['currency_id'] = currency.browse(r.get('currency_id')) + full_account.append(r) + return full_account + + def _sum_partner(self, data, partner, field): + if field not in ['debit', 'credit', 'debit - credit']: + return + result = 0.0 + query_get_data = self.env['account.move.line'].with_context( + data['form'].get('used_context', {}))._query_get() + reconcile_clause = "" if data['form'][ + 'reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL ' + + params = [partner.id, tuple(data['computed']['move_state']), + tuple(data['computed']['account_ids'])] + \ + query_get_data[2] + query = """SELECT sum(""" + field + """) + FROM """ + query_get_data[0] + """, account_move AS m + WHERE "account_move_line".partner_id = %s + AND m.id = "account_move_line".move_id + AND m.state IN %s + AND account_id IN %s + AND """ + query_get_data[1] + reconcile_clause + self.env.cr.execute(query, tuple(params)) + + contemp = self.env.cr.fetchone() + if contemp is not None: + result = contemp[0] or 0.0 + return result + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + data['computed'] = {} + + obj_partner = self.env['res.partner'] + query_get_data = self.env['account.move.line'].with_context( + data['form'].get('used_context', {}))._query_get() + data['computed']['move_state'] = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + data['computed']['move_state'] = ['posted'] + result_selection = data['form'].get('result_selection', 'customer') + if result_selection == 'supplier': + data['computed']['ACCOUNT_TYPE'] = ['payable'] + elif result_selection == 'customer': + data['computed']['ACCOUNT_TYPE'] = ['receivable'] + else: + data['computed']['ACCOUNT_TYPE'] = ['payable', 'receivable'] + + self.env.cr.execute(""" + SELECT a.id + FROM account_account a + WHERE a.internal_type IN %s + AND NOT a.deprecated""", + (tuple(data['computed']['ACCOUNT_TYPE']),)) + data['computed']['account_ids'] = [a for (a,) in + self.env.cr.fetchall()] + params = [tuple(data['computed']['move_state']), + tuple(data['computed']['account_ids'])] + query_get_data[2] + reconcile_clause = "" if data['form'][ + 'reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL ' + query = """ + SELECT DISTINCT "account_move_line".partner_id + FROM """ + query_get_data[0] + """, account_account AS account, account_move AS am + WHERE "account_move_line".partner_id IS NOT NULL + AND "account_move_line".account_id = account.id + AND am.id = "account_move_line".move_id + AND am.state IN %s + AND "account_move_line".account_id IN %s + AND NOT account.deprecated + AND """ + query_get_data[1] + reconcile_clause + self.env.cr.execute(query, tuple(params)) + partner_ids = [res['partner_id'] for res in self.env.cr.dictfetchall()] + partners = obj_partner.browse(partner_ids) + partners = sorted(partners, key=lambda x: (x.ref or '', x.name or '')) + return { + 'doc_ids': partner_ids, + 'doc_model': self.env['res.partner'], + 'data': data, + 'docs': partners, + 'time': time, + 'lines': self._lines, + 'sum_partner': self._sum_partner, + } diff --git a/base_accounting_kit/report/report_partner_ledger.xml b/base_accounting_kit/report/report_partner_ledger.xml new file mode 100644 index 0000000..cd5e9fb --- /dev/null +++ b/base_accounting_kit/report/report_partner_ledger.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_partnerledger"> + <t t-call="web.html_container"> + <t t-call="web.internal_layout"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <div class="page"> + <h2>Partner Ledger</h2> + <div class="row"> + <div class="col-3"> + <strong>Company:</strong> + <p t-esc="env.company.name"/> + </div> + <div class="col-3"> + <t t-if="data['form']['date_from']"> + <strong>Date from :</strong> + <span t-esc="data['form']['date_from']"/> + <br/> + </t> + <t t-if="data['form']['date_to']"> + <strong>Date to :</strong> + <span t-esc="data['form']['date_to']"/> + </t> + </div> + <div class="col-3"> + <strong>Target Moves:</strong> + <p t-if="data['form']['target_move'] == 'all'">All Entries</p> + <p t-if="data['form']['target_move'] == 'posted'">All Posted Entries</p> + </div> + </div> + + <table class="table table-sm table-reports"> + <thead> + <tr> + <th>Date</th> + <th>JRNL</th> + <th>Account</th> + <th>Ref</th> + <th>Debit</th> + <th>Credit</th> + <th>Balance</th> + <th t-if="data['form']['amount_currency']">Currency</th> + </tr> + </thead> + <t t-foreach="docs" t-as="o"> + <tbody> + <tr> + <td colspan="4"> + <strong t-esc="o.ref"/> + - + <strong t-esc="o.name"/> + </td> + <td class="text-right"> + <strong t-esc="sum_partner(data, o, 'debit')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <strong t-esc="sum_partner(data, o, 'credit')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <strong t-esc="sum_partner(data, o, 'debit - credit')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + <tr t-foreach="lines(data, o)" t-as="line"> + <td> + <span t-esc="line['date']"/> + </td> + <td> + <span t-esc="line['code']"/> + </td> + <td> + <span t-esc="line['a_code']"/> + </td> + <td> + <span t-esc="line['displayed_name']"/> + </td> + <td class="text-right"> + <span t-esc="line['debit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['credit']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-esc="line['progress']" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right" t-if="data['form']['amount_currency']"> + <t t-if="line['currency_id']"> + <span t-esc="line['amount_currency']" + t-options="{'widget': 'monetary', 'display_currency': line['currency_id']}"/> + </t> + </td> + </tr> + </tbody> + </t> + </table> + </div> + </t> + </t> + </template> +</odoo> diff --git a/base_accounting_kit/report/report_tax.py b/base_accounting_kit/report/report_tax.py new file mode 100644 index 0000000..61445ef --- /dev/null +++ b/base_accounting_kit/report/report_tax.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +from _datetime import datetime + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportTax(models.AbstractModel): + _name = 'report.base_accounting_kit.report_tax' + _description = 'Tax Report' + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + return { + 'data': data['form'], + 'lines': self.get_lines(data.get('form')), + } + + def _sql_from_amls_one(self): + sql = """SELECT "account_move_line".tax_line_id, COALESCE(SUM("account_move_line".debit-"account_move_line".credit), 0) + FROM %s + WHERE %s AND "account_move_line".tax_exigible GROUP BY "account_move_line".tax_line_id""" + return sql + + def _sql_from_amls_two(self): + sql = """SELECT r.account_tax_id, COALESCE(SUM("account_move_line".debit-"account_move_line".credit), 0) + FROM %s + INNER JOIN account_move_line_account_tax_rel r ON ("account_move_line".id = r.account_move_line_id) + INNER JOIN account_tax t ON (r.account_tax_id = t.id) + WHERE %s AND "account_move_line".tax_exigible GROUP BY r.account_tax_id""" + return sql + + def _compute_from_amls(self, options, taxes): + # compute the tax amount + sql = self._sql_from_amls_one() + tables, where_clause, where_params = self.env[ + 'account.move.line']._query_get() + query = sql % (tables, where_clause) + self.env.cr.execute(query, where_params) + results = self.env.cr.fetchall() + for result in results: + if result[0] in taxes: + taxes[result[0]]['tax'] = abs(result[1]) + + # compute the net amount + sql2 = self._sql_from_amls_two() + query = sql2 % (tables, where_clause) + self.env.cr.execute(query, where_params) + results = self.env.cr.fetchall() + for result in results: + if result[0] in taxes: + taxes[result[0]]['net'] = abs(result[1]) + + @api.model + def get_lines(self, options): + taxes = {} + for tax in self.env['account.tax'].search( + [('type_tax_use', '!=', 'none')]): + if tax.children_tax_ids: + for child in tax.children_tax_ids: + if child.type_tax_use != 'none': + continue + taxes[child.id] = {'tax': 0, 'net': 0, 'name': child.name, + 'type': tax.type_tax_use} + else: + taxes[tax.id] = {'tax': 0, 'net': 0, 'name': tax.name, + 'type': tax.type_tax_use} + if options['date_from'] and not options['date_to']: + self.with_context(date_from=options['date_from'], + strict_range=True)._compute_from_amls(options, + taxes) + elif options['date_to'] and not options['date_from']: + self.with_context(date_to=options['date_to'], + strict_range=True)._compute_from_amls(options, + taxes) + elif options['date_from'] and options['date_to']: + self.with_context(date_from=options['date_from'], + date_to=options['date_to'], + strict_range=True)._compute_from_amls(options, + taxes) + else: + date_to = str(datetime.today().date()) + self.with_context(date_to=date_to, + strict_range=True)._compute_from_amls(options, + taxes) + + groups = dict((tp, []) for tp in ['sale', 'purchase']) + for tax in taxes.values(): + if tax['tax']: + groups[tax['type']].append(tax) + return groups diff --git a/base_accounting_kit/report/report_tax.xml b/base_accounting_kit/report/report_tax.xml new file mode 100644 index 0000000..b648379 --- /dev/null +++ b/base_accounting_kit/report/report_tax.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_tax"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <t t-call="web.internal_layout"> + <div class="page"> + <h3>Tax Report</h3> + <div class="row"> + <div class="col-3"> + <strong>Company:</strong> + <p t-esc="env.company.name"/> + </div> + <div> + <t t-if="data['date_from']"> + <strong>Date from :</strong> + <span t-esc="data['date_from']"/> + </t> + <br/> + <t t-if="data['date_to']"> + <strong>Date to :</strong> + <span t-esc="data['date_to']"/> + </t> + </div> + </div> + <table class="table table-sm table-reports"> + <thead> + <tr align="left"> + <th>Sale</th> + <th>Net</th> + <th>Tax</th> + </tr> + </thead> + <tr align="left" t-foreach="lines['sale']" t-as="line"> + <td> + <span t-esc="line.get('name')"/> + </td> + <td> + <span t-att-style="style" t-esc="line.get('net')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td> + <span t-att-style="style" t-esc="line.get('tax')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + <br/> + <tr align="left"> + <td> + <strong>Purchase</strong> + </td> + <td></td> + <td></td> + </tr> + <tr align="left" t-foreach="lines['purchase']" t-as="line"> + <td> + <span t-esc="line.get('name')"/> + </td> + <td> + <span t-att-style="style" t-esc="line.get('net')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td> + <span t-att-style="style" t-esc="line.get('tax')" + t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + </table> + </div> + </t> + </t> + </template> +</odoo> diff --git a/base_accounting_kit/report/report_trial_balance.py b/base_accounting_kit/report/report_trial_balance.py new file mode 100644 index 0000000..d8bbe4d --- /dev/null +++ b/base_accounting_kit/report/report_trial_balance.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) +# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + +import time + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportTrialBalance(models.AbstractModel): + _name = 'report.base_accounting_kit.report_trial_balance' + _description = 'Trial Balance Report' + + def _get_accounts(self, accounts, display_account): + """ compute the balance, debit and credit for the provided accounts + :Arguments: + `accounts`: list of accounts record, + `display_account`: it's used to display either all accounts or those accounts which balance is > 0 + :Returns a list of dictionary of Accounts with following key and value + `name`: Account name, + `code`: Account code, + `credit`: total amount of credit, + `debit`: total amount of debit, + `balance`: total amount of balance, + """ + + account_result = {} + # Prepare sql query base on selected parameters from wizard + tables, where_clause, where_params = self.env[ + 'account.move.line']._query_get() + tables = tables.replace('"', '') + if not tables: + tables = 'account_move_line' + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + # compute the balance, debit and credit for the provided accounts + request = ( + "SELECT account_id AS id, SUM(debit) AS debit, SUM(credit) AS credit, (SUM(debit) - SUM(credit)) AS balance" + \ + " FROM " + tables + " WHERE account_id IN %s " + filters + " GROUP BY account_id") + params = (tuple(accounts.ids),) + tuple(where_params) + self.env.cr.execute(request, params) + for row in self.env.cr.dictfetchall(): + account_result[row.pop('id')] = row + + account_res = [] + for account in accounts: + res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance']) + currency = account.currency_id and account.currency_id or account.company_id.currency_id + res['code'] = account.code + res['name'] = account.name + if account.id in account_result: + res['debit'] = account_result[account.id].get('debit') + res['credit'] = account_result[account.id].get('credit') + res['balance'] = account_result[account.id].get('balance') + if display_account == 'all': + account_res.append(res) + if display_account == 'not_zero' and not currency.is_zero( + res['balance']): + account_res.append(res) + if display_account == 'movement' and ( + not currency.is_zero(res['debit']) or not currency.is_zero( + res['credit'])): + account_res.append(res) + return account_res + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get('active_model'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse( + self.env.context.get('active_ids', [])) + display_account = data['form'].get('display_account') + accounts = docs if model == 'account.account' else self.env[ + 'account.account'].search([]) + account_res = self.with_context( + data['form'].get('used_context'))._get_accounts(accounts, + display_account) + return { + 'doc_ids': self.ids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'Accounts': account_res, + } diff --git a/base_accounting_kit/report/report_trial_balance.xml b/base_accounting_kit/report/report_trial_balance.xml new file mode 100644 index 0000000..88d8f1f --- /dev/null +++ b/base_accounting_kit/report/report_trial_balance.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="report_trial_balance"> + <t t-call="web.html_container"> + <t t-set="data_report_margin_top" t-value="12"/> + <t t-set="data_report_header_spacing" t-value="9"/> + <t t-set="data_report_dpi" t-value="110"/> + <t t-call="web.internal_layout"> + <div class="page"> + <h2><span t-esc="env.company.name"/>: Trial Balance</h2> + + <div class="row mt32"> + <div class="col-4"> + <strong>Display Account:</strong> + <p> + <span t-if="data['display_account'] == 'all'">All accounts</span> + <span t-if="data['display_account'] == 'movement'">With movements</span> + <span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span> + </p> + </div> + <div class="col-4"> + <p> + <t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t> + <t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t> + </p> + </div> + <div class="col-4"> + <strong>Target Moves:</strong> + <p> + <span t-if="data['target_move'] == 'all'">All Entries</span> + <span t-if="data['target_move'] == 'posted'">All Posted Entries</span> + </p> + </div> + </div> + + <table class="table table-sm table-reports"> + <thead> + <tr> + <th>Code</th> + <th>Account</th> + <th class="text-right">Debit</th> + <th class="text-right">Credit</th> + <th class="text-right">Balance</th> + </tr> + </thead> + <tbody> + <tr t-foreach="Accounts" t-as="account"> + <td> + <span t-att-style="style" t-esc="account['code']"/> + </td> + <td> + <span style="color: white;" t-esc="'..'"/> + <span t-att-style="style" t-esc="account['name']"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="account['debit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="account['credit']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + <td class="text-right"> + <span t-att-style="style" t-esc="account['balance']" t-options="{'widget': 'monetary', 'display_currency': env.company.currency_id}"/> + </td> + </tr> + </tbody> + </table> + </div> + </t> + </t> + </template> +</odoo> |
