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.

323 lines
15 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2017 Eficent Business and IT Consulting Services S.L.
  3. # (http://www.eficent.com)
  4. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  5. from datetime import datetime, timedelta
  6. from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
  7. from openerp import api, fields, models
  8. class CustomerOutstandingStatement(models.AbstractModel):
  9. """Model of Customer Outstanding Statement"""
  10. _name = 'report.customer_outstanding_statement.statement'
  11. def _format_date_to_partner_lang(self, str_date, partner_id):
  12. lang_code = self.env['res.partner'].browse(partner_id).lang
  13. lang_id = self.env['res.lang']._lang_get(lang_code)
  14. lang = self.env['res.lang'].browse(lang_id)
  15. date = datetime.strptime(str_date, DEFAULT_SERVER_DATE_FORMAT).date()
  16. return date.strftime(lang.date_format)
  17. def _display_lines_sql_q1(self, partners, date_end):
  18. return """
  19. SELECT m.name as move_id, l.partner_id, l.date, l.name,
  20. l.ref, l.blocked, l.currency_id, l.company_id,
  21. CASE WHEN (l.currency_id is not null AND l.amount_currency > 0.0)
  22. THEN sum(l.amount_currency)
  23. ELSE sum(l.debit)
  24. END as debit,
  25. CASE WHEN (l.currency_id is not null AND l.amount_currency < 0.0)
  26. THEN sum(l.amount_currency * (-1))
  27. ELSE sum(l.credit)
  28. END as credit,
  29. CASE WHEN l.balance > 0.0
  30. THEN l.balance - sum(coalesce(pd.amount, 0.0))
  31. ELSE l.balance + sum(coalesce(pc.amount, 0.0))
  32. END AS open_amount,
  33. CASE WHEN l.balance > 0.0
  34. THEN l.amount_currency - sum(coalesce(pd.amount_currency, 0.0))
  35. ELSE l.amount_currency + sum(coalesce(pc.amount_currency, 0.0))
  36. END AS open_amount_currency,
  37. CASE WHEN l.date_maturity is null
  38. THEN l.date
  39. ELSE l.date_maturity
  40. END as date_maturity
  41. FROM account_move_line l
  42. JOIN account_account_type at ON (at.id = l.user_type_id)
  43. JOIN account_move m ON (l.move_id = m.id)
  44. LEFT JOIN (SELECT pr.*
  45. FROM account_partial_reconcile pr
  46. INNER JOIN account_move_line l2
  47. ON pr.credit_move_id = l2.id
  48. WHERE l2.date <= '%s'
  49. ) as pd ON pd.debit_move_id = l.id
  50. LEFT JOIN (SELECT pr.*
  51. FROM account_partial_reconcile pr
  52. INNER JOIN account_move_line l2
  53. ON pr.debit_move_id = l2.id
  54. WHERE l2.date <= '%s'
  55. ) as pc ON pc.credit_move_id = l.id
  56. WHERE l.partner_id IN (%s) AND at.type = 'receivable'
  57. AND not l.reconciled AND l.date <= '%s'
  58. GROUP BY l.partner_id, m.name, l.date, l.date_maturity, l.name,
  59. l.ref, l.blocked, l.currency_id,
  60. l.balance, l.amount_currency, l.company_id
  61. """ % (date_end, date_end, partners, date_end)
  62. def _display_lines_sql_q2(self):
  63. return """
  64. SELECT partner_id, currency_id, move_id, date, date_maturity,
  65. debit, credit, name, ref, blocked, company_id,
  66. CASE WHEN currency_id is not null
  67. THEN open_amount_currency
  68. ELSE open_amount
  69. END as open_amount
  70. FROM Q1
  71. """
  72. def _display_lines_sql_q3(self, company_id):
  73. return """
  74. SELECT Q2.partner_id, move_id, date, date_maturity, Q2.name, ref,
  75. debit, credit, debit-credit AS amount, blocked,
  76. COALESCE(Q2.currency_id, c.currency_id) AS currency_id, open_amount
  77. FROM Q2
  78. JOIN res_company c ON (c.id = Q2.company_id)
  79. WHERE c.id = %s
  80. """ % company_id
  81. def _get_account_display_lines(self, company_id, partner_ids, date_end):
  82. res = dict(map(lambda x: (x, []), partner_ids))
  83. partners = ', '.join([str(i) for i in partner_ids])
  84. date_end = datetime.strptime(
  85. date_end, DEFAULT_SERVER_DATE_FORMAT).date()
  86. self.env.cr.execute("""WITH Q1 AS (%s), Q2 AS (%s), Q3 AS (%s)
  87. SELECT partner_id, currency_id, move_id, date, date_maturity, debit,
  88. credit, amount, open_amount, name, ref, blocked
  89. FROM Q3
  90. ORDER BY date, date_maturity, move_id""" % (
  91. self._display_lines_sql_q1(partners, date_end),
  92. self._display_lines_sql_q2(),
  93. self._display_lines_sql_q3(company_id)))
  94. for row in self.env.cr.dictfetchall():
  95. res[row.pop('partner_id')].append(row)
  96. return res
  97. def _show_buckets_sql_q1(self, partners, date_end):
  98. return """
  99. SELECT l.partner_id, l.currency_id, l.company_id, l.move_id,
  100. CASE WHEN l.balance > 0.0
  101. THEN l.balance - sum(coalesce(pd.amount, 0.0))
  102. ELSE l.balance + sum(coalesce(pc.amount, 0.0))
  103. END AS open_due,
  104. CASE WHEN l.balance > 0.0
  105. THEN l.amount_currency - sum(coalesce(pd.amount_currency, 0.0))
  106. ELSE l.amount_currency + sum(coalesce(pc.amount_currency, 0.0))
  107. END AS open_due_currency,
  108. CASE WHEN l.date_maturity is null
  109. THEN l.date
  110. ELSE l.date_maturity
  111. END as date_maturity
  112. FROM account_move_line l
  113. JOIN account_account_type at ON (at.id = l.user_type_id)
  114. JOIN account_move m ON (l.move_id = m.id)
  115. LEFT JOIN (SELECT pr.*
  116. FROM account_partial_reconcile pr
  117. INNER JOIN account_move_line l2
  118. ON pr.credit_move_id = l2.id
  119. WHERE l2.date <= '%s'
  120. ) as pd ON pd.debit_move_id = l.id
  121. LEFT JOIN (SELECT pr.*
  122. FROM account_partial_reconcile pr
  123. INNER JOIN account_move_line l2
  124. ON pr.debit_move_id = l2.id
  125. WHERE l2.date <= '%s'
  126. ) as pc ON pc.credit_move_id = l.id
  127. WHERE l.partner_id IN (%s) AND at.type = 'receivable'
  128. AND not l.reconciled AND not l.blocked
  129. GROUP BY l.partner_id, l.currency_id, l.date, l.date_maturity,
  130. l.amount_currency, l.balance, l.move_id,
  131. l.company_id
  132. """ % (date_end, date_end, partners)
  133. def _show_buckets_sql_q2(self, today, minus_30, minus_60, minus_90,
  134. minus_120):
  135. return """
  136. SELECT partner_id, currency_id, date_maturity, open_due,
  137. open_due_currency, move_id, company_id,
  138. CASE
  139. WHEN '%s' <= date_maturity AND currency_id is null
  140. THEN open_due
  141. WHEN '%s' <= date_maturity AND currency_id is not null
  142. THEN open_due_currency
  143. ELSE 0.0
  144. END as current,
  145. CASE
  146. WHEN '%s' < date_maturity AND date_maturity < '%s'
  147. AND currency_id is null THEN open_due
  148. WHEN '%s' < date_maturity AND date_maturity < '%s'
  149. AND currency_id is not null
  150. THEN open_due_currency
  151. ELSE 0.0
  152. END as b_1_30,
  153. CASE
  154. WHEN '%s' < date_maturity AND date_maturity <= '%s'
  155. AND currency_id is null THEN open_due
  156. WHEN '%s' < date_maturity AND date_maturity <= '%s'
  157. AND currency_id is not null
  158. THEN open_due_currency
  159. ELSE 0.0
  160. END as b_30_60,
  161. CASE
  162. WHEN '%s' < date_maturity AND date_maturity <= '%s'
  163. AND currency_id is null THEN open_due
  164. WHEN '%s' < date_maturity AND date_maturity <= '%s'
  165. AND currency_id is not null
  166. THEN open_due_currency
  167. ELSE 0.0
  168. END as b_60_90,
  169. CASE
  170. WHEN '%s' < date_maturity AND date_maturity <= '%s'
  171. AND currency_id is null THEN open_due
  172. WHEN '%s' < date_maturity AND date_maturity <= '%s'
  173. AND currency_id is not null
  174. THEN open_due_currency
  175. ELSE 0.0
  176. END as b_90_120,
  177. CASE
  178. WHEN date_maturity <= '%s' AND currency_id is null
  179. THEN open_due
  180. WHEN date_maturity <= '%s' AND currency_id is not null
  181. THEN open_due_currency
  182. ELSE 0.0
  183. END as b_over_120
  184. FROM Q1
  185. GROUP BY partner_id, currency_id, date_maturity, open_due,
  186. open_due_currency, move_id, company_id
  187. """ % (today, today, minus_30, today, minus_30, today, minus_60,
  188. minus_30, minus_60, minus_30, minus_90, minus_60, minus_90,
  189. minus_60, minus_120, minus_90, minus_120, minus_90, minus_120,
  190. minus_120)
  191. def _show_buckets_sql_q3(self, company_id):
  192. return """
  193. SELECT Q2.partner_id, current, b_1_30, b_30_60, b_60_90, b_90_120,
  194. b_over_120,
  195. COALESCE(Q2.currency_id, c.currency_id) AS currency_id
  196. FROM Q2
  197. JOIN res_company c ON (c.id = Q2.company_id)
  198. WHERE c.id = %s
  199. """ % company_id
  200. def _show_buckets_sql_q4(self):
  201. return """
  202. SELECT partner_id, currency_id, sum(current) as current,
  203. sum(b_1_30) as b_1_30,
  204. sum(b_30_60) as b_30_60,
  205. sum(b_60_90) as b_60_90,
  206. sum(b_90_120) as b_90_120,
  207. sum(b_over_120) as b_over_120
  208. FROM Q3
  209. GROUP BY partner_id, currency_id
  210. """
  211. _bucket_dates = {
  212. 'today': fields.date.today(),
  213. 'minus_30': fields.date.today() - timedelta(days=30),
  214. 'minus_60': fields.date.today() - timedelta(days=60),
  215. 'minus_90': fields.date.today() - timedelta(days=90),
  216. 'minus_120': fields.date.today() - timedelta(days=120),
  217. }
  218. def _get_account_show_buckets(self, company_id, partner_ids, date_end):
  219. res = dict(map(lambda x: (x, []), partner_ids))
  220. partners = ', '.join([str(i) for i in partner_ids])
  221. date_end = datetime.strptime(
  222. date_end, DEFAULT_SERVER_DATE_FORMAT).date()
  223. self.env.cr.execute("""WITH Q1 AS (%s), Q2 AS (%s),
  224. Q3 AS (%s), Q4 AS (%s)
  225. SELECT partner_id, currency_id, current, b_1_30, b_30_60, b_60_90,
  226. b_90_120, b_over_120,
  227. current+b_1_30+b_30_60+b_60_90+b_90_120+b_over_120
  228. AS balance
  229. FROM Q4
  230. GROUP BY partner_id, currency_id, current, b_1_30, b_30_60, b_60_90,
  231. b_90_120, b_over_120""" % (
  232. self._show_buckets_sql_q1(partners, date_end),
  233. self._show_buckets_sql_q2(
  234. self._bucket_dates['today'],
  235. self._bucket_dates['minus_30'],
  236. self._bucket_dates['minus_60'],
  237. self._bucket_dates['minus_90'],
  238. self._bucket_dates['minus_120']),
  239. self._show_buckets_sql_q3(company_id),
  240. self._show_buckets_sql_q4()))
  241. for row in self.env.cr.dictfetchall():
  242. res[row.pop('partner_id')].append(row)
  243. return res
  244. @api.multi
  245. def render_html(self, data):
  246. company_id = data['company_id']
  247. partner_ids = data['partner_ids']
  248. date_end = data['date_end']
  249. today = fields.Date.today()
  250. buckets_to_display = {}
  251. lines_to_display, amount_due = {}, {}
  252. currency_to_display = {}
  253. today_display, date_end_display = {}, {}
  254. lines = self._get_account_display_lines(
  255. company_id, partner_ids, date_end)
  256. for partner_id in partner_ids:
  257. lines_to_display[partner_id], amount_due[partner_id] = {}, {}
  258. currency_to_display[partner_id] = {}
  259. today_display[partner_id] = self._format_date_to_partner_lang(
  260. today, partner_id)
  261. date_end_display[partner_id] = self._format_date_to_partner_lang(
  262. date_end, partner_id)
  263. for line in lines[partner_id]:
  264. currency = self.env['res.currency'].browse(line['currency_id'])
  265. if currency not in lines_to_display[partner_id]:
  266. lines_to_display[partner_id][currency] = []
  267. currency_to_display[partner_id][currency] = currency
  268. amount_due[partner_id][currency] = 0.0
  269. if not line['blocked']:
  270. amount_due[partner_id][currency] += line['open_amount']
  271. line['balance'] = amount_due[partner_id][currency]
  272. line['date'] = self._format_date_to_partner_lang(
  273. line['date'], partner_id)
  274. line['date_maturity'] = self._format_date_to_partner_lang(
  275. line['date_maturity'], partner_id)
  276. lines_to_display[partner_id][currency].append(line)
  277. if data['show_aging_buckets']:
  278. buckets = self._get_account_show_buckets(
  279. company_id, partner_ids, date_end)
  280. for partner_id in partner_ids:
  281. buckets_to_display[partner_id] = {}
  282. for line in buckets[partner_id]:
  283. currency = self.env['res.currency'].browse(
  284. line['currency_id'])
  285. if currency not in buckets_to_display[partner_id]:
  286. buckets_to_display[partner_id][currency] = []
  287. buckets_to_display[partner_id][currency] = line
  288. docargs = {
  289. 'doc_ids': partner_ids,
  290. 'doc_model': 'res.partner',
  291. 'docs': self.env['res.partner'].browse(partner_ids),
  292. 'Amount_Due': amount_due,
  293. 'Lines': lines_to_display,
  294. 'Buckets': buckets_to_display,
  295. 'Currencies': currency_to_display,
  296. 'Show_Buckets': data['show_aging_buckets'],
  297. 'Filter_non_due_partners': data['filter_non_due_partners'],
  298. 'Date_end': date_end_display,
  299. 'Date': today_display,
  300. }
  301. return self.env['report'].render(
  302. 'customer_outstanding_statement.statement', values=docargs)