Scope CEO portfolio by active companies

This commit is contained in:
metatroncubeswdev 2026-08-30 11:25:42 -04:00
parent b17a5aaf7a
commit 462e91f00c

View File

@ -187,6 +187,7 @@ class McsOperatingDashboard(models.Model):
month_end = month_start + relativedelta(months=1, days=-1) month_end = month_start + relativedelta(months=1, days=-1)
week_start = today - relativedelta(days=today.weekday()) week_start = today - relativedelta(days=today.weekday())
week_end = week_start + relativedelta(days=6) week_end = week_start + relativedelta(days=6)
company_ids = self._dashboard_company_ids()
Lead = self.env["crm.lead"] Lead = self.env["crm.lead"]
Project = self.env["project.project"] Project = self.env["project.project"]
@ -197,7 +198,10 @@ class McsOperatingDashboard(models.Model):
Partner = self.env["res.partner"] Partner = self.env["res.partner"]
CeoAction = self.env["mcs.ceo.action"] CeoAction = self.env["mcs.ceo.action"]
active_projects = Project.search([("active", "=", True)]) active_projects = Project.search(
[("active", "=", True)]
+ self._company_domain(Project, company_ids, include_shared=True)
)
client_projects = active_projects.filtered( client_projects = active_projects.filtered(
lambda project: project.mcs_delivery_type lambda project: project.mcs_delivery_type
not in ("internal_product", "internal_r_and_d", "sales_initiative") not in ("internal_product", "internal_r_and_d", "sales_initiative")
@ -207,7 +211,12 @@ class McsOperatingDashboard(models.Model):
in ("internal_product", "internal_r_and_d") in ("internal_product", "internal_r_and_d")
) )
open_pipeline = Lead.search( open_pipeline = Lead.search(
[("type", "=", "opportunity"), ("active", "=", True), ("probability", "<", 100)] [
("type", "=", "opportunity"),
("active", "=", True),
("probability", "<", 100),
]
+ self._company_domain(Lead, company_ids, include_shared=True)
) )
won_month = Lead.search( won_month = Lead.search(
[ [
@ -217,6 +226,7 @@ class McsOperatingDashboard(models.Model):
("date_closed", ">=", month_start), ("date_closed", ">=", month_start),
("date_closed", "<=", month_end), ("date_closed", "<=", month_end),
] ]
+ self._company_domain(Lead, company_ids, include_shared=True)
) )
invoices_month = Invoice.search( invoices_month = Invoice.search(
[ [
@ -225,6 +235,7 @@ class McsOperatingDashboard(models.Model):
("invoice_date", ">=", month_start), ("invoice_date", ">=", month_start),
("invoice_date", "<=", month_end), ("invoice_date", "<=", month_end),
] ]
+ self._company_domain(Invoice, company_ids, include_shared=False)
) )
receivables = Invoice.search( receivables = Invoice.search(
[ [
@ -232,18 +243,31 @@ class McsOperatingDashboard(models.Model):
("state", "=", "posted"), ("state", "=", "posted"),
("payment_state", "not in", ["paid", "in_payment", "reversed"]), ("payment_state", "not in", ["paid", "in_payment", "reversed"]),
] ]
+ self._company_domain(Invoice, company_ids, include_shared=False)
)
posted_invoices = Invoice.search(
[("move_type", "=", "out_invoice"), ("state", "=", "posted")]
+ self._company_domain(Invoice, company_ids, include_shared=False)
) )
overdue_receivables = receivables.filtered( overdue_receivables = receivables.filtered(
lambda invoice: invoice.invoice_date_due and invoice.invoice_date_due < today lambda invoice: invoice.invoice_date_due and invoice.invoice_date_due < today
) )
timesheets_month = Timesheet.search( timesheets_month = Timesheet.search(
[("date", ">=", month_start), ("date", "<=", month_end)] [("date", ">=", month_start), ("date", "<=", month_end)]
+ self._company_domain(Timesheet, company_ids, include_shared=True)
) )
timesheets_week = Timesheet.search( timesheets_week = Timesheet.search(
[("date", ">=", week_start), ("date", "<=", week_end)] [("date", ">=", week_start), ("date", "<=", week_end)]
+ self._company_domain(Timesheet, company_ids, include_shared=True)
)
active_employees = Employee.search(
[("active", "=", True)]
+ self._company_domain(Employee, company_ids, include_shared=True)
)
active_tasks = Task.search(
[("active", "=", True)]
+ self._company_domain(Task, company_ids, include_shared=True)
) )
active_employees = Employee.search([("active", "=", True)])
active_tasks = Task.search([("active", "=", True)])
monthly_target = sum(active_projects.mapped("mcs_monthly_revenue_target")) monthly_target = sum(active_projects.mapped("mcs_monthly_revenue_target"))
contracted_revenue = sum(client_projects.mapped("mcs_contracted_revenue")) contracted_revenue = sum(client_projects.mapped("mcs_contracted_revenue"))
@ -293,6 +317,8 @@ class McsOperatingDashboard(models.Model):
"month_end": fields.Date.to_string(month_end), "month_end": fields.Date.to_string(month_end),
"week_start": fields.Date.to_string(week_start), "week_start": fields.Date.to_string(week_start),
"week_end": fields.Date.to_string(week_end), "week_end": fields.Date.to_string(week_end),
"company_ids": company_ids,
"companies": self.env["res.company"].browse(company_ids).mapped("name"),
}, },
"company_pulse": { "company_pulse": {
"revenue_coverage": self._format_percent(revenue_coverage), "revenue_coverage": self._format_percent(revenue_coverage),
@ -336,19 +362,32 @@ class McsOperatingDashboard(models.Model):
"pipeline_coverage_status": self._coverage_status(pipeline_coverage), "pipeline_coverage_status": self._coverage_status(pipeline_coverage),
"funnel": self._crm_funnel_data(open_pipeline), "funnel": self._crm_funnel_data(open_pipeline),
}, },
"portfolio": self._portfolio_rows(Partner, client_projects), "portfolio": self._portfolio_rows(Partner, client_projects, posted_invoices),
"project_health": self._project_health_distribution(active_projects), "project_health": self._project_health_distribution(active_projects),
"capacity": self._capacity_rows(active_employees, active_tasks, timesheets_week), "capacity": self._capacity_rows(active_employees, active_tasks, timesheets_week),
"time_distribution": self._time_distribution(timesheets_week), "time_distribution": self._time_distribution(timesheets_week),
"allocation_guardrail": self._allocation_guardrail(timesheets_week), "allocation_guardrail": self._allocation_guardrail(timesheets_week),
"ceo_actions": self._ceo_action_rows(CeoAction), "ceo_actions": self._ceo_action_rows(CeoAction),
"internal_bets": self._internal_bet_rows(internal_projects, timesheets_month), "internal_bets": self._internal_bet_rows(internal_projects, timesheets_month),
"actions": self._dashboard_actions(today, month_start, month_end, week_start, week_end), "actions": self._dashboard_actions(
today, month_start, month_end, week_start, week_end, company_ids
),
} }
def _format_percent(self, value): def _format_percent(self, value):
return round(value or 0, 1) return round(value or 0, 1)
def _dashboard_company_ids(self):
company_ids = self.env.context.get("allowed_company_ids") or self.env.companies.ids
return company_ids or [self.env.company.id]
def _company_domain(self, Model, company_ids, include_shared=True):
if "company_id" not in Model._fields:
return []
if include_shared:
return ["|", ("company_id", "=", False), ("company_id", "in", company_ids)]
return [("company_id", "in", company_ids)]
def _monthly_operating_cost(self): def _monthly_operating_cost(self):
try: try:
Contract = self.env["hr.contract"] Contract = self.env["hr.contract"]
@ -356,6 +395,9 @@ class McsOperatingDashboard(models.Model):
return 0 return 0
contracts = Contract.sudo().search( contracts = Contract.sudo().search(
[("state", "=", "open"), ("employee_id.active", "=", True)] [("state", "=", "open"), ("employee_id.active", "=", True)]
+ self._company_domain(
Contract, self._dashboard_company_ids(), include_shared=True
)
) )
return sum(contracts.mapped("wage")) return sum(contracts.mapped("wage"))
@ -435,32 +477,53 @@ class McsOperatingDashboard(models.Model):
) )
return rows return rows
def _portfolio_rows(self, Partner, client_projects): def _portfolio_rows(self, Partner, client_projects, posted_invoices):
partners = Partner.search([("mcs_is_client_account", "=", True)]) partners = (
Partner.search([("mcs_is_client_account", "=", True)])
| client_projects.mapped("partner_id")
| posted_invoices.mapped("commercial_partner_id")
)
rows = [] rows = []
for partner in partners: for partner in partners:
projects = client_projects.filtered(lambda project: project.partner_id == partner) projects = client_projects.filtered(lambda project: project.partner_id == partner)
invoices = posted_invoices.filtered(
lambda invoice: invoice.commercial_partner_id == partner.commercial_partner_id
)
unpaid_invoices = invoices.filtered(
lambda invoice: invoice.payment_state
not in ("paid", "in_payment", "reversed")
)
overdue_invoices = unpaid_invoices.filtered(
lambda invoice: invoice.invoice_date_due
and invoice.invoice_date_due < fields.Date.context_today(self)
)
if partner.parent_id and not projects: if partner.parent_id and not projects:
continue continue
if ( if (
not projects not projects
and not partner.mcs_total_invoiced and not invoices
and not partner.mcs_total_outstanding
): ):
continue continue
currencies = invoices.mapped("currency_id") or partner.mcs_currency_id
currency_name = (
currencies[0].name
if len(currencies) == 1
else "Mixed"
)
invoice_dates = [date for date in invoices.mapped("invoice_date") if date]
rows.append( rows.append(
{ {
"id": partner.id, "id": partner.id,
"name": partner.display_name, "name": partner.display_name,
"currency": partner.mcs_currency_id.name or "", "currency": currency_name,
"health": partner.mcs_account_health, "health": partner.mcs_account_health,
"active_workstreams": len(projects), "active_workstreams": len(projects),
"revenue": sum(projects.mapped("mcs_monthly_revenue_target")), "revenue": sum(projects.mapped("mcs_monthly_revenue_target")),
"total_invoiced": partner.mcs_total_invoiced, "total_invoiced": sum(invoices.mapped("amount_total_signed")),
"outstanding": partner.mcs_total_outstanding, "outstanding": sum(unpaid_invoices.mapped("amount_residual_signed")),
"overdue": partner.mcs_total_overdue, "overdue": sum(overdue_invoices.mapped("amount_residual_signed")),
"last_invoice_date": fields.Date.to_string(partner.mcs_last_invoice_date) "last_invoice_date": fields.Date.to_string(max(invoice_dates))
if partner.mcs_last_invoice_date if invoice_dates
else "", else "",
"hours": sum(projects.mapped("mcs_actual_hours")), "hours": sum(projects.mapped("mcs_actual_hours")),
"next_milestone": next( "next_milestone": next(
@ -626,7 +689,11 @@ class McsOperatingDashboard(models.Model):
) )
return rows return rows
def _dashboard_actions(self, today, month_start, month_end, week_start, week_end): def _dashboard_actions(self, today, month_start, month_end, week_start, week_end, company_ids):
Project = self.env["project.project"]
Lead = self.env["crm.lead"]
Invoice = self.env["account.move"]
Timesheet = self.env["account.analytic.line"]
return { return {
"monthly_revenue_projects": { "monthly_revenue_projects": {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -634,7 +701,8 @@ class McsOperatingDashboard(models.Model):
"res_model": "project.project", "res_model": "project.project",
"view_mode": "tree,form,kanban", "view_mode": "tree,form,kanban",
"views": self._action_views("tree", "form", "kanban"), "views": self._action_views("tree", "form", "kanban"),
"domain": [("active", "=", True), ("mcs_monthly_revenue_target", ">", 0)], "domain": [("active", "=", True), ("mcs_monthly_revenue_target", ">", 0)]
+ self._company_domain(Project, company_ids, include_shared=True),
}, },
"contracted_projects": { "contracted_projects": {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -642,7 +710,8 @@ class McsOperatingDashboard(models.Model):
"res_model": "project.project", "res_model": "project.project",
"view_mode": "tree,form,kanban", "view_mode": "tree,form,kanban",
"views": self._action_views("tree", "form", "kanban"), "views": self._action_views("tree", "form", "kanban"),
"domain": [("active", "=", True), ("mcs_contracted_revenue", ">", 0)], "domain": [("active", "=", True), ("mcs_contracted_revenue", ">", 0)]
+ self._company_domain(Project, company_ids, include_shared=True),
}, },
"pipeline": { "pipeline": {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -650,7 +719,12 @@ class McsOperatingDashboard(models.Model):
"res_model": "crm.lead", "res_model": "crm.lead",
"view_mode": "kanban,tree,form,pivot,graph", "view_mode": "kanban,tree,form,pivot,graph",
"views": self._action_views("kanban", "tree", "form", "pivot", "graph"), "views": self._action_views("kanban", "tree", "form", "pivot", "graph"),
"domain": [("type", "=", "opportunity"), ("active", "=", True), ("probability", "<", 100)], "domain": [
("type", "=", "opportunity"),
("active", "=", True),
("probability", "<", 100),
]
+ self._company_domain(Lead, company_ids, include_shared=True),
}, },
"overdue_receivables": { "overdue_receivables": {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -663,7 +737,8 @@ class McsOperatingDashboard(models.Model):
("state", "=", "posted"), ("state", "=", "posted"),
("payment_state", "not in", ["paid", "in_payment", "reversed"]), ("payment_state", "not in", ["paid", "in_payment", "reversed"]),
("invoice_date_due", "<", fields.Date.to_string(today)), ("invoice_date_due", "<", fields.Date.to_string(today)),
], ]
+ self._company_domain(Invoice, company_ids, include_shared=False),
}, },
"capacity": { "capacity": {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -674,7 +749,8 @@ class McsOperatingDashboard(models.Model):
"domain": [ "domain": [
("date", ">=", fields.Date.to_string(week_start)), ("date", ">=", fields.Date.to_string(week_start)),
("date", "<=", fields.Date.to_string(week_end)), ("date", "<=", fields.Date.to_string(week_end)),
], ]
+ self._company_domain(Timesheet, company_ids, include_shared=True),
"context": {"group_by": "employee_id"}, "context": {"group_by": "employee_id"},
}, },
"projects_at_risk": { "projects_at_risk": {
@ -683,7 +759,11 @@ class McsOperatingDashboard(models.Model):
"res_model": "project.project", "res_model": "project.project",
"view_mode": "tree,form,kanban", "view_mode": "tree,form,kanban",
"views": self._action_views("tree", "form", "kanban"), "views": self._action_views("tree", "form", "kanban"),
"domain": [("active", "=", True), ("mcs_portfolio_health", "in", ["at_risk", "blocked"])], "domain": [
("active", "=", True),
("mcs_portfolio_health", "in", ["at_risk", "blocked"]),
]
+ self._company_domain(Project, company_ids, include_shared=True),
}, },
"ceo_actions": { "ceo_actions": {
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
@ -699,7 +779,8 @@ class McsOperatingDashboard(models.Model):
"res_model": "project.project", "res_model": "project.project",
"view_mode": "tree,form,kanban", "view_mode": "tree,form,kanban",
"views": self._action_views("tree", "form", "kanban"), "views": self._action_views("tree", "form", "kanban"),
"domain": [("mcs_delivery_type", "in", ["internal_product", "internal_r_and_d"])], "domain": [("mcs_delivery_type", "in", ["internal_product", "internal_r_and_d"])]
+ self._company_domain(Project, company_ids, include_shared=True),
}, },
} }