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.

132 lines
5.7 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2015 Therp BV <http://therp.nl>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import collections
  5. try:
  6. import sqlparse
  7. except ImportError as err:
  8. import logging
  9. logging.debug(err)
  10. import datetime
  11. from psycopg2.extensions import AsIs
  12. from dateutil.relativedelta import relativedelta
  13. from openerp import api, models, fields
  14. class AnalyticEntriesReport(models.Model):
  15. _inherit = 'analytic.entries.report'
  16. fiscalyear_id = fields.Many2one('account.fiscalyear', 'Fiscal year')
  17. period_id = fields.Many2one('account.period', 'Fiscal period')
  18. def init(self, cr):
  19. """Here, we try to be less invasive than the usual blunt overwrite of
  20. the sql view"""
  21. # added joins to account_period & account_move_line
  22. # added appropriate fields to select list and group by
  23. super(AnalyticEntriesReport, self).init(cr)
  24. cr.execute("select pg_get_viewdef(%s::regclass)", (self._table,))
  25. for statement in sqlparse.parse(cr.fetchone()[0]):
  26. current_keyword = None
  27. for token in statement:
  28. if token.is_keyword:
  29. current_keyword = token
  30. if isinstance(token, sqlparse.sql.IdentifierList) and\
  31. current_keyword.value == 'SELECT':
  32. last = None
  33. for last in token:
  34. pass
  35. token.insert_after(last, sqlparse.sql.Token(
  36. sqlparse.tokens.Generic,
  37. ',coalesce(ml.period_id, p.id) as period_id,'
  38. 'coalesce(p_from_move.fiscalyear_id, p.fiscalyear_id) '
  39. 'as fiscalyear_id'
  40. ))
  41. if isinstance(
  42. token,
  43. (sqlparse.sql.IdentifierList, sqlparse.sql.Parenthesis)
  44. ) and current_keyword.value == 'FROM':
  45. def find_table(token):
  46. if isinstance(token, sqlparse.sql.Identifier) and\
  47. token.get_real_name() == 'account_analytic_line':
  48. return token
  49. if not isinstance(token, collections.Iterable):
  50. return
  51. for child_token in token:
  52. result = find_table(child_token)
  53. if result:
  54. return result
  55. table = find_table(token)
  56. assert table
  57. table.parent.insert_after(table, sqlparse.sql.Token(
  58. sqlparse.tokens.Generic,
  59. ' left outer join account_period p '
  60. 'on p.special = False and p.date_start <= a.date '
  61. 'and p.date_stop >= a.date '
  62. 'left outer join account_move_line ml '
  63. 'on a.move_id = ml.id '
  64. 'left outer join account_period p_from_move '
  65. 'on ml.period_id = p_from_move.id '))
  66. if isinstance(token, sqlparse.sql.IdentifierList) and\
  67. current_keyword.value == 'GROUP BY':
  68. last = None
  69. for last in token:
  70. pass
  71. token.insert_after(last, sqlparse.sql.Token(
  72. sqlparse.tokens.Generic,
  73. ', coalesce(p_from_move.fiscalyear_id,'
  74. 'p.fiscalyear_id),'
  75. 'coalesce(ml.period_id, p.id)'))
  76. cr.execute("create or replace view %s as (%s)",
  77. (AsIs(self._table), AsIs(str(statement)[:-1])))
  78. @api.model
  79. def _apply_custom_operators(self, domain):
  80. adjusted_domain = []
  81. for proposition in domain:
  82. if not isinstance(proposition, tuple) and\
  83. not isinstance(proposition, list) or\
  84. len(proposition) != 3:
  85. # we can't use expression.is_leaf here because of our custom
  86. # operator
  87. adjusted_domain.append(proposition)
  88. continue
  89. field, operator, value = proposition
  90. if field.endswith('fiscalyear_id') and operator == 'offset':
  91. date = datetime.date.today() + relativedelta(years=value)
  92. fiscalyear_id = self.env['account.fiscalyear'].find(dt=date)
  93. adjusted_domain.append((field, '=', fiscalyear_id))
  94. elif field.endswith('period_id') and operator == 'offset':
  95. current_period = self.env['account.period'].with_context(
  96. account_period_prefer_normal=True).find()
  97. direction = '>='
  98. if value < 0:
  99. direction = '<='
  100. periods = current_period.search(
  101. [
  102. ('date_start', direction, current_period.date_start),
  103. ('special', '=', False),
  104. ], limit=(abs(value) + 1) or 1, order='date_start ' +
  105. ('asc' if direction == '>=' else 'desc')
  106. )
  107. adjusted_domain.append((field, '=', periods[value].id))
  108. else:
  109. adjusted_domain.append(proposition)
  110. return adjusted_domain
  111. @api.model
  112. def read_group(self, domain, fields, groupby, offset=0, limit=None,
  113. orderby=False, lazy=True):
  114. '''Override read_group to respect filters whose domain can't be
  115. computed on the client side'''
  116. adjusted_domain = self._apply_custom_operators(domain)
  117. return super(AnalyticEntriesReport, self).read_group(
  118. adjusted_domain, fields, groupby,
  119. offset=offset, limit=limit, orderby=orderby, lazy=lazy)