# -*- coding: utf-8 -*- from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError from datetime import datetime class YuthonMedicalManage(models.Model): _name = 'yuthon.medical.manage' _description = '体检费管理' employee_id = fields.Many2one('hr.employee', string="员工姓名") employee_number = fields.Char( string='序号', related='employee_id.number', store=True, readonly=True) start_date = fields.Date(string='开始日期') year = fields.Char(string="年份") current_year_remaining = fields.Float(string="本年剩余", compute="_compute_current_year_remaining") entry_date = fields.Date(related='employee_id.entry_date', string="入职日期", store=True) remainder_the_year = fields.Float(string="本年剩余") last_year_quota = fields.Float(string="去年额度") last_year_used = fields.Float(string="去年使用", compute="_compute_last_year_used") last_year_remaining = fields.Float(string="去年剩余") prev_last_year_remaining = fields.Float(string="前年剩余") this_year_usage = fields.Float(string='本年使用', compute="_compute_this_year_usage") this_year_quota = fields.Float(string='本年额度') birthday = fields.Date(related='employee_id.birthday', string="出生年月") marital_status = fields.Selection([('married', '已婚'), ('unmarried', '未婚')], related='employee_id.marital', string='婚姻状况') termination_date = fields.Date(string="离退日期", compute="_compute_termination_date") company_id = fields.Many2one(related="employee_id.company_id", string="公司", default=lambda self: self.env.company) @api.depends('employee_id') def _compute_termination_date(self): for record in self: record.termination_date = record.employee_id.contract_id.termination_date @api.depends('this_year_quota', 'this_year_usage', 'last_year_remaining') def _compute_current_year_remaining(self): for record in self: record.current_year_remaining = record.this_year_quota + record.last_year_remaining - record.this_year_usage if record.current_year_remaining != record.remainder_the_year: record.remainder_the_year = record.current_year_remaining def update_remainder_the_year(self): soong_ids = self.env["yuthon.medical.manage"].browse( self._context.get('active_ids', self._context.get('active_id'))) for soong_id in soong_ids: soong_id.remainder_the_year = soong_id.this_year_quota + soong_id.last_year_remaining - soong_id.this_year_usage if soong_id.year: soong_id.start_date = f"{soong_id.year}-01-01" @api.depends('employee_id', 'year') def _compute_last_year_used(self): if not self: return for record in self: line_domain = ([ ('employee_id', '=', record.employee_id.id), ('enable', '=', True), ('medical_date', '>=', f"{str(int(record.year)-1)}-01-01"), ('medical_date', '<=', f"{str(int(record.year)-1)}-12-31") ]) all_lines = self.env['yuthon.medical.line'].search(line_domain) record.last_year_used = sum(all_lines.mapped('medical_fee')) @api.depends('employee_id', 'year') def _compute_this_year_usage(self): if not self: return for record in self: line_domain = ([ ('employee_id', '=', record.employee_id.id), ('enable', '=', True), ('medical_date', '>=', f"{record.year}-01-01"), ('medical_date', '<=', f"{record.year}-12-31") ]) all_lines = self.env['yuthon.medical.line'].search(line_domain) record.this_year_usage = sum(all_lines.mapped('medical_fee')) def _calculate_medical_quota(self, employee, year): current_date = fields.Date.today() valid_rules = self.env['yuthon.medical.rule'].search([ ('start_date', '<=', current_date), '|', ('end_date', '>=', current_date), ('end_date', '=', False) ], order='start_date desc', limit=1) rule = valid_rules id_card = employee.identification_id.strip() if employee.identification_id else '' birth_str = id_card[6:14] birth_date = datetime.strptime(birth_str, '%Y%m%d').date() # 计算周岁(今天 - 出生日期) today = fields.Date.today() age = 0 if birth_date: age = today.year - birth_date.year base_quota = rule.base_amount if age >= rule.starting_age and rule.increase_interval_years > 0 and rule.increment_amount > 0: exceed_age_years = age - rule.starting_age increment_times = exceed_age_years // rule.increase_interval_years base_quota += rule.increment_amount * increment_times if rule.cap > 0: base_quota = min(base_quota, rule.cap) # 女性婚姻状态补助 final_quota = base_quota if employee.user_sex == 'girl': if employee.marital == 'single': final_quota += rule.allowance_unmarried_women elif employee.marital in ['married', 'divorced']: final_quota += rule.allowance_married_women entry_date = fields.Date.from_string(employee.entry_date) if not entry_date: return final_quota entry_year = entry_date.year # 判断是否为当年入职 if entry_year == int(year): entry_month = entry_date.month ratio = (12 - entry_month + 1) / 12 final_quota = final_quota * ratio final_quota = round(max(final_quota, 0.0), 2) return final_quota def medical_manage(self): current_date = fields.Date.today() current_year = str(current_date.year) last_year = str(int(current_year) - 1) valid_rules = self.env['yuthon.medical.rule'].search([ ('start_date', '<=', current_date), '|', ('end_date', '>=', current_date), ('end_date', '=', False) ], order='start_date desc', limit=1) rule = valid_rules target_companies = rule.party_ids.mapped('party') party_setting_records = self.env['yuthon.party.setting'].search([ ('party', 'in', target_companies) ]).ids open_employees = self.env['hr.employee'].search([ ('user_state', '=', 'in'), ('company_id.name', '=', '集团本部'), ('contract_id.party_id', 'in', party_setting_records), ('contract_type_id.name', '!=', '实习协议') ]) for employee in open_employees: medical_manage_rec = self.env['yuthon.medical.manage'].search([ ('employee_id', '=', employee.id), ('year', '=', current_year) ], limit=1) if not medical_manage_rec: medical_manage_rec = self.env['yuthon.medical.manage'].create({ 'employee_id': employee.id, 'year': current_year, 'this_year_usage': 0.0, }) else: continue this_year_quota = self._calculate_medical_quota(employee, current_year) if not this_year_quota: medical_manage_rec.unlink() continue medical_manage_rec.write({'this_year_quota': this_year_quota, }) medical_manage_rec.remainder_the_year = medical_manage_rec.this_year_quota + medical_manage_rec.last_year_remaining - medical_manage_rec.this_year_usage medical_manage_rec.start_date = current_date # 回填上年数据 last_year_rec = self.env['yuthon.medical.manage'].search([ ('employee_id', '=', employee.id), ('year', '=', last_year) ], limit=1) if last_year_rec: medical_manage_rec.write({ 'last_year_quota': last_year_rec.this_year_quota, 'last_year_remaining': min(last_year_rec.this_year_quota * 2, last_year_rec.current_year_remaining), }) # 回填前年数据 prev_last_year_vals = { 'prev_last_year_remaining': min(last_year_rec.this_year_quota * 2, last_year_rec.current_year_remaining), } medical_manage_rec.write(prev_last_year_vals) def compute_medical_manage(self): medical_records = self.env['yuthon.medical.manage'].browse(self.env.context.get('active_ids')) for medical_record in medical_records: current_year = str(medical_record.year) last_year = str(int(current_year) - 1) for employee in medical_record.employee_id: this_year_quota = self._calculate_medical_quota(employee, current_year) medical_record.write({'this_year_quota': this_year_quota, }) medical_record.remainder_the_year = medical_record.this_year_quota + medical_record.last_year_remaining - medical_record.this_year_usage # 回填上年数据 last_year_rec = self.env['yuthon.medical.manage'].search([ ('employee_id', '=', employee.id), ('year', '=', last_year) ], limit=1) if last_year_rec: medical_record.write({ 'last_year_quota': last_year_rec.this_year_quota, 'last_year_remaining': min(last_year_rec.this_year_quota * 2, last_year_rec.current_year_remaining), }) # 回填前年数据 prev_last_year_vals = { 'prev_last_year_remaining': min(last_year_rec.last_year_quota * 2, last_year_rec.last_year_remaining), } medical_record.write(prev_last_year_vals) def button_details(self): tree_id = self.env.ref('yuthon_hr_employee.yuthon_medical_line_tree').id return { "type": "ir.actions.act_window", "name": "查看详情", "res_model": "yuthon.medical.line", "views": [[tree_id, "tree"]], "domain": [('employee_id', '=', self.employee_id.id)], } def property_reimbursement_form(self): form_id = self.env.ref('yuthon_hr_employee_approval.view_property_reimbursement_form_form').id return { "type": "ir.actions.act_window", "name": "报销单", "res_model": "property.reimbursement.form", "views": [[form_id, "form"]], } def medical_manage_wizard(self): return { 'name': '体检费导入', 'type': 'ir.actions.act_window', 'res_model': 'medical.manage.wizard', 'view_mode': 'form', 'target': 'new', } def update_initial_value(self): medical_ids = self.env["yuthon.medical.manage"].browse( self._context.get('active_ids', self._context.get('active_id'))) for rec in medical_ids: current_year = str(rec.year) last_year = str(int(current_year) - 1) prev_last_year = str(int(current_year) - 2) last_year_quota = self._calculate_medical_quota(rec.employee_id, last_year) this_year_quota = self._calculate_medical_quota(rec.employee_id, current_year) prev_last_year_quota = self._calculate_medical_quota(rec.employee_id, prev_last_year) prev_last_year_remaining = min(prev_last_year_quota * 2, rec.prev_last_year_remaining) last_year_remaining = min(last_year_quota * 2, rec.last_year_remaining) rec.write({ 'this_year_quota': this_year_quota, 'last_year_quota': last_year_quota, 'prev_last_year_remaining': prev_last_year_remaining, 'last_year_remaining': last_year_remaining, }) class YuthonMedicalLine(models.Model): _name = 'yuthon.medical.line' _description = '体检费明细' employee_id = fields.Many2one('hr.employee', string="姓名") medical_date = fields.Date(string="报销日期") medical_fee = fields.Float(string="金额") enable = fields.Boolean(string="启用", default=True) remake = fields.Text(string="备注") document_ids = fields.Many2many('ir.attachment', string="附件") @api.constrains('employee_id', 'medical_date') def _check_duplicate_medical_line(self): for rec in self: domain = [ ('employee_id', '=', rec.employee_id.id), ('medical_date', '=', rec.medical_date), ('id', '!=', rec.id) ] duplicate_rec = self.search(domain, limit=1) if duplicate_rec: if not self.env.context.get('confirm_update_medical_line'): raise UserError(_( f"检测到员工【{rec.employee_id.name}】在【{rec.medical_date}】已有体检费记录,请重新选择日期" )) def write(self, vals): res = super(YuthonMedicalLine, self).write(vals) if 'medical_fee' in vals or 'medical_date' in vals or 'employee_id' in vals: for line in self: current_year = fields.Date.from_string(line.medical_date).year employee = line.employee_id current_manage = self.env['yuthon.medical.manage'].search([ ('employee_id', '=', employee.id), ('year', '=', str(current_year)) ], limit=1) if current_manage: current_manage._compute_this_year_usage() current_manage._compute_current_year_remaining() next_manage = self.env['yuthon.medical.manage'].search([ ('employee_id', '=', employee.id), ('year', '=', str(current_year + 1)) ], limit=1) if next_manage: next_manage.last_year_remaining = current_manage.current_year_remaining return res