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.

187 lines
6.3 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2011 Raphaël Valyi, Renato Lima, Guewen Baconnier, Sodexis
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import time
  5. from openerp import api, models, fields, _
  6. from openerp.exceptions import UserError
  7. from openerp.tools.safe_eval import safe_eval
  8. class SaleException(models.Model):
  9. _name = 'sale.exception'
  10. _description = "Sale Exceptions"
  11. _order = 'active desc, sequence asc'
  12. name = fields.Char('Exception Name', required=True, translate=True)
  13. description = fields.Text('Description', translate=True)
  14. sequence = fields.Integer(
  15. string='Sequence',
  16. help="Gives the sequence order when applying the test")
  17. model = fields.Selection(
  18. [('sale.order', 'Sale Order'),
  19. ('sale.order.line', 'Sale Order Line')],
  20. string='Apply on', required=True)
  21. active = fields.Boolean('Active')
  22. code = fields.Text(
  23. 'Python Code',
  24. help="Python code executed to check if the exception apply or "
  25. "not. The code must apply block = True to apply the "
  26. "exception.",
  27. default="""
  28. # Python code. Use failed = True to block the sale order.
  29. # You can use the following variables :
  30. # - self: ORM model of the record which is checked
  31. # - order or line: browse_record of the sale order or sale order line
  32. # - object: same as order or line, browse_record of the sale order or
  33. # sale order line
  34. # - pool: ORM model pool (i.e. self.pool)
  35. # - time: Python time module
  36. # - cr: database cursor
  37. # - uid: current user id
  38. # - context: current context
  39. """)
  40. sale_order_ids = fields.Many2many(
  41. 'sale.order',
  42. 'sale_order_exception_rel', 'exception_id', 'sale_order_id',
  43. string='Sale Orders',
  44. readonly=True)
  45. class SaleOrder(models.Model):
  46. _inherit = 'sale.order'
  47. _order = 'main_exception_id asc, date_order desc, name desc'
  48. main_exception_id = fields.Many2one(
  49. 'sale.exception',
  50. compute='_get_main_error',
  51. string='Main Exception',
  52. store=True)
  53. exception_ids = fields.Many2many(
  54. 'sale.exception',
  55. 'sale_order_exception_rel', 'sale_order_id', 'exception_id',
  56. string='Exceptions')
  57. ignore_exceptions = fields.Boolean('Ignore Exceptions', copy=False)
  58. @api.one
  59. @api.depends('state', 'exception_ids')
  60. def _get_main_error(self):
  61. if self.state == 'draft' and self.exception_ids:
  62. self.main_exception_id = self.exception_ids[0]
  63. else:
  64. self.main_exception_id = False
  65. @api.model
  66. def test_all_draft_orders(self):
  67. order_set = self.search([('state', '=', 'draft')])
  68. order_set.test_exceptions()
  69. return True
  70. @api.multi
  71. def _popup_exceptions(self):
  72. action = self.env.ref('sale_exception.action_sale_exception_confirm')
  73. action = action.read()[0]
  74. action.update({
  75. 'context': {
  76. 'active_id': self.ids[0],
  77. 'active_ids': self.ids
  78. }
  79. })
  80. return action
  81. @api.multi
  82. def action_confirm(self):
  83. if self.detect_exceptions():
  84. return self._popup_exceptions()
  85. else:
  86. return super(SaleOrder, self).action_confirm()
  87. @api.multi
  88. def action_cancel(self):
  89. for order in self:
  90. if order.ignore_exceptions:
  91. order.ignore_exceptions = False
  92. return super(SaleOrder, self).action_cancel()
  93. @api.multi
  94. def test_exceptions(self):
  95. """
  96. Condition method for the workflow from draft to confirm
  97. """
  98. if self.detect_exceptions():
  99. return False
  100. return True
  101. @api.multi
  102. def detect_exceptions(self):
  103. """returns the list of exception_ids for all the considered sale orders
  104. as a side effect, the sale order's exception_ids column is updated with
  105. the list of exceptions related to the SO
  106. """
  107. exception_obj = self.env['sale.exception']
  108. order_exceptions = exception_obj.search(
  109. [('model', '=', 'sale.order')])
  110. line_exceptions = exception_obj.search(
  111. [('model', '=', 'sale.order.line')])
  112. all_exception_ids = []
  113. for order in self:
  114. if order.ignore_exceptions:
  115. continue
  116. exception_ids = order._detect_exceptions(order_exceptions,
  117. line_exceptions)
  118. order.exception_ids = [(6, 0, exception_ids)]
  119. all_exception_ids += exception_ids
  120. return all_exception_ids
  121. @api.model
  122. def _exception_rule_eval_context(self, obj_name, rec):
  123. user = self.env['res.users'].browse(self._uid)
  124. return {obj_name: rec,
  125. 'self': self.pool.get(rec._name),
  126. 'object': rec,
  127. 'obj': rec,
  128. 'pool': self.pool,
  129. 'cr': self._cr,
  130. 'uid': self._uid,
  131. 'user': user,
  132. 'time': time,
  133. # copy context to prevent side-effects of eval
  134. 'context': self._context.copy()}
  135. @api.model
  136. def _rule_eval(self, rule, obj_name, rec):
  137. expr = rule.code
  138. space = self._exception_rule_eval_context(obj_name, rec)
  139. try:
  140. safe_eval(expr,
  141. space,
  142. mode='exec',
  143. nocopy=True) # nocopy allows to return 'result'
  144. except Exception, e:
  145. raise UserError(
  146. _('Error when evaluating the sale exception '
  147. 'rule:\n %s \n(%s)') % (rule.name, e))
  148. return space.get('failed', False)
  149. @api.multi
  150. def _detect_exceptions(self, order_exceptions,
  151. line_exceptions):
  152. self.ensure_one()
  153. exception_ids = []
  154. for rule in order_exceptions:
  155. if self._rule_eval(rule, 'order', self):
  156. exception_ids.append(rule.id)
  157. for order_line in self.order_line:
  158. for rule in line_exceptions:
  159. if rule.id in exception_ids:
  160. # we do not matter if the exception as already been
  161. # found for an order line of this order
  162. continue
  163. if self._rule_eval(rule, 'line', order_line):
  164. exception_ids.append(rule.id)
  165. return exception_ids