Add Metatroncube operating system addon
This commit is contained in:
parent
206d6bba68
commit
a11795f9da
1
addons/mcs_operating_system/__init__.py
Normal file
1
addons/mcs_operating_system/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import models
|
||||
35
addons/mcs_operating_system/__manifest__.py
Normal file
35
addons/mcs_operating_system/__manifest__.py
Normal 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,
|
||||
}
|
||||
62
addons/mcs_operating_system/data/crm_data.xml
Normal file
62
addons/mcs_operating_system/data/crm_data.xml
Normal 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>
|
||||
11
addons/mcs_operating_system/data/cron_data.xml
Normal file
11
addons/mcs_operating_system/data/cron_data.xml
Normal 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>
|
||||
27
addons/mcs_operating_system/data/operating_data.xml
Normal file
27
addons/mcs_operating_system/data/operating_data.xml
Normal 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>
|
||||
117
addons/mcs_operating_system/data/project_data.xml
Normal file
117
addons/mcs_operating_system/data/project_data.xml
Normal 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>
|
||||
7
addons/mcs_operating_system/models/__init__.py
Normal file
7
addons/mcs_operating_system/models/__init__.py
Normal 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
|
||||
110
addons/mcs_operating_system/models/crm_lead.py
Normal file
110
addons/mcs_operating_system/models/crm_lead.py
Normal 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()
|
||||
174
addons/mcs_operating_system/models/dashboard.py
Normal file
174
addons/mcs_operating_system/models/dashboard.py
Normal 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},
|
||||
}
|
||||
20
addons/mcs_operating_system/models/hr_employee.py
Normal file
20
addons/mcs_operating_system/models/hr_employee.py
Normal 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)
|
||||
235
addons/mcs_operating_system/models/operating.py
Normal file
235
addons/mcs_operating_system/models/operating.py
Normal 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
|
||||
)
|
||||
146
addons/mcs_operating_system/models/project.py
Normal file
146
addons/mcs_operating_system/models/project.py
Normal 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)
|
||||
)
|
||||
12
addons/mcs_operating_system/models/sale_order.py
Normal file
12
addons/mcs_operating_system/models/sale_order.py
Normal 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
|
||||
)
|
||||
19
addons/mcs_operating_system/models/timesheet.py
Normal file
19
addons/mcs_operating_system/models/timesheet.py
Normal 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")
|
||||
12
addons/mcs_operating_system/security/ir.model.access.csv
Normal file
12
addons/mcs_operating_system/security/ir.model.access.csv
Normal 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
|
||||
|
12
addons/mcs_operating_system/security/security.xml
Normal file
12
addons/mcs_operating_system/security/security.xml
Normal 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>
|
||||
59
addons/mcs_operating_system/views/crm_lead_views.xml
Normal file
59
addons/mcs_operating_system/views/crm_lead_views.xml
Normal 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>
|
||||
67
addons/mcs_operating_system/views/dashboard_views.xml
Normal file
67
addons/mcs_operating_system/views/dashboard_views.xml
Normal 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>
|
||||
13
addons/mcs_operating_system/views/hr_employee_views.xml
Normal file
13
addons/mcs_operating_system/views/hr_employee_views.xml
Normal 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>
|
||||
16
addons/mcs_operating_system/views/menu.xml
Normal file
16
addons/mcs_operating_system/views/menu.xml
Normal 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>
|
||||
314
addons/mcs_operating_system/views/operating_views.xml
Normal file
314
addons/mcs_operating_system/views/operating_views.xml
Normal 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 < 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>
|
||||
103
addons/mcs_operating_system/views/project_views.xml
Normal file
103
addons/mcs_operating_system/views/project_views.xml
Normal 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>
|
||||
34
addons/mcs_operating_system/views/timesheet_views.xml
Normal file
34
addons/mcs_operating_system/views/timesheet_views.xml
Normal 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>
|
||||
Loading…
x
Reference in New Issue
Block a user