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.

373 lines
17 KiB

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