Files
gzth/room/models/room_booking.py
T
2026-07-06 16:37:09 +08:00

402 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import qrcode
import base64
from io import BytesIO
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError, UserError
import os
from datetime import timedelta
from datetime import datetime
"""富文本字段保存无效"""
class RoomBooking(models.Model):
_name = "room.booking"
_inherit = ['mail.thread.main.attachment', 'mail.activity.mixin', 'soong.workflow.mixin']
_description = "Room Booking"
_order = "start_datetime desc, id"
name = fields.Char(string="标题", compute="_compute_name", store=True)
code = fields.Char(string="流水号", default=lambda self: self.env['ir.sequence'].next_by_code('room_booking_approval_code'))
room_id = fields.Many2one("room.room", string="Room", required=True, ondelete="cascade",
group_expand="_read_group_room_id", tracking=4)
start_datetime = fields.Datetime(string="Start Datetime", required=True, tracking=2)
start_datetime_local = fields.Datetime(string="本地开始时间", compute="_compute_start_datetime_local", store=False)
stop_datetime = fields.Datetime(string="End Datetime", required=True, tracking=3)
organizer_id = fields.Many2one("res.users", string="Organizer",
default=lambda self: self.env.user.id if not self.env.user._is_public() else False,
tracking=5)
office_id = fields.Many2one(related="room_id.office_id", string="Office", readonly=True, store=True)
company_id = fields.Many2one(related="user_id.employee_id.company_id", string="Company", readonly=True, store=True)
department_id = fields.Many2one('hr.department', related='user_id.employee_id.department_id', string="部门")
is_notice = fields.Boolean(string="是否发布通知", default=True)
user_id = fields.Many2one('res.users', string='申请人', default=lambda self: self.env.user.id)
task_time_ids = fields.Many2many('yuthon.task.time', string='提醒')
meeting_type = fields.Selection([('up', '线上'), ('down', '线下'), ('exterior', '外部')], default='down', string="会议形式",
required=True)
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
default='normal')
meeting_type_id = fields.Many2one('room.meeting.type', string="会议类型")
self_users_ids = fields.Many2many(
'res.users',
string="当前经办人",
relation='issue_room_approval_self_users_res_users_rel',
column1='approval_id',
column2='self_user_id',
default=lambda self: [(6, 0, [self.env.user.id])]
)
room_users_ids = fields.Many2many(
'res.users',
string="参会人员",
relation='issue_room_approval_room_users_res_users_rel',
column1='approval_id',
column2='room_user_id'
)
room_users_names = fields.Char(string="参会人员char", compute="_compute_room_users", store=True)
other_company_id = fields.Many2one('res.partner', string="外部公司")
host = fields.Many2one('hr.employee', string="主持人")
stationed_group = fields.Boolean(string="派驻组")
recorder_ids = fields.Many2many('hr.employee', string='记录员')
other_company_user_ids = fields.Many2many('other.meet.employee', string="其他人员")
fleet_id = fields.Many2one('fleet.vehicle', string="车辆")
meeting_place = fields.Char(string="会议地点")
meeting_sing_ids = fields.One2many('room.meeting.sign', 'room_booking_id', string="签到详情")
meeting_data = fields.Html(string="会议说明", sanitize=False)
wechat_meetingid = fields.Char("企业微信会议室ID")
documents_ids = fields.Many2many('ir.attachment', string="会议材料附件")
is_meeting = fields.Boolean(compute="_compute_is_meeting", store=True)
should_user = fields.Integer(string="应到")
right_user = fields.Integer(string="实到")
driver = fields.Many2one('hr.employee', string="司机")
partner_ids = fields.Many2many('res.partner', string="日程安排人员")
is_this_company = fields.Boolean(string='本公司', default=True)
work_end = fields.Boolean(string="结束")
name_title = fields.Char(string="会议通知")
# 关联流程
relevancy_type = fields.Selection([('none', '无')], string="关联流程类型")
@api.depends('meeting_type_id')
def _compute_is_meeting(self):
for record in self:
if record.meeting_type_id:
record.is_meeting = record.meeting_type_id.name in ['党委会', '班子会', '董事会']
else:
record.is_meeting = False
@api.depends('room_users_ids')
def _compute_room_users(self):
for i in self:
employee_list = [rec.name for rec in i.room_users_ids]
i.room_users_names = ','.join(employee_list)
# @api.model_create_multi
# def create(self, vals_list):
# bookings = super(RoomBooking, self).create(vals_list)
# for room, bookings in bookings.grouped("room_id").items():
# room._notify_booking_view("create", bookings)
# data = vals_list[0]
# if data.get('partner_ids'):
# self.env['calendar.event'].create({
# 'name': data['name'],
# 'start': data['start_datetime'],
# 'stop': data['stop_datetime'],
# 'partner_ids': data['partner_ids'],
# 'sth_type': 'meet',
# 'room_booking_id': bookings.id,
# })
# return bookings
@api.onchange('room_id')
def onchange_meeting_data(self):
self.meeting_data = self.room_id.description
def button_meeting_data(self):
"""
<select>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
<option value="option3">Option 3</option>
</select>
"""
if self.room_users_ids:
# 便利内部人员和其他人员
html_select = '<select>\n<option value="name0"> </option>\n'
for index, user in enumerate(self.room_users_ids, start=1):
value = f'name{index}'
option_text = user.name
html_select += f' <option value="{value}">{option_text}</option>\n'
for index, partner in enumerate(self.other_company_user_ids, start=99):
value_partner = f'name{index}'
option_partner_name = partner.name
html_select += f' <option value="{value_partner}">{option_partner_name}</option>\n'
html_select += '</select>'
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
file_path_template = os.path.join(parent_dir, 'static', 'src', 'html', 'template1.html')
with open(file_path_template, 'r', encoding='utf-8') as file:
html_content = file.read().replace('姓名', html_select)
self.write({'meeting_data': html_content})
# @api.onchange('department_ids', 'employee_ids', 'users_role_ids', 'room_users_ids')
# def onchange_partner_ids(self):
# for par in self:
# if par.room_users_ids:
# res_users_list = [users.partner_id.id.origin for users in par.room_users_ids]
# par.partner_ids = [(6, 0, res_users_list)]
# else:
# par.partner_ids = [(5, 0, 0)]
def meet_notice(self):
if self.is_notice:
for internal in self.room_users_ids:
self.env['room.meeting.sign'].create({
'room_booking_id': self.id,
'sign_user_name': internal.name,
'partner_name': internal.employee_id.department_id.name,
'job': internal.employee_id.job_id.name,
'mobile': internal.employee_id.mobile_phone,
'user_type': 'insiders',
'sign_type': 'no',
})
event = self.env['calendar.event'].create({
'name': self.name,
'start': self.start_datetime,
'stop': self.stop_datetime,
})
for partner in self.room_users_ids.employee_ids:
self.env['calendar.attendee'].create({
'event_id': event.id,
'partner_id': partner.address_id.id,
})
task_type = self.env['task.type.setting'].search([('name', '=', '面试')])
employee_records = self.env['hr.employee'].search([('user_id', 'in', self.room_users_ids.ids)])
if not task_type:
task_type = self.env['task.type.setting'].create({
'name': '面试',
})
date_part = self.start_datetime.date()
self.env['yuthon.will.task'].create({
'start_date': date_part,
'task_type':task_type.id,
'employee_ids': [(6, 0, employee_records.ids)],
'task_state':'will',
'name': '面试安排'
})
# 企业用户发送通知
partner_ids = self.sudo().env['res.users'].search([('id', 'in', self.room_users_ids.ids)]).mapped(
'partner_id').ids
self.sudo().with_context(lang=self.env.lang)._message_auto_subscribe_notify(partner_ids, 'room.message_yuthon_room_users_data')
def meeting_sign_in(self):
# 页面填写信息之后返回给地址
code_url = self.get_base_url() + "/web/room?room_id=" + str(self.id)
# 制作填写表单的地址
qrcode_url = qrcode.make(code_url)
buffer = BytesIO()
qrcode_url.save(buffer, format='PNG')
qrcode_image = base64.b64encode(buffer.getvalue()).decode('utf-8')
# 返回给向导试图的字段
action = {
'type': 'ir.actions.act_window',
'name': _("签到二维码"),
'res_model': 'room.booking.sign.wizard',
'views': [(False, 'form')],
'target': 'new',
'context': {
'default_qrcode_image': qrcode_image,
}
}
return action
def meeting_partner_create(self):
# 页面填写信息之后返回给地址
code_url = self.get_base_url() + "/web/room/partner/create?room_id=" + str(self.id)
# 制作填写表单的地址
qrcode_url = qrcode.make(code_url)
buffer = BytesIO()
qrcode_url.save(buffer, format='PNG')
qrcode_image = base64.b64encode(buffer.getvalue()).decode('utf-8')
# 返回给向导试图的字段
action = {
'type': 'ir.actions.act_window',
'name': _("会议人员录入"),
'res_model': 'room.booking.sign.wizard',
'views': [(False, 'form')],
'target': 'new',
'context': {
'efault_default_qrcode_image': qrcode_image,
}
}
return action
def sync_room_get_attendee_list(self):
for record in self:
if record.wechat_meetingid and record.meeting_type == "up" and record.start_datetime < fields.Datetime.now() and record.stop_datetime > fields.Datetime.now():
sync_result, sync_data = self.sudo().env["wecom.apps"].sync_get_attendee_list(record.wechat_meetingid)
if sync_result:
attendees = sync_data.get("attendees", [])
for attendee in attendees:
if attendee.get("userid", ""):
user_obj = self.sudo().env["res_users"].search(
[("oauth_uid", '=', attendee.get("userid", ""))])
for obj in user_obj:
sign_obj = self.sudo().env["room.meeting.sign"].search(
[("sign_user_id", '=', obj.id), ("room_booking_id", "=", record.id)])
sign_obj.write({"sign_type": 'yes'})
@api.constrains("start_datetime", "stop_datetime")
def _check_date_boundaries(self):
for booking in self:
if booking.start_datetime >= booking.stop_datetime:
raise ValidationError(_(
"The start date of %(booking_name)s must be earlier than the end date.",
booking_name=booking.name
))
@api.constrains("start_datetime", "stop_datetime")
def _check_unique_slot(self):
min_start = min(self.mapped("start_datetime"))
max_stop = max(self.mapped("stop_datetime"))
bookings_by_room = self.search([("room_id", "in", self.room_id.ids), ("start_datetime", "<", max_stop),
("stop_datetime", ">", min_start)]).grouped("room_id")
for booking in self:
if bookings_by_room.get(booking.room_id) and bookings_by_room[booking.room_id].filtered(
lambda b: b.id != booking.id and b.start_datetime < booking.stop_datetime and b.stop_datetime > booking.start_datetime
):
raise ValidationError(_(
"Room %(room_name)s is already booked during the selected time slot.",
room_name=booking.room_id.name
))
# @api.model
# def create(self, vals):
#
# booking =
# employee_records = self.env['hr.employee'].search([('user_id', 'in', booking.room_users_ids.ids)]) if booking.room_users_ids else self.env['hr.employee']
#
# task_name = booking.name or f'会议_{booking.start_datetime.date()}'
#
# address = booking.room_id.name if booking.room_id else ''
#
# remarks = booking.meeting_data or ''
# # 确保room_users_ids有值
# room_users_ids = booking.room_users_ids.ids if booking.room_users_ids else []
#
# self_users_ids = booking.self_users_ids.ids if booking.self_users_ids else []
# # 创建日程记录
# task = self.env['yuthon.will.task'].create({
# 'name': task_name,
# 'start_date': booking.start_datetime.date(),
# 'task_type': 'meet',
# 'room_booking_id': booking.id,
# 'employee_ids': [(6, 0, employee_records.ids)] if employee_records else [(5, 0, 0)],
# 'task_state': 'will',
# 'address': address,
# 'remarks': remarks,
# 'users_ids': [(6, 0, room_users_ids)] if room_users_ids else [(5, 0, 0)],
# 'self_users_ids': [(6, 0, self_users_ids)] if self_users_ids else [(5, 0, 0)],
# })
# return super(RoomBooking, self).create(vals)
# ------------------------------------------------------
# CRUD / ORM
# ------------------------------------------------------
def sync_wechat_room(self):
for record in self:
admin_userid = record.user_id.oauth_uid
title = record.name
meeting_start = record.start_datetime.timestamp()
meeting_duration = record.stop_datetime.timestamp() - record.start_datetime.timestamp()
if not record.wechat_meetingid:
create_result, create_data = self.sudo().env["wecom.apps"].sync_meeting_create(
admin_userid=admin_userid,
title=title,
meeting_start=meeting_start,
meeting_duration=meeting_duration,
kwargs={
"location": record.room_id.name,
"invitees": {
"userid": [r.oauth_uid for r in record.room_users_ids]
},
}
)
if create_result:
record.wechat_meetingid = create_data.get("meetingid", "")
if not create_result:
raise UserError("同步企业微信会议失败:{0}".format(str(create_data)))
if record.wechat_meetingid:
update_result, update_data = self.sudo().env["wecom.apps"].sync_meeting_update(
meetingid=record.wechat_meetingid,
kwargs={
"title": title,
"meeting_start": meeting_start,
"meeting_duration": meeting_duration,
"location": record.room_id.name,
"invitees": {
"userid": [r.oauth_uid for r in record.room_users_ids]
},
}
)
if not update_result:
raise UserError("更新企业微信会议失败:{0}".format(str(update_data)))
def unlink(self):
# Notify frontend of deleted bookings
bookings_by_room = self.grouped("room_id")
for room, bookings in bookings_by_room.items():
room._notify_booking_view("delete", bookings)
if self.wechat_meetingid:
self.sudo().env["wecom.apps"].sync_meeting_cancel(meetingid=self.wechat_meetingid)
return super(RoomBooking, self).unlink()
def write(self, vals):
bookings_by_room = self.grouped("room_id")
res = super(RoomBooking, self).write(vals)
# Notify frontend of updated bookings
if new_room_id := vals.get("room_id"):
new_room = self.env["room.room"].browse(new_room_id)
for room, bookings in bookings_by_room.items():
room._notify_booking_view("delete", bookings)
new_room._notify_booking_view("create", bookings)
elif {"name", "start_datetime", "stop_datetime"} & vals.keys():
for room, bookings in bookings_by_room.items():
room._notify_booking_view("update", bookings)
# self.sync_wechat_room()
return res
@api.model
def _read_group_room_id(self, rooms, domain, order):
# Display all the rooms in the gantt view even if they have no booking,
# and order them by office first, then by usual order (because the
# office name is shown in the display name)
if self.env.context.get("room_booking_gantt_show_all_rooms"):
room_ids = rooms._search([], order="office_id," + order)
return rooms.browse(room_ids)
return rooms
def action_print_room_booking_agenda(self):
"""打印会议议程安排表"""
self.ensure_one()
report = self.env.ref('room.action_room_booking_agenda_report')
return report.report_action(self)
@api.depends('start_datetime')
def _compute_start_datetime_local(self):
"""计算本地时区时间(UTC+8)"""
for record in self:
if record.start_datetime:
record.start_datetime_local = record.start_datetime + timedelta(hours=8)
else:
record.start_datetime_local = False
def get_edit_button_state(self):
"""会议室预约不做流程控制,编辑按钮常显示"""
self.ensure_one()
return {
'show_edit': True,
'self_users_ids': self.self_users_ids.ids,
'work_end': self.work_end,
}