Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
@@ -297,7 +297,10 @@ class SoongWorkflowRecord(models.Model):
|
||||
valid_employee_ids = self.env['hr.employee'].search([
|
||||
('department_id', 'in', department_ids)
|
||||
]).ids
|
||||
domain = [('employee_ids', 'in', valid_employee_ids)]
|
||||
valid_user_ids = self.env['res.users'].search([
|
||||
('employee_id', 'in', valid_employee_ids)
|
||||
]).ids
|
||||
domain = [('users_ids', 'in', valid_user_ids)]
|
||||
if (operator == '=' and not value) or (operator == '!=' and value):
|
||||
domain = ['!'] + domain
|
||||
return domain
|
||||
|
||||
@@ -494,7 +494,6 @@
|
||||
<field name="res_model">soong.workflow.record</field>
|
||||
<field name="view_mode">tree,form,search</field>
|
||||
<field name="context">{'default_order': 'display_end_time desc,id desc'}</field>
|
||||
<!-- <field name="context">{'search_default_group_by_group_soong_state': True, 'search_default_group_by_workflow_id': True}</field>-->
|
||||
<field name="domain">[('reclaim_state', '=', False), ('is_company', '=', True)]</field>
|
||||
<field name="search_view_id" ref="soong_workflow_record_search1"/>
|
||||
<field name="view_id" ref="soong_workflow_record_tree"/>
|
||||
|
||||
@@ -582,7 +582,6 @@ class SoongWorkflowCommentWizard(models.TransientModel):
|
||||
user_ids = [employee.user_id.id for employee in self.employee_ids if employee.user_id]
|
||||
if hasattr(related_record, 'self_users_ids'):
|
||||
related_record.self_users_ids = [(6, 0, user_ids)] if user_ids else [(5,)]
|
||||
workflow_record_id.users_ids = [(6, 0, user_ids)]
|
||||
if self.node_btn_name in ['结束', '确认']:
|
||||
is_work_remind = False
|
||||
current_user = self.env.user
|
||||
@@ -752,7 +751,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
|
||||
description=f"标题为{record_id.name}的{record_id._description}已结束,请归档",
|
||||
url=url, btntxt="查看详情")
|
||||
|
||||
# self.node_btn_id.with_context(cx).run()
|
||||
self.node_btn_id.with_context(cx).run()
|
||||
if self.node_btn_name == "分发":
|
||||
record_id.is_send = True
|
||||
menu_id = self.env['ir.ui.menu'].search([('name', '=', '行政收文')], limit=1)
|
||||
|
||||
@@ -600,14 +600,24 @@ class HolidaysRequestBatch(models.Model):
|
||||
})
|
||||
actual_vacation_report.update_line()
|
||||
actual_vacation_report._compute_working_overtime()
|
||||
domain = [
|
||||
('employee_id', '=', self.users_id.employee_ids.id),
|
||||
('is_visible', '=', False),
|
||||
('is_void', '=', False),
|
||||
('text', '!=', '上月额度继承'),
|
||||
]
|
||||
if self.users_id.employee_ids.company_id.name == '集团本部':
|
||||
domain.append(('start_time', '>=', datetime(2025, 12, 1)),)
|
||||
if self.env.user.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('employee_id.name', '=', '吴欣欣'),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
'&', ('update_type', '=', 'add'),
|
||||
'&', ('workflow_record_id', '!=', False),
|
||||
'&', ('start_time', '>=', datetime(2025, 12, 31, 23, 59, 59)),
|
||||
'&', ('vacation_id', '!=', False),
|
||||
('employee_id.is_annual_salary', '!=', True)
|
||||
]
|
||||
else:
|
||||
domain = [
|
||||
('workflow_record_id', '!=', False),
|
||||
('update_type', '=', 'add'),
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
]
|
||||
overtime_records = self.env['vacation.over.line'].search(domain, order='start_time ASC')
|
||||
remaining_deduct = hours
|
||||
for record in overtime_records:
|
||||
|
||||
@@ -720,6 +720,7 @@ class YuthonVacationReports(models.Model):
|
||||
probation_days = 0 # 试用期天数
|
||||
regular_days = 0 # 转正天数
|
||||
actual_attendance = 0 # 实到天数 = 应到天数 - 请假总天数 - 异常天数
|
||||
leave_total_days = 0 # 累计请假天数
|
||||
is_retired = rec.employee_id.user_state in ('leave', 'withdraw')
|
||||
if rec.employee_id and rec.vacation_date and not is_retired:
|
||||
year = rec.vacation_date.year
|
||||
@@ -740,15 +741,35 @@ class YuthonVacationReports(models.Model):
|
||||
])
|
||||
|
||||
if attendances:
|
||||
# 1. 过滤有效考勤:仅排除休息/异常,保留请假记录
|
||||
valid_attendances = [
|
||||
att for att in attendances
|
||||
if (att.check_in or att.check_out)
|
||||
and att.shift_name != '休息'
|
||||
and (not att.workflow_id or att.workflow_id.workflow_id.name != '请假')
|
||||
and not att.is_abnormal
|
||||
]
|
||||
leave_hours = 0
|
||||
leave_days = 0
|
||||
processed_leave_codes = set() # 记录已处理的请假单code,避免重复扣减
|
||||
|
||||
for att in attendances:
|
||||
if att.workflow_id and att.workflow_id.workflow_id.name == '请假':
|
||||
leave_record = self.env[att.workflow_id.res_model].sudo().browse(att.workflow_id.res_id)
|
||||
if leave_record.code and leave_record.code in processed_leave_codes:
|
||||
continue
|
||||
|
||||
if leave_record.days:
|
||||
leave_days += leave_record.days
|
||||
if leave_record.code:
|
||||
processed_leave_codes.add(leave_record.code)
|
||||
|
||||
total_leave_days = leave_days
|
||||
leave_total_days += leave_days
|
||||
if valid_attendances:
|
||||
actual_attendance += 1
|
||||
daily_actual = max(1 - total_leave_days, 0)
|
||||
actual_attendance += daily_actual
|
||||
|
||||
rec.actual_attendance = max(actual_attendance, 0)
|
||||
|
||||
for att in attendances:
|
||||
if att.is_late_card:
|
||||
@@ -1614,6 +1635,7 @@ class YuthonVacationReports(models.Model):
|
||||
# 原有domain(先筛选出基础数据)
|
||||
if self.env.user.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('employee_id.name', '=', '吴欣欣'),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
|
||||
@@ -109,9 +109,16 @@ class YuthonMedicalManage(models.Model):
|
||||
current_date = fields.Date.today()
|
||||
current_year = str(current_date.year)
|
||||
last_year = str(int(current_year) - 1)
|
||||
target_companies = ['广州市天河投资集团有限公司', '广州市天投城市建设开发有限公司',
|
||||
'广州天投云擎数字科技有限公司']
|
||||
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', '=', '集团本部')
|
||||
('company_id.name', '=', '集团本部'),
|
||||
('contract_id.party_id', 'in', party_setting_records),
|
||||
('active', '=', True)
|
||||
])
|
||||
for employee in open_employees:
|
||||
medical_manage_rec = self.env['yuthon.medical.manage'].search([
|
||||
|
||||
@@ -16,6 +16,7 @@ class YuthonPartySetting(models.Model):
|
||||
address = fields.Text(string='通讯地址')
|
||||
contact = fields.Char(string='联系人')
|
||||
phone = fields.Char(string='电话')
|
||||
level = fields.Selection([('one', '一级'), ('two', '二级'), ('three', '三级')], string='层级')
|
||||
effective_date = fields.Date(string="生效日期", default=fields.Date.today)
|
||||
expiration_date = fields.Date(string="失效日期")
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<field name="address"/>
|
||||
<field name="contact"/>
|
||||
<field name="phone"/>
|
||||
<field name="level"/>
|
||||
<field name="effective_date"/>
|
||||
<field name="expiration_date"/>
|
||||
</tree>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<header>
|
||||
<button name="return_onboard_approval" type="object" string="入职申请" class="oe_highlight"/>
|
||||
<button name="return_onboard_approval" type="object" string="办理入职" class="oe_highlight"/>
|
||||
</header>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
'security/issue_security_group.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/issue_library_code_data.xml',
|
||||
'data/notice_room.xml',
|
||||
'views/issue_library_views.xml',
|
||||
'views/issue_type_views.xml',
|
||||
'views/issue_approval_views.xml',
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record forcecreate="True" id="notice_room_task" model="ir.cron">
|
||||
<field name="name">定时任务:会议通知</field>
|
||||
<field name="model_id" ref="model_room_booking"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.notice_room()</field>
|
||||
<field eval="False" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 09:30:00')"/>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,6 +1,7 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta, date
|
||||
|
||||
|
||||
class RoomBooking(models.Model):
|
||||
@@ -52,6 +53,86 @@ class RoomBooking(models.Model):
|
||||
else:
|
||||
return [('company_id', 'not in', company_ids)]
|
||||
|
||||
def notice_room(self):
|
||||
booking_records = self.search([
|
||||
('work_end', '=', True),
|
||||
('task_time_ids', '!=', False),
|
||||
('start_datetime', '!=', False)
|
||||
])
|
||||
if not booking_records:
|
||||
return
|
||||
|
||||
now = fields.Datetime.now()
|
||||
for booking in booking_records:
|
||||
remind_time_thresholds = []
|
||||
for task_time in booking.task_time_ids:
|
||||
if not task_time.number or task_time.number <= 0:
|
||||
continue
|
||||
if task_time.task_unit == 'min':
|
||||
threshold = timedelta(minutes=task_time.number)
|
||||
elif task_time.task_unit == 'hour':
|
||||
threshold = timedelta(hours=task_time.number)
|
||||
elif task_time.task_unit == 'day':
|
||||
threshold = timedelta(days=task_time.number)
|
||||
else:
|
||||
continue
|
||||
remind_time_thresholds.append(threshold)
|
||||
|
||||
if not remind_time_thresholds:
|
||||
continue
|
||||
|
||||
meeting_start = fields.Datetime.to_datetime(booking.start_datetime) + timedelta(hours=8)
|
||||
actual_diff = meeting_start - now
|
||||
if actual_diff.total_seconds() < 0:
|
||||
continue
|
||||
|
||||
need_remind = False
|
||||
for threshold in remind_time_thresholds:
|
||||
diff_seconds = abs(actual_diff.total_seconds() - threshold.total_seconds())
|
||||
if diff_seconds <= 60: # 误差≤60秒即判定为匹配
|
||||
need_remind = True
|
||||
break
|
||||
|
||||
if need_remind:
|
||||
remind_users = self._get_remind_users(booking)
|
||||
employee_ids = self.env['hr.employee'].search([('user_id', 'in', remind_users.ids)])
|
||||
url = f'https://oa.thtzjt.com/web#id={self.id}&menu_id=9539&action=811&model=room.booking&view_type=form'
|
||||
data = self.name + '\n'
|
||||
time_str = fields.Datetime.to_datetime(self.start_datetime + timedelta(hours=8)).strftime('%H:%M:%S')
|
||||
data += f'会议时间:{time_str}'
|
||||
for employee_id in employee_ids:
|
||||
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='task',
|
||||
user_id=employee_id.wecom_userid,
|
||||
title="会议通知", description=data,
|
||||
url=url, btntxt="详细信息")
|
||||
|
||||
def _get_remind_users(self, booking):
|
||||
remind_user_ids = set()
|
||||
# ① 组织者
|
||||
if booking.organizer_id:
|
||||
remind_user_ids.add(booking.organizer_id.id)
|
||||
# ② 记录人
|
||||
if booking.user_id:
|
||||
remind_user_ids.add(booking.user_id.id)
|
||||
# ③ 出席人员
|
||||
if booking.room_users_ids:
|
||||
remind_user_ids.update(booking.room_users_ids.ids)
|
||||
# ④ 传达人
|
||||
for line in booking.issue_meeting_line_ids:
|
||||
if line.employee_id and line.employee_id.user_id:
|
||||
remind_user_ids.add(line.employee_id.user_id.id)
|
||||
# ⑤ 列席人员
|
||||
for line in booking.issue_meeting_line_ids:
|
||||
if line.attendees_ids:
|
||||
for employee in line.attendees_ids:
|
||||
remind_user_ids.add(employee.user_id.id)
|
||||
|
||||
# ⑥ 主持人
|
||||
if booking.host and booking.host.user_id:
|
||||
remind_user_ids.add(booking.host.user_id.id)
|
||||
return self.env['res.users'].browse(list(remind_user_ids)).filtered(lambda u: u.active)
|
||||
|
||||
|
||||
@api.depends('number_of_meetings', 'meeting_type_id', 'start_datetime')
|
||||
def _compute_name(self):
|
||||
if self.meeting_type_id and self.start_datetime:
|
||||
|
||||
@@ -576,15 +576,30 @@ class IssueLibrary(models.Model):
|
||||
('department_id', 'in', all_dept_ids),
|
||||
('active', '=', True)
|
||||
])
|
||||
target_dept_names = ['办公室', '财务部', '风控法务部', '纪检审计部', '综合办公室']
|
||||
target_managers = self.env['hr.employee'].search([
|
||||
('company_id', '=', current_user.company_id.id),
|
||||
('department_id.name', 'in', target_dept_names),
|
||||
('user_role_id.name', '=', '部门经理'),
|
||||
('active', '=', True)
|
||||
])
|
||||
|
||||
manager_employee = self.env['hr.employee'].search([
|
||||
('id', 'in', target_employees.ids),
|
||||
('id', 'in', target_managers.ids),
|
||||
('user_role_id.name', '=', '部门经理')
|
||||
], limit=1)
|
||||
|
||||
attendees_ids = []
|
||||
if target_managers and manager_employee:
|
||||
attendees_ids = target_managers.filtered(lambda emp: emp.id != manager_employee.id).ids
|
||||
elif target_managers:
|
||||
attendees_ids = target_managers.id
|
||||
for issue in self:
|
||||
issue_details.append((0, 0, {
|
||||
'issue_library_id': issue.id,
|
||||
'employee_id': manager_employee.id,
|
||||
'designated_person_ids': [(6, 0, target_employees.ids)],
|
||||
'attendees_ids': [(6, 0, attendees_ids)],
|
||||
}))
|
||||
if target_meeting_type_id:
|
||||
continue
|
||||
|
||||
@@ -221,7 +221,7 @@ class YuthonNotice(models.Model):
|
||||
msg_id = False
|
||||
touser = "|".join(we_id_list) if we_id_list else ""
|
||||
if touser:
|
||||
url = f'http://oa.thtzjt.com/webh5#/notice?id={self.id}'
|
||||
url = f'https://phone.thtzjt.com/phone/Notice/110?employee_id='
|
||||
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(
|
||||
category='notice',
|
||||
touser=touser,
|
||||
|
||||
@@ -66,6 +66,8 @@ class YuthonOfficial(models.Model):
|
||||
deadline = fields.Datetime(string="办理期限")
|
||||
done_date = fields.Date(string="完成时间")
|
||||
workflow_inspector_ids = fields.One2many('soong.workflow.inspector', 'yuthon_official_id', string="督办事项")
|
||||
approve_state = fields.Selection([("start", "草稿"), ("pending", "审批中"), ("end", "结束")], string="审批状态",
|
||||
compute='_compute_workflow', index=True)
|
||||
room_booking_ids = fields.One2many('room.booking', 'yuthon_official_id', string="会议关联")
|
||||
issue_library_ids = fields.One2many('issue.library', 'yuthon_official_id', string="议题关联")
|
||||
work_end = fields.Boolean(string="结束")
|
||||
@@ -77,6 +79,37 @@ class YuthonOfficial(models.Model):
|
||||
|
||||
name_title = fields.Char(default=_get_default_name_title)
|
||||
self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])])
|
||||
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',
|
||||
search='_search_part_of_company')
|
||||
|
||||
@api.depends('code')
|
||||
def _compute_workflow(self):
|
||||
for record in self:
|
||||
record.approve_state = False
|
||||
workflow = self.env['soong.workflow.record'].sudo().search(
|
||||
[('code', '=', record.code)],
|
||||
limit=1
|
||||
)
|
||||
if workflow:
|
||||
record.approve_state = workflow.approve_state
|
||||
|
||||
@api.depends_context('uid', 'company')
|
||||
@api.depends('company_id')
|
||||
def _compute_is_company(self):
|
||||
active_company_ids = self.env.companies
|
||||
for employee in self:
|
||||
employee.is_company = employee.company_id in active_company_ids
|
||||
|
||||
def _search_part_of_company(self, operator, value):
|
||||
company_ids = self.env.companies.ids # 获取公司ID列表
|
||||
# 处理反向操作符
|
||||
if not value:
|
||||
operator = '!=' if operator == '=' else '='
|
||||
# 使用company_id in company_ids的结构
|
||||
if operator == '=':
|
||||
return [('company_id', 'in', company_ids)]
|
||||
else: # operator == '!='
|
||||
return [('company_id', 'not in', company_ids)]
|
||||
|
||||
@api.depends('name')
|
||||
def _compute_text(self):
|
||||
|
||||
@@ -20,6 +20,7 @@ class YuthonOfficialIncomingNormal(models.Model):
|
||||
main_text = fields.Text(string='正文')
|
||||
come_file_id = fields.Char(string="来文编号")
|
||||
incoming_file_id = fields.Char(string="收文编号")
|
||||
approve_state = fields.Selection([("start", "草稿"), ("pending", "审批中"), ("end", "结束")], string="审批状态", compute='_compute_workflow', index=True)
|
||||
urgency_level = fields.Selection([('normal', '一般'), ('urgent', '紧急'), ('extra_urgent', '特急')], string="紧急程度", default='normal')
|
||||
secret_level = fields.Selection([('normal', '一般'), ('urgent', '秘密'), ('secret', '机密'), ('so_secret', '绝密')], string="密级")
|
||||
time_date = fields.Datetime(string="日期", default=fields.Datetime.now)
|
||||
@@ -45,7 +46,8 @@ class YuthonOfficialIncomingNormal(models.Model):
|
||||
room_booking_ids = fields.One2many('room.booking', 'incoming_normal_id', string="会议关联")
|
||||
issue_library_ids = fields.One2many('issue.library', 'incoming_normal_id', string="议题关联")
|
||||
related_issues_id = fields.Many2one('issue.library', string="关联议题")
|
||||
|
||||
is_void = fields.Boolean(string="作废", compute='_compute_workflow', store=True)
|
||||
|
||||
# 显示用的办结时间(如果有审批日志,取最后一条日志的时间;否则使用write_date)
|
||||
display_end_time = fields.Datetime(
|
||||
string="办结时间",
|
||||
@@ -103,6 +105,20 @@ class YuthonOfficialIncomingNormal(models.Model):
|
||||
else:
|
||||
return [('company_id', 'not in', company_ids)]
|
||||
|
||||
@api.depends('code')
|
||||
def _compute_workflow(self):
|
||||
for record in self:
|
||||
record.approve_state = False
|
||||
record.is_void = False
|
||||
workflow = self.env['soong.workflow.record'].sudo().search(
|
||||
[('code', '=', record.code)],
|
||||
limit=1
|
||||
)
|
||||
if workflow:
|
||||
record.approve_state = workflow.approve_state
|
||||
if workflow.reclaim_state == True:
|
||||
record.is_void = True
|
||||
|
||||
@api.onchange('is_issues')
|
||||
def _onchange_is_issues(self):
|
||||
if self.is_issues:
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
<field name="urgency_level" column_invisible="1"/>
|
||||
<field name="urgency_level" widget="urgency_level_badge" nolabel="1" optional="show"/>
|
||||
<field name="text" string="标题"/>
|
||||
<field name="create_uid" string="创建人"/>
|
||||
<field name="department_id"/>
|
||||
<field name="create_uid" string="创建人" column_invisible="1"/>
|
||||
<field name="department_id" column_invisible="1"/>
|
||||
<field name="time_date"/>
|
||||
<field name="display_end_time" string="办结时间" optional="show"/>
|
||||
<field name="approve_state" string="审批状态" widget="badge"
|
||||
decoration-danger="approve_state == 'end'" decoration-success="approve_state == 'pending'"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -104,7 +106,7 @@
|
||||
<record id="yuthon_official_incoming_normal_action" model="ir.actions.act_window">
|
||||
<field name="name">行政收文</field>
|
||||
<field name="res_model">yuthon.official.incoming.normal</field>
|
||||
<field name="domain">[('is_company','=',True)]</field>
|
||||
<field name="domain">[('is_company','=',True), ('is_void','=',False)]</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -9,11 +9,13 @@
|
||||
<field name="urgency_level" column_invisible="1"/>
|
||||
<field name="urgency_level" widget="urgency_level_badge" nolabel="1" optional="show"/>
|
||||
<field name="text" string="标题"/>
|
||||
<field name="create_uid" string="创建人"/>
|
||||
<field name="create_uid" string="创建人" column_invisible="1"/>
|
||||
<field name="reference_cold"/>
|
||||
<field name="department_id"/>
|
||||
<field name="department_id" column_invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="official_state" readonly="1"/>
|
||||
<field name="approve_state" string="审批状态" widget="badge"
|
||||
decoration-danger="approve_state == 'end'" decoration-success="approve_state == 'pending'"/>
|
||||
<button name="receipt_processing" type="object" string="转收文" class="oe_highlight"/>
|
||||
</tree>
|
||||
</field>
|
||||
@@ -69,6 +71,7 @@
|
||||
</div>
|
||||
<group string="文件基本信息">
|
||||
<group col="2">
|
||||
<field name="is_company" invisible="1"/>
|
||||
<field name="is_back" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="work_end" invisible="1"/>
|
||||
@@ -127,6 +130,7 @@
|
||||
<record id="send_receive_messages_management_action" model="ir.actions.act_window">
|
||||
<field name="name">行政发文</field>
|
||||
<field name="res_model">yuthon.official</field>
|
||||
<field name="domain">[('is_company','=',True)]</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
'wizard/meter_price_wizard.xml',
|
||||
'wizard/property_statements_wizard_views.xml',
|
||||
'wizard/utility_meter_wizard_views.xml',
|
||||
'wizard/tenant_import_wizard_views.xml',
|
||||
'views/menuitem.xml',
|
||||
],
|
||||
'assets': {
|
||||
|
||||
@@ -270,7 +270,7 @@ class MeterArchivesLine(models.Model):
|
||||
type = fields.Selection([("public", "公共"), ("fees", "收费")], string="类型")
|
||||
employee_ids = fields.Many2many("hr.employee", string="抄表人")
|
||||
remarks = fields.Text(string="备注")
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="yes", string="是否固定线损")
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定线损")
|
||||
is_fix_price = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定单价")
|
||||
archives_state = fields.Selection([("stop", "停用"), ("use", "正常"), ("rent", "出租")], string="状态", default="use",)
|
||||
state = fields.Selection([("reading", "已抄"), ("unreading", "未抄")], string="抄表状态", default="unreading",)
|
||||
|
||||
@@ -236,7 +236,7 @@ class MeterWaterLine(models.Model):
|
||||
remarks = fields.Text(string='备注')
|
||||
employee_ids = fields.Many2many('hr.employee', string='抄表人')
|
||||
maximum_count = fields.Integer(string='最大位数')
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="yes", string="是否固定线损")
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定线损")
|
||||
is_fix_price = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定单价")
|
||||
is_push = fields.Boolean(string='推送', default=False)
|
||||
|
||||
|
||||
@@ -102,4 +102,5 @@ access_meter_water_line, meter.water.line,model_meter_water_line,base.group_user
|
||||
access_yuthon_epitope_values, yuthon.epitope.values,model_yuthon_epitope_values,base.group_user,1,1,1,1
|
||||
access_task_push_wizard,task.push.wizard,model_task_push_wizard,base.group_user,1,1,1,1
|
||||
access_utility_meter_wizard,utility.meter.wizard,model_utility_meter_wizard,base.group_user,1,1,1,1
|
||||
access_invoicing_information_line,invoicing.information.line,model_invoicing_information_line,base.group_user,1,1,1,1
|
||||
access_invoicing_information_line,invoicing.information.line,model_invoicing_information_line,base.group_user,1,1,1,1
|
||||
access_tenant_import_wizard,tenant.import.wizard,model_tenant_import_wizard,base.group_user,1,1,1,1
|
||||
|
@@ -11,3 +11,4 @@ from . import initialize_wizard
|
||||
from . import electric_water_wizard
|
||||
from . import task_push_wizard
|
||||
from . import utility_meter_wizard
|
||||
from . import tenant_import_wizard
|
||||
|
||||
@@ -651,13 +651,9 @@ class PropertyLeaseContract(models.Model):
|
||||
if rec.electricity_fee_ids:
|
||||
for line in rec.electricity_fee_ids:
|
||||
line.is_fix_loss = 'no'
|
||||
if line.unit_price > 0:
|
||||
line.is_fix_price = 'no'
|
||||
if rec.water_fee_ids:
|
||||
for line in rec.water_fee_ids:
|
||||
line.is_fix_loss = 'no'
|
||||
if line.unit_price > 0:
|
||||
line.is_fix_price = 'yes'
|
||||
|
||||
def update_admin_id(self):
|
||||
contract_ids = self.env["property.lease.contract"].browse(
|
||||
@@ -670,13 +666,11 @@ class PropertyLeaseContract(models.Model):
|
||||
for fee in rec.water_fee_ids:
|
||||
fee.line_loss = 0.02
|
||||
|
||||
|
||||
@api.onchange("lease_ids")
|
||||
def onchange_lease_ids(self):
|
||||
for rec in self:
|
||||
start = rec.term_start_time
|
||||
end = rec.term_end_time
|
||||
# 初始化值
|
||||
rec.is_water = False
|
||||
rec.is_electricity = False
|
||||
rec.water_fee_ids = [(5, 0, 0)]
|
||||
@@ -685,18 +679,24 @@ class PropertyLeaseContract(models.Model):
|
||||
water_fee_values = []
|
||||
message_fee_values = []
|
||||
electricity_fee_values = []
|
||||
|
||||
# 用于去重的ID集合:记录已添加的水表/电表ID
|
||||
added_water_meter_ids = set()
|
||||
added_electric_meter_ids = set()
|
||||
|
||||
if rec.lease_ids:
|
||||
# 处理每个租赁记录
|
||||
for lease in rec.lease_ids:
|
||||
if lease.address_id:
|
||||
# 处理水表
|
||||
# 处理水表:增加去重逻辑
|
||||
if lease.address_id.is_meter and lease.address_id.water_meter_ids:
|
||||
active_water_meters = lease.address_id.water_meter_ids.filtered(
|
||||
lambda m: not m.demolition_date or m.demolition_date > fields.Date.today()
|
||||
)
|
||||
for meter in active_water_meters:
|
||||
# 确保meter记录有效
|
||||
if meter.property_meter_water_id:
|
||||
if meter.property_meter_water_id and meter.property_meter_water_id.id not in added_water_meter_ids:
|
||||
# 记录已添加的水表ID,防止重复
|
||||
added_water_meter_ids.add(meter.property_meter_water_id.id)
|
||||
water_fee_values.append([0, 0, {
|
||||
'contract_id': rec.id,
|
||||
'property_meter_water_id': meter.property_meter_water_id.id,
|
||||
@@ -708,8 +708,10 @@ class PropertyLeaseContract(models.Model):
|
||||
lambda m: not m.demolition_date or m.demolition_date > fields.Date.today()
|
||||
)
|
||||
for meter in active_electric_meters:
|
||||
# 确保meter记录有效
|
||||
if meter.property_meter_archives_id:
|
||||
# 确保meter记录有效 + 未被添加过
|
||||
if meter.property_meter_archives_id and meter.property_meter_archives_id.id not in added_electric_meter_ids:
|
||||
# 记录已添加的电表ID,防止重复
|
||||
added_electric_meter_ids.add(meter.property_meter_archives_id.id)
|
||||
electricity_fee_values.append([0, 0, {
|
||||
'contract_id': rec.id,
|
||||
'property_meter_archives_id': meter.property_meter_archives_id.id,
|
||||
@@ -2472,7 +2474,7 @@ class PropertyWaterFeeLine(models.Model):
|
||||
end_date = fields.Date(related='contract_id.term_end_time', string="结束日期")
|
||||
unit_price = fields.Float(string='固定单价', digits=(16, 4))
|
||||
line_loss = fields.Float(string='固定线损', digits=(16, 4))
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="yes", string="是否固定线损")
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定线损")
|
||||
is_fix_price = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定单价")
|
||||
epitope_values_id = fields.Many2one(related='property_meter_water_id.epitope_values_id', string="表位置")
|
||||
|
||||
@@ -2499,7 +2501,7 @@ class PropertyElectricFeeLine(models.Model):
|
||||
end_date = fields.Date(related='contract_id.term_end_time', string="结束日期")
|
||||
unit_price = fields.Float(string='固定单价', digits=(16, 4))
|
||||
line_loss = fields.Float(string='固定线损', digits=(16, 4))
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="yes", string="是否固定线损")
|
||||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定线损")
|
||||
is_fix_price = fields.Selection([('yes', '是'),('no', '否')], default="no", string="是否固定单价")
|
||||
epitope_values_id = fields.Many2one(related='property_meter_archives_id.epitope_values_id', string="表位置")
|
||||
|
||||
|
||||
@@ -77,7 +77,11 @@ class CurrentReceivableRentReport(models.Model):
|
||||
"序号": idx,
|
||||
"应收开始": rec.due_date.strftime("%Y-%m-%d") if rec.due_date else "",
|
||||
"应收结束": rec.due_date_end.strftime("%Y-%m-%d") if rec.due_date_end else "",
|
||||
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
|
||||
"承租方名称": {
|
||||
'model': 'property.tenant.information',
|
||||
'id': rec.lessee_id.id,
|
||||
'display_name': rec.lessee_id.name,
|
||||
} if rec.lessee_id else "",
|
||||
"原始金额": round(amt, 2),
|
||||
"减免金额": round(discount, 2) if discount else "",
|
||||
"实际金额(含税)": round(actual_amt, 2),
|
||||
@@ -85,8 +89,16 @@ class CurrentReceivableRentReport(models.Model):
|
||||
"实际金额(不含税)": round(actual_amt_nt, 2),
|
||||
"物业名称": rec.property_name or "",
|
||||
"产权证": rec.title_deed or "",
|
||||
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
|
||||
"管理员": rec.admin_id.name if rec.admin_id else "",
|
||||
"合同编号": {
|
||||
'model': 'property.lease.contract',
|
||||
'id': rec.contract_id.id,
|
||||
'display_name': rec.contract_id.contract_code,
|
||||
} if rec.contract_id else "",
|
||||
"管理员": {
|
||||
'model': 'res.users',
|
||||
'id': rec.admin_id.id,
|
||||
'display_name': rec.admin_id.name,
|
||||
} if rec.admin_id else "",
|
||||
"acc_type": rec.acc_type if rec.acc_type else False,
|
||||
})
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class ReceivableReport extends Component {
|
||||
this.gridRef = useRef("grid");
|
||||
console.log(this.props.action.context.report_config)
|
||||
|
||||
this.serviceOrm = useService("orm")
|
||||
this.serviceOrm = useService("orm") this.actionService = useService("action"); // 获取 actionService 用于跳转
|
||||
|
||||
// 计算 本月的开始,结束
|
||||
|
||||
@@ -46,10 +46,17 @@ class ReceivableReport extends Component {
|
||||
onMounted(async () => {
|
||||
// 加载 ag-grid
|
||||
//await loadJS("https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js");
|
||||
|
||||
// 统一处理 columns:给所有列添加穿透渲染器
|
||||
const columns = this.config.columns || [];
|
||||
columns.forEach((col) => {
|
||||
col.cellRenderer = this.linkRender.bind(this);
|
||||
});
|
||||
|
||||
this.gridOptions = {
|
||||
localeText: AG_GRID_LOCALE_CN,
|
||||
theme: agGrid.themeMaterial,
|
||||
columnDefs: this.config.columns || [],
|
||||
columnDefs: columns,
|
||||
rowData: [],
|
||||
rowClassRules: {
|
||||
'ag-row-new-rent': function(params) {
|
||||
@@ -102,6 +109,9 @@ class ReceivableReport extends Component {
|
||||
items.push("chartRange");
|
||||
return items;
|
||||
},
|
||||
context: {
|
||||
actionService: this.actionService // 传递 actionService 用于穿透跳转
|
||||
}
|
||||
|
||||
};
|
||||
this.grid = new agGrid.createGrid(this.gridRef.el, this.gridOptions);
|
||||
@@ -109,6 +119,39 @@ class ReceivableReport extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 穿透渲染器:将对象格式的字段渲染为可点击的链接
|
||||
* 如果字段值是对象(包含 model、id、display_name),则渲染为可点击链接
|
||||
* 否则直接显示原值
|
||||
*/
|
||||
linkRender(params) {
|
||||
if (!params.value) return '';
|
||||
|
||||
// 如果是对象格式(包含 model 和 id),渲染为可点击链接
|
||||
if (typeof params.value === 'object' && params.value.model && params.value.id) {
|
||||
const { model, id, display_name } = params.value;
|
||||
|
||||
const el = document.createElement('div');
|
||||
el.className = 'o_record_cell';
|
||||
el.innerText = display_name || '';
|
||||
el.classList.add('btn');
|
||||
el.classList.add('btn-link');
|
||||
el.onclick = () => {
|
||||
params.context.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: model,
|
||||
res_id: id,
|
||||
views: [[false, 'form']]
|
||||
});
|
||||
};
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
// 否则直接返回原值(纯文本/数字)
|
||||
return params.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变布尔值
|
||||
* @param e
|
||||
|
||||
Reference in New Issue
Block a user