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.

126 lines
4.1 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("Calculation range (days)", default=14)
  12. average_consumption = fields.Float(
  13. string="Average Consumption",
  14. compute="_compute_average_daily_consumption",
  15. readonly=True,
  16. digits=(100, 2),
  17. )
  18. total_consumption = fields.Float(
  19. string="Total Consumption",
  20. default=0,
  21. compute="_compute_total_consumption",
  22. store=True,
  23. readonly=True,
  24. digits=(100, 2),
  25. )
  26. estimated_stock_coverage = fields.Float(
  27. string="Estimated Stock Coverage (days)",
  28. compute="_compute_estimated_stock_coverage",
  29. default=0,
  30. digits=(100, 2),
  31. readonly=True,
  32. )
  33. @api.multi
  34. @api.depends("total_consumption")
  35. def _compute_average_daily_consumption(self):
  36. for template in self:
  37. if template.calculation_range > 0:
  38. avg = template.total_consumption / template.calculation_range
  39. else:
  40. avg = 0
  41. template.average_consumption = avg
  42. return True
  43. @api.multi
  44. @api.depends("calculation_range")
  45. def _compute_total_consumption(self):
  46. for template in self:
  47. products = self.env["product.product"].search(
  48. [("product_tmpl_id", "=", template.id)]
  49. )
  50. today = dt.date.today()
  51. pol_date_limit = today - dt.timedelta(
  52. days=template.calculation_range
  53. )
  54. order_lines = self.env["pos.order.line"].search(
  55. [
  56. ("product_id", "in", products.ids),
  57. (
  58. "create_date",
  59. ">",
  60. fields.Datetime.to_string(pol_date_limit),
  61. ), # noqa
  62. ]
  63. )
  64. if order_lines:
  65. order_lines = order_lines.filtered(
  66. lambda ol: ol.order_id.state
  67. in ["done", "invoiced", "paid"]
  68. ) # noqa
  69. template.total_consumption = sum(order_lines.mapped("qty"))
  70. else:
  71. template.total_consumption = 0
  72. return True
  73. @api.multi
  74. @api.depends("total_consumption")
  75. def _compute_estimated_stock_coverage(self):
  76. for product_template in self:
  77. qty = product_template.qty_available
  78. avg = product_template.average_consumption
  79. if avg > 0:
  80. product_template.estimated_stock_coverage = qty / avg
  81. else:
  82. # todo what would be a good default value? (not float(inf))
  83. product_template.estimated_stock_coverage = 9999
  84. return True
  85. @api.model
  86. def _batch_compute_total_consumption(self):
  87. products = self.env["product.template"].search([("active", "=", True)])
  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. """ # noqa
  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(
  107. product.id, product.total_consumption
  108. )