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.

201 lines
6.9 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, ValidationError
  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_exception = fields.Boolean('Ignore Exceptions', copy=False)
  58. @api.one
  59. @api.depends('exception_ids', 'ignore_exception')
  60. def _get_main_error(self):
  61. if not self.ignore_exception 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.one
  82. @api.constrains('ignore_exception', 'order_line', 'state')
  83. def check_sale_exception_constrains(self):
  84. if self.state == 'sale':
  85. exception_ids = self.detect_exceptions()
  86. if exception_ids:
  87. exceptions = self.env['sale.exception'].browse(exception_ids)
  88. raise ValidationError('\n'.join(exceptions.mapped('name')))
  89. @api.onchange('order_line')
  90. def onchange_ignore_exception(self):
  91. if self.state == 'sale':
  92. self.ignore_exception = False
  93. @api.multi
  94. def action_confirm(self):
  95. if self.detect_exceptions():
  96. return self._popup_exceptions()
  97. else:
  98. return super(SaleOrder, self).action_confirm()
  99. @api.multi
  100. def action_cancel(self):
  101. for order in self:
  102. if order.ignore_exception:
  103. order.ignore_exception = False
  104. return super(SaleOrder, self).action_cancel()
  105. @api.multi
  106. def test_exceptions(self):
  107. """
  108. Condition method for the workflow from draft to confirm
  109. """
  110. if self.detect_exceptions():
  111. return False
  112. return True
  113. @api.multi
  114. def detect_exceptions(self):
  115. """returns the list of exception_ids for all the considered sale orders
  116. as a side effect, the sale order's exception_ids column is updated with
  117. the list of exceptions related to the SO
  118. """
  119. exception_obj = self.env['sale.exception']
  120. order_exceptions = exception_obj.search(
  121. [('model', '=', 'sale.order')])
  122. line_exceptions = exception_obj.search(
  123. [('model', '=', 'sale.order.line')])
  124. all_exception_ids = []
  125. for order in self:
  126. if order.ignore_exception:
  127. continue
  128. exception_ids = order._detect_exceptions(order_exceptions,
  129. line_exceptions)
  130. order.exception_ids = [(6, 0, exception_ids)]
  131. all_exception_ids += exception_ids
  132. return all_exception_ids
  133. @api.model
  134. def _exception_rule_eval_context(self, obj_name, rec):
  135. user = self.env['res.users'].browse(self._uid)
  136. return {obj_name: rec,
  137. 'self': self.pool.get(rec._name),
  138. 'object': rec,
  139. 'obj': rec,
  140. 'pool': self.pool,
  141. 'cr': self._cr,
  142. 'uid': self._uid,
  143. 'user': user,
  144. 'time': time,
  145. # copy context to prevent side-effects of eval
  146. 'context': self._context.copy()}
  147. @api.model
  148. def _rule_eval(self, rule, obj_name, rec):
  149. expr = rule.code
  150. space = self._exception_rule_eval_context(obj_name, rec)
  151. try:
  152. safe_eval(expr,
  153. space,
  154. mode='exec',
  155. nocopy=True) # nocopy allows to return 'result'
  156. except Exception, e:
  157. raise UserError(
  158. _('Error when evaluating the sale exception '
  159. 'rule:\n %s \n(%s)') % (rule.name, e))
  160. return space.get('failed', False)
  161. @api.multi
  162. def _detect_exceptions(self, order_exceptions,
  163. line_exceptions):
  164. self.ensure_one()
  165. exception_ids = []
  166. for rule in order_exceptions:
  167. if self._rule_eval(rule, 'order', self):
  168. exception_ids.append(rule.id)
  169. for order_line in self.order_line:
  170. for rule in line_exceptions:
  171. if rule.id in exception_ids:
  172. # we do not matter if the exception as already been
  173. # found for an order line of this order
  174. continue
  175. if self._rule_eval(rule, 'line', order_line):
  176. exception_ids.append(rule.id)
  177. return exception_ids