Add Metatroncube operating system addon

This commit is contained in:
metatroncubeswdev 2026-08-30 00:48:25 -04:00
parent 206d6bba68
commit a11795f9da
23 changed files with 1606 additions and 0 deletions

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,35 @@
{
"name": "Metatroncube Operating System",
"version": "17.0.1.0.0",
"category": "Productivity",
"summary": "Revenue, delivery, capacity, and profitability operating system",
"author": "Metatroncube Software Solutions",
"license": "LGPL-3",
"depends": [
"account",
"calendar",
"contacts",
"crm",
"hr",
"hr_timesheet",
"project",
"sale_management",
],
"data": [
"security/security.xml",
"security/ir.model.access.csv",
"data/crm_data.xml",
"data/project_data.xml",
"data/operating_data.xml",
"data/cron_data.xml",
"views/crm_lead_views.xml",
"views/project_views.xml",
"views/timesheet_views.xml",
"views/hr_employee_views.xml",
"views/operating_views.xml",
"views/dashboard_views.xml",
"views/menu.xml",
],
"installable": True,
"application": True,
}

View File

@ -0,0 +1,62 @@
<odoo>
<record id="crm_stage_target" model="crm.stage">
<field name="name">Target</field>
<field name="sequence">10</field>
</record>
<record id="crm_stage_contacted" model="crm.stage">
<field name="name">Contacted</field>
<field name="sequence">20</field>
</record>
<record id="crm_stage_replied" model="crm.stage">
<field name="name">Replied</field>
<field name="sequence">30</field>
</record>
<record id="crm_stage_discovery" model="crm.stage">
<field name="name">Discovery</field>
<field name="sequence">40</field>
</record>
<record id="crm_stage_qualified" model="crm.stage">
<field name="name">Qualified</field>
<field name="sequence">50</field>
</record>
<record id="crm_stage_proposal" model="crm.stage">
<field name="name">Proposal</field>
<field name="sequence">60</field>
</record>
<record id="crm_stage_negotiation" model="crm.stage">
<field name="name">Negotiation</field>
<field name="sequence">70</field>
</record>
<record id="crm_stage_won" model="crm.stage">
<field name="name">Won</field>
<field name="sequence">80</field>
<field name="fold">1</field>
<field name="is_won">1</field>
</record>
<record id="crm_stage_lost" model="crm.stage">
<field name="name">Lost</field>
<field name="sequence">90</field>
<field name="fold">1</field>
</record>
<record id="utm_source_referral" model="utm.source"><field name="name">Referral</field></record>
<record id="utm_source_existing_client" model="utm.source"><field name="name">Existing Client</field></record>
<record id="utm_source_linkedin" model="utm.source"><field name="name">LinkedIn</field></record>
<record id="utm_source_cold_email" model="utm.source"><field name="name">Cold Email</field></record>
<record id="utm_source_cold_call" model="utm.source"><field name="name">Cold Call</field></record>
<record id="utm_source_website" model="utm.source"><field name="name">Website</field></record>
<record id="utm_source_google" model="utm.source"><field name="name">Google</field></record>
<record id="utm_source_facebook" model="utm.source"><field name="name">Facebook</field></record>
<record id="utm_source_instagram" model="utm.source"><field name="name">Instagram</field></record>
<record id="utm_source_networking" model="utm.source"><field name="name">Networking</field></record>
<record id="utm_source_agency_partner" model="utm.source"><field name="name">Agency Partner</field></record>
<record id="utm_source_canada" model="utm.source"><field name="name">Canada</field></record>
<record id="utm_source_india" model="utm.source"><field name="name">India</field></record>
<record id="utm_source_zambia" model="utm.source"><field name="name">Zambia</field></record>
<record id="utm_source_other" model="utm.source"><field name="name">Other</field></record>
<record id="crm_tag_revenue_website_sprint" model="crm.tag"><field name="name">Revenue Website Sprint</field></record>
<record id="crm_tag_growth_retainer" model="crm.tag"><field name="name">Growth Retainer</field></record>
<record id="crm_tag_mvp_launch_sprint" model="crm.tag"><field name="name">MVP Launch Sprint</field></record>
<record id="crm_tag_white_label_agency" model="crm.tag"><field name="name">White-Label Agency Partnership</field></record>
</odoo>

View File

@ -0,0 +1,11 @@
<odoo>
<record id="ir_cron_mcs_schedule_missing_followups" model="ir.cron">
<field name="name">MCS: Schedule Missing CRM Follow-Ups</field>
<field name="model_id" ref="crm.model_crm_lead"/>
<field name="state">code</field>
<field name="code">model._cron_mcs_schedule_missing_followups()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="active">1</field>
</record>
</odoo>

View File

@ -0,0 +1,27 @@
<odoo>
<record id="dashboard_ceo" model="mcs.operating.dashboard">
<field name="name">CEO Dashboard</field>
</record>
<record id="goal_monthly_payroll_gap" model="mcs.company.goal">
<field name="name">Close enough revenue to cover monthly payroll gap</field>
<field name="owner_id" ref="base.user_admin"/>
<field name="priority">p0</field>
<field name="next_action">Set current monthly cash gap and assign close targets.</field>
<field name="project_ids" eval="[(4, ref('project_30_day_revenue_sprint'))]"/>
</record>
<record id="goal_clickstocart_final_release" model="mcs.company.goal">
<field name="name">Complete ClicksToCart final release</field>
<field name="owner_id" ref="base.user_admin"/>
<field name="priority">p0</field>
<field name="next_action">Confirm P0/P1 launch blockers.</field>
<field name="project_ids" eval="[(4, ref('project_clickstocart_final_release'))]"/>
</record>
<record id="goal_hondavert_current_milestone" model="mcs.company.goal">
<field name="name">Complete Hondavert current development milestone</field>
<field name="owner_id" ref="base.user_admin"/>
<field name="priority">p1</field>
<field name="next_action">Confirm scope and assign feature deliverables.</field>
<field name="project_ids" eval="[(4, ref('project_hondavert_current_phase'))]"/>
</record>
</odoo>

View File

@ -0,0 +1,117 @@
<odoo>
<record id="project_stage_planned" model="project.task.type">
<field name="name">Planned</field>
<field name="sequence">10</field>
</record>
<record id="project_stage_ready" model="project.task.type">
<field name="name">Ready</field>
<field name="sequence">20</field>
</record>
<record id="project_stage_in_progress" model="project.task.type">
<field name="name">In Progress</field>
<field name="sequence">30</field>
</record>
<record id="project_stage_review" model="project.task.type">
<field name="name">Review</field>
<field name="sequence">40</field>
</record>
<record id="project_stage_client_review" model="project.task.type">
<field name="name">Client Review</field>
<field name="sequence">50</field>
</record>
<record id="project_stage_blocked" model="project.task.type">
<field name="name">Blocked</field>
<field name="sequence">60</field>
</record>
<record id="project_stage_ready_to_deploy" model="project.task.type">
<field name="name">Ready to Deploy</field>
<field name="sequence">70</field>
</record>
<record id="project_stage_completed" model="project.task.type">
<field name="name">Completed</field>
<field name="sequence">80</field>
<field name="fold">1</field>
</record>
<record id="project_tag_frontend" model="project.tags"><field name="name">Frontend</field></record>
<record id="project_tag_backend" model="project.tags"><field name="name">Backend</field></record>
<record id="project_tag_mobile" model="project.tags"><field name="name">Mobile</field></record>
<record id="project_tag_api" model="project.tags"><field name="name">API</field></record>
<record id="project_tag_qa" model="project.tags"><field name="name">QA</field></record>
<record id="project_tag_deployment" model="project.tags"><field name="name">Deployment</field></record>
<record id="project_tag_client_feedback" model="project.tags"><field name="name">Client Feedback</field></record>
<record id="project_tag_documentation" model="project.tags"><field name="name">Documentation</field></record>
<record id="project_30_day_revenue_sprint" model="project.project">
<field name="name">30-Day Revenue Sprint</field>
<field name="user_id" ref="base.user_admin"/>
<field name="allow_timesheets">1</field>
<field name="allow_milestones">1</field>
<field name="mcs_initiative_type">sales</field>
<field name="mcs_classification">active</field>
<field name="mcs_revenue_connected">1</field>
<field name="mcs_next_action">Build current prospect list and assign follow-ups.</field>
</record>
<record id="milestone_revenue_week_1" model="project.milestone">
<field name="name">Week 1 - Pipeline Build</field>
<field name="project_id" ref="project_30_day_revenue_sprint"/>
</record>
<record id="milestone_revenue_week_2" model="project.milestone">
<field name="name">Week 2 - Conversations</field>
<field name="project_id" ref="project_30_day_revenue_sprint"/>
</record>
<record id="milestone_revenue_week_3" model="project.milestone">
<field name="name">Week 3 - Proposal + Follow-Up</field>
<field name="project_id" ref="project_30_day_revenue_sprint"/>
</record>
<record id="milestone_revenue_week_4" model="project.milestone">
<field name="name">Week 4 - Close + Collect</field>
<field name="project_id" ref="project_30_day_revenue_sprint"/>
</record>
<record id="project_clickstocart_final_release" model="project.project">
<field name="name">ClicksToCart - Final Release</field>
<field name="user_id" ref="base.user_admin"/>
<field name="allow_timesheets">1</field>
<field name="allow_milestones">1</field>
<field name="mcs_initiative_type">client</field>
<field name="mcs_classification">finish_now</field>
<field name="mcs_revenue_connected">1</field>
<field name="mcs_next_action">Lock final release scope and confirm launch blockers.</field>
</record>
<record id="milestone_ctc_scope_locked" model="project.milestone"><field name="name">Release Scope Locked</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="milestone_ctc_bugs_resolved" model="project.milestone"><field name="name">P0/P1 Bugs Resolved</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="milestone_ctc_qa_completed" model="project.milestone"><field name="name">QA Completed</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="milestone_ctc_uat" model="project.milestone"><field name="name">UAT / Client Approval</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="milestone_ctc_deploy" model="project.milestone"><field name="name">Production Deployment</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="milestone_ctc_monitoring" model="project.milestone"><field name="name">Post-Launch Monitoring</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="milestone_ctc_closed" model="project.milestone"><field name="name">Project Closed</field><field name="project_id" ref="project_clickstocart_final_release"/></record>
<record id="project_hondavert_current_phase" model="project.project">
<field name="name">Hondavert - Current Development Phase</field>
<field name="user_id" ref="base.user_admin"/>
<field name="allow_timesheets">1</field>
<field name="allow_milestones">1</field>
<field name="mcs_initiative_type">client</field>
<field name="mcs_classification">finish_now</field>
<field name="mcs_revenue_connected">1</field>
<field name="mcs_next_action">Confirm current scope and assign deliverable-based tasks.</field>
</record>
<record id="milestone_hondavert_scope" model="project.milestone"><field name="name">Current Scope Confirmed</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_core_dev" model="project.milestone"><field name="name">Core Development</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_internal_qa" model="project.milestone"><field name="name">Internal QA</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_client_review" model="project.milestone"><field name="name">Client Review</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_fixes" model="project.milestone"><field name="name">Fixes</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_staging" model="project.milestone"><field name="name">Staging Approval</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_deploy" model="project.milestone"><field name="name">Production Deployment</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="milestone_hondavert_closed" model="project.milestone"><field name="name">Phase Closed</field><field name="project_id" ref="project_hondavert_current_phase"/></record>
<record id="project_idea_parking_lot" model="project.project">
<field name="name">Idea Parking Lot</field>
<field name="user_id" ref="base.user_admin"/>
<field name="allow_timesheets">0</field>
<field name="mcs_initiative_type">parking_lot</field>
<field name="mcs_classification">parked</field>
<field name="mcs_next_action">Review parked ideas twice per week.</field>
</record>
</odoo>

View File

@ -0,0 +1,7 @@
from . import crm_lead
from . import dashboard
from . import hr_employee
from . import operating
from . import project
from . import sale_order
from . import timesheet

View File

@ -0,0 +1,110 @@
from odoo import api, fields, models
class CrmLead(models.Model):
_inherit = "crm.lead"
mcs_service_offer = fields.Selection(
[
("revenue_website_sprint", "Revenue Website Sprint"),
("growth_retainer", "Growth Retainer"),
("mvp_launch_sprint", "MVP Launch Sprint"),
("white_label_agency", "White-Label Agency Partnership"),
("other", "Other"),
],
string="Service Interest",
tracking=True,
)
mcs_expected_monthly_revenue = fields.Monetary(
string="Expected Monthly Revenue", tracking=True
)
mcs_one_time_project_value = fields.Monetary(
string="One-Time Project Value", tracking=True
)
mcs_proposal_sent_date = fields.Date(string="Proposal Sent Date", tracking=True)
mcs_last_contact_date = fields.Date(string="Last Contact Date", tracking=True)
mcs_followup_date = fields.Date(string="Follow-Up Date", tracking=True)
mcs_next_action = fields.Char(string="Next Action", tracking=True)
mcs_revenue_priority = fields.Selection(
[
("existing_upsell", "Existing Client Upsell"),
("existing_referral", "Existing Client Referral"),
("canadian_client", "Canadian Client"),
("canadian_agency", "Canadian Agency Partnership"),
("warm_network", "Warm Network"),
("high_value_outbound", "High-Value Outbound"),
("india", "India Opportunity"),
("low_ticket", "Smaller Low-Ticket Retainer"),
],
string="Revenue Priority",
tracking=True,
)
mcs_has_next_activity = fields.Boolean(
string="Has Next Activity", compute="_compute_mcs_operating_flags", store=True
)
mcs_missing_operating_data = fields.Boolean(
string="Missing Operating Data", compute="_compute_mcs_operating_flags", store=True
)
mcs_operating_notes = fields.Text(string="Operating Notes")
@api.depends(
"activity_ids",
"mcs_next_action",
"mcs_followup_date",
"date_deadline",
"expected_revenue",
"mcs_expected_monthly_revenue",
"mcs_one_time_project_value",
"user_id",
"type",
"active",
"probability",
)
def _compute_mcs_operating_flags(self):
for lead in self:
is_open_opportunity = (
lead.active and lead.type == "opportunity" and lead.probability < 100
)
lead.mcs_has_next_activity = bool(
lead.activity_ids or lead.mcs_next_action or lead.mcs_followup_date
)
has_value = bool(
lead.expected_revenue
or lead.mcs_expected_monthly_revenue
or lead.mcs_one_time_project_value
)
lead.mcs_missing_operating_data = bool(
is_open_opportunity
and (
not lead.user_id
or not lead.date_deadline
or not lead.mcs_has_next_activity
or not has_value
)
)
def action_mcs_schedule_followup(self):
todo = self.env.ref("mail.mail_activity_data_todo")
today = fields.Date.context_today(self)
for lead in self:
if lead.activity_ids:
continue
lead.activity_schedule(
todo.id,
date_deadline=lead.mcs_followup_date or lead.date_deadline or today,
summary=lead.mcs_next_action or "Follow up",
user_id=lead.user_id.id or self.env.user.id,
)
return True
def _cron_mcs_schedule_missing_followups(self):
leads = self.search(
[
("type", "=", "opportunity"),
("active", "=", True),
("probability", "<", 100),
("activity_ids", "=", False),
],
limit=100,
)
leads.action_mcs_schedule_followup()

View File

@ -0,0 +1,174 @@
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
class McsOperatingDashboard(models.Model):
_name = "mcs.operating.dashboard"
_description = "Operating Dashboard"
name = fields.Char(default="CEO Dashboard", required=True)
currency_id = fields.Many2one(
"res.currency", default=lambda self: self.env.company.currency_id
)
month_start = fields.Date(compute="_compute_metrics")
month_end = fields.Date(compute="_compute_metrics")
monthly_revenue_target = fields.Monetary(compute="_compute_metrics")
revenue_closed_month = fields.Monetary(compute="_compute_metrics")
monthly_cash_gap = fields.Monetary(compute="_compute_metrics")
new_mrr_won = fields.Monetary(compute="_compute_metrics")
pipeline_value = fields.Monetary(compute="_compute_metrics")
weighted_pipeline = fields.Monetary(compute="_compute_metrics")
proposals_outstanding = fields.Integer(compute="_compute_metrics")
deals_expected_month = fields.Integer(compute="_compute_metrics")
cash_collected = fields.Monetary(compute="_compute_metrics")
invoices_overdue = fields.Integer(compute="_compute_metrics")
active_clients = fields.Integer(compute="_compute_metrics")
opportunities_created = fields.Integer(compute="_compute_metrics")
opportunities_won = fields.Integer(compute="_compute_metrics")
conversion_rate = fields.Float(compute="_compute_metrics")
followups_due_today = fields.Integer(compute="_compute_metrics")
projects_at_risk = fields.Integer(compute="_compute_metrics")
blockers = fields.Integer(compute="_compute_metrics")
overdue_tasks = fields.Integer(compute="_compute_metrics")
team_capacity_hours = fields.Float(compute="_compute_metrics")
planned_hours = fields.Float(compute="_compute_metrics")
actual_hours = fields.Float(compute="_compute_metrics")
billable_hours = fields.Float(compute="_compute_metrics")
non_billable_hours = fields.Float(compute="_compute_metrics")
utilization = fields.Float(compute="_compute_metrics")
@api.depends_context("uid")
def _compute_metrics(self):
today = fields.Date.context_today(self)
month_start = today.replace(day=1)
month_end = month_start + relativedelta(months=1, days=-1)
Lead = self.env["crm.lead"]
Project = self.env["project.project"]
Task = self.env["project.task"]
Timesheet = self.env["account.analytic.line"]
Invoice = self.env["account.move"]
Employee = self.env["hr.employee"]
open_pipeline = Lead.search(
[("type", "=", "opportunity"), ("active", "=", True), ("probability", "<", 100)]
)
won_month = Lead.search(
[
("type", "=", "opportunity"),
("active", "=", True),
("probability", "=", 100),
("date_closed", ">=", month_start),
("date_closed", "<=", month_end),
]
)
invoices_month = Invoice.search(
[
("move_type", "=", "out_invoice"),
("state", "=", "posted"),
("invoice_date", ">=", month_start),
("invoice_date", "<=", month_end),
]
)
overdue_invoices = Invoice.search(
[
("move_type", "=", "out_invoice"),
("state", "=", "posted"),
("payment_state", "not in", ["paid", "in_payment", "reversed"]),
("invoice_date_due", "<", today),
]
)
timesheets_month = Timesheet.search(
[("date", ">=", month_start), ("date", "<=", month_end)]
)
active_projects = Project.search([("active", "=", True)])
active_tasks = Task.search([("active", "=", True)])
employees = Employee.search([("active", "=", True)])
for dashboard in self:
dashboard.month_start = month_start
dashboard.month_end = month_end
dashboard.monthly_revenue_target = sum(
active_projects.mapped("mcs_monthly_revenue_target")
)
dashboard.revenue_closed_month = sum(won_month.mapped("expected_revenue"))
dashboard.monthly_cash_gap = (
dashboard.monthly_revenue_target - dashboard.revenue_closed_month
)
dashboard.new_mrr_won = sum(won_month.mapped("mcs_expected_monthly_revenue"))
dashboard.pipeline_value = sum(open_pipeline.mapped("expected_revenue"))
dashboard.weighted_pipeline = sum(
lead.expected_revenue * lead.probability / 100 for lead in open_pipeline
)
dashboard.proposals_outstanding = Lead.search_count(
[
("type", "=", "opportunity"),
("active", "=", True),
("mcs_proposal_sent_date", "!=", False),
("probability", "<", 100),
]
)
dashboard.deals_expected_month = Lead.search_count(
[
("type", "=", "opportunity"),
("active", "=", True),
("date_deadline", ">=", month_start),
("date_deadline", "<=", month_end),
("probability", "<", 100),
]
)
dashboard.cash_collected = sum(
invoice.amount_total - invoice.amount_residual for invoice in invoices_month
)
dashboard.invoices_overdue = len(overdue_invoices)
dashboard.active_clients = len(set(active_projects.mapped("partner_id").ids))
created = Lead.search_count(
[
("type", "=", "opportunity"),
("create_date", ">=", month_start),
("create_date", "<=", month_end),
]
)
dashboard.opportunities_created = created
dashboard.opportunities_won = len(won_month)
dashboard.conversion_rate = len(won_month) / created * 100 if created else 0
dashboard.followups_due_today = self.env["mail.activity"].search_count(
[
("user_id", "=", self.env.user.id),
("date_deadline", "<=", today),
("res_model", "=", "crm.lead"),
]
)
dashboard.projects_at_risk = len(active_projects.filtered("mcs_at_risk"))
dashboard.blockers = len(active_tasks.filtered("mcs_is_blocked"))
dashboard.overdue_tasks = len(
active_tasks.filtered(
lambda task: task.date_deadline
and fields.Date.to_date(task.date_deadline) < today
and not (task.stage_id and task.stage_id.fold)
)
)
dashboard.team_capacity_hours = sum(employees.mapped("mcs_available_hours_week"))
dashboard.planned_hours = sum(active_tasks.mapped("allocated_hours"))
dashboard.actual_hours = sum(timesheets_month.mapped("unit_amount"))
dashboard.billable_hours = sum(
timesheets_month.filtered(
lambda line: line.mcs_billable_classification == "billable"
).mapped("unit_amount")
)
dashboard.non_billable_hours = dashboard.actual_hours - dashboard.billable_hours
dashboard.utilization = (
dashboard.billable_hours / dashboard.team_capacity_hours * 100
if dashboard.team_capacity_hours
else 0
)
def action_refresh(self):
return {
"type": "ir.actions.act_window",
"name": "CEO Dashboard",
"res_model": "mcs.operating.dashboard",
"view_mode": "form",
"target": "current",
"context": {"create": False},
}

View File

@ -0,0 +1,20 @@
from odoo import fields, models
class HrEmployee(models.Model):
_inherit = "hr.employee"
mcs_operating_role = fields.Selection(
[
("founder", "Founder"),
("senior_developer", "Senior Developer"),
("junior_developer", "Junior Developer"),
("designer", "Graphic Designer"),
("video_editor", "Video Editor"),
("seo_specialist", "SEO Specialist"),
("ads_specialist", "Ads Specialist"),
("operations", "Operations"),
],
string="Operating Role",
)
mcs_available_hours_week = fields.Float(string="Available Hours / Week", default=40)

View File

@ -0,0 +1,235 @@
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
)

View File

@ -0,0 +1,146 @@
from odoo import api, fields, models
WORK_CATEGORY_SELECTION = [
("billable_delivery", "Billable Delivery"),
("internal_product", "Internal Product"),
("sales_support", "Sales Support"),
("rework", "Rework"),
("client_support", "Client Support"),
("admin", "Admin"),
("meetings", "Meetings"),
("qa", "QA"),
("research", "Research"),
]
class ProjectProject(models.Model):
_inherit = "project.project"
mcs_initiative_type = fields.Selection(
[
("client", "Client Engagement"),
("internal", "Internal Initiative"),
("product", "Product Development"),
("sales", "Sales Execution"),
("parking_lot", "Idea Parking Lot"),
],
string="Initiative Type",
tracking=True,
)
mcs_classification = fields.Selection(
[
("finish_now", "Finish Now"),
("active", "Active"),
("parked", "Parked"),
("kill", "Kill"),
],
string="Operating Classification",
default="active",
tracking=True,
)
mcs_revenue_connected = fields.Boolean(string="Connected to Revenue", tracking=True)
mcs_monthly_revenue_target = fields.Monetary(string="Monthly Revenue Target")
mcs_contracted_revenue = fields.Monetary(string="Contracted Revenue")
mcs_cash_gap = fields.Monetary(string="Cash Gap", compute="_compute_mcs_cash_gap")
mcs_planned_hours = fields.Float(
string="Planned Hours", compute="_compute_mcs_project_hours", store=True
)
mcs_actual_hours = fields.Float(
string="Actual Hours", compute="_compute_mcs_project_hours", store=True
)
mcs_utilization = fields.Float(
string="Planned vs Actual %", compute="_compute_mcs_project_hours", store=True
)
mcs_next_action = fields.Char(string="Next Action", tracking=True)
mcs_blocker = fields.Text(string="Blocker", tracking=True)
mcs_at_risk = fields.Boolean(
string="At Risk", compute="_compute_mcs_at_risk", store=True
)
@api.depends("mcs_monthly_revenue_target", "mcs_contracted_revenue")
def _compute_mcs_cash_gap(self):
for project in self:
project.mcs_cash_gap = (
project.mcs_monthly_revenue_target - project.mcs_contracted_revenue
)
@api.depends("tasks.allocated_hours", "tasks.effective_hours")
def _compute_mcs_project_hours(self):
for project in self:
planned = sum(project.tasks.mapped("allocated_hours"))
actual = sum(project.tasks.mapped("effective_hours"))
project.mcs_planned_hours = planned
project.mcs_actual_hours = actual
project.mcs_utilization = actual / planned * 100 if planned else 0
@api.depends("tasks.mcs_at_risk", "mcs_blocker", "mcs_next_action")
def _compute_mcs_at_risk(self):
for project in self:
project.mcs_at_risk = bool(
project.mcs_blocker or not project.mcs_next_action or any(project.tasks.mapped("mcs_at_risk"))
)
class ProjectTask(models.Model):
_inherit = "project.task"
mcs_priority = fields.Selection(
[
("p0", "P0 - Critical"),
("p1", "P1 - High"),
("p2", "P2 - Normal"),
("p3", "P3 - Low"),
],
string="Operating Priority",
default="p2",
tracking=True,
)
mcs_definition_of_done = fields.Html(string="Definition of Done")
mcs_next_action = fields.Char(string="Next Action", tracking=True)
mcs_blocker = fields.Text(string="Blocker", tracking=True)
mcs_client_internal = fields.Selection(
[("client", "Client"), ("internal", "Internal")],
string="Client / Internal",
default="client",
tracking=True,
)
mcs_work_category = fields.Selection(WORK_CATEGORY_SELECTION, string="Work Category")
mcs_is_blocked = fields.Boolean(string="Blocked", compute="_compute_mcs_flags", store=True)
mcs_at_risk = fields.Boolean(string="At Risk", compute="_compute_mcs_flags", store=True)
mcs_missing_operating_data = fields.Boolean(
string="Missing Operating Data", compute="_compute_mcs_flags", store=True
)
@api.depends(
"user_ids",
"date_deadline",
"allocated_hours",
"mcs_definition_of_done",
"mcs_next_action",
"mcs_blocker",
"depend_on_ids",
"stage_id.fold",
)
def _compute_mcs_flags(self):
today = fields.Date.context_today(self)
for task in self:
is_done = bool(task.stage_id and task.stage_id.fold)
has_deadline_passed = bool(
task.date_deadline and fields.Date.to_date(task.date_deadline) < today
)
task.mcs_is_blocked = bool(task.mcs_blocker or task.depend_on_ids)
task.mcs_missing_operating_data = bool(
not is_done
and (
not task.user_ids
or not task.date_deadline
or not task.allocated_hours
or not task.mcs_definition_of_done
or not task.mcs_next_action
)
)
task.mcs_at_risk = bool(
not is_done
and (task.mcs_is_blocked or has_deadline_passed or task.mcs_missing_operating_data)
)

View File

@ -0,0 +1,12 @@
from odoo import fields, models
class SaleOrder(models.Model):
_inherit = "sale.order"
mcs_service_offer = fields.Selection(
related="opportunity_id.mcs_service_offer", string="Service Offer", store=True
)
mcs_revenue_priority = fields.Selection(
related="opportunity_id.mcs_revenue_priority", string="Revenue Priority", store=True
)

View File

@ -0,0 +1,19 @@
from odoo import fields, models
from .project import WORK_CATEGORY_SELECTION
class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"
mcs_work_category = fields.Selection(WORK_CATEGORY_SELECTION, string="Work Category")
mcs_billable_classification = fields.Selection(
[
("billable", "Billable"),
("non_billable", "Non-Billable"),
("internal", "Internal"),
],
string="Billable Classification",
default="billable",
)
mcs_is_rework = fields.Boolean(string="Rework")

View File

@ -0,0 +1,12 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mcs_company_goal_user,mcs.company.goal.user,model_mcs_company_goal,mcs_operating_system.group_mcs_operating_user,1,1,1,0
access_mcs_company_goal_manager,mcs.company.goal.manager,model_mcs_company_goal,mcs_operating_system.group_mcs_operating_manager,1,1,1,1
access_mcs_idea_parking_user,mcs.idea.parking.user,model_mcs_idea_parking,mcs_operating_system.group_mcs_operating_user,1,1,1,0
access_mcs_idea_parking_manager,mcs.idea.parking.manager,model_mcs_idea_parking,mcs_operating_system.group_mcs_operating_manager,1,1,1,1
access_mcs_operating_review_user,mcs.operating.review.user,model_mcs_operating_review,mcs_operating_system.group_mcs_operating_user,1,1,1,0
access_mcs_operating_review_manager,mcs.operating.review.manager,model_mcs_operating_review,mcs_operating_system.group_mcs_operating_manager,1,1,1,1
access_mcs_sales_target_user,mcs.sales.target.user,model_mcs_sales_target,mcs_operating_system.group_mcs_operating_user,1,1,1,0
access_mcs_sales_target_manager,mcs.sales.target.manager,model_mcs_sales_target,mcs_operating_system.group_mcs_operating_manager,1,1,1,1
access_mcs_client_profitability_user,mcs.client.profitability.user,model_mcs_client_profitability,mcs_operating_system.group_mcs_operating_user,1,1,1,0
access_mcs_client_profitability_manager,mcs.client.profitability.manager,model_mcs_client_profitability,mcs_operating_system.group_mcs_operating_manager,1,1,1,1
access_mcs_operating_dashboard_user,mcs.operating.dashboard.user,model_mcs_operating_dashboard,mcs_operating_system.group_mcs_operating_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mcs_company_goal_user mcs.company.goal.user model_mcs_company_goal mcs_operating_system.group_mcs_operating_user 1 1 1 0
3 access_mcs_company_goal_manager mcs.company.goal.manager model_mcs_company_goal mcs_operating_system.group_mcs_operating_manager 1 1 1 1
4 access_mcs_idea_parking_user mcs.idea.parking.user model_mcs_idea_parking mcs_operating_system.group_mcs_operating_user 1 1 1 0
5 access_mcs_idea_parking_manager mcs.idea.parking.manager model_mcs_idea_parking mcs_operating_system.group_mcs_operating_manager 1 1 1 1
6 access_mcs_operating_review_user mcs.operating.review.user model_mcs_operating_review mcs_operating_system.group_mcs_operating_user 1 1 1 0
7 access_mcs_operating_review_manager mcs.operating.review.manager model_mcs_operating_review mcs_operating_system.group_mcs_operating_manager 1 1 1 1
8 access_mcs_sales_target_user mcs.sales.target.user model_mcs_sales_target mcs_operating_system.group_mcs_operating_user 1 1 1 0
9 access_mcs_sales_target_manager mcs.sales.target.manager model_mcs_sales_target mcs_operating_system.group_mcs_operating_manager 1 1 1 1
10 access_mcs_client_profitability_user mcs.client.profitability.user model_mcs_client_profitability mcs_operating_system.group_mcs_operating_user 1 1 1 0
11 access_mcs_client_profitability_manager mcs.client.profitability.manager model_mcs_client_profitability mcs_operating_system.group_mcs_operating_manager 1 1 1 1
12 access_mcs_operating_dashboard_user mcs.operating.dashboard.user model_mcs_operating_dashboard mcs_operating_system.group_mcs_operating_user 1 0 0 0

View File

@ -0,0 +1,12 @@
<odoo>
<record id="group_mcs_operating_user" model="res.groups">
<field name="name">Operating System User</field>
<field name="category_id" ref="base.module_category_productivity"/>
</record>
<record id="group_mcs_operating_manager" model="res.groups">
<field name="name">Operating System Manager</field>
<field name="category_id" ref="base.module_category_productivity"/>
<field name="implied_ids" eval="[(4, ref('group_mcs_operating_user'))]"/>
</record>
</odoo>

View File

@ -0,0 +1,59 @@
<odoo>
<record id="view_crm_lead_form_mcs_operating" model="ir.ui.view">
<field name="name">crm.lead.form.mcs.operating</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_lead_view_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="action_mcs_schedule_followup" type="object" string="Schedule Follow-Up" invisible="type != 'opportunity' or activity_ids"/>
</xpath>
<xpath expr="//notebook" position="inside">
<page string="Operating System" name="mcs_operating_system">
<group>
<group string="Commercial">
<field name="mcs_service_offer"/>
<field name="mcs_revenue_priority"/>
<field name="mcs_expected_monthly_revenue"/>
<field name="mcs_one_time_project_value"/>
</group>
<group string="Execution">
<field name="mcs_next_action"/>
<field name="mcs_followup_date"/>
<field name="mcs_last_contact_date"/>
<field name="mcs_proposal_sent_date"/>
<field name="mcs_has_next_activity"/>
<field name="mcs_missing_operating_data"/>
</group>
</group>
<field name="mcs_operating_notes" placeholder="Revenue notes, blockers, buyer context, and close plan."/>
</page>
</xpath>
</field>
</record>
<record id="view_crm_case_tree_mcs_operating" model="ir.ui.view">
<field name="name">crm.lead.tree.mcs.operating</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_tree_view_oppor"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="decoration-danger">mcs_missing_operating_data</attribute>
</xpath>
<xpath expr="//field[@name='expected_revenue']" position="after">
<field name="mcs_expected_monthly_revenue" optional="show"/>
<field name="mcs_service_offer" optional="show"/>
<field name="mcs_next_action" optional="show"/>
<field name="mcs_followup_date" optional="show"/>
<field name="mcs_missing_operating_data" optional="show"/>
</xpath>
</field>
</record>
<record id="action_mcs_revenue_board" model="ir.actions.act_window">
<field name="name">Revenue Board</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">kanban,tree,form,calendar,pivot,graph</field>
<field name="domain">[('type','=','opportunity')]</field>
<field name="context">{'default_type': 'opportunity', 'search_default_assigned_to_me': 1}</field>
</record>
</odoo>

View File

@ -0,0 +1,67 @@
<odoo>
<record id="view_mcs_operating_dashboard_form" model="ir.ui.view">
<field name="name">mcs.operating.dashboard.form</field>
<field name="model">mcs.operating.dashboard</field>
<field name="arch" type="xml">
<form create="0" edit="0" delete="0">
<header>
<button name="action_refresh" type="object" string="Refresh" class="btn-primary"/>
</header>
<sheet>
<group>
<field name="name"/>
</group>
<group>
<field name="month_start"/>
<field name="month_end"/>
<field name="currency_id" invisible="1"/>
</group>
<group string="Revenue">
<group>
<field name="monthly_revenue_target"/>
<field name="revenue_closed_month"/>
<field name="monthly_cash_gap"/>
<field name="new_mrr_won"/>
<field name="pipeline_value"/>
<field name="weighted_pipeline"/>
</group>
<group>
<field name="proposals_outstanding"/>
<field name="deals_expected_month"/>
<field name="cash_collected"/>
<field name="invoices_overdue"/>
<field name="active_clients"/>
<field name="conversion_rate"/>
</group>
</group>
<group string="Execution">
<group>
<field name="followups_due_today"/>
<field name="projects_at_risk"/>
<field name="blockers"/>
<field name="overdue_tasks"/>
</group>
<group>
<field name="team_capacity_hours"/>
<field name="planned_hours"/>
<field name="actual_hours"/>
<field name="billable_hours"/>
<field name="non_billable_hours"/>
<field name="utilization"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_mcs_operating_dashboard" model="ir.actions.act_window">
<field name="name">CEO Dashboard</field>
<field name="res_model">mcs.operating.dashboard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_mcs_operating_dashboard_form"/>
<field name="res_id" ref="mcs_operating_system.dashboard_ceo"/>
<field name="target">current</field>
<field name="context">{'create': False}</field>
</record>
</odoo>

View File

@ -0,0 +1,13 @@
<odoo>
<record id="view_employee_form_mcs_operating" model="ir.ui.view">
<field name="name">hr.employee.form.mcs.operating</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='job_title']" position="after">
<field name="mcs_operating_role"/>
<field name="mcs_available_hours_week"/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,16 @@
<odoo>
<menuitem id="menu_mcs_operating_root" name="MCS Operating System" sequence="5" groups="mcs_operating_system.group_mcs_operating_user"/>
<menuitem id="menu_mcs_operating_dashboard" name="CEO Dashboard" parent="menu_mcs_operating_root" action="action_mcs_operating_dashboard" sequence="10"/>
<menuitem id="menu_mcs_operating_revenue" name="Revenue Board" parent="menu_mcs_operating_root" action="action_mcs_revenue_board" sequence="20"/>
<menuitem id="menu_mcs_operating_ship" name="Ship Board" parent="menu_mcs_operating_root" action="action_mcs_ship_board" sequence="30"/>
<menuitem id="menu_mcs_operating_capacity" name="Capacity Board" parent="menu_mcs_operating_root" action="action_mcs_capacity_board" sequence="40"/>
<menuitem id="menu_mcs_operating_management" name="Management" parent="menu_mcs_operating_root" sequence="50"/>
<menuitem id="menu_mcs_company_goals" name="Company Goals" parent="menu_mcs_operating_management" action="action_mcs_company_goal" sequence="10"/>
<menuitem id="menu_mcs_sales_targets" name="Sales Targets" parent="menu_mcs_operating_management" action="action_mcs_sales_target" sequence="20"/>
<menuitem id="menu_mcs_operating_reviews" name="Operating Reviews" parent="menu_mcs_operating_management" action="action_mcs_operating_review" sequence="30"/>
<menuitem id="menu_mcs_idea_parking" name="Idea Parking Lot" parent="menu_mcs_operating_management" action="action_mcs_idea_parking" sequence="40"/>
<menuitem id="menu_mcs_client_profitability" name="Client Profitability" parent="menu_mcs_operating_management" action="action_mcs_client_profitability" sequence="50"/>
<menuitem id="menu_mcs_operating_projects" name="Operating Projects" parent="menu_mcs_operating_management" action="action_mcs_projects" sequence="60"/>
</odoo>

View File

@ -0,0 +1,314 @@
<odoo>
<record id="view_mcs_company_goal_tree" model="ir.ui.view">
<field name="name">mcs.company.goal.tree</field>
<field name="model">mcs.company.goal</field>
<field name="arch" type="xml">
<tree decoration-success="state == 'done'" decoration-danger="state == 'blocked'">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="priority"/>
<field name="owner_id"/>
<field name="date_deadline"/>
<field name="target_amount"/>
<field name="next_action"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="view_mcs_company_goal_form" model="ir.ui.view">
<field name="name">mcs.company.goal.form</field>
<field name="model">mcs.company.goal</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_active" type="object" string="Set Active" invisible="state == 'active'"/>
<button name="action_blocked" type="object" string="Block" invisible="state == 'blocked'"/>
<button name="action_done" type="object" string="Done" class="btn-primary" invisible="state == 'done'"/>
<field name="state" widget="statusbar" statusbar_visible="planned,active,blocked,done"/>
</header>
<sheet>
<group>
<group>
<field name="name"/>
<field name="priority"/>
<field name="owner_id"/>
<field name="date_deadline"/>
</group>
<group>
<field name="target_amount"/>
<field name="currency_id" invisible="1"/>
<field name="next_action"/>
<field name="blocker"/>
</group>
</group>
<group>
<field name="project_ids" widget="many2many_tags"/>
<field name="opportunity_ids" widget="many2many_tags"/>
</group>
<field name="notes"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<record id="action_mcs_company_goal" model="ir.actions.act_window">
<field name="name">Company Goals</field>
<field name="res_model">mcs.company.goal</field>
<field name="view_mode">tree,form</field>
</record>
<record id="view_mcs_idea_parking_tree" model="ir.ui.view">
<field name="name">mcs.idea.parking.tree</field>
<field name="model">mcs.idea.parking</field>
<field name="arch" type="xml">
<tree decoration-muted="decision in ('park','kill')" decoration-success="decision == 'approve'">
<field name="name"/>
<field name="date_added"/>
<field name="potential_value"/>
<field name="urgency"/>
<field name="owner_id"/>
<field name="review_date"/>
<field name="decision"/>
</tree>
</field>
</record>
<record id="view_mcs_idea_parking_form" model="ir.ui.view">
<field name="name">mcs.idea.parking.form</field>
<field name="model">mcs.idea.parking</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_approve" type="object" string="Approve" class="btn-primary" invisible="decision == 'approve'"/>
<button name="action_park" type="object" string="Park" invisible="decision == 'park'"/>
<button name="action_kill" type="object" string="Kill" invisible="decision == 'kill'"/>
<field name="decision" widget="statusbar" statusbar_visible="pending,approve,park,kill"/>
</header>
<sheet>
<group>
<group>
<field name="name"/>
<field name="date_added"/>
<field name="owner_id"/>
<field name="review_date"/>
</group>
<group>
<field name="potential_value"/>
<field name="urgency"/>
<field name="project_id"/>
</group>
</group>
<field name="notes"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<record id="action_mcs_idea_parking" model="ir.actions.act_window">
<field name="name">Idea Parking Lot</field>
<field name="res_model">mcs.idea.parking</field>
<field name="view_mode">tree,form</field>
</record>
<record id="view_mcs_operating_review_tree" model="ir.ui.view">
<field name="name">mcs.operating.review.tree</field>
<field name="model">mcs.operating.review</field>
<field name="arch" type="xml">
<tree>
<field name="review_date"/>
<field name="review_type"/>
<field name="owner_id"/>
<field name="name"/>
</tree>
</field>
</record>
<record id="view_mcs_operating_review_form" model="ir.ui.view">
<field name="name">mcs.operating.review.form</field>
<field name="model">mcs.operating.review</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="review_type"/>
<field name="review_date"/>
<field name="owner_id"/>
</group>
<group>
<field name="name"/>
</group>
</group>
<notebook>
<page string="Daily">
<field name="shipped_yesterday" placeholder="What shipped yesterday?"/>
<field name="ships_today" placeholder="What ships today?"/>
<field name="blockers" placeholder="What is blocked and who needs what?"/>
</page>
<page string="Weekly / Monthly">
<field name="commitments"/>
<field name="revenue_notes"/>
<field name="delivery_notes"/>
<field name="capacity_notes"/>
<field name="cash_notes"/>
</page>
<page string="Decisions">
<field name="decisions"/>
<field name="next_actions"/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<record id="action_mcs_operating_review" model="ir.actions.act_window">
<field name="name">Operating Reviews</field>
<field name="res_model">mcs.operating.review</field>
<field name="view_mode">tree,form</field>
</record>
<record id="view_mcs_sales_target_tree" model="ir.ui.view">
<field name="name">mcs.sales.target.tree</field>
<field name="model">mcs.sales.target</field>
<field name="arch" type="xml">
<tree>
<field name="target_date"/>
<field name="user_id"/>
<field name="outbound_done"/>
<field name="outbound_target"/>
<field name="followup_done"/>
<field name="followup_target"/>
<field name="call_done"/>
<field name="call_target"/>
<field name="proposal_done"/>
<field name="proposal_target"/>
<field name="referral_done"/>
<field name="referral_target"/>
</tree>
</field>
</record>
<record id="view_mcs_sales_target_form" model="ir.ui.view">
<field name="name">mcs.sales.target.form</field>
<field name="model">mcs.sales.target</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="target_date"/>
<field name="user_id"/>
</group>
<group string="Daily Founder Sales Targets">
<group>
<field name="outbound_target"/>
<field name="outbound_done"/>
<field name="followup_target"/>
<field name="followup_done"/>
<field name="call_target"/>
<field name="call_done"/>
</group>
<group>
<field name="proposal_target"/>
<field name="proposal_done"/>
<field name="referral_target"/>
<field name="referral_done"/>
</group>
</group>
<field name="notes"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<record id="action_mcs_sales_target" model="ir.actions.act_window">
<field name="name">Sales Targets</field>
<field name="res_model">mcs.sales.target</field>
<field name="view_mode">tree,form,pivot,graph</field>
</record>
<record id="view_mcs_client_profitability_tree" model="ir.ui.view">
<field name="name">mcs.client.profitability.tree</field>
<field name="model">mcs.client.profitability</field>
<field name="arch" type="xml">
<tree decoration-danger="gross_contribution &lt; 0" decoration-warning="recommendation in ('reprice','rescope','reduce','exit')">
<field name="partner_id"/>
<field name="project_id"/>
<field name="period_start"/>
<field name="period_end"/>
<field name="client_revenue"/>
<field name="internal_delivery_cost"/>
<field name="gross_contribution"/>
<field name="gross_margin"/>
<field name="recommendation"/>
</tree>
</field>
</record>
<record id="view_mcs_client_profitability_form" model="ir.ui.view">
<field name="name">mcs.client.profitability.form</field>
<field name="model">mcs.client.profitability</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="partner_id"/>
<field name="project_id"/>
<field name="period_start"/>
<field name="period_end"/>
</group>
<group>
<field name="currency_id" invisible="1"/>
<field name="client_revenue"/>
<field name="internal_hourly_cost"/>
<field name="internal_delivery_cost"/>
<field name="gross_contribution"/>
<field name="gross_margin"/>
<field name="recommendation"/>
</group>
</group>
<group string="Hours">
<field name="delivery_hours"/>
<field name="account_management_hours"/>
<field name="rework_hours"/>
<field name="revision_hours"/>
</group>
<field name="notes"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<record id="action_mcs_client_profitability" model="ir.actions.act_window">
<field name="name">Client Profitability</field>
<field name="res_model">mcs.client.profitability</field>
<field name="view_mode">tree,form,pivot,graph</field>
</record>
</odoo>

View File

@ -0,0 +1,103 @@
<odoo>
<record id="view_project_form_mcs_operating" model="ir.ui.view">
<field name="name">project.project.form.mcs.operating</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="Operating System" name="mcs_operating_system">
<group>
<group string="Classification">
<field name="mcs_initiative_type"/>
<field name="mcs_classification"/>
<field name="mcs_revenue_connected"/>
<field name="mcs_at_risk"/>
</group>
<group string="Revenue / Capacity">
<field name="mcs_monthly_revenue_target"/>
<field name="mcs_contracted_revenue"/>
<field name="mcs_cash_gap"/>
<field name="mcs_planned_hours"/>
<field name="mcs_actual_hours"/>
<field name="mcs_utilization"/>
</group>
</group>
<group>
<field name="mcs_next_action"/>
<field name="mcs_blocker"/>
</group>
</page>
</xpath>
</field>
</record>
<record id="view_project_task_form_mcs_operating" model="ir.ui.view">
<field name="name">project.task.form.mcs.operating</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="Operating System" name="mcs_operating_system">
<group>
<group string="Commitment">
<field name="mcs_priority"/>
<field name="mcs_client_internal"/>
<field name="mcs_work_category"/>
<field name="milestone_id"/>
<field name="allocated_hours"/>
<field name="effective_hours"/>
</group>
<group string="Control">
<field name="mcs_next_action"/>
<field name="mcs_is_blocked"/>
<field name="mcs_at_risk"/>
<field name="mcs_missing_operating_data"/>
</group>
</group>
<group string="Blocker">
<field name="mcs_blocker" nolabel="1"/>
</group>
<group string="Definition of Done">
<field name="mcs_definition_of_done" nolabel="1"/>
</group>
</page>
</xpath>
</field>
</record>
<record id="view_project_task_tree_mcs_operating" model="ir.ui.view">
<field name="name">project.task.tree.mcs.operating</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_tree2"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="decoration-danger">mcs_at_risk</attribute>
<attribute name="decoration-warning">mcs_is_blocked</attribute>
</xpath>
<xpath expr="//field[@name='stage_id']" position="after">
<field name="mcs_priority" optional="show"/>
<field name="milestone_id" optional="show"/>
<field name="mcs_next_action" optional="show"/>
<field name="allocated_hours" optional="show"/>
<field name="effective_hours" optional="show"/>
<field name="mcs_is_blocked" optional="show"/>
<field name="mcs_missing_operating_data" optional="show"/>
</xpath>
</field>
</record>
<record id="action_mcs_ship_board" model="ir.actions.act_window">
<field name="name">Ship Board</field>
<field name="res_model">project.task</field>
<field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>
<field name="domain">[('active','=',True)]</field>
<field name="context">{'search_default_open_tasks': 1, 'group_by': 'project_id'}</field>
</record>
<record id="action_mcs_projects" model="ir.actions.act_window">
<field name="name">Operating Projects</field>
<field name="res_model">project.project</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('active','=',True)]</field>
</record>
</odoo>

View File

@ -0,0 +1,34 @@
<odoo>
<record id="view_timesheet_line_form_mcs_operating" model="ir.ui.view">
<field name="name">account.analytic.line.form.mcs.operating</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='unit_amount']" position="after">
<field name="mcs_work_category"/>
<field name="mcs_billable_classification"/>
<field name="mcs_is_rework"/>
</xpath>
</field>
</record>
<record id="view_timesheet_line_tree_mcs_operating" model="ir.ui.view">
<field name="name">account.analytic.line.tree.mcs.operating</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='unit_amount']" position="after">
<field name="mcs_work_category" optional="show"/>
<field name="mcs_billable_classification" optional="show"/>
<field name="mcs_is_rework" optional="show"/>
</xpath>
</field>
</record>
<record id="action_mcs_capacity_board" model="ir.actions.act_window">
<field name="name">Capacity Board</field>
<field name="res_model">account.analytic.line</field>
<field name="view_mode">tree,pivot,graph,form</field>
<field name="context">{'group_by': 'employee_id'}</field>
</record>
</odoo>