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.

421 lines
18 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)
  5. #
  6. # Author: Guewen Baconnier (Camptocamp)
  7. #
  8. # WARNING: This program as such is intended to be used by professional
  9. # programmers who take the whole responsability of assessing all potential
  10. # consequences resulting from its eventual inadequacies and bugs
  11. # End users who are looking for a ready-to-use solution with commercial
  12. # garantees and support are strongly adviced to contract a Free Software
  13. # Service Company
  14. #
  15. # This program is Free Software; you can redistribute it and/or
  16. # modify it under the terms of the GNU General Public License
  17. # as published by the Free Software Foundation; either version 2
  18. # of the License, or (at your option) any later version.
  19. #
  20. # This program is distributed in the hope that it will be useful,
  21. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. # GNU General Public License for more details.
  24. #
  25. # You should have received a copy of the GNU General Public License
  26. # along with this program; if not, write to the Free Software
  27. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  28. #
  29. ##############################################################################
  30. import time
  31. from lxml import etree
  32. from datetime import datetime
  33. from openerp import fields, models
  34. from openerp.tools.translate import _
  35. # pylint: disable=deprecated-module
  36. from openerp.osv.orm import setup_modifiers
  37. def previous_year_date(date, nb_prev=1):
  38. if not date:
  39. return False
  40. parsed_date = datetime.strptime(date, '%Y-%m-%d')
  41. previous_date = datetime(year=parsed_date.year - nb_prev,
  42. month=parsed_date.month,
  43. day=parsed_date.day)
  44. return previous_date
  45. class AccountBalanceCommonWizard(models.TransientModel):
  46. """Will launch trial balance report and pass required args"""
  47. # pylint: disable=consider-merging-classes-inherited
  48. _inherit = "account.common.account.report"
  49. _name = "account.common.balance.report"
  50. _description = "Common Balance Report"
  51. # an update module should be done if changed
  52. # in order to create fields in db
  53. COMPARISON_LEVEL = 3
  54. COMPARE_SELECTION = [('filter_no', 'No Comparison'),
  55. ('filter_year', 'Fiscal Year'),
  56. ('filter_date', 'Date'),
  57. ('filter_period', 'Periods'),
  58. ('filter_opening', 'Opening Only')]
  59. M2O_DYNAMIC_FIELDS = [f % index for f in ["comp%s_fiscalyear_id",
  60. "comp%s_period_from",
  61. "comp%s_period_to"]
  62. for index in range(COMPARISON_LEVEL)]
  63. SIMPLE_DYNAMIC_FIELDS = [f % index for f in ["comp%s_filter",
  64. "comp%s_date_from",
  65. "comp%s_date_to"]
  66. for index in range(COMPARISON_LEVEL)]
  67. DYNAMIC_FIELDS = M2O_DYNAMIC_FIELDS + SIMPLE_DYNAMIC_FIELDS
  68. # pylint: disable=old-api7-method-defined
  69. def _get_account_ids(self, cr, uid, context=None):
  70. res = False
  71. if context.get('active_model', False) == 'account.account' \
  72. and context.get('active_ids', False):
  73. res = context['active_ids']
  74. return res
  75. account_ids = fields.Many2many(
  76. 'account.account', string='Filter on accounts',
  77. help="Only selected accounts will be printed. Leave empty to \
  78. print all accounts.", default=lambda self: self._get_account_ids(),
  79. )
  80. filter = fields.Selection(
  81. [('filter_no', 'No Filters'),
  82. ('filter_date', 'Date'),
  83. ('filter_period', 'Periods'),
  84. ('filter_opening', 'Opening Only')],
  85. "Filter by", required=True,
  86. help='Filter by date: no opening balance will be displayed. '
  87. '(opening balance can only be computed based on period to be \
  88. correct).'
  89. )
  90. # Set statically because of the impossibility of changing the selection
  91. # field when changing chart_account_id
  92. account_level = fields.Selection(
  93. [('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'),
  94. ('6', '6')], string="Account level"
  95. )
  96. # pylint: disable=attribute-deprecated
  97. _columns = {}
  98. for index in range(COMPARISON_LEVEL):
  99. _columns.update(
  100. {"comp%s_filter" % index:
  101. fields.fields.selection(
  102. COMPARE_SELECTION, string='Compare By', required=True),
  103. "comp%s_fiscalyear_id" % index:
  104. fields.fields.many2one('account.fiscalyear', 'Fiscal Year'),
  105. "comp%s_period_from" % index:
  106. fields.fields.many2one('account.period', 'Start Period'),
  107. "comp%s_period_to" % index:
  108. fields.fields.many2one('account.period', 'End Period'),
  109. "comp%s_date_from" % index:
  110. fields.fields.date("Start Date"),
  111. "comp%s_date_to" % index:
  112. fields.fields.date("End Date")})
  113. # pylint: disable=old-api7-method-defined
  114. def _check_fiscalyear(self, cr, uid, ids, context=None):
  115. obj = self.read(
  116. cr, uid, ids[0], ['fiscalyear_id', 'filter'], context=context)
  117. if not obj['fiscalyear_id'] and obj['filter'] == 'filter_no':
  118. return False
  119. return True
  120. _constraints = [
  121. (_check_fiscalyear,
  122. 'When no Fiscal year is selected, you must choose to filter by \
  123. periods or by date.', ['filter']),
  124. ]
  125. # pylint: disable=old-api7-method-defined
  126. def default_get(self, cr, uid, fields, context=None):
  127. """
  128. To get default values for the object.
  129. @param self: The object pointer.
  130. @param cr: A database cursor
  131. @param uid: ID of the user currently logged in
  132. @param fields: List of fields for which we want default values
  133. @param context: A standard dictionary
  134. @return: A dictionary which of fields with values.
  135. """
  136. res = super(AccountBalanceCommonWizard, self).default_get(
  137. cr, uid, fields, context=context)
  138. for index in range(self.COMPARISON_LEVEL):
  139. field = "comp%s_filter" % (index,)
  140. if not res.get(field, False):
  141. res[field] = 'filter_no'
  142. return res
  143. # pylint: disable=old-api7-method-defined
  144. def fields_view_get(self, cr, uid, view_id=None, view_type='form',
  145. context=None, toolbar=False, submenu=False):
  146. res = super(AccountBalanceCommonWizard, self).fields_view_get(
  147. cr, uid, view_id, view_type, context=context, toolbar=toolbar,
  148. submenu=submenu)
  149. res['fields'].update(self.fields_get(cr, uid,
  150. allfields=self.DYNAMIC_FIELDS,
  151. context=context, write_access=True))
  152. eview = etree.fromstring(res['arch'])
  153. placeholder = eview.xpath("//page[@name='placeholder']")
  154. if placeholder:
  155. placeholder = placeholder[0]
  156. for index in range(self.COMPARISON_LEVEL):
  157. page = etree.Element(
  158. 'page',
  159. {'name': "comp%s" % index,
  160. 'string': _("Comparison %s") % (index + 1, )})
  161. group = etree.Element('group')
  162. page.append(group)
  163. def modifiers_and_append(elem):
  164. setup_modifiers(elem)
  165. group.append(elem)
  166. modifiers_and_append(etree.Element(
  167. 'field',
  168. {'name': "comp%s_filter" % index,
  169. 'on_change': "onchange_comp_filter(%(index)s, filter,\
  170. comp%(index)s_filter, fiscalyear_id, date_from, date_to)"
  171. % {'index': index}}))
  172. modifiers_and_append(etree.Element(
  173. 'field',
  174. {'name': "comp%s_fiscalyear_id" % index,
  175. 'attrs':
  176. "{'required': [('comp%(index)s_filter','in',\
  177. ('filter_year','filter_opening'))],"
  178. " 'invisible': [('comp%(index)s_filter','not in',\
  179. ('filter_year','filter_opening'))]}" % {'index': index}}))
  180. dates_attrs = "{'required': [('comp%(index)s_filter','=',\
  181. 'filter_date')], " \
  182. " 'invisible': [('comp%(index)s_filter','!=',\
  183. 'filter_date')]}" % {
  184. 'index': index}
  185. modifiers_and_append(etree.Element(
  186. 'separator',
  187. {'string': _('Dates'),
  188. 'colspan': '4',
  189. 'attrs': dates_attrs}))
  190. modifiers_and_append(etree.Element(
  191. 'field',
  192. {'name': "comp%s_date_from" % index,
  193. 'attrs': dates_attrs}))
  194. modifiers_and_append(etree.Element(
  195. 'field',
  196. {'name': "comp%s_date_to" % index,
  197. 'attrs': dates_attrs}))
  198. periods_attrs = "{'required': [('comp%(index)s_filter','=',\
  199. 'filter_period')]," \
  200. " 'invisible': [('comp%(index)s_filter','!=',\
  201. 'filter_period')]}" % {
  202. 'index': index}
  203. periods_domain = "[('special', '=', False)]"
  204. modifiers_and_append(etree.Element(
  205. 'separator',
  206. {'string': _('Periods'),
  207. 'colspan': '4',
  208. 'attrs': periods_attrs}))
  209. modifiers_and_append(etree.Element(
  210. 'field',
  211. {'name': "comp%s_period_from" % index,
  212. 'attrs': periods_attrs,
  213. 'domain': periods_domain}))
  214. modifiers_and_append(etree.Element(
  215. 'field',
  216. {'name': "comp%s_period_to" % index,
  217. 'attrs': periods_attrs,
  218. 'domain': periods_domain}))
  219. placeholder.addprevious(page)
  220. placeholder.getparent().remove(placeholder)
  221. res['arch'] = etree.tostring(eview)
  222. return res
  223. # pylint: disable=old-api7-method-defined
  224. def onchange_filter(self, cr, uid, ids, filter='filter_no',
  225. fiscalyear_id=False, context=None):
  226. res = {}
  227. if filter == 'filter_no':
  228. res['value'] = {'period_from': False,
  229. 'period_to': False,
  230. 'date_from': False,
  231. 'date_to': False}
  232. if filter == 'filter_date':
  233. if fiscalyear_id:
  234. fyear = self.pool.get('account.fiscalyear').browse(
  235. cr, uid, fiscalyear_id, context=context)
  236. date_from = fyear.date_start
  237. date_to = fyear.date_stop > time.strftime(
  238. '%Y-%m-%d') and time.strftime('%Y-%m-%d') \
  239. or fyear.date_stop
  240. else:
  241. date_from, date_to = time.strftime(
  242. '%Y-01-01'), time.strftime('%Y-%m-%d')
  243. res['value'] = {'period_from': False, 'period_to':
  244. False, 'date_from': date_from, 'date_to': date_to}
  245. if filter == 'filter_period' and fiscalyear_id:
  246. start_period = end_period = False
  247. cr.execute('''
  248. SELECT * FROM (SELECT p.id
  249. FROM account_period p
  250. LEFT JOIN account_fiscalyear f
  251. ON (p.fiscalyear_id = f.id)
  252. WHERE f.id = %s
  253. AND COALESCE(p.special, FALSE) = FALSE
  254. ORDER BY p.date_start ASC
  255. LIMIT 1) AS period_start
  256. UNION ALL
  257. SELECT * FROM (SELECT p.id
  258. FROM account_period p
  259. LEFT JOIN account_fiscalyear f
  260. ON (p.fiscalyear_id = f.id)
  261. WHERE f.id = %s
  262. AND p.date_start < NOW()
  263. AND COALESCE(p.special, FALSE) = FALSE
  264. ORDER BY p.date_stop DESC
  265. LIMIT 1) AS period_stop''',
  266. (fiscalyear_id, fiscalyear_id))
  267. periods = [i[0] for i in cr.fetchall()]
  268. if periods:
  269. start_period = end_period = periods[0]
  270. if len(periods) > 1:
  271. end_period = periods[1]
  272. res['value'] = {'period_from': start_period, 'period_to':
  273. end_period, 'date_from': False, 'date_to': False}
  274. return res
  275. # pylint: disable=old-api7-method-defined
  276. def onchange_comp_filter(self, cr, uid, ids, index,
  277. main_filter='filter_no', comp_filter='filter_no',
  278. fiscalyear_id=False, start_date=False,
  279. stop_date=False, context=None):
  280. res = {}
  281. fy_obj = self.pool.get('account.fiscalyear')
  282. last_fiscalyear_id = False
  283. if fiscalyear_id:
  284. fiscalyear = fy_obj.browse(cr, uid, fiscalyear_id, context=context)
  285. last_fiscalyear_ids = fy_obj.search(
  286. cr, uid, [('date_stop', '<', fiscalyear.date_start)],
  287. limit=self.COMPARISON_LEVEL, order='date_start desc',
  288. context=context)
  289. if last_fiscalyear_ids:
  290. if len(last_fiscalyear_ids) > index:
  291. # first element for the comparison 1, second element for
  292. # the comparison 2
  293. last_fiscalyear_id = last_fiscalyear_ids[index]
  294. fy_id_field = "comp%s_fiscalyear_id" % (index,)
  295. period_from_field = "comp%s_period_from" % (index,)
  296. period_to_field = "comp%s_period_to" % (index,)
  297. date_from_field = "comp%s_date_from" % (index,)
  298. date_to_field = "comp%s_date_to" % (index,)
  299. if comp_filter == 'filter_no':
  300. res['value'] = {
  301. fy_id_field: False,
  302. period_from_field: False,
  303. period_to_field: False,
  304. date_from_field: False,
  305. date_to_field: False
  306. }
  307. if comp_filter in ('filter_year', 'filter_opening'):
  308. res['value'] = {
  309. fy_id_field: last_fiscalyear_id,
  310. period_from_field: False,
  311. period_to_field: False,
  312. date_from_field: False,
  313. date_to_field: False
  314. }
  315. if comp_filter == 'filter_date':
  316. dates = {}
  317. if main_filter == 'filter_date':
  318. dates = {
  319. 'date_start': previous_year_date(start_date, index + 1).
  320. strftime('%Y-%m-%d'),
  321. 'date_stop': previous_year_date(stop_date, index + 1).
  322. strftime('%Y-%m-%d'),
  323. }
  324. elif last_fiscalyear_id:
  325. dates = fy_obj.read(
  326. cr, uid, last_fiscalyear_id, ['date_start', 'date_stop'],
  327. context=context)
  328. res['value'] = {fy_id_field: False,
  329. period_from_field: False,
  330. period_to_field: False,
  331. date_from_field: dates.get('date_start', False),
  332. date_to_field: dates.get('date_stop', False)}
  333. if comp_filter == 'filter_period' and last_fiscalyear_id:
  334. start_period = end_period = False
  335. cr.execute('''
  336. SELECT * FROM (SELECT p.id
  337. FROM account_period p
  338. LEFT JOIN account_fiscalyear f
  339. ON (p.fiscalyear_id = f.id)
  340. WHERE f.id = %(fiscalyear)s
  341. AND COALESCE(p.special, FALSE) = FALSE
  342. ORDER BY p.date_start ASC
  343. LIMIT 1) AS period_start
  344. UNION ALL
  345. SELECT * FROM (SELECT p.id
  346. FROM account_period p
  347. LEFT JOIN account_fiscalyear f
  348. ON (p.fiscalyear_id = f.id)
  349. WHERE f.id = %(fiscalyear)s
  350. AND p.date_start < NOW()
  351. AND COALESCE(p.special, FALSE) = FALSE
  352. ORDER BY p.date_stop DESC
  353. LIMIT 1) AS period_stop''',
  354. {'fiscalyear': last_fiscalyear_id})
  355. periods = [i[0] for i in cr.fetchall()]
  356. if periods and len(periods) > 1:
  357. start_period = end_period = periods[0]
  358. if len(periods) > 1:
  359. end_period = periods[1]
  360. res['value'] = {fy_id_field: False,
  361. period_from_field: start_period,
  362. period_to_field: end_period,
  363. date_from_field: False,
  364. date_to_field: False}
  365. return res
  366. # pylint: disable=old-api7-method-defined
  367. def pre_print_report(self, cr, uid, ids, data, context=None):
  368. data = super(AccountBalanceCommonWizard, self).pre_print_report(
  369. cr, uid, ids, data, context=context)
  370. if context is None:
  371. context = {}
  372. # will be used to attach the report on the main account
  373. data['ids'] = [data['form']['chart_account_id']]
  374. fields_to_read = ['account_ids', 'account_level']
  375. fields_to_read += self.DYNAMIC_FIELDS
  376. vals = self.read(cr, uid, ids, fields_to_read, context=context)[0]
  377. # extract the id from the m2o tuple (id, name)
  378. for field in self.M2O_DYNAMIC_FIELDS:
  379. if isinstance(vals[field], tuple):
  380. vals[field] = vals[field][0]
  381. vals['max_comparison'] = self.COMPARISON_LEVEL
  382. data['form'].update(vals)
  383. return data