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.

124 lines
4.0 KiB

  1. # -*- encoding: utf-8 -*-
  2. from openerp import models, fields, api
  3. import datetime as dt
  4. class ProductTemplate(models.Model):
  5. _inherit = "product.template"
  6. consumption_calculation_method = fields.Selection(
  7. selection=[('sales_history', 'Sales History')],
  8. string='Consumption Calculation Method',
  9. default='sales_history',
  10. )
  11. calculation_range = fields.Integer(
  12. 'Calculation range (days)',
  13. default=365, # todo sensible defaults, 14, 28?
  14. )
  15. average_consumption = fields.Float(
  16. string='Average Consumption',
  17. compute='_compute_average_daily_consumption',
  18. readonly=True,
  19. digits=(100, 2),
  20. )
  21. total_consumption = fields.Float(
  22. string='Total Consumption',
  23. default=0,
  24. readonly=True,
  25. digits=(100, 2),
  26. )
  27. estimated_stock_coverage = fields.Float(
  28. string='Estimated Stock Coverage (days)',
  29. compute='_compute_estimated_stock_coverage',
  30. default=0,
  31. digits=(100, 2),
  32. readonly=True,
  33. )
  34. @api.multi
  35. @api.depends('calculation_range')
  36. def _compute_average_daily_consumption(self):
  37. for template in self:
  38. if template.calculation_range > 0:
  39. avg = template.total_consumption / template.calculation_range
  40. else:
  41. avg = 0
  42. template.average_consumption = avg
  43. return True
  44. @api.multi
  45. @api.onchange('calculation_range')
  46. def _compute_total_consumption(self):
  47. for template in self:
  48. products = (
  49. self.env['product.product']
  50. .search([('product_tmpl_id', '=', template.id)]))
  51. today = dt.date.today()
  52. pol_date_limit = (
  53. today - dt.timedelta(days=template.calculation_range))
  54. order_lines = (
  55. self.env['pos.order.line']
  56. .search([
  57. ('product_id', 'in', products.ids),
  58. ('create_date', '>',
  59. fields.Datetime.to_string(pol_date_limit))
  60. ])
  61. )
  62. if order_lines:
  63. order_lines = order_lines.filtered(
  64. lambda oi: oi.order_id.state in ['done', 'invoiced', 'paid']) # noqa
  65. res = sum(order_lines.mapped('qty'))
  66. else:
  67. res = 0
  68. template.total_consumption = res
  69. return True
  70. @api.multi
  71. @api.depends('calculation_range')
  72. def _compute_estimated_stock_coverage(self):
  73. for product_template in self:
  74. qty = product_template.qty_available
  75. avg = product_template.average_consumption
  76. if avg > 0:
  77. product_template.estimated_stock_coverage = qty / avg
  78. else:
  79. # todo what would be a good default value? (not float(inf))
  80. product_template.estimated_stock_coverage = 9999
  81. return True
  82. @api.model
  83. def _batch_compute_total_consumption(self):
  84. products = (
  85. self.env['product.template']
  86. .search([('active', '=', True)])
  87. )
  88. query = """
  89. select
  90. template.id as product_template_id,
  91. sum(pol.qty) as total_consumption
  92. from pos_order_line pol
  93. join pos_order po ON pol.order_id = po.id
  94. join product_product product ON pol.product_id = product.id
  95. join product_template template ON product.product_tmpl_id = template.id
  96. where po.state in ('done', 'invoiced', 'paid')
  97. and template.active
  98. and pol.create_date
  99. BETWEEN date_trunc('day', now()) - calculation_range * interval '1 days'
  100. and date_trunc('day', now())
  101. group by product_template_id
  102. """
  103. self.env.cr.execute(query)
  104. results = {pid: qty for pid, qty in self.env.cr.fetchall()}
  105. for product in products:
  106. product.total_consumption = results.get(product.id, product.total_consumption)