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" ) )