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.

260 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 datetime import datetime
  5. from dateutil.relativedelta import relativedelta
  6. from openerp import api, fields, models
  7. class ResPartner(models.Model):
  8. _inherit = 'res.partner'
  9. move_line_ids = fields.One2many(
  10. comodel_name='account.move.line',
  11. inverse_name='partner_id',
  12. string='Account Moves'
  13. )
  14. risk_invoice_draft_include = fields.Boolean(
  15. string='Include Draft Invoices', help='Full risk computation')
  16. risk_invoice_draft_limit = fields.Monetary(
  17. string='Limit In Draft Invoices', help='Set 0 if it is not locked')
  18. risk_invoice_draft = fields.Monetary(
  19. compute='_compute_risk_invoice', store=True,
  20. string='Total Draft Invoices',
  21. help='Total amount of invoices in Draft or Pro-forma state')
  22. risk_invoice_open_include = fields.Boolean(
  23. string='Include Open Invoices/Principal Balance',
  24. help='Full risk computation.\n'
  25. 'Residual amount of move lines not reconciled with the same '
  26. 'account that is set as partner receivable and date maturity '
  27. 'not exceeded, considering Due Margin set in account settings.')
  28. risk_invoice_open_limit = fields.Monetary(
  29. string='Limit In Open Invoices/Principal Balance',
  30. help='Set 0 if it is not locked')
  31. risk_invoice_open = fields.Monetary(
  32. compute='_compute_risk_account_amount', store=True,
  33. string='Total Open Invoices/Principal Balance',
  34. help='Residual amount of move lines not reconciled with the same '
  35. 'account that is set as partner receivable and date maturity '
  36. 'not exceeded, considering Due Margin set in account settings.')
  37. risk_invoice_unpaid_include = fields.Boolean(
  38. string='Include Unpaid Invoices/Principal Balance',
  39. help='Full risk computation.\n'
  40. 'Residual amount of move lines not reconciled with the same '
  41. 'account that is set as partner receivable and date maturity '
  42. 'exceeded, considering Due Margin set in account settings.')
  43. risk_invoice_unpaid_limit = fields.Monetary(
  44. string='Limit In Unpaid Invoices/Principal Balance',
  45. help='Set 0 if it is not locked')
  46. risk_invoice_unpaid = fields.Monetary(
  47. compute='_compute_risk_account_amount', store=True,
  48. string='Total Unpaid Invoices/Principal Balance',
  49. help='Residual amount of move lines not reconciled with the same '
  50. 'account that is set as partner receivable and date maturity '
  51. 'exceeded, considering Due Margin set in account settings.')
  52. risk_account_amount_include = fields.Boolean(
  53. string='Include Other Account Open Amount',
  54. help='Full risk computation.\n'
  55. 'Residual amount of move lines not reconciled with distinct '
  56. 'account that is set as partner receivable and date maturity '
  57. 'not exceeded, considering Due Margin set in account settings.')
  58. risk_account_amount_limit = fields.Monetary(
  59. string='Limit Other Account Open Amount',
  60. help='Set 0 if it is not locked')
  61. risk_account_amount = fields.Monetary(
  62. compute='_compute_risk_account_amount', store=True,
  63. string='Total Other Account Open Amount',
  64. help='Residual amount of move lines not reconciled with distinct '
  65. 'account that is set as partner receivable and date maturity '
  66. 'not exceeded, considering Due Margin set in account settings.')
  67. risk_account_amount_unpaid_include = fields.Boolean(
  68. string='Include Other Account Unpaid Amount',
  69. help='Full risk computation.\n'
  70. 'Residual amount of move lines not reconciled with distinct '
  71. 'account that is set as partner receivable and date maturity '
  72. 'exceeded, considering Due Margin set in account settings.')
  73. risk_account_amount_unpaid_limit = fields.Monetary(
  74. string='Limit Other Account Unpaid Amount',
  75. help='Set 0 if it is not locked')
  76. risk_account_amount_unpaid = fields.Monetary(
  77. compute='_compute_risk_account_amount', store=True,
  78. string='Total Other Account Unpaid Amount',
  79. help='Residual amount of move lines not reconciled with distinct '
  80. 'account that is set as partner receivable and date maturity '
  81. 'exceeded, considering Due Margin set in account settings.')
  82. risk_total = fields.Monetary(
  83. compute='_compute_risk_exception',
  84. string='Total Risk', help='Sum of total risk included')
  85. risk_exception = fields.Boolean(
  86. compute='_compute_risk_exception',
  87. string='Risk Exception',
  88. help='It Indicate if partner risk exceeded')
  89. credit_policy = fields.Char()
  90. risk_allow_edit = fields.Boolean(compute='_compute_risk_allow_edit')
  91. credit_limit = fields.Float(track_visibility='onchange')
  92. @api.multi
  93. def _compute_risk_allow_edit(self):
  94. is_editable = self.env.user.has_group(
  95. 'base.group_sale_manager') or self.env.user.has_group(
  96. 'account.group_account_manager')
  97. for partner in self.filtered('customer'):
  98. partner.risk_allow_edit = is_editable
  99. @api.multi
  100. @api.depends(
  101. 'customer', 'invoice_ids', 'invoice_ids.state',
  102. 'invoice_ids.amount_total',
  103. 'child_ids.invoice_ids', 'child_ids.invoice_ids.state',
  104. 'child_ids.invoice_ids.amount_total')
  105. def _compute_risk_invoice(self):
  106. all_partners_and_children = {}
  107. all_partner_ids = []
  108. for partner in self.filtered('customer'):
  109. if not partner.id:
  110. continue
  111. all_partners_and_children[partner] = self.with_context(
  112. active_test=False).search([('id', 'child_of', partner.id)]).ids
  113. all_partner_ids += all_partners_and_children[partner]
  114. if not all_partner_ids:
  115. return
  116. total_group = self.env['account.invoice'].sudo().read_group(
  117. [('type', 'in', ['out_invoice', 'out_refund']),
  118. ('state', 'in', ['draft', 'proforma', 'proforma2']),
  119. ('partner_id', 'in', self.ids)],
  120. ['partner_id', 'amount_total'],
  121. ['partner_id'])
  122. for partner, child_ids in all_partners_and_children.items():
  123. partner.risk_invoice_draft = sum(
  124. x['amount_total']
  125. for x in total_group if x['partner_id'][0] in child_ids)
  126. @api.model
  127. def _risk_account_groups(self):
  128. max_date = self._max_risk_date_due()
  129. return {
  130. 'open': {
  131. 'domain': [('reconciled', '=', False),
  132. ('account_id.internal_type', '=', 'receivable'),
  133. ('date_maturity', '>=', max_date)],
  134. 'fields': ['partner_id', 'account_id', 'amount_residual'],
  135. 'group_by': ['partner_id', 'account_id']
  136. },
  137. 'unpaid': {
  138. 'domain': [('reconciled', '=', False),
  139. ('account_id.internal_type', '=', 'receivable'),
  140. ('date_maturity', '<', max_date)],
  141. 'fields': ['partner_id', 'account_id', 'amount_residual'],
  142. 'group_by': ['partner_id', 'account_id']
  143. }
  144. }
  145. @api.multi
  146. @api.depends('move_line_ids.amount_residual',
  147. 'move_line_ids.date_maturity',
  148. 'company_id.invoice_unpaid_margin')
  149. def _compute_risk_account_amount(self):
  150. AccountMoveLine = self.env['account.move.line'].sudo()
  151. customers = self.filtered(lambda x: x.customer and not x.parent_id)
  152. if not customers:
  153. return
  154. groups = self._risk_account_groups()
  155. for key, group in groups.iteritems():
  156. group['read_group'] = AccountMoveLine.read_group(
  157. group['domain'] + [('partner_id', 'in', customers.ids)],
  158. group['fields'],
  159. group['group_by'],
  160. lazy=False,
  161. )
  162. for partner in customers:
  163. partner.update(partner._prepare_risk_account_vals(groups))
  164. @api.multi
  165. def _prepare_risk_account_vals(self, groups):
  166. vals = {
  167. 'risk_invoice_open': 0.0,
  168. 'risk_invoice_unpaid': 0.0,
  169. 'risk_account_amount': 0.0,
  170. 'risk_account_amount_unpaid': 0.0,
  171. }
  172. for reg in groups['open']['read_group']:
  173. if reg['partner_id'][0] != self.id:
  174. continue
  175. if self.property_account_receivable_id.id == reg['account_id'][0]:
  176. vals['risk_invoice_open'] += reg['amount_residual']
  177. else:
  178. vals['risk_account_amount'] += reg['amount_residual']
  179. for reg in groups['unpaid']['read_group']:
  180. if reg['partner_id'][0] != self.id:
  181. continue # pragma: no cover
  182. if self.property_account_receivable_id.id == reg['account_id'][0]:
  183. vals['risk_invoice_unpaid'] += reg['amount_residual']
  184. else:
  185. vals['risk_account_amount_unpaid'] += reg['amount_residual']
  186. return vals
  187. @api.multi
  188. @api.depends(lambda x: x._get_depends_compute_risk_exception())
  189. def _compute_risk_exception(self):
  190. risk_field_list = self._risk_field_list()
  191. for partner in self.filtered('customer'):
  192. amount = 0.0
  193. for risk_field in risk_field_list:
  194. field_value = getattr(partner, risk_field[0], 0.0)
  195. max_value = getattr(partner, risk_field[1], 0.0)
  196. if max_value and field_value > max_value:
  197. partner.risk_exception = True
  198. if getattr(partner, risk_field[2], False):
  199. amount += field_value
  200. partner.risk_total = amount
  201. if partner.credit_limit and amount > partner.credit_limit:
  202. partner.risk_exception = True
  203. @api.model
  204. def _max_risk_date_due(self):
  205. return fields.Date.to_string(datetime.today().date() - relativedelta(
  206. days=self.env.user.company_id.invoice_unpaid_margin))
  207. @api.model
  208. def _risk_field_list(self):
  209. return [
  210. ('risk_invoice_draft', 'risk_invoice_draft_limit',
  211. 'risk_invoice_draft_include'),
  212. ('risk_invoice_open', 'risk_invoice_open_limit',
  213. 'risk_invoice_open_include'),
  214. ('risk_invoice_unpaid', 'risk_invoice_unpaid_limit',
  215. 'risk_invoice_unpaid_include'),
  216. ('risk_account_amount', 'risk_account_amount_limit',
  217. 'risk_account_amount_include'),
  218. ('risk_account_amount_unpaid', 'risk_account_amount_unpaid_limit',
  219. 'risk_account_amount_unpaid_include'),
  220. ]
  221. @api.model
  222. def _get_depends_compute_risk_exception(self):
  223. res = []
  224. for x in self._risk_field_list():
  225. res.extend((x[0], x[1], x[2], 'child_ids.%s' % x[0],
  226. 'child_ids.%s' % x[1], 'child_ids.%s' % x[2]))
  227. res.extend(('credit_limit', 'child_ids.credit_limit'))
  228. return res
  229. @api.model
  230. def process_unpaid_invoices(self):
  231. max_date = self._max_risk_date_due()
  232. ConfigParameter = self.env['ir.config_parameter']
  233. last_check = ConfigParameter.get_param(
  234. 'partner_financial_risk.last_check', default='2016-01-01')
  235. move_lines = self.env['account.move.line'].search([
  236. ('reconciled', '=', False),
  237. ('account_id.internal_type', '=', 'receivable'),
  238. ('date_maturity', '>=', last_check),
  239. ('date_maturity', '<', max_date)])
  240. move_lines.mapped('partner_id')._compute_risk_account_amount()
  241. ConfigParameter.set_param(
  242. 'partner_financial_risk.last_check', max_date)
  243. return True