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.

413 lines
18 KiB

  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.osv import fields, orm
  34. from openerp.tools.translate import _
  35. def previous_year_date(date, nb_prev=1):
  36. if not date:
  37. return False
  38. parsed_date = datetime.strptime(date, '%Y-%m-%d')
  39. previous_date = datetime(year=parsed_date.year - nb_prev,
  40. month=parsed_date.month,
  41. day=parsed_date.day)
  42. return previous_date
  43. class AccountBalanceCommonWizard(orm.TransientModel):
  44. """Will launch trial balance report and pass required args"""
  45. _inherit = "account.common.account.report"
  46. _name = "account.common.balance.report"
  47. _description = "Common Balance Report"
  48. # an update module should be done if changed
  49. # in order to create fields in db
  50. COMPARISON_LEVEL = 3
  51. COMPARE_SELECTION = [('filter_no', 'No Comparison'),
  52. ('filter_year', 'Fiscal Year'),
  53. ('filter_date', 'Date'),
  54. ('filter_period', 'Periods'),
  55. ('filter_opening', 'Opening Only')]
  56. M2O_DYNAMIC_FIELDS = [f % index for f in ["comp%s_fiscalyear_id",
  57. "comp%s_period_from",
  58. "comp%s_period_to"]
  59. for index in range(COMPARISON_LEVEL)]
  60. SIMPLE_DYNAMIC_FIELDS = [f % index for f in ["comp%s_filter",
  61. "comp%s_date_from",
  62. "comp%s_date_to"]
  63. for index in range(COMPARISON_LEVEL)]
  64. DYNAMIC_FIELDS = M2O_DYNAMIC_FIELDS + SIMPLE_DYNAMIC_FIELDS
  65. def _get_account_ids(self, cr, uid, context=None):
  66. res = False
  67. if context.get('active_model', False) == 'account.account' \
  68. and context.get('active_ids', False):
  69. res = context['active_ids']
  70. return res
  71. _columns = {
  72. 'account_ids': fields.many2many(
  73. 'account.account', string='Filter on accounts',
  74. help="Only selected accounts will be printed. Leave empty to \
  75. print all accounts."),
  76. 'filter': fields.selection(
  77. [('filter_no', 'No Filters'),
  78. ('filter_date', 'Date'),
  79. ('filter_period', 'Periods'),
  80. ('filter_opening', 'Opening Only')],
  81. "Filter by",
  82. required=True,
  83. help='Filter by date: no opening balance will be displayed. '
  84. '(opening balance can only be computed based on period to be \
  85. correct).'),
  86. # Set statically because of the impossibility of changing the selection
  87. # field when changing chart_account_id
  88. 'account_level': fields.selection(
  89. [('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'),
  90. ('6', '6')], string="Account level"),
  91. }
  92. for index in range(COMPARISON_LEVEL):
  93. _columns.update(
  94. {"comp%s_filter" % index:
  95. fields.selection(
  96. COMPARE_SELECTION, string='Compare By', required=True),
  97. "comp%s_fiscalyear_id" % index:
  98. fields.many2one('account.fiscalyear', 'Fiscal Year'),
  99. "comp%s_period_from" % index:
  100. fields.many2one('account.period', 'Start Period'),
  101. "comp%s_period_to" % index:
  102. fields.many2one('account.period', 'End Period'),
  103. "comp%s_date_from" % index:
  104. fields.date("Start Date"),
  105. "comp%s_date_to" % index:
  106. fields.date("End Date")})
  107. _defaults = {
  108. 'account_ids': _get_account_ids,
  109. }
  110. def _check_fiscalyear(self, cr, uid, ids, context=None):
  111. obj = self.read(
  112. cr, uid, ids[0], ['fiscalyear_id', 'filter'], context=context)
  113. if not obj['fiscalyear_id'] and obj['filter'] == 'filter_no':
  114. return False
  115. return True
  116. _constraints = [
  117. (_check_fiscalyear,
  118. 'When no Fiscal year is selected, you must choose to filter by \
  119. periods or by date.', ['filter']),
  120. ]
  121. def default_get(self, cr, uid, fields, context=None):
  122. """
  123. To get default values for the object.
  124. @param self: The object pointer.
  125. @param cr: A database cursor
  126. @param uid: ID of the user currently logged in
  127. @param fields: List of fields for which we want default values
  128. @param context: A standard dictionary
  129. @return: A dictionary which of fields with values.
  130. """
  131. res = super(AccountBalanceCommonWizard, self).default_get(
  132. cr, uid, fields, context=context)
  133. for index in range(self.COMPARISON_LEVEL):
  134. field = "comp%s_filter" % (index,)
  135. if not res.get(field, False):
  136. res[field] = 'filter_no'
  137. return res
  138. def fields_view_get(self, cr, uid, view_id=None, view_type='form',
  139. context=None, toolbar=False, submenu=False):
  140. res = super(AccountBalanceCommonWizard, self).fields_view_get(
  141. cr, uid, view_id, view_type, context=context, toolbar=toolbar,
  142. submenu=submenu)
  143. res['fields'].update(self.fields_get(cr, uid,
  144. allfields=self.DYNAMIC_FIELDS,
  145. context=context, write_access=True))
  146. eview = etree.fromstring(res['arch'])
  147. placeholder = eview.xpath("//page[@name='placeholder']")
  148. if placeholder:
  149. placeholder = placeholder[0]
  150. for index in range(self.COMPARISON_LEVEL):
  151. page = etree.Element(
  152. 'page',
  153. {'name': "comp%s" % index,
  154. 'string': _("Comparison %s") % (index + 1, )})
  155. group = etree.Element('group')
  156. page.append(group)
  157. def modifiers_and_append(elem):
  158. orm.setup_modifiers(elem)
  159. group.append(elem)
  160. modifiers_and_append(etree.Element(
  161. 'field',
  162. {'name': "comp%s_filter" % index,
  163. 'on_change': "onchange_comp_filter(%(index)s, filter,\
  164. comp%(index)s_filter, fiscalyear_id, date_from, date_to)"
  165. % {'index': index}}))
  166. modifiers_and_append(etree.Element(
  167. 'field',
  168. {'name': "comp%s_fiscalyear_id" % index,
  169. 'attrs':
  170. "{'required': [('comp%(index)s_filter','in',\
  171. ('filter_year','filter_opening'))],"
  172. " 'invisible': [('comp%(index)s_filter','not in',\
  173. ('filter_year','filter_opening'))]}" % {'index': index}}))
  174. dates_attrs = "{'required': [('comp%(index)s_filter','=',\
  175. 'filter_date')], " \
  176. " 'invisible': [('comp%(index)s_filter','!=',\
  177. 'filter_date')]}" % {
  178. 'index': index}
  179. modifiers_and_append(etree.Element(
  180. 'separator',
  181. {'string': _('Dates'),
  182. 'colspan': '4',
  183. 'attrs': dates_attrs}))
  184. modifiers_and_append(etree.Element(
  185. 'field',
  186. {'name': "comp%s_date_from" % index,
  187. 'attrs': dates_attrs}))
  188. modifiers_and_append(etree.Element(
  189. 'field',
  190. {'name': "comp%s_date_to" % index,
  191. 'attrs': dates_attrs}))
  192. periods_attrs = "{'required': [('comp%(index)s_filter','=',\
  193. 'filter_period')]," \
  194. " 'invisible': [('comp%(index)s_filter','!=',\
  195. 'filter_period')]}" % {
  196. 'index': index}
  197. periods_domain = "[('special', '=', False)]"
  198. modifiers_and_append(etree.Element(
  199. 'separator',
  200. {'string': _('Periods'),
  201. 'colspan': '4',
  202. 'attrs': periods_attrs}))
  203. modifiers_and_append(etree.Element(
  204. 'field',
  205. {'name': "comp%s_period_from" % index,
  206. 'attrs': periods_attrs,
  207. 'domain': periods_domain}))
  208. modifiers_and_append(etree.Element(
  209. 'field',
  210. {'name': "comp%s_period_to" % index,
  211. 'attrs': periods_attrs,
  212. 'domain': periods_domain}))
  213. placeholder.addprevious(page)
  214. placeholder.getparent().remove(placeholder)
  215. res['arch'] = etree.tostring(eview)
  216. return res
  217. def onchange_filter(self, cr, uid, ids, filter='filter_no',
  218. fiscalyear_id=False, context=None):
  219. res = {}
  220. if filter == 'filter_no':
  221. res['value'] = {'period_from': False,
  222. 'period_to': False,
  223. 'date_from': False,
  224. 'date_to': False}
  225. if filter == 'filter_date':
  226. if fiscalyear_id:
  227. fyear = self.pool.get('account.fiscalyear').browse(
  228. cr, uid, fiscalyear_id, context=context)
  229. date_from = fyear.date_start
  230. date_to = fyear.date_stop > time.strftime(
  231. '%Y-%m-%d') and time.strftime('%Y-%m-%d') \
  232. or fyear.date_stop
  233. else:
  234. date_from, date_to = time.strftime(
  235. '%Y-01-01'), time.strftime('%Y-%m-%d')
  236. res['value'] = {'period_from': False, 'period_to':
  237. False, 'date_from': date_from, 'date_to': date_to}
  238. if filter == 'filter_period' and fiscalyear_id:
  239. start_period = end_period = False
  240. cr.execute('''
  241. SELECT * FROM (SELECT p.id
  242. FROM account_period p
  243. LEFT JOIN account_fiscalyear f
  244. ON (p.fiscalyear_id = f.id)
  245. WHERE f.id = %s
  246. AND COALESCE(p.special, FALSE) = FALSE
  247. ORDER BY p.date_start ASC
  248. LIMIT 1) AS period_start
  249. UNION ALL
  250. SELECT * FROM (SELECT p.id
  251. FROM account_period p
  252. LEFT JOIN account_fiscalyear f
  253. ON (p.fiscalyear_id = f.id)
  254. WHERE f.id = %s
  255. AND p.date_start < NOW()
  256. AND COALESCE(p.special, FALSE) = FALSE
  257. ORDER BY p.date_stop DESC
  258. LIMIT 1) AS period_stop''',
  259. (fiscalyear_id, fiscalyear_id))
  260. periods = [i[0] for i in cr.fetchall()]
  261. if periods:
  262. start_period = end_period = periods[0]
  263. if len(periods) > 1:
  264. end_period = periods[1]
  265. res['value'] = {'period_from': start_period, 'period_to':
  266. end_period, 'date_from': False, 'date_to': False}
  267. return res
  268. def onchange_comp_filter(self, cr, uid, ids, index,
  269. main_filter='filter_no', comp_filter='filter_no',
  270. fiscalyear_id=False, start_date=False,
  271. stop_date=False, context=None):
  272. res = {}
  273. fy_obj = self.pool.get('account.fiscalyear')
  274. last_fiscalyear_id = False
  275. if fiscalyear_id:
  276. fiscalyear = fy_obj.browse(cr, uid, fiscalyear_id, context=context)
  277. last_fiscalyear_ids = fy_obj.search(
  278. cr, uid, [('date_stop', '<', fiscalyear.date_start)],
  279. limit=self.COMPARISON_LEVEL, order='date_start desc',
  280. context=context)
  281. if last_fiscalyear_ids:
  282. if len(last_fiscalyear_ids) > index:
  283. # first element for the comparison 1, second element for
  284. # the comparison 2
  285. last_fiscalyear_id = last_fiscalyear_ids[index]
  286. fy_id_field = "comp%s_fiscalyear_id" % (index,)
  287. period_from_field = "comp%s_period_from" % (index,)
  288. period_to_field = "comp%s_period_to" % (index,)
  289. date_from_field = "comp%s_date_from" % (index,)
  290. date_to_field = "comp%s_date_to" % (index,)
  291. if comp_filter == 'filter_no':
  292. res['value'] = {
  293. fy_id_field: False,
  294. period_from_field: False,
  295. period_to_field: False,
  296. date_from_field: False,
  297. date_to_field: False
  298. }
  299. if comp_filter in ('filter_year', 'filter_opening'):
  300. res['value'] = {
  301. fy_id_field: last_fiscalyear_id,
  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 == 'filter_date':
  308. dates = {}
  309. if main_filter == 'filter_date':
  310. dates = {
  311. 'date_start': previous_year_date(start_date, index + 1).
  312. strftime('%Y-%m-%d'),
  313. 'date_stop': previous_year_date(stop_date, index + 1).
  314. strftime('%Y-%m-%d'),
  315. }
  316. elif last_fiscalyear_id:
  317. dates = fy_obj.read(
  318. cr, uid, last_fiscalyear_id, ['date_start', 'date_stop'],
  319. context=context)
  320. res['value'] = {fy_id_field: False,
  321. period_from_field: False,
  322. period_to_field: False,
  323. date_from_field: dates.get('date_start', False),
  324. date_to_field: dates.get('date_stop', False)}
  325. if comp_filter == 'filter_period' and last_fiscalyear_id:
  326. start_period = end_period = False
  327. cr.execute('''
  328. SELECT * FROM (SELECT p.id
  329. FROM account_period p
  330. LEFT JOIN account_fiscalyear f
  331. ON (p.fiscalyear_id = f.id)
  332. WHERE f.id = %(fiscalyear)s
  333. AND COALESCE(p.special, FALSE) = FALSE
  334. ORDER BY p.date_start ASC
  335. LIMIT 1) AS period_start
  336. UNION ALL
  337. SELECT * FROM (SELECT p.id
  338. FROM account_period p
  339. LEFT JOIN account_fiscalyear f
  340. ON (p.fiscalyear_id = f.id)
  341. WHERE f.id = %(fiscalyear)s
  342. AND p.date_start < NOW()
  343. AND COALESCE(p.special, FALSE) = FALSE
  344. ORDER BY p.date_stop DESC
  345. LIMIT 1) AS period_stop''',
  346. {'fiscalyear': last_fiscalyear_id})
  347. periods = [i[0] for i in cr.fetchall()]
  348. if periods and len(periods) > 1:
  349. start_period = end_period = periods[0]
  350. if len(periods) > 1:
  351. end_period = periods[1]
  352. res['value'] = {fy_id_field: False,
  353. period_from_field: start_period,
  354. period_to_field: end_period,
  355. date_from_field: False,
  356. date_to_field: False}
  357. return res
  358. def pre_print_report(self, cr, uid, ids, data, context=None):
  359. data = super(AccountBalanceCommonWizard, self).pre_print_report(
  360. cr, uid, ids, data, context=context)
  361. if context is None:
  362. context = {}
  363. # will be used to attach the report on the main account
  364. data['ids'] = [data['form']['chart_account_id']]
  365. fields_to_read = ['account_ids', 'account_level']
  366. fields_to_read += self.DYNAMIC_FIELDS
  367. vals = self.read(cr, uid, ids, fields_to_read, context=context)[0]
  368. # extract the id from the m2o tuple (id, name)
  369. for field in self.M2O_DYNAMIC_FIELDS:
  370. if isinstance(vals[field], tuple):
  371. vals[field] = vals[field][0]
  372. vals['max_comparison'] = self.COMPARISON_LEVEL
  373. data['form'].update(vals)
  374. return data