gooderp18绿色标准版
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

169 lignes
7.9KB

  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from collections import defaultdict
  4. from odoo import api, fields, tools, models, _
  5. from odoo.exceptions import UserError, ValidationError
  6. class UoMCategory(models.Model):
  7. _name = 'uom.category'
  8. _description = 'Product UoM Categories'
  9. name = fields.Char('Unit of Measure Category', required=True, translate=True)
  10. class Uom(models.Model):
  11. _inherit = 'uom'
  12. category_id = fields.Many2one(
  13. 'uom.category', 'Category', required=True, ondelete='cascade',
  14. help="Conversion between Units of Measure can only occur if they belong to the same category. The conversion will be made based on the ratios.")
  15. factor = fields.Float(
  16. 'Ratio', default=1.0, digits=0, required=True, # force NUMERIC with unlimited precision
  17. help='How much bigger or smaller this unit is compared to the reference Unit of Measure for this category: 1 * (reference unit) = ratio * (this unit)')
  18. factor_inv = fields.Float(
  19. 'Bigger Ratio', compute='_compute_factor_inv', digits=0, # force NUMERIC with unlimited precision
  20. readonly=True, required=True,
  21. help='How many times this Unit of Measure is bigger than the reference Unit of Measure in this category: 1 * (this unit) = ratio * (reference unit)')
  22. rounding = fields.Float(
  23. 'Rounding Precision', default=0.01, digits=0, required=True,
  24. help="The computed quantity will be a multiple of this value. "
  25. "Use 1.0 for a Unit of Measure that cannot be further split, such as a piece.")
  26. uom_type = fields.Selection([
  27. ('bigger', 'Bigger than the reference Unit of Measure'),
  28. ('reference', 'Reference Unit of Measure for this category'),
  29. ('smaller', 'Smaller than the reference Unit of Measure')], 'Type',
  30. default='reference', required=True)
  31. _sql_constraints = [
  32. ('factor_gt_zero', 'CHECK (factor!=0)', 'The conversion ratio for a unit of measure cannot be 0!'),
  33. ('rounding_gt_zero', 'CHECK (rounding>0)', 'The rounding precision must be strictly positive.'),
  34. ('factor_reference_is_one', "CHECK((uom_type = 'reference' AND factor = 1.0) OR (uom_type != 'reference'))", "The reference unit must have a conversion factor equal to 1.")
  35. ]
  36. @api.depends('factor')
  37. def _compute_factor_inv(self):
  38. for uom in self:
  39. uom.factor_inv = uom.factor and (1.0 / uom.factor) or 0.0
  40. @api.onchange('uom_type')
  41. def _onchange_uom_type(self):
  42. if self.uom_type == 'reference':
  43. self.factor = 1
  44. @api.constrains('category_id', 'uom_type', 'active')
  45. def _check_category_reference_uniqueness(self):
  46. categ_res = self.read_group(
  47. [("category_id", "in", self.category_id.ids)],
  48. ["category_id", "uom_type"],
  49. ["category_id", "uom_type"],
  50. lazy=False,
  51. )
  52. uom_by_category = defaultdict(int)
  53. ref_by_category = {}
  54. for res in categ_res:
  55. uom_by_category[res["category_id"][0]] += res["__count"]
  56. if res["uom_type"] == "reference":
  57. ref_by_category[res["category_id"][0]] = res["__count"]
  58. for category in self.category_id:
  59. reference_count = ref_by_category.get(category.id, 0)
  60. if reference_count > 1:
  61. raise ValidationError(_("UoM category %s should only have one reference unit of measure.", category.name))
  62. elif reference_count == 0 and uom_by_category.get(category.id, 0) > 0:
  63. raise ValidationError(_("UoM category %s should have a reference unit of measure.", category.name))
  64. @api.constrains('category_id')
  65. def _validate_uom_category(self):
  66. for uom in self:
  67. reference_uoms = self.env['uom'].search([
  68. ('category_id', '=', uom.category_id.id),
  69. ('uom_type', '=', 'reference')])
  70. if len(reference_uoms) > 1:
  71. raise ValidationError(_("UoM category %s should only have one reference unit of measure.") % (self.category_id.name))
  72. @api.model_create_multi
  73. def create(self, vals_list):
  74. for values in vals_list:
  75. if 'factor_inv' in values:
  76. factor_inv = values.pop('factor_inv')
  77. values['factor'] = factor_inv and (1.0 / factor_inv) or 0.0
  78. return super(Uom, self).create(vals_list)
  79. def write(self, values):
  80. if 'factor_inv' in values:
  81. factor_inv = values.pop('factor_inv')
  82. values['factor'] = factor_inv and (1.0 / factor_inv) or 0.0
  83. return super(Uom, self).write(values)
  84. def unlink(self):
  85. # UoM with external IDs shouldn't be deleted since they will most probably break the app somewhere else.
  86. # For example, in addons/product/models/product_template.py, cubic meters are used in `_get_volume_uom_id_from_ir_config_parameter()`,
  87. # meters in `_get_length_uom_id_from_ir_config_parameter()`, and so on.
  88. if self.env['ir.model.data'].search_count([('model', '=', self._name), ('res_id', 'in', self.ids)]):
  89. raise UserError(_("You cannot delete this UoM as it is used by the system. You should rather archive it."))
  90. return super(Uom, self).unlink()
  91. @api.model
  92. def name_create(self, name):
  93. """ The UoM category and factor are required, so we'll have to add temporary values
  94. for imported UoMs """
  95. values = {
  96. self._rec_name: name,
  97. 'factor': 1
  98. }
  99. # look for the category based on the english name, i.e. no context on purpose!
  100. # TODO: should find a way to have it translated but not created until actually used
  101. if not self._context.get('default_category_id'):
  102. EnglishUoMCateg = self.env['uom.category'].with_context({})
  103. misc_category = EnglishUoMCateg.search([('name', '=', 'Unsorted/Imported Units')])
  104. if misc_category:
  105. values['category_id'] = misc_category.id
  106. else:
  107. values['category_id'] = EnglishUoMCateg.name_create('Unsorted/Imported Units')[0]
  108. new_uom = self.create(values)
  109. return new_uom.name_get()[0]
  110. def _compute_quantity(self, qty, to_unit, round=True, rounding_method='UP', raise_if_failure=True):
  111. """ Convert the given quantity from the current UoM `self` into a given one
  112. :param qty: the quantity to convert
  113. :param to_unit: the destination UoM record (uom)
  114. :param raise_if_failure: only if the conversion is not possible
  115. - if true, raise an exception if the conversion is not possible (different UoM category),
  116. - otherwise, return the initial quantity
  117. """
  118. if not self or not qty:
  119. return qty
  120. self.ensure_one()
  121. if self != to_unit and self.category_id.id != to_unit.category_id.id:
  122. if raise_if_failure:
  123. raise UserError(_('The unit of measure %s defined on the order line doesn\'t belong to the same category as the unit of measure %s defined on the product. Please correct the unit of measure defined on the order line or on the product, they should belong to the same category.') % (self.name, to_unit.name))
  124. else:
  125. return qty
  126. if self == to_unit:
  127. amount = qty
  128. else:
  129. amount = qty / self.factor
  130. if to_unit:
  131. amount = amount * to_unit.factor
  132. if to_unit and round:
  133. amount = tools.float_round(amount, precision_rounding=to_unit.rounding, rounding_method=rounding_method)
  134. return amount
  135. def _compute_price(self, price, to_unit):
  136. self.ensure_one()
  137. if not self or not price or not to_unit or self == to_unit:
  138. return price
  139. if self.category_id.id != to_unit.category_id.id:
  140. return price
  141. amount = price * self.factor
  142. if to_unit:
  143. amount = amount / to_unit.factor
  144. return amount
上海开阖软件有限公司 沪ICP备12045867号-1