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.

273 lines
12 KiB

[FIX] partner_financial_risk: Allow contact creation Without this patch, when creating a new contact from the partner form, users got this traceback: ``` Traceback (most recent call last): File "<console>", line 1, in <module> File "/opt/odoo/custom/src/odoo/openerp/api.py", line 248, in wrapper return new_api(self, *args, **kwargs) File "/opt/odoo/auto/addons/partner_financial_risk/models/res_partner.py", line 119, in _compute_risk_invoice active_test=False).search([('id', 'child_of', partner.id)]).ids File "/opt/odoo/custom/src/odoo/openerp/api.py", line 248, in wrapper return new_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 490, in new_api result = method(self._model, cr, uid, *args, **old_kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 1668, in search return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/addons/base/res/res_partner.py", line 626, in _search count=count, access_rights_uid=access_rights_uid) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 4793, in _search query = self._where_calc(cr, user, args, context=context) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 4564, in _where_calc e = expression.expression(cr, user, domain, self, context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 644, in __init__ self.parse(cr, uid, context=context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 834, in parse ids2 = to_ids(right, model, context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 708, in to_ids return list(value) TypeError: 'NewId' object is not iterable ``` Patch includes a recomputation when the partner is or not set as customer, since the field is used in the method.
7 years ago
[FIX] partner_financial_risk: Allow contact creation Without this patch, when creating a new contact from the partner form, users got this traceback: ``` Traceback (most recent call last): File "<console>", line 1, in <module> File "/opt/odoo/custom/src/odoo/openerp/api.py", line 248, in wrapper return new_api(self, *args, **kwargs) File "/opt/odoo/auto/addons/partner_financial_risk/models/res_partner.py", line 119, in _compute_risk_invoice active_test=False).search([('id', 'child_of', partner.id)]).ids File "/opt/odoo/custom/src/odoo/openerp/api.py", line 248, in wrapper return new_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 490, in new_api result = method(self._model, cr, uid, *args, **old_kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 1668, in search return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/addons/base/res/res_partner.py", line 626, in _search count=count, access_rights_uid=access_rights_uid) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 4793, in _search query = self._where_calc(cr, user, args, context=context) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 4564, in _where_calc e = expression.expression(cr, user, domain, self, context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 644, in __init__ self.parse(cr, uid, context=context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 834, in parse ids2 = to_ids(right, model, context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 708, in to_ids return list(value) TypeError: 'NewId' object is not iterable ``` Patch includes a recomputation when the partner is or not set as customer, since the field is used in the method.
7 years ago
[FIX] partner_financial_risk: Allow contact creation Without this patch, when creating a new contact from the partner form, users got this traceback: ``` Traceback (most recent call last): File "<console>", line 1, in <module> File "/opt/odoo/custom/src/odoo/openerp/api.py", line 248, in wrapper return new_api(self, *args, **kwargs) File "/opt/odoo/auto/addons/partner_financial_risk/models/res_partner.py", line 119, in _compute_risk_invoice active_test=False).search([('id', 'child_of', partner.id)]).ids File "/opt/odoo/custom/src/odoo/openerp/api.py", line 248, in wrapper return new_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 490, in new_api result = method(self._model, cr, uid, *args, **old_kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 1668, in search return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/addons/base/res/res_partner.py", line 626, in _search count=count, access_rights_uid=access_rights_uid) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 4793, in _search query = self._where_calc(cr, user, args, context=context) File "/opt/odoo/custom/src/odoo/openerp/api.py", line 250, in wrapper return old_api(self, *args, **kwargs) File "/opt/odoo/custom/src/odoo/openerp/models.py", line 4564, in _where_calc e = expression.expression(cr, user, domain, self, context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 644, in __init__ self.parse(cr, uid, context=context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 834, in parse ids2 = to_ids(right, model, context) File "/opt/odoo/custom/src/odoo/openerp/osv/expression.py", line 708, in to_ids return list(value) TypeError: 'NewId' object is not iterable ``` Patch includes a recomputation when the partner is or not set as customer, since the field is used in the method.
7 years ago
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. from collections import defaultdict
  5. from datetime import datetime
  6. from dateutil.relativedelta import relativedelta
  7. from openerp import api, fields, models
  8. class ResPartner(models.Model):
  9. _inherit = 'res.partner'
  10. move_line_ids = fields.One2many(
  11. comodel_name='account.move.line',
  12. inverse_name='partner_id',
  13. string='Account Moves'
  14. )
  15. risk_invoice_draft_include = fields.Boolean(
  16. string='Include Draft Invoices', help='Full risk computation')
  17. risk_invoice_draft_limit = fields.Monetary(
  18. string='Limit In Draft Invoices', help='Set 0 if it is not locked')
  19. risk_invoice_draft = fields.Monetary(
  20. compute='_compute_risk_invoice', store=True,
  21. string='Total Draft Invoices',
  22. help='Total amount of invoices in Draft or Pro-forma state')
  23. risk_invoice_open_include = fields.Boolean(
  24. string='Include Open Invoices/Principal Balance',
  25. help='Full risk computation.\n'
  26. 'Residual amount of move lines not reconciled with the same '
  27. 'account that is set as partner receivable and date maturity '
  28. 'not exceeded, considering Due Margin set in account settings.')
  29. risk_invoice_open_limit = fields.Monetary(
  30. string='Limit In Open Invoices/Principal Balance',
  31. help='Set 0 if it is not locked')
  32. risk_invoice_open = fields.Monetary(
  33. compute='_compute_risk_account_amount', store=True,
  34. string='Total Open Invoices/Principal Balance',
  35. help='Residual amount of move lines not reconciled with the same '
  36. 'account that is set as partner receivable and date maturity '
  37. 'not exceeded, considering Due Margin set in account settings.')
  38. risk_invoice_unpaid_include = fields.Boolean(
  39. string='Include Unpaid Invoices/Principal Balance',
  40. help='Full risk computation.\n'
  41. 'Residual amount of move lines not reconciled with the same '
  42. 'account that is set as partner receivable and date maturity '
  43. 'exceeded, considering Due Margin set in account settings.')
  44. risk_invoice_unpaid_limit = fields.Monetary(
  45. string='Limit In Unpaid Invoices/Principal Balance',
  46. help='Set 0 if it is not locked')
  47. risk_invoice_unpaid = fields.Monetary(
  48. compute='_compute_risk_account_amount', store=True,
  49. string='Total Unpaid Invoices/Principal Balance',
  50. help='Residual amount of move lines not reconciled with the same '
  51. 'account that is set as partner receivable and date maturity '
  52. 'exceeded, considering Due Margin set in account settings.')
  53. risk_account_amount_include = fields.Boolean(
  54. string='Include Other Account Open Amount',
  55. help='Full risk computation.\n'
  56. 'Residual amount of move lines not reconciled with distinct '
  57. 'account that is set as partner receivable and date maturity '
  58. 'not exceeded, considering Due Margin set in account settings.')
  59. risk_account_amount_limit = fields.Monetary(
  60. string='Limit Other Account Open Amount',
  61. help='Set 0 if it is not locked')
  62. risk_account_amount = fields.Monetary(
  63. compute='_compute_risk_account_amount', store=True,
  64. string='Total Other Account Open Amount',
  65. help='Residual amount of move lines not reconciled with distinct '
  66. 'account that is set as partner receivable and date maturity '
  67. 'not exceeded, considering Due Margin set in account settings.')
  68. risk_account_amount_unpaid_include = fields.Boolean(
  69. string='Include Other Account Unpaid Amount',
  70. help='Full risk computation.\n'
  71. 'Residual amount of move lines not reconciled with distinct '
  72. 'account that is set as partner receivable and date maturity '
  73. 'exceeded, considering Due Margin set in account settings.')
  74. risk_account_amount_unpaid_limit = fields.Monetary(
  75. string='Limit Other Account Unpaid Amount',
  76. help='Set 0 if it is not locked')
  77. risk_account_amount_unpaid = fields.Monetary(
  78. compute='_compute_risk_account_amount', store=True,
  79. string='Total Other Account Unpaid Amount',
  80. help='Residual amount of move lines not reconciled with distinct '
  81. 'account that is set as partner receivable and date maturity '
  82. 'exceeded, considering Due Margin set in account settings.')
  83. risk_total = fields.Monetary(
  84. compute='_compute_risk_exception',
  85. string='Total Risk', help='Sum of total risk included')
  86. risk_exception = fields.Boolean(
  87. compute='_compute_risk_exception',
  88. string='Risk Exception',
  89. help='It Indicate if partner risk exceeded')
  90. credit_policy = fields.Char()
  91. risk_allow_edit = fields.Boolean(compute='_compute_risk_allow_edit')
  92. credit_limit = fields.Float(track_visibility='onchange')
  93. @api.multi
  94. def _compute_risk_allow_edit(self):
  95. is_editable = self.env.user.has_group(
  96. 'base.group_sale_manager') or self.env.user.has_group(
  97. 'account.group_account_manager')
  98. for partner in self.filtered('customer'):
  99. partner.risk_allow_edit = is_editable
  100. @api.multi
  101. @api.depends(
  102. 'customer', 'invoice_ids', 'invoice_ids.state',
  103. 'invoice_ids.amount_total',
  104. 'child_ids.invoice_ids', 'child_ids.invoice_ids.state',
  105. 'child_ids.invoice_ids.amount_total')
  106. def _compute_risk_invoice(self):
  107. all_partners_and_children = {}
  108. all_partner_ids = []
  109. for partner in self.filtered('customer'):
  110. if not partner.id:
  111. continue
  112. all_partners_and_children[partner] = self.with_context(
  113. active_test=False).search([('id', 'child_of', partner.id)]).ids
  114. all_partner_ids += all_partners_and_children[partner]
  115. if not all_partner_ids:
  116. return
  117. total_group = self.env['account.invoice'].sudo().read_group(
  118. [('type', 'in', ['out_invoice', 'out_refund']),
  119. ('state', 'in', ['draft', 'proforma', 'proforma2']),
  120. ('partner_id', 'in', self.ids)],
  121. ['partner_id', 'amount_total'],
  122. ['partner_id'])
  123. for partner, child_ids in all_partners_and_children.items():
  124. partner.risk_invoice_draft = sum(
  125. x['amount_total']
  126. for x in total_group if x['partner_id'][0] in child_ids)
  127. @api.model
  128. def _risk_account_groups(self):
  129. max_date = self._max_risk_date_due()
  130. return {
  131. 'open': {
  132. 'domain': [('reconciled', '=', False),
  133. ('account_id.internal_type', '=', 'receivable'),
  134. ('date_maturity', '>=', max_date)],
  135. 'fields': ['partner_id', 'account_id', 'amount_residual'],
  136. 'group_by': ['partner_id', 'account_id']
  137. },
  138. 'unpaid': {
  139. 'domain': [('reconciled', '=', False),
  140. ('account_id.internal_type', '=', 'receivable'),
  141. ('date_maturity', '<', max_date)],
  142. 'fields': ['partner_id', 'account_id', 'amount_residual'],
  143. 'group_by': ['partner_id', 'account_id']
  144. }
  145. }
  146. @api.multi
  147. @api.depends('move_line_ids.amount_residual',
  148. 'move_line_ids.date_maturity',
  149. 'company_id.invoice_unpaid_margin')
  150. def _compute_risk_account_amount(self):
  151. AccountMoveLine = self.env['account.move.line'].sudo()
  152. customers = self.filtered(lambda x: x.customer and not x.parent_id)
  153. if not customers:
  154. return
  155. groups = self._risk_account_groups()
  156. for key, group in groups.iteritems():
  157. group['read_group'] = AccountMoveLine.read_group(
  158. group['domain'] + [('partner_id', 'in', customers.ids)],
  159. group['fields'],
  160. group['group_by'],
  161. lazy=False,
  162. )
  163. for partner in customers:
  164. partner.update(partner._prepare_risk_account_vals(groups))
  165. @api.multi
  166. def _prepare_risk_account_vals(self, groups):
  167. vals = {
  168. 'risk_invoice_open': 0.0,
  169. 'risk_invoice_unpaid': 0.0,
  170. 'risk_account_amount': 0.0,
  171. 'risk_account_amount_unpaid': 0.0,
  172. }
  173. for reg in groups['open']['read_group']:
  174. if reg['partner_id'][0] != self.id:
  175. continue
  176. if self.property_account_receivable_id.id == reg['account_id'][0]:
  177. vals['risk_invoice_open'] += reg['amount_residual']
  178. else:
  179. vals['risk_account_amount'] += reg['amount_residual']
  180. for reg in groups['unpaid']['read_group']:
  181. if reg['partner_id'][0] != self.id:
  182. continue # pragma: no cover
  183. if self.property_account_receivable_id.id == reg['account_id'][0]:
  184. vals['risk_invoice_unpaid'] += reg['amount_residual']
  185. else:
  186. vals['risk_account_amount_unpaid'] += reg['amount_residual']
  187. return vals
  188. @api.multi
  189. @api.depends(lambda x: x._get_depends_compute_risk_exception())
  190. def _compute_risk_exception(self):
  191. risk_field_list = self._risk_field_list()
  192. for partner in self.filtered('customer'):
  193. amount = 0.0
  194. for risk_field in risk_field_list:
  195. field_value = getattr(partner, risk_field[0], 0.0)
  196. max_value = getattr(partner, risk_field[1], 0.0)
  197. if max_value and field_value > max_value:
  198. partner.risk_exception = True
  199. if getattr(partner, risk_field[2], False):
  200. amount += field_value
  201. partner.risk_total = amount
  202. if partner.credit_limit and amount > partner.credit_limit:
  203. partner.risk_exception = True
  204. @api.model
  205. def _max_risk_date_due(self):
  206. return fields.Date.to_string(datetime.today().date() - relativedelta(
  207. days=self.env.user.company_id.invoice_unpaid_margin))
  208. @api.model
  209. def _risk_field_list(self):
  210. return [
  211. ('risk_invoice_draft', 'risk_invoice_draft_limit',
  212. 'risk_invoice_draft_include'),
  213. ('risk_invoice_open', 'risk_invoice_open_limit',
  214. 'risk_invoice_open_include'),
  215. ('risk_invoice_unpaid', 'risk_invoice_unpaid_limit',
  216. 'risk_invoice_unpaid_include'),
  217. ('risk_account_amount', 'risk_account_amount_limit',
  218. 'risk_account_amount_include'),
  219. ('risk_account_amount_unpaid', 'risk_account_amount_unpaid_limit',
  220. 'risk_account_amount_unpaid_include'),
  221. ]
  222. @api.model
  223. def _get_depends_compute_risk_exception(self):
  224. res = []
  225. for x in self._risk_field_list():
  226. res.extend((x[0], x[1], x[2], 'child_ids.%s' % x[0],
  227. 'child_ids.%s' % x[1], 'child_ids.%s' % x[2]))
  228. res.extend(('credit_limit', 'child_ids.credit_limit'))
  229. return res
  230. @api.model
  231. def process_unpaid_invoices(self):
  232. max_date = self._max_risk_date_due()
  233. ConfigParameter = self.env['ir.config_parameter']
  234. last_check = ConfigParameter.get_param(
  235. 'partner_financial_risk.last_check', default='2016-01-01')
  236. groups = self.env['account.move.line'].sudo().read_group(
  237. [('reconciled', '=', False),
  238. ('partner_id', '!=', False),
  239. ('account_id.internal_type', '=', 'receivable'),
  240. ('date_maturity', '>=', last_check),
  241. ('date_maturity', '<', max_date)],
  242. ['company_id', 'partner_id'],
  243. ['company_id', 'partner_id'],
  244. lazy=False,
  245. )
  246. group_dic = defaultdict(list)
  247. for group in groups:
  248. group_dic[group['company_id'][0]].append(group['partner_id'][0])
  249. for company_id, partner_ids in group_dic.iteritems():
  250. partners = self.browse(partner_ids)
  251. partners.with_context(
  252. force_company=company_id,
  253. )._compute_risk_account_amount()
  254. ConfigParameter.set_param(
  255. 'partner_financial_risk.last_check', max_date)
  256. return True