VIA CRM Lead (Service Development) — Gap Analysis Review
Comparing current Odoo 17 CRM form against VIA Service Development spec · Generated March 14, 2026 · Task fe7887bf
0 of 0 items reviewed
sale.order form. Fields marked with * (Service Type, County of Service, Region of Service, Last Name, First Name, MA Number, Internal ID) must be filled before clicking.
action_sale_quotations_new() handle the transition to sale.order. The _handle_partner_assignment() method syncs data lead→partner when a quotation is created.
- Layer 1 — Stage-based required attributes: Billing-critical fields (payer, payer_program, plan dates, priority) keep their
requiredattribute in the XML, updated torequired="stage_type == 'enrollment'". This preserves the greyish-blue fill color on required fields and prevents saving without them once the lead reaches Enrollment. Same mechanism as today, remapped fromactivationtoenrollment. - Layer 2 — Button-click validation: The Activate Authorization button validates the spec's starred fields (Service Type, County/Region of Service, Last Name, First Name, MA Number, Internal ID) before launching sale.order. Some of these overlap with stage-required fields; others (like Service Type, County/Region) may not be stage-required but are needed for authorization.
- Button behavior (confirmed): Button is always visible but greyed out (disabled) until all required fields are filled. Uses
invisibleorreadonlyattribute with a condition checking the starred fields. - On click: launches sale.order form, triggers
_handle_partner_assignment()to sync 50+ fields lead→partner - Replaces the current quotation-from-won-stage flow (
action_sale_quotations_new())
_handle_partner_assignment() still fires on button click (it syncs 50+ fields lead→partner). The button's Python method should call the existing sync logic before opening sale.order.
crm.crm_case_kanban_view_leads. Standard kanban shows opportunity name, expected revenue, tags, activity icons, partner name.
active Boolean for archiving. VIA hides "Mark Lost" (which normally archives). No "Archive" stage exists. Current status field has values: pre_auth, authorized, inactive — no "Archive" value.
- Add "Archive" stage to the pipeline (Stage 4)
- Build an Archive wizard (modeled on VIA's existing employee departure wizard):
—archive_reason_id: Many2one to a configurable reason model (VIA can add/edit reasons without code)
—archive_description: Free text, required ("Give more details about why this opportunity is being archived")
—archive_date: Auto-set to today or user-selectable - On archive: cancel all open/scheduled activities for this lead
- No follower notification on archive (not required at this time)
- Allow reactivation: move back from Archive stage, clear archive fields, restore to the stage matching current date field state
- Add search filter for archived opportunities
- Separate project: Automated follow-up cadences (chained event follow-up for non-responsive leads, escalating intervals) are out of scope for this form restructure. Leads that don't convert should eventually enter a long-tail follow-up sequence, but that's a CRM workflow/marketing automation project.
hr.employee, required. If different from "Entered by" (auto-set to creating user), send notification.
user_id (Salesperson, Many2one → res.users). No hr.employee-based assignment field exists. No "Entered by" field exists on crm.lead currently.
- Add
assigned_employee_idfield (Many2one → hr.employee, required) - Add
entered_by_user_idfield (Many2one → res.users, default=current user, readonly) - Build notification logic: if assigned employee's user differs from entered_by, send message
- Decide relationship to existing
user_id(Salesperson) — keep, hide, or replace?
user_id (res.users) for assignment. The spec wants hr.employee. Should we keep user_id as well (some Odoo features depend on it), or hide it and use only the new employee field? What notification method — Odoo chatter message, email, or both?
Service Line: Selection LOV filtered by Service Team — Supports Broker (PDS only), HTTS (PDS), IHCS (HCBS), Residential (HCBS). "Cannot appear in multi-select" note for Supports Broker.
service_team or service_line fields exist on crm.lead. Odoo base has team_id (Many2one → crm.team) which is used for the sales team — different concept.
- Add
service_teamselection field (PDS, HCBS) - Add
service_lineselection field with 4 values - Implement filtering: Service Line options change based on Service Team selection
- Determine relationship to existing
team_id(crm.team) — keep both? Map service_team to team_id?
team_id (crm.team record), or be a completely separate field?
service_ids is a Many2many → product.product with many2many_tags widget. This allows MULTIPLE services. The spec says "Only 1 Service Type per opportunity" — fundamentally different data model (Selection vs Many2many).
- Add new
service_typeselection field with the 16 spec values (including procedure codes as technical keys or separate field) - Keep or hide existing
service_idsMany2many — don't delete to preserve historical data - New field enforces single selection per opportunity (as spec requires: "one per service type")
- Procedure codes may need to be stored for billing integration
support_coordinator(Char) andsc_phone_no(Char) — free-text fields on the crm.lead Pre-Authorization tab, synced via all 3 lead↔partner sync methods- Studio action 8955 — provides a smart button on the SC's own res.partner form that links SC → their assigned Person Supported records. This is a Studio-built relational feature, not just a display field.
- Studio
x_studio_support_coordinatorfield — a Studio-created field on res.partner that stores the SC relationship from the partner side. Studio also adds a "Referrals" tab on the SC's partner form showing linked records.
- On crm.lead: Add
supports_coordinator_id(Many2one → res.partner, domain:partner_type = 'support_coordinator'). Add related fieldssc_phone_relatedandsc_email_relatedauto-populated from the linked partner. Move from Pre-Authorization tab to main form right column. - On res.partner (SC's form): Add code-based smart button replacing Studio action 8955. Add One2many tab showing the SC's assigned Person Supported records (replacing Studio's Referrals tab on the SC form).
- Studio cleanup: Deactivate Studio action 8955. Remove
x_studio_support_coordinatorfield after data migration to the new Many2one. - Sync update: Update all 3 sync methods (create, write, _handle_partner_assignment) to sync the Many2one
supports_coordinator_idinstead of the Charsupport_coordinator. - Data migration: Map existing Char values and Studio field data to res.partner records. Keep old Char fields hidden temporarily until migration is verified.
support_coordinator Char field is in the lead↔partner sync list. Changing to Many2one requires updating all 3 sync methods plus the partner create method. The Studio x_studio_support_coordinator field may also have downstream references that need auditing before removal.
date_referral, referral_meeting_date, project_auth_date, projected_units, authorized_date are all on the Pre-Authorization tab (group_1_pre_auth).
authorized_date is NOT in the spec's main form — clarify if it's still needed.
authorized_date field exists on Pre-Auth tab but is NOT listed in the spec's main form. Is it being removed, or should it remain somewhere? Also: project_auth_date currently controls ISP Data tab visibility — this logic must be preserved even if the field moves.
pre_auth_county_id (Many2one → via.res.county) and pre_auth_region (Selection: western, central, north_east, south_east) exist on the Pre-Authorization tab. The existing distinguish_region on lead_info has the same 4 values with slightly different keys. The USPS module already has the county→region mapping for all 67 PA counties.
- Move
pre_auth_county_idandpre_auth_regionfrom Pre-Auth tab to main form right column - Rename labels to "County of Service" and "Region of Service"
- Implement auto-populate: when County is selected, Region fills automatically using the USPS module's county→region mapping
- Resolve
distinguish_regionkey mismatch (westernon lead vswestin spec/partner) — spec says "West" - Make both required for Activate Authorization button
status field has values: pre_auth (Pre-Auth), authorized (Authorized), inactive (Inactive). Completely different set of values.
- Replace
statusLOV values with:active,hold,archive - Migrate existing data: map
pre_auth→active,authorized→ ?,inactive→archive - Move field position to bottom section of main form
- "Archive" value triggers stage movement (ties to D-E)
status field is in the partner→lead sync list. Partner has completely different values (active, inactive, discharged, deceased). The sync already has a mismatch — this change creates a third set of values. The sync logic for this field needs explicit handling.
Left: Last Name*, First Name*, Email, Mobile, Phone, Address (Line 1, Line 2, City/State/Zip), County of Residence, Region of Residence.
Right: DOB, MA Number*, Internal ID*, Sex, Gender, Sexual Orientation, Race, Ethnicity, Primary Language, Religion, Dev Disability, Intellectual Disability, Communication Plan, Mobility, Living Arrangement.
demo_graphics) has: 2-column layout with race/ethnicity (col 1), sexual_orientation/religion (col 2), then radio widgets for disabilities, ethnicity HTML checkbox table, and notes. Contact info and demographics fields are split between main form and this tab.
- Rename tab from "Demographics" to "Person Supported Details"
- Restructure to 2-column layout per spec
- Move contact info (name, email, phone, mobile, address) from main form into left column
- Move demographic fields (DOB, ma_number, etc.) from main form into right column
- Add County/Region of Residence (distinct from County/Region of Service on main form)
- Remove radio widgets — all fields become standard Selection dropdowns per spec
- Remove ethnicity HTML checkbox table
- Remove duplicate
ethnicitydisplay and notes field from this tab
PS-2: If no match, prompt to create a new res.partner record via quick-add button.
partner_id (Many2one → res.partner) exists on the form header, hidden in lead mode, visible in opportunity mode. When set, partner→lead sync copies ~50 fields. firstname/lastname are computed from partner_id but also stored/editable.
- Implement onchange on firstname/lastname that searches res.partner for matches
- Show matching contacts as a dropdown or dialog for selection
- On selection: set
partner_id, which triggers existing partner→lead sync (all ~50 fields auto-populate) - If no match: show "Create New Contact" button that creates res.partner and links it
- This replaces the current hidden
partner_idfield approach — the user never directly interacts with partner_id
ethnicity field displayed as checkbox widget.
- Change all radio widgets to standard Selection dropdowns
- Remove the ethnicity detail HTML checkbox table (8 Boolean fields)
- Remove duplicate ethnicity field display
- Keep the Boolean fields on the model (don't delete data) but hide from the form
Religion: Christian, Buddhist, Hindu, Muslim, Jewish, Sikh, None, Other, Prefer not to say.
native_other, partner has native_hawaiian. Lead has prefer_to_self_describe, partner has self_described. Spec says "Self-Described" (matches partner). Lead missing two_or_more. Spec says "Other" (matches partner's other), not lead's other_race_ethnicity_or_origin.Religion mismatches: Lead has
no_religion, partner has none. Spec says "None" (matches partner). Lead has another, partner has other. Spec says "Other" (matches partner).
- Update crm.lead LOV technical keys to match the spec (which aligns with res.partner):
- Race:
native_other→native_hawaiian,other_race_ethnicity_or_origin→other,prefer_to_self_describe→self_described - Religion:
no_religion→none,another→other - Region:
western→west(if distinguish_region kept) - Add missing
two_or_moreto race on lead (spec doesn't include it — confirm) - Data migration: update existing records with old keys to new keys
- This fixes the long-standing LOV mismatch issue documented in Session 3
county_id (Many2one → via.res.county) exists on the address block. distinguish_region exists on lead_info. These are separate from pre_auth_county_id / pre_auth_region (which are "County/Region of Service" on main form). The spec distinguishes residence from service counties.
- Use existing
county_idas County of Residence (already on address block) - Use
distinguish_regionas Region of Residence (already computed/stored) - Move both to Person Supported Details tab, left column, after address
- Implement auto-populate Region from County (same as County of Service, using USPS module mapping)
1. "What information are you looking for?" (Text, auto-populate from web inquiry form)
2. "What services are you looking for?" (Text, auto-populate from web inquiry form)
3. "Notes" (Text, free form)
4. "Entered by" (Many2one → res.users, auto-populates to logged-in user who creates the opportunity)
internal_notes tab (page name internal_notes) with description_sale field. No "What information are you looking for?" or "What services are you looking for?" fields exist. pre_auth_notes exists but is shared between Demographics and Pre-Auth tabs. No "Entered by" field on crm.lead.
- Add 2 new Text fields:
inquiry_info_looking_for,inquiry_services_looking_for - Add
entered_by_user_id(Many2one → res.users, default=current user, readonly) — same field as D-F if combined - Keep or repurpose existing
internal_notes/description_saleas the "Notes" free text - Web inquiry form auto-population requires website form integration (separate scope)
pre_auth_notes field from Demographics/Pre-Auth
LOW
~0.5 hrs
pre_auth_notes appears on BOTH the Demographics tab AND the Pre-Authorization tab — same database field displayed twice. Editing in either location changes the same value.
pre_auth_notes from the Demographics and Pre-Authorization tabs. If the Notes tab uses a different field, decide whether to migrate existing pre_auth_notes data into the new Notes field.
res.partner contacts and associate them with the opportunity. Display as view-only cards (Name, Contact Type, Relationship, Title, Mobile, Email). Click to open full contact record. Remove associations. Sort alphabetically. Quick-add new contacts if they don't exist. Associations persist to res.partner when authorized. Required when "Self Initiated Inquiry" is NOT checked.
representative_* fields (firstname, lastname, address, email, phone, mobile, notes). Visible only when partner_relationship != 'self'. On conversion, res_partner.py create() (lines 660–690) creates a child contact (partner_type='family_member') from these flat fields and sets crm_id.partner_relative_id to the new child's ID. The same child-creation code is commented out in _handle_partner_assignment().
- Remove from form: Representative Details tab, all 12
representative_*field displays (keep fields on model for data integrity) - Remove from Python: Child contact creation in
res_partner.py create()(lines 660–690),partner_relative_idassignment (line 698), commented-out representative block in_handle_partner_assignment() - Build new: Many2many field (e.g.
related_contact_ids) on crm.lead → res.partner, with junction table storing relationship type per link - New tab: Kanban-style view-only cards with search/link widget and quick-add button
- Persistence: On authorization, copy contact associations to the partner record (design for this depends on VIA's answer — see questions below)
- Data migration: Existing leads with representative data need a migration script to create res.partner records from the flat fields and link them via the new Many2many
res.partner records before the person supported converts. Currently, nobody becomes a res.partner until conversion — all data lives as flat fields on the lead. This means that during intake, the inquiring party would be created as a real contact immediately (via search-and-link or quick-add), while the person they're inquiring about remains as flat fields on the crm.lead until authorization. VIA must confirm this is the intended workflow.
res_partner.py create() and _handle_partner_assignment() must be rewritten. The current child_ids creation (lines 660–690) and the partner_relative_id assignment are both replaced by the Many2many persistence mechanism.
res.partner records during intake — before the person supported is authorized? This is what the spec implies (search existing contacts or quick-add new ones), but it's a change from the current workflow where nobody becomes a contact until conversion.
partner_relationship_ids field on res.partner, (c) create child_ids records as today, or (d) some other mechanism.
partner_relationship field (Self, Mother, Father, etc.) partially serves this role — when relationship is "Self", the person supported IS the inquiring party.
- Add
self_initiated_inquiryBoolean field - When checked: Related Contacts tab is optional
- When unchecked: Related Contacts tab is required (must have at least one contact)
- Determine placement: main form? Person Supported Details tab?
- Consider relationship to
partner_relationshipfield — if Self Initiated, relationship is implicitly "Self"
partner_relationship field? If the person supported is making their own inquiry (Self Initiated = Yes), does that mean partner_relationship should auto-set to "Self"?
partner_relationship from main form
MEDIUM
~2 hrs
partner_relationship field appears in any spec section. The concept is replaced by the Related Contacts tab's contact associations and potentially the Self Initiated Inquiry checkbox.
partner_relationship is on the main form (both lead_partner and opportunity_partner groups). It drives: Representative Details tab visibility, representative field required rules, child contact creation on conversion, is_legal_guardian visibility. It's in the partner→lead sync list. Default: 'self'.
- Hide
partner_relationshipfrom the main form (don't delete — keep on model for data integrity) - Remove its role as Representative tab visibility driver (tab being replaced anyway — RC-A)
- If Self Initiated Inquiry checkbox is added (RC-B), consider auto-setting partner_relationship based on it
- Update partner→lead sync to handle the field being hidden but still synced
partner_relationship is in all 3 sync methods (create, write, _handle_partner_assignment). It also controls child contact creation during _create_customer(). Hiding it from the form is safe, but the model-level logic must be updated to use the new Related Contacts architecture.
- Option A: Build view-only Authorization tab on crm.lead that displays kanban cards from sale.order (requires sale.order integration)
- Option B: Skip the tab on crm.lead, build authorization management entirely in sale.order (simpler, cleaner separation)
- Additional fields mentioned: Payer ID, Payer Program, ICD10 Code, Modifier, EVV — all "consider for sale.order"
pre_auth_details) has: date_referral, referral_meeting_date, project_auth_date, authorized_date, projected_units (left column); support_coordinator, sc_phone_no, pre_auth_region, pre_auth_county_id (right column); pre_auth_notes (bottom).
invisible="1" to preserve inheritance stability, or remove it entirely if no other views inherit from it.
isp_data) shows isp_ids (One2many → via.isp) with product_id and outcome_phrase columns. Conditionally visible when project_auth_date is set. On conversion, ISP records are moved (not copied) from lead to partner.
- Hide the ISP Data tab from the CRM form
- Keep
isp_idsfield on the model — ISP data may still be created and moved during conversion - If ISP data entry is needed, it would happen on the partner form (Referrals tab) after conversion
via_company_type (PA vs NJ) drives extensive conditional logic: distinguish_region visibility, assessment_date visibility, is_person_graduating visibility, is_legal_guardian conditions, mci_number label/visibility, source_platform default (HCSIS vs iRecord), program_type values (10 PA vs 2 NJ), ma_number required relaxation for NJ.
- Remove company-type conditional visibility rules from the CRM form
- Keep
via_company_typefield on the model (don't delete) - Simplify to PA-only behavior (all PA county/region LOVs, HCSIS default, etc.)
- NJ-only fields (assessment_date, is_person_graduating) become hidden or removed
Export Responses
Click to generate YAML for Claude ingestion. Paste into a new Claude chat to resume implementation planning.
stage_type field has 3 values: introduction, discussion, activation. No automatic stage movement based on date fields. Stages are manually dragged in kanban.
- Create/rename stage records: Inquiry, Discussion, Enrollment, Archive
- Remap
stage_type:introduction→ Inquiry,discussion→ Discussion,activation→ Enrollment - Override
write()to evaluate date fields in reverse priority order on every save:
1. If Opportunity Status = Archive → Archive (overrides all)
2. Ifproject_auth_dateis set → Enrollment
3. Ifdate_referralORreferral_meeting_dateis set → Discussion
4. Ifinquiry_dateis set → Inquiry - Disable kanban drag-and-drop for stage changes (set statusbar
clickable="False", kanbangroup_create="false") - The "activation path" is the overall workflow from Inquiry through to sale.order — not a specific stage
- Required-field enforcement currently on
stage_type == 'activation'transfers to the Enrollment stage or the Activate Authorization button validation
stage_id (hidden when won) and secondary won_stage_id (visible when won, non-clickable). is_won_stage Boolean drives field requirements and partner_relative_id readonly. "Mark Won" and "Mark Lost" buttons are hidden but the won-stage logic is active.
- Remove
won_stage_idstatusbar from the form view - Remove all
is_won_stage-dependent visibility and required rules - Stage-based required attributes stay — update from
stage_type == 'activation' or is_won_stage == Truetostage_type == 'enrollment'. This preserves the greyish-blue visual indicator on required fields and prevents saving without them once the lead reaches Enrollment. Same mechanism as today, remapped to the new stage. - The Activate Authorization button (D-C) adds a second validation gate for the spec's starred fields before launching sale.order.
- Keep "Mark Won"/"Mark Lost" hidden (already done)
Left: Opportunity Name, Person Supported (auto from tab), Service Team, Service Line, Inquiry Date, Referral Date, Referral Meeting Date, Projected Auth Date, Projected Units, Funding Source.
Right: Activate Authorization button, Service Type, Supports Coordinator (Many2one → res.partner), SC phone/email (related), County of Service, Region of Service.
Bottom: Opportunity Status, Assigned to, Tags.
lead_info group: gender, distinguish_region, assessment_date, graduating, legal_guardian, service_ids, company_type, internal_unique_id, ma_number, mci_number, program_type, funding_source, status_of_plan, plan dates, status, plan_tier, payer, payer_program, function, source_platform, partner_relative_id.
Most demographic fields are on the main form, not in tabs.
- ~25 fields removed from main form → moved to Person Supported Details tab
- ~5 new fields added to main form (Service Team, Service Line, Inquiry Date, Opportunity Status, Assigned to)
- Several existing fields repositioned (funding_source, projected_units stay; plan dates stay)
- Opportunity vs Lead type distinction may need revisiting — spec doesn't reference the type='lead' vs type='opportunity' split
via_tab_person_supported_details) that manages the same-named tab on BOTH the crm.lead and res.partner forms. Demographics fields moved from the main form will land in this shared tab module's crm.lead view XML, not inline in via_crm. This is already deployed on dev (commit d29bc20).
The Odoo base CRM form defines two parallel layouts controlled by the type field:
lead_partner+lead_infogroups → visible whentype = 'lead'opportunity_partnergroup → visible whentype = 'opportunity'
VIA's current code injects the same contact fields into both layouts (name, DOB, email, phone, etc. are xpath'd into lead_partner AND opportunity_partner separately). This means both types already show essentially the same form — the dual layout is maintenance overhead with no functional benefit.
Database evidence (dev): INQUIRY stage has 8 leads and 9 opportunities — both types exist because the type is set by which menu was used to create the record, not by any business rule. No VIA code flips the type automatically.
Automation check: Both VIA automated actions on crm.lead ("Initial outreach: PDS" and "Auto-advance to Discussion") filter by stage and date fields only — neither checks the type field. All automations fire identically for leads and opportunities.
Server actions: Zero custom server actions are bound to crm.lead. The standard "Convert to Opportunity" and "Mark Won/Lost" buttons are already hidden by VIA's view customizations.
Recommendation: Unify to a single layout with all records as type='opportunity'. This eliminates the duplicate field injection, simplifies the restructure (one layout instead of two), and has zero impact on VIA's stages, automations, security rules, or sync logic. The spec already describes a single unified form — this aligns code with intent.
Action needed from VIA: Confirm that all Service Development records should be type='opportunity' from creation. If yes, the restructure is significantly simpler. If VIA wants to keep the lead/opportunity distinction for any reason (reporting, filtering, etc.), please explain the use case so we can accommodate it.
inquiry_date field exists on crm.lead. date_referral exists (currently on Pre-Authorization tab).
inquiry_date Date field to the main form left column. Connect to stage auto-advance logic (D-A).
type='lead' anywhere.
type field ('lead' vs 'opportunity'). VIA's form has separate field groups for each: lead_partner, lead_info (type=lead) and opportunity_partner (type=opportunity). Many fields are conditionally visible based on type. The lead_priority group hides tag_ids and priority for opportunities.
- Set all SD records to
type='opportunity'(or configure CRM settings to skip lead stage) - Remove type-conditional visibility rules from the form view
- Consolidate field groups into the single spec layout
- Keep
typefield on the model but always default to 'opportunity' for SD
- Contact info: firstname, lastname, email, phone, mobile, address block
- Demographics: DOB, gender, ma_number, internal_unique_id_code, sex, sexual_orientation, race, ethnicity, primary_language, religion, disabilities, mobility, living_arrangement
- Removed from form entirely (not in spec): contact_name, title, partner_relationship, assessment_date, is_person_graduating, is_legal_guardian, mci_number, status_of_plan, plan_tier, payer, payer_program, function, source_platform, partner_relative_id
payer, payer_program (Sandata/billing), ma_number (PROMISe), source_platform (HCSIS/iRecord). These must remain accessible — either on a tab or on sale.order. Cannot simply delete them.
VIA CRM Lead (Service Development) — Gap Analysis Review
Comparing current Odoo 17 CRM form against VIA Service Development spec · Generated March 14, 2026 · Task fe7887bf
0 of 0 items reviewed
sale.order form. Fields marked with * (Service Type, County of Service, Region of Service, Last Name, First Name, MA Number, Internal ID) must be filled before clicking.
action_sale_quotations_new() handle the transition to sale.order. The _handle_partner_assignment() method syncs data lead→partner when a quotation is created.
- Layer 1 — Stage-based required attributes: Billing-critical fields (payer, payer_program, plan dates, priority) keep their
requiredattribute in the XML, updated torequired="stage_type == 'enrollment'". This preserves the greyish-blue fill color on required fields and prevents saving without them once the lead reaches Enrollment. Same mechanism as today, remapped fromactivationtoenrollment. - Layer 2 — Button-click validation: The Activate Authorization button validates the spec's starred fields (Service Type, County/Region of Service, Last Name, First Name, MA Number, Internal ID) before launching sale.order. Some of these overlap with stage-required fields; others (like Service Type, County/Region) may not be stage-required but are needed for authorization.
- Button behavior (confirmed): Button is always visible but greyed out (disabled) until all required fields are filled. Uses
invisibleorreadonlyattribute with a condition checking the starred fields. - On click: launches sale.order form, triggers
_handle_partner_assignment()to sync 50+ fields lead→partner - Replaces the current quotation-from-won-stage flow (
action_sale_quotations_new())
_handle_partner_assignment() still fires on button click (it syncs 50+ fields lead→partner). The button's Python method should call the existing sync logic before opening sale.order.
crm.crm_case_kanban_view_leads. Standard kanban shows opportunity name, expected revenue, tags, activity icons, partner name.
active Boolean for archiving. VIA hides "Mark Lost" (which normally archives). No "Archive" stage exists. Current status field has values: pre_auth, authorized, inactive — no "Archive" value.
- Add "Archive" stage to the pipeline (Stage 4)
- Build an Archive wizard (modeled on VIA's existing employee departure wizard):
—archive_reason_id: Many2one to a configurable reason model (VIA can add/edit reasons without code)
—archive_description: Free text, required ("Give more details about why this opportunity is being archived")
—archive_date: Auto-set to today or user-selectable - On archive: cancel all open/scheduled activities for this lead
- No follower notification on archive (not required at this time)
- Allow reactivation: move back from Archive stage, clear archive fields, restore to the stage matching current date field state
- Add search filter for archived opportunities
- Separate project: Automated follow-up cadences (chained event follow-up for non-responsive leads, escalating intervals) are out of scope for this form restructure. Leads that don't convert should eventually enter a long-tail follow-up sequence, but that's a CRM workflow/marketing automation project.
hr.employee, required. If different from "Entered by" (auto-set to creating user), send notification.
user_id (Salesperson, Many2one → res.users). No hr.employee-based assignment field exists. No "Entered by" field exists on crm.lead currently.
- Add
assigned_employee_idfield (Many2one → hr.employee, required) - Add
entered_by_user_idfield (Many2one → res.users, default=current user, readonly) - Build notification logic: if assigned employee's user differs from entered_by, send message
- Decide relationship to existing
user_id(Salesperson) — keep, hide, or replace?
user_id (res.users) for assignment. The spec wants hr.employee. Should we keep user_id as well (some Odoo features depend on it), or hide it and use only the new employee field? What notification method — Odoo chatter message, email, or both?
Service Line: Selection LOV filtered by Service Team — Supports Broker (PDS only), HTTS (PDS), IHCS (HCBS), Residential (HCBS). "Cannot appear in multi-select" note for Supports Broker.
service_team or service_line fields exist on crm.lead. Odoo base has team_id (Many2one → crm.team) which is used for the sales team — different concept.
- Add
service_teamselection field (PDS, HCBS) - Add
service_lineselection field with 4 values - Implement filtering: Service Line options change based on Service Team selection
- Determine relationship to existing
team_id(crm.team) — keep both? Map service_team to team_id?
team_id (crm.team record), or be a completely separate field?
service_ids is a Many2many → product.product with many2many_tags widget. This allows MULTIPLE services. The spec says "Only 1 Service Type per opportunity" — fundamentally different data model (Selection vs Many2many).
- Add new
service_typeselection field with the 16 spec values (including procedure codes as technical keys or separate field) - Keep or hide existing
service_idsMany2many — don't delete to preserve historical data - New field enforces single selection per opportunity (as spec requires: "one per service type")
- Procedure codes may need to be stored for billing integration
support_coordinator(Char) andsc_phone_no(Char) — free-text fields on the crm.lead Pre-Authorization tab, synced via all 3 lead↔partner sync methods- Studio action 8955 — provides a smart button on the SC's own res.partner form that links SC → their assigned Person Supported records. This is a Studio-built relational feature, not just a display field.
- Studio
x_studio_support_coordinatorfield — a Studio-created field on res.partner that stores the SC relationship from the partner side. Studio also adds a "Referrals" tab on the SC's partner form showing linked records.
- On crm.lead: Add
supports_coordinator_id(Many2one → res.partner, domain:partner_type = 'support_coordinator'). Add related fieldssc_phone_relatedandsc_email_relatedauto-populated from the linked partner. Move from Pre-Authorization tab to main form right column. - On res.partner (SC's form): Add code-based smart button replacing Studio action 8955. Add One2many tab showing the SC's assigned Person Supported records (replacing Studio's Referrals tab on the SC form).
- Studio cleanup: Deactivate Studio action 8955. Remove
x_studio_support_coordinatorfield after data migration to the new Many2one. - Sync update: Update all 3 sync methods (create, write, _handle_partner_assignment) to sync the Many2one
supports_coordinator_idinstead of the Charsupport_coordinator. - Data migration: Map existing Char values and Studio field data to res.partner records. Keep old Char fields hidden temporarily until migration is verified.
support_coordinator Char field is in the lead↔partner sync list. Changing to Many2one requires updating all 3 sync methods plus the partner create method. The Studio x_studio_support_coordinator field may also have downstream references that need auditing before removal.
date_referral, referral_meeting_date, project_auth_date, projected_units, authorized_date are all on the Pre-Authorization tab (group_1_pre_auth).
authorized_date is NOT in the spec's main form — clarify if it's still needed.
authorized_date field exists on Pre-Auth tab but is NOT listed in the spec's main form. Is it being removed, or should it remain somewhere? Also: project_auth_date currently controls ISP Data tab visibility — this logic must be preserved even if the field moves.
pre_auth_county_id (Many2one → via.res.county) and pre_auth_region (Selection: western, central, north_east, south_east) exist on the Pre-Authorization tab. The existing distinguish_region on lead_info has the same 4 values with slightly different keys. The USPS module already has the county→region mapping for all 67 PA counties.
- Move
pre_auth_county_idandpre_auth_regionfrom Pre-Auth tab to main form right column - Rename labels to "County of Service" and "Region of Service"
- Implement auto-populate: when County is selected, Region fills automatically using the USPS module's county→region mapping
- Resolve
distinguish_regionkey mismatch (westernon lead vswestin spec/partner) — spec says "West" - Make both required for Activate Authorization button
status field has values: pre_auth (Pre-Auth), authorized (Authorized), inactive (Inactive). Completely different set of values.
- Replace
statusLOV values with:active,hold,archive - Migrate existing data: map
pre_auth→active,authorized→ ?,inactive→archive - Move field position to bottom section of main form
- "Archive" value triggers stage movement (ties to D-E)
status field is in the partner→lead sync list. Partner has completely different values (active, inactive, discharged, deceased). The sync already has a mismatch — this change creates a third set of values. The sync logic for this field needs explicit handling.
Left: Last Name*, First Name*, Email, Mobile, Phone, Address (Line 1, Line 2, City/State/Zip), County of Residence, Region of Residence.
Right: DOB, MA Number*, Internal ID*, Sex, Gender, Sexual Orientation, Race, Ethnicity, Primary Language, Religion, Dev Disability, Intellectual Disability, Communication Plan, Mobility, Living Arrangement.
demo_graphics) has: 2-column layout with race/ethnicity (col 1), sexual_orientation/religion (col 2), then radio widgets for disabilities, ethnicity HTML checkbox table, and notes. Contact info and demographics fields are split between main form and this tab.
- Rename tab from "Demographics" to "Person Supported Details"
- Restructure to 2-column layout per spec
- Move contact info (name, email, phone, mobile, address) from main form into left column
- Move demographic fields (DOB, ma_number, etc.) from main form into right column
- Add County/Region of Residence (distinct from County/Region of Service on main form)
- Remove radio widgets — all fields become standard Selection dropdowns per spec
- Remove ethnicity HTML checkbox table
- Remove duplicate
ethnicitydisplay and notes field from this tab
PS-2: If no match, prompt to create a new res.partner record via quick-add button.
partner_id (Many2one → res.partner) exists on the form header, hidden in lead mode, visible in opportunity mode. When set, partner→lead sync copies ~50 fields. firstname/lastname are computed from partner_id but also stored/editable.
- Implement onchange on firstname/lastname that searches res.partner for matches
- Show matching contacts as a dropdown or dialog for selection
- On selection: set
partner_id, which triggers existing partner→lead sync (all ~50 fields auto-populate) - If no match: show "Create New Contact" button that creates res.partner and links it
- This replaces the current hidden
partner_idfield approach — the user never directly interacts with partner_id
ethnicity field displayed as checkbox widget.
- Change all radio widgets to standard Selection dropdowns
- Remove the ethnicity detail HTML checkbox table (8 Boolean fields)
- Remove duplicate ethnicity field display
- Keep the Boolean fields on the model (don't delete data) but hide from the form
Religion: Christian, Buddhist, Hindu, Muslim, Jewish, Sikh, None, Other, Prefer not to say.
native_other, partner has native_hawaiian. Lead has prefer_to_self_describe, partner has self_described. Spec says "Self-Described" (matches partner). Lead missing two_or_more. Spec says "Other" (matches partner's other), not lead's other_race_ethnicity_or_origin.Religion mismatches: Lead has
no_religion, partner has none. Spec says "None" (matches partner). Lead has another, partner has other. Spec says "Other" (matches partner).
- Update crm.lead LOV technical keys to match the spec (which aligns with res.partner):
- Race:
native_other→native_hawaiian,other_race_ethnicity_or_origin→other,prefer_to_self_describe→self_described - Religion:
no_religion→none,another→other - Region:
western→west(if distinguish_region kept) - Add missing
two_or_moreto race on lead (spec doesn't include it — confirm) - Data migration: update existing records with old keys to new keys
- This fixes the long-standing LOV mismatch issue documented in Session 3
county_id (Many2one → via.res.county) exists on the address block. distinguish_region exists on lead_info. These are separate from pre_auth_county_id / pre_auth_region (which are "County/Region of Service" on main form). The spec distinguishes residence from service counties.
- Use existing
county_idas County of Residence (already on address block) - Use
distinguish_regionas Region of Residence (already computed/stored) - Move both to Person Supported Details tab, left column, after address
- Implement auto-populate Region from County (same as County of Service, using USPS module mapping)
1. "What information are you looking for?" (Text, auto-populate from web inquiry form)
2. "What services are you looking for?" (Text, auto-populate from web inquiry form)
3. "Notes" (Text, free form)
4. "Entered by" (Many2one → res.users, auto-populates to logged-in user who creates the opportunity)
internal_notes tab (page name internal_notes) with description_sale field. No "What information are you looking for?" or "What services are you looking for?" fields exist. pre_auth_notes exists but is shared between Demographics and Pre-Auth tabs. No "Entered by" field on crm.lead.
- Add 2 new Text fields:
inquiry_info_looking_for,inquiry_services_looking_for - Add
entered_by_user_id(Many2one → res.users, default=current user, readonly) — same field as D-F if combined - Keep or repurpose existing
internal_notes/description_saleas the "Notes" free text - Web inquiry form auto-population requires website form integration (separate scope)
pre_auth_notes field from Demographics/Pre-Auth
LOW
~0.5 hrs
pre_auth_notes appears on BOTH the Demographics tab AND the Pre-Authorization tab — same database field displayed twice. Editing in either location changes the same value.
pre_auth_notes from the Demographics and Pre-Authorization tabs. If the Notes tab uses a different field, decide whether to migrate existing pre_auth_notes data into the new Notes field.
res.partner contacts and associate them with the opportunity. Display as view-only cards (Name, Contact Type, Relationship, Title, Mobile, Email). Click to open full contact record. Remove associations. Sort alphabetically. Quick-add new contacts if they don't exist. Associations persist to res.partner when authorized. Required when "Self Initiated Inquiry" is NOT checked.
representative_* fields (firstname, lastname, address, email, phone, mobile, notes). Visible only when partner_relationship != 'self'. On conversion, res_partner.py create() (lines 660–690) creates a child contact (partner_type='family_member') from these flat fields and sets crm_id.partner_relative_id to the new child's ID. The same child-creation code is commented out in _handle_partner_assignment().
- Remove from form: Representative Details tab, all 12
representative_*field displays (keep fields on model for data integrity) - Remove from Python: Child contact creation in
res_partner.py create()(lines 660–690),partner_relative_idassignment (line 698), commented-out representative block in_handle_partner_assignment() - Build new: Many2many field (e.g.
related_contact_ids) on crm.lead → res.partner, with junction table storing relationship type per link - New tab: Kanban-style view-only cards with search/link widget and quick-add button
- Persistence: On authorization, copy contact associations to the partner record (design for this depends on VIA's answer — see questions below)
- Data migration: Existing leads with representative data need a migration script to create res.partner records from the flat fields and link them via the new Many2many
res.partner records before the person supported converts. Currently, nobody becomes a res.partner until conversion — all data lives as flat fields on the lead. This means that during intake, the inquiring party would be created as a real contact immediately (via search-and-link or quick-add), while the person they're inquiring about remains as flat fields on the crm.lead until authorization. VIA must confirm this is the intended workflow.
res_partner.py create() and _handle_partner_assignment() must be rewritten. The current child_ids creation (lines 660–690) and the partner_relative_id assignment are both replaced by the Many2many persistence mechanism.
res.partner records during intake — before the person supported is authorized? This is what the spec implies (search existing contacts or quick-add new ones), but it's a change from the current workflow where nobody becomes a contact until conversion.
partner_relationship_ids field on res.partner, (c) create child_ids records as today, or (d) some other mechanism.
partner_relationship field (Self, Mother, Father, etc.) partially serves this role — when relationship is "Self", the person supported IS the inquiring party.
- Add
self_initiated_inquiryBoolean field - When checked: Related Contacts tab is optional
- When unchecked: Related Contacts tab is required (must have at least one contact)
- Determine placement: main form? Person Supported Details tab?
- Consider relationship to
partner_relationshipfield — if Self Initiated, relationship is implicitly "Self"
partner_relationship field? If the person supported is making their own inquiry (Self Initiated = Yes), does that mean partner_relationship should auto-set to "Self"?
partner_relationship from main form
MEDIUM
~2 hrs
partner_relationship field appears in any spec section. The concept is replaced by the Related Contacts tab's contact associations and potentially the Self Initiated Inquiry checkbox.
partner_relationship is on the main form (both lead_partner and opportunity_partner groups). It drives: Representative Details tab visibility, representative field required rules, child contact creation on conversion, is_legal_guardian visibility. It's in the partner→lead sync list. Default: 'self'.
- Hide
partner_relationshipfrom the main form (don't delete — keep on model for data integrity) - Remove its role as Representative tab visibility driver (tab being replaced anyway — RC-A)
- If Self Initiated Inquiry checkbox is added (RC-B), consider auto-setting partner_relationship based on it
- Update partner→lead sync to handle the field being hidden but still synced
partner_relationship is in all 3 sync methods (create, write, _handle_partner_assignment). It also controls child contact creation during _create_customer(). Hiding it from the form is safe, but the model-level logic must be updated to use the new Related Contacts architecture.
- Option A: Build view-only Authorization tab on crm.lead that displays kanban cards from sale.order (requires sale.order integration)
- Option B: Skip the tab on crm.lead, build authorization management entirely in sale.order (simpler, cleaner separation)
- Additional fields mentioned: Payer ID, Payer Program, ICD10 Code, Modifier, EVV — all "consider for sale.order"
pre_auth_details) has: date_referral, referral_meeting_date, project_auth_date, authorized_date, projected_units (left column); support_coordinator, sc_phone_no, pre_auth_region, pre_auth_county_id (right column); pre_auth_notes (bottom).
invisible="1" to preserve inheritance stability, or remove it entirely if no other views inherit from it.
isp_data) shows isp_ids (One2many → via.isp) with product_id and outcome_phrase columns. Conditionally visible when project_auth_date is set. On conversion, ISP records are moved (not copied) from lead to partner.
- Hide the ISP Data tab from the CRM form
- Keep
isp_idsfield on the model — ISP data may still be created and moved during conversion - If ISP data entry is needed, it would happen on the partner form (Referrals tab) after conversion
via_company_type (PA vs NJ) drives extensive conditional logic: distinguish_region visibility, assessment_date visibility, is_person_graduating visibility, is_legal_guardian conditions, mci_number label/visibility, source_platform default (HCSIS vs iRecord), program_type values (10 PA vs 2 NJ), ma_number required relaxation for NJ.
- Remove company-type conditional visibility rules from the CRM form
- Keep
via_company_typefield on the model (don't delete) - Simplify to PA-only behavior (all PA county/region LOVs, HCSIS default, etc.)
- NJ-only fields (assessment_date, is_person_graduating) become hidden or removed
Export Responses
Click to generate YAML for Claude ingestion. Paste into a new Claude chat to resume implementation planning.
stage_type field has 3 values: introduction, discussion, activation. No automatic stage movement based on date fields. Stages are manually dragged in kanban.
- Create/rename stage records: Inquiry, Discussion, Enrollment, Archive
- Remap
stage_type:introduction→ Inquiry,discussion→ Discussion,activation→ Enrollment - Override
write()to evaluate date fields in reverse priority order on every save:
1. If Opportunity Status = Archive → Archive (overrides all)
2. Ifproject_auth_dateis set → Enrollment
3. Ifdate_referralORreferral_meeting_dateis set → Discussion
4. Ifinquiry_dateis set → Inquiry - Disable kanban drag-and-drop for stage changes (set statusbar
clickable="False", kanbangroup_create="false") - The "activation path" is the overall workflow from Inquiry through to sale.order — not a specific stage
- Required-field enforcement currently on
stage_type == 'activation'transfers to the Enrollment stage or the Activate Authorization button validation
stage_id (hidden when won) and secondary won_stage_id (visible when won, non-clickable). is_won_stage Boolean drives field requirements and partner_relative_id readonly. "Mark Won" and "Mark Lost" buttons are hidden but the won-stage logic is active.
- Remove
won_stage_idstatusbar from the form view - Remove all
is_won_stage-dependent visibility and required rules - Stage-based required attributes stay — update from
stage_type == 'activation' or is_won_stage == Truetostage_type == 'enrollment'. This preserves the greyish-blue visual indicator on required fields and prevents saving without them once the lead reaches Enrollment. Same mechanism as today, remapped to the new stage. - The Activate Authorization button (D-C) adds a second validation gate for the spec's starred fields before launching sale.order.
- Keep "Mark Won"/"Mark Lost" hidden (already done)
Left: Opportunity Name, Person Supported (auto from tab), Service Team, Service Line, Inquiry Date, Referral Date, Referral Meeting Date, Projected Auth Date, Projected Units, Funding Source.
Right: Activate Authorization button, Service Type, Supports Coordinator (Many2one → res.partner), SC phone/email (related), County of Service, Region of Service.
Bottom: Opportunity Status, Assigned to, Tags.
lead_info group: gender, distinguish_region, assessment_date, graduating, legal_guardian, service_ids, company_type, internal_unique_id, ma_number, mci_number, program_type, funding_source, status_of_plan, plan dates, status, plan_tier, payer, payer_program, function, source_platform, partner_relative_id.
Most demographic fields are on the main form, not in tabs.
- ~25 fields removed from main form → moved to Person Supported Details tab
- ~5 new fields added to main form (Service Team, Service Line, Inquiry Date, Opportunity Status, Assigned to)
- Several existing fields repositioned (funding_source, projected_units stay; plan dates stay)
- Opportunity vs Lead type distinction may need revisiting — spec doesn't reference the type='lead' vs type='opportunity' split
via_tab_person_supported_details) that manages the same-named tab on BOTH the crm.lead and res.partner forms. Demographics fields moved from the main form will land in this shared tab module's crm.lead view XML, not inline in via_crm. This is already deployed on dev (commit d29bc20).
The Odoo base CRM form defines two parallel layouts controlled by the type field:
lead_partner+lead_infogroups → visible whentype = 'lead'opportunity_partnergroup → visible whentype = 'opportunity'
VIA's current code injects the same contact fields into both layouts (name, DOB, email, phone, etc. are xpath'd into lead_partner AND opportunity_partner separately). This means both types already show essentially the same form — the dual layout is maintenance overhead with no functional benefit.
Database evidence (dev): INQUIRY stage has 8 leads and 9 opportunities — both types exist because the type is set by which menu was used to create the record, not by any business rule. No VIA code flips the type automatically.
Automation check: Both VIA automated actions on crm.lead ("Initial outreach: PDS" and "Auto-advance to Discussion") filter by stage and date fields only — neither checks the type field. All automations fire identically for leads and opportunities.
Server actions: Zero custom server actions are bound to crm.lead. The standard "Convert to Opportunity" and "Mark Won/Lost" buttons are already hidden by VIA's view customizations.
Recommendation: Unify to a single layout with all records as type='opportunity'. This eliminates the duplicate field injection, simplifies the restructure (one layout instead of two), and has zero impact on VIA's stages, automations, security rules, or sync logic. The spec already describes a single unified form — this aligns code with intent.
Action needed from VIA: Confirm that all Service Development records should be type='opportunity' from creation. If yes, the restructure is significantly simpler. If VIA wants to keep the lead/opportunity distinction for any reason (reporting, filtering, etc.), please explain the use case so we can accommodate it.
inquiry_date field exists on crm.lead. date_referral exists (currently on Pre-Authorization tab).
inquiry_date Date field to the main form left column. Connect to stage auto-advance logic (D-A).
type='lead' anywhere.
type field ('lead' vs 'opportunity'). VIA's form has separate field groups for each: lead_partner, lead_info (type=lead) and opportunity_partner (type=opportunity). Many fields are conditionally visible based on type. The lead_priority group hides tag_ids and priority for opportunities.
- Set all SD records to
type='opportunity'(or configure CRM settings to skip lead stage) - Remove type-conditional visibility rules from the form view
- Consolidate field groups into the single spec layout
- Keep
typefield on the model but always default to 'opportunity' for SD
- Contact info: firstname, lastname, email, phone, mobile, address block
- Demographics: DOB, gender, ma_number, internal_unique_id_code, sex, sexual_orientation, race, ethnicity, primary_language, religion, disabilities, mobility, living_arrangement
- Removed from form entirely (not in spec): contact_name, title, partner_relationship, assessment_date, is_person_graduating, is_legal_guardian, mci_number, status_of_plan, plan_tier, payer, payer_program, function, source_platform, partner_relative_id
payer, payer_program (Sandata/billing), ma_number (PROMISe), source_platform (HCSIS/iRecord). These must remain accessible — either on a tab or on sale.order. Cannot simply delete them.