Files
gzth/room/models/room_booking.py
T
2026-02-02 17:23:39 +08:00

365 lines
17 KiB
Python

# -*- 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
"""富文本字段保存无效"""
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="标题", required=True, 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)
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.onchange('meeting_type_id')
def _onchange_meeting_type_id(self):
if self.meeting_type_id:
self.room_users_ids = self.meeting_type_id.users_ids
self.host = self.meeting_type_id.host_user_id.id if self.meeting_type_id.host_user_id else False
else:
self.room_users_ids = [(5, 0, 0)]
self.host = False
# @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',
})
# 缺少逻辑
# for external in self.other_user_ids:
# self.env['room.meeting.sign'].create({
# 'room_booking_id': self.id,
# 'partner_name': external.name,
# 'user_type': 'outsiders',
# '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
))
# ------------------------------------------------------
# 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