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 /dynamic_accounts_report/wizard/ageing.py | |
| parent | b57188be371d36d96caac4b8d65a40745c0e972c (diff) | |
initial commit
Diffstat (limited to 'dynamic_accounts_report/wizard/ageing.py')
| -rw-r--r-- | dynamic_accounts_report/wizard/ageing.py | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/dynamic_accounts_report/wizard/ageing.py b/dynamic_accounts_report/wizard/ageing.py new file mode 100644 index 0000000..652ff29 --- /dev/null +++ b/dynamic_accounts_report/wizard/ageing.py @@ -0,0 +1,682 @@ +import time +from datetime import datetime + +from dateutil.relativedelta import relativedelta +from odoo import fields, models, api, _ +from odoo.tools import float_is_zero + +import io +import json + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class AgeingView(models.TransientModel): + _inherit = "account.common.report" + _name = 'account.partner.ageing' + + period_length = fields.Integer(string='Period Length (days)', + required=True, default=30) + date_from = fields.Date(default=lambda *a: time.strftime('%Y-%m-%d')) + result_selection = fields.Selection([('customer', 'Receivable Accounts'), + ('supplier', 'Payable Accounts'), + ('customer_supplier', + 'Receivable and Payable Accounts') + ], string="Partner's", required=True, + default='customer') + + partner_ids = fields.Many2many( + 'res.partner', string='Partner' + ) + partner_category_ids = fields.Many2many( + 'res.partner.category', string='Partner Tag', + ) + + @api.model + def view_report(self, option): + r = self.env['account.partner.ageing'].search([('id', '=', option[0])]) + + data = { + 'result_selection': r.result_selection, + 'model': self, + 'journals': r.journal_ids, + 'target_move': r.target_move, + 'period_length': r.period_length, + 'partners': r.partner_ids, + 'partner_tags': r.partner_category_ids, + + } + if r.date_from: + data.update({ + 'date_from': r.date_from, + }) + + filters = self.get_filter(option) + + records = self._get_report_values(data) + + currency = self._get_currency() + + return { + 'name': "Partner Ageing", + 'type': 'ir.actions.client', + 'tag': 'p_a', + 'filters': filters, + 'report_lines': records['Partners'], + 'currency': currency, + } + + def get_filter(self, option): + data = self.get_filter_data(option) + filters = {} + + if data.get('target_move'): + filters['target_move'] = data.get('target_move') + if data.get('date_from'): + filters['date_from'] = data.get('date_from') + if data.get('result_selection') == 'customer': + filters['result_selection'] = 'Receivable' + elif data.get('result_selection') == 'supplier': + filters['result_selection'] = 'Payable' + else: + filters['result_selection'] = 'Receivable and Payable' + + if data.get('partners'): + filters['partners'] = self.env['res.partner'].browse( + data.get('partners')).mapped('name') + else: + filters['partners'] = ['All'] + + if data.get('partner_tags', []): + filters['partner_tags'] = self.env['res.partner.category'].browse( + data.get('partner_tags', [])).mapped('name') + else: + filters['partner_tags'] = ['All'] + + filters['company_id'] = '' + filters['company_name'] = data.get('company_name') + filters['partners_list'] = data.get('partners_list') + filters['category_list'] = data.get('category_list') + filters['company_name'] = data.get('company_name') + filters['target_move'] = data.get('target_move').capitalize() + + + return filters + + def get_filter_data(self, option): + r = self.env['account.partner.ageing'].search([('id', '=', option[0])]) + default_filters = {} + company_id = self.env.company + company_domain = [('company_id', '=', company_id.id)] + partner = r.partner_ids if r.partner_ids else self.env[ + 'res.partner'].search([]) + categories = r.partner_category_ids if r.partner_category_ids \ + else self.env['res.partner.category'].search([]) + + filter_dict = { + 'partners': r.partner_ids.ids, + 'partner_tags': r.partner_category_ids.ids, + 'company_id': company_id.id, + 'date_from': r.date_from, + + 'target_move': r.target_move, + 'result_selection': r.result_selection, + 'partners_list': [(p.id, p.name) for p in partner], + 'category_list': [(c.id, c.name) for c in categories], + 'company_name': company_id and company_id.name, + } + filter_dict.update(default_filters) + return filter_dict + + def _get_report_values(self, data): + docs = data['model'] + date_from = data.get('date_from').strftime('%Y-%m-%d') + if data['result_selection'] == 'customer': + account_type = ['receivable'] + elif data['result_selection'] == 'supplier': + account_type = ['payable'] + else: + account_type = ['payable', 'receivable'] + target_move = data['target_move'] + partners = data.get('partners') + if data['partner_tags']: + partners = self.env['res.partner'].search( + [('category_id', 'in', data['partner_tags'].ids)]) + + account_res = self._get_partner_move_lines(data, partners, date_from, + target_move, + account_type, + data['period_length']) + + return { + 'doc_ids': self.ids, + 'docs': docs, + 'time': time, + 'Partners': account_res, + + } + + @api.model + def create(self, vals): + vals['target_move'] = 'posted' + res = super(AgeingView, self).create(vals) + return res + + def write(self, vals): + if vals.get('target_move'): + vals.update({'target_move': vals.get('target_move').lower()}) + + if vals.get('partner_ids'): + vals.update( + {'partner_ids': [(4, j) for j in vals.get('partner_ids')]}) + if not vals.get('partner_ids'): + vals.update({'partner_ids': [(5,)]}) + if vals.get('partner_category_ids'): + vals.update({'partner_category_ids': [(4, j) for j in vals.get( + 'partner_category_ids')]}) + if not vals.get('partner_category_ids'): + vals.update({'partner_category_ids': [(5,)]}) + + res = super(AgeingView, self).write(vals) + return res + + def _get_partner_move_lines(self, data, partners, date_from, target_move, + account_type, + period_length): + + 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)) + + 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),) + partner_list = '(l.partner_id IS NOT NULL)' + if partners: + list = tuple(partners.ids) + tuple([0]) + if list: + partner_list = '(l.partner_id IS NULL OR l.partner_id IN %s)' + arg_list += (tuple(list),) + 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 + AND ''' + partner_list + ''' + + 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 + move_id = line.move_id.id + move_name = line.move_id.name + date_maturity = line.date_maturity + account_id = line.account_id.name + account_code = line.account_id.code + jrnl_id = line.journal_id.name + currency_id = line.company_id.currency_id.position + currency_symbol = line.company_id.currency_id.symbol + + 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, + 'partner_id': partner_id, + 'move': move_name, + 'jrnl': jrnl_id, + 'currency': currency_id, + 'symbol': currency_symbol, + 'acc_name': account_id, + 'mov_id': move_id, + 'acc_code': account_code, + 'date': date_maturity, + 'amount': line_amount, + 'period6': 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 + move_id = line.move_id.id + move_name = line.move_id.name + date_maturity = line.date_maturity + account_id = line.account_id.name + account_code = line.account_id.code + jrnl_id = line.journal_id.name + currency_id = line.company_id.currency_id.position + currency_symbol = line.company_id.currency_id.symbol + 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 + if i + 1 == 5: + period5 = i + 1 + lines[partner_id].append({ + 'period5': period5, + 'line': line, + 'partner_id': partner_id, + 'move': move_name, + 'currency': currency_id, + 'symbol': currency_symbol, + 'jrnl': jrnl_id, + 'acc_name': account_id, + 'mov_id': move_id, + 'acc_code': account_code, + 'date': date_maturity, + 'amount': line_amount, + }) + elif i + 1 == 4: + period4 = i + 1 + lines[partner_id].append({ + + 'period4': period4, + 'line': line, + 'partner_id': partner_id, + 'move': move_name, + 'jrnl': jrnl_id, + 'acc_name': account_id, + 'currency': currency_id, + 'symbol': currency_symbol, + 'mov_id': move_id, + 'acc_code': account_code, + 'date': date_maturity, + 'amount': line_amount, + }) + elif i + 1 == 3: + period3 = i + 1 + lines[partner_id].append({ + + 'period3': period3, + 'line': line, + 'partner_id': partner_id, + 'move': move_name, + 'jrnl': jrnl_id, + 'acc_name': account_id, + 'currency': currency_id, + 'symbol': currency_symbol, + 'mov_id': move_id, + 'acc_code': account_code, + 'date': date_maturity, + 'amount': line_amount, + }) + elif i + 1 == 2: + period2 = i + 1 + lines[partner_id].append({ + + 'period2': period2, + 'line': line, + 'partner_id': partner_id, + 'move': move_name, + 'jrnl': jrnl_id, + 'acc_name': account_id, + 'currency': currency_id, + 'symbol': currency_symbol, + 'mov_id': move_id, + 'acc_code': account_code, + 'date': date_maturity, + 'amount': line_amount, + }) + else: + period1 = i + 1 + lines[partner_id].append({ + + 'period1': period1, + 'line': line, + 'partner_id': partner_id, + 'move': move_name, + 'jrnl': jrnl_id, + 'acc_name': account_id, + 'currency': currency_id, + 'symbol': currency_symbol, + 'mov_id': move_id, + 'acc_code': account_code, + 'date': date_maturity, + 'amount': line_amount, + }) + + 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 + for rec in lines: + if partner['partner_id'] == rec: + child_lines = lines[rec] + values['child_lines'] = child_lines + 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_currency(self): + journal = self.env['account.journal'].browse( + self.env.context.get('default_journal_id', False)) + if journal.currency_id: + return journal.currency_id.id + lang = self.env.user.lang + if not lang: + lang = 'en_US' + lang = lang.replace("_", '-') + currency_array = [self.env.company.currency_id.symbol, + self.env.company.currency_id.position, lang] + return currency_array + + def get_dynamic_xlsx_report(self, data, response, report_data, dfr_data ): + + report_data_main = json.loads(report_data) + output = io.BytesIO() + + filters = json.loads(data) + + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + head = workbook.add_format({'align': 'center', 'bold': True, + 'font_size': '20px'}) + sub_heading = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '10px', + 'border': 1, + 'border_color': 'black'}) + heading = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '10px', + 'border': 2, + 'border_color': 'black'}) + txt = workbook.add_format({'font_size': '10px', 'border': 1}) + txt_l = workbook.add_format( + {'font_size': '10px', 'border': 1, 'bold': True}) + txt_v = workbook.add_format( + {'align': 'right', 'font_size': '10px', 'border': 1}) + sheet.merge_range('A2:H3', + filters.get('company_name') + ':' + ' Partner Ageing', + head) + date_head = workbook.add_format({'align': 'center', 'bold': True, + 'font_size': '10px'}) + date_style = workbook.add_format({'align': 'center', + 'font_size': '10px'}) + if filters.get('date_from'): + sheet.merge_range('A4:B4', + 'As On Date: ' + filters.get('date_from'), + date_head) + sheet.merge_range('C4:E4', + 'Account Type: ' + filters.get('result_selection'), + date_head) + sheet.merge_range('A5:B5', + 'Target Moves: ' + filters.get('target_move'), + date_head) + sheet.merge_range('D5:F5', ' Partners: ' + ', '.join( + [lt or '' for lt in + filters['partners']]), date_head) + sheet.merge_range('G5:H5', ' Partner Type: ' + ', '.join( + [lt or '' for lt in + filters['partner_tags']]), + date_head) + + sheet.merge_range('A7:C7', 'Partner', heading) + sheet.write('D7', 'Total', heading) + sheet.write('E7', 'Not Due', heading) + sheet.write('F7', '0-30', heading) + sheet.write('G7', '30-60', heading) + sheet.write('H7', '60-90', heading) + sheet.write('I7', '90-120', heading) + sheet.write('J7', '120+', heading) + + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(5, 0, 15) + sheet.set_column(6, 1, 15) + sheet.set_column(7, 2, 15) + sheet.set_column(8, 3, 15) + sheet.set_column(9, 4, 15) + sheet.set_column(10, 5, 15) + sheet.set_column(11, 6, 15) + + for rec_data in report_data_main[0]: + one_lst = [] + two_lst = [] + + row += 1 + sheet.merge_range(row, col, row, col + 2, rec_data['name'], txt_l) + sheet.write(row, col + 3, rec_data['total'], txt_l) + sheet.write(row, col + 4, rec_data['direction'], txt_l) + sheet.write(row, col + 5, rec_data['4'], txt_l) + sheet.write(row, col + 6, rec_data['3'], txt_l) + sheet.write(row, col + 7, rec_data['2'], txt_l) + sheet.write(row, col + 8, rec_data['1'], txt_l) + sheet.write(row, col + 9, rec_data['0'], txt_l) + row += 1 + sheet.write(row, col, 'Entry Label', sub_heading) + sheet.write(row, col + 1, 'Due Date', sub_heading) + sheet.write(row, col + 2, 'Journal', sub_heading) + sheet.write(row, col + 3, 'Account', sub_heading) + sheet.write(row, col + 4, 'Not Due', sub_heading) + sheet.write(row, col + 5, '0 - 30', sub_heading) + sheet.write(row, col + 6, '30 - 60', sub_heading) + sheet.write(row, col + 7, '60 - 90', sub_heading) + sheet.write(row, col + 8, '90 - 120', sub_heading) + sheet.write(row, col + 9, '120 +', sub_heading) + + for line_data in rec_data['child_lines']: + row += 1 + sheet.write(row, col, line_data.get('move'), txt) + sheet.write(row, col + 1, line_data.get('date'), txt) + sheet.write(row, col + 2, line_data.get('jrnl'), txt) + sheet.write(row, col + 3, line_data.get('acc_code'), txt) + if line_data.get('period6'): + sheet.write(row, col + 4, line_data.get('amount'), txt) + else: + sheet.write(row, col + 4, "0", txt_v) + if line_data.get('period5'): + sheet.write(row, col + 5, line_data.get('amount'), txt) + else: + sheet.write(row, col + 5, "0", txt_v) + if line_data.get('period4'): + sheet.write(row, col + 6, line_data.get('amount'), txt) + else: + sheet.write(row, col + 6, "0", txt_v) + if line_data.get('period3'): + sheet.write(row, col + 7, line_data.get('amount'), txt) + else: + sheet.write(row, col + 7, "0", txt_v) + if line_data.get('period2'): + sheet.write(row, col + 8, line_data.get('amount'), txt) + else: + sheet.write(row, col + 8, "0", txt_v) + if line_data.get('period1'): + sheet.write(row, col + 9, line_data.get('amount'), txt) + else: + sheet.write(row, col + 9, "0", txt_v) + + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() |
