Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
pengyuthon
2026-02-23 11:27:42 +08:00
26 changed files with 317 additions and 45 deletions
@@ -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)
+18 -8
View File
@@ -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"/>
+1
View File
@@ -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',
+18
View File
@@ -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:
+16 -1
View File
@@ -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
+1 -1
View File
@@ -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,
+33
View File
@@ -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>
+1
View File
@@ -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)
+2 -1
View File
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
102 access_utility_meter_wizard utility.meter.wizard model_utility_meter_wizard base.group_user 1 1 1 1
103 access_invoicing_information_line invoicing.information.line model_invoicing_information_line base.group_user 1 1 1 1
104 access_tenant_import_wizard tenant.import.wizard model_tenant_import_wizard base.group_user 1 1 1 1
105
106
+1
View File
@@ -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