You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							309 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							309 lines
						
					
					
						
							14 KiB
						
					
					
				| # -*- coding: utf-8 -*- | |
| ############################################################################# | |
| # | |
| #    Cybrosys Technologies Pvt. Ltd. | |
| # | |
| #    Copyright (C) 2023-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.account_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.account_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._convert(line.balance, user_currency, | |
|                                                line.company_id, date_from) | |
|             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._convert( | |
|                         from_amount=partial_line.amount, | |
|                         to_currency=user_currency, | |
|                         company=partial_line.company_id, | |
|                         date=date_from | |
|                     ) | |
|             for partial_line in line.matched_credit_ids: | |
|                 if partial_line.max_date <= date_from: | |
|                     line_amount -= ResCurrency._convert( | |
|                         from_amount=partial_line.amount, | |
|                         to_currency=user_currency, | |
|                         company=partial_line.company_id, | |
|                         date=date_from | |
|                     ) | |
|             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.account_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._convert( | |
|                     from_amount=line.balance, | |
|                     to_currency=user_currency, | |
|                     company=line.company_id, | |
|                     date=date_from | |
|                 ) | |
|                 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._convert( | |
|                             from_amount=partial_line.amount, | |
|                             to_currency=user_currency, | |
|                             company=partial_line.company_id, | |
|                             date=date_from | |
|                         ) | |
|                 for partial_line in line.matched_credit_ids: | |
|                     if partial_line.max_date <= date_from: | |
|                         line_amount -= ResCurrency._convert( | |
|                             from_amount=partial_line.amount, | |
|                             to_currency=user_currency, | |
|                             company=partial_line.company_id, | |
|                             date=date_from | |
|                         ) | |
|                 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.")) | |
|         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 = ['asset_receivable'] | |
|         elif data['form']['result_selection'] == 'supplier': | |
|             account_type = ['liability_payable'] | |
|         else: | |
|             account_type = ['liability_payable', 'asset_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, | |
|         }
 | |
| 
 |