添加功能

This commit is contained in:
李鹏宇
2026-06-24 18:19:03 +08:00
parent 78b06329d2
commit 61b6cd7da3
11 changed files with 289 additions and 3 deletions
+1
View File
@@ -40,6 +40,7 @@
'wizard/hr_job_add_wizard_views.xml',
'wizard/medical_manage_wizard_viwes.xml',
'wizard/reset_password_wizard_viwes.xml',
'views/yuthon_hr_task_views.xml',
],
'assets': {
'web.assets_backend': [
@@ -68,5 +68,31 @@
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="cron_retire_reminder" model="ir.cron">
<field name="name">定时任务:退休提醒</field>
<field name="model_id" ref="model_hr_employee"/>
<field name="state">code</field>
<field name="code">model.task_retire_reminder()</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now() + relativedelta(hour=9, minute=0, second=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="cron_separation_reminder" model="ir.cron">
<field name="name">定时任务:离职提醒</field>
<field name="model_id" ref="model_hr_employee"/>
<field name="state">code</field>
<field name="code">model.task_separation_reminder()</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now() + relativedelta(hour=9, minute=0, second=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
</data>
</odoo>
+1
View File
@@ -14,4 +14,5 @@ from . import soong_user_role
from . import yuthon_medical_manage
from . import yuthon_medical_rule
from . import res_users
from . import yuthon_hr_task
@@ -1230,3 +1230,107 @@ class YuthonHrEmployee(models.Model):
url=url, btntxt="详细信息")
employee.write({'is_create': True})
def _get_notify_users(self, task_config):
"""从 task_config 获取需要通知的用户列表.
"""
user_ids = set(task_config.user_ids.ids)
role_ids = set(task_config.users_role_group_ids.mapped('user_role_ids').ids)
if task_config.users_role_id:
role_ids.add(task_config.users_role_id.id)
if role_ids:
all_target_roles = self.env['soong.user.role'].browse(list(role_ids))
role_employees = self.env['hr.employee'].search([
'|',
('user_role_id', 'in', all_target_roles.ids),
('user_role_ids', 'in', all_target_roles.ids),
('user_id', '!=', False),
])
for emp in role_employees:
user_ids.add(emp.user_id.id)
return self.env['res.users'].browse(list(user_ids))
def task_retire_reminder(self):
today = fields.Date.today()
target_date = today + timedelta(days=30)
employees = self.search([
('retire_date', '=', target_date),
('user_state', 'in', ['in', 'borrow', 'expatriate', 'adjunct']),
])
if not employees:
return
task_config = self.env['yuthon.hr.task'].search([('cron_id.name', '=', '定时任务:退休提醒')], limit=1)
if not task_config:
return
notify_users = self._get_notify_users(task_config)
if not notify_users:
return
count = len(employees)
for user in notify_users:
hr_employee = user.employee_ids.filtered(lambda e: e.wecom_userid)[:1]
if not hr_employee:
continue
result_data = self.sudo().env['wecom.apps'].sync_send_message_textcard(
category='oa',
user_id=hr_employee.wecom_userid,
title='退休提醒',
description=f"提醒:有{count}个人即将退休,请移至工作台查看。",
url='https://oa.thtzjt.com/web?debug=1#action=3584&cids=1&menu_id=9668',
btntxt='详细信息',
)
confirm = self.env['yuthon.task.confirm'].create({
'task_id': task_config.id,
'user_id': user.id,
'state': 'read',
})
if result_data and len(result_data) > 1:
msg_id = result_data[1].get('msgid')
if msg_id:
confirm.sudo().write({'msg_id': msg_id})
def task_separation_reminder(self):
today = fields.Date.today()
target_date = today + timedelta(days=30)
workflow_records = self.env['soong.workflow.record'].search([
('workflow_id.name', '=', '离职申请'),
('approve_state', '=', 'end'),
('reclaim_state', '=', False),
])
leaving_ids = []
for record in workflow_records:
try:
leaving = self.env[record.res_model].browse(record.res_id)
if leaving and leaving.separation_date == target_date and not leaving.is_execution:
leaving_ids.append(leaving)
except Exception:
continue
if not leaving_ids:
return
task_config = self.env['yuthon.hr.task'].search([('cron_id.name', '=', '定时任务:离职提醒')], limit=1)
if not task_config:
return
notify_users = self._get_notify_users(task_config)
if not notify_users:
return
count = len(leaving_ids)
for user in notify_users:
hr_employee = user.employee_ids.filtered(lambda e: e.wecom_userid)[:1]
if not hr_employee:
continue
result_data = self.sudo().env['wecom.apps'].sync_send_message_textcard(
category='oa',
user_id=hr_employee.wecom_userid,
title='离职提醒',
description=f"提醒:有{count}个人即将离职,请移至工作台查看。",
url='https://oa.thtzjt.com/web?debug=1#action=3584&cids=1&menu_id=9668',
btntxt='详细信息',
)
confirm = self.env['yuthon.task.confirm'].create({
'task_id': task_config.id,
'user_id': user.id,
'state': 'read',
})
if result_data and len(result_data) > 1:
msg_id = result_data[1].get('msgid')
if msg_id:
confirm.sudo().write({'msg_id': msg_id})
@@ -24,4 +24,6 @@ access_child_care_line,child_care_line,model_child_care_line,base.group_user,1,1
access_child_education_line,child_education_line,model_child_education_line,base.group_user,1,1,1,1
access_elderly_support_line,elderly_support_line,model_elderly_support_line,base.group_user,1,1,1,1
access_reset_password_wizard, reset_password_wizard,model_reset_password_wizard,base.group_user,1,1,1,1
access_medical_manage_wizard, medical_manage_wizard,model_medical_manage_wizard,base.group_user,1,1,1,1
access_medical_manage_wizard, medical_manage_wizard,model_medical_manage_wizard,base.group_user,1,1,1,1
access_yuthon_hr_task,yuthon_hr_task,model_yuthon_hr_task,base.group_user,1,1,1,1
access_yuthon_task_confirm,yuthon_task_confirm,model_yuthon_task_confirm,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
24 access_child_education_line child_education_line model_child_education_line base.group_user 1 1 1 1
25 access_elderly_support_line elderly_support_line model_elderly_support_line base.group_user 1 1 1 1
26 access_reset_password_wizard reset_password_wizard model_reset_password_wizard base.group_user 1 1 1 1
27 access_medical_manage_wizard medical_manage_wizard model_medical_manage_wizard base.group_user 1 1 1 1
28 access_yuthon_hr_task yuthon_hr_task model_yuthon_hr_task base.group_user 1 1 1 1
29 access_yuthon_task_confirm yuthon_task_confirm model_yuthon_task_confirm base.group_user 1 1 1 1
@@ -255,6 +255,7 @@ class ReceivableModifyLine(models.Model):
continue
all_lines = self.search([('receivable_id', '=', rec.receivable_id.id)])
all_lines |= rec # 确保当前记录包含在内(处理新建未保存的记录)
sorted_lines = all_lines.sorted(
key=lambda l: (
0 if l.reconciliation_date else 1,
@@ -270,6 +271,9 @@ class ReceivableModifyLine(models.Model):
rec.delivered1 = rec.receivable_id.original_amount if remaining <= 0 else remaining
break
cumsum += line.amount
else:
# 兜底:确保 delivered1 总是被赋值
rec.delivered1 = 0
remake = fields.Text(related='receivable_id.remark', string='备注', store=False)
# lessee_id = fields.Many2one(
+1
View File
@@ -88,6 +88,7 @@
'views/property_delegated_agent_views.xml',
'views/property_transaction_confirmed.xml',
'views/property_meeting_approval_views.xml',
'views/yuthon_property_task_views.xml',
'views/receivable_modify_line_views.xml',
'wizard/property_add_wizard_views.xml',
'wizard/property_contract_wizard.xml',
@@ -40,4 +40,30 @@
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="cron_property_vacant_reminder" model="ir.cron">
<field name="name">定时任务:物业空置提醒</field>
<field name="model_id" ref="model_property_lease_contract"/>
<field name="state">code</field>
<field name="code">model.task_property_vacant_reminder()</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(datetime.now() + relativedelta(hour=9, minute=0, second=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="cron_property_contract_reminder" model="ir.cron">
<field name="name">定时任务:物业合同到期提醒</field>
<field name="model_id" ref="model_property_lease_contract"/>
<field name="state">code</field>
<field name="code">model.task_property_contract_reminder()</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(datetime.now() + relativedelta(hour=9, minute=0, second=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
</odoo>
+2 -1
View File
@@ -63,4 +63,5 @@ from . import property_payment_notice
from . import property_bills_list
from . import property_fee_spending
from . import property_fee_form
from . import receivable_modify_line
from . import receivable_modify_line
from . import yuthon_property_task
@@ -2547,6 +2547,124 @@ class PropertyLeaseContract(models.Model):
report_ref = self.env.ref('yuthon_property_approval.action_property_lease_contract_print_report')
return report_ref.report_action(self)
def _get_property_notify_users(self, task_config):
"""从 task_config 获取需要通知的用户列表.
- user_ids: 直接指定的人
- users_role_id + users_role_group_ids: 角色组 -> 角色 -> 花名册中 user_role_id(主角色)或 user_role_ids(辅助角色)匹配的员工
"""
user_ids = set(task_config.user_ids.ids)
role_ids = set(task_config.users_role_group_ids.mapped('user_role_ids').ids)
if task_config.users_role_id:
role_ids.add(task_config.users_role_id.id)
if role_ids:
all_target_roles = self.env['soong.user.role'].browse(list(role_ids))
role_employees = self.env['hr.employee'].search([
'|',
('user_role_id', 'in', all_target_roles.ids),
('user_role_ids', 'in', all_target_roles.ids),
('user_id', '!=', False),
])
for emp in role_employees:
user_ids.add(emp.user_id.id)
return self.env['res.users'].browse(list(user_ids))
def task_property_vacant_reminder(self):
"""物业空置提醒:以物业为单位,合同实际结束日期距今已3个月或以上,或没有合同."""
today = fields.Date.today()
three_months_ago = today - timedelta(days=90)
properties = self.env['yuthon.property'].search([])
vacant_properties = self.env['yuthon.property']
for prop in properties:
contract_lines = self.env['property.lease.contract.line'].search([
('address_id', '=', prop.id),
])
if not contract_lines:
vacant_properties |= prop
continue
contracts = contract_lines.mapped('lease_id')
active_contracts = contracts.filtered(
lambda c: c.contact_state not in ['complete', 'terminate', 'early_termination', 'settle']
)
if active_contracts:
continue
ended_contracts = contracts.filtered(
lambda c: c.contact_state in ['complete', 'terminate', 'early_termination', 'settle'] and c.actual_end_time
)
if not ended_contracts:
continue
last_end_time = max(ended_contracts.mapped('actual_end_time'))
if last_end_time <= three_months_ago:
vacant_properties |= prop
if not vacant_properties:
return
task_config = self.env['yuthon.property.task'].search([('cron_id.name', '=', '定时任务:物业空置提醒')], limit=1)
if not task_config:
return
notify_users = self._get_property_notify_users(task_config)
if not notify_users:
return
count = len(vacant_properties)
for user in notify_users:
hr_employee = user.employee_ids.filtered(lambda e: e.wecom_userid)[:1]
if not hr_employee:
continue
result_data = self.sudo().env['wecom.apps'].sync_send_message_textcard(
category='oa',
user_id=hr_employee.wecom_userid,
title='物业空置提醒',
description=f"提醒:有{count}个物业已空置,请移至工作台查看。",
url='https://oa.thtzjt.com/web?debug=1#action=3584&cids=1&menu_id=9668',
btntxt='详细信息',
)
confirm = self.env['yuthon.property.task.confirm'].create({
'task_id': task_config.id,
'user_id': user.id,
'state': 'read',
})
if result_data and len(result_data) > 1:
msg_id = result_data[1].get('msgid')
if msg_id:
confirm.sudo().write({'msg_id': msg_id})
def task_property_contract_reminder(self):
"""物业合同到期提醒:距实际结束日期还有3个月."""
today = fields.Date.today()
target_date = today + timedelta(days=90)
contracts = self.search([
('actual_end_time', '=', target_date),
('contact_state', 'in', ['to', 'ing']),
])
if not contracts:
return
task_config = self.env['yuthon.property.task'].search([('cron_id.name', '=', '定时任务:物业合同到期提醒')], limit=1)
if not task_config:
return
notify_users = self._get_property_notify_users(task_config)
if not notify_users:
return
count = len(contracts)
for user in notify_users:
hr_employee = user.employee_ids.filtered(lambda e: e.wecom_userid)[:1]
if not hr_employee:
continue
result_data = self.sudo().env['wecom.apps'].sync_send_message_textcard(
category='oa',
user_id=hr_employee.wecom_userid,
title='物业合同到期提醒',
description=f"提醒:有{count}个物业即将到期,请移至工作台查看。",
url='https://oa.thtzjt.com/web?debug=1#action=3584&cids=1&menu_id=9668',
btntxt='详细信息',
)
confirm = self.env['yuthon.property.task.confirm'].create({
'task_id': task_config.id,
'user_id': user.id,
'state': 'read',
})
if result_data and len(result_data) > 1:
msg_id = result_data[1].get('msgid')
if msg_id:
confirm.sudo().write({'msg_id': msg_id})
class PropertyLeaseContractLine(models.Model):
_name = 'property.lease.contract.line'
_description = '物业列表明细行'
@@ -143,4 +143,6 @@ access_refund_wizard,refund_wizard,model_refund_wizard,base.group_user,1,1,1,1
access_refund_wizard_line,refund_wizard_line,model_refund_wizard_line,base.group_user,1,1,1,1
access_property_business_type,property_business_type,model_property_business_type,base.group_user,1,1,1,1
access_property_payment_type,property_payment_type,model_property_payment_type,base.group_user,1,1,1,1
access_property_contract_rental_wizard,property_contract_rental_wizard,model_property_contract_rental_wizard,base.group_user,1,1,1,1
access_property_contract_rental_wizard,property_contract_rental_wizard,model_property_contract_rental_wizard,base.group_user,1,1,1,1
access_yuthon_property_task,yuthon_property_task,model_yuthon_property_task,base.group_user,1,1,1,1
access_yuthon_property_task_confirm,yuthon_property_task_confirm,model_yuthon_property_task_confirm,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
143
144
145
146
147
148