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.

114 lines
3.7 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2015-2016 Yannick Vaucher (Camptocamp SA)
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import logging
  5. from datetime import datetime
  6. from dateutil.relativedelta import relativedelta
  7. from openerp import api, exceptions, fields, models
  8. from openerp.tools.translate import _
  9. _logger = logging.getLogger(__name__)
  10. class RecordLifespan(models.Model):
  11. """ Configure records lifespans per model
  12. After the lifespan is expired (compared to the `write_date` of the
  13. records), the records are deactivated.
  14. """
  15. _name = 'record.lifespan'
  16. _order = 'model'
  17. model_id = fields.Many2one(
  18. 'ir.model',
  19. string='Model',
  20. required=True,
  21. domain=[('has_an_active_field', '=', True)],
  22. )
  23. model = fields.Char(
  24. related='model_id.model',
  25. string='Model Name',
  26. store=True,
  27. )
  28. months = fields.Integer(
  29. required=True,
  30. help="Number of month after which the records will be set to inactive "
  31. "based on their write date"
  32. )
  33. _sql_constraints = [
  34. ('months_gt_0', 'check (months > 0)',
  35. "Months must be a value greater than 0"),
  36. ]
  37. @api.model
  38. def _scheduler_archive_records(self):
  39. lifespans = self.search([])
  40. _logger.info('Records archiver starts archiving records')
  41. for lifespan in lifespans:
  42. try:
  43. lifespan.archive_records()
  44. except exceptions.UserError as e:
  45. _logger.error("Archiver error:\n%s", e[1])
  46. _logger.info('Rusty Records now rest in peace')
  47. return True
  48. @api.multi
  49. def _archive_domain(self, expiration_date):
  50. """ Returns the domain used to find the records to archive.
  51. Can be inherited to change the archived records for a model.
  52. """
  53. model = self.env[self.model_id.model]
  54. domain = [('write_date', '<', expiration_date)]
  55. if 'state' in model._columns:
  56. domain += [('state', 'in', ('done', 'cancel'))]
  57. return domain
  58. @api.multi
  59. def _archive_lifespan_records(self):
  60. """ Archive the records for a lifespan, so for a model.
  61. Can be inherited to customize the archive strategy.
  62. The default strategy is to change the field ``active`` to False
  63. on the records having a ``write_date`` older than the lifespan.
  64. Only done and canceled records will be deactivated.
  65. """
  66. self.ensure_one()
  67. today = datetime.today()
  68. model_name = self.model_id.model
  69. model = self.env[model_name]
  70. if not isinstance(model, models.Model):
  71. raise exceptions.UserError(
  72. _('Model %s not found') % model_name)
  73. if 'active' not in model._columns:
  74. raise exceptions.UserError(
  75. _('Model %s has no active field') % model_name)
  76. delta = relativedelta(months=self.months)
  77. expiration_date = fields.Datetime.to_string(today - delta)
  78. domain = self._archive_domain(expiration_date)
  79. recs = model.search(domain)
  80. if not recs:
  81. return
  82. # use a SQL query to bypass tracking always messages on write for
  83. # object inheriting mail.thread
  84. query = ("UPDATE %s SET active = FALSE WHERE id in %%s"
  85. ) % model._table
  86. self.env.cr.execute(query, (tuple(recs.ids),))
  87. recs.invalidate_cache()
  88. _logger.info(
  89. 'Archived %s %s older than %s',
  90. len(recs.ids), model_name, expiration_date)
  91. @api.multi
  92. def archive_records(self):
  93. """ Call the archiver for several record lifespans """
  94. for lifespan in self:
  95. lifespan._archive_lifespan_records()
  96. return True