diff --git a/partner_ageing_billwise/__init__.py b/partner_ageing_billwise/__init__.py new file mode 100644 index 000000000..51d98fd8f --- /dev/null +++ b/partner_ageing_billwise/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import report +from . import models diff --git a/partner_ageing_billwise/__manifest__.py b/partner_ageing_billwise/__manifest__.py new file mode 100644 index 000000000..ab7f9f74a --- /dev/null +++ b/partner_ageing_billwise/__manifest__.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2017-TODAY Cybrosys Technologies(). +# Author: LINTO C.T.() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +{ + 'name': 'Aged Partner Balance Bill-Wise', + 'version': '10.0.1.0.0', + 'category': 'Accounting', + 'summary': 'Bill-Wise Aged Partner Balance', + 'description': """ + This module provides features to take a pdf report of bill-wise aged partner balance. + """, + 'author': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'depends': ['account_accountant'], + 'data': [ + 'report/report_agedpartnerbalance.xml', + 'views/report_aged_partner_billwise.xml', + ], + 'demo': [], + 'images': ['static/description/banner.jpg'], + 'license': 'LGPL-3', + 'installable': True, + 'application': False, + 'auto_install': False, +} diff --git a/partner_ageing_billwise/models/__init__.py b/partner_ageing_billwise/models/__init__.py new file mode 100644 index 000000000..b70d58871 --- /dev/null +++ b/partner_ageing_billwise/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import report_aged_partner_balance diff --git a/partner_ageing_billwise/models/report_aged_partner_balance.py b/partner_ageing_billwise/models/report_aged_partner_balance.py new file mode 100644 index 000000000..e9ad9e3c7 --- /dev/null +++ b/partner_ageing_billwise/models/report_aged_partner_balance.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from odoo import fields, models + + +class AgedTrialBalanceBillwise(models.TransientModel): + _inherit = 'account.aged.trial.balance' + _description = 'Account Aged Trial balance Report Bill-wise' + + report_type = fields.Selection([('bill', 'Bill-wise'), ('customer', 'Customer-wise')], default='customer', + string="Report Type") + + def _print_report(self, data): + data['form']['report_type'] = self.report_type + return super(AgedTrialBalanceBillwise, self)._print_report(data) diff --git a/partner_ageing_billwise/report/__init__.py b/partner_ageing_billwise/report/__init__.py new file mode 100644 index 000000000..676b399b5 --- /dev/null +++ b/partner_ageing_billwise/report/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import account_aged_partner_balance diff --git a/partner_ageing_billwise/report/account_aged_partner_balance.py b/partner_ageing_billwise/report/account_aged_partner_balance.py new file mode 100644 index 000000000..991000a6d --- /dev/null +++ b/partner_ageing_billwise/report/account_aged_partner_balance.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- + +import time +from datetime import datetime +from dateutil.relativedelta import relativedelta +from odoo import api, models, _ +from odoo.tools import float_is_zero + + +class ReportAgedPartnerBalanceBillwise(models.AbstractModel): + + _inherit = 'report.account.report_agedpartnerbalance' + + def _get_billwise_move_lines(self, account_type, date_from, target_move, period_length): + periods = {} + start = datetime.strptime(date_from, "%Y-%m-%d") + for i in range(5)[::-1]: + stop = start - relativedelta(days=period_length) + periods[str(i)] = { + 'name': (i!=0 and (str((5-(i+1)) * period_length) + '-' + str((5-i) * period_length)) or ('+'+str(4 * period_length))), + 'stop': start.strftime('%Y-%m-%d'), + 'start': (i!=0 and stop.strftime('%Y-%m-%d') or False), + } + start = stop - relativedelta(days=1) + res = [] + total = [] + cr = self.env.cr + user_company = self.env.user.company_id.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 create_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, user_company) + 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 = %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 = %s''' + cr.execute(query, (tuple(move_state), tuple(account_type), date_from, tuple(partner_ids), date_from, user_company)) + 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 = line.balance + if line.balance == 0: + continue + for partial_line in line.matched_debit_ids: + if partial_line.create_date[:10] <= date_from: + line_amount += partial_line.amount + for partial_line in line.matched_credit_ids: + if partial_line.create_date[:10] <= date_from: + line_amount -= partial_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] = {'': } + 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, user_company) + + 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 = %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 = line.balance + if line.balance == 0: + continue + for partial_line in line.matched_debit_ids: + if partial_line.create_date[:10] <= date_from: + line_amount += partial_line.amount + for partial_line in line.matched_credit_ids: + if partial_line.create_date[:10] <= date_from: + line_amount -= partial_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: + 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.user.company_id.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.user.company_id.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: + res.append(values) + return res, total, lines + + @api.model + def render_html(self, docids, data=None): + 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'] + + if data['form'].get('report_type') == 'bill': + movelines, total, dummy = self._get_billwise_move_lines(account_type, date_from, target_move, data['form']['period_length']) + for partner in dummy: + for line in dummy[partner]: + line['intervals'] = { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + '4': 0, + '5': 0, + 'total': 0 + } + line['intervals'][str(line['period']-1)] = line['amount'] + line['intervals']['total'] += line['amount'] + else: + movelines, total, dummy = self._get_partner_move_lines(account_type, date_from, target_move, data['form']['period_length']) + docargs = { + 'doc_ids': self.ids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'lines': dummy, + 'get_partner_lines': movelines, + 'get_direction': total, + } + return self.env['report'].render('account.report_agedpartnerbalance', docargs) diff --git a/partner_ageing_billwise/report/report_agedpartnerbalance.xml b/partner_ageing_billwise/report/report_agedpartnerbalance.xml new file mode 100644 index 000000000..19b85eed8 --- /dev/null +++ b/partner_ageing_billwise/report/report_agedpartnerbalance.xml @@ -0,0 +1,106 @@ + + + + diff --git a/partner_ageing_billwise/static/description/banner.jpg b/partner_ageing_billwise/static/description/banner.jpg new file mode 100644 index 000000000..8ef2237a7 Binary files /dev/null and b/partner_ageing_billwise/static/description/banner.jpg differ diff --git a/partner_ageing_billwise/static/description/bill-wise-repo.png b/partner_ageing_billwise/static/description/bill-wise-repo.png new file mode 100644 index 000000000..a6b4025c9 Binary files /dev/null and b/partner_ageing_billwise/static/description/bill-wise-repo.png differ diff --git a/partner_ageing_billwise/static/description/customer-wise.png b/partner_ageing_billwise/static/description/customer-wise.png new file mode 100644 index 000000000..8a2dc4151 Binary files /dev/null and b/partner_ageing_billwise/static/description/customer-wise.png differ diff --git a/partner_ageing_billwise/static/description/cybro_logo.png b/partner_ageing_billwise/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/partner_ageing_billwise/static/description/cybro_logo.png differ diff --git a/partner_ageing_billwise/static/description/icon.png b/partner_ageing_billwise/static/description/icon.png new file mode 100644 index 000000000..6d11b1aec Binary files /dev/null and b/partner_ageing_billwise/static/description/icon.png differ diff --git a/partner_ageing_billwise/static/description/index.html b/partner_ageing_billwise/static/description/index.html new file mode 100644 index 000000000..aabaa3ab6 --- /dev/null +++ b/partner_ageing_billwise/static/description/index.html @@ -0,0 +1,108 @@ +
+
+

Aged Partner Balance Bill-Wise

+

+

Cybrosys Technologies

+
+
+
+ Bill-wise aged partner balance report.
+ Customer-wise aged partner balance report.
+
+
+
+ +
+
+
+

Overview

+

+ By default, Odoo doesn't have a bill-wise aged partner balance report. + With this module, we can have a pdf report of bill-wise and customer-wise(Odoo's default) + aged partner balance reports. +

+
+
+
+ +
+
+
+

+

Specify Report Type

+

+

+
+
+ +
+
+
+

+ A new field is added to the aged partner balance wizard to specify the report type. + We can have two types of reports, bill-wise or customer-wise. Bill-wise option will output + a bill-wise aged partner balance and customer-wise option will output a customer wise + aged partner balance report. +

+
+
+
+ +
+
+
+

+

Bill-Wise Report

+

+

+
+
+ +
+
+
+
+ +
+
+
+

+

Customer-Wise Report

+

+

+
+
+ +
+
+
+

Type of report will be mentioned in the report.

+
+
+
+ +
+

Need Any Help?

+ +
diff --git a/partner_ageing_billwise/static/description/report_type_wiz.png b/partner_ageing_billwise/static/description/report_type_wiz.png new file mode 100644 index 000000000..574214d66 Binary files /dev/null and b/partner_ageing_billwise/static/description/report_type_wiz.png differ diff --git a/partner_ageing_billwise/views/report_aged_partner_billwise.xml b/partner_ageing_billwise/views/report_aged_partner_billwise.xml new file mode 100644 index 000000000..a7267385b --- /dev/null +++ b/partner_ageing_billwise/views/report_aged_partner_billwise.xml @@ -0,0 +1,13 @@ + + + + Aged Partner Balance Billwise + account.aged.trial.balance + + + + + + + +