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
14 KiB

  1. # © 2016 Julien Coux (Camptocamp)
  2. # Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com)
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. from odoo import models, api
  5. from odoo.tools import float_is_zero
  6. from datetime import date, datetime
  7. import operator
  8. class OpenItemsReport(models.AbstractModel):
  9. _name = 'report.account_financial_report.open_items'
  10. _description = "Open Items Report"
  11. @api.model
  12. def get_html(self, given_context=None):
  13. return self._get_html()
  14. def _get_html(self):
  15. result = {}
  16. rcontext = {}
  17. context = dict(self.env.context)
  18. rcontext.update(context.get('data'))
  19. active_id = context.get('active_id')
  20. wiz = self.env['open.items.report.wizard'].browse(active_id)
  21. rcontext['o'] = wiz
  22. result['html'] = self.env.ref(
  23. 'account_financial_report.report_open_items').render(rcontext)
  24. return result
  25. def _get_account_partial_reconciled(self, company_id, date_at_object):
  26. domain = [('max_date', '>', date_at_object),
  27. ('company_id', '=', company_id)]
  28. fields = ['debit_move_id', 'credit_move_id', 'amount']
  29. accounts_partial_reconcile = \
  30. self.env['account.partial.reconcile'].search_read(
  31. domain=domain,
  32. fields=fields
  33. )
  34. debit_amount = {}
  35. credit_amount = {}
  36. for account_partial_reconcile_data in accounts_partial_reconcile:
  37. debit_move_id = account_partial_reconcile_data['debit_move_id'][0]
  38. credit_move_id = account_partial_reconcile_data['credit_move_id'][0]
  39. if debit_move_id not in debit_amount.keys():
  40. debit_amount[debit_move_id] = 0.0
  41. debit_amount[debit_move_id] += \
  42. account_partial_reconcile_data['amount']
  43. if credit_move_id not in credit_amount.keys():
  44. credit_amount[credit_move_id] = 0.0
  45. credit_amount[credit_move_id] += \
  46. account_partial_reconcile_data['amount']
  47. account_partial_reconcile_data.update({
  48. 'debit_move_id': debit_move_id,
  49. 'credit_move_id': credit_move_id,
  50. })
  51. return accounts_partial_reconcile, debit_amount, credit_amount
  52. @api.model
  53. def _get_new_move_lines_domain(self, new_ml_ids, account_ids, company_id,
  54. partner_ids, target_moves):
  55. domain = [('account_id', 'in', account_ids),
  56. ('company_id', '=', company_id),
  57. ('id', 'in', new_ml_ids)]
  58. if partner_ids:
  59. domain += [('partner_id', 'in', partner_ids)]
  60. if target_moves == 'posted':
  61. domain += [('move_id.state', '=', 'posted')]
  62. return domain
  63. def _recalculate_move_lines(self, move_lines, debit_ids, credit_ids,
  64. debit_amount, credit_amount, ml_ids,
  65. account_ids, company_id, partner_ids,
  66. target_moves):
  67. debit_ids = set(debit_ids)
  68. credit_ids = set(credit_ids)
  69. in_credit_but_not_in_debit = credit_ids - debit_ids
  70. reconciled_ids = list(debit_ids) + list(in_credit_but_not_in_debit)
  71. reconciled_ids = set(reconciled_ids)
  72. ml_ids = set(ml_ids)
  73. new_ml_ids = reconciled_ids - ml_ids
  74. new_ml_ids = list(new_ml_ids)
  75. new_domain = self._get_new_move_lines_domain(new_ml_ids, account_ids,
  76. company_id, partner_ids,
  77. target_moves)
  78. ml_fields = [
  79. 'id', 'name', 'date', 'move_id', 'journal_id', 'account_id',
  80. 'partner_id', 'amount_residual', 'date_maturity', 'ref',
  81. 'debit', 'credit', 'reconciled', 'currency_id', 'amount_currency',
  82. 'amount_residual_currency']
  83. new_move_lines = self.env['account.move.line'].search_read(
  84. domain=new_domain, fields=ml_fields
  85. )
  86. move_lines = move_lines + new_move_lines
  87. for move_line in move_lines:
  88. ml_id = move_line['id']
  89. if ml_id in debit_ids:
  90. move_line['amount_residual'] += debit_amount[ml_id]
  91. if ml_id in credit_ids:
  92. move_line['amount_residual'] -= credit_amount[ml_id]
  93. return move_lines
  94. @api.model
  95. def _get_move_lines_domain(self, company_id, account_ids, partner_ids,
  96. target_move, date_from):
  97. domain = [('account_id', 'in', account_ids),
  98. ('company_id', '=', company_id),
  99. ('reconciled', '=', False)]
  100. if partner_ids:
  101. domain += [('partner_id', 'in', partner_ids)]
  102. if target_move == 'posted':
  103. domain += [('move_id.state', '=', 'posted')]
  104. if date_from:
  105. domain += [('date', '>', date_from)]
  106. return domain
  107. def _get_accounts_data(self, accounts_ids):
  108. accounts = self.env['account.account'].browse(accounts_ids)
  109. accounts_data = {}
  110. for account in accounts:
  111. accounts_data.update({account.id: {
  112. 'id': account.id,
  113. 'code': account.code,
  114. 'name': account.name,
  115. 'hide_account': False,
  116. 'currency_id': account.currency_id or False,
  117. 'currency_name': account.currency_id.name}
  118. })
  119. return accounts_data
  120. def _get_journals_data(self, journals_ids):
  121. journals = self.env['account.journal'].browse(journals_ids)
  122. journals_data = {}
  123. for journal in journals:
  124. journals_data.update({journal.id: {'id': journal.id,
  125. 'code': journal.code}})
  126. return journals_data
  127. def _get_data(
  128. self, account_ids, partner_ids, date_at_object,
  129. target_move, company_id, date_from):
  130. domain = self._get_move_lines_domain(company_id, account_ids,
  131. partner_ids, target_move,
  132. date_from)
  133. ml_fields = [
  134. 'id', 'name', 'date', 'move_id', 'journal_id', 'account_id',
  135. 'partner_id', 'amount_residual', 'date_maturity', 'ref',
  136. 'debit', 'credit', 'reconciled', 'currency_id', 'amount_currency',
  137. 'amount_residual_currency']
  138. move_lines = self.env['account.move.line'].search_read(
  139. domain=domain, fields=ml_fields
  140. )
  141. journals_ids = set()
  142. partners_ids = set()
  143. partners_data = {}
  144. if date_at_object < date.today():
  145. acc_partial_rec, debit_amount, credit_amount = \
  146. self._get_account_partial_reconciled(company_id,
  147. date_at_object)
  148. if acc_partial_rec:
  149. ml_ids = list(map(operator.itemgetter('id'), move_lines))
  150. debit_ids = list(map(operator.itemgetter('debit_move_id'),
  151. acc_partial_rec))
  152. credit_ids = list(map(operator.itemgetter('credit_move_id'),
  153. acc_partial_rec))
  154. move_lines = self._recalculate_move_lines(
  155. move_lines, debit_ids, credit_ids,
  156. debit_amount, credit_amount, ml_ids, account_ids,
  157. company_id, partner_ids, target_move
  158. )
  159. move_lines = [move_line for move_line in move_lines if
  160. move_line['date'] <= date_at_object and not
  161. float_is_zero(move_line['amount_residual'],
  162. precision_digits=2)]
  163. open_items_move_lines_data = {}
  164. for move_line in move_lines:
  165. journals_ids.add(move_line['journal_id'][0])
  166. acc_id = move_line['account_id'][0]
  167. # Partners data
  168. if move_line['partner_id']:
  169. prt_id = move_line['partner_id'][0]
  170. prt_name = move_line['partner_id'][1]
  171. else:
  172. prt_id = 0
  173. prt_name = "Missing Partner"
  174. if prt_id not in partners_ids:
  175. partners_data.update({
  176. prt_id: {'id': prt_id, 'name': prt_name}
  177. })
  178. partners_ids.add(prt_id)
  179. # Move line update
  180. original = 0
  181. if not float_is_zero(move_line['credit'], precision_digits=2):
  182. original = move_line['credit']*(-1)
  183. if not float_is_zero(move_line['debit'], precision_digits=2):
  184. original = move_line['debit']
  185. if move_line['ref'] == move_line['name']:
  186. if move_line['ref']:
  187. ref_label = move_line['ref']
  188. else:
  189. ref_label = ''
  190. elif not move_line['ref']:
  191. ref_label = move_line['name']
  192. elif not move_line['name']:
  193. ref_label = move_line['ref']
  194. else:
  195. ref_label = move_line['ref'] + str(' - ') + move_line['name']
  196. move_line.update({
  197. 'date': move_line['date'],
  198. 'date_maturity': move_line["date_maturity"]
  199. and move_line["date_maturity"].strftime("%d/%m/%Y"),
  200. 'original': original,
  201. 'partner_id': prt_id,
  202. 'partner_name': prt_name,
  203. 'ref_label': ref_label,
  204. 'journal_id': move_line['journal_id'][0],
  205. 'move_name': move_line['move_id'][1],
  206. 'currency_id': move_line['currency_id'][0]
  207. if move_line['currency_id'] else False,
  208. 'currency_name': move_line['currency_id'][1]
  209. if move_line['currency_id'] else False,
  210. })
  211. # Open Items Move Lines Data
  212. if acc_id not in open_items_move_lines_data.keys():
  213. open_items_move_lines_data[acc_id] = {prt_id: [move_line]}
  214. else:
  215. if prt_id not in open_items_move_lines_data[acc_id].keys():
  216. open_items_move_lines_data[acc_id][prt_id] = [move_line]
  217. else:
  218. open_items_move_lines_data[acc_id][prt_id].append(move_line)
  219. journals_data = self._get_journals_data(list(journals_ids))
  220. accounts_data = self._get_accounts_data(
  221. open_items_move_lines_data.keys())
  222. return move_lines, partners_data, journals_data, accounts_data, \
  223. open_items_move_lines_data
  224. @api.model
  225. def _calculate_amounts(self, open_items_move_lines_data):
  226. total_amount = {}
  227. for account_id in open_items_move_lines_data.keys():
  228. total_amount[account_id] = {}
  229. total_amount[account_id]['residual'] = 0.0
  230. for partner_id in open_items_move_lines_data[account_id].keys():
  231. total_amount[account_id][partner_id] = {}
  232. total_amount[account_id][partner_id]['residual'] = 0.0
  233. for move_line in open_items_move_lines_data[account_id][
  234. partner_id]:
  235. total_amount[account_id][partner_id]['residual'] += \
  236. move_line['amount_residual']
  237. total_amount[account_id]['residual'] += move_line[
  238. 'amount_residual']
  239. return total_amount
  240. @api.model
  241. def _order_open_items_by_date(
  242. self, open_items_move_lines_data, show_partner_details):
  243. new_open_items = {}
  244. if not show_partner_details:
  245. for acc_id in open_items_move_lines_data.keys():
  246. new_open_items[acc_id] = {}
  247. move_lines = []
  248. for prt_id in open_items_move_lines_data[acc_id]:
  249. for move_line in open_items_move_lines_data[acc_id][prt_id]:
  250. move_lines += [move_line]
  251. move_lines = sorted(move_lines, key=lambda k: (k['date']))
  252. new_open_items[acc_id] = move_lines
  253. else:
  254. for acc_id in open_items_move_lines_data.keys():
  255. new_open_items[acc_id] = {}
  256. for prt_id in open_items_move_lines_data[acc_id]:
  257. new_open_items[acc_id][prt_id] = {}
  258. move_lines = []
  259. for move_line in open_items_move_lines_data[acc_id][prt_id]:
  260. move_lines += [move_line]
  261. move_lines = sorted(move_lines, key=lambda k: (k['date']))
  262. new_open_items[acc_id][prt_id] = move_lines
  263. return new_open_items
  264. @api.multi
  265. def _get_report_values(self, docids, data):
  266. wizard_id = data['wizard_id']
  267. company = self.env['res.company'].browse(data['company_id'])
  268. company_id = data['company_id']
  269. account_ids = data['account_ids']
  270. partner_ids = data['partner_ids']
  271. date_at = data['date_at']
  272. date_at_object = datetime.strptime(date_at, '%Y-%m-%d').date()
  273. date_from = data['date_from']
  274. target_move = data['target_move']
  275. show_partner_details = data['show_partner_details']
  276. move_lines_data, partners_data, journals_data, accounts_data, \
  277. open_items_move_lines_data = self._get_data(
  278. account_ids, partner_ids, date_at_object,
  279. target_move, company_id, date_from)
  280. total_amount = self._calculate_amounts(open_items_move_lines_data)
  281. open_items_move_lines_data = self._order_open_items_by_date(
  282. open_items_move_lines_data, show_partner_details
  283. )
  284. return{
  285. 'doc_ids': [wizard_id],
  286. 'doc_model': 'open.items.report.wizard',
  287. 'docs': self.env['open.items.report.wizard'].browse(wizard_id),
  288. 'foreign_currency': data['foreign_currency'],
  289. 'show_partner_details': data['show_partner_details'],
  290. 'company_name': company.display_name,
  291. 'currency_name': company.currency_id.name,
  292. 'date_at': date_at_object.strftime("%d/%m/%Y"),
  293. 'hide_account_at_0': data['hide_account_at_0'],
  294. 'target_move': data['target_move'],
  295. 'journals_data': journals_data,
  296. 'partners_data': partners_data,
  297. 'accounts_data': accounts_data,
  298. 'total_amount': total_amount,
  299. 'Open_Items': open_items_move_lines_data,
  300. }