279 lines
9.7 KiB
Python
279 lines
9.7 KiB
Python
from odoo import api, fields, models
|
|
|
|
|
|
class McsCompanyGoal(models.Model):
|
|
_name = "mcs.company.goal"
|
|
_description = "Company Goal"
|
|
_inherit = ["mail.thread", "mail.activity.mixin"]
|
|
_order = "sequence, date_deadline, id"
|
|
|
|
name = fields.Char(required=True, tracking=True)
|
|
sequence = fields.Integer(default=10)
|
|
owner_id = fields.Many2one("res.users", string="Owner", default=lambda self: self.env.user)
|
|
date_deadline = fields.Date(string="Deadline", tracking=True)
|
|
state = fields.Selection(
|
|
[
|
|
("planned", "Planned"),
|
|
("active", "Active"),
|
|
("blocked", "Blocked"),
|
|
("done", "Done"),
|
|
("cancelled", "Cancelled"),
|
|
],
|
|
default="active",
|
|
required=True,
|
|
tracking=True,
|
|
)
|
|
priority = fields.Selection(
|
|
[("p0", "P0"), ("p1", "P1"), ("p2", "P2"), ("p3", "P3")],
|
|
default="p1",
|
|
required=True,
|
|
)
|
|
target_amount = fields.Monetary(string="Target Amount")
|
|
currency_id = fields.Many2one(
|
|
"res.currency", default=lambda self: self.env.company.currency_id
|
|
)
|
|
next_action = fields.Char(string="Next Action")
|
|
blocker = fields.Text()
|
|
project_ids = fields.Many2many("project.project", string="Projects / Initiatives")
|
|
opportunity_ids = fields.Many2many("crm.lead", string="Opportunities")
|
|
notes = fields.Html()
|
|
|
|
def action_done(self):
|
|
self.write({"state": "done"})
|
|
|
|
def action_blocked(self):
|
|
self.write({"state": "blocked"})
|
|
|
|
def action_active(self):
|
|
self.write({"state": "active"})
|
|
|
|
|
|
class McsIdeaParking(models.Model):
|
|
_name = "mcs.idea.parking"
|
|
_description = "Idea Parking Lot"
|
|
_inherit = ["mail.thread", "mail.activity.mixin"]
|
|
_order = "review_date, urgency desc, id desc"
|
|
|
|
name = fields.Char(string="Idea", required=True, tracking=True)
|
|
date_added = fields.Date(default=fields.Date.context_today, required=True)
|
|
potential_value = fields.Selection(
|
|
[("low", "Low"), ("medium", "Medium"), ("high", "High"), ("strategic", "Strategic")],
|
|
default="medium",
|
|
)
|
|
urgency = fields.Selection(
|
|
[("low", "Low"), ("medium", "Medium"), ("high", "High"), ("urgent", "Urgent")],
|
|
default="low",
|
|
)
|
|
owner_id = fields.Many2one("res.users", string="Owner", default=lambda self: self.env.user)
|
|
decision = fields.Selection(
|
|
[
|
|
("pending", "Pending"),
|
|
("approve", "Approve"),
|
|
("park", "Park"),
|
|
("kill", "Kill"),
|
|
],
|
|
default="pending",
|
|
required=True,
|
|
tracking=True,
|
|
)
|
|
review_date = fields.Date(string="Review Date")
|
|
project_id = fields.Many2one("project.project", string="Converted Project")
|
|
notes = fields.Html()
|
|
|
|
def action_approve(self):
|
|
self.write({"decision": "approve"})
|
|
|
|
def action_park(self):
|
|
self.write({"decision": "park"})
|
|
|
|
def action_kill(self):
|
|
self.write({"decision": "kill"})
|
|
|
|
|
|
class McsOperatingReview(models.Model):
|
|
_name = "mcs.operating.review"
|
|
_description = "Operating Review"
|
|
_inherit = ["mail.thread", "mail.activity.mixin"]
|
|
_order = "review_date desc, id desc"
|
|
|
|
name = fields.Char(compute="_compute_name", store=True)
|
|
review_type = fields.Selection(
|
|
[
|
|
("daily", "Daily Operating Meeting"),
|
|
("monday", "Monday Commitments"),
|
|
("wednesday", "Wednesday Revenue Review"),
|
|
("friday", "Friday Operating Review"),
|
|
("monthly", "Monthly Operating Review"),
|
|
],
|
|
required=True,
|
|
default="daily",
|
|
tracking=True,
|
|
)
|
|
review_date = fields.Date(default=fields.Date.context_today, required=True)
|
|
owner_id = fields.Many2one("res.users", string="Owner", default=lambda self: self.env.user)
|
|
shipped_yesterday = fields.Html()
|
|
ships_today = fields.Html()
|
|
blockers = fields.Html()
|
|
commitments = fields.Html()
|
|
revenue_notes = fields.Html()
|
|
delivery_notes = fields.Html()
|
|
capacity_notes = fields.Html()
|
|
cash_notes = fields.Html()
|
|
decisions = fields.Html()
|
|
next_actions = fields.Html()
|
|
|
|
@api.depends("review_type", "review_date")
|
|
def _compute_name(self):
|
|
labels = dict(self._fields["review_type"].selection)
|
|
for review in self:
|
|
review.name = "%s - %s" % (
|
|
labels.get(review.review_type, "Operating Review"),
|
|
review.review_date or fields.Date.context_today(review),
|
|
)
|
|
|
|
|
|
class McsSalesTarget(models.Model):
|
|
_name = "mcs.sales.target"
|
|
_description = "Sales Target"
|
|
_inherit = ["mail.thread", "mail.activity.mixin"]
|
|
_order = "target_date desc, user_id"
|
|
|
|
name = fields.Char(compute="_compute_name", store=True)
|
|
target_date = fields.Date(default=fields.Date.context_today, required=True)
|
|
user_id = fields.Many2one("res.users", string="Owner", default=lambda self: self.env.user)
|
|
outbound_target = fields.Integer(default=25)
|
|
outbound_done = fields.Integer()
|
|
followup_target = fields.Integer(default=8)
|
|
followup_done = fields.Integer()
|
|
call_target = fields.Integer(default=3)
|
|
call_done = fields.Integer()
|
|
proposal_target = fields.Integer(default=1)
|
|
proposal_done = fields.Integer()
|
|
referral_target = fields.Integer(default=2)
|
|
referral_done = fields.Integer()
|
|
notes = fields.Html()
|
|
|
|
@api.depends("target_date", "user_id")
|
|
def _compute_name(self):
|
|
for target in self:
|
|
target.name = "%s - %s" % (
|
|
target.user_id.name or "Sales",
|
|
target.target_date or fields.Date.context_today(target),
|
|
)
|
|
|
|
|
|
class McsClientProfitability(models.Model):
|
|
_name = "mcs.client.profitability"
|
|
_description = "Client Profitability"
|
|
_inherit = ["mail.thread", "mail.activity.mixin"]
|
|
_order = "period_start desc, partner_id"
|
|
|
|
name = fields.Char(compute="_compute_name", store=True)
|
|
partner_id = fields.Many2one("res.partner", string="Client", required=True)
|
|
project_id = fields.Many2one("project.project", string="Project")
|
|
period_start = fields.Date(required=True)
|
|
period_end = fields.Date(required=True)
|
|
currency_id = fields.Many2one(
|
|
"res.currency", default=lambda self: self.env.company.currency_id
|
|
)
|
|
client_revenue = fields.Monetary()
|
|
delivery_hours = fields.Float()
|
|
account_management_hours = fields.Float()
|
|
rework_hours = fields.Float()
|
|
revision_hours = fields.Float()
|
|
internal_hourly_cost = fields.Monetary(default=0)
|
|
internal_delivery_cost = fields.Monetary(
|
|
compute="_compute_profitability", store=True
|
|
)
|
|
gross_contribution = fields.Monetary(compute="_compute_profitability", store=True)
|
|
gross_margin = fields.Float(compute="_compute_profitability", store=True)
|
|
recommendation = fields.Selection(
|
|
[
|
|
("keep", "Keep"),
|
|
("reprice", "Reprice"),
|
|
("rescope", "Rescope"),
|
|
("automate", "Automate"),
|
|
("reduce", "Reduce"),
|
|
("exit", "Exit"),
|
|
],
|
|
default="keep",
|
|
tracking=True,
|
|
)
|
|
notes = fields.Html()
|
|
|
|
@api.depends("partner_id", "period_start", "period_end")
|
|
def _compute_name(self):
|
|
for record in self:
|
|
record.name = "%s - %s to %s" % (
|
|
record.partner_id.display_name or "Client",
|
|
record.period_start or "",
|
|
record.period_end or "",
|
|
)
|
|
|
|
@api.depends(
|
|
"delivery_hours",
|
|
"account_management_hours",
|
|
"rework_hours",
|
|
"revision_hours",
|
|
"internal_hourly_cost",
|
|
"client_revenue",
|
|
)
|
|
def _compute_profitability(self):
|
|
for record in self:
|
|
hours = (
|
|
record.delivery_hours
|
|
+ record.account_management_hours
|
|
+ record.rework_hours
|
|
+ record.revision_hours
|
|
)
|
|
record.internal_delivery_cost = hours * record.internal_hourly_cost
|
|
record.gross_contribution = record.client_revenue - record.internal_delivery_cost
|
|
record.gross_margin = (
|
|
record.gross_contribution / record.client_revenue * 100
|
|
if record.client_revenue
|
|
else 0
|
|
)
|
|
|
|
@api.onchange("project_id", "period_start", "period_end")
|
|
def _onchange_project_profitability_source(self):
|
|
for record in self:
|
|
record._apply_project_profitability_values()
|
|
|
|
def action_recompute_from_project(self):
|
|
for record in self:
|
|
record._apply_project_profitability_values()
|
|
return True
|
|
|
|
def _apply_project_profitability_values(self):
|
|
if not self.project_id or not self.period_start or not self.period_end:
|
|
return
|
|
lines = self.env["account.analytic.line"].search(
|
|
[
|
|
("project_id", "=", self.project_id.id),
|
|
("date", ">=", self.period_start),
|
|
("date", "<=", self.period_end),
|
|
]
|
|
)
|
|
self.partner_id = self.project_id.partner_id
|
|
self.client_revenue = (
|
|
self.project_id.mcs_contracted_revenue
|
|
or self.project_id.mcs_monthly_revenue_target
|
|
)
|
|
self.delivery_hours = sum(
|
|
lines.filtered(
|
|
lambda line: line.mcs_work_category
|
|
in ("billable_delivery", "client_support", "qa")
|
|
).mapped("unit_amount")
|
|
)
|
|
self.account_management_hours = sum(
|
|
lines.filtered(
|
|
lambda line: line.mcs_work_category in ("meetings", "admin")
|
|
).mapped("unit_amount")
|
|
)
|
|
self.rework_hours = sum(lines.filtered("mcs_is_rework").mapped("unit_amount"))
|
|
self.revision_hours = sum(
|
|
lines.filtered(lambda line: line.mcs_work_category == "rework").mapped(
|
|
"unit_amount"
|
|
)
|
|
)
|