|
|
# © 2016 Julien Coux (Camptocamp) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields, api, _
class OpenItemsReport(models.TransientModel): """ Here, we just define class fields.
For methods, go more bottom at this file.
The class hierarchy is : * OpenItemsReport ** OpenItemsReportAccount *** OpenItemsReportPartner **** OpenItemsReportMoveLine """
_name = 'report_open_items'
# Filters fields, used for data computation date_at = fields.Date() only_posted_moves = fields.Boolean() hide_account_balance_at_0 = fields.Boolean() company_id = fields.Many2one(comodel_name='res.company') filter_account_ids = fields.Many2many(comodel_name='account.account') filter_partner_ids = fields.Many2many(comodel_name='res.partner')
# Flag fields, used for report display has_second_currency = fields.Boolean()
# Data fields, used to browse report data account_ids = fields.One2many( comodel_name='report_open_items_account', inverse_name='report_id' )
class OpenItemsReportAccount(models.TransientModel):
_name = 'report_open_items_account' _order = 'code ASC'
report_id = fields.Many2one( comodel_name='report_open_items', ondelete='cascade', index=True )
# Data fields, used to keep link with real object account_id = fields.Many2one( 'account.account', index=True )
# Data fields, used for report display code = fields.Char() name = fields.Char() final_amount_residual = fields.Float(digits=(16, 2))
# Data fields, used to browse report data partner_ids = fields.One2many( comodel_name='report_open_items_partner', inverse_name='report_account_id' )
class OpenItemsReportPartner(models.TransientModel):
_name = 'report_open_items_partner'
report_account_id = fields.Many2one( comodel_name='report_open_items_account', ondelete='cascade', index=True )
# Data fields, used to keep link with real object partner_id = fields.Many2one( 'res.partner', index=True )
# Data fields, used for report display name = fields.Char() final_amount_residual = fields.Float(digits=(16, 2))
# Data fields, used to browse report data move_line_ids = fields.One2many( comodel_name='report_open_items_move_line', inverse_name='report_partner_id' )
@api.model def _generate_order_by(self, order_spec, query): """Custom order to display "No partner allocated" at last position.""" return """
ORDER BY CASE WHEN "report_open_items_partner"."partner_id" IS NOT NULL THEN 0 ELSE 1 END, "report_open_items_partner"."name" """
class OpenItemsReportMoveLine(models.TransientModel):
_name = 'report_open_items_move_line'
report_partner_id = fields.Many2one( comodel_name='report_open_items_partner', ondelete='cascade', index=True )
# Data fields, used to keep link with real object move_line_id = fields.Many2one('account.move.line')
# Data fields, used for report display date = fields.Date() date_due = fields.Date() entry = fields.Char() journal = fields.Char() account = fields.Char() partner = fields.Char() label = fields.Char() amount_total_due = fields.Float(digits=(16, 2)) amount_residual = fields.Float(digits=(16, 2)) currency_name = fields.Char() amount_total_due_currency = fields.Float(digits=(16, 2)) amount_residual_currency = fields.Float(digits=(16, 2))
class OpenItemsReportCompute(models.TransientModel): """ Here, we just define methods.
For class fields, go more top at this file. """
_inherit = 'report_open_items'
@api.multi def print_report(self, report_type): self.ensure_one() if report_type == 'xlsx': report_name = 'a_f_r.report_open_items_xlsx' else: report_name = 'account_financial_report.' \ 'report_open_items_qweb' return self.env['ir.actions.report'].search( [('report_name', '=', report_name), ('report_type', '=', report_type)], limit=1).report_action(self)
def _get_html(self): result = {} rcontext = {} context = dict(self.env.context) report = self.browse(context.get('active_id')) if report: rcontext['o'] = report result['html'] = self.env.ref( 'account_financial_report.report_open_items').render( rcontext) return result
@api.model def get_html(self, given_context=None): return self._get_html()
@api.multi def compute_data_for_report(self): self.ensure_one() # Compute report data self._inject_account_values() self._inject_partner_values() self._inject_line_values() self._inject_line_values(only_empty_partner_line=True) self._clean_partners_and_accounts() self._compute_partners_and_accounts_cumul() if self.hide_account_balance_at_0: self._clean_partners_and_accounts( only_delete_account_balance_at_0=True ) # Compute display flag self._compute_has_second_currency() # Refresh cache because all data are computed with SQL requests self.refresh()
def _inject_account_values(self): """Inject report values for report_open_items_account.""" query_inject_account = """
WITH accounts AS ( SELECT a.id, a.code, a.name, a.user_type_id FROM account_account a INNER JOIN account_move_line ml ON a.id = ml.account_id AND ml.date <= %s """
if self.filter_partner_ids: query_inject_account += """
INNER JOIN res_partner p ON ml.partner_id = p.id """
if self.only_posted_moves: query_inject_account += """
INNER JOIN account_move m ON ml.move_id = m.id AND m.state = 'posted' """
query_inject_account += """
WHERE a.company_id = %s AND a.reconcile IS true """
if self.filter_account_ids: query_inject_account += """
AND a.id IN %s """
if self.filter_partner_ids: query_inject_account += """
AND p.id IN %s """
query_inject_account += """
GROUP BY a.id ) INSERT INTO report_open_items_account ( report_id, create_uid, create_date, account_id, code, name ) SELECT %s AS report_id, %s AS create_uid, NOW() AS create_date, a.id AS account_id, a.code, a.name FROM accounts a """
query_inject_account_params = ( self.date_at, self.company_id.id, ) if self.filter_account_ids: query_inject_account_params += ( tuple(self.filter_account_ids.ids), ) if self.filter_partner_ids: query_inject_account_params += ( tuple(self.filter_partner_ids.ids), ) query_inject_account_params += ( self.id, self.env.uid, ) self.env.cr.execute(query_inject_account, query_inject_account_params)
def _inject_partner_values(self): """ Inject report values for report_open_items_partner. """ # pylint: disable=sql-injection query_inject_partner = """
WITH accounts_partners AS ( SELECT ra.id AS report_account_id, a.id AS account_id, at.include_initial_balance AS include_initial_balance, p.id AS partner_id, COALESCE( CASE WHEN NULLIF(p.name, '') IS NOT NULL AND NULLIF(p.ref, '') IS NOT NULL THEN p.name || ' (' || p.ref || ')' ELSE p.name END, '""" + _('No partner allocated') + """' ) AS partner_name FROM report_open_items_account ra INNER JOIN account_account a ON ra.account_id = a.id INNER JOIN account_account_type at ON a.user_type_id = at.id INNER JOIN account_move_line ml ON a.id = ml.account_id AND ml.date <= %s """
if self.only_posted_moves: query_inject_partner += """
INNER JOIN account_move m ON ml.move_id = m.id AND m.state = 'posted' """
query_inject_partner += """
LEFT JOIN res_partner p ON ml.partner_id = p.id WHERE ra.report_id = %s """
if self.filter_partner_ids: query_inject_partner += """
AND p.id IN %s """
query_inject_partner += """
GROUP BY ra.id, a.id, p.id, at.include_initial_balance ) INSERT INTO report_open_items_partner ( report_account_id, create_uid, create_date, partner_id, name ) SELECT ap.report_account_id, %s AS create_uid, NOW() AS create_date, ap.partner_id, ap.partner_name FROM accounts_partners ap """
query_inject_partner_params = ( self.date_at, self.id, ) if self.filter_partner_ids: query_inject_partner_params += ( tuple(self.filter_partner_ids.ids), ) query_inject_partner_params += ( self.env.uid, ) self.env.cr.execute(query_inject_partner, query_inject_partner_params)
def _get_line_sub_query_move_lines(self, only_empty_partner_line=False, positive_balance=True): """ Return subquery used to compute sum amounts on lines """ sub_query = """
SELECT ml.id, ml.balance, SUM( CASE WHEN ml_past.id IS NOT NULL THEN pr.amount ELSE NULL END ) AS partial_amount, ml.amount_currency, SUM( CASE WHEN ml_past.id IS NOT NULL THEN pr.amount_currency ELSE NULL END ) AS partial_amount_currency, ml.currency_id FROM report_open_items_partner rp INNER JOIN report_open_items_account ra ON rp.report_account_id = ra.id INNER JOIN account_move_line ml ON ra.account_id = ml.account_id """
if not only_empty_partner_line: sub_query += """
AND rp.partner_id = ml.partner_id """
elif only_empty_partner_line: sub_query += """
AND ml.partner_id IS NULL """
if not positive_balance: sub_query += """
LEFT JOIN account_partial_reconcile pr ON ml.balance < 0 AND pr.credit_move_id = ml.id LEFT JOIN account_move_line ml_future ON ml.balance < 0 AND pr.debit_move_id = ml_future.id AND ml_future.date > %s LEFT JOIN account_move_line ml_past ON ml.balance < 0 AND pr.debit_move_id = ml_past.id AND ml_past.date <= %s """
else: sub_query += """
LEFT JOIN account_partial_reconcile pr ON ml.balance > 0 AND pr.debit_move_id = ml.id LEFT JOIN account_move_line ml_future ON ml.balance > 0 AND pr.credit_move_id = ml_future.id AND ml_future.date > %s LEFT JOIN account_move_line ml_past ON ml.balance > 0 AND pr.credit_move_id = ml_past.id AND ml_past.date <= %s """
sub_query += """
WHERE ra.report_id = %s GROUP BY ml.id, ml.balance, ml.amount_currency HAVING ( ml.full_reconcile_id IS NULL OR MAX(ml_future.id) IS NOT NULL ) """
return sub_query
def _inject_line_values(self, only_empty_partner_line=False): """ Inject report values for report_open_items_move_line.
The "only_empty_partner_line" value is used to compute data without partner. """
query_inject_move_line = """
WITH move_lines_amount AS ( """
query_inject_move_line += self._get_line_sub_query_move_lines( only_empty_partner_line=only_empty_partner_line, positive_balance=True ) query_inject_move_line += """
UNION """
query_inject_move_line += self._get_line_sub_query_move_lines( only_empty_partner_line=only_empty_partner_line, positive_balance=False ) query_inject_move_line += """
), move_lines AS ( SELECT id, CASE WHEN SUM(partial_amount) > 0 THEN CASE WHEN balance > 0 THEN balance - SUM(partial_amount) ELSE balance + SUM(partial_amount) END ELSE balance END AS amount_residual, CASE WHEN SUM(partial_amount_currency) > 0 THEN CASE WHEN amount_currency > 0 THEN amount_currency - SUM(partial_amount_currency) ELSE amount_currency + SUM(partial_amount_currency) END ELSE amount_currency END AS amount_residual_currency, currency_id FROM move_lines_amount GROUP BY id, balance, amount_currency, currency_id ) INSERT INTO report_open_items_move_line ( report_partner_id, create_uid, create_date, move_line_id, date, date_due, entry, journal, account, partner, label, amount_total_due, amount_residual, currency_name, amount_total_due_currency, amount_residual_currency ) SELECT rp.id AS report_partner_id, %s AS create_uid, NOW() AS create_date, ml.id AS move_line_id, ml.date, ml.date_maturity, m.name AS entry, j.code AS journal, a.code AS account, """
if not only_empty_partner_line: query_inject_move_line += """
CASE WHEN NULLIF(p.name, '') IS NOT NULL AND NULLIF(p.ref, '') IS NOT NULL THEN p.name || ' (' || p.ref || ')' ELSE p.name END AS partner, """
elif only_empty_partner_line: query_inject_move_line += """
'""" + _('No partner allocated') + """' AS partner, """
query_inject_move_line += """
CONCAT_WS(' - ', NULLIF(ml.ref, ''), NULLIF(ml.name, '')) AS label, ml.balance, ml2.amount_residual, c.name AS currency_name, ml.amount_currency, ml2.amount_residual_currency FROM report_open_items_partner rp INNER JOIN report_open_items_account ra ON rp.report_account_id = ra.id INNER JOIN account_move_line ml ON ra.account_id = ml.account_id INNER JOIN move_lines ml2 ON ml.id = ml2.id AND ml2.amount_residual IS NOT NULL AND ml2.amount_residual != 0 INNER JOIN account_move m ON ml.move_id = m.id INNER JOIN account_journal j ON ml.journal_id = j.id INNER JOIN account_account a ON ml.account_id = a.id """
if not only_empty_partner_line: query_inject_move_line += """
INNER JOIN res_partner p ON ml.partner_id = p.id AND rp.partner_id = p.id """
query_inject_move_line += """
LEFT JOIN account_full_reconcile fr ON ml.full_reconcile_id = fr.id LEFT JOIN res_currency c ON ml2.currency_id = c.id WHERE ra.report_id = %s AND ml.date <= %s """
if self.only_posted_moves: query_inject_move_line += """
AND m.state = 'posted' """
if only_empty_partner_line: query_inject_move_line += """
AND ml.partner_id IS NULL AND rp.partner_id IS NULL """
if not only_empty_partner_line: query_inject_move_line += """
ORDER BY a.code, p.name, ml.date, ml.id """
elif only_empty_partner_line: query_inject_move_line += """
ORDER BY a.code, ml.date, ml.id """
self.env.cr.execute( query_inject_move_line, (self.date_at, self.date_at, self.id, self.date_at, self.date_at, self.id, self.env.uid, self.id, self.date_at,) )
def _compute_partners_and_accounts_cumul(self): """ Compute cumulative amount for
report_open_items_partner and report_open_items_account. """
query_compute_partners_cumul = """
UPDATE report_open_items_partner SET final_amount_residual = ( SELECT SUM(rml.amount_residual) AS final_amount_residual FROM report_open_items_move_line rml WHERE rml.report_partner_id = report_open_items_partner.id ) WHERE id IN ( SELECT rp.id FROM report_open_items_account ra INNER JOIN report_open_items_partner rp ON ra.id = rp.report_account_id WHERE ra.report_id = %s ) """
params_compute_partners_cumul = (self.id,) self.env.cr.execute(query_compute_partners_cumul, params_compute_partners_cumul) query_compute_accounts_cumul = """
UPDATE report_open_items_account SET final_amount_residual = ( SELECT SUM(rp.final_amount_residual) AS final_amount_residual FROM report_open_items_partner rp WHERE rp.report_account_id = report_open_items_account.id ) WHERE report_id = %s """
params_compute_accounts_cumul = (self.id,) self.env.cr.execute(query_compute_accounts_cumul, params_compute_accounts_cumul)
def _clean_partners_and_accounts(self, only_delete_account_balance_at_0=False): """ Delete empty data for
report_open_items_partner and report_open_items_account.
The "only_delete_account_balance_at_0" value is used to delete also the data with cumulative amounts at 0. """
query_clean_partners = """
DELETE FROM report_open_items_partner WHERE id IN ( SELECT DISTINCT rp.id FROM report_open_items_account ra INNER JOIN report_open_items_partner rp ON ra.id = rp.report_account_id LEFT JOIN report_open_items_move_line rml ON rp.id = rml.report_partner_id WHERE ra.report_id = %s """
if not only_delete_account_balance_at_0: query_clean_partners += """
AND rml.id IS NULL """
elif only_delete_account_balance_at_0: query_clean_partners += """
AND ( rp.final_amount_residual IS NULL OR rp.final_amount_residual = 0 ) """
query_clean_partners += """
) """
params_clean_partners = (self.id,) self.env.cr.execute(query_clean_partners, params_clean_partners) query_clean_accounts = """
DELETE FROM report_open_items_account WHERE id IN ( SELECT DISTINCT ra.id FROM report_open_items_account ra LEFT JOIN report_open_items_partner rp ON ra.id = rp.report_account_id WHERE ra.report_id = %s """
if not only_delete_account_balance_at_0: query_clean_accounts += """
AND rp.id IS NULL """
elif only_delete_account_balance_at_0: query_clean_accounts += """
AND ( ra.final_amount_residual IS NULL OR ra.final_amount_residual = 0 ) """
query_clean_accounts += """
) """
params_clean_accounts = (self.id,) self.env.cr.execute(query_clean_accounts, params_clean_accounts)
def _compute_has_second_currency(self): """ Compute "has_second_currency" flag which will used for display.""" query_update_has_second_currency = """
UPDATE report_open_items SET has_second_currency = ( SELECT TRUE FROM report_open_items_move_line l INNER JOIN report_open_items_partner p ON l.report_partner_id = p.id INNER JOIN report_open_items_account a ON p.report_account_id = a.id WHERE a.report_id = %s AND l.currency_name IS NOT NULL LIMIT 1 ) WHERE id = %s """
params = (self.id,) * 2 self.env.cr.execute(query_update_has_second_currency, params)
|