!4856 添加功能

Merge pull request !4856 from 李鹏宇/dev_zhou
This commit is contained in:
李鹏宇
2025-09-03 11:25:09 +00:00
committed by Gitee
12 changed files with 260 additions and 73 deletions
@@ -347,6 +347,7 @@ class SoongWorkflowNodeButton(models.Model):
],
)
is_review_invisible = fields.Boolean(string="使用范围", default=False)
illustrate = fields.Text(string="说明")
reviewer_id = fields.Many2one(comodel_name="res.users", string="Reviewer")
reviewer_group_id = fields.Many2one(comodel_name="res.groups", string="Reviewer group")
users_role_id = fields.Many2one(comodel_name="soong.user.role", string="主角色",)
@@ -168,7 +168,7 @@
<field name="has_icon"/>
<field name="icon" invisible="has_icon == False" required="has_icon == True"/>
</group>
<group>
<group col="2">
<group>
<field name="is_review_invisible"/>
<field name="invisible_expression" colspan="2" invisible="is_review_invisible == False"
@@ -177,8 +177,10 @@
options="{'model': 'invisible_expression_model', 'in_dialog': True,}"
nolabel="1"/>
</group>
<group>
<field name="illustrate"/>
</group>
</group>
<group/>
</group>
<group col="3">
<group string="按当前申请人自动匹配">
@@ -499,7 +499,21 @@ class SoongWorkflowCommentWizard(models.TransientModel):
self.env['soong.workflow.record'].search([('code', '=', record_id.code)]).write({'approve_state': 'end',
"employee_ids": False,
"node_id": False,})
# 给申请人发消息
biz_record = self.env[self.res_model].browse(self.res_id)
if hasattr(biz_record, 'user_id') and biz_record.user_id:
employee = biz_record.user_id.employee_id
elif hasattr(biz_record, 'users_id') and biz_record.users_id:
employee = biz_record.users_id.employee_id
action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0]
url = f'https://oa.thtzjt.com/web?login={employee.user_id.login}&oauth_uid={employee.wecom_userid}#id={record_id.id}&cids=2&action={action_id.id}&model={record_id._name}&view_type=form&menu_id=10104'
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa',
user_id=employee.wecom_userid,
title=record_id._description,
description=f"编号为{record_id.code}的{record_id._description}已结束",
url=url, btntxt="查看详情")
recipients = []
if self.node_btn_id.workflow_id.archive_type == 'applicant':
biz_record = self.env[self.res_model].browse(self.res_id)
+1 -7
View File
@@ -60,8 +60,7 @@
<group>
<field name="is_day"/>
</group>
<group invisible="not is_day" col="2">
<group>
<group invisible="not is_day">
<!-- <field name="leave_ids" invisible="not is_day"-->
<!-- context="{-->
<!-- 'default_holiday_type': 'employee',-->
@@ -126,13 +125,10 @@
</tree>
</field>
</group>
<group/>
</group>
<group>
<field name="is_hour"/>
</group>
<group invisible="not is_hour">
<group>
<field name="hour_leave_ids" invisible="not is_hour" style="width: 150%;" string=" "
context="{
'default_holiday_type': 'employee',
@@ -177,8 +173,6 @@
<field name="number_of_days_display" column_invisible="True"/>
</tree>
</field>
</group>
<group/>
</group>
<group>
<field name="department_id" invisible="1"/>
@@ -27,5 +27,31 @@
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="yuthon_vacation_attendance_confirmation" model="ir.cron">
<field name="name">定时任务:发送我的考勤确认</field>
<field name="model_id" ref="model_yuthon_vacation_reports"/>
<field name="state">code</field>
<field name="code">model.attendance_confirmation()</field>
<field eval="False" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">months</field>
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 00:00:00')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="yuthon_vacation_automatic_confirmation" model="ir.cron">
<field name="name">定时任务:考勤报表自动确认</field>
<field name="model_id" ref="model_yuthon_vacation_reports"/>
<field name="state">code</field>
<field name="code">model.automatic_confirmation()</field>
<field eval="False" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">months</field>
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 00:00:00')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
</data>
</odoo>
@@ -939,8 +939,26 @@ class HrAttendance(models.Model):
record.shift_name = ''
else:
record.shift_name = '行政班次'
self._update_vacation_report(record)
return record
def write(self, vals):
result = super(HrAttendance, self).write(vals)
for record in self:
self._update_vacation_report(record)
return result
def _update_vacation_report(self, record):
"""更新相关的假期报表日志"""
if record.current_data and record.employee_id:
current_date = record.current_data.replace(day=1)
report = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', record.employee_id.id),
('vacation_date', '=', current_date)
], limit=1)
if report:
report.compute_attendance_fields()
def button_all(self):
attendance_ids = self.env["hr.attendance"].browse(self._context.get('active_ids', self._context.get('active_id')))
for record in attendance_ids:
@@ -353,27 +353,44 @@ class HrAttendanceDetail(models.Model):
if self._process_shift_days(current_data):
return is_early_card, is_late_card, is_abnormal, is_warning, absenteeism_card
is_tianhe_property = employee.company_id.name == "天河物管"
# 迟到判断
if check_in and start_datetime and check_in > start_datetime:
if employee.late_allowed > 0 :
is_late_card = False
employee.late_allowed -= 1
else:
is_late_card = True
late_delta = (check_in - start_datetime).total_seconds() / 60
# 迟到超过60分钟标记为旷工
if late_delta > 60:
absenteeism_card = True
if is_tianhe_property:
# 天河物管:2分钟包容时间
if employee.late_allowed == 0:
if late_delta > 2:
is_late_card = True
else:
# 迟到超过10分钟 扣允许迟到次数 并且也算迟到
employee.late_allowed -= 1
if late_delta > 10:
is_late_card = True
# 迟到超过60分钟标记为旷工
if late_delta > 60:
absenteeism_card = True
else:
if check_in > start_datetime:
is_late_card = True
# 迟到超过60分钟标记为旷工
if late_delta > 60:
absenteeism_card = True
# 早退判断
if check_out and end_datetime and check_out < end_datetime:
is_early_card = True
# 计算早退时长(分钟)
if check_out and end_datetime:
early_delta = (end_datetime - check_out).total_seconds() / 60
# 早退超过60分钟标记为旷工
if early_delta > 60:
absenteeism_card = True
if is_tianhe_property:
# 天河物管:超过2分钟才算早退
if early_delta > 2:
is_early_card = True
if early_delta > 60:
absenteeism_card = True
else:
if check_out < end_datetime:
is_early_card = True
if early_delta > 60:
absenteeism_card = True
# 异常和警告判断
if is_early_card or is_late_card:
@@ -4,6 +4,7 @@ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import calendar
from datetime import date
from dateutil.relativedelta import relativedelta
class YuthonVacationReports(models.Model):
@@ -15,6 +16,7 @@ class YuthonVacationReports(models.Model):
employee_id = fields.Many2one('hr.employee', string='员工')
department_id = fields.Many2one(related='employee_id.department_id', string="部门")
company_id = fields.Many2one(related="employee_id.company_id", string="公司")
late_allowed = fields.Float(related="employee_id.late_allowed", string="允许迟到次数")
leave_ids = fields.One2many('vacation.leave.line', 'vacation_id', string='年假明细')
overtime_ids = fields.One2many('vacation.over.line', 'vacation_id', string='加班明细')
meal_fee_ids = fields.One2many('vacation.meal.line', 'vacation_id', string='误餐明细')
@@ -52,13 +54,13 @@ class YuthonVacationReports(models.Model):
internship_days = fields.Float(string='实习天数', group_operator=None)
should_attendance = fields.Integer(string="应到", store=True, group_operator=None)
actual_attendance = fields.Integer(string="实到", compute='_compute_attendance_fields', store=True,group_operator=None)
actual_attendance = fields.Integer(string="实到", compute='compute_attendance_fields', store=True,group_operator=None)
is_late_card = fields.Integer(string="迟到", compute='_compute_attendance_fields', store=True, group_operator=None)
is_early_card = fields.Integer(string="早退", compute='_compute_attendance_fields', store=True, group_operator=None)
is_no_card = fields.Integer(string="缺卡", compute='_compute_attendance_fields', store=True, group_operator=None)
is_work = fields.Integer(string='旷工', compute='_compute_attendance_fields', store=True, group_operator=None)
is_abnormal = fields.Integer(string="异常", compute='_compute_attendance_fields', store=True, group_operator=None)
is_late_card = fields.Integer(string="迟到", compute='compute_attendance_fields', store=True, group_operator=None)
is_early_card = fields.Integer(string="早退", compute='compute_attendance_fields', store=True, group_operator=None)
is_no_card = fields.Integer(string="缺卡", compute='compute_attendance_fields', store=True, group_operator=None)
is_work = fields.Integer(string='旷工', compute='compute_attendance_fields', store=True, group_operator=None)
is_abnormal = fields.Integer(string="异常", compute='compute_attendance_fields', store=True, group_operator=None)
vacation_state = fields.Selection(
selection=[
@@ -71,13 +73,49 @@ class YuthonVacationReports(models.Model):
tracking=3,
default='unconfirmed')
def confirm_state(self):
for rec in self:
rec.vacation_state = 'confirmed'
# 计算下个月的日期(基于当前记录的vacation_date)
current_date = rec.vacation_date
if current_date.month == 12:
next_month_date = current_date.replace(year=current_date.year + 1, month=1, day=1)
else:
next_month_date = current_date.replace(month=current_date.month + 1, day=1)
# 查找下个月是否已有该员工的记录
next_month_record = self.search([
('employee_id', '=', rec.employee_id.id),
('vacation_date', '=', next_month_date)
], limit=1)
inherit_vals = {
'leave': rec.leave,
'working_overtime': rec.working_overtime,
'medical_fee': rec.medical_fee,
}
if next_month_record:
# 如果下个月记录已存在,更新继承字段
next_month_record.write(inherit_vals)
# 处理年假明细行继承
if rec.leave:
leave_hours = rec.leave * 8
self._update_leave_lines(next_month_record, [rec], leave_hours)
# 处理补休明细行继承
if rec.working_overtime:
overtime_hours = rec.working_overtime * 8
self._update_over_lines(next_month_record, [rec], overtime_hours)
@api.depends('vacation_date')
def _compute_display_date(self):
for rec in self:
rec.display_date = rec.vacation_date.strftime('%Y年%m月')
@api.depends('employee_id', 'vacation_date')
def _compute_attendance_fields(self):
def compute_attendance_fields(self):
for rec in self:
# 初始化字段值
is_late = 0
@@ -131,28 +169,19 @@ class YuthonVacationReports(models.Model):
self._context.get('active_ids', self._context.get('active_id', self.ids))
)
for rec in records:
leave_days = 0
actual_attendance = 0
if rec.employee_id and rec.vacation_date:
year = rec.vacation_date.year
month = rec.vacation_date.month
days_in_month = calendar.monthrange(year, month)[1]
month_start = date(year, month, 1)
month_end = date(year, month, days_in_month)
leave_days = rec._create_leave_record(month_start, month_end)
for day in range(1, days_in_month + 1):
current_date = date(year, month, day)
attendances = self.env["hr.attendance"].sudo().search([
('employee_id', '=', rec.employee_id.id),
("current_data", '=', current_date)
])
if attendances:
has_valid_check = any(att.check_in or att.check_out for att in attendances)
if has_valid_check:
actual_attendance += 1
rec.compute_attendance_fields()
rec.should_attendance = leave_days
rec.actual_attendance = actual_attendance
def update_data(self):
august_start = fields.Date.from_string('2024-08-01')
august_end = fields.Date.from_string('2024-08-31')
august_records = self.env["yuthon.vacation.reports"].search([
('vacation_date', '>=', august_start),
('vacation_date', '<=', august_end)
])
if august_records:
august_records.write({
'vacation_date': august_start
})
@api.depends('other_fee_ids', 'other_fee_ids.holiday_type', 'other_fee_ids.lines',
@@ -240,8 +269,8 @@ class YuthonVacationReports(models.Model):
return f'{month}月{day}日'
def button_put(self):
menu_id = self.env['ir.ui.menu'].search([('name', '=', '考勤确认'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=hr.attendance.month.detail&view_type=list&menu_id={menu_id.id}'
menu_id = self.env['ir.ui.menu'].search([('name', '=', '我的考勤确认'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=yuthon.vacation.reports&view_type=list&menu_id={menu_id.id}'
we_id = self.env['hr.employee'].search([('id', '=', self.employee_id.id)]).wecom_userid
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='default', user_id=we_id, title="考勤确认",
description=self.employee_id.name + '请确认你的考勤:',
@@ -260,7 +289,7 @@ class YuthonVacationReports(models.Model):
detail_ids = self.env["yuthon.vacation.reports"].browse(self._context.get('active_ids', self._context.get('active_id')))
unique_employee_ids = list({detail.employee_id.id for detail in detail_ids if detail.employee_id})
for employee_id in unique_employee_ids:
menu_id = self.env['ir.ui.menu'].search([('name', '=', '考勤确认'), ('parent_id', '!=', False)])
menu_id = self.env['ir.ui.menu'].search([('name', '=', '我的考勤确认'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=hr.attendance.month.detail&view_type=list&menu_id={menu_id.id}'
we_id = self.env['hr.employee'].search([('id', '=', employee_id)]).wecom_userid
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='attendance', user_id=we_id, title="考勤确认",
@@ -276,6 +305,58 @@ class YuthonVacationReports(models.Model):
}
}
def attendance_confirmation(self):
for record in self:
employee = self.env['hr.employee'].search([('name', '=', '叶华锋')])
menu_id = self.env['ir.ui.menu'].search([('name', '=', '我的考勤确认'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=yuthon.vacation.reports&view_type=list&menu_id={menu_id.id}'
we_id = self.env['hr.employee'].search([('id', '=', employee.id)]).wecom_userid
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='default', user_id=we_id,
title="考勤确认",
description=employee.name + '请确认你的考勤:',
url=url, btntxt="详细信息")
def automatic_confirmation(self):
for rec in self:
today = fields.Date.today()
employee = self.env['hr.employee'].search([('name', '=', '叶华锋')])
rec.vacation_state = 'confirmed'
# 计算上个月报表的日期
current_date = today
if current_date.month == 12:
last_month_date = current_date.replace(year=current_date.year - 1, month=1, day=1)
else:
last_month_date = current_date.replace(month=current_date.month - 1, day=1)
last_month_record = self.search([
('employee_id', '=', employee.id),
('vacation_date', '=', last_month_date)
], limit=1)
next_month_record = self.search([
('employee_id', '=', employee.id),
('vacation_date', '=', last_month_date + relativedelta(months=1))
], limit=1)
inherit_vals = {
'leave': last_month_record.leave,
'working_overtime': last_month_record.working_overtime,
'medical_fee': last_month_record.medical_fee,
}
if next_month_record:
# 如果下个月记录已存在,更新继承字段
next_month_record.write(inherit_vals)
# 处理年假明细行继承
if rec.leave:
leave_hours = rec.leave * 8
self._update_leave_lines(next_month_record, [rec], leave_hours)
# 处理补休明细行继承
if rec.working_overtime:
overtime_hours = rec.working_overtime * 8
self._update_over_lines(next_month_record, [rec], overtime_hours)
@api.depends('medical_fee_ids.update_type', 'medical_fee_ids.lines', 'medical_fee_ids.is_void')
def _compute_medical_fee(self):
@@ -444,13 +525,15 @@ class YuthonVacationReports(models.Model):
], limit=1)
if not existing_record and not is_resource_user:
# 获取上月记录
# 仅查找已确认的上月记录
last_month_record = self.search([
('employee_id', '=', employee.id),
('vacation_date', '>=', fields.Date.to_string(last_month_first)),
('vacation_date', '<=', fields.Date.to_string(last_month_last))
('vacation_date', '<=', fields.Date.to_string(last_month_last)),
('vacation_state', '=', 'confirmed') # 关键条件:仅已确认的记录
], limit=1)
inherit_vals = {}
# 只有找到已确认的上月记录时才进行继承
if last_month_record:
inherit_vals = {
'leave': last_month_record.leave,
@@ -458,13 +541,13 @@ class YuthonVacationReports(models.Model):
'medical_fee': last_month_record.medical_fee,
}
# 创建本月报告主记录
new_report = self.create({
'employee_id': employee.id,
'vacation_date': today,
**inherit_vals
})
if last_month_record:
# 创建本月报告主记录(包含继承的值)
new_report = self.create({
'employee_id': employee.id,
'vacation_date': today,
**inherit_vals
})
# 处理年假明细行(继承额度)
if last_month_record.leave:
leave_hours = last_month_record.leave * 8
@@ -475,6 +558,13 @@ class YuthonVacationReports(models.Model):
overtime_hours = last_month_record.working_overtime * 8
self._update_over_lines(new_report, [last_month_record], overtime_hours)
else:
# 如果没有已确认的上月记录,创建空的本月报告
self.create({
'employee_id': employee.id,
'vacation_date': today,
})
def _update_leave_lines(self, report, allocations, number_of_hours_display):
for alloc in allocations:
self.env['vacation.leave.line'].create({
@@ -571,6 +661,16 @@ class VacationOverline(models.Model):
adjust_date = fields.Date(string='调整至')
text = fields.Text(string='备注')
def open_record(self):
if self.workflow_record_id:
return {
"name": "流程详情",
'type': 'ir.actions.act_window',
'res_model': f'{self.workflow_record_id.res_model}',
'res_id': self.workflow_record_id.res_id,
'view_mode': 'form',
}
# def _sync_to_employee(self, reverse=False):
# """将数据同步到关联员工
# :param reverse: 是否反向操作(用于删除场景)
@@ -28,8 +28,8 @@
<field name="is_no_card" optional="show" class="list_ids_width"/>
<field name="is_early_card" optional="show" class="list_ids_width"/>
<field name="is_late_card" optional="show" class="list_ids_width"/>
<field name="absenteeism_card" optional="show" class="list_ids_width"/>
<field name="is_abnormal" optional="show" class="list_ids_width"/>
<field name="absenteeism_card" optional="show" class="list_ids_width" string="旷工"/>
<field name="is_abnormal" optional="show" class="list_ids_width" sting="异常"/>
<field name="abnormal" optional="show" column_invisible="1"/>
<field name="work_time_type" optional="hidden"/>
<field name="is_approval" optional="hidden"/>
@@ -69,8 +69,8 @@
<field name="is_no_card" optional="show" class="list_ids_width"/>
<field name="is_early_card" optional="show" class="list_ids_width"/>
<field name="is_late_card" optional="show" class="list_ids_width"/>
<field name="absenteeism_card" optional="show" class="list_ids_width"/>
<field name="is_abnormal" optional="show" class="list_ids_width"/>
<field name="absenteeism_card" optional="show" class="list_ids_width" sting="旷工"/>
<field name="is_abnormal" optional="show" class="list_ids_width" string="异常"/>
<field name="abnormal" optional="show" column_invisible="1"/>
<field name="work_time_type" optional="hidden"/>
<field name="is_approval" optional="hidden"/>
@@ -14,6 +14,7 @@
<field name="is_no_card" options='{ "fg_color": "white: is_no_card == False"}'/>
<field name="is_work" options='{ "fg_color": "white: is_work == False"}'/>
<field name="is_abnormal" options='{ "fg_color": "white: not is_abnormal"}'/>
<field name="late_allowed" options='{ "fg_color": "white: late_allowed == False"}'/>
<field name="should_attendance" options='{ "fg_color": "white: should_attendance == False"}'/>
<field name="actual_attendance" options='{ "fg_color": "white: actual_attendance == False"}'/>
<field name="right_days" options='{ "fg_color": "white: right_days == False"}'/>
@@ -54,11 +55,13 @@
<field name="department_id" column_invisible="1"/>
<field name="company_id" column_invisible="1"/>
<field name="vacation_date" column_invisible="1"/>
<field name="late_allowed"/>
<field name="is_late_card" options='{ "fg_color": "white: is_late_card == False"}'/>
<field name="is_early_card" options='{ "fg_color": "white: is_early_card == False"}'/>
<field name="is_no_card" options='{ "fg_color": "white: is_no_card == False"}'/>
<field name="is_work" options='{ "fg_color": "white: is_work == False"}'/>
<field name="is_abnormal" options='{ "fg_color": "white: not is_abnormal"}'/>
<field name="late_allowed" options='{ "fg_color": "white: late_allowed == False"}'/>
<field name="should_attendance" options='{ "fg_color": "white: should_attendance == False"}'/>
<field name="actual_attendance" options='{ "fg_color": "white: actual_attendance == False"}'/>
<field name="right_days" options='{ "fg_color": "white: right_days == False"}'/>
@@ -86,6 +89,7 @@
<field name="vacation_state" widget="badge" decoration-info="vacation_state == 'unconfirmed'"
decoration-success="vacation_state == 'confirmed'"/>
<button name="log" type="object" string="详情" class="oe_highlight"/>
<button name="confirm_state" type="object" string="确认" class="oe_highlight"/>
</tree>
</field>
</record>
@@ -138,6 +142,7 @@
<button name="overtime_allocation" string="分配" type="object" class="btn btn-primary"/>
<field name="overtime_ids" context="{'default_employee_id': employee_id}">
<tree editable="bottom" delete="false" decoration-muted="is_void">
<button name="open_record" type="object" string="详情" class="btn-primary" column_invisible="not workflow_record_id"/>
<field name="vacation_id" column_invisible="1"/>
<field name="employee_id" column_invisible="1"/>
<field name="workflow_record_id" domain="[('workflow_id.name', '=', '加班'), ('create_uid.employee_id', '=', employee_id)]"/>
@@ -442,6 +447,13 @@
<field name="state">code</field>
<field name="code">action = model._compute_attendance_fields1()</field>
</record>
<record id="all_update_data" model="ir.actions.server">
<field name="name">更新日期</field>
<field name="model_id" ref="yuthon_hr_attendance.model_yuthon_vacation_reports"/>
<field name="binding_model_id" ref="yuthon_hr_attendance.model_yuthon_vacation_reports"/>
<field name="state">code</field>
<field name="code">action = model.update_data()</field>
</record>
<record id="details_tree_view" model="ir.ui.view">
<field name="name">details.tree.tree</field>
<field name="model">details.tree</field>
@@ -106,4 +106,7 @@ class AttendanceCorrectWizard(models.TransientModel):
values['is_check_out_red'] = True
values['is_early_card'] = False
# 应用修改
self.attendance_id.write(values)
self.attendance_id.write(values)
@@ -39,9 +39,9 @@
<field name="work_end" invisible="1"/>
<field name="name"/>
<field name="users_id" options="{'no_create': True}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="approval_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="approval_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="remake"/>
<field name="document_ids" widget="preview_many2many"/>
<field name="department_id" invisible="1"/>