Files
Odoo-Modules/fusion_service_charges/__manifest__.py
gsinghpal ecca8e357f feat(billing): seed Westin/Mobility service charges on first install only
New module `fusion_service_charges` that creates the standard
service-billing product catalog for Westin Healthcare and Mobility
Specialties:

  Standard Service
    SVC-STD-CALL       Service Call (incl. 30 min)         $95
    SVC-STD-LABOUR     Standard Labour (hourly)            $85
    SVC-INSHOP-LABOUR  In-Shop Labour (hourly)             $75
    SVC-RUSH-CALL      Rush Service Call                   $120
    SVC-AH-CALL        After-Hours Service Call            $140
  Lift & Elevating
    SVC-LIFT-CALL      Lift Service Call (incl. 30 min)    $160
    SVC-LIFT-LABOUR    Lift Labour (hourly)                $110
  Delivery / Pickup
    DEL-LOCAL          Local (within Brampton)             $35
    DEL-OUT            Outside Local Area                  $60
    DEL-RUSH           Rush Delivery / Pickup              $60
    DEL-LIFT-CHAIR     Lift Chair Delivery + Set-up        $120
    DEL-HOSP-BED       Hospital Bed Delivery + Set-up      $120
    DEL-STAIRLIFT      Stairlift Delivery + Set-up         $300
    SVC-STAIRLIFT-RM   Stairlift Removal                   $300

Loading pattern (intentional):

- Products created via post_init_hook on FIRST install only.
- Manifest's `data` list is EMPTY so no XML is loaded on `-u`.
- Hook is idempotent — sentinel ir.model.data xmlid check skips
  records that already exist. Safe to re-run.
- User edits / deletes survive every upgrade (proven on entech-
  westin: edited SVC-STD-CALL price to $999.99 → ran -u → price
  stuck. Reset to $95 after test.).
- Uninstall + reinstall does re-seed (ir.model.data sentinels drop
  on uninstall, fresh install treats it as new).

Per-km surcharges (Rush, Outside Local, After-Hours) are noted in
the product description so the dispatcher knows to add a separate
mileage line. Formula-based pricelist for auto-mileage is out of
scope — matches current manual workflow on both shops.

Odoo 19 compatibility: dropped uom_po_id from the create vals
(retired in 18; uom_id is now the single source of truth for sale
and purchase UoM on product.template).

Deployed and verified on:
- odoo-westin / westin-v19 (Docker: odoo-dev-app)   — 14 products
- odoo-mobility / mobility (Docker: odoo-mobility-app) — 14 products

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

59 lines
2.1 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
{
'name': 'Fusion — Service Charges',
'version': '19.0.1.0.0',
'category': 'Sales',
'summary': (
'Standard service-call, labour, delivery, and installation '
'products for Westin Healthcare and Mobility Specialties.'
),
'description': """
Fusion — Service Charges
==========================
Seeds the service-billing product catalog used by Westin Healthcare
and Mobility Specialties:
* Standard Service: Service Call, Labour (hourly), In-Shop Labour,
Rush Service Call, After-Hours Service Call
* Lift & Elevating Service: Service Call, Labour (hourly)
* Delivery / Pickup: Local, Outside Local Area, Rush, Lift Chair
set-up, Hospital Bed set-up, Stairlift set-up, Stairlift Removal
Loading pattern (deliberate):
* Products are created via post_init_hook on FIRST install only.
* No data XML is registered in the manifest, so ``-u`` upgrades
never touch the records. Edits and deletions made by sales/ops
survive every upgrade.
* The hook is idempotent — sentinel xmlid check skips already-
seeded products. Re-running the hook by hand is safe.
* Re-installing the module after uninstall re-creates the products
(the ir.model.data sentinels go away on uninstall, so the next
install treats it as fresh).
Per-km surcharges (Rush, Outside Local) ARE captured on the
product as a hint in the product description; actual km billing
is left as a manual SO-line tweak by the dispatcher (matches
current shop workflow — formula-based pricing would need a
sale.order.line.onchange to compute, out of scope here).
""",
'author': 'Nexa Systems Inc.',
'website': 'https://www.nexasystems.ca',
'license': 'OPL-1',
'depends': [
'product',
'uom',
],
# Empty on purpose — no data XML. See the docstring on
# _seed_service_charges_once() for why every product is created
# imperatively via the post_init_hook.
'data': [],
'post_init_hook': 'post_init_hook',
'installable': True,
'application': False,
'auto_install': False,
}