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.

98 lines
3.7 KiB

4 years ago
  1. # Copyright 2020 Coop IT Easy SCRL fs
  2. # Robin Keunen <robin@coopiteasy.be>
  3. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  4. from odoo import _, api, fields, models
  5. from odoo.exceptions import ValidationError
  6. class ProductTemplate(models.Model):
  7. _inherit = "product.template"
  8. @api.multi
  9. @api.constrains("computation_range")
  10. def _check_computation_range(self):
  11. for template in self:
  12. if template.computation_range <= 0:
  13. raise ValidationError(
  14. _("Computation range must be greater than 0.")
  15. )
  16. computation_range = fields.Integer("Computation range (days)", default=14)
  17. range_sales = fields.Float(
  18. string="Sales over Range",
  19. compute="_compute_stock_coverage",
  20. store=True,
  21. )
  22. daily_sales = fields.Float(
  23. string="Daily Sales", compute="_compute_stock_coverage", store=True
  24. )
  25. stock_coverage = fields.Float(
  26. string="Stock Coverage (days)",
  27. compute="_compute_stock_coverage",
  28. store=True,
  29. )
  30. effective_sale_price = fields.Float(
  31. string="Effective Sale Price",
  32. compute="_compute_stock_coverage",
  33. store=True,
  34. help="SUM (total sale price without vat / sold quantity ) "
  35. "/ count (pos order line)",
  36. )
  37. @api.multi
  38. @api.depends("computation_range", "virtual_available", "active")
  39. def _compute_stock_coverage(self):
  40. query = """
  41. select template.id as product_template_id,
  42. sum(pol.qty) as total_sales,
  43. sum(pol.qty) / template.computation_range as daily_sales,
  44. sum(pol.price_subtotal / pol.qty) /
  45. count(pol.id) as effective_sale_price,
  46. sum(pol.price_subtotal_incl / pol.qty) /
  47. count(pol.id) as effective_sale_price_incl
  48. from pos_order_line pol
  49. join pos_order po ON pol.order_id = po.id
  50. join product_product product ON pol.product_id = product.id
  51. join product_template template
  52. ON product.product_tmpl_id = template.id
  53. where po.state in ('done', 'invoiced', 'paid')
  54. and template.active
  55. and po.date_order
  56. BETWEEN now() - template.computation_range * interval '1 days'
  57. and now()
  58. and pol.qty != 0
  59. and template.id in %(template_ids)s
  60. group by product_template_id
  61. """
  62. if self.ids: # on RecordSet
  63. template_ids = tuple(self.ids)
  64. elif self._origin: # on temporary object (on_change)
  65. template_ids = (self._origin.id,)
  66. else: # on temporary object (creation)
  67. return True
  68. self.env.cr.execute(query, {"template_ids": template_ids})
  69. results = {
  70. pid: (qty, avg, esp, espi)
  71. for pid, qty, avg, esp, espi in self.env.cr.fetchall()
  72. }
  73. for template in self:
  74. qty, avg, esp, espi = results.get(template.id, (0, 0, 0, 0))
  75. template.range_sales = qty
  76. template.daily_sales = avg
  77. if any(template.taxes_id.mapped("price_include")):
  78. template.effective_sale_price = espi
  79. else:
  80. template.effective_sale_price = esp
  81. if avg != 0:
  82. template.stock_coverage = template.virtual_available / avg
  83. else:
  84. template.stock_coverage = 9999
  85. @api.model
  86. def cron_compute_stock_coverage(self):
  87. templates = self.env["product.template"].search([])
  88. templates._compute_stock_coverage()