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.

196 lines
6.8 KiB

  1. # -*- coding: utf-8 -*-
  2. import logging
  3. from openerp import models, fields, api
  4. from openerp.exceptions import ValidationError
  5. _logger = logging.getLogger(__name__)
  6. class ComputedPurchaseOrderLine(models.Model):
  7. _description = 'Computed Purchase Order Line'
  8. _name = 'computed.purchase.order.line'
  9. computed_purchase_order_id = fields.Many2one(
  10. 'computed.purchase.order',
  11. string='Computed Purchase Order',
  12. )
  13. product_template_id = fields.Many2one(
  14. 'product.template',
  15. string='Linked Product Template',
  16. required=True,
  17. help='Product')
  18. name = fields.Char(
  19. string='Product Name',
  20. related='product_template_id.name',
  21. read_only=True)
  22. supplierinfo_id = fields.Many2one(
  23. 'product.supplierinfo',
  24. string='Supplier information',
  25. compute='_compute_supplierinfo',
  26. store=True,
  27. readonly=True,
  28. )
  29. category_id = fields.Many2one(
  30. 'product.category',
  31. string='Internal Category',
  32. related='product_template_id.categ_id',
  33. read_only=True)
  34. uom_id = fields.Many2one(
  35. 'product.uom',
  36. string='Unit of Measure',
  37. read_only=True,
  38. related='product_template_id.uom_id',
  39. help="Default Unit of Measure used for all stock operation.")
  40. qty_available = fields.Float(
  41. string='Stock Quantity',
  42. related='product_template_id.qty_available',
  43. read_only=True,
  44. help='Quantity currently in stock. Does not take '
  45. 'into account incoming orders.')
  46. virtual_available = fields.Float(
  47. string='Forecast Quantity',
  48. related='product_template_id.virtual_available',
  49. read_only=True,
  50. help='Virtual quantity taking into account current stock, incoming '
  51. 'orders and outgoing sales.')
  52. average_consumption = fields.Float(
  53. string='Average Consumption',
  54. related='product_template_id.average_consumption',
  55. read_only=True)
  56. stock_coverage = fields.Float(
  57. string='Stock Coverage',
  58. related='product_template_id.estimated_stock_coverage',
  59. read_only=True,
  60. )
  61. minimum_purchase_qty = fields.Float(
  62. string='Minimum Purchase Quantity',
  63. compute='_depends_on_product_template',
  64. )
  65. purchase_quantity = fields.Float(
  66. string='Purchase Quantity',
  67. required=True,
  68. default=0.)
  69. uom_po_id = fields.Many2one(
  70. 'product.uom',
  71. string='Purchase Unit of Measure',
  72. read_only=True,
  73. related='product_template_id.uom_po_id',
  74. help="Default Unit of Measure used for all stock operation.")
  75. product_price = fields.Float(
  76. string='Product Price (w/o VAT)',
  77. compute='_depends_on_product_template',
  78. read_only=True,
  79. help='Supplier Product Price by buying unit. Price is without VAT')
  80. virtual_coverage = fields.Float(
  81. string='Expected Stock Coverage',
  82. compute='_depends_on_purchase_quantity',
  83. help='Expected stock coverage (in days) based on current stocks and average daily consumption') # noqa
  84. subtotal = fields.Float(
  85. string='Subtotal (w/o VAT)',
  86. compute='_depends_on_purchase_quantity')
  87. @api.multi
  88. @api.depends('product_template_id')
  89. def _depends_on_product_template(self):
  90. for cpol in self:
  91. # get supplier info
  92. cpol.minimum_purchase_qty = cpol.supplierinfo_id.min_qty
  93. cpol.product_price = cpol.supplierinfo_id.price
  94. @api.multi
  95. @api.onchange('product_template_id')
  96. def _onchange_purchase_quantity(self):
  97. for cpol in self:
  98. cpol.purchase_quantity = cpol.supplierinfo_id.min_qty
  99. @api.depends('purchase_quantity')
  100. @api.multi
  101. def _depends_on_purchase_quantity(self):
  102. for cpol in self:
  103. cpol.subtotal = cpol.product_price * cpol.purchase_quantity
  104. avg = cpol.average_consumption
  105. if avg > 0:
  106. qty = ((cpol.virtual_available / cpol.uom_id.factor)
  107. + (cpol.purchase_quantity / cpol.uom_po_id.factor))
  108. cpol.virtual_coverage = qty / avg
  109. else:
  110. # todo what would be a good default value? (not float(inf))
  111. cpol.virtual_coverage = 9999
  112. return True
  113. @api.multi
  114. @api.depends('product_template_id')
  115. @api.onchange('product_template_id')
  116. def _compute_supplierinfo(self):
  117. for cpol in self:
  118. if not cpol.product_template_id:
  119. cpol.supplierinfo_id = False
  120. else:
  121. SupplierInfo = self.env['product.supplierinfo']
  122. si = SupplierInfo.search([
  123. ('product_tmpl_id', '=', cpol.product_template_id.id),
  124. ('name', '=', cpol.product_template_id.main_supplier_id.id) # noqa
  125. ])
  126. if len(si) == 0:
  127. raise ValidationError(
  128. u'No supplier information set for {name}'
  129. .format(name=cpol.product_template_id.name))
  130. elif len(si) == 1:
  131. cpol.supplierinfo_id = si
  132. else:
  133. _logger.warning(
  134. u'product {name} has several suppliers, chose last'
  135. .format(name=cpol.product_template_id.name)
  136. )
  137. si = si.sorted(key=lambda r: r.create_date, reverse=True)
  138. cpol.supplierinfo_id = si[0]
  139. @api.constrains('purchase_quantity')
  140. def _check_minimum_purchase_quantity(self):
  141. for cpol in self:
  142. if cpol.purchase_quantity < 0:
  143. raise ValidationError(u'Purchase quantity for {product_name} must be greater than 0' # noqa
  144. .format(product_name=cpol.product_template_id.name))
  145. elif 0 < cpol.purchase_quantity < cpol.minimum_purchase_qty:
  146. raise ValidationError(u'Purchase quantity for {product_name} must be greater than {min_qty}' # noqa
  147. .format(product_name=cpol.product_template_id.name,
  148. min_qty=cpol.minimum_purchase_qty))
  149. @api.multi
  150. def get_default_product_product(self):
  151. self.ensure_one()
  152. ProductProduct = self.env['product.product']
  153. products = ProductProduct.search([
  154. ('product_tmpl_id', '=', self.product_template_id.id)
  155. ])
  156. products = products.sorted(
  157. key=lambda product: product.create_date,
  158. reverse=True
  159. )
  160. if products:
  161. return products[0]
  162. else:
  163. raise ValidationError(
  164. u'%s:%s template has no variant set'
  165. % (self.product_template_id.id, self.product_template_id.name)
  166. )