736 lines
44 KiB
Python
736 lines
44 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2024-2026 Nexa Systems Inc.
|
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
|
|
|
import base64
|
|
import json
|
|
import logging
|
|
import xml.etree.ElementTree as ET
|
|
from collections import OrderedDict
|
|
from datetime import datetime
|
|
|
|
from odoo import api, models
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FusionXmlParser(models.AbstractModel):
|
|
"""Utility to parse ADP application XML files and create/update
|
|
client profiles and application data records.
|
|
|
|
Captures ALL ~300 XML fields for round-trip export fidelity.
|
|
"""
|
|
_name = 'fusion.xml.parser'
|
|
_description = 'ADP XML Parser'
|
|
|
|
# ------------------------------------------------------------------
|
|
# PUBLIC API
|
|
# ------------------------------------------------------------------
|
|
@api.model
|
|
def parse_from_binary(self, binary_data, sale_order=None):
|
|
"""Parse from binary field (base64 encoded).
|
|
Returns tuple (profile, application_data) or (False, False).
|
|
"""
|
|
if not binary_data:
|
|
return False, False
|
|
try:
|
|
xml_content = base64.b64decode(binary_data).decode('utf-8')
|
|
except Exception as e:
|
|
_logger.warning('Failed to decode XML binary: %s', e)
|
|
return False, False
|
|
return self.parse_and_create(xml_content, sale_order)
|
|
|
|
@api.model
|
|
def parse_and_create(self, xml_content, sale_order=None):
|
|
"""Parse raw XML string, create/update profile and application data.
|
|
Returns tuple (profile, application_data) or (False, False).
|
|
"""
|
|
try:
|
|
root = ET.fromstring(xml_content)
|
|
except ET.ParseError as e:
|
|
_logger.warning('Failed to parse ADP XML: %s', e)
|
|
return False, False
|
|
|
|
form = root.find('Form')
|
|
if form is None:
|
|
form = root
|
|
|
|
# Step 1: Build complete JSON dict (every field, dot-notation keys)
|
|
json_dict = self._xml_to_json(form)
|
|
|
|
# Step 2: Extract individual model fields from JSON
|
|
model_vals = self._json_to_model_vals(json_dict)
|
|
model_vals['raw_xml'] = xml_content
|
|
model_vals['xml_data_json'] = json.dumps(json_dict, ensure_ascii=False)
|
|
|
|
# Step 3: Create/update profile
|
|
profile = self._find_or_create_profile(model_vals, sale_order)
|
|
|
|
# Step 4: Create application data record
|
|
model_vals['profile_id'] = profile.id
|
|
model_vals['sale_order_id'] = sale_order.id if sale_order else False
|
|
app_data = self.env['fusion.adp.application.data'].create(model_vals)
|
|
|
|
return profile, app_data
|
|
|
|
@api.model
|
|
def reparse_existing(self, app_data_record):
|
|
"""Re-parse an existing application data record from its raw_xml.
|
|
Updates all fields in place without creating a new record.
|
|
"""
|
|
if not app_data_record.raw_xml:
|
|
return False
|
|
try:
|
|
root = ET.fromstring(app_data_record.raw_xml)
|
|
except ET.ParseError as e:
|
|
_logger.warning('Failed to re-parse XML: %s', e)
|
|
return False
|
|
|
|
form = root.find('Form')
|
|
if form is None:
|
|
form = root
|
|
|
|
json_dict = self._xml_to_json(form)
|
|
model_vals = self._json_to_model_vals(json_dict)
|
|
model_vals['xml_data_json'] = json.dumps(json_dict, ensure_ascii=False)
|
|
|
|
# Remove fields that shouldn't be overwritten
|
|
model_vals.pop('raw_xml', None)
|
|
model_vals.pop('profile_id', None)
|
|
model_vals.pop('sale_order_id', None)
|
|
|
|
app_data_record.write(model_vals)
|
|
|
|
# Also update the linked profile
|
|
if app_data_record.profile_id:
|
|
profile_vals = {}
|
|
if model_vals.get('medical_condition'):
|
|
profile_vals['medical_condition'] = model_vals['medical_condition']
|
|
if model_vals.get('mobility_status'):
|
|
profile_vals['mobility_status'] = model_vals['mobility_status']
|
|
if model_vals.get('applicant_first_name'):
|
|
profile_vals['first_name'] = model_vals['applicant_first_name']
|
|
if model_vals.get('applicant_last_name'):
|
|
profile_vals['last_name'] = model_vals['applicant_last_name']
|
|
assessment = model_vals.get('assessment_date')
|
|
if assessment:
|
|
profile_vals['last_assessment_date'] = assessment
|
|
if profile_vals:
|
|
app_data_record.profile_id.write(profile_vals)
|
|
|
|
return True
|
|
|
|
# ------------------------------------------------------------------
|
|
# STEP 1: XML -> FLAT JSON DICT (every field preserved)
|
|
# ------------------------------------------------------------------
|
|
def _xml_to_json(self, form):
|
|
"""Convert the entire Form element to a flat JSON dict with dot-notation keys."""
|
|
d = OrderedDict()
|
|
d['deviceCategory'] = self._t(form, 'deviceCategory')
|
|
d['VersionNumber'] = self._t(form, 'VersionNumber')
|
|
|
|
# Section 1
|
|
s1 = form.find('section1')
|
|
if s1 is not None:
|
|
for tag in ['applicantLastname', 'applicantFirstname', 'applicantMiddleinitial',
|
|
'healthNo', 'versionNo', 'DateOfBirth', 'nameLTCH',
|
|
'unitNo', 'streetNo', 'streetName', 'rrRoute',
|
|
'city', 'province', 'postalCode',
|
|
'homePhone', 'busPhone', 'phoneExtension']:
|
|
d[f'section1.{tag}'] = self._t(s1, tag)
|
|
cob = s1.find('confirmationOfBenefit')
|
|
if cob is not None:
|
|
for tag in ['q1Yn', 'q1Ifyes', 'q2Yn', 'q3Yn']:
|
|
d[f'section1.confirmationOfBenefit.{tag}'] = self._t(cob, tag)
|
|
|
|
# Section 2
|
|
s2 = form.find('section2')
|
|
if s2 is not None:
|
|
de = s2.find('devicesandEligibility')
|
|
if de is not None:
|
|
for tag in ['condition', 'status', 'none', 'forearm', 'wheeled', 'manual',
|
|
'power', 'addOn', 'scooter', 'seating', 'tiltSystem', 'reclineSystem',
|
|
'legRests', 'frame', 'stroller', 'deviceForearm', 'deviceWheeled',
|
|
'deviceManual', 'deviceAmbulation', 'deviceDependent', 'deviceDynamic',
|
|
'manualDyanmic', 'manualWheelchair', 'powerBase', 'powerScooter',
|
|
'ambulation', 'positioning', 'highTech', 'standingFrame',
|
|
'adpFunded', 'nonADPFunded']:
|
|
d[f'section2.devicesandEligibility.{tag}'] = self._t(de, tag)
|
|
|
|
# Section 2a
|
|
s2a = s2.find('section2a')
|
|
if s2a is not None:
|
|
for tag in ['walker', 'paediatricFrame', 'forearmCrutches', 'none',
|
|
'reason', 'replacementStatus', 'replacementSize', 'replacementADP', 'replacementSpecial',
|
|
'confirmation1', 'confirmation2', 'confirmation3', 'confirmation4', 'confirmation5', 'confirmation6',
|
|
'seatHeight', 'seatHeightmeasurement', 'handleHeight', 'handleHeightmeasurement',
|
|
'handGrips', 'forearm', 'widthHandles', 'widthHandlesmeasurement',
|
|
'clientWeight', 'clientWeightmeasurement',
|
|
'brakes', 'brakeType', 'noWheels', 'wheelSize', 'backSupport',
|
|
'adpWalker', 'adpFrame', 'adpStanding',
|
|
'nonADP1', 'nonADP2', 'nonADP3', 'nonADP4', 'nonADP5', 'nonADP6', 'nonADP7', 'nonADP8', 'nonADP9',
|
|
'setup1', 'setup2', 'setup3', 'setup4', 'setup5', 'setup6', 'setup7', 'setup8', 'setup9',
|
|
'setup10', 'setup11', 'setup12', 'setup13', 'setup14', 'setup15', 'setup16', 'setup17', 'setup18',
|
|
'custom', 'costLabour']:
|
|
d[f'section2.section2a.{tag}'] = self._t(s2a, tag)
|
|
|
|
# Section 2b
|
|
s2b = s2.find('section2b')
|
|
if s2b is not None:
|
|
for tag in ['baseDevice', 'powerAddOndevice',
|
|
'reason', 'replacementStatus', 'replacementSize', 'replacementADP', 'replacementSpecial',
|
|
'confirmation1', 'confirmation2', 'confirmation3', 'confirmation4', 'confirmation5',
|
|
'confirmation6', 'confirmation7', 'confirmation8', 'confirmation9', 'confirmation10',
|
|
'confirmation11', 'confirmation12', 'confirmation13',
|
|
'seatWidth', 'seatWidthmeasurement', 'seatDepth', 'seatDepthmeasurement',
|
|
'floorHeight', 'floorHeightmeasurement', 'caneHeight', 'caneHeightmeasurement',
|
|
'backHeight', 'backHeightmeasurement', 'restLength', 'restLengthmeasurement',
|
|
'clientWeight', 'clientWeightmeasurement',
|
|
'adjustableTension', 'heavyDuty', 'recliner', 'footplates', 'legrests',
|
|
'spoke', 'projected', 'standardManual', 'gradeAids', 'casterPin',
|
|
'amputeeAxle', 'quickRelease', 'stroller', 'oxygen', 'ventilator',
|
|
'titanium', 'clothingGuards', 'oneArm', 'uniLateral', 'plastic',
|
|
'rationale',
|
|
'nonADP1', 'nonADP2', 'nonADP3', 'nonADP4', 'nonADP5', 'nonADP6', 'nonADP7', 'nonADP8', 'nonADP9',
|
|
'setup1', 'setup2', 'setup3', 'setup4', 'setup5', 'setup6', 'setup7', 'setup8', 'setup9',
|
|
'setup10', 'setup11', 'setup12', 'setup13', 'setup14', 'setup15', 'setup16', 'setup17', 'setup18',
|
|
'custom', 'costLabour']:
|
|
d[f'section2.section2b.{tag}'] = self._t(s2b, tag)
|
|
|
|
# Section 2c
|
|
s2c = s2.find('section2c')
|
|
if s2c is not None:
|
|
for tag in ['baseDevice',
|
|
'reason', 'replacementStatus', 'replacementSize', 'replacementADP', 'replacementSpecial',
|
|
'confirmation1', 'confirmation2', 'confirmation3', 'confirmation4', 'confirmation5',
|
|
'seatWidth', 'seatWidthmeasurement', 'backHeight', 'backHeightmeasurement',
|
|
'floorHeight', 'floorHeightmeasurement', 'restLength', 'restLengthmeasurement',
|
|
'seatDepth', 'seatDepthmeasurement', 'clientWeight', 'clientWeightmeasurement',
|
|
'adjustableTension', 'midline', 'manualRecline', 'footplates', 'legrests',
|
|
'swingaway', 'onePiece', 'seatPackage1', 'seatPackage2', 'oxygen', 'ventilator',
|
|
'spControls1', 'spControls2', 'spControls3', 'spControls4', 'spControls5', 'spControls6',
|
|
'autoCorrection', 'rationale',
|
|
'powerTilt', 'powerRecline', 'tiltAndRecline', 'powerElevating', 'ControlBox',
|
|
'nonADP1', 'nonADP2', 'nonADP3', 'nonADP4', 'nonADP5', 'nonADP6', 'nonADP7', 'nonADP8', 'nonADP9',
|
|
'setup1', 'setup2', 'setup3', 'setup4', 'setup5', 'setup6', 'setup7', 'setup8', 'setup9',
|
|
'setup10', 'setup11', 'setup12', 'setup13', 'setup14', 'setup15', 'setup16', 'setup17', 'setup18',
|
|
'custom', 'costLabour']:
|
|
d[f'section2.section2c.{tag}'] = self._t(s2c, tag)
|
|
|
|
# Section 2d
|
|
s2d = s2.find('section2d')
|
|
if s2d is not None:
|
|
for tag in ['seatM', 'seatCF', 'coverM', 'coverCF', 'optionM', 'optionCF', 'hardwareM', 'hardwareCF',
|
|
'adductorM', 'adductorCF', 'pommelCF',
|
|
'backM', 'backCF', 'supportoptionM', 'supportoptionCF', 'backcoverCF', 'backHardwareM', 'backHardwareCF',
|
|
'completeM', 'completeCF',
|
|
'headrestM', 'headrestCF', 'headoptionCF', 'headhardwareM', 'headhardwareCF',
|
|
'beltM', 'beltCF', 'beltoptionCF',
|
|
'armsupportM', 'armsupportCF', 'armoptionM', 'armoptionCF', 'armhardwareM', 'armhardwareCF',
|
|
'trayM', 'trayCF', 'trayoptionM', 'trayoptionCF',
|
|
'lateralsupportM', 'lateralsupportCF', 'lateraloptionCF', 'lateralhardwareCF',
|
|
'footsupportM', 'footsupportCF', 'footoptionM', 'footoptionCF', 'foothardwareM', 'foothardwareCF',
|
|
'reason', 'replacementStatus', 'replacementSize', 'replacementADP', 'replacementSpecial',
|
|
'confirmation1', 'confirmation2',
|
|
'nonADP1', 'nonADP2', 'nonADP3', 'nonADP4', 'nonADP5', 'nonADP6', 'nonADP7', 'nonADP8', 'nonADP9',
|
|
'setup1', 'setup2', 'setup3', 'setup4', 'setup5', 'setup6', 'setup7', 'setup8', 'setup9',
|
|
'setup10', 'setup11', 'setup12', 'setup13', 'setup14', 'setup15', 'setup16', 'setup17', 'setup18',
|
|
'custom', 'costLabour']:
|
|
d[f'section2.section2d.{tag}'] = self._t(s2d, tag)
|
|
|
|
# Section 3
|
|
s3 = form.find('section3')
|
|
if s3 is not None:
|
|
sig = s3.find('sig')
|
|
if sig is not None:
|
|
for tag in ['signature', 'person', 'Date']:
|
|
d[f'section3.sig.{tag}'] = self._t(sig, tag)
|
|
contact = s3.find('contact')
|
|
if contact is not None:
|
|
for tag in ['relationship', 'applicantLastname', 'applicantFirstname', 'applicantMiddleinitial',
|
|
'unitNo', 'streetNo', 'streetName', 'rrRoute',
|
|
'city', 'province', 'postalCode', 'homePhone', 'busPhone', 'phoneExtension']:
|
|
d[f'section3.contact.{tag}'] = self._t(contact, tag)
|
|
|
|
# Section 4
|
|
s4 = form.find('section4')
|
|
if s4 is not None:
|
|
auth = s4.find('authorizer')
|
|
if auth is not None:
|
|
for tag in ['authorizerLastname', 'authorizerFirstname', 'busPhone', 'phoneExtension', 'adpNo', 'signature', 'Date']:
|
|
d[f'section4.authorizer.{tag}'] = self._t(auth, tag)
|
|
vendor = s4.find('vendor')
|
|
if vendor is not None:
|
|
for tag in ['vendorBusName', 'adpVendorRegNo', 'vendorLastfirstname', 'positionTitle', 'vendorLocation', 'busPhone', 'phoneExtension', 'signature', 'Date']:
|
|
d[f'section4.vendor.{tag}'] = self._t(vendor, tag)
|
|
v2 = s4.find('vendor2')
|
|
if v2 is not None:
|
|
for tag in ['vendorBusName', 'adpVendorRegNo', 'vendorLastfirstname', 'positionTitle', 'vendorLocation', 'busPhone', 'phoneExtension', 'signature', 'Date']:
|
|
d[f'section4.vendor2.{tag}'] = self._t(v2, tag)
|
|
eq = s4.find('equipmentSpec')
|
|
if eq is not None:
|
|
d['section4.equipmentSpec.vendorInvoiceNo'] = self._t(eq, 'vendorInvoiceNo')
|
|
d['section4.equipmentSpec.vendorADPRegNo'] = self._t(eq, 'vendorADPRegNo')
|
|
t2 = eq.find('Table2')
|
|
if t2 is not None:
|
|
r1 = t2.find('Row1')
|
|
if r1 is not None:
|
|
for tag in ['Cell1', 'Cell2', 'Cell3', 'Cell4', 'Cell5']:
|
|
d[f'section4.equipmentSpec.Table2.Row1.{tag}'] = self._t(r1, tag)
|
|
pod = s4.find('proofOfDelivery')
|
|
if pod is not None:
|
|
for tag in ['signature', 'receivedBy', 'Date']:
|
|
d[f'section4.proofOfDelivery.{tag}'] = self._t(pod, tag)
|
|
note = s4.find('noteToADP')
|
|
if note is not None:
|
|
for tag in ['section1', 'section2a', 'section2b', 'section2c', 'section2d',
|
|
'section3and4', 'vendorReplacement', 'vendorCustom', 'fundingChart', 'letter']:
|
|
d[f'section4.noteToADP.{tag}'] = self._t(note, tag)
|
|
|
|
return d
|
|
|
|
# ------------------------------------------------------------------
|
|
# STEP 2: JSON DICT -> MODEL FIELD VALUES
|
|
# ------------------------------------------------------------------
|
|
def _json_to_model_vals(self, d):
|
|
"""Map flat JSON dict to fusion.adp.application.data field values."""
|
|
g = d.get # shorthand
|
|
vals = {}
|
|
|
|
# Metadata
|
|
vals['device_category'] = g('deviceCategory', '') or 'MD'
|
|
vals['version_number'] = g('VersionNumber', '')
|
|
|
|
# Section 1 - Applicant
|
|
vals['applicant_last_name'] = g('section1.applicantLastname', '')
|
|
vals['applicant_first_name'] = g('section1.applicantFirstname', '')
|
|
vals['applicant_middle_initial'] = g('section1.applicantMiddleinitial', '')
|
|
vals['health_card_number'] = g('section1.healthNo', '')
|
|
vals['health_card_version'] = g('section1.versionNo', '')
|
|
vals['date_of_birth'] = self._pd(g('section1.DateOfBirth', ''))
|
|
vals['ltch_name'] = g('section1.nameLTCH', '')
|
|
vals['unit_number'] = g('section1.unitNo', '')
|
|
vals['street_number'] = g('section1.streetNo', '')
|
|
vals['street_name'] = g('section1.streetName', '')
|
|
vals['rural_route'] = g('section1.rrRoute', '')
|
|
vals['city'] = g('section1.city', '')
|
|
vals['province'] = g('section1.province', '')
|
|
vals['postal_code'] = g('section1.postalCode', '')
|
|
vals['home_phone'] = g('section1.homePhone', '')
|
|
vals['business_phone'] = g('section1.busPhone', '')
|
|
vals['phone_extension'] = g('section1.phoneExtension', '')
|
|
|
|
# Benefits
|
|
q1 = g('section1.confirmationOfBenefit.q1Yn', '').lower()
|
|
vals['receives_social_assistance'] = q1 == 'yes'
|
|
q1type = g('section1.confirmationOfBenefit.q1Ifyes', '').lower()
|
|
vals['benefit_owp'] = 'owp' in q1type if q1type else False
|
|
vals['benefit_odsp'] = 'odsp' in q1type if q1type else False
|
|
vals['benefit_acsd'] = 'acsd' in q1type if q1type else False
|
|
if vals['benefit_owp']:
|
|
vals['benefit_type'] = 'owp'
|
|
elif vals['benefit_odsp']:
|
|
vals['benefit_type'] = 'odsp'
|
|
elif vals['benefit_acsd']:
|
|
vals['benefit_type'] = 'acsd'
|
|
vals['wsib_eligible'] = g('section1.confirmationOfBenefit.q2Yn', '').lower() == 'yes'
|
|
vals['vac_eligible'] = g('section1.confirmationOfBenefit.q3Yn', '').lower() == 'yes'
|
|
|
|
# Section 2 - Devices & Eligibility
|
|
vals['medical_condition'] = g('section2.devicesandEligibility.condition', '')
|
|
vals['mobility_status'] = g('section2.devicesandEligibility.status', '')
|
|
|
|
# Previously funded
|
|
vals['prev_funded_none'] = bool(g('section2.devicesandEligibility.none', ''))
|
|
vals['prev_funded_forearm'] = bool(g('section2.devicesandEligibility.forearm', ''))
|
|
vals['prev_funded_wheeled'] = bool(g('section2.devicesandEligibility.wheeled', ''))
|
|
vals['prev_funded_manual'] = bool(g('section2.devicesandEligibility.manual', ''))
|
|
vals['prev_funded_power'] = bool(g('section2.devicesandEligibility.power', ''))
|
|
vals['prev_funded_addon'] = bool(g('section2.devicesandEligibility.addOn', ''))
|
|
vals['prev_funded_scooter'] = bool(g('section2.devicesandEligibility.scooter', ''))
|
|
vals['prev_funded_seating'] = bool(g('section2.devicesandEligibility.seating', ''))
|
|
vals['prev_funded_tilt'] = bool(g('section2.devicesandEligibility.tiltSystem', ''))
|
|
vals['prev_funded_recline'] = bool(g('section2.devicesandEligibility.reclineSystem', ''))
|
|
vals['prev_funded_legrests'] = bool(g('section2.devicesandEligibility.legRests', ''))
|
|
vals['prev_funded_frame'] = bool(g('section2.devicesandEligibility.frame', ''))
|
|
vals['prev_funded_stroller'] = bool(g('section2.devicesandEligibility.stroller', ''))
|
|
|
|
# Devices currently required
|
|
vals['device_forearm_crutches'] = bool(g('section2.devicesandEligibility.deviceForearm', ''))
|
|
vals['device_wheeled_walker'] = bool(g('section2.devicesandEligibility.deviceWheeled', ''))
|
|
vals['device_manual_wheelchair'] = bool(g('section2.devicesandEligibility.deviceManual', ''))
|
|
vals['device_ambulation_manual'] = bool(g('section2.devicesandEligibility.deviceAmbulation', ''))
|
|
vals['device_dependent_wheelchair'] = bool(g('section2.devicesandEligibility.deviceDependent', ''))
|
|
vals['device_dynamic_tilt'] = bool(g('section2.devicesandEligibility.deviceDynamic', ''))
|
|
vals['device_manual_dynamic'] = bool(g('section2.devicesandEligibility.manualDyanmic', ''))
|
|
vals['device_manual_power_addon'] = bool(g('section2.devicesandEligibility.manualWheelchair', ''))
|
|
vals['device_power_base'] = bool(g('section2.devicesandEligibility.powerBase', ''))
|
|
vals['device_power_scooter'] = bool(g('section2.devicesandEligibility.powerScooter', ''))
|
|
vals['device_ambulation_power'] = bool(g('section2.devicesandEligibility.ambulation', ''))
|
|
vals['device_positioning'] = bool(g('section2.devicesandEligibility.positioning', ''))
|
|
vals['device_high_tech'] = bool(g('section2.devicesandEligibility.highTech', ''))
|
|
vals['device_standing_frame'] = bool(g('section2.devicesandEligibility.standingFrame', ''))
|
|
vals['device_adp_funded_mods'] = bool(g('section2.devicesandEligibility.adpFunded', ''))
|
|
vals['device_non_adp_funded_mods'] = bool(g('section2.devicesandEligibility.nonADPFunded', ''))
|
|
|
|
# Section 2a - Walkers
|
|
vals['s2a_base_device'] = g('section2.section2a.walker', '')
|
|
vals['s2a_paediatric_frame'] = g('section2.section2a.paediatricFrame', '')
|
|
vals['s2a_forearm_crutches'] = g('section2.section2a.forearmCrutches', '')
|
|
vals['s2a_none'] = g('section2.section2a.none', '')
|
|
vals['s2a_reason'] = g('section2.section2a.reason', '')
|
|
vals['s2a_replacement_status'] = g('section2.section2a.replacementStatus', '')
|
|
vals['s2a_replacement_size'] = g('section2.section2a.replacementSize', '')
|
|
vals['s2a_replacement_adp'] = g('section2.section2a.replacementADP', '')
|
|
vals['s2a_replacement_special'] = g('section2.section2a.replacementSpecial', '')
|
|
for i in range(1, 7):
|
|
vals[f's2a_confirm{i}'] = g(f'section2.section2a.confirmation{i}', '')
|
|
vals['s2a_seat_height'] = g('section2.section2a.seatHeight', '')
|
|
vals['s2a_seat_height_unit'] = g('section2.section2a.seatHeightmeasurement', '')
|
|
vals['s2a_handle_height'] = g('section2.section2a.handleHeight', '')
|
|
vals['s2a_handle_height_unit'] = g('section2.section2a.handleHeightmeasurement', '')
|
|
vals['s2a_hand_grips'] = g('section2.section2a.handGrips', '')
|
|
vals['s2a_forearm_attachments'] = g('section2.section2a.forearm', '')
|
|
vals['s2a_width_handles'] = g('section2.section2a.widthHandles', '')
|
|
vals['s2a_width_handles_unit'] = g('section2.section2a.widthHandlesmeasurement', '')
|
|
vals['s2a_client_weight'] = g('section2.section2a.clientWeight', '')
|
|
vals['s2a_client_weight_unit'] = g('section2.section2a.clientWeightmeasurement', '')
|
|
vals['s2a_brakes'] = g('section2.section2a.brakes', '')
|
|
vals['s2a_brake_type'] = g('section2.section2a.brakeType', '')
|
|
vals['s2a_num_wheels'] = g('section2.section2a.noWheels', '')
|
|
vals['s2a_wheel_size'] = g('section2.section2a.wheelSize', '')
|
|
vals['s2a_back_support'] = g('section2.section2a.backSupport', '')
|
|
vals['s2a_adp_walker'] = g('section2.section2a.adpWalker', '')
|
|
vals['s2a_adp_frame'] = g('section2.section2a.adpFrame', '')
|
|
vals['s2a_adp_standing'] = g('section2.section2a.adpStanding', '')
|
|
vals['s2a_custom'] = g('section2.section2a.custom', '')
|
|
vals['s2a_cost_labour'] = g('section2.section2a.costLabour', '')
|
|
|
|
# Section 2b - Manual Wheelchairs
|
|
vals['s2b_base_device'] = g('section2.section2b.baseDevice', '')
|
|
vals['s2b_power_addon'] = g('section2.section2b.powerAddOndevice', '')
|
|
vals['s2b_reason'] = g('section2.section2b.reason', '')
|
|
vals['s2b_replacement_status'] = g('section2.section2b.replacementStatus', '')
|
|
vals['s2b_replacement_size'] = g('section2.section2b.replacementSize', '')
|
|
vals['s2b_replacement_adp'] = g('section2.section2b.replacementADP', '')
|
|
vals['s2b_replacement_special'] = g('section2.section2b.replacementSpecial', '')
|
|
for i in range(1, 14):
|
|
vals[f's2b_confirm{i}'] = g(f'section2.section2b.confirmation{i}', '')
|
|
vals['s2b_seat_width'] = g('section2.section2b.seatWidth', '')
|
|
vals['s2b_seat_width_unit'] = g('section2.section2b.seatWidthmeasurement', '')
|
|
vals['s2b_seat_depth'] = g('section2.section2b.seatDepth', '')
|
|
vals['s2b_seat_depth_unit'] = g('section2.section2b.seatDepthmeasurement', '')
|
|
vals['s2b_floor_height'] = g('section2.section2b.floorHeight', '')
|
|
vals['s2b_floor_height_unit'] = g('section2.section2b.floorHeightmeasurement', '')
|
|
vals['s2b_cane_height'] = g('section2.section2b.caneHeight', '')
|
|
vals['s2b_cane_height_unit'] = g('section2.section2b.caneHeightmeasurement', '')
|
|
vals['s2b_back_height'] = g('section2.section2b.backHeight', '')
|
|
vals['s2b_back_height_unit'] = g('section2.section2b.backHeightmeasurement', '')
|
|
vals['s2b_rest_length'] = g('section2.section2b.restLength', '')
|
|
vals['s2b_rest_length_unit'] = g('section2.section2b.restLengthmeasurement', '')
|
|
vals['s2b_client_weight'] = g('section2.section2b.clientWeight', '')
|
|
vals['s2b_client_weight_unit'] = g('section2.section2b.clientWeightmeasurement', '')
|
|
vals['s2b_adjustable_tension'] = bool(g('section2.section2b.adjustableTension', ''))
|
|
vals['s2b_heavy_duty'] = bool(g('section2.section2b.heavyDuty', ''))
|
|
vals['s2b_recliner'] = bool(g('section2.section2b.recliner', ''))
|
|
vals['s2b_footplates'] = bool(g('section2.section2b.footplates', ''))
|
|
vals['s2b_legrests'] = bool(g('section2.section2b.legrests', ''))
|
|
vals['s2b_spoke'] = bool(g('section2.section2b.spoke', ''))
|
|
vals['s2b_projected'] = bool(g('section2.section2b.projected', ''))
|
|
vals['s2b_standard_manual'] = bool(g('section2.section2b.standardManual', ''))
|
|
vals['s2b_grade_aids'] = bool(g('section2.section2b.gradeAids', ''))
|
|
vals['s2b_caster_pin'] = bool(g('section2.section2b.casterPin', ''))
|
|
vals['s2b_amputee_axle'] = bool(g('section2.section2b.amputeeAxle', ''))
|
|
vals['s2b_quick_release'] = bool(g('section2.section2b.quickRelease', ''))
|
|
vals['s2b_stroller'] = bool(g('section2.section2b.stroller', ''))
|
|
vals['s2b_oxygen'] = bool(g('section2.section2b.oxygen', ''))
|
|
vals['s2b_ventilator'] = bool(g('section2.section2b.ventilator', ''))
|
|
vals['s2b_titanium'] = bool(g('section2.section2b.titanium', ''))
|
|
vals['s2b_clothing_guards'] = bool(g('section2.section2b.clothingGuards', ''))
|
|
vals['s2b_one_arm'] = bool(g('section2.section2b.oneArm', ''))
|
|
vals['s2b_uni_lateral'] = bool(g('section2.section2b.uniLateral', ''))
|
|
vals['s2b_plastic'] = bool(g('section2.section2b.plastic', ''))
|
|
vals['s2b_rationale'] = g('section2.section2b.rationale', '')
|
|
vals['s2b_custom'] = g('section2.section2b.custom', '')
|
|
vals['s2b_cost_labour'] = g('section2.section2b.costLabour', '')
|
|
|
|
# Section 2c - Power Bases / Scooters
|
|
vals['s2c_base_device'] = g('section2.section2c.baseDevice', '')
|
|
vals['s2c_reason'] = g('section2.section2c.reason', '')
|
|
vals['s2c_replacement_status'] = g('section2.section2c.replacementStatus', '')
|
|
vals['s2c_replacement_size'] = g('section2.section2c.replacementSize', '')
|
|
vals['s2c_replacement_adp'] = g('section2.section2c.replacementADP', '')
|
|
vals['s2c_replacement_special'] = g('section2.section2c.replacementSpecial', '')
|
|
for i in range(1, 6):
|
|
vals[f's2c_confirm{i}'] = g(f'section2.section2c.confirmation{i}', '')
|
|
vals['s2c_seat_width'] = g('section2.section2c.seatWidth', '')
|
|
vals['s2c_seat_width_unit'] = g('section2.section2c.seatWidthmeasurement', '')
|
|
vals['s2c_back_height'] = g('section2.section2c.backHeight', '')
|
|
vals['s2c_back_height_unit'] = g('section2.section2c.backHeightmeasurement', '')
|
|
vals['s2c_floor_height'] = g('section2.section2c.floorHeight', '')
|
|
vals['s2c_floor_height_unit'] = g('section2.section2c.floorHeightmeasurement', '')
|
|
vals['s2c_rest_length'] = g('section2.section2c.restLength', '')
|
|
vals['s2c_rest_length_unit'] = g('section2.section2c.restLengthmeasurement', '')
|
|
vals['s2c_seat_depth'] = g('section2.section2c.seatDepth', '')
|
|
vals['s2c_seat_depth_unit'] = g('section2.section2c.seatDepthmeasurement', '')
|
|
vals['s2c_client_weight'] = g('section2.section2c.clientWeight', '')
|
|
vals['s2c_client_weight_unit'] = g('section2.section2c.clientWeightmeasurement', '')
|
|
vals['s2c_adjustable_tension'] = bool(g('section2.section2c.adjustableTension', ''))
|
|
vals['s2c_midline'] = bool(g('section2.section2c.midline', ''))
|
|
vals['s2c_manual_recline'] = bool(g('section2.section2c.manualRecline', ''))
|
|
vals['s2c_footplates'] = bool(g('section2.section2c.footplates', ''))
|
|
vals['s2c_legrests'] = bool(g('section2.section2c.legrests', ''))
|
|
vals['s2c_swingaway'] = bool(g('section2.section2c.swingaway', ''))
|
|
vals['s2c_one_piece'] = bool(g('section2.section2c.onePiece', ''))
|
|
vals['s2c_seat_package_1'] = bool(g('section2.section2c.seatPackage1', ''))
|
|
vals['s2c_seat_package_2'] = bool(g('section2.section2c.seatPackage2', ''))
|
|
vals['s2c_oxygen'] = bool(g('section2.section2c.oxygen', ''))
|
|
vals['s2c_ventilator'] = bool(g('section2.section2c.ventilator', ''))
|
|
vals['s2c_sp_controls_1'] = bool(g('section2.section2c.spControls1', ''))
|
|
vals['s2c_sp_controls_2'] = bool(g('section2.section2c.spControls2', ''))
|
|
vals['s2c_sp_controls_3'] = bool(g('section2.section2c.spControls3', ''))
|
|
vals['s2c_sp_controls_4'] = bool(g('section2.section2c.spControls4', ''))
|
|
vals['s2c_sp_controls_5'] = bool(g('section2.section2c.spControls5', ''))
|
|
vals['s2c_sp_controls_6'] = bool(g('section2.section2c.spControls6', ''))
|
|
vals['s2c_auto_correction'] = bool(g('section2.section2c.autoCorrection', ''))
|
|
vals['s2c_rationale'] = g('section2.section2c.rationale', '')
|
|
vals['s2c_power_tilt'] = bool(g('section2.section2c.powerTilt', ''))
|
|
vals['s2c_power_recline'] = bool(g('section2.section2c.powerRecline', ''))
|
|
vals['s2c_tilt_and_recline'] = bool(g('section2.section2c.tiltAndRecline', ''))
|
|
vals['s2c_power_elevating'] = bool(g('section2.section2c.powerElevating', ''))
|
|
vals['s2c_control_box'] = bool(g('section2.section2c.ControlBox', ''))
|
|
vals['s2c_custom'] = g('section2.section2c.custom', '')
|
|
vals['s2c_cost_labour'] = g('section2.section2c.costLabour', '')
|
|
|
|
# Section 2d - Positioning/Seating
|
|
vals['s2d_seat_modular'] = bool(g('section2.section2d.seatM', ''))
|
|
vals['s2d_seat_custom'] = bool(g('section2.section2d.seatCF', ''))
|
|
vals['s2d_seat_cover_modular'] = bool(g('section2.section2d.coverM', ''))
|
|
vals['s2d_seat_cover_custom'] = bool(g('section2.section2d.coverCF', ''))
|
|
vals['s2d_seat_option_modular'] = bool(g('section2.section2d.optionM', ''))
|
|
vals['s2d_seat_option_custom'] = bool(g('section2.section2d.optionCF', ''))
|
|
vals['s2d_seat_hardware_modular'] = bool(g('section2.section2d.hardwareM', ''))
|
|
vals['s2d_seat_hardware_custom'] = bool(g('section2.section2d.hardwareCF', ''))
|
|
vals['s2d_adductor_modular'] = bool(g('section2.section2d.adductorM', ''))
|
|
vals['s2d_adductor_custom'] = bool(g('section2.section2d.adductorCF', ''))
|
|
vals['s2d_pommel_custom'] = bool(g('section2.section2d.pommelCF', ''))
|
|
vals['s2d_back_modular'] = bool(g('section2.section2d.backM', ''))
|
|
vals['s2d_back_custom'] = bool(g('section2.section2d.backCF', ''))
|
|
vals['s2d_back_option_modular'] = bool(g('section2.section2d.supportoptionM', ''))
|
|
vals['s2d_back_option_custom'] = bool(g('section2.section2d.supportoptionCF', ''))
|
|
vals['s2d_back_cover_custom'] = bool(g('section2.section2d.backcoverCF', ''))
|
|
vals['s2d_back_hardware_modular'] = bool(g('section2.section2d.backHardwareM', ''))
|
|
vals['s2d_back_hardware_custom'] = bool(g('section2.section2d.backHardwareCF', ''))
|
|
vals['s2d_complete_modular'] = bool(g('section2.section2d.completeM', ''))
|
|
vals['s2d_complete_custom'] = bool(g('section2.section2d.completeCF', ''))
|
|
vals['s2d_headrest_modular'] = bool(g('section2.section2d.headrestM', ''))
|
|
vals['s2d_headrest_custom'] = bool(g('section2.section2d.headrestCF', ''))
|
|
vals['s2d_head_option_custom'] = bool(g('section2.section2d.headoptionCF', ''))
|
|
vals['s2d_head_hardware_modular'] = bool(g('section2.section2d.headhardwareM', ''))
|
|
vals['s2d_head_hardware_custom'] = bool(g('section2.section2d.headhardwareCF', ''))
|
|
vals['s2d_belt_modular'] = bool(g('section2.section2d.beltM', ''))
|
|
vals['s2d_belt_custom'] = bool(g('section2.section2d.beltCF', ''))
|
|
vals['s2d_belt_option_custom'] = bool(g('section2.section2d.beltoptionCF', ''))
|
|
vals['s2d_arm_modular'] = bool(g('section2.section2d.armsupportM', ''))
|
|
vals['s2d_arm_custom'] = bool(g('section2.section2d.armsupportCF', ''))
|
|
vals['s2d_arm_option_modular'] = bool(g('section2.section2d.armoptionM', ''))
|
|
vals['s2d_arm_option_custom'] = bool(g('section2.section2d.armoptionCF', ''))
|
|
vals['s2d_arm_hardware_modular'] = bool(g('section2.section2d.armhardwareM', ''))
|
|
vals['s2d_arm_hardware_custom'] = bool(g('section2.section2d.armhardwareCF', ''))
|
|
vals['s2d_tray_modular'] = bool(g('section2.section2d.trayM', ''))
|
|
vals['s2d_tray_custom'] = bool(g('section2.section2d.trayCF', ''))
|
|
vals['s2d_tray_option_modular'] = bool(g('section2.section2d.trayoptionM', ''))
|
|
vals['s2d_tray_option_custom'] = bool(g('section2.section2d.trayoptionCF', ''))
|
|
vals['s2d_lateral_modular'] = bool(g('section2.section2d.lateralsupportM', ''))
|
|
vals['s2d_lateral_custom'] = bool(g('section2.section2d.lateralsupportCF', ''))
|
|
vals['s2d_lateral_option_custom'] = bool(g('section2.section2d.lateraloptionCF', ''))
|
|
vals['s2d_lateral_hardware_custom'] = bool(g('section2.section2d.lateralhardwareCF', ''))
|
|
vals['s2d_foot_modular'] = bool(g('section2.section2d.footsupportM', ''))
|
|
vals['s2d_foot_custom'] = bool(g('section2.section2d.footsupportCF', ''))
|
|
vals['s2d_foot_option_modular'] = bool(g('section2.section2d.footoptionM', ''))
|
|
vals['s2d_foot_option_custom'] = bool(g('section2.section2d.footoptionCF', ''))
|
|
vals['s2d_foot_hardware_modular'] = bool(g('section2.section2d.foothardwareM', ''))
|
|
vals['s2d_foot_hardware_custom'] = bool(g('section2.section2d.foothardwareCF', ''))
|
|
vals['s2d_reason'] = g('section2.section2d.reason', '')
|
|
vals['s2d_replacement_status'] = g('section2.section2d.replacementStatus', '')
|
|
vals['s2d_replacement_size'] = g('section2.section2d.replacementSize', '')
|
|
vals['s2d_replacement_adp'] = g('section2.section2d.replacementADP', '')
|
|
vals['s2d_replacement_special'] = g('section2.section2d.replacementSpecial', '')
|
|
vals['s2d_confirm1'] = g('section2.section2d.confirmation1', '')
|
|
vals['s2d_confirm2'] = g('section2.section2d.confirmation2', '')
|
|
vals['s2d_custom'] = g('section2.section2d.custom', '')
|
|
vals['s2d_cost_labour'] = g('section2.section2d.costLabour', '')
|
|
|
|
# Section 3 - Consent
|
|
vals['consent_date'] = self._pd(g('section3.sig.Date', ''))
|
|
person = g('section3.sig.person', '').lower()
|
|
vals['consent_signed_by'] = 'applicant' if 'applicant' in person else ('agent' if 'agent' in person else False)
|
|
vals['agent_relationship'] = g('section3.contact.relationship', '')
|
|
vals['agent_last_name'] = g('section3.contact.applicantLastname', '')
|
|
vals['agent_first_name'] = g('section3.contact.applicantFirstname', '')
|
|
vals['agent_middle_initial'] = g('section3.contact.applicantMiddleinitial', '')
|
|
vals['agent_unit'] = g('section3.contact.unitNo', '')
|
|
vals['agent_street_no'] = g('section3.contact.streetNo', '')
|
|
vals['agent_street_name'] = g('section3.contact.streetName', '')
|
|
vals['agent_rural_route'] = g('section3.contact.rrRoute', '')
|
|
vals['agent_city'] = g('section3.contact.city', '')
|
|
vals['agent_province'] = g('section3.contact.province', '')
|
|
vals['agent_postal_code'] = g('section3.contact.postalCode', '')
|
|
vals['agent_home_phone'] = g('section3.contact.homePhone', '')
|
|
vals['agent_bus_phone'] = g('section3.contact.busPhone', '')
|
|
vals['agent_phone_ext'] = g('section3.contact.phoneExtension', '')
|
|
|
|
# Section 4 - Authorizer
|
|
vals['authorizer_last_name'] = g('section4.authorizer.authorizerLastname', '')
|
|
vals['authorizer_first_name'] = g('section4.authorizer.authorizerFirstname', '')
|
|
vals['authorizer_phone'] = g('section4.authorizer.busPhone', '')
|
|
vals['authorizer_phone_ext'] = g('section4.authorizer.phoneExtension', '')
|
|
vals['authorizer_adp_number'] = g('section4.authorizer.adpNo', '')
|
|
vals['assessment_date'] = self._pd(g('section4.authorizer.Date', ''))
|
|
vals['application_date'] = vals['consent_date'] or vals['assessment_date']
|
|
|
|
# Section 4 - Vendor 1
|
|
vals['vendor_business_name'] = g('section4.vendor.vendorBusName', '')
|
|
vals['vendor_adp_number'] = g('section4.vendor.adpVendorRegNo', '')
|
|
vals['vendor_representative'] = g('section4.vendor.vendorLastfirstname', '')
|
|
vals['vendor_position'] = g('section4.vendor.positionTitle', '')
|
|
vals['vendor_location'] = g('section4.vendor.vendorLocation', '')
|
|
vals['vendor_phone'] = g('section4.vendor.busPhone', '')
|
|
vals['vendor_phone_ext'] = g('section4.vendor.phoneExtension', '')
|
|
vals['vendor_sign_date'] = self._pd(g('section4.vendor.Date', ''))
|
|
|
|
# Section 4 - Vendor 2
|
|
vals['vendor2_business_name'] = g('section4.vendor2.vendorBusName', '')
|
|
vals['vendor2_adp_number'] = g('section4.vendor2.adpVendorRegNo', '')
|
|
vals['vendor2_representative'] = g('section4.vendor2.vendorLastfirstname', '')
|
|
vals['vendor2_position'] = g('section4.vendor2.positionTitle', '')
|
|
vals['vendor2_location'] = g('section4.vendor2.vendorLocation', '')
|
|
vals['vendor2_phone'] = g('section4.vendor2.busPhone', '')
|
|
vals['vendor2_phone_ext'] = g('section4.vendor2.phoneExtension', '')
|
|
vals['vendor2_sign_date'] = self._pd(g('section4.vendor2.Date', ''))
|
|
|
|
# Equipment Spec
|
|
vals['equip_vendor_invoice_no'] = g('section4.equipmentSpec.vendorInvoiceNo', '')
|
|
vals['equip_vendor_adp_reg'] = g('section4.equipmentSpec.vendorADPRegNo', '')
|
|
vals['equip_cell1'] = g('section4.equipmentSpec.Table2.Row1.Cell1', '')
|
|
vals['equip_cell2'] = g('section4.equipmentSpec.Table2.Row1.Cell2', '')
|
|
vals['equip_cell3'] = g('section4.equipmentSpec.Table2.Row1.Cell3', '')
|
|
vals['equip_cell4'] = g('section4.equipmentSpec.Table2.Row1.Cell4', '')
|
|
vals['equip_cell5'] = g('section4.equipmentSpec.Table2.Row1.Cell5', '')
|
|
vals['pod_received_by'] = g('section4.proofOfDelivery.receivedBy', '')
|
|
vals['pod_date'] = self._pd(g('section4.proofOfDelivery.Date', ''))
|
|
|
|
# Note to ADP
|
|
vals['note_section1'] = bool(g('section4.noteToADP.section1', ''))
|
|
vals['note_section2a'] = bool(g('section4.noteToADP.section2a', ''))
|
|
vals['note_section2b'] = bool(g('section4.noteToADP.section2b', ''))
|
|
vals['note_section2c'] = bool(g('section4.noteToADP.section2c', ''))
|
|
vals['note_section2d'] = bool(g('section4.noteToADP.section2d', ''))
|
|
vals['note_section3and4'] = bool(g('section4.noteToADP.section3and4', ''))
|
|
vals['note_vendor_replacement'] = g('section4.noteToADP.vendorReplacement', '')
|
|
vals['note_vendor_custom'] = g('section4.noteToADP.vendorCustom', '')
|
|
vals['note_funding_chart'] = g('section4.noteToADP.fundingChart', '')
|
|
vals['note_letter'] = g('section4.noteToADP.letter', '')
|
|
|
|
return vals
|
|
|
|
# ------------------------------------------------------------------
|
|
# PROFILE MANAGEMENT
|
|
# ------------------------------------------------------------------
|
|
def _find_or_create_profile(self, vals, sale_order=None):
|
|
"""Find or create a client profile from parsed application data."""
|
|
Profile = self.env['fusion.client.profile']
|
|
hc = (vals.get('health_card_number') or '').strip()
|
|
first = (vals.get('applicant_first_name') or '').strip()
|
|
last = (vals.get('applicant_last_name') or '').strip()
|
|
dob = vals.get('date_of_birth')
|
|
|
|
profile = False
|
|
if hc:
|
|
profile = Profile.search([('health_card_number', '=', hc)], limit=1)
|
|
if not profile and first and last and dob:
|
|
profile = Profile.search([
|
|
('first_name', '=ilike', first),
|
|
('last_name', '=ilike', last),
|
|
('date_of_birth', '=', dob),
|
|
], limit=1)
|
|
|
|
profile_vals = {
|
|
'first_name': first,
|
|
'last_name': last,
|
|
'middle_initial': vals.get('applicant_middle_initial', ''),
|
|
'health_card_number': hc,
|
|
'health_card_version': vals.get('health_card_version', ''),
|
|
'date_of_birth': dob,
|
|
'ltch_name': vals.get('ltch_name', ''),
|
|
'unit_number': vals.get('unit_number', ''),
|
|
'street_number': vals.get('street_number', ''),
|
|
'street_name': vals.get('street_name', ''),
|
|
'rural_route': vals.get('rural_route', ''),
|
|
'city': vals.get('city', ''),
|
|
'province': vals.get('province', '') or 'ON',
|
|
'postal_code': vals.get('postal_code', ''),
|
|
'home_phone': vals.get('home_phone', ''),
|
|
'business_phone': vals.get('business_phone', ''),
|
|
'phone_extension': vals.get('phone_extension', ''),
|
|
'medical_condition': vals.get('medical_condition', ''),
|
|
'mobility_status': vals.get('mobility_status', ''),
|
|
}
|
|
if vals.get('receives_social_assistance'):
|
|
profile_vals['receives_social_assistance'] = True
|
|
profile_vals['benefit_type'] = vals.get('benefit_type')
|
|
if vals.get('wsib_eligible'):
|
|
profile_vals['wsib_eligible'] = True
|
|
if vals.get('vac_eligible'):
|
|
profile_vals['vac_eligible'] = True
|
|
if vals.get('assessment_date'):
|
|
profile_vals['last_assessment_date'] = vals['assessment_date']
|
|
|
|
# Link to partner
|
|
if sale_order and sale_order.partner_id:
|
|
profile_vals['partner_id'] = sale_order.partner_id.id
|
|
elif not profile or not profile.partner_id:
|
|
partner = self._find_partner(first, last)
|
|
if partner:
|
|
profile_vals['partner_id'] = partner.id
|
|
|
|
if profile:
|
|
profile.write(profile_vals)
|
|
else:
|
|
profile = Profile.create(profile_vals)
|
|
|
|
return profile
|
|
|
|
def _find_partner(self, first_name, last_name):
|
|
"""Try to find a matching res.partner."""
|
|
if not first_name or not last_name:
|
|
return False
|
|
Partner = self.env['res.partner']
|
|
partner = Partner.search([('name', 'ilike', f'{first_name} {last_name}')], limit=1)
|
|
if not partner:
|
|
partner = Partner.search([('name', 'ilike', f'{last_name}, {first_name}')], limit=1)
|
|
return partner or False
|
|
|
|
# ------------------------------------------------------------------
|
|
# HELPERS
|
|
# ------------------------------------------------------------------
|
|
@staticmethod
|
|
def _t(element, tag):
|
|
"""Get text of child element, empty string if missing."""
|
|
child = element.find(tag)
|
|
if child is not None and child.text:
|
|
return child.text.strip()
|
|
return ''
|
|
|
|
@staticmethod
|
|
def _pd(date_str):
|
|
"""Parse date string, return date or False."""
|
|
if not date_str:
|
|
return False
|
|
for fmt in ('%Y/%m/%d', '%Y-%m-%d', '%Y%m%d'):
|
|
try:
|
|
return datetime.strptime(date_str.strip(), fmt).date()
|
|
except ValueError:
|
|
continue
|
|
return False
|