Currently, a service can be configured to be invoiced on milestones. This means that the user has to manually update the delivered quantity on the SO in order to invoice it. We now also have the notion of milestones in the project module. It would be nice to have a link in between the milestone and the corresponding SOL, so that the delivered quantity is automatically updated when the milestone is reached. This would help the users save time, limit the risk of error and reduce the confusion of having unrelated milestones at two different places. To this end, this commit adds a new "milestones" invoice policy that computes the delivered quantity of a SOL based on the milestones that have been reached, and renames the previous "milestones" policy into "manual". Task-2558889 Closes https://github.com/odoo/odoo/pull/81684 closes odoo/odoo#81684 Related: odoo/upgrade#3265 Signed-off-by: Laurent Stukkens (ltu) <ltu@odoo.com>
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import _, api, fields, models
|
|
|
|
class ProjectMilestone(models.Model):
|
|
_name = 'project.milestone'
|
|
_description = "Project Milestone"
|
|
_inherit = ['mail.thread']
|
|
_order = 'deadline, is_reached desc, name'
|
|
|
|
def _get_default_project_id(self):
|
|
return self.env.context.get('default_project_id') or self.env.context.get('active_id')
|
|
|
|
name = fields.Char(required=True)
|
|
project_id = fields.Many2one('project.project', required=True, default=_get_default_project_id)
|
|
deadline = fields.Date(tracking=True, copy=False)
|
|
is_reached = fields.Boolean(string="Reached", default=False, copy=False)
|
|
reached_date = fields.Date(compute='_compute_reached_date', store=True)
|
|
|
|
# computed non-stored fields
|
|
is_deadline_exceeded = fields.Boolean(compute="_compute_is_deadline_exceeded")
|
|
is_deadline_future = fields.Boolean(compute="_compute_is_deadline_future")
|
|
|
|
@api.depends('is_reached')
|
|
def _compute_reached_date(self):
|
|
for ms in self:
|
|
ms.reached_date = ms.is_reached and fields.Date.context_today(self)
|
|
|
|
@api.depends('is_reached', 'deadline')
|
|
def _compute_is_deadline_exceeded(self):
|
|
today = fields.Date.context_today(self)
|
|
for ms in self:
|
|
ms.is_deadline_exceeded = not ms.is_reached and ms.deadline and ms.deadline < today
|
|
|
|
@api.depends('deadline')
|
|
def _compute_is_deadline_future(self):
|
|
for ms in self:
|
|
ms.is_deadline_future = ms.deadline and ms.deadline > fields.Date.context_today(self)
|
|
|
|
def toggle_is_reached(self, is_reached):
|
|
self.ensure_one()
|
|
self.update({'is_reached': is_reached})
|
|
return self._get_data()
|
|
|
|
@api.model
|
|
def _get_fields_to_export(self):
|
|
return ['id', 'name', 'deadline', 'is_reached', 'reached_date', 'is_deadline_exceeded', 'is_deadline_future']
|
|
|
|
def _get_data(self):
|
|
self.ensure_one()
|
|
return {field: self[field] for field in self._get_fields_to_export()}
|
|
|
|
def _get_data_list(self):
|
|
return [ms._get_data() for ms in self]
|
|
|
|
def action_get_list_view(self):
|
|
return {
|
|
'name': _('Milestones'),
|
|
'view_mode': 'tree',
|
|
'res_model': 'project.milestone',
|
|
'views': [(self.env.ref('project.project_milestone_view_tree').id, 'tree'), (False, 'form')],
|
|
'type': 'ir.actions.act_window',
|
|
'domain': "[('project_id', '=', active_id)]",
|
|
'context': {**self.env.context, 'default_project_id': self.env.context.get('active_id')},
|
|
'help': """
|
|
<p class="o_view_nocontent_smiling_face">
|
|
No milestones found. Let's create one!
|
|
</p><p>
|
|
Track major progress points that must be reached to achieve success.
|
|
</p>
|
|
""",
|
|
}
|