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.

150 lines
5.3 KiB

  1. # Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
  2. # Copyright 2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
  3. # License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html
  4. from odoo import _, api, fields, models
  5. from odoo.exceptions import UserError, ValidationError
  6. class CustomInfoProperty(models.Model):
  7. """Name of the custom information property."""
  8. _description = "Custom information property"
  9. _name = "custom.info.property"
  10. _order = "template_id, category_sequence, category_id, sequence, id"
  11. _sql_constraints = [
  12. ("name_template",
  13. "UNIQUE (name, template_id)",
  14. "Another property with that name exists for that template."),
  15. ]
  16. name = fields.Char(required=True, translate=True)
  17. sequence = fields.Integer(index=True)
  18. category_id = fields.Many2one(
  19. comodel_name="custom.info.category",
  20. string="Category",
  21. )
  22. category_sequence = fields.Integer(
  23. string="Category Sequence",
  24. related="category_id.sequence",
  25. store=True,
  26. readonly=True,
  27. )
  28. template_id = fields.Many2one(
  29. comodel_name='custom.info.template', string='Template',
  30. required=True, ondelete="cascade",
  31. )
  32. model = fields.Char(
  33. related="template_id.model", readonly=True, auto_join=True,
  34. )
  35. info_value_ids = fields.One2many(
  36. comodel_name="custom.info.value",
  37. inverse_name="property_id",
  38. string="Property Values")
  39. default_value = fields.Char(
  40. translate=True,
  41. help="Will be applied by default to all custom values of this "
  42. "property. This is a char field, so you have to enter some value "
  43. "that can be converted to the field type you choose.",
  44. )
  45. required = fields.Boolean()
  46. minimum = fields.Float(
  47. help="For numeric fields, it means the minimum possible value; "
  48. "for text fields, it means the minimum possible length. "
  49. "If it is bigger than the maximum, then this check is skipped",
  50. )
  51. maximum = fields.Float(
  52. default=-1,
  53. help="For numeric fields, it means the maximum possible value; "
  54. "for text fields, it means the maximum possible length. "
  55. "If it is smaller than the minimum, then this check is skipped",
  56. )
  57. field_type = fields.Selection(
  58. selection=[
  59. ("str", "Text"),
  60. ("int", "Whole number"),
  61. ("float", "Decimal number"),
  62. ("bool", "Yes/No"),
  63. ("date", "Date"),
  64. ("id", "Selection"),
  65. ],
  66. compute="_compute_field_type",
  67. store=True,
  68. )
  69. widget = fields.Selection(
  70. selection=[
  71. ("boolean", "Boolean"),
  72. ("float", "Decimal"),
  73. ("integer", "Integer"),
  74. ("date", "Date"),
  75. ("char", "Single line text"),
  76. ("text", "Multi line Text"),
  77. ("html", "Complex text"),
  78. ("many2one", "Choice"),
  79. ],
  80. default="char",
  81. required=True,
  82. help="Type of information that can be stored in the property.",
  83. )
  84. option_ids = fields.Many2many(
  85. comodel_name="custom.info.option",
  86. string="Options",
  87. help="When the field type is 'selection', choose the available "
  88. "options here.",
  89. )
  90. @api.model
  91. def _get_field_type_map(self):
  92. return {
  93. "boolean": "bool",
  94. "float": "float",
  95. "integer": "int",
  96. "date": "date",
  97. "char": "str",
  98. "text": "str",
  99. "html": "str",
  100. "many2one": "id",
  101. }
  102. @api.depends('widget')
  103. def _compute_field_type(self):
  104. field_type_map = self._get_field_type_map()
  105. for record in self:
  106. record.field_type = field_type_map.get(record.widget, 'str')
  107. @api.multi
  108. def check_access_rule(self, operation):
  109. """You access a property if you access its template."""
  110. self.mapped("template_id").check_access_rule(operation)
  111. return super().check_access_rule(operation)
  112. def _check_default_value_one(self):
  113. if self.default_value:
  114. try:
  115. self.env["custom.info.value"]._transform_value(
  116. self.default_value, self.field_type, self)
  117. except ValueError:
  118. selection = dict(
  119. self._fields["field_type"].get_description(self.env)
  120. ["selection"])
  121. raise ValidationError(
  122. _("Default value %s cannot be converted to type %s.") %
  123. (self.default_value, selection[self.field_type]))
  124. @api.constrains("default_value", "field_type")
  125. def _check_default_value(self):
  126. """Ensure the default value is valid."""
  127. for rec in self:
  128. rec._check_default_value_one()
  129. @api.multi
  130. @api.onchange("required", "field_type")
  131. def _onchange_required_warn(self):
  132. """Warn if the required flag implies a possible weird behavior."""
  133. if self.required:
  134. if self.field_type == "bool":
  135. raise UserError(
  136. _("If you require a Yes/No field, you can only set Yes."))
  137. if self.field_type in {"int", "float"}:
  138. raise UserError(
  139. _("If you require a numeric field, you cannot set it to "
  140. "zero."))