Files
Odoo-Modules/fusion_claims/models/dashboard.py
gsinghpal 4025789ba0 feat(fusion_claims): expand dashboard with this-month, pipeline, aging, recent exports + full-width
Adds 4 new sections:
- This Month rollup: submitted/approved/delivered/billed counts MTD
- Pipeline $ by stage: pre-submit / submitted / approved / ready-to-bill amounts
- Aging buckets: 30-59d, 60-89d, 90+ days
- Recent ADP Exports: last 5 with totals

Also overrides Odoo's form-sheet max-width on .o_fc_dashboard so the
dashboard uses the full browser width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 04:26:25 -04:00

767 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
# Copyright 2024-2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
from odoo import api, fields, models
class FusionClaimsDashboard(models.TransientModel):
_name = 'fusion.claims.dashboard'
_inherit = 'fusion_claims.adp.posting.schedule.mixin'
_description = 'Fusion Claims Dashboard'
_rec_name = 'name'
name = fields.Char(default='Dashboard', readonly=True)
# =========================================================================
# Role-aware filter
# =========================================================================
is_manager = fields.Boolean(compute='_compute_is_manager')
def _compute_is_manager(self):
manager_group = self.env.ref('fusion_claims.group_fusion_claims_manager',
raise_if_not_found=False)
sale_mgr_group = self.env.ref('sales_team.group_sale_manager',
raise_if_not_found=False)
for rec in self:
user = rec.env.user
rec.is_manager = bool(
(manager_group and user.has_group('fusion_claims.group_fusion_claims_manager'))
or (sale_mgr_group and user.has_group('sales_team.group_sale_manager'))
)
def _role_filter_domain(self):
"""Common domain prefix for SO-based counts.
Managers (fusion_claims.group_fusion_claims_manager or
sales_team.group_sale_manager) see everything.
Other users see only SOs where they are the salesperson.
"""
self.ensure_one()
if self.is_manager:
return []
return [('user_id', '=', self.env.user.id)]
def _month_start(self):
from datetime import date
return date.today().replace(day=1)
# =========================================================================
# Header banner
# =========================================================================
posting_period_label = fields.Char(compute='_compute_banner')
posting_period_start = fields.Date(compute='_compute_banner')
posting_period_end = fields.Date(compute='_compute_banner')
submission_deadline_dt = fields.Datetime(compute='_compute_banner')
is_pre_first_posting = fields.Boolean(compute='_compute_banner')
def _compute_banner(self):
from datetime import date, datetime, time, timedelta
import pytz
today = date.today()
for rec in self:
base_date = rec._get_adp_posting_base_date()
rec.is_pre_first_posting = today < base_date
current = rec._get_current_posting_date(today)
nxt = rec._get_next_posting_date(today)
# If we're sitting on a posting date, current == next; treat
# the period as the one starting today.
if current == nxt:
period_start = current
period_end = current + timedelta(days=rec._get_adp_posting_frequency())
else:
period_start = current
period_end = nxt
rec.posting_period_start = period_start
rec.posting_period_end = period_end
if rec.is_pre_first_posting:
rec.posting_period_label = f"Posting starts {base_date.strftime('%b %d')}"
else:
rec.posting_period_label = (
f"{period_start.strftime('%b %d')} "
f"{period_end.strftime('%b %d')}"
)
wednesday = rec._get_posting_week_wednesday(nxt)
naive_deadline = datetime.combine(wednesday, time(18, 0, 0))
# Store as UTC; users see it in their TZ; OWL widget computes in local TZ.
tz = pytz.timezone(rec.env.user.tz or 'America/Toronto')
local_deadline = tz.localize(naive_deadline)
rec.submission_deadline_dt = local_deadline.astimezone(pytz.UTC).replace(tzinfo=None)
# =========================================================================
# KPI tiles (3-up)
# =========================================================================
currency_id = fields.Many2one('res.currency', compute='_compute_kpis')
kpi_ready_amount = fields.Monetary(compute='_compute_kpis',
currency_field='currency_id')
kpi_ready_count = fields.Integer(compute='_compute_kpis')
kpi_claimed_amount = fields.Monetary(compute='_compute_kpis',
currency_field='currency_id')
kpi_claimed_count = fields.Integer(compute='_compute_kpis')
kpi_ar_amount = fields.Monetary(compute='_compute_kpis',
currency_field='currency_id')
kpi_ar_count = fields.Integer(compute='_compute_kpis')
def _invoice_role_filter(self):
"""Role filter for invoices — applied through linked SO's user_id."""
self.ensure_one()
if self.is_manager:
return []
return [('x_fc_source_sale_order_id.user_id', '=', self.env.user.id)]
def _compute_kpis(self):
Move = self.env['account.move'].sudo()
for rec in self:
rec.currency_id = rec.env.company.currency_id
inv_filter = rec._invoice_role_filter()
# KPI 1: Ready to Claim
ready_domain = inv_filter + [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('x_fc_adp_billing_status', '=', 'waiting'),
('adp_exported', '=', False),
]
ready_invoices = Move.search(ready_domain)
rec.kpi_ready_count = len(ready_invoices)
rec.kpi_ready_amount = sum(ready_invoices.mapped('amount_total'))
# KPI 2: Claimed This Period
claimed_domain = inv_filter + [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('x_fc_adp_billing_status', 'in', ['submitted', 'resubmitted']),
('adp_export_date', '>=', rec.posting_period_start),
]
claimed_invoices = Move.search(claimed_domain)
rec.kpi_claimed_count = len(claimed_invoices)
rec.kpi_claimed_amount = sum(claimed_invoices.mapped('amount_total'))
# KPI 3: Total AR (ADP-portion invoices, unpaid)
ar_domain = inv_filter + [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('x_fc_invoice_type', '=', 'adp'),
('payment_state', 'in', ['not_paid', 'partial']),
]
ar_invoices = Move.search(ar_domain)
rec.kpi_ar_count = len(ar_invoices)
rec.kpi_ar_amount = sum(ar_invoices.mapped('amount_total'))
# =========================================================================
# Activities (left column)
# =========================================================================
my_activities_count = fields.Integer(compute='_compute_activities')
my_activities_html = fields.Html(compute='_compute_activities', sanitize=False)
def _compute_activities(self):
Activity = self.env['mail.activity'].sudo()
domain = [
('user_id', '=', self.env.user.id),
('res_model', 'in', ['sale.order', 'account.move', 'fusion.technician.task']),
]
for rec in self:
activities = Activity.search(domain, order='date_deadline asc', limit=10)
rec.my_activities_count = Activity.search_count(domain)
if not activities:
rec.my_activities_html = (
'<p class="o_fc_empty">No activities assigned.</p>'
)
continue
from datetime import date
today = date.today()
rows = []
for act in activities:
overdue = act.date_deadline and act.date_deadline < today
row_class = 'o_fc_activity_row o_fc_activity_overdue' if overdue else 'o_fc_activity_row'
deadline_text = act.date_deadline.strftime('%b %d') if act.date_deadline else ''
url = f'/odoo/{act.res_model.replace(".", "_")}/{act.res_id}'
rows.append(
f'<div class="{row_class}">'
f'<a href="{url}"><b>{act.summary or act.activity_type_id.name or "Activity"}</b></a>'
f'<span class="o_fc_activity_deadline">{deadline_text}</span>'
f'</div>'
)
rec.my_activities_html = '\n'.join(rows)
# =========================================================================
# Bottlenecks (left column) + Other funder counts
# =========================================================================
bottleneck_no_pod_count = fields.Integer(compute='_compute_secondary_counts')
bottleneck_no_response_count = fields.Integer(compute='_compute_secondary_counts')
count_odsp = fields.Integer(compute='_compute_secondary_counts')
count_wsib = fields.Integer(compute='_compute_secondary_counts')
count_insurance = fields.Integer(compute='_compute_secondary_counts')
count_mdc = fields.Integer(compute='_compute_secondary_counts')
count_hardship = fields.Integer(compute='_compute_secondary_counts')
count_acsd = fields.Integer(compute='_compute_secondary_counts')
def _compute_secondary_counts(self):
from datetime import date, timedelta
SO = self.env['sale.order'].sudo()
cutoff_14d_ago = date.today() - timedelta(days=14)
for rec in self:
base = rec._role_filter_domain()
active = base + [('state', '!=', 'cancel')]
rec.bottleneck_no_pod_count = SO.search_count(base + [
('x_fc_adp_application_status', 'in', ['approved', 'approved_deduction']),
('x_fc_proof_of_delivery', '=', False),
])
rec.bottleneck_no_response_count = SO.search_count(base + [
('x_fc_adp_application_status', 'in', ['submitted', 'resubmitted']),
('x_fc_claim_submission_date', '<', cutoff_14d_ago),
])
rec.count_odsp = SO.search_count(active + [
('x_fc_sale_type', 'in', ['odsp', 'adp_odsp']),
])
rec.count_wsib = SO.search_count(active + [('x_fc_sale_type', '=', 'wsib')])
rec.count_insurance = SO.search_count(active + [('x_fc_sale_type', '=', 'insurance')])
rec.count_mdc = SO.search_count(active + [('x_fc_sale_type', '=', 'muscular_dystrophy')])
rec.count_hardship = SO.search_count(active + [('x_fc_sale_type', '=', 'hardship')])
rec.count_acsd = SO.search_count(active + [('x_fc_client_type', '=', 'ACS')])
# =========================================================================
# ADP Pre-Approval (right column, 4 tiles)
# =========================================================================
adp_waiting_app_count = fields.Integer(compute='_compute_workflow_counts')
adp_app_received_count = fields.Integer(compute='_compute_workflow_counts')
adp_ready_submit_count = fields.Integer(compute='_compute_workflow_counts')
adp_needs_correction_count = fields.Integer(compute='_compute_workflow_counts')
# =========================================================================
# ADP Post-Approval (right column, 4 tiles)
# =========================================================================
adp_approved_count = fields.Integer(compute='_compute_workflow_counts')
adp_ready_delivery_count = fields.Integer(compute='_compute_workflow_counts')
adp_ready_bill_count = fields.Integer(compute='_compute_workflow_counts')
adp_on_hold_count = fields.Integer(compute='_compute_workflow_counts')
# =========================================================================
# MOD (right column, 5 tiles)
# =========================================================================
mod_awaiting_funding_count = fields.Integer(compute='_compute_workflow_counts')
mod_funding_approved_count = fields.Integer(compute='_compute_workflow_counts')
mod_pca_received_count = fields.Integer(compute='_compute_workflow_counts')
mod_project_complete_count = fields.Integer(compute='_compute_workflow_counts')
mod_pod_submitted_count = fields.Integer(compute='_compute_workflow_counts')
def _compute_workflow_counts(self):
SO = self.env['sale.order'].sudo()
for rec in self:
base = rec._role_filter_domain()
# ADP Pre-Approval
rec.adp_waiting_app_count = SO.search_count(base + [
('x_fc_adp_application_status', 'in',
['waiting_for_application', 'assessment_completed']),
])
rec.adp_app_received_count = SO.search_count(base + [
('x_fc_adp_application_status', '=', 'application_received'),
])
rec.adp_ready_submit_count = SO.search_count(base + [
('x_fc_adp_application_status', '=', 'ready_submission'),
])
rec.adp_needs_correction_count = SO.search_count(base + [
('x_fc_adp_application_status', '=', 'needs_correction'),
])
# ADP Post-Approval
rec.adp_approved_count = SO.search_count(base + [
('x_fc_adp_application_status', 'in', ['approved', 'approved_deduction']),
])
rec.adp_ready_delivery_count = SO.search_count(base + [
('x_fc_adp_application_status', '=', 'ready_delivery'),
])
rec.adp_ready_bill_count = SO.search_count(base + [
('x_fc_adp_application_status', '=', 'ready_bill'),
])
rec.adp_on_hold_count = SO.search_count(base + [
('x_fc_adp_application_status', '=', 'on_hold'),
])
# MOD
rec.mod_awaiting_funding_count = SO.search_count(base + [
('x_fc_mod_status', '=', 'awaiting_funding'),
])
rec.mod_funding_approved_count = SO.search_count(base + [
('x_fc_mod_status', '=', 'funding_approved'),
])
rec.mod_pca_received_count = SO.search_count(base + [
('x_fc_mod_status', '=', 'contract_received'),
])
rec.mod_project_complete_count = SO.search_count(base + [
('x_fc_mod_status', '=', 'project_complete'),
])
rec.mod_pod_submitted_count = SO.search_count(base + [
('x_fc_mod_status', '=', 'pod_submitted'),
])
# =========================================================================
# This Month rollup (4-up secondary KPI strip)
# =========================================================================
count_month_submitted = fields.Integer(compute='_compute_this_month')
count_month_approved = fields.Integer(compute='_compute_this_month')
count_month_delivered = fields.Integer(compute='_compute_this_month')
count_month_billed = fields.Integer(compute='_compute_this_month')
def _compute_this_month(self):
SO = self.env['sale.order'].sudo()
for rec in self:
base = rec._role_filter_domain()
ms = rec._month_start()
rec.count_month_submitted = SO.search_count(base + [
('x_fc_claim_submission_date', '>=', ms),
])
rec.count_month_approved = SO.search_count(base + [
('x_fc_claim_approval_date', '>=', ms),
])
rec.count_month_delivered = SO.search_count(base + [
('x_fc_adp_delivery_date', '>=', ms),
])
rec.count_month_billed = SO.search_count(base + [
('x_fc_billing_date', '>=', ms),
])
# =========================================================================
# Pipeline $ by stage (4-up money-in-motion strip)
# =========================================================================
pipeline_pre_amount = fields.Monetary(compute='_compute_pipeline',
currency_field='currency_id')
pipeline_submitted_amount = fields.Monetary(compute='_compute_pipeline',
currency_field='currency_id')
pipeline_approved_amount = fields.Monetary(compute='_compute_pipeline',
currency_field='currency_id')
pipeline_ready_bill_amount = fields.Monetary(compute='_compute_pipeline',
currency_field='currency_id')
def _compute_pipeline(self):
SO = self.env['sale.order'].sudo()
for rec in self:
base = rec._role_filter_domain()
pre = SO.search(base + [
('x_fc_adp_application_status', 'in',
['waiting_for_application', 'assessment_completed',
'application_received', 'ready_submission']),
])
sub = SO.search(base + [
('x_fc_adp_application_status', 'in', ['submitted', 'resubmitted']),
])
app = SO.search(base + [
('x_fc_adp_application_status', 'in', ['approved', 'approved_deduction']),
])
bill = SO.search(base + [
('x_fc_adp_application_status', '=', 'ready_bill'),
])
rec.pipeline_pre_amount = sum(pre.mapped('amount_total'))
rec.pipeline_submitted_amount = sum(sub.mapped('amount_total'))
rec.pipeline_approved_amount = sum(app.mapped('amount_total'))
rec.pipeline_ready_bill_amount = sum(bill.mapped('amount_total'))
# =========================================================================
# Aging buckets (disjoint: 30-59d, 60-89d, 90+d)
# =========================================================================
aging_30_count = fields.Integer(compute='_compute_aging')
aging_60_count = fields.Integer(compute='_compute_aging')
aging_90_count = fields.Integer(compute='_compute_aging')
def _compute_aging(self):
from datetime import date, timedelta
SO = self.env['sale.order'].sudo()
today = date.today()
cut_30 = today - timedelta(days=30)
cut_60 = today - timedelta(days=60)
cut_90 = today - timedelta(days=90)
# "Active" = SO not cancelled at order level, AND if it has an ADP
# status, it's not in a terminal ADP state.
terminal_adp = ['case_closed', 'cancelled', 'expired', 'withdrawn']
for rec in self:
base = rec._role_filter_domain() + [
('state', '!=', 'cancel'),
'|',
('x_fc_adp_application_status', '=', False),
('x_fc_adp_application_status', 'not in', terminal_adp),
]
rec.aging_30_count = SO.search_count(base + [
('create_date', '<', cut_30),
('create_date', '>=', cut_60),
])
rec.aging_60_count = SO.search_count(base + [
('create_date', '<', cut_60),
('create_date', '>=', cut_90),
])
rec.aging_90_count = SO.search_count(base + [
('create_date', '<', cut_90),
])
# =========================================================================
# Recent ADP Exports (last 5)
# =========================================================================
recent_exports_html = fields.Html(compute='_compute_recent_exports',
sanitize=False)
recent_exports_count = fields.Integer(compute='_compute_recent_exports')
def _compute_recent_exports(self):
Exp = self.env['fusion_claims.adp.export.record'].sudo()
for rec in self:
records = Exp.search([], order='export_date desc', limit=5)
rec.recent_exports_count = Exp.search_count([])
if not records:
rec.recent_exports_html = (
'<p class="o_fc_empty">No exports yet.</p>'
)
continue
rows = []
for r in records:
total = sum(r.invoice_ids.mapped('amount_total'))
date_str = (r.export_date.strftime('%b %d, %Y')
if r.export_date else '')
label = r.posting_period_label or r.name or 'Export'
inv_count = r.invoice_count or 0
rows.append(
f'<div class="o_fc_export_row" '
f'data-export-id="{r.id}">'
f'<div class="o_fc_export_label">'
f'<b>{label}</b>'
f'<br/><small>{date_str} · {inv_count} inv</small>'
f'</div>'
f'<div class="o_fc_export_amount">${total:,.0f}</div>'
f'</div>'
)
rec.recent_exports_html = '\n'.join(rows)
# =========================================================================
# Open-list action methods
# =========================================================================
def _so_list_action(self, name, domain):
return {
'type': 'ir.actions.act_window',
'name': name,
'res_model': 'sale.order',
'view_mode': 'list,form',
'domain': self._role_filter_domain() + domain,
'target': 'current',
}
# ----- ADP Pre-Approval -----
def action_open_adp_waiting_app(self):
return self._so_list_action('ADP — Waiting for Application', [
('x_fc_adp_application_status', 'in',
['waiting_for_application', 'assessment_completed']),
])
def action_open_adp_app_received(self):
return self._so_list_action('ADP — Application Received', [
('x_fc_adp_application_status', '=', 'application_received'),
])
def action_open_adp_ready_submit(self):
return self._so_list_action('ADP — Ready for Submission', [
('x_fc_adp_application_status', '=', 'ready_submission'),
])
def action_open_adp_needs_correction(self):
return self._so_list_action('ADP — Needs Correction', [
('x_fc_adp_application_status', '=', 'needs_correction'),
])
# ----- ADP Post-Approval -----
def action_open_adp_approved(self):
return self._so_list_action('ADP — Approved', [
('x_fc_adp_application_status', 'in', ['approved', 'approved_deduction']),
])
def action_open_adp_ready_delivery(self):
return self._so_list_action('ADP — Ready for Delivery', [
('x_fc_adp_application_status', '=', 'ready_delivery'),
])
def action_open_adp_ready_bill(self):
return self._so_list_action('ADP — Ready to Bill', [
('x_fc_adp_application_status', '=', 'ready_bill'),
])
def action_open_adp_on_hold(self):
return self._so_list_action('ADP — On Hold', [
('x_fc_adp_application_status', '=', 'on_hold'),
])
# ----- MOD -----
def action_open_mod_awaiting_funding(self):
return self._so_list_action('MOD — Awaiting Funding', [
('x_fc_mod_status', '=', 'awaiting_funding'),
])
def action_open_mod_funding_approved(self):
return self._so_list_action('MOD — Funding Approved', [
('x_fc_mod_status', '=', 'funding_approved'),
])
def action_open_mod_pca_received(self):
return self._so_list_action('MOD — PCA Received', [
('x_fc_mod_status', '=', 'contract_received'),
])
def action_open_mod_project_complete(self):
return self._so_list_action('MOD — Project Complete', [
('x_fc_mod_status', '=', 'project_complete'),
])
def action_open_mod_pod_submitted(self):
return self._so_list_action('MOD — POD Submitted', [
('x_fc_mod_status', '=', 'pod_submitted'),
])
# ----- Other funders -----
def action_open_odsp_cases(self):
return self._so_list_action('ODSP Cases', [
('state', '!=', 'cancel'),
('x_fc_sale_type', 'in', ['odsp', 'adp_odsp']),
])
def action_open_wsib_cases(self):
return self._so_list_action('WSIB Cases', [
('state', '!=', 'cancel'),
('x_fc_sale_type', '=', 'wsib'),
])
def action_open_insurance_cases(self):
return self._so_list_action('Insurance Cases', [
('state', '!=', 'cancel'),
('x_fc_sale_type', '=', 'insurance'),
])
def action_open_mdc_cases(self):
return self._so_list_action('Muscular Dystrophy Cases', [
('state', '!=', 'cancel'),
('x_fc_sale_type', '=', 'muscular_dystrophy'),
])
def action_open_hardship_cases(self):
return self._so_list_action('Hardship Cases', [
('state', '!=', 'cancel'),
('x_fc_sale_type', '=', 'hardship'),
])
def action_open_acsd_cases(self):
return self._so_list_action('ACSD Cases', [
('state', '!=', 'cancel'),
('x_fc_client_type', '=', 'ACS'),
])
# ----- Bottlenecks -----
def action_open_bottleneck_no_pod(self):
return self._so_list_action('Bottleneck — Approved without POD', [
('x_fc_adp_application_status', 'in', ['approved', 'approved_deduction']),
('x_fc_proof_of_delivery', '=', False),
])
def action_open_bottleneck_no_response(self):
from datetime import date, timedelta
cutoff = date.today() - timedelta(days=14)
return self._so_list_action('Bottleneck — Submitted, no response', [
('x_fc_adp_application_status', 'in', ['submitted', 'resubmitted']),
('x_fc_claim_submission_date', '<', cutoff),
])
# ----- Activities -----
def action_open_my_activities(self):
return {
'type': 'ir.actions.act_window',
'name': 'My Activities',
'res_model': 'mail.activity',
'view_mode': 'list,form',
'domain': [
('user_id', '=', self.env.user.id),
('res_model', 'in', ['sale.order', 'account.move',
'fusion.technician.task']),
],
'target': 'current',
}
# ----- KPI drill-downs -----
def action_open_kpi_ready(self):
return {
'type': 'ir.actions.act_window',
'name': 'Ready to Claim (ADP)',
'res_model': 'account.move',
'view_mode': 'list,form',
'domain': self._invoice_role_filter() + [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('x_fc_adp_billing_status', '=', 'waiting'),
('adp_exported', '=', False),
],
'target': 'current',
}
def action_open_kpi_claimed(self):
return {
'type': 'ir.actions.act_window',
'name': 'Claimed This Period',
'res_model': 'account.move',
'view_mode': 'list,form',
'domain': self._invoice_role_filter() + [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('x_fc_adp_billing_status', 'in', ['submitted', 'resubmitted']),
('adp_export_date', '>=', self.posting_period_start),
],
'target': 'current',
}
def action_open_kpi_ar(self):
return {
'type': 'ir.actions.act_window',
'name': 'Total AR (ADP)',
'res_model': 'account.move',
'view_mode': 'list,form',
'domain': self._invoice_role_filter() + [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('x_fc_invoice_type', '=', 'adp'),
('payment_state', 'in', ['not_paid', 'partial']),
],
'target': 'current',
}
# =========================================================================
# Create-SO hotlinks
# =========================================================================
def _create_so_action(self, name, ctx_extra):
context = dict(self.env.context)
context.update(ctx_extra)
return {
'type': 'ir.actions.act_window',
'name': name,
'res_model': 'sale.order',
'view_mode': 'form',
'view_id': False,
'context': context,
'target': 'current',
}
def action_create_adp_so(self):
return self._create_so_action('New ADP Order',
{'default_x_fc_sale_type': 'adp'})
def action_create_mod_so(self):
return self._create_so_action('New MOD Order',
{'default_x_fc_sale_type': 'march_of_dimes'})
def action_create_odsp_so(self):
return self._create_so_action('New ODSP Order', {
'default_x_fc_sale_type': 'odsp',
'default_x_fc_odsp_division': 'standard',
})
def action_create_wsib_so(self):
return self._create_so_action('New WSIB Order',
{'default_x_fc_sale_type': 'wsib'})
def action_create_insurance_so(self):
return self._create_so_action('New Insurance Order',
{'default_x_fc_sale_type': 'insurance'})
def action_create_mdc_so(self):
return self._create_so_action('New MDC Order',
{'default_x_fc_sale_type': 'muscular_dystrophy'})
def action_create_hardship_so(self):
return self._create_so_action('New Hardship Order',
{'default_x_fc_sale_type': 'hardship'})
def action_create_private_so(self):
return self._create_so_action('New Private Order',
{'default_x_fc_sale_type': 'direct_private'})
# =========================================================================
# Additional drill-downs (This Month, Pipeline, Aging, Exports)
# =========================================================================
def action_open_month_submitted(self):
return self._so_list_action('Submitted This Month', [
('x_fc_claim_submission_date', '>=', self._month_start()),
])
def action_open_month_approved(self):
return self._so_list_action('Approved This Month', [
('x_fc_claim_approval_date', '>=', self._month_start()),
])
def action_open_month_delivered(self):
return self._so_list_action('Delivered This Month', [
('x_fc_adp_delivery_date', '>=', self._month_start()),
])
def action_open_month_billed(self):
return self._so_list_action('Billed This Month', [
('x_fc_billing_date', '>=', self._month_start()),
])
def action_open_pipeline_pre(self):
return self._so_list_action('Pipeline — Pre-Submission', [
('x_fc_adp_application_status', 'in',
['waiting_for_application', 'assessment_completed',
'application_received', 'ready_submission']),
])
def action_open_pipeline_submitted(self):
return self._so_list_action('Pipeline — Submitted to ADP', [
('x_fc_adp_application_status', 'in', ['submitted', 'resubmitted']),
])
def action_open_aging_30(self):
from datetime import date, timedelta
today = date.today()
terminal_adp = ['case_closed', 'cancelled', 'expired', 'withdrawn']
return self._so_list_action('Aging — 30 to 59 Days', [
('state', '!=', 'cancel'),
'|',
('x_fc_adp_application_status', '=', False),
('x_fc_adp_application_status', 'not in', terminal_adp),
('create_date', '<', today - timedelta(days=30)),
('create_date', '>=', today - timedelta(days=60)),
])
def action_open_aging_60(self):
from datetime import date, timedelta
today = date.today()
terminal_adp = ['case_closed', 'cancelled', 'expired', 'withdrawn']
return self._so_list_action('Aging — 60 to 89 Days', [
('state', '!=', 'cancel'),
'|',
('x_fc_adp_application_status', '=', False),
('x_fc_adp_application_status', 'not in', terminal_adp),
('create_date', '<', today - timedelta(days=60)),
('create_date', '>=', today - timedelta(days=90)),
])
def action_open_aging_90(self):
from datetime import date, timedelta
today = date.today()
terminal_adp = ['case_closed', 'cancelled', 'expired', 'withdrawn']
return self._so_list_action('Aging — 90+ Days', [
('state', '!=', 'cancel'),
'|',
('x_fc_adp_application_status', '=', False),
('x_fc_adp_application_status', 'not in', terminal_adp),
('create_date', '<', today - timedelta(days=90)),
])
def action_open_recent_exports(self):
return {
'type': 'ir.actions.act_window',
'name': 'ADP Export History',
'res_model': 'fusion_claims.adp.export.record',
'view_mode': 'list,form',
'target': 'current',
}