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.
 
 
 
 

295 lines
13 KiB

# © 2016 Julien Coux (Camptocamp)
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, api
from odoo.tools import float_is_zero
from odoo.osv import expression
from datetime import date, datetime
import pandas as pd
class OpenItemsReport(models.AbstractModel):
_name = 'report.account_financial_report.open_items'
@api.model
def get_html(self, given_context=None):
return self._get_html()
def _get_html(self):
result = {}
rcontext = {}
context = dict(self.env.context)
rcontext.update(context.get('data'))
active_id = context.get('active_id')
wiz = self.env['open.items.report.wizard'].browse(active_id)
rcontext['o'] = wiz
result['html'] = self.env.ref(
'account_financial_report.report_open_items').render(rcontext)
return result
def _get_account_partial_reconciled(self, move_lines_data, date_at_object):
reconciled_ids = []
for move_line in move_lines_data:
if move_line['reconciled']:
reconciled_ids += [move_line['id']]
domain = [('max_date', '>=', date_at_object)]
domain += expression.OR(
[[('debit_move_id', 'in', reconciled_ids)],
[('credit_move_id', 'in', reconciled_ids)]])
fields = ['debit_move_id', 'credit_move_id', 'amount']
accounts_partial_reconcile = \
self.env['account.partial.reconcile'].search_read(
domain=domain,
fields=fields
)
debit_accounts_partial_amount = {}
credit_accounts_partial_amount = {}
for account_partial_reconcile_data in accounts_partial_reconcile:
debit_move_id = account_partial_reconcile_data['debit_move_id'][0]
credit_move_id = account_partial_reconcile_data['credit_move_id'][0]
if debit_move_id not in debit_accounts_partial_amount.keys():
debit_accounts_partial_amount[debit_move_id] = 0.0
debit_accounts_partial_amount[debit_move_id] += \
account_partial_reconcile_data['amount']
if credit_move_id not in credit_accounts_partial_amount.keys():
credit_accounts_partial_amount[credit_move_id] = 0.0
credit_accounts_partial_amount[credit_move_id] += \
account_partial_reconcile_data['amount']
account_partial_reconcile_data.update({
'debit_move_id': debit_move_id,
'credit_move_id': credit_move_id,
})
return accounts_partial_reconcile, debit_accounts_partial_amount, \
credit_accounts_partial_amount
@api.model
def _get_query_domain(self, account_ids, partner_ids, date_at_object,
target_move, company_id, date_from):
query = """
WHERE aml.account_id in %s and aml.company_id = %s
""" % (tuple(account_ids) if len(account_ids) > 1 else "(%s)" %
account_ids[0], company_id)
if date_from:
query += " and aml.date >= '%s'" % date_from
if partner_ids:
query += " and aml.partner_id in %s" % (tuple(partner_ids),)
if target_move == 'posted':
query += " and am.state = 'posted'"
if date_at_object >= date.today():
query += " and aml.reconciled IS FALSE"
else:
query += """ and ((aml.reconciled IS FALSE OR aml.date >= '%s')
OR aml.full_reconcile_id IS NOT NULL)""" % date_at_object
return query
@api.model
def _get_query(self, account_ids, partner_ids, date_at_object,
target_move, company_id, date_from):
aml_fields = [
'id', 'date', 'move_id', 'journal_id', 'account_id', 'partner_id',
'ref', 'date_maturity', 'amount_residual', 'amount_currency',
'amount_residual_currency', 'debit', 'credit', 'currency_id',
'reconciled', 'full_reconcile_id']
query = ""
# SELECT
for field in aml_fields:
if not query:
query = "SELECT aml.%s" % field
else:
query += ", aml.%s" % field
# name from res_partner
query += ", rp.name as partner_name"
# name from res_currency
query += ", rc.name as currency_name"
# state and name from account_move
query += ", am.state, am.name as move_name"
# FROM
query += """
FROM account_move_line as aml
LEFT JOIN res_partner as rp
ON aml.partner_id = rp.id
LEFT JOIN res_currency as rc
ON aml.currency_id = rc.id
LEFT JOIN account_move as am
ON am.id = aml.move_id
"""
# WHERE
query += self._get_query_domain(account_ids, partner_ids,
date_at_object, target_move,
company_id, date_from)
return query
def _get_accounts_data(self, accounts_ids):
accounts = self.env['account.account'].browse(accounts_ids)
accounts_data = {}
for account in accounts:
accounts_data.update({account.id: {
'id': account.id,
'code': account.code,
'name': account.name,
'hide_account': False,
'currency_id': account.currency_id or False,
'currency_name': account.currency_id.name}
})
return accounts_data
def _get_journals_data(self, journals_ids):
journals = self.env['account.journal'].browse(journals_ids)
journals_data = {}
for journal in journals:
journals_data.update({journal.id: {'id': journal.id,
'code': journal.code}})
return journals_data
def _get_data(self, account_ids, partner_ids, date_at_object, target_move,
company_id, date_from):
query = self._get_query(account_ids, partner_ids, date_at_object,
target_move, company_id, date_from)
self._cr.execute(query)
move_lines_data = pd.DataFrame(self._cr.dictfetchall())
account_ids = set(move_lines_data.account_id.to_list())
accounts_data = self._get_accounts_data(list(account_ids))
journal_ids = set(move_lines_data.journal_id.to_list())
journals_data = self._get_journals_data(list(journal_ids))
move_lines_data = move_lines_data.fillna(0).to_dict(orient='records')
if date_at_object < date.today():
accounts_partial_reconcile, debit_accounts_partial_amount, \
credit_accounts_partial_amount = \
self._get_account_partial_reconciled(move_lines_data,
date_at_object)
if accounts_partial_reconcile:
accounts_partial_reconcile_data = pd.DataFrame(
accounts_partial_reconcile)
debit_ids = set(accounts_partial_reconcile_data.debit_move_id
.to_list())
credit_ids = set(
accounts_partial_reconcile_data.credit_move_id.to_list())
for move_line in move_lines_data:
if move_line['id'] in debit_ids:
move_line['amount_residual'] += \
debit_accounts_partial_amount[move_line['id']]
if move_line['id'] in credit_ids:
move_line['amount_residual'] -= \
credit_accounts_partial_amount[move_line['id']]
moves_lines_to_remove = []
for move_line in move_lines_data:
if move_line['date'] > date_at_object or float_is_zero(
move_line['amount_residual'], precision_digits=2):
moves_lines_to_remove.append(move_line)
if len(moves_lines_to_remove) > 0:
for move_line_to_remove in moves_lines_to_remove:
move_lines_data.remove(move_line_to_remove)
partners_data = {
0: {
'id': 0,
'name': 'Missing Partner'
}
}
open_items_move_lines_data = {}
for move_line in move_lines_data:
no_partner = True
# Partners data
if move_line['partner_id'] and not pd.isna(move_line['partner_id']):
no_partner = False
partners_data.update({
move_line['partner_id']: {
'id': move_line['partner_id'],
'name': move_line['partner_name'],
'currency_id': accounts_data[move_line[
'account_id']]['currency_id'],
}
})
else:
partners_data[0]['currency_id'] = accounts_data[move_line[
'account_id']]['currency_id']
# Move line update
original = 0
if not float_is_zero(move_line['credit'], precision_digits=2):
original = move_line['credit']*(-1)
if not float_is_zero(move_line['debit'], precision_digits=2):
original = move_line['debit']
move_line.update({
'date': move_line['date'].strftime("%d/%m/%Y"),
'date_maturity': move_line['date_maturity'].strftime("%d/%m/%Y"),
'original': original,
'partner_id': 0 if no_partner else move_line['partner_id'],
'partner_name': '' if no_partner else move_line['partner_name'],
'ref': '' if not move_line['ref'] else move_line['ref'],
'account': accounts_data[move_line['account_id']]['code'],
'journal': journals_data[move_line['journal_id']]['code'],
})
# Open Items Move Lines Data
if move_line['account_id'] not in open_items_move_lines_data.keys():
open_items_move_lines_data[move_line['account_id']] = {
move_line['partner_id']: [move_line]}
else:
if move_line['partner_id'] not in \
open_items_move_lines_data[move_line[
'account_id']].keys():
open_items_move_lines_data[move_line['account_id']][
move_line['partner_id']] = [move_line]
else:
open_items_move_lines_data[move_line['account_id']][
move_line['partner_id']].append(move_line)
return move_lines_data, partners_data, journals_data, accounts_data, \
open_items_move_lines_data
@api.model
def _calculate_amounts(self, open_items_move_lines_data):
total_amount = {}
for account_id in open_items_move_lines_data.keys():
total_amount[account_id] = {}
total_amount[account_id]['residual'] = 0.0
for partner_id in open_items_move_lines_data[account_id].keys():
total_amount[account_id][partner_id] = {}
total_amount[account_id][partner_id]['residual'] = 0.0
for move_line in open_items_move_lines_data[account_id][
partner_id]:
total_amount[account_id][partner_id]['residual'] += \
move_line['amount_residual']
total_amount[account_id]['residual'] += move_line[
'amount_residual']
return total_amount
@api.multi
def _get_report_values(self, docids, data):
wizard_id = data['wizard_id']
company = self.env['res.company'].browse(data['company_id'])
company_id = data['company_id']
account_ids = data['account_ids']
partner_ids = data['partner_ids']
date_at = data['date_at']
date_at_object = datetime.strptime(date_at, '%Y-%m-%d').date()
date_from = data['date_from']
target_move = data['target_move']
move_lines_data, partners_data, journals_data, accounts_data, \
open_items_move_lines_data = self._get_data(
account_ids, partner_ids, date_at_object,
target_move, company_id, date_from)
total_amount = self._calculate_amounts(open_items_move_lines_data)
return{
'doc_ids': [wizard_id],
'doc_model': 'open.items.report.wizard',
'docs': self.env['open.items.report.wizard'].browse(wizard_id),
'foreign_currency': data['foreign_currency'],
'company_name': company.display_name,
'currency_name': company.currency_id.name,
'date_at': date_at_object.strftime("%d/%m/%Y"),
'hide_account_at_0': data['hide_account_at_0'],
'target_move': data['target_move'],
'partners_data': partners_data,
'accounts_data': accounts_data,
'total_amount': total_amount,
'Open_Items': open_items_move_lines_data,
}