# -*- coding: utf-8 -*- # Copyright 2024-2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) from dateutil.relativedelta import relativedelta from odoo import models, fields, api, _ class FusionLTCFacility(models.Model): _name = 'fusion.ltc.facility' _description = 'LTC Facility' _inherit = ['mail.thread', 'mail.activity.mixin'] _order = 'name' name = fields.Char( string='Facility Name', required=True, tracking=True, ) code = fields.Char( string='Code', copy=False, readonly=True, default=lambda self: _('New'), ) active = fields.Boolean(default=True) image_1920 = fields.Image(string='Image', max_width=1920, max_height=1920) partner_id = fields.Many2one( 'res.partner', string='Contact Record', help='The facility as a contact in the system', tracking=True, ) # Address street = fields.Char(string='Street') street2 = fields.Char(string='Street2') city = fields.Char(string='City') state_id = fields.Many2one( 'res.country.state', string='Province', domain="[('country_id', '=', country_id)]", ) zip = fields.Char(string='Postal Code') country_id = fields.Many2one('res.country', string='Country') phone = fields.Char(string='Phone') email = fields.Char(string='Email') website = fields.Char(string='Website') # Key contacts director_of_care_id = fields.Many2one( 'res.partner', string='Director of Care', tracking=True, ) service_supervisor_id = fields.Many2one( 'res.partner', string='Service Supervisor', tracking=True, ) physiotherapist_ids = fields.Many2many( 'res.partner', 'ltc_facility_physiotherapist_rel', 'facility_id', 'partner_id', string='Physiotherapists', help='Primary contacts for equipment recommendations and communication', ) # Structure number_of_floors = fields.Integer(string='Number of Floors') floor_ids = fields.One2many( 'fusion.ltc.floor', 'facility_id', string='Floors', ) # Contract contract_start_date = fields.Date(string='Contract Start Date', tracking=True) contract_end_date = fields.Date(string='Contract End Date', tracking=True) contract_file = fields.Binary( string='Contract Document', attachment=True, ) contract_file_filename = fields.Char(string='Contract Filename') contract_notes = fields.Text(string='Contract Notes') # Cleanup scheduling cleanup_frequency = fields.Selection([ ('quarterly', 'Quarterly (Every 3 Months)'), ('semi_annual', 'Semi-Annual (Every 6 Months)'), ('annual', 'Annual (Yearly)'), ('custom', 'Custom Interval'), ], string='Cleanup Frequency', default='quarterly') cleanup_interval_days = fields.Integer( string='Custom Interval (Days)', help='Number of days between cleanups when using custom interval', ) next_cleanup_date = fields.Date( string='Next Cleanup Date', compute='_compute_next_cleanup_date', store=True, readonly=False, tracking=True, ) # Related records repair_ids = fields.One2many('fusion.ltc.repair', 'facility_id', string='Repairs') cleanup_ids = fields.One2many('fusion.ltc.cleanup', 'facility_id', string='Cleanups') # Computed counts repair_count = fields.Integer(compute='_compute_repair_count', string='Total Repairs') active_repair_count = fields.Integer(compute='_compute_repair_count', string='Active Repairs') cleanup_count = fields.Integer(compute='_compute_cleanup_count', string='Cleanups') notes = fields.Html(string='Notes') @api.model_create_multi def create(self, vals_list): for vals in vals_list: if vals.get('code', _('New')) == _('New'): vals['code'] = self.env['ir.sequence'].next_by_code('fusion.ltc.facility') or _('New') return super().create(vals_list) @api.depends('contract_start_date', 'cleanup_frequency', 'cleanup_interval_days') def _compute_next_cleanup_date(self): today = fields.Date.context_today(self) for facility in self: start = facility.contract_start_date freq = facility.cleanup_frequency if not start or not freq: if not facility.next_cleanup_date: facility.next_cleanup_date = False continue interval = facility._get_cleanup_interval_days() delta = relativedelta(days=interval) candidate = start + delta while candidate < today: candidate += delta facility.next_cleanup_date = candidate def action_preview_contract(self): self.ensure_one() if not self.contract_file: return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('No Document'), 'message': _('No contract document has been uploaded yet.'), 'type': 'warning', 'sticky': False, }, } attachment = self.env['ir.attachment'].search([ ('res_model', '=', self._name), ('res_id', '=', self.id), ('res_field', '=', 'contract_file'), ], limit=1) if not attachment: attachment = self.env['ir.attachment'].search([ ('res_model', '=', self._name), ('res_id', '=', self.id), ('name', '=', self.contract_file_filename or 'contract_file'), ], limit=1, order='id desc') if attachment: return { 'type': 'ir.actions.client', 'tag': 'fusion_claims.preview_document', 'params': { 'attachment_id': attachment.id, 'title': _('Contract - %s', self.name), }, } return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Error'), 'message': _('Could not load contract document.'), 'type': 'danger', 'sticky': False, }, } def _compute_repair_count(self): for facility in self: repairs = facility.repair_ids facility.repair_count = len(repairs) facility.active_repair_count = len(repairs.filtered( lambda r: r.stage_id and not r.stage_id.fold )) def _compute_cleanup_count(self): for facility in self: facility.cleanup_count = len(facility.cleanup_ids) def action_view_repairs(self): self.ensure_one() return { 'name': _('Repairs - %s', self.name), 'type': 'ir.actions.act_window', 'res_model': 'fusion.ltc.repair', 'view_mode': 'kanban,list,form', 'domain': [('facility_id', '=', self.id)], 'context': {'default_facility_id': self.id}, } def action_view_cleanups(self): self.ensure_one() return { 'name': _('Cleanups - %s', self.name), 'type': 'ir.actions.act_window', 'res_model': 'fusion.ltc.cleanup', 'view_mode': 'list,kanban,form', 'domain': [('facility_id', '=', self.id)], 'context': {'default_facility_id': self.id}, } def _get_cleanup_interval_days(self): mapping = { 'quarterly': 90, 'semi_annual': 180, 'annual': 365, } if self.cleanup_frequency == 'custom': return self.cleanup_interval_days or 90 return mapping.get(self.cleanup_frequency, 90) class FusionLTCFloor(models.Model): _name = 'fusion.ltc.floor' _description = 'LTC Facility Floor' _order = 'sequence, name' facility_id = fields.Many2one( 'fusion.ltc.facility', string='Facility', required=True, ondelete='cascade', ) name = fields.Char(string='Floor Name', required=True) sequence = fields.Integer(string='Sequence', default=10) station_ids = fields.One2many( 'fusion.ltc.station', 'floor_id', string='Nursing Stations', ) head_nurse_id = fields.Many2one( 'res.partner', string='Head Nurse', ) physiotherapist_id = fields.Many2one( 'res.partner', string='Physiotherapist', help='Floor-level physiotherapist if different from facility level', ) class FusionLTCStation(models.Model): _name = 'fusion.ltc.station' _description = 'LTC Nursing Station' _order = 'sequence, name' floor_id = fields.Many2one( 'fusion.ltc.floor', string='Floor', required=True, ondelete='cascade', ) name = fields.Char(string='Station Name', required=True) sequence = fields.Integer(string='Sequence', default=10) head_nurse_id = fields.Many2one( 'res.partner', string='Head Nurse', ) phone = fields.Char(string='Phone') class FusionLTCFamilyContact(models.Model): _name = 'fusion.ltc.family.contact' _description = 'LTC Resident Family Contact' _order = 'is_poa desc, name' partner_id = fields.Many2one( 'res.partner', string='Resident', required=True, ondelete='cascade', ) name = fields.Char(string='Contact Name', required=True) relationship = fields.Selection([ ('spouse', 'Spouse'), ('child', 'Child'), ('sibling', 'Sibling'), ('parent', 'Parent'), ('guardian', 'Guardian'), ('poa', 'Power of Attorney'), ('other', 'Other'), ], string='Relationship') phone = fields.Char(string='Phone') phone2 = fields.Char(string='Phone 2') email = fields.Char(string='Email') is_poa = fields.Boolean(string='Is POA', help='Is this person the Power of Attorney?') notes = fields.Char(string='Notes')