模块迁移
This commit is contained in:
parent
782bea26af
commit
1fb7ae189c
3
yuthon_hr_employee/__init__.py
Normal file
3
yuthon_hr_employee/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from . import wizard
|
||||||
34
yuthon_hr_employee/__manifest__.py
Normal file
34
yuthon_hr_employee/__manifest__.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# email:pengyuthon@163.com
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Yuthon Hr',
|
||||||
|
'summary': '',
|
||||||
|
'description': '''
|
||||||
|
''',
|
||||||
|
'version': '18.0.2.0.0',
|
||||||
|
'category': 'Human Resources/Employees',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'author': 'pengyuthon',
|
||||||
|
'website': 'https://www.pengyuthon.com',
|
||||||
|
'depends': ['base', 'hr'],
|
||||||
|
'data': [
|
||||||
|
'data/employee_code_data.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/yuthon_hr_employee_views.xml',
|
||||||
|
'views/inherit_res_user_views.xml',
|
||||||
|
'views/inherit_hr_work_location_views.xml',
|
||||||
|
'views/inherit_res_groups_views.xml',
|
||||||
|
'wizard/hr_employee_wizard_views.xml',
|
||||||
|
'wizard/hr_employee_time_wizard_views.xml',
|
||||||
|
'wizard/reset_password_wizard_viwes.xml',
|
||||||
|
],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_backend': [
|
||||||
|
'yuthon_hr_employee/static/scss/yuthon_employee_scss.scss',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
1
yuthon_hr_employee/controllers/__init__.py
Normal file
1
yuthon_hr_employee/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import employee_banner_route
|
||||||
29
yuthon_hr_employee/controllers/employee_banner_route.py
Normal file
29
yuthon_hr_employee/controllers/employee_banner_route.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from odoo.http import Controller, request, route
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeBannerRoute(Controller):
|
||||||
|
|
||||||
|
@route('/employee/banner/route', type='json', auth='user')
|
||||||
|
def employee_banner_route(self, **kwargs):
|
||||||
|
employees = request.env['hr.employee'].sudo()
|
||||||
|
hired_count = employees.search_count([('use_user_type', '=', 'formal_one')]) # 正式
|
||||||
|
in_hired_count = employees.search_count([]) # 在职
|
||||||
|
all_hired_count = employees.search_count([]) # 全职
|
||||||
|
return {
|
||||||
|
# 'html': f"""
|
||||||
|
# <div style="display: flex; justify-content: space-around; align-items: center; text-align: center; padding: 20px; width: 100%;margin-top:25px;margin-bottom:25px;">
|
||||||
|
# <div style="flex: 1; padding: 20px;">
|
||||||
|
# <h2>正式</h2>
|
||||||
|
# <p style="font-size: 24px; color: #4CAF50;">{hired_count}</p>
|
||||||
|
# </div>
|
||||||
|
# <div style="flex: 1; padding: 20px;">
|
||||||
|
# <h2>在职</h2>
|
||||||
|
# <p style="font-size: 24px; color: #2196F3;">{in_hired_count}</p>
|
||||||
|
# </div>
|
||||||
|
# <div style="flex: 1; padding: 20px;">
|
||||||
|
# <h2>全职</h2>
|
||||||
|
# <p style="font-size: 24px; color: #FF5722;">{all_hired_count}</p>
|
||||||
|
# </div>
|
||||||
|
# </div>
|
||||||
|
# """
|
||||||
|
}
|
||||||
9
yuthon_hr_employee/data/employee_code_data.xml
Normal file
9
yuthon_hr_employee/data/employee_code_data.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="employee_code_sequence" model="ir.sequence">
|
||||||
|
<field name="name">员工编号</field>
|
||||||
|
<field name="code">employee_code</field>
|
||||||
|
<field name="prefix">US%(y)s%(month)s%(day)s</field>
|
||||||
|
<field name="padding">3</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
4
yuthon_hr_employee/models/__init__.py
Normal file
4
yuthon_hr_employee/models/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from . import yuthon_hr_employee
|
||||||
|
from . import hr_employee_page
|
||||||
|
from . import inherit_res_user
|
||||||
|
from . import inherit_res_groups
|
||||||
66
yuthon_hr_employee/models/hr_employee_page.py
Normal file
66
yuthon_hr_employee/models/hr_employee_page.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonEmergencyContactLine(models.Model):
|
||||||
|
_name = 'yuthon.emergency.contact.line'
|
||||||
|
_description = "紧急联系人明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
name = fields.Char(string="姓名")
|
||||||
|
user_sfz = fields.Char(string="身份证")
|
||||||
|
user_relationship = fields.Selection([("father", "父亲"),('mother', '母亲'), ("spouse", "妻子"), ('boys', '儿子'), ('daughter', '女儿'), ('didi', '弟弟'),
|
||||||
|
('husband', '丈夫'), ('sister', '姐姐'), ('brother', '哥哥'), ('sister_do', '妹妹')], string="关系")
|
||||||
|
workplace = fields.Char(string="工作单位")
|
||||||
|
post = fields.Char(string="岗位")
|
||||||
|
phone_number = fields.Char(string="手机号")
|
||||||
|
home_address = fields.Char(string="家庭住址")
|
||||||
|
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonWorkExperienceLine(models.Model):
|
||||||
|
_name = 'yuthon.work.experience.line'
|
||||||
|
_description = "工作经验明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
start_date = fields.Date(string="开始日期")
|
||||||
|
end_date = fields.Date(string="结束日期")
|
||||||
|
work_place = fields.Char(string="工作单位")
|
||||||
|
post = fields.Char(string="岗位")
|
||||||
|
contact_user = fields.Char(string="联系人")
|
||||||
|
phone_number = fields.Char(string="联系人手机号")
|
||||||
|
reason_leaving = fields.Char(string="离职原因")
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonEducationExperienceLine(models.Model):
|
||||||
|
_name = 'yuthon.education.experience.line'
|
||||||
|
_description = "教育经历明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
name_school = fields.Char(string="学校名称")
|
||||||
|
start_date = fields.Date(string="入学时间")
|
||||||
|
end_date = fields.Date(string="毕业时间")
|
||||||
|
in_school_duties = fields.Char(string="在校职务")
|
||||||
|
professional = fields.Char(string="专业")
|
||||||
|
learning_style = fields.Selection([
|
||||||
|
('full_time', '全日制'),
|
||||||
|
('upgrade', '专升本'),
|
||||||
|
('adult_education', '成人教育'),
|
||||||
|
('self_study', '自考'),
|
||||||
|
('online_education', '网络教育'),
|
||||||
|
('correspondence', '函授'),
|
||||||
|
], string="学习方式")
|
||||||
|
part_time_degree = fields.Char(string="学历")
|
||||||
|
full_time_degree = fields.Char(string="学位")
|
||||||
|
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonSkillsCertificatesLine(models.Model):
|
||||||
|
_name = 'yuthon.skills.certificates.line'
|
||||||
|
_description = "技能证书管理明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
rank_name = fields.Char(string='职级名称')
|
||||||
|
rank_type = fields.Char(string='职级类型')
|
||||||
|
rank_time = fields.Date(string='职级时间')
|
||||||
|
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
52
yuthon_hr_employee/models/inherit_hr_job.py
Normal file
52
yuthon_hr_employee/models/inherit_hr_job.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class HrJob(models.Model):
|
||||||
|
_inherit = "hr.job"
|
||||||
|
_order = 'number asc'
|
||||||
|
|
||||||
|
number = fields.Char(string="序号")
|
||||||
|
job_type_id = fields.Many2one('hr.job.type', string="岗位类别")
|
||||||
|
start_date = fields.Date(string="生效日期", default=lambda self: fields.Date.today())
|
||||||
|
end_date = fields.Date(string="失效日期", default=fields.Date.from_string('2099-01-01'))
|
||||||
|
is_enable = fields.Boolean(string="是否启用", default=False, store=True)
|
||||||
|
compiling_external = fields.Integer(string="编制外人数")
|
||||||
|
compiling_status = fields.Selection([('1', '缺编'), ('2', '满编')], string="状态")
|
||||||
|
missing_number = fields.Integer(string="缺编人数")
|
||||||
|
establishment_quotas = fields.Integer(string="编制定员")
|
||||||
|
job_personnel_ids = fields.Many2many('hr.employee', string='在岗人员')
|
||||||
|
recruitment_employees = fields.Integer(string="招聘人数")
|
||||||
|
email = fields.Char(string="邮箱")
|
||||||
|
job_personnel = fields.Text(string="在岗人员")
|
||||||
|
remarks = fields.Text(string="备注")
|
||||||
|
is_recruitment_status = fields.Boolean(string="招聘状态")
|
||||||
|
company_id = fields.Many2one('res.company', string='公司')
|
||||||
|
is_published = fields.Boolean(string="已发布")
|
||||||
|
website = fields.Char(string="网站")
|
||||||
|
skill_ids = fields.Many2many('hr.skill', string="技能")
|
||||||
|
date_from = fields.Date(string="开始日期")
|
||||||
|
|
||||||
|
#任职要求
|
||||||
|
degree = fields.Selection([('high', '高中'), ('junior_college', '大专'), ('bachelor', '本科'),
|
||||||
|
('master', '硕士'), ('doctor', '博士')], string="学历")
|
||||||
|
sex = fields.Selection([('boy', '男'), ('girl', '女'), ('other', '不限')], string="性别")
|
||||||
|
age_start = fields.Integer(string="年龄")
|
||||||
|
age_end = fields.Integer(string="年龄")
|
||||||
|
political_aspects = fields.Selection([('cpc_dang', '中共党员'), ('nld', '民盟'),
|
||||||
|
('league_member', '共青团员'), ('people', '群众')], string="政治面貌")
|
||||||
|
professional = fields.Char(string="专业")
|
||||||
|
job_title = fields.Char(string="职称")
|
||||||
|
admissions = fields.Char(string="执业资格")
|
||||||
|
years_of_service = fields.Integer(string="工作年限")
|
||||||
|
competency_requirements = fields.Text(string="岗位职责")
|
||||||
|
other_notes = fields.Text(string="任职要求")
|
||||||
|
function = fields.Text(string="岗位职责")
|
||||||
|
department_ids = fields.Many2one('hr.department', string="所属部门")
|
||||||
|
recruitment_target1 = fields.Selection([('new', '新员工'), ('transfer', '内部调动')], string="招聘目标")
|
||||||
|
address_id = fields.Many2one('res.partner', string="工作地点")
|
||||||
|
industry_id = fields.Many2one('res.partner', string="行业")
|
||||||
|
recruit_employee_id = fields.Many2one('hr.employee', string="招聘负责人")
|
||||||
|
interviewer_employee_id = fields.Many2one('hr.employee', string="面试官")
|
||||||
|
|
||||||
|
|
||||||
63
yuthon_hr_employee/models/inherit_res_groups.py
Normal file
63
yuthon_hr_employee/models/inherit_res_groups.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class ResGroups(models.Model):
|
||||||
|
_inherit = "res.groups"
|
||||||
|
|
||||||
|
users_ids = fields.Many2many('res.users', string="权限用户")
|
||||||
|
employee_ids = fields.Many2many('hr.employee', string="权限员工")
|
||||||
|
department_ids = fields.Many2many("hr.department", string="权限部门")
|
||||||
|
|
||||||
|
def users_unlink(self):
|
||||||
|
users_to_unlink = self.users.filtered(lambda user: user.name != 'Administrator')
|
||||||
|
users_to_unlink2 = self.users.filtered(lambda user: user.name != '叶华峰')
|
||||||
|
self.write({'users': [(3, user.id) for user in users_to_unlink]})
|
||||||
|
self.write({'users': [(3, user.id) for user in users_to_unlink2]})
|
||||||
|
|
||||||
|
def domain_unlink(self):
|
||||||
|
self.write({'users_ids': [(5, 0, 0)]})
|
||||||
|
self.write({'employee_ids': [(5, 0, 0)]})
|
||||||
|
self.write({'department_ids': [(5, 0, 0)]})
|
||||||
|
|
||||||
|
def rule_unlink(self):
|
||||||
|
self.rule_groups.unlink()
|
||||||
|
self.model_access.unlink()
|
||||||
|
|
||||||
|
@api.onchange('employee_ids', 'department_ids')
|
||||||
|
def _onchange_update_users(self):
|
||||||
|
"""动态更新 users 字段,基于员工、部门的选择"""
|
||||||
|
all_user_ids = set()
|
||||||
|
|
||||||
|
# 1. 添加选中员工对应的用户
|
||||||
|
for employee in self.employee_ids:
|
||||||
|
if employee.user_id:
|
||||||
|
all_user_ids.add(employee.user_id.id)
|
||||||
|
|
||||||
|
# 2. 添加选中部门及其子部门的所有员工对应的用户
|
||||||
|
def get_department_employees(department):
|
||||||
|
valid_user_ids = set()
|
||||||
|
if not department:
|
||||||
|
return valid_user_ids
|
||||||
|
|
||||||
|
# 处理虚拟记录(onchange时可能出现)
|
||||||
|
dept_id = department.id.origin if hasattr(department.id, 'origin') else department.id
|
||||||
|
dept_id = int(dept_id) if isinstance(dept_id, str) else dept_id
|
||||||
|
|
||||||
|
# 当前部门员工
|
||||||
|
employees = self.env['hr.employee'].search([('department_id', '=', dept_id)])
|
||||||
|
valid_user_ids.update([emp.user_id.id for emp in employees if emp.user_id])
|
||||||
|
|
||||||
|
# 递归子部门
|
||||||
|
child_depts = self.env['hr.department'].search([('parent_id', '=', dept_id)])
|
||||||
|
for child in child_depts:
|
||||||
|
valid_user_ids.update(get_department_employees(child))
|
||||||
|
|
||||||
|
return valid_user_ids
|
||||||
|
|
||||||
|
for department in self.department_ids:
|
||||||
|
all_user_ids.update(get_department_employees(department))
|
||||||
|
|
||||||
|
# 最终更新用户
|
||||||
|
self.users = [(6, 0, list(all_user_ids))]
|
||||||
56
yuthon_hr_employee/models/inherit_res_user.py
Normal file
56
yuthon_hr_employee/models/inherit_res_user.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
import pypinyin
|
||||||
|
|
||||||
|
|
||||||
|
class ResUsers(models.Model):
|
||||||
|
_inherit = "res.users"
|
||||||
|
|
||||||
|
# access_token2 = fields.Char(string='OAuth 访问令牌2', help='验证消息通知。')
|
||||||
|
company_ids1 = fields.Many2many('res.company', string='公司', compute='_compute_company_ids1', store=True)
|
||||||
|
@api.depends("company_ids")
|
||||||
|
def _compute_company_ids1(self):
|
||||||
|
for record in self:
|
||||||
|
employee_id = record.env['hr.employee'].search([('user_id', '=', record.id)], limit=1)
|
||||||
|
is_admin = record.has_group('base.group_system')
|
||||||
|
if is_admin:
|
||||||
|
record.company_ids1 = employee_id.company_id
|
||||||
|
else:
|
||||||
|
record.company_ids1 = record.company_ids
|
||||||
|
|
||||||
|
def update_company_ids1(self):
|
||||||
|
cron_ids = self.env["res.users"].browse(
|
||||||
|
self._context.get('active_ids', self._context.get('active_id')))
|
||||||
|
for cron in cron_ids:
|
||||||
|
cron._compute_company_ids1()
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
user = super(ResUsers, self).create(vals)
|
||||||
|
user._compute_company_ids1()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def all_create_users(self):
|
||||||
|
employee_ids = self.browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||||
|
for employee in employee_ids:
|
||||||
|
employee_id = self.env['hr.employee'].search([('name', '=', employee.name)])
|
||||||
|
if not employee_id:
|
||||||
|
employee_data = {
|
||||||
|
'name': employee.name,
|
||||||
|
'company_id': employee.env.company.id,
|
||||||
|
'user_id': employee.id,
|
||||||
|
}
|
||||||
|
self.env['hr.employee'].create(employee_data)
|
||||||
|
else:
|
||||||
|
raise UserError('该用户:%s已经创建员工,请不要重复操作' % employee_id.name)
|
||||||
|
|
||||||
|
def all_users_email(self):
|
||||||
|
employee_ids = self.browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||||
|
for employee in employee_ids:
|
||||||
|
if not employee.partner_id.email:
|
||||||
|
pinyin_list = pypinyin.lazy_pinyin(employee.name, style=pypinyin.NORMAL)
|
||||||
|
name_pinyin = ''.join(pinyin_list).lower()
|
||||||
|
email = f"{name_pinyin}@thtjzt.com"
|
||||||
|
employee.partner_id.write({'email': email})
|
||||||
373
yuthon_hr_employee/models/yuthon_hr_employee.py
Normal file
373
yuthon_hr_employee/models/yuthon_hr_employee.py
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from pypinyin import pinyin, lazy_pinyin, STYLE_FIRST_LETTER
|
||||||
|
from odoo.osv import expression
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from datetime import date, datetime
|
||||||
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
|
||||||
|
nation_list = [('a', '汉族'), ('b', '壮族'), ('v', '满族'), ('d', '回族'), ('e', '苗族'), ('f', '维吾尔族'),
|
||||||
|
('g', '土家族'), ('h', '彝族'), ('i', '蒙古族'), ('j', '藏族'), ('k', '布依族'), ('l', '侗族'),
|
||||||
|
('m', '瑶族'), ('n', '朝鲜族'), ('o', '白族'), ('p', '哈尼族'), ('q', '哈萨克族'), ('r', '黎族'),
|
||||||
|
('s', '傣族'), ('t', '畲族'), ('u', '傈僳族'), ('v', '仡佬族'), ('w', '东乡族'), ('x', '拉祜族'),
|
||||||
|
('y', '水族'), ('z', '佤族'), ('a1', '纳西族'), ('b1', '羌族'), ('c1', '仫佬族'), ('d1', '锡伯族'),
|
||||||
|
('e1', '柯尔克孜族'), ('f1', '达斡尔族'), ('g1', '景颇族'), ('h1', '毛南族'), ('i1', '撒拉族'),
|
||||||
|
('j1', '布朗族'), ('k1', '塔吉克族'), ('l1', '阿昌族'), ('m1', '普米族'), ('n1', '鄂温克族'), ('o1', '怒族'),
|
||||||
|
('p1', '京族'), ('q1', '基诺族'), ('r1', '德昂族'), ('s1', '保安族'), ('t1', '俄罗斯族'), ('u1', '裕固族'),
|
||||||
|
('v1', '乌孜别克族'), ('w1', '门巴族'), ('x1', '鄂伦春族'), ('y1', '独龙族'), ('z1', '塔塔尔族'), ('a2', '赫哲族'),
|
||||||
|
('b2', '高山族'), ('c2', '珞巴族'), ('d2', '壮族')]
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonHrEmployee(models.Model):
|
||||||
|
"""
|
||||||
|
人员管理系统 - 教学版
|
||||||
|
对 hr.employee 模型的扩展,添加个人信息、工作信息、教育经历等字段
|
||||||
|
"""
|
||||||
|
_inherit = "hr.employee"
|
||||||
|
_order = 'number asc'
|
||||||
|
|
||||||
|
# 基本信息
|
||||||
|
compress_image = fields.Binary(string="压缩头像", attachment=True)
|
||||||
|
company_ids = fields.Many2many('res.company', related="user_id.company_ids")
|
||||||
|
number = fields.Char(string="序号", store=True)
|
||||||
|
is_soldiers = fields.Boolean(string="是否为退伍军人")
|
||||||
|
user_number = fields.Char(string="员工编号", default=lambda self: self.env['ir.sequence'].next_by_code('employee_code'))
|
||||||
|
use_user_type = fields.Selection([('formal_one', '正式合同(一签)'), ('labor', '劳务协议'), ('formal_two', '正式合同(二签)'),
|
||||||
|
('formal_none', '正式合同(无固定)'),
|
||||||
|
('part_time', '兼职协议'), ('employment', '用工协议'), ('internship', '实习协议')], string="用工形式")
|
||||||
|
date_end = fields.Date(string="合同结束日期")
|
||||||
|
end_comparison = fields.Selection([('red', '小于30天'), ('yellow', '小于60天'), ('blue', '小于90天')], string="结束时间对比(颜色)",
|
||||||
|
compute='_compute_end_comparison', store=True)
|
||||||
|
|
||||||
|
# 个人信息
|
||||||
|
user_sex = fields.Selection([('girl', '女'), ('boy', '男')], string="性别")
|
||||||
|
user_state = fields.Selection([('in', '在职'), ('leave', '离职'), ('withdraw', '退休'), ('borrow', '借调'),
|
||||||
|
('expatriate', '外派'), ('adjunct', '在职(兼职)')],
|
||||||
|
string="员工状态", default='in')
|
||||||
|
user_number_start_time = fields.Date(string="身份证开始时间")
|
||||||
|
department_ids = fields.Many2many('hr.department', string="辅助部门")
|
||||||
|
user_number_end_time = fields.Date(string="身份证到期时间")
|
||||||
|
is_identification = fields.Boolean(string="是否填写身份证")
|
||||||
|
card_documents_id = fields.Many2one('ir.attachment', string="身份证正面")
|
||||||
|
card_back_documents_id = fields.Many2one('ir.attachment', string="身份证反面")
|
||||||
|
hukou_documents_id = fields.Many2one('ir.attachment', string="户口本")
|
||||||
|
marriage_documents_id = fields.Many2one('ir.attachment', string="结婚证")
|
||||||
|
nation = fields.Selection(nation_list, string="民族")
|
||||||
|
old = fields.Integer(string="年龄", compute='_compute_info', store=True)
|
||||||
|
birthday = fields.Date(string="出生年月", compute='_compute_info', tracking=False)
|
||||||
|
birthday_month = fields.Integer(string="生日月份", compute='_compute_info')
|
||||||
|
user_type = fields.Char(string="人员类别")
|
||||||
|
retire_date = fields.Date(string="退休日期")
|
||||||
|
retire_years = fields.Integer(string="离退休年限", compute='_compute_retire_years')
|
||||||
|
political_aspects = fields.Selection([('cpc_dang', '中共党员'), ('nld', '民盟'),
|
||||||
|
('league_member', '共青团员'), ('people', '群众')], string="政治面貌")
|
||||||
|
dang_data = fields.Date(string="入团(党)时间")
|
||||||
|
user_origin = fields.Char(string="籍贯")
|
||||||
|
user_location = fields.Char(string="户口所在地")
|
||||||
|
is_location = fields.Boolean(string="是否本地")
|
||||||
|
bank_account = fields.Char(string="银行卡号")
|
||||||
|
user_location_type = fields.Selection([('formal', '本地农业'), ('labor', '本地非农业'),
|
||||||
|
('part_time', '外地农业'), ('practice', '外地非农业')], string="户籍类型")
|
||||||
|
is_local_location = fields.Char(
|
||||||
|
string="是否外地",
|
||||||
|
compute='_compute_is_local_location',
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends('user_location_type')
|
||||||
|
def _compute_is_local_location(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.user_location_type in ('formal', 'labor'):
|
||||||
|
rec.is_local_location = '否'
|
||||||
|
else:
|
||||||
|
rec.is_local_location = '是'
|
||||||
|
|
||||||
|
# 工作信息
|
||||||
|
entry_date = fields.Date(string="入职日期")
|
||||||
|
confirmation_date = fields.Date(string="转正日期")
|
||||||
|
company_old = fields.Char(string="司龄", compute='_compute_company_old')
|
||||||
|
company_old_year = fields.Integer(string="司龄(年)", compute='_compute_company_old')
|
||||||
|
regularization_year = fields.Integer(string="转正(年)", compute='_compute_regularization_year')
|
||||||
|
open_start_time = fields.Date(string="参加工作时间")
|
||||||
|
is_membership = fields.Boolean(string="工会会员")
|
||||||
|
cumulative_work_month = fields.Integer(string="累计工作月数", compute='_compute_social_insurance_info')
|
||||||
|
cumulative_work_years = fields.Char(string="累计工作年限", compute='_compute_cumulative_work_years')
|
||||||
|
interrupt_work_month = fields.Integer(string="中断工作月数")
|
||||||
|
compute_char = fields.Char(string="工龄", compute='_compute_social_insurance_info')
|
||||||
|
compute_month = fields.Integer(string="工龄(月)")
|
||||||
|
social_insurance_date = fields.Date(string="缴纳社保日期")
|
||||||
|
social_insurance_time = fields.Char(string="社保时长", compute='_compute_social_insurance_info', store=True)
|
||||||
|
|
||||||
|
# 教育信息
|
||||||
|
high_degree = fields.Char(string="最高学历")
|
||||||
|
high_degree2 = fields.Char(string="最高学位")
|
||||||
|
graduate_school = fields.Char(string="毕业院校")
|
||||||
|
profession = fields.Char(string="专业")
|
||||||
|
graduate_date = fields.Date(string="毕业时间")
|
||||||
|
job_lv_date = fields.Char(string="职级等级/签发日期(人事员)")
|
||||||
|
job_lv = fields.Char(string="专业技术/技能等级(劳动局)")
|
||||||
|
job_qualification_date = fields.Date(string="职业资格/考取日期")
|
||||||
|
job_lv_change = fields.Char(string="职级变动")
|
||||||
|
move_note_date = fields.Text(string="异动情况/移动日期")
|
||||||
|
|
||||||
|
# 紧急联系人
|
||||||
|
emergency_contacts_name = fields.Char(string="称谓")
|
||||||
|
|
||||||
|
# 其他信息
|
||||||
|
archiving_agency = fields.Char(string="存档机构")
|
||||||
|
over_data_time = fields.Datetime(string="最后更新数据时间")
|
||||||
|
|
||||||
|
# 紧急联系人
|
||||||
|
emergency_contact_ids = fields.One2many('yuthon.emergency.contact.line', 'employee_id', string="紧急联系人明细行")
|
||||||
|
# 工作经历
|
||||||
|
work_experience_ids = fields.One2many('yuthon.work.experience.line', 'employee_id', string="工作经历明细行")
|
||||||
|
# 教育经历
|
||||||
|
education_experience_ids = fields.One2many('yuthon.education.experience.line', 'employee_id', string="教育经历明细行")
|
||||||
|
# 技能证书管理
|
||||||
|
skills_certificates_ids = fields.One2many('yuthon.skills.certificates.line', 'employee_id', string="教育经历明细行")
|
||||||
|
|
||||||
|
# 拼音搜索
|
||||||
|
pinyin_name = fields.Char(string="拼音名称", compute='_compute_pinyin', index=True, store=True)
|
||||||
|
pinyin_min = fields.Char(string='拼音缩写', compute='_compute_pinyin', index=True, store=True)
|
||||||
|
|
||||||
|
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company', search='_search_part_of_company')
|
||||||
|
resource_calendar_id = fields.Many2one(compute='_compute_resource_calendar_id', string="工作时间", store=True)
|
||||||
|
|
||||||
|
marital = fields.Selection([
|
||||||
|
('single', '未婚'),
|
||||||
|
('married', 'Married'),
|
||||||
|
('cohabitant', 'Legal Cohabitant'),
|
||||||
|
('widower', 'Widower'),
|
||||||
|
('divorced', 'Divorced')
|
||||||
|
], string='Marital Status', groups="hr.group_hr_user", default='single', tracking=True)
|
||||||
|
|
||||||
|
def get_contract_count(self):
|
||||||
|
return self.env['hr.employee'].search_count([('end_comparison', 'in', ['red', 'yellow', 'blue'])])
|
||||||
|
|
||||||
|
def update_end_comparison(self):
|
||||||
|
employees = self.env['hr.employee'].search([])
|
||||||
|
for employee in employees:
|
||||||
|
employee._compute_end_comparison()
|
||||||
|
|
||||||
|
@api.depends('date_end')
|
||||||
|
def _compute_end_comparison(self):
|
||||||
|
for record in self:
|
||||||
|
now = date.today()
|
||||||
|
if record.date_end:
|
||||||
|
if record.date_end > now:
|
||||||
|
delta = record.date_end - now
|
||||||
|
if 60 <= delta.days < 90:
|
||||||
|
record.end_comparison = 'blue'
|
||||||
|
elif 30 <= delta.days < 60:
|
||||||
|
record.end_comparison = 'yellow'
|
||||||
|
elif delta.days < 30:
|
||||||
|
record.end_comparison = 'red'
|
||||||
|
else:
|
||||||
|
record.end_comparison = None
|
||||||
|
elif record.date_end < now:
|
||||||
|
record.end_comparison = 'red'
|
||||||
|
else:
|
||||||
|
record.end_comparison = None
|
||||||
|
else:
|
||||||
|
record.end_comparison = None
|
||||||
|
|
||||||
|
def get_my_employee_action(self):
|
||||||
|
current_user = self.env.user
|
||||||
|
my_employee = self.search([('user_id', '=', current_user.id)], limit=1)
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': '个人基础信息',
|
||||||
|
'res_model': 'hr.employee',
|
||||||
|
'res_id': my_employee.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'view_type': 'form',
|
||||||
|
'target': 'current',
|
||||||
|
'view_id': self.env.ref('yuthon_hr_employee.view_basic_personal_information').id,
|
||||||
|
}
|
||||||
|
|
||||||
|
def change_password_wizard(self):
|
||||||
|
return {
|
||||||
|
'name': '修改密码',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'reset.password.wizard',
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {'default_employee_id': self.env.user.employee_ids.id},
|
||||||
|
}
|
||||||
|
|
||||||
|
@api.depends('confirmation_date')
|
||||||
|
def _compute_regularization_year(self):
|
||||||
|
for record in self:
|
||||||
|
if record.confirmation_date:
|
||||||
|
record.regularization_year = fields.Date.today().year - record.confirmation_date.year
|
||||||
|
else:
|
||||||
|
record.regularization_year = 0
|
||||||
|
|
||||||
|
@api.depends('entry_date')
|
||||||
|
def _compute_company_old(self):
|
||||||
|
"""司龄计算"""
|
||||||
|
for record in self:
|
||||||
|
if record.entry_date:
|
||||||
|
today = datetime.now().date()
|
||||||
|
record.company_old_year = today.year - record.entry_date.year + 1
|
||||||
|
years_diff = today.year - record.entry_date.year
|
||||||
|
months_diff = today.month - record.entry_date.month
|
||||||
|
if today.month < record.entry_date.month:
|
||||||
|
years_diff -= 1
|
||||||
|
months_diff += 12
|
||||||
|
record.company_old = str(years_diff) + '年' + str(months_diff) + '月'
|
||||||
|
else:
|
||||||
|
record.company_old = None
|
||||||
|
record.company_old_year = 0
|
||||||
|
|
||||||
|
@api.depends('open_start_time', 'interrupt_work_month')
|
||||||
|
def _compute_social_insurance_info(self):
|
||||||
|
for record in self:
|
||||||
|
cumulative_months = 0
|
||||||
|
if record.open_start_time:
|
||||||
|
today = datetime.now().date()
|
||||||
|
delta = relativedelta(today, record.open_start_time)
|
||||||
|
cumulative_months = delta.years * 12 + delta.months
|
||||||
|
record.cumulative_work_month = cumulative_months
|
||||||
|
total_months = cumulative_months - record.interrupt_work_month
|
||||||
|
record.compute_month = total_months
|
||||||
|
if total_months <= 0:
|
||||||
|
social_insurance_time_str = "0个月"
|
||||||
|
else:
|
||||||
|
years = total_months // 12
|
||||||
|
months = total_months % 12
|
||||||
|
social_insurance_time_str = f"{years}年{months}个月" if years > 0 and months > 0 else (
|
||||||
|
f"{years}年" if years > 0 else f"{months}个月")
|
||||||
|
record.social_insurance_time = social_insurance_time_str
|
||||||
|
record.compute_char = social_insurance_time_str
|
||||||
|
|
||||||
|
@api.depends('cumulative_work_month')
|
||||||
|
def _compute_cumulative_work_years(self):
|
||||||
|
for record in self:
|
||||||
|
if record.cumulative_work_month:
|
||||||
|
if record.cumulative_work_month <= 0:
|
||||||
|
record.social_insurance_time = "0个月"
|
||||||
|
years = record.cumulative_work_month // 12
|
||||||
|
months = record.cumulative_work_month % 12
|
||||||
|
if years > 0:
|
||||||
|
result_str = f"{years}年"
|
||||||
|
if months > 0:
|
||||||
|
result_str += f"{months}个月"
|
||||||
|
else:
|
||||||
|
result_str = f"{months}个月"
|
||||||
|
record.cumulative_work_years = result_str
|
||||||
|
else:
|
||||||
|
record.cumulative_work_years = None
|
||||||
|
|
||||||
|
@api.depends('identification_id')
|
||||||
|
def _compute_info(self):
|
||||||
|
for record in self:
|
||||||
|
if record.identification_id:
|
||||||
|
self.is_identification = True
|
||||||
|
id_number = record.identification_id.upper()
|
||||||
|
if len(id_number) != 18:
|
||||||
|
record._clear_info()
|
||||||
|
if not (all(c.isdigit() for c in id_number[:-1]) and (
|
||||||
|
id_number[-1].isdigit() or id_number[-1] == 'X')):
|
||||||
|
raise ValueError("身份证号码包含无效字符")
|
||||||
|
|
||||||
|
year = int(id_number[6:10])
|
||||||
|
month = int(id_number[10:12])
|
||||||
|
day = int(id_number[12:14])
|
||||||
|
record.birthday = date(year, month, day)
|
||||||
|
record.birthday_month = month
|
||||||
|
|
||||||
|
today = date.today()
|
||||||
|
if year is not None:
|
||||||
|
record.old = today.year - year - ((today.month, today.day) < (month, day))
|
||||||
|
else:
|
||||||
|
record.old = 0
|
||||||
|
gender_code = int(id_number[-2])
|
||||||
|
record.user_sex = 'girl' if gender_code % 2 == 0 else 'boy'
|
||||||
|
else:
|
||||||
|
record._clear_info()
|
||||||
|
self.is_identification = False
|
||||||
|
|
||||||
|
def _clear_info(self):
|
||||||
|
self.birthday = False
|
||||||
|
self.birthday_month = False
|
||||||
|
self.old = 0
|
||||||
|
self.user_sex = False
|
||||||
|
|
||||||
|
@api.depends('retire_date')
|
||||||
|
def _compute_retire_years(self):
|
||||||
|
"""离退休年限计算"""
|
||||||
|
for record in self:
|
||||||
|
if record.retire_date:
|
||||||
|
today = datetime.now().date()
|
||||||
|
years_diff = today.year - record.retire_date.year
|
||||||
|
compute = years_diff
|
||||||
|
if compute < 0:
|
||||||
|
record.retire_years = -1 * compute
|
||||||
|
else:
|
||||||
|
record.retire_years = 0
|
||||||
|
else:
|
||||||
|
record.retire_years = 0
|
||||||
|
|
||||||
|
@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):
|
||||||
|
if operator not in ('=', '!=') or not isinstance(value, bool):
|
||||||
|
raise UserError(('Operation not supported'))
|
||||||
|
company_ids = self.env.companies.ids
|
||||||
|
if not value:
|
||||||
|
operator = '!=' if operator == '=' else '='
|
||||||
|
if operator == '=':
|
||||||
|
return [('company_id', 'in', company_ids)]
|
||||||
|
else:
|
||||||
|
return [('company_id', 'not in', company_ids)]
|
||||||
|
|
||||||
|
@api.onchange('user_location')
|
||||||
|
def _onchange_user_location(self):
|
||||||
|
if self.user_location:
|
||||||
|
self.is_location = True
|
||||||
|
else:
|
||||||
|
self.is_location = False
|
||||||
|
|
||||||
|
@api.depends('name')
|
||||||
|
def _compute_pinyin(self):
|
||||||
|
for employee in self:
|
||||||
|
employee.pinyin_name = ''.join(lazy_pinyin(employee.name)) if employee.name else ''
|
||||||
|
employee.pinyin_min = ''.join(lazy_pinyin(employee.name, style=STYLE_FIRST_LETTER)) if employee.name else ''
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _name_search(self, name, domain=None, operator='ilike', limit=None, order=None):
|
||||||
|
domain = domain or []
|
||||||
|
if name:
|
||||||
|
has_chinese = any('\u4e00' <= char <= '\u9fff' for char in name)
|
||||||
|
if has_chinese:
|
||||||
|
search_domain = [('name', operator, name)]
|
||||||
|
else:
|
||||||
|
search_domain = ['|',
|
||||||
|
('pinyin_name', operator, name),
|
||||||
|
('name', '=', name)]
|
||||||
|
domain = expression.AND([search_domain, domain])
|
||||||
|
return self._search(domain, limit=limit, order=order)
|
||||||
|
|
||||||
|
def action_hr_employee(self):
|
||||||
|
return {
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"name": "批量更新员工",
|
||||||
|
"res_model": "yuthon.hr.employee.wizard",
|
||||||
|
"view_mode": "form",
|
||||||
|
"target": "new",
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_hr_time_form(self):
|
||||||
|
return {
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"name": "批量更新时间表",
|
||||||
|
"res_model": "yuthon.hr.employee.time.wizard",
|
||||||
|
"view_mode": "form",
|
||||||
|
"target": "new",
|
||||||
|
}
|
||||||
61
yuthon_hr_employee/models/yuthon_hr_employee_pages.py
Normal file
61
yuthon_hr_employee/models/yuthon_hr_employee_pages.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonWorkExperienceLine(models.Model):
|
||||||
|
_name = 'yuthon.work.experience.line'
|
||||||
|
_description = "工作经验明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
start_date = fields.Date(string="开始日期")
|
||||||
|
end_date = fields.Date(string="结束日期")
|
||||||
|
work_place = fields.Char(string="工作单位")
|
||||||
|
post = fields.Char(string="岗位")
|
||||||
|
contact_user = fields.Char(string="联系人")
|
||||||
|
phone_number = fields.Char(string="联系人手机号")
|
||||||
|
reason_leaving = fields.Char(string="离职原因")
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonEducationExperienceLine(models.Model):
|
||||||
|
_name = 'yuthon.education.experience.line'
|
||||||
|
_description = "教育经历明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
name_school = fields.Char(string="学校名称")
|
||||||
|
start_date = fields.Date(string="入学时间")
|
||||||
|
end_date = fields.Date(string="毕业时间")
|
||||||
|
in_school_duties = fields.Char(string="在校职务")
|
||||||
|
professional = fields.Char(string="专业")
|
||||||
|
learning_style = fields.Selection([('full_time', '全日制'), ('upgrade', '专升本'), ('adult_education', '成人教育'),
|
||||||
|
('self_study', '自考'), ('online_education', '网络教育'),('correspondence', '函授')], string="学习方式")
|
||||||
|
part_time_degree = fields.Char(string="学历")
|
||||||
|
full_time_degree = fields.Char(string="学位")
|
||||||
|
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
|
|
||||||
|
class YuthonEmergencyContactLine(models.Model):
|
||||||
|
_name = 'yuthon.emergency.contact.line'
|
||||||
|
_description = "紧急联系人明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
name = fields.Char(string="姓名")
|
||||||
|
user_sfz = fields.Char(string="身份证")
|
||||||
|
user_relationship = fields.Selection([("father", "父亲"), ('mother', '母亲'), ("spouse", "妻子"),
|
||||||
|
('boys', '儿子'), ('daughter', '女儿'),('didi', '弟弟'),
|
||||||
|
('husband', '丈夫'), ('sister', '姐姐'), ('brother', '哥哥'),
|
||||||
|
('sister_do', '妹妹')], string="关系")
|
||||||
|
workplace = fields.Char(string="工作单位")
|
||||||
|
post = fields.Char(string="岗位")
|
||||||
|
phone_number = fields.Char(string="手机号")
|
||||||
|
home_address = fields.Char(string="家庭住址")
|
||||||
|
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonSkillsCertificatesLine(models.Model):
|
||||||
|
_name = 'yuthon.skills.certificates.line'
|
||||||
|
_description = "技能证书管理明细行"
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
rank_name = fields.Char(string='职级名称')
|
||||||
|
rank_type = fields.Char(string='职级类型')
|
||||||
|
rank_time = fields.Date(string='职级时间')
|
||||||
|
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
8
yuthon_hr_employee/security/ir.model.access.csv
Normal file
8
yuthon_hr_employee/security/ir.model.access.csv
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_yuthon_hr_employee_wizard,yuthon_hr_employee_wizard,model_yuthon_hr_employee_wizard,base.group_user,1,1,1,1
|
||||||
|
access_yuthon_emergency_contact_line,yuthon_emergency_contact_line,model_yuthon_emergency_contact_line,base.group_user,1,1,1,1
|
||||||
|
access_yuthon_work_experience_line,yuthon_work_experience_line,model_yuthon_work_experience_line,base.group_user,1,1,1,1
|
||||||
|
access_yuthon_education_experience_line,yuthon_education_experience_line,model_yuthon_education_experience_line,base.group_user,1,1,1,1
|
||||||
|
access_yuthon_skills_certificates_line,yuthon_skills_certificates_line,model_yuthon_skills_certificates_line,base.group_user,1,1,1,1
|
||||||
|
access_yuthon_hr_employee_time_wizard,yuthon_hr_employee_time_wizard,model_yuthon_hr_employee_time_wizard,base.group_user,1,1,1,1
|
||||||
|
access_reset_password_wizard,reset_password_wizard,model_reset_password_wizard,base.group_user,1,1,1,1
|
||||||
|
BIN
yuthon_hr_employee/static/description/icon.png
Normal file
BIN
yuthon_hr_employee/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
1
yuthon_hr_employee/static/description/icon.svg
Normal file
1
yuthon_hr_employee/static/description/icon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="50" height="50" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M34 17a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" fill="#985184"/><path d="M12 24a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z" fill="#FBB945"/><path d="M46 24a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z" fill="#1AD3BB"/><path d="M25 30H4a4 4 0 0 0-4 4v4a4 4 0 0 0 4 4h21V30Z" fill="#FBB945"/><path d="M46 30H25v12h21a4 4 0 0 0 4-4v-4a4 4 0 0 0-4-4Z" fill="#1AD3BB"/><path d="M12 30h14c6.627 0 12 5.373 12 12H24c-6.627 0-12-5.373-12-12Z" fill="#985184"/></svg>
|
||||||
|
After Width: | Height: | Size: 511 B |
BIN
yuthon_hr_employee/static/others/user_template.xlsx
Normal file
BIN
yuthon_hr_employee/static/others/user_template.xlsx
Normal file
Binary file not shown.
13
yuthon_hr_employee/static/scss/yuthon_employee_scss.scss
Normal file
13
yuthon_hr_employee/static/scss/yuthon_employee_scss.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.list_width{
|
||||||
|
width: 400px !important;
|
||||||
|
max-width: 400px !important;
|
||||||
|
}
|
||||||
|
.list_ids_width{
|
||||||
|
width: 400px !important;
|
||||||
|
max-width: 400px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list_employee_width{
|
||||||
|
width: 80px !important;
|
||||||
|
max-width: 80px !important;
|
||||||
|
}
|
||||||
206
yuthon_hr_employee/views/inherit_hr_job_views.xml
Normal file
206
yuthon_hr_employee/views/inherit_hr_job_views.xml
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="hr_job_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">hr.job.inherit</field>
|
||||||
|
<field name="model">hr.job</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_hr_job_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//list" position="attributes">
|
||||||
|
<attribute name="limit">20</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='name']" position="attributes">
|
||||||
|
<attribute name="string">岗位</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//list" position="attributes">
|
||||||
|
<attribute name="decoration-danger">compiling_status == '1'</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="attributes">
|
||||||
|
<attribute name="string">所属部门</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='expected_employees']" position="attributes">
|
||||||
|
<attribute name="column_invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='no_of_employee']" position="attributes">
|
||||||
|
<attribute name="column_invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='no_of_recruitment']" position="attributes">
|
||||||
|
<attribute name="column_invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="after">
|
||||||
|
<field name="job_personnel_ids" column_invisible="1"/>
|
||||||
|
<field name="job_personnel" class="list_width"/>
|
||||||
|
<field name="establishment_quotas"/>
|
||||||
|
<field name="no_of_employee" string="在编人数"/>
|
||||||
|
<field name="compiling_external"/>
|
||||||
|
<field name="missing_number"/>
|
||||||
|
<field name="recruitment_employees"/>
|
||||||
|
<field name="compiling_status"/>
|
||||||
|
<field name="start_date" optional="hide"/>
|
||||||
|
<field name="end_date" optional="hide"/>
|
||||||
|
<field name="is_enable" optional="hide"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="hr_job_inherit_form" model="ir.ui.view">
|
||||||
|
<field name="name">hr.job.inherit.form</field>
|
||||||
|
<field name="model">hr.job</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_hr_job_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='recruitment_page']" position="attributes">
|
||||||
|
<attribute name="string">基础信息</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='job_description_page']" position="attributes">
|
||||||
|
<attribute name="string">任职要求</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='job_description_page']" position="after">
|
||||||
|
<attribute name="string">其他信息</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="after">
|
||||||
|
<group string="岗位信息">
|
||||||
|
<field name="number"/>
|
||||||
|
<field name="company_id"/>
|
||||||
|
<field name="department_ids"/>
|
||||||
|
<field name="job_personnel_ids" widget="many2many_tags"/>
|
||||||
|
<field name="establishment_quotas"/>
|
||||||
|
<field name="no_of_employee" string="在编人数"/>
|
||||||
|
<field name="recruitment_employees"/>
|
||||||
|
<field name="compiling_external"/>
|
||||||
|
<field name="missing_number" force_save="1"/>
|
||||||
|
<field name="compiling_status"/>
|
||||||
|
<field name="function"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
<div name="recruitment_target" position="after">
|
||||||
|
<group string="招聘信息">
|
||||||
|
<field name="address_id"/>
|
||||||
|
<field name="industry_id"/>
|
||||||
|
<field name="email"/>
|
||||||
|
<field name="recruit_employee_id"/>
|
||||||
|
<field name="interviewer_employee_id"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="is_enable"/>
|
||||||
|
<field name="is_published"/>
|
||||||
|
</group>
|
||||||
|
</div>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="attributes">
|
||||||
|
<attribute name="string">所属部门</attribute>
|
||||||
|
<attribute name="required">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 隐藏活动附件聊天界面 -->
|
||||||
|
<xpath expr="//chatter" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 隐藏活动日期 -->
|
||||||
|
<xpath expr="//field[@name='date_from']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 隐藏新员工 -->
|
||||||
|
<xpath expr="//label[@for='no_of_recruitment']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='no_of_recruitment']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='description']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 隐藏描述 -->
|
||||||
|
<xpath expr="//div[@name='recruitment_target']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//field[@name='description']" position="after">
|
||||||
|
<group col="3">
|
||||||
|
<group>
|
||||||
|
<label for="degree"/>
|
||||||
|
<div>
|
||||||
|
<field name="degree" class="oe_inline"/>及以上
|
||||||
|
</div>
|
||||||
|
<field name="sex"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="political_aspects"/>
|
||||||
|
<field name="admissions"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="professional"/>
|
||||||
|
<field name="job_title"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<label for="age_start"/>
|
||||||
|
<div>
|
||||||
|
<field name="age_start" class="oe_inline"/>至
|
||||||
|
<field name="age_end" class="oe_inline"/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<label for="years_of_service"/>
|
||||||
|
<div>
|
||||||
|
<field name="years_of_service" class="oe_inline"/>年及以上
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="competency_requirements"/>
|
||||||
|
<field name="other_notes"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<!-- <xpath expr="//field[@name='contract_type_id']" position="attributes">-->
|
||||||
|
<!-- <attribute name="string">合同类型</attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 隐藏skill_ids -->
|
||||||
|
<record id="custom_hr_job_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">custom.hr.job.form.inherit</field>
|
||||||
|
<field name="model">hr.job</field>
|
||||||
|
<field name="inherit_id" ref="hr_recruitment_skills.hr_job_form_inherit_hr_recruitment_skills"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='skill_ids']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!-- 隐藏原生的工作地点,另外的电子邮箱等 -->
|
||||||
|
<record id="hr_job_inherit_hide_user_id" model="ir.ui.view">
|
||||||
|
<field name="name">hr.job.form1</field>
|
||||||
|
<field name="model">hr.job</field>
|
||||||
|
<field name="inherit_id" ref="hr_recruitment.hr_job_survey"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='user_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='interviewer_ids']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='address_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//label[@for='address_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div[contains(@class, 'o_row') and .//field[@name='address_id']]" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='industry_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//label[@for='alias_name']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div[@name='alias_def']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
||||||
17
yuthon_hr_employee/views/inherit_hr_work_location_views.xml
Normal file
17
yuthon_hr_employee/views/inherit_hr_work_location_views.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="hr_work_location_inherit_form" model="ir.ui.view">
|
||||||
|
<field name="name">hr.work.location.inherit.form</field>
|
||||||
|
<field name="model">hr.work.location</field>
|
||||||
|
<field name="inherit_id" ref="hr.hr_work_location_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//form" position="attributes">
|
||||||
|
<attribute name="create">0</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='address_id']" position="attributes">
|
||||||
|
<attribute name="string">联系人</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
||||||
28
yuthon_hr_employee/views/inherit_res_groups_views.xml
Normal file
28
yuthon_hr_employee/views/inherit_res_groups_views.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="res_groups_view_form2" model="ir.ui.view">
|
||||||
|
<field name="name">unlink.res.groups.form</field>
|
||||||
|
<field name="model">res.groups</field>
|
||||||
|
<field name="inherit_id" ref="base.view_groups_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//form" position="attributes">
|
||||||
|
<attribute name="create">0</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//group" position="before">
|
||||||
|
<header>
|
||||||
|
<button class="oe_highlight" name="users_unlink" string="清除用户" type="object"/>
|
||||||
|
<button class="oe_highlight" name="domain_unlink" string="清除过滤" type="object"/>
|
||||||
|
<button class="oe_highlight" name="rule_unlink" string="清除规则权限" type="object"/>
|
||||||
|
</header>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='share']" position="after">
|
||||||
|
<field name="users_ids" widget="many2many_tags" invisible="1"/>
|
||||||
|
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||||
|
<field name="department_ids" widget="many2many_tags" options='{"no_create": True}'/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='menu_access']" position="attributes">
|
||||||
|
<attribute name="widget">many2many_checkboxes</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
29
yuthon_hr_employee/views/inherit_res_user_views.xml
Normal file
29
yuthon_hr_employee/views/inherit_res_user_views.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="all_data_create_user" model="ir.actions.server">
|
||||||
|
<field name="name">批量创建员工</field>
|
||||||
|
<field name="model_id" ref="base.model_res_users"/>
|
||||||
|
<field name="binding_model_id" ref="base.model_res_users" />
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">records.all_create_users()</field>
|
||||||
|
<field name="groups_id" eval="[(4, ref('base.group_system'))]"/>
|
||||||
|
</record>
|
||||||
|
<record id="all_users_email" model="ir.actions.server">
|
||||||
|
<field name="name">更新邮件信息</field>
|
||||||
|
<field name="model_id" ref="base.model_res_users"/>
|
||||||
|
<field name="binding_model_id" ref="base.model_res_users" />
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">records.all_users_email()</field>
|
||||||
|
<field name="groups_id" eval="[(4, ref('base.group_system'))]"/>
|
||||||
|
</record>
|
||||||
|
<record id="inherit_res_users_tree" model="ir.ui.view">
|
||||||
|
<field name="name">res.users.tree.inherit</field>
|
||||||
|
<field name="model">res.users</field>
|
||||||
|
<field name="inherit_id" ref="base.view_users_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='lang']" position="attributes">
|
||||||
|
<attribute name="column_invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
524
yuthon_hr_employee/views/yuthon_hr_employee_views.xml
Normal file
524
yuthon_hr_employee/views/yuthon_hr_employee_views.xml
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_basic_personal_information" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.basic.personal.information</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="个人基础信息" create="false" delete="0">
|
||||||
|
<sheet>
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name"/>
|
||||||
|
<h1>
|
||||||
|
<field name="name" readonly="1"/>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="o_employee_avatar m-0 p-0">
|
||||||
|
<field name="image_1920" widget="image" class="oe_avatar m-0"
|
||||||
|
options="{"zoom": true, "preview_image":"avatar_128"}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<group col="2" readonly="1">
|
||||||
|
<group>
|
||||||
|
<field name="mobile_phone"/>
|
||||||
|
<field name="work_phone"/>
|
||||||
|
<field name="work_email"/>
|
||||||
|
<field name="user_state" readonly="1" options="{'no_open': True}"/>
|
||||||
|
<field name="company_id" readonly="1" options="{'no_open': True}"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="department_id" readonly="1" options="{'no_open': True}"/>
|
||||||
|
<field name="department_ids" widget="many2many_tags" readonly="1"
|
||||||
|
options="{'no_open': True}"/>
|
||||||
|
<field name="number" readonly="1"/>
|
||||||
|
<field name="user_number" readonly="1" invisible="1"/>
|
||||||
|
<field name="parent_id" readonly="1" options="{'no_open': True}" string="上级领导"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page string="工作经历">
|
||||||
|
<field name="work_experience_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="work_place"/>
|
||||||
|
<field name="post"/>
|
||||||
|
<field name="contact_user"/>
|
||||||
|
<field name="phone_number"/>
|
||||||
|
<field name="reason_leaving"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="教育经历">
|
||||||
|
<field name="education_experience_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="learning_style"/>
|
||||||
|
<field name="name_school"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="part_time_degree"/>
|
||||||
|
<field name="full_time_degree"/>
|
||||||
|
<field name="professional"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page name="技能证书管理">
|
||||||
|
<field name="skills_certificates_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="rank_name"/>
|
||||||
|
<field name="rank_type"/>
|
||||||
|
<field name="rank_time"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="紧急联系人">
|
||||||
|
<field name="emergency_contact_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="user_relationship"/>
|
||||||
|
<field name="user_sfz"/>
|
||||||
|
<field name="workplace"/>
|
||||||
|
<field name="post"/>
|
||||||
|
<field name="phone_number"/>
|
||||||
|
<field name="home_address"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_employee_inherit_del_form" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.inherit.del.form.inherit</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- 源码的信息暂时隐藏 -->
|
||||||
|
<xpath expr="//form" position="attributes">
|
||||||
|
<attribute name="create">0</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='parent_id']" position="attributes">
|
||||||
|
<attribute name="string">上级领导</attribute>
|
||||||
|
<attribute name="options">"{'no_create': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='coach_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='resource_calendar_id']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
<attribute name="options">"{'no_open': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='tz']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='company_id']" position="attributes">
|
||||||
|
<attribute name="options">"{'no_open': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='work_email']" position="attributes">
|
||||||
|
<attribute name="options">"{'no_open': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="attributes">
|
||||||
|
<attribute name="options">"{'no_open': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='work_email']" position="after">
|
||||||
|
<field name="user_state"/>
|
||||||
|
<field name="company_ids" invisible="1"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="attributes">
|
||||||
|
<attribute name="widget">ztree_select</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='bank_account_id']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='personal_information']" position="replace">
|
||||||
|
<page name="personal_information" string="个人隐私信息">
|
||||||
|
<group>
|
||||||
|
<group string="基本信息">
|
||||||
|
<field name="private_street" string="家庭住址"/>
|
||||||
|
<field name="user_origin"/>
|
||||||
|
<field name="user_location"/>
|
||||||
|
<field name="is_location" invisible="1"/>
|
||||||
|
<field name="hukou_documents_id" invisible="not is_location" widget="preview_many2one"/>
|
||||||
|
<field name="user_location_type"/>
|
||||||
|
<field name="private_email" string="邮箱"/>
|
||||||
|
<field name="private_phone" string="电话"/>
|
||||||
|
<field name="pinyin_name"/>
|
||||||
|
<field name="pinyin_min"/>
|
||||||
|
<!-- 增加两个字段 -->
|
||||||
|
<field name="is_soldiers"/>
|
||||||
|
<field name="bank_account_id" context="{'default_partner_id': work_contact_id}"
|
||||||
|
options="{'no_quick_create': True}" readonly="not id"/>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group string="基本信息">
|
||||||
|
<field name="identification_id"/>
|
||||||
|
<field name="user_sex"/>
|
||||||
|
<field name="user_number_start_time"/>
|
||||||
|
<field name="user_number_end_time"/>
|
||||||
|
<field name="nation"/>
|
||||||
|
<field name="birthday"/>
|
||||||
|
<label for="old"/>
|
||||||
|
<div>
|
||||||
|
<field name="old" style="width: 5%"/>
|
||||||
|
</div>
|
||||||
|
<label for="birthday_month"/>
|
||||||
|
<div>
|
||||||
|
<field name="birthday_month" style="width: 5%"/>
|
||||||
|
</div>
|
||||||
|
<field name="political_aspects"/>
|
||||||
|
<field name="dang_data"/>
|
||||||
|
<!-- 原字段的关联 暂时保留并隐藏 -->
|
||||||
|
<field name="country_of_birth" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
<group string="教育">
|
||||||
|
<field name="high_degree"/>
|
||||||
|
<field name="high_degree2"/>
|
||||||
|
<field name="job_lv_change"/>
|
||||||
|
<field name="move_note_date"/>
|
||||||
|
</group>
|
||||||
|
<group string="家庭状态">
|
||||||
|
<field name="marital"/>
|
||||||
|
<field name="marriage_documents_id" invisible="marital != 'married'"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
widget="preview_many2one"/>
|
||||||
|
<field name="spouse_complete_name" invisible="marital not in ['married', 'cohabitant']"/>
|
||||||
|
<field name="spouse_birthdate" invisible="marital not in ['married', 'cohabitant']"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//group[@name='identification_group']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='job_id']" position="attributes">
|
||||||
|
<attribute name="options">"{'no_open': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='parent_id']" position="attributes">
|
||||||
|
<attribute name="options">"{'no_create': True, 'no_open': True}"</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='job_id']" position="after">
|
||||||
|
<field name="number"/>
|
||||||
|
<field name="user_number" invisible="1"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='department_id']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='job_id']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='identification_id']" position="after">
|
||||||
|
<field name="is_identification" invisible="1"/>
|
||||||
|
<field name="card_documents_id" widget="preview_many2one" invisible="not is_identification"/>
|
||||||
|
<field name="card_back_documents_id" widget="preview_many2one" invisible="not is_identification"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='category_ids']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='category_ids']" position="after">
|
||||||
|
<!-- <field name="user_type"/>-->
|
||||||
|
<field name="retire_date"/>
|
||||||
|
<!-- <label for="retire_years"/>-->
|
||||||
|
<!-- <div>-->
|
||||||
|
<!-- <field name="retire_years" style="width: 5%"/>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <field name="user_salary"/>-->
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//group[@name='active_group']" position="after">
|
||||||
|
<group string="工作信息">
|
||||||
|
<field name="entry_date"/>
|
||||||
|
<field name="confirmation_date"/>
|
||||||
|
<field name="company_old"/>
|
||||||
|
<field name="regularization_year" invisible="1"/>
|
||||||
|
<field name="open_start_time"/>
|
||||||
|
<field name="cumulative_work_month"/>
|
||||||
|
<field name="cumulative_work_years"/>
|
||||||
|
<field name="interrupt_work_month"/>
|
||||||
|
<field name="compute_month" invisible="1"/>
|
||||||
|
<field name="compute_char"/>
|
||||||
|
<field name="social_insurance_date"/>
|
||||||
|
<field name="social_insurance_time"/>
|
||||||
|
<field name="is_membership"/>
|
||||||
|
</group>
|
||||||
|
<group string="其他">
|
||||||
|
<field name="archiving_agency"/>
|
||||||
|
<field name="over_data_time"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='hr_settings']" position="before">
|
||||||
|
<page string="工作经历">
|
||||||
|
<field name="work_experience_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="work_place"/>
|
||||||
|
<field name="post"/>
|
||||||
|
<field name="contact_user"/>
|
||||||
|
<field name="phone_number"/>
|
||||||
|
<field name="reason_leaving"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="教育经历">
|
||||||
|
<field name="education_experience_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="learning_style"/>
|
||||||
|
<field name="name_school"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="part_time_degree"/>
|
||||||
|
<field name="full_time_degree"/>
|
||||||
|
<field name="professional"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page name="技能证书管理">
|
||||||
|
<field name="skills_certificates_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="rank_name"/>
|
||||||
|
<field name="rank_type"/>
|
||||||
|
<field name="rank_time"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="紧急联系人">
|
||||||
|
<field name="emergency_contact_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="user_relationship"/>
|
||||||
|
<field name="user_sfz"/>
|
||||||
|
<field name="workplace"/>
|
||||||
|
<field name="post"/>
|
||||||
|
<field name="phone_number"/>
|
||||||
|
<field name="home_address"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_employee_inherit_del_list" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.inherit.del.tree</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_employee_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//list" position="replace">
|
||||||
|
<list string="员工列表" limit="15"
|
||||||
|
decoration-info="end_comparison == 'blue'"
|
||||||
|
decoration-danger="end_comparison == 'red'"
|
||||||
|
decoration-warning="end_comparison == 'yellow'"
|
||||||
|
default_order="number asc">
|
||||||
|
<header>
|
||||||
|
</header>
|
||||||
|
<field name="end_comparison" column_invisible="1"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="department_id"/>
|
||||||
|
<field name="work_phone"/>
|
||||||
|
<field name="mobile_phone"/>
|
||||||
|
<field name="work_email"/>
|
||||||
|
<field name="political_aspects"/>
|
||||||
|
<field name="dang_data"/>
|
||||||
|
<field name="date_end"/>
|
||||||
|
<!-- <field name="version"/>-->
|
||||||
|
<field name="identification_id"/>
|
||||||
|
<field name="user_sex"/>
|
||||||
|
<field name="nation"/>
|
||||||
|
<field name="birthday_month"/>
|
||||||
|
<field name="old"/>
|
||||||
|
<field name="entry_date"/>
|
||||||
|
<field name="confirmation_date"/>
|
||||||
|
<field name="interrupt_work_month"/>
|
||||||
|
<field name="cumulative_work_month"/>
|
||||||
|
<field name="cumulative_work_years"/>
|
||||||
|
<field name="graduate_school"/>
|
||||||
|
<field name="profession"/>
|
||||||
|
<field name="high_degree"/>
|
||||||
|
<field name="high_degree2"/>
|
||||||
|
<field name="graduate_date"/>
|
||||||
|
<field name="user_type"/>
|
||||||
|
<field name="number"/>
|
||||||
|
<field name="company_id"/>
|
||||||
|
<field name="work_location_id" column_invisible="1"/>
|
||||||
|
</list>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--通讯录 -->
|
||||||
|
<record id="view_employee_contacts_tree" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.inherit.del.form</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list string="通讯录" create="false" delete="false" editable="bottom">
|
||||||
|
<field name="name" readonly="1"/>
|
||||||
|
<field name="retire_date" readonly="1"/>
|
||||||
|
<field name="work_phone" readonly="1"/>
|
||||||
|
<field name="mobile_phone" readonly="1"/>
|
||||||
|
<field name="department_id" readonly="1"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_employee_contacts_search" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.contacts.search</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="通讯录">
|
||||||
|
<field name="name" string="姓名" />
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="member_view_search" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.search</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="工会会员">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="department_id"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--会员列表-->
|
||||||
|
<record id="view_employee_member_tree" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.member</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list string="会员列表" create="false" delete="false" editable="bottom" default_order="number asc">
|
||||||
|
<field name="name" readonly="1"/>
|
||||||
|
<field name="work_phone" readonly="1"/>
|
||||||
|
<field name="department_id" readonly="1" options='{"no_open": True, "no_create": True}'/>
|
||||||
|
<field name="user_type" readonly="1"/>
|
||||||
|
<field name="is_local_location" readonly="1" string="是否外地"/>
|
||||||
|
<field name="identification_id" readonly="1"/>
|
||||||
|
<field name="entry_date" readonly="1"/>
|
||||||
|
<field name="departure_date" readonly="1"/>
|
||||||
|
<field name="mobile_phone" readonly="1"/>
|
||||||
|
<field name="marital" readonly="1"/>
|
||||||
|
<field name="is_company" column_invisible="1"/>
|
||||||
|
<field name="company_id" column_invisible="1"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--手机转交:树形列表(桌面端弹窗使用) -->
|
||||||
|
<record id="view_employee_phone_tree" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.member</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="priority" eval="20"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list string="人员" create="false" delete="false" editable="bottom">
|
||||||
|
<field name="name" readonly="1"/>
|
||||||
|
<field name="department_id" readonly="1" options='{"no_open": True, "no_create": True}'/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_employee_inherit_filter" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.inherit.search</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_employee_filter"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='job_id']" position="after">
|
||||||
|
<field name="pinyin_name"/>
|
||||||
|
<field name="pinyin_min"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//filter[@name='inactive']" position="after">
|
||||||
|
<separator/>
|
||||||
|
<filter string="在职" name="filter_user_state_in" domain="[('user_state', '=', 'in')]"/>
|
||||||
|
<filter string="离职" name="filter_user_state_leave" domain="[('user_state', '=', 'leave')]"/>
|
||||||
|
<filter string="退休" name="filter_user_state_withdraw" domain="[('user_state', '=', 'withdraw')]"/>
|
||||||
|
<filter string="借调" name="filter_user_state_borrow" domain="[('user_state', '=', 'borrow')]"/>
|
||||||
|
<filter string="外派" name="filter_user_state_expatriate" domain="[('user_state', '=', 'expatriate')]"/>
|
||||||
|
<filter string="在职(兼职)" name="filter_user_state_adjunct" domain="[('user_state', '=', 'adjunct')]"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//filter[@name='group_manager']" position="after">
|
||||||
|
<filter name="group_is_membership" string="工会会员" context="{'group_by': 'is_membership'}"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="即将到期" name="end"
|
||||||
|
domain="[('end_comparison', 'in', ['red', 'yellow', 'blue'])]"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="all_data_write_user" model="ir.actions.server">
|
||||||
|
<field name="name">批量更新员工</field>
|
||||||
|
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="binding_model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">action = model.action_hr_employee()</field>
|
||||||
|
<field name="groups_id" eval="[(4, ref('base.group_system'))]"/>
|
||||||
|
</record>
|
||||||
|
<record id="hr.open_view_employee_list_my" model="ir.actions.act_window">
|
||||||
|
<field name="view_mode">list,kanban,form,activity,graph,pivot</field>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'list', 'view_id': ref('hr.view_employee_tree')}),
|
||||||
|
(0, 0, {'view_mode': 'form', 'view_id': ref('hr.view_employee_form')})]"/>
|
||||||
|
</record>
|
||||||
|
<record id="all_employee_contacts_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">通讯录</field>
|
||||||
|
<field name="res_model">hr.employee</field>
|
||||||
|
<field name="domain">[('is_company', '=', True)]</field>
|
||||||
|
<field name="view_mode">list,kanban,form,search</field>
|
||||||
|
<field name="search_view_id" ref="view_employee_contacts_search"/>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_hr_employee.view_employee_contacts_tree')}),
|
||||||
|
(0, 0, {'view_mode': 'form', 'view_id': ref('hr.view_employee_form')})]"/>
|
||||||
|
</record>
|
||||||
|
<record id="action_server_my_employee" model="ir.actions.server">
|
||||||
|
<field name="name">个人基础信息</field>
|
||||||
|
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="binding_model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
action = model.get_my_employee_action()
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_password_reset_board" model="ir.actions.server">
|
||||||
|
<field name="name">重置密码</field>
|
||||||
|
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="binding_model_id" ref="hr.model_hr_employee"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">
|
||||||
|
action = model.change_password_wizard()
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="all_employee_member_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">会员列表</field>
|
||||||
|
<field name="res_model">hr.employee</field>
|
||||||
|
<field name="domain">[('is_membership', '=', True), ('is_company', '=', True)]</field>
|
||||||
|
<field name="view_mode">list,kanban,form,search</field>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_hr_employee.view_employee_member_tree')}),
|
||||||
|
(0, 0, {'view_mode': 'form', 'view_id': ref('hr.view_employee_form')})]"/>
|
||||||
|
<field name="search_view_id" ref="member_view_search"/>
|
||||||
|
</record>
|
||||||
|
<record id="hr.open_view_employee_list_my" model="ir.actions.act_window">
|
||||||
|
<field name="view_mode">list,kanban,form,activity,graph,pivot</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- <record id="all_data_write_time_form" model="ir.actions.server">-->
|
||||||
|
<!-- <field name="name">批量更新时间表</field>-->
|
||||||
|
<!-- <field name="model_id" ref="hr.model_hr_employee"/>-->
|
||||||
|
<!-- <field name="binding_model_id" ref="hr.model_hr_employee"/>-->
|
||||||
|
<!-- <field name="state">code</field>-->
|
||||||
|
<!-- <field name="code">action = model.action_hr_time_form()</field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
<!-- <record id="view_employee_form_inherit_hr_attendance" model="ir.ui.view">-->
|
||||||
|
<!-- <field name="name">hr.employee.attendance.form</field>-->
|
||||||
|
<!-- <field name="model">hr.employee</field>-->
|
||||||
|
<!-- <field name="inherit_id" ref="hr_attendance.view_employee_form_inherit_hr_attendance"/>-->
|
||||||
|
<!-- <field name="arch" type="xml">-->
|
||||||
|
<!-- <!– 源码的信息暂时隐藏 –>-->
|
||||||
|
<!-- <xpath expr="//group[@name='managers']" position="attributes">-->
|
||||||
|
<!-- <attribute name="invisible">1</attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
<menuitem id="menu_personal_information" name="个人信息" parent="hr.menu_hr_root" action="action_server_my_employee" sequence="45"/>
|
||||||
|
</odoo>
|
||||||
131
yuthon_hr_employee/views/yuthon_inherit_hr_employee.xml
Normal file
131
yuthon_hr_employee/views/yuthon_inherit_hr_employee.xml
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="yuthon_inherit_hr_employee" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.inherit.hr.employee</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- 隐藏个人隐私信息里紧急,教育,家庭状态,工作许可证 -->
|
||||||
|
<xpath expr="//page[@name='personal_information']" position="replace">
|
||||||
|
<page name="Personal Information" string="个人基础信息" groups="hr.group_hr_user">
|
||||||
|
<group>
|
||||||
|
<group string="基本信息">
|
||||||
|
<field name="home_address"/>
|
||||||
|
<field name="user_origin"/>
|
||||||
|
<field name="user_location"/>
|
||||||
|
<field name="user_location_type"/>
|
||||||
|
<!-- 原生邮箱和电话 -->
|
||||||
|
<field name="private_email" placeholder="e.g. myprivateemail@example.com" string="邮箱"/>
|
||||||
|
<field name="private_phone" string="电话"/>
|
||||||
|
<field name="pinyin_name" invisible="1"/>
|
||||||
|
<field name="pinyin_min" invisible="1"/>
|
||||||
|
<field name="dang_data"/>
|
||||||
|
<field name="is_location"/>
|
||||||
|
<field name="is_soldiers"/>
|
||||||
|
<field name="job_lv_salary"/>
|
||||||
|
<field name="bank_account_id" context="{'default_partner_id': work_contact_id}"
|
||||||
|
options="{'no_quick_create': True}" readonly="not id"/>
|
||||||
|
</group>
|
||||||
|
<group string="个人信息">
|
||||||
|
<field name="identification_id"/>
|
||||||
|
<field name="card_documents_id" widget="preview_many2one" invisible="not is_identification"/>
|
||||||
|
<field name="card_back_documents_id" widget="preview_many2one" invisible="not is_identification"/>
|
||||||
|
<field name="user_sex"/>
|
||||||
|
<field name="user_number_start_time"/>
|
||||||
|
<field name="user_number_end_time"/>
|
||||||
|
<field name="nation"/>
|
||||||
|
<field name="birthday"/>
|
||||||
|
<field name="old"/>
|
||||||
|
<field name="birthday_month"/>
|
||||||
|
<field name="political_aspects"/>
|
||||||
|
</group>
|
||||||
|
<!-- 隐藏个人隐私信息里紧急,教育,家庭状态,工作许可证 -->
|
||||||
|
<!-- <group string="Emergency" name="emergency">-->
|
||||||
|
<!-- <field name="emergency_contact"/>-->
|
||||||
|
<!-- <field name="emergency_phone" class="o_force_ltr"/>-->
|
||||||
|
<!-- <separator string="Family Status"/>-->
|
||||||
|
<!-- <field name="marital"/>-->
|
||||||
|
<!-- <field name="spouse_complete_name" invisible="marital not in ['married', 'cohabitant']"/>-->
|
||||||
|
<!-- <field name="spouse_birthdate" invisible="marital not in ['married', 'cohabitant']"/>-->
|
||||||
|
<!-- <field name="children"/>-->
|
||||||
|
<!-- </group>-->
|
||||||
|
<!-- <group string="Education">-->
|
||||||
|
<!-- <field name="certificate"/>-->
|
||||||
|
<!-- <field name="study_field"/>-->
|
||||||
|
<!-- <field name="study_school"/>-->
|
||||||
|
<!-- <separator name="has_work_permit" string="Work Permit"/>-->
|
||||||
|
<!-- <field name="visa_no"/>-->
|
||||||
|
<!-- <field name="permit_no"/>-->
|
||||||
|
<!-- <field name="visa_expire"/>-->
|
||||||
|
<!-- <field name="work_permit_expiration_date"/>-->
|
||||||
|
<!-- <field name="work_permit_name" invisible="1"/>-->
|
||||||
|
<!-- <field name="has_work_permit" widget="work_permit_upload" filename="work_permit_name"/>-->
|
||||||
|
<!-- </group>-->
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='parent_id']" position="before">
|
||||||
|
<field name="user_number"/>
|
||||||
|
<field name="number"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='public']" position="before">
|
||||||
|
<page string="工作经历">
|
||||||
|
<field name="work_experience_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="work_place"/>
|
||||||
|
<field name="post"/>
|
||||||
|
<field name="contact_user"/>
|
||||||
|
<field name="phone_number"/>
|
||||||
|
<field name="reason_leaving"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="教育经历">
|
||||||
|
<field name="education_experience_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="learning_style"/>
|
||||||
|
<field name="name_school"/>
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="in_school_duties"/>
|
||||||
|
<field name="professional"/>
|
||||||
|
<field name="part_time_degree"/>
|
||||||
|
<field name="full_time_degree"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="技能证书">
|
||||||
|
<field name="skills_certificates_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="rank_name"/>
|
||||||
|
<field name="rank_type"/>
|
||||||
|
<field name="rank_time"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="紧急联系人">
|
||||||
|
<field name="emergency_contact_ids">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="employee_id" column_invisible="1"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="user_relationship"/>
|
||||||
|
<field name="user_sfz"/>
|
||||||
|
<field name="workplace"/>
|
||||||
|
<field name="post"/>
|
||||||
|
<field name="phone_number"/>
|
||||||
|
<field name="home_address"/>
|
||||||
|
<field name="documents_ids" widget="preview_many2many"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
3
yuthon_hr_employee/wizard/__init__.py
Normal file
3
yuthon_hr_employee/wizard/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from . import hr_employee_wizard
|
||||||
|
from . import hr_employee_time_wizard
|
||||||
|
from . import reset_password_wizard
|
||||||
34
yuthon_hr_employee/wizard/hr_employee_time_wizard.py
Normal file
34
yuthon_hr_employee/wizard/hr_employee_time_wizard.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _, SUPERUSER_ID
|
||||||
|
from odoo.exceptions import ValidationError, UserError
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonHrEmployeeTimeWizard(models.Model):
|
||||||
|
_name = 'yuthon.hr.employee.time.wizard'
|
||||||
|
|
||||||
|
department_id = fields.Many2one('hr.department', string='部门')
|
||||||
|
employee_ids = fields.Many2many('hr.employee', compute='_compute_all_employees', string='所有员工' ,store=True, )
|
||||||
|
attendance_id = fields.Many2one('resource.calendar', string='考勤时间表')
|
||||||
|
|
||||||
|
@api.depends('department_id')
|
||||||
|
def _compute_all_employees(self):
|
||||||
|
for record in self:
|
||||||
|
if record.department_id:
|
||||||
|
record.employee_ids = self.get_all_employees(record.department_id)
|
||||||
|
else:
|
||||||
|
record.employee_ids = False
|
||||||
|
|
||||||
|
def get_all_employees(self, department):
|
||||||
|
employees = self.env['hr.employee'].search([('department_id', '=', department.id)])
|
||||||
|
child_departments = self.env['hr.department'].search([('parent_id', '=', department.id)])
|
||||||
|
for child_department in child_departments:
|
||||||
|
employees += self.get_all_employees(child_department)
|
||||||
|
|
||||||
|
return employees
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
for employee_id in self.employee_ids:
|
||||||
|
if self.attendance_id:
|
||||||
|
employee_id.write({'resource_calendar_id': self.attendance_id.id})
|
||||||
|
else:
|
||||||
|
employee_id.write({'resource_calendar_id': False})
|
||||||
22
yuthon_hr_employee/wizard/hr_employee_time_wizard_views.xml
Normal file
22
yuthon_hr_employee/wizard/hr_employee_time_wizard_views.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_hr_employee_time_wizard_form" model='ir.ui.view'>
|
||||||
|
<field name="name">考勤时间表</field>
|
||||||
|
<field name="model">yuthon.hr.employee.time.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="考勤时间表">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="department_id" widget="ztree_select" options='{"no_create": True}'/>
|
||||||
|
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||||
|
<field name="attendance_id"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button name="confirm" string="确认" type="object" class="oe_highlight"/>
|
||||||
|
<button string="返回" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
369
yuthon_hr_employee/wizard/hr_employee_wizard.py
Normal file
369
yuthon_hr_employee/wizard/hr_employee_wizard.py
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _, SUPERUSER_ID
|
||||||
|
from odoo.exceptions import ValidationError, UserError
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import xlrd
|
||||||
|
import base64
|
||||||
|
import datetime
|
||||||
|
from datetime import datetime
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
class YuthonHrEmployeeWizard(models.Model):
|
||||||
|
_name = 'yuthon.hr.employee.wizard'
|
||||||
|
|
||||||
|
excel_file = fields.Binary(string="上传Excel表")
|
||||||
|
excel_name = fields.Char(string="文件名")
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
"""
|
||||||
|
导入excel文件,按行读取数据并处理
|
||||||
|
:return: 导入员工信息表并更新
|
||||||
|
"""
|
||||||
|
if not self.excel_file:
|
||||||
|
raise ValidationError('请上传文件。')
|
||||||
|
book = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel_file))
|
||||||
|
sh = book.sheet_by_index(0)
|
||||||
|
for rx in range(1, sh.nrows):
|
||||||
|
row = sh.row(rx)
|
||||||
|
self.update_bank_id(row)
|
||||||
|
return {
|
||||||
|
'name': '批量更新员工',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'view_mode': 'list',
|
||||||
|
'res_model': 'hr.employee',
|
||||||
|
}
|
||||||
|
|
||||||
|
def employee_user_data(self, row):
|
||||||
|
employee_name = self.env['hr.employee'].search([('name', '=', row[0].value)])
|
||||||
|
base_date = datetime(1900, 1, 1)
|
||||||
|
if row[1].value:
|
||||||
|
entry_date = base_date + timedelta(days=row[1].value - 2) if row[1].value > 0 else base_date
|
||||||
|
else:
|
||||||
|
entry_date = None
|
||||||
|
if row[2].value:
|
||||||
|
open_start_time = base_date + timedelta(days=row[2].value - 2) if row[2].value > 0 else base_date
|
||||||
|
else:
|
||||||
|
open_start_time = None
|
||||||
|
# if row[6].value:
|
||||||
|
# dang_data = base_date + timedelta(days=row[6].value - 2) if row[6].value > 0 else base_date
|
||||||
|
# else:
|
||||||
|
# dang_data = None
|
||||||
|
# political_aspects_dict = {'中共党员': 'cpc_dang', '民盟': 'nld', '共青团员': 'league_member', '群众': 'people', '': ''}
|
||||||
|
# political_aspects = political_aspects_dict[row[5].value]
|
||||||
|
# card = row[1].value if row[1].value else None
|
||||||
|
# last_attendance = row[7].value
|
||||||
|
# if row[2].value:
|
||||||
|
# date1 = pd.to_datetime(row[2].value, unit='d', origin='1899-12-30')
|
||||||
|
# else:
|
||||||
|
# date1 = None
|
||||||
|
# interrupt_work_month = row[3].value
|
||||||
|
# number = row[0].value
|
||||||
|
employee_date = {
|
||||||
|
'entry_date': entry_date,
|
||||||
|
'open_start_time': open_start_time,
|
||||||
|
# 'dang_data':dang_data,
|
||||||
|
# 'identification_id':card,
|
||||||
|
# 'political_aspects': political_aspects,
|
||||||
|
# 'last_attendance': last_attendance,
|
||||||
|
}
|
||||||
|
if employee_name:
|
||||||
|
employee_name.write(employee_date)
|
||||||
|
|
||||||
|
|
||||||
|
# text = row[1].value
|
||||||
|
# text = text.replace('\\', '')
|
||||||
|
# if department_ids:
|
||||||
|
# department_id = department_ids[0]
|
||||||
|
# else:
|
||||||
|
# raise UserError(f'{text}不存在,请检查。')
|
||||||
|
# = self.env['hr.job'].search([('name', '=', row[2].value)])
|
||||||
|
# use_user_type_dict = {'正式职工': 'formal', '劳务协议': 'labor', '兼职协议': 'part_time', '兼职用工': 'employment', '': ''}
|
||||||
|
# use_user_type = use_user_type_dict[row[1].value]
|
||||||
|
# job_lv_id = self.env['yuthon.job.lv'].search([('name', '=', row[9].value)])
|
||||||
|
# marital_dict = {'单身': 'single', '已婚': 'married', '合法同居者': 'cohabitant', '丧偶': 'widower', '离异': 'divorced', '': ''}
|
||||||
|
# marital = marital_dict[row[19].value]
|
||||||
|
# role = row[4].value.split(',')
|
||||||
|
# role_list =[]
|
||||||
|
# user_role_id = self.env['soong.user.role']
|
||||||
|
# for i in role:
|
||||||
|
# role_id = user_role_id.search([('name', '=', i)])
|
||||||
|
# if not role_id:
|
||||||
|
# role_id = user_role_id.create({'name': i})
|
||||||
|
# role_list.append(role_id.id)
|
||||||
|
# use_user_type_dict = {'正式合同': 'formal_one', '劳务协议': 'labor', '正式合同(二签)': 'formal_two', '正式合同(无固定)': 'formal_none',
|
||||||
|
# '兼职协议': 'part_time', '用工协议': 'employment', '实习协议': 'internship'}
|
||||||
|
#
|
||||||
|
# user_role_id = self.env['soong.user.role'].search([('name', '=', row[3].value)], limit=1)
|
||||||
|
# job_id = self.env['hr.job'].search([('name', '=', row[4].value)])
|
||||||
|
# parent_id = self.env['hr.employee'].search([('name', '=', row[6].value)])
|
||||||
|
# open_start_time = row[17].value
|
||||||
|
# if open_start_time:
|
||||||
|
# if type(open_start_time) == float:
|
||||||
|
# entry_date = datetime.fromtimestamp(open_start_time)
|
||||||
|
# else:
|
||||||
|
# entry_date = datetime.strptime(open_start_time, '%Y-%m-%d')
|
||||||
|
# phone = row[21].value if row[21].value else None
|
||||||
|
# employee_date = {
|
||||||
|
# 'identification_id':card,
|
||||||
|
# 'open_start_time':entry_date,
|
||||||
|
# 'mobile_phone':phone,
|
||||||
|
# }
|
||||||
|
|
||||||
|
# employee_data = {
|
||||||
|
# 'use_user_type': use_user_type if use_user_type else None, # 用工形式
|
||||||
|
# 'marital': marital if marital else None, # 婚姻
|
||||||
|
# 'user_type': row[23].value if row[23].value else None, # 人员类别
|
||||||
|
# 'high_degree': row[30].value if row[30].value else None, # 最高学历
|
||||||
|
# 'seniority_prize_pick_up_status': row[43].value if row[43].value else None, # 工龄相关年功奖领取情况
|
||||||
|
# 'high_degree2': row[31].value if row[31].value else None, # 最高学位
|
||||||
|
# 'new_start_contract_data': datetime.strptime(row[28].value, '%Y/%m/%d').date() if row[28].value else None, # 最新合同开始日期
|
||||||
|
# 'political_aspects': political_aspects if political_aspects else None, # 政治面貌
|
||||||
|
# 'birthday': datetime.strptime(row[20].value, '%Y/%m/%d').date() if row[20].value else None, # 出生年月
|
||||||
|
# 'dang_data': datetime.strptime(row[27].value, '%Y/%m/%d').date() if row[27].value else None, # 入党(团)时间
|
||||||
|
# 'retire_date': datetime.strptime(row[24].value, '%Y/%m/%d').date() if row[24].value else None, # 退休日期
|
||||||
|
# 'new_end_contract_data': datetime.strptime(row[29].value, '%Y/%m/%d').date() if row[29].value else None, # 最新合同终止日期
|
||||||
|
# 'job_lv_id': job_lv_id.id, # 最新职级 有几个没有
|
||||||
|
# 'user_sex': 'boy' if row[18].value == '男' else 'girl', # 性别
|
||||||
|
# 'nation': row[17].value if row[17].value else None, # 民族
|
||||||
|
# 'identification_id': row[14].value if row[14].value else None, # 身份证号
|
||||||
|
# 'entry_date': datetime.strptime(row[11].value, '%Y/%m/%d').date() if row[11].value else None, # 入职时间
|
||||||
|
# 'confirmation_date': datetime.strptime(row[12].value, '%Y/%m/%d').date() if row[12].value else None, # 转正时间
|
||||||
|
# 'user_number_start_time': datetime.strptime(row[15].value, '%Y/%m/%d').date() if row[15].value else None, # 身份证有效期开始时间
|
||||||
|
# 'user_number_end_time': datetime.strptime(row[16].value, '%Y/%m/%d').date() if row[16].value else None, # 身份证有效期结束时间
|
||||||
|
# 'mobile_phone': int(row[8].value) if row[8].value else None, # 手机号码
|
||||||
|
# 'work_phone': int(row[7].value) if row[7].value else None, # 办公电话
|
||||||
|
# 'user_number': row[3].value if row[3].value else None, # 员工编号
|
||||||
|
# }
|
||||||
|
# if employee_name:
|
||||||
|
# employee_name.write(employee_date)
|
||||||
|
#
|
||||||
|
# # 处理
|
||||||
|
# for rx in range(1, sh2.nrows):
|
||||||
|
# row2 = sh2.row(rx)
|
||||||
|
# if row2[0].value == row[4].value:
|
||||||
|
# existing_education = employee_name.education_experience_ids.filtered(lambda e: e.name_school == row2[1].value)
|
||||||
|
# education_data = {
|
||||||
|
# 'name_school': row2[1].value if row2[1].value else None, # 学校名称
|
||||||
|
# 'end_date': datetime.strptime(row2[2].value, '%Y/%m/%d').date() if row2[2].value else None, # 毕业时间
|
||||||
|
# 'professional': row2[3].value if row2[3].value else None, # 专业
|
||||||
|
# 'Part_time_degree': row2[4].value if row2[4].value else None, # 非全日制学历学位
|
||||||
|
# 'full_time_degree': row2[5].value if row2[5].value else None, # 全日制学历学位
|
||||||
|
# }
|
||||||
|
# if existing_education:
|
||||||
|
# existing_education.write(education_data)
|
||||||
|
# else:
|
||||||
|
# employee_name.write({
|
||||||
|
# 'education_experience_ids': [(0, 0, education_data)]
|
||||||
|
# })
|
||||||
|
#
|
||||||
|
# for rx in range(1, sh3.nrows):
|
||||||
|
# row3 = sh3.row(rx)
|
||||||
|
# if row3[0].value == row[4].value:
|
||||||
|
# existing_certificate = employee_name.skills_certificates_ids.filtered(lambda s: s.rank_name == row3[1].value)
|
||||||
|
# certificate_data = {
|
||||||
|
# 'rank_name': row3[1].value if row3[1].value else None, # 职级名称
|
||||||
|
# 'rank_time': datetime.strptime(row3[2].value, '%Y/%m/%d').date() if row3[2].value else None, # 职级时间
|
||||||
|
# }
|
||||||
|
# if existing_certificate:
|
||||||
|
# existing_certificate.write(certificate_data)
|
||||||
|
# else:
|
||||||
|
# employee_name.write({
|
||||||
|
# 'skills_certificates_ids': [(0, 0, certificate_data)]
|
||||||
|
# })
|
||||||
|
def employee_date(self, value):
|
||||||
|
date = pd.to_datetime(value, unit='d', origin='1899-12-30').date()
|
||||||
|
return date
|
||||||
|
|
||||||
|
|
||||||
|
def update_bank_account(self,row):
|
||||||
|
contacts_list = []
|
||||||
|
employee_id = self.env['hr.employee'].search([('name', '=', row[1].value)])
|
||||||
|
company_id = self.env['res.company'].search([('name', '=', row[0].value)])
|
||||||
|
if row[4].value:
|
||||||
|
work_phone = int(row[4].value)
|
||||||
|
else:
|
||||||
|
work_phone = None
|
||||||
|
if row[5].value:
|
||||||
|
mobile_phone = int(row[5].value)
|
||||||
|
else:
|
||||||
|
mobile_phone = None
|
||||||
|
if row[6].value:
|
||||||
|
entry_date = self.employee_date(row[6].value)
|
||||||
|
else:
|
||||||
|
entry_date = None
|
||||||
|
if row[7].value:
|
||||||
|
confirmation_date = self.employee_date(row[7].value)
|
||||||
|
else:
|
||||||
|
confirmation_date = None
|
||||||
|
identification_id = row[9].value
|
||||||
|
if row[10].value:
|
||||||
|
user_number_start_time = self.employee_date(row[10].value)
|
||||||
|
else:
|
||||||
|
user_number_start_time = None
|
||||||
|
if row[11].value and row[11].value != "长期":
|
||||||
|
user_number_end_time = self.employee_date(row[11].value)
|
||||||
|
else:
|
||||||
|
user_number_end_time = None
|
||||||
|
user_type = row[17].value
|
||||||
|
if row[18].value:
|
||||||
|
retire_date = self.employee_date(row[18].value)
|
||||||
|
else:
|
||||||
|
retire_date = None
|
||||||
|
|
||||||
|
political_aspects_dict = {'中共党员': 'cpc_dang', '民盟': 'nld', '共青团员': 'league_member', '群众': 'people',
|
||||||
|
'': ''}
|
||||||
|
political_aspects = political_aspects_dict[row[20].value]
|
||||||
|
if row[21].value and row[21].value != "/":
|
||||||
|
dang_data = self.employee_date(row[21].value)
|
||||||
|
else:
|
||||||
|
dang_data = None
|
||||||
|
user_origin = row[22].value
|
||||||
|
user_location = row[23].value
|
||||||
|
if row[26].value:
|
||||||
|
open_start_time = self.employee_date(row[26].value)
|
||||||
|
else:
|
||||||
|
open_start_time = None
|
||||||
|
interrupt_work_month = row[27].value
|
||||||
|
cumulative_work_month = row[28].value
|
||||||
|
part_time_degree = row[29].value
|
||||||
|
full_time_degree = row[30].value
|
||||||
|
name_school = row[31].value
|
||||||
|
professional = row[32].value
|
||||||
|
job_lv = row[33].value
|
||||||
|
|
||||||
|
private_street = row[35].value
|
||||||
|
contract_id = self.env['hr.contract'].search([('employee_id', '=', employee_id.id)])
|
||||||
|
education_experience_list = []
|
||||||
|
education_experience_list.append([0, 0, {'part_time_degree': part_time_degree,
|
||||||
|
'full_time_degree': full_time_degree,
|
||||||
|
'name_school': name_school,
|
||||||
|
'professional': professional,
|
||||||
|
}])
|
||||||
|
if row[24].value:
|
||||||
|
date_start = self.employee_date(row[24].value)
|
||||||
|
else:
|
||||||
|
date_start = None
|
||||||
|
date_end = row[25].value
|
||||||
|
if date_end == '无固定期限':
|
||||||
|
date_end = False
|
||||||
|
else:
|
||||||
|
date_end = self.employee_date(date_end)
|
||||||
|
contract_type_id = self.env['hr.contract.type'].search([('name', '=', row[34].value)])
|
||||||
|
|
||||||
|
if contract_id:
|
||||||
|
contract_id.write({
|
||||||
|
'date_start': date_start,
|
||||||
|
'date_end': date_end,
|
||||||
|
'contract_type_id': contract_type_id.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
name = row[36].value
|
||||||
|
relationship_dict = {
|
||||||
|
'父亲': 'father',
|
||||||
|
'姐妹': 'sisters',
|
||||||
|
'妻子': 'spouse',
|
||||||
|
'女儿': 'daughter',
|
||||||
|
'丈夫': 'husband',
|
||||||
|
'姐姐': 'sister',
|
||||||
|
'兄弟': 'brother',
|
||||||
|
'妹妹': 'sister_do',
|
||||||
|
'父母': 'parents',
|
||||||
|
'子女': 'children',
|
||||||
|
'姐弟': 'siblings',
|
||||||
|
'母亲': 'mother',
|
||||||
|
'母女': 'daughter',
|
||||||
|
}
|
||||||
|
if row[37].value:
|
||||||
|
user_relationship = relationship_dict[row[37].value]
|
||||||
|
else:
|
||||||
|
user_relationship = False
|
||||||
|
phone_number = row[38].value
|
||||||
|
archiving_agency = row[39].value
|
||||||
|
contacts_list.append([0, 0, {'name': name,
|
||||||
|
'user_relationship': user_relationship,
|
||||||
|
'phone_number': phone_number,
|
||||||
|
}])
|
||||||
|
write_uid = self.env['res.users'].search([('id', '=', employee_id.user_id.id)])
|
||||||
|
user_type_dict = {
|
||||||
|
'正式合同(一签)': 'formal_one',
|
||||||
|
'劳务协议': 'labor',
|
||||||
|
'正式合同(二签)': 'formal_two',
|
||||||
|
'正式合同(无固定)': 'formal_none',
|
||||||
|
'兼职协议': 'part_time',
|
||||||
|
'用工协议': 'employment',
|
||||||
|
'实习协议': 'internship',
|
||||||
|
}
|
||||||
|
use_user_type = user_type_dict[row[34].value]
|
||||||
|
|
||||||
|
employee_date= {
|
||||||
|
'use_user_type': use_user_type,
|
||||||
|
'company_id': company_id.id,
|
||||||
|
'work_phone': work_phone,
|
||||||
|
'mobile_phone': mobile_phone,
|
||||||
|
'entry_date': entry_date,
|
||||||
|
'dang_data':dang_data,
|
||||||
|
'confirmation_date': confirmation_date,
|
||||||
|
'user_number_start_time': user_number_start_time,
|
||||||
|
'user_number_end_time': user_number_end_time,
|
||||||
|
'retire_date': retire_date,
|
||||||
|
'open_start_time': open_start_time,
|
||||||
|
'identification_id': identification_id,
|
||||||
|
'user_type': user_type,
|
||||||
|
'political_aspects': political_aspects,
|
||||||
|
'user_origin': user_origin,
|
||||||
|
'user_location': user_location,
|
||||||
|
'interrupt_work_month': interrupt_work_month,
|
||||||
|
'private_street': private_street,
|
||||||
|
'job_lv': job_lv,
|
||||||
|
'write_uid': write_uid,
|
||||||
|
'archiving_agency': archiving_agency,
|
||||||
|
'emergency_contact_ids': contacts_list,
|
||||||
|
'education_experience_ids': education_experience_list
|
||||||
|
}
|
||||||
|
#
|
||||||
|
if employee_id:
|
||||||
|
employee_id.write({'education_experience_ids': [(5, 0, 0)]})
|
||||||
|
employee_id.write({'emergency_contact_ids': [(5, 0, 0)]})
|
||||||
|
employee_id.write(employee_date)
|
||||||
|
|
||||||
|
|
||||||
|
def update_lc_employee(self, row):
|
||||||
|
employee_id = self.env['hr.employee'].search([('name', '=', row[0].value)])
|
||||||
|
structure_type_id = self.env['hr.payroll.structure.type'].search([('name', '=', row[2].value), ('company_id.name', '=', '天河粮储')])
|
||||||
|
if row[3].value:
|
||||||
|
entry_date = self.employee_date(row[3].value)
|
||||||
|
else:
|
||||||
|
entry_date = None
|
||||||
|
if row[4].value:
|
||||||
|
open_start_time = self.employee_date(row[4].value)
|
||||||
|
else:
|
||||||
|
open_start_time = None
|
||||||
|
interrupt_work_month = row[6].value
|
||||||
|
if employee_id:
|
||||||
|
employee_date = {
|
||||||
|
'entry_date': entry_date,
|
||||||
|
'open_start_time': open_start_time,
|
||||||
|
'interrupt_work_month': interrupt_work_month,
|
||||||
|
}
|
||||||
|
employee_id.write(employee_date)
|
||||||
|
employee_id.contract_id.structure_type_id = structure_type_id.id,
|
||||||
|
|
||||||
|
def update_bank_id(self, row):
|
||||||
|
employee_id = self.env['hr.employee'].search([('name', '=', row[0].value)])
|
||||||
|
bank_account_id = self.env['res.partner.bank'].search([('acc_number', '=', row[1].value)], limit=1)
|
||||||
|
res_id = self.env['res.partner'].search([('name', '=', row[0].value)])
|
||||||
|
if not bank_account_id:
|
||||||
|
bank_account_id = self.env['res.partner.bank'].create({
|
||||||
|
'acc_number': row[1].value,
|
||||||
|
'partner_id': res_id.id,
|
||||||
|
})
|
||||||
|
if employee_id:
|
||||||
|
employee_date = {
|
||||||
|
'bank_account_id': bank_account_id.id,
|
||||||
|
}
|
||||||
|
employee_id.write(employee_date)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
34
yuthon_hr_employee/wizard/hr_employee_wizard_views.xml
Normal file
34
yuthon_hr_employee/wizard/hr_employee_wizard_views.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="user_template_downloads_tian_he_action" model="ir.actions.act_url">
|
||||||
|
<field name="name">员工模板 下载</field>
|
||||||
|
<field name="target">self</field>
|
||||||
|
<field name="url">/yuthon_hr_employee/static/others/user_template.xlsx</field>
|
||||||
|
</record>
|
||||||
|
<record id="hr_employee_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">审批意见弹窗</field>
|
||||||
|
<field name="model">yuthon.hr.employee.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<header>
|
||||||
|
<button name="%(user_template_downloads_tian_he_action)d" string="模板下载" type="action"
|
||||||
|
class="btn btn-primary"/>
|
||||||
|
</header>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="excel_file" filename="excel_name"/>
|
||||||
|
<field name="excel_name" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button name="confirm" string="确认" type="object" class="oe_highlight"/>
|
||||||
|
<button string="返回" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
15
yuthon_hr_employee/wizard/reset_password_wizard.py
Normal file
15
yuthon_hr_employee/wizard/reset_password_wizard.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _, SUPERUSER_ID
|
||||||
|
from odoo.exceptions import ValidationError, UserError
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordWizard(models.TransientModel):
|
||||||
|
_name = 'reset.password.wizard'
|
||||||
|
|
||||||
|
employee_id = fields.Many2one('hr.employee', string="关联员工")
|
||||||
|
password = fields.Char(string='新密码', required=True)
|
||||||
|
|
||||||
|
def reset_password(self):
|
||||||
|
user = self.employee_id.user_id
|
||||||
|
user.sudo().password = self.password
|
||||||
|
|
||||||
21
yuthon_hr_employee/wizard/reset_password_wizard_viwes.xml
Normal file
21
yuthon_hr_employee/wizard/reset_password_wizard_viwes.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="reset_password_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">reset.password.wizard.form</field>
|
||||||
|
<field name="model">reset.password.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="employee_id" invisible="1"/>
|
||||||
|
<field name="password"/>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button name="reset_password" type="object" string="确定修改" class="oe_highlight"/>
|
||||||
|
<button special="cancel" string="取消" class="oe_link"/>
|
||||||
|
</footer>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@ -1,3 +1,2 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import models
|
from . import models
|
||||||
from . import controllers
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
'name': "Yuthon notice",
|
'name': "yuthon notice",
|
||||||
'summary': "通知公告",
|
'summary': "通知公告",
|
||||||
'description': """
|
'description': """
|
||||||
|
|
||||||
@ -10,15 +10,15 @@
|
|||||||
'category': 'Human Resources/Attendances',
|
'category': 'Human Resources/Attendances',
|
||||||
'version': '0.1',
|
'version': '0.1',
|
||||||
'license': 'LGPL-3',
|
'license': 'LGPL-3',
|
||||||
'depends': ['base', 'hr', 'mail'],
|
'depends': ['base', 'hr'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'data/notice_code_data.xml',
|
'data/notice_code_data.xml',
|
||||||
'data/notice_cron_data.xml',
|
|
||||||
'data/notice_type_data.xml',
|
'data/notice_type_data.xml',
|
||||||
|
'report/notice_print_report.xml',
|
||||||
|
'report/notice_print_template.xml',
|
||||||
'views/yuthon_notice_views.xml',
|
'views/yuthon_notice_views.xml',
|
||||||
'views/yuthon_notice_type_views.xml',
|
'views/yuthon_notice_type_views.xml',
|
||||||
'views/yuthon_cs_tree_views.xml',
|
|
||||||
'views/yuthon_confirm_users_views.xml',
|
'views/yuthon_confirm_users_views.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
@ -31,3 +31,4 @@
|
|||||||
'installable': True,
|
'installable': True,
|
||||||
'application': True,
|
'application': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,4 +3,5 @@
|
|||||||
from . import yuthon_notice
|
from . import yuthon_notice
|
||||||
from . import yuthon_notice_type
|
from . import yuthon_notice_type
|
||||||
from . import yuthon_confirm_users
|
from . import yuthon_confirm_users
|
||||||
|
from . import hr_department
|
||||||
|
|
||||||
|
|||||||
19
yuthon_notice/models/hr_department.py
Normal file
19
yuthon_notice/models/hr_department.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, models
|
||||||
|
|
||||||
|
|
||||||
|
class HrDepartment(models.Model):
|
||||||
|
_inherit = 'hr.department'
|
||||||
|
|
||||||
|
@api.depends('name', 'parent_id.name')
|
||||||
|
@api.depends_context('show_parent_department')
|
||||||
|
def _compute_display_name(self):
|
||||||
|
"""当上下文中 show_parent_department=True 时,显示 '直接上级 / 当前部门',只取一级"""
|
||||||
|
if self.env.context.get('show_parent_department'):
|
||||||
|
for record in self:
|
||||||
|
if record.parent_id:
|
||||||
|
record.display_name = '%s / %s' % (record.parent_id.name, record.name)
|
||||||
|
else:
|
||||||
|
record.display_name = record.name
|
||||||
|
return
|
||||||
|
return super()._compute_display_name()
|
||||||
@ -6,33 +6,85 @@ from odoo.exceptions import UserError, ValidationError
|
|||||||
class YuthonConfirmUsers(models.Model):
|
class YuthonConfirmUsers(models.Model):
|
||||||
_name = "yuthon.confirm.users"
|
_name = "yuthon.confirm.users"
|
||||||
_description = "公告确认信息"
|
_description = "公告确认信息"
|
||||||
|
_order = "employee_number asc"
|
||||||
|
|
||||||
employee_id = fields.Many2one('hr.employee', string='员工')
|
employee_id = fields.Many2one('hr.employee', string='员工')
|
||||||
notice_id = fields.Many2one('yuthon.notice', string="公告名称")
|
notice_id = fields.Many2one('yuthon.notice', string="公告名称")
|
||||||
notice_code = fields.Char(string="公告编号")
|
notice_code = fields.Char(string="公告编号")
|
||||||
notice_name = fields.Char(string="公告名称")
|
notice_name = fields.Char(string="公告名称")
|
||||||
state = fields.Selection([('no', '未确认'), ('yes', '已确认')], string="状态")
|
state = fields.Selection([('to_sent', '待发送'), ('no', '待确认'), ('yes', '已确认')], string="状态")
|
||||||
|
create_time = fields.Datetime(string='创建时间', default=fields.Datetime.now)
|
||||||
|
release_date = fields.Date(string="发布日期")
|
||||||
|
msg_id = fields.Char(string="Msg_id")
|
||||||
|
|
||||||
|
# 从员工带出的字段
|
||||||
|
company_id = fields.Many2one(related='employee_id.company_id', string='公司', store=True)
|
||||||
|
department_id = fields.Many2one(related='employee_id.department_id', string='部门', store=True)
|
||||||
|
employee_number = fields.Char(related='employee_id.number', string='序号', store=True)
|
||||||
|
|
||||||
|
def withdrawn(self):
|
||||||
|
if self.state == 'to_sent':
|
||||||
|
raise UserError('该通知还未发送,无法撤回')
|
||||||
|
if self.msg_id:
|
||||||
|
self.sudo().env["wecom.apps"].back_message(self.msg_id, category='notice')
|
||||||
|
self.state = 'to_sent'
|
||||||
|
self.msg_id = '已撤回'
|
||||||
|
|
||||||
|
def all_withdrawn(self):
|
||||||
|
confirm_noice_ids = self.env["yuthon.confirm.users"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||||
|
for record in confirm_noice_ids:
|
||||||
|
if record.state == 'state':
|
||||||
|
raise UserError(f'{record.notice_id.name}通知还未发送,无法撤回')
|
||||||
|
record.withdrawn()
|
||||||
|
|
||||||
def sync_confirm_users_notices(self):
|
def sync_confirm_users_notices(self):
|
||||||
menu_id = self.env['ir.ui.menu'].search([('name', '=', '通知公告')])
|
|
||||||
for record in self.search([('state', '=', 'no')]):
|
for record in self.search([('state', '=', 'no')]):
|
||||||
partner_ids = record.employee_id.user_id.partner_id.id
|
partner_ids = record.employee_id.user_id.partner_id.id
|
||||||
record.with_context(lang=record.env.lang)._message_auto_subscribe_notify(partner_ids, 'yuthon_notice.message_yuthon_confirm_users_data')
|
record.with_context(lang=record.env.lang)._message_auto_subscribe_notify(partner_ids, 'yuthon_notice.message_yuthon_confirm_users_data')
|
||||||
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=yuthon.notice&view_type=list&menu_id={menu_id.id}'
|
url = f'https://phone.thtzjt.com/phone/Notice/{self.notice_id.id}?employee_id={record.employee_id.id}'
|
||||||
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', user_id=record.employee_id.wecom_userid, title="通知公告",
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', touser=record.employee_id.wecom_userid, title="通知公告",
|
||||||
description="您有待确认的通知公告请尽快确认", url=url, btntxt="详细信息")
|
description=f"您有一条【{record.notice_id.name}】的通知公告请尽快确认", url=url, btntxt="详细信息")
|
||||||
|
|
||||||
|
|
||||||
def reminders_notice(self):
|
def reminders_notice(self):
|
||||||
for rem in self:
|
|
||||||
if rem.state == 'yes':
|
|
||||||
raise UserError('已确认不可催办')
|
|
||||||
confirm_noice_ids = self.env["yuthon.confirm.users"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
confirm_noice_ids = self.env["yuthon.confirm.users"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||||
|
i = 0
|
||||||
for rec in confirm_noice_ids:
|
for rec in confirm_noice_ids:
|
||||||
menu_id = self.env['ir.ui.menu'].search([('name', '=', '通知公告')])
|
url = f'https://phone.thtzjt.com/phone/Notice/{self.notice_id.id}?employee_id={rec.employee_id.id}'
|
||||||
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=yuthon.notice&view_type=list&menu_id={menu_id.id}'
|
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', touser=rec.employee_id.wecom_userid,
|
||||||
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', user_id=rec.employee_id.wecom_userid,
|
title="通知公告(催办)", description=f"您有一条【{rec.notice_id.name}】的通知公告请尽快确认",
|
||||||
title="通知公告(催办)", description="您有待确认的通知公告请尽快确认",
|
|
||||||
url=url, btntxt="详细信息")
|
url=url, btntxt="详细信息")
|
||||||
partner_ids = rec.employee_id.user_id.partner_id.id
|
if result_data:
|
||||||
rec.with_context(lang=rec.env.lang)._message_auto_subscribe_notify(partner_ids,
|
res_dict = result_data[1]
|
||||||
'yuthon_notice.message_yuthon_confirm_users_data')
|
rec.write({'msg_id': res_dict.get('msgid')})
|
||||||
|
if res_dict.get('errmsg') == 'ok':
|
||||||
|
i = i + 1
|
||||||
|
url = f'https://phone.thtzjt.com/phone/Notice/{self.notice_id.id}?employee_id=298'
|
||||||
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice',
|
||||||
|
touser='18562027762',
|
||||||
|
title="通知公告",
|
||||||
|
description="成功发送消息通知" + str(i),
|
||||||
|
url=url, btntxt="详细信息")
|
||||||
|
|
||||||
|
def reminders_notice_send(self):
|
||||||
|
confirm_noice_ids = self.env["yuthon.confirm.users"].browse(
|
||||||
|
self._context.get('active_ids', self._context.get('active_id')))
|
||||||
|
i = 0
|
||||||
|
for rec in confirm_noice_ids:
|
||||||
|
url = f'https://phone.thtzjt.com/phone/Notice/{self.notice_id.id}?employee_id={rec.employee_id.id}'
|
||||||
|
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice',
|
||||||
|
touser=rec.employee_id.wecom_userid,
|
||||||
|
title="通知公告",
|
||||||
|
description=f"您有一条【{rec.notice_id.name}】待确认的通知公告请尽快确认",
|
||||||
|
url=url, btntxt="详细信息")
|
||||||
|
if result_data:
|
||||||
|
res_dict = result_data[1]
|
||||||
|
rec.write({'msg_id': res_dict.get('msgid')})
|
||||||
|
if res_dict.get('errmsg') == 'ok':
|
||||||
|
i = i + 1
|
||||||
|
url = f'https://phone.thtzjt.com/phone/Notice/{self.notice_id.id}?employee_id=298'
|
||||||
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice',
|
||||||
|
touser='18562027762',
|
||||||
|
title="通知公告",
|
||||||
|
description="成功发送消息通知" + str(i),
|
||||||
|
url=url, btntxt="详细信息")
|
||||||
@ -2,6 +2,7 @@
|
|||||||
from odoo import api, fields, models, tools, _
|
from odoo import api, fields, models, tools, _
|
||||||
from odoo.exceptions import ValidationError, UserError
|
from odoo.exceptions import ValidationError, UserError
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import datetime, timedelta, date
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -22,17 +23,15 @@ class YuthonNotice(models.Model):
|
|||||||
create_date1 = fields.Date(string="发布日期", default=fields.Date.today())
|
create_date1 = fields.Date(string="发布日期", default=fields.Date.today())
|
||||||
start_date = fields.Date(string="生效日期", default=fields.Date.today())
|
start_date = fields.Date(string="生效日期", default=fields.Date.today())
|
||||||
end_date = fields.Date(string="终止日期")
|
end_date = fields.Date(string="终止日期")
|
||||||
sequence = fields.Integer(string="顺序",compute='_compute_sequence',store=True)
|
sequence = fields.Integer(string="顺序", default=1, compute='_compute_sequence')
|
||||||
employee_ids = fields.Many2many('hr.employee', string="人员范围")
|
employee_ids = fields.Many2many('hr.employee', string="人员范围")
|
||||||
department_ids = fields.Many2many('hr.department', string='部门范围')
|
department_ids = fields.Many2many('hr.department', string='部门范围')
|
||||||
users_role_ids = fields.Many2many('res.groups', string='角色范围')
|
|
||||||
users_role_group_ids = fields.Many2many('res.groups', string='角色组', relation='yuthon_notice_role_group_rel')
|
|
||||||
notice_state = fields.Selection([('no', '未发布'), ('yes', '已发布'), ('stop', '已终止')], default='no', string="公告状态")
|
notice_state = fields.Selection([('no', '未发布'), ('yes', '已发布'), ('stop', '已终止')], default='no', string="公告状态")
|
||||||
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
|
||||||
is_notice_roger = fields.Selection([('yes', '已读'), ('no', '未读')], default='no', string="确认状态")
|
is_notice_roger = fields.Selection([('yes', '已读'), ('no', '未读')], default='no', string="确认状态")
|
||||||
is_change_notice = fields.Boolean(string="是否发送通知", default=True)
|
is_change_notice = fields.Boolean(string="是否发送通知", default=True)
|
||||||
attn_notice = fields.Html(string="经办人意见")
|
attn_notice = fields.Html(string="经办人意见")
|
||||||
notice_text = fields.Html(string="正文")
|
notice_text = fields.Html(string="正文")
|
||||||
|
notice_text1 = fields.Html(string="正文")
|
||||||
read_number = fields.Integer(string='阅读量', compute="_compute_read_number", store=True)
|
read_number = fields.Integer(string='阅读量', compute="_compute_read_number", store=True)
|
||||||
is_this_company = fields.Boolean(string='本公司', default=True)
|
is_this_company = fields.Boolean(string='本公司', default=True)
|
||||||
approval_user_id = fields.Many2one('res.users', string="审核人")
|
approval_user_id = fields.Many2one('res.users', string="审核人")
|
||||||
@ -41,6 +40,7 @@ class YuthonNotice(models.Model):
|
|||||||
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度', default='normal')
|
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度', default='normal')
|
||||||
active = fields.Boolean('Active', default=True, tracking=True)
|
active = fields.Boolean('Active', default=True, tracking=True)
|
||||||
|
|
||||||
|
|
||||||
work_end = fields.Boolean(string="结束")
|
work_end = fields.Boolean(string="结束")
|
||||||
users_ids = fields.Many2many(
|
users_ids = fields.Many2many(
|
||||||
'res.users',
|
'res.users',
|
||||||
@ -55,9 +55,36 @@ class YuthonNotice(models.Model):
|
|||||||
column1='notice_id',
|
column1='notice_id',
|
||||||
column2='user_id',
|
column2='user_id',
|
||||||
string="当前经办人",
|
string="当前经办人",
|
||||||
store=True,
|
default=lambda self: [(6, 0, [self.env.user.id])]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# @api.onchange('users_ids')
|
||||||
|
# def _onchange_users_ids(self):
|
||||||
|
# today = date.today()
|
||||||
|
# self.start_date = today
|
||||||
|
# self.create_date1 = today
|
||||||
|
|
||||||
|
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',
|
||||||
|
search='_search_part_of_company')
|
||||||
|
|
||||||
|
publish_date = fields.Date(string='发布日期')
|
||||||
|
|
||||||
|
@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
|
||||||
|
if not value:
|
||||||
|
operator = '!=' if operator == '=' else '='
|
||||||
|
if operator == '=':
|
||||||
|
return [('company_id', 'in', company_ids)]
|
||||||
|
else:
|
||||||
|
return [('company_id', 'not in', company_ids)]
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
for i in vals_list:
|
for i in vals_list:
|
||||||
@ -72,42 +99,104 @@ class YuthonNotice(models.Model):
|
|||||||
|
|
||||||
@api.constrains('employee_ids', 'department_ids', 'users_role_ids', 'users_role_group_ids')
|
@api.constrains('employee_ids', 'department_ids', 'users_role_ids', 'users_role_group_ids')
|
||||||
def _constrains_employee_related_fields(self):
|
def _constrains_employee_related_fields(self):
|
||||||
|
"""
|
||||||
|
约束验证:当关联字段变化时,更新users_ids
|
||||||
|
"""
|
||||||
def get_department_employees(department_id):
|
def get_department_employees(department_id):
|
||||||
|
"""递归获取部门及其子部门的员工对应的user_id"""
|
||||||
valid_user_ids = set()
|
valid_user_ids = set()
|
||||||
|
# 查找当前部门的员工(使用id替代id.origin)
|
||||||
employee_ids = self.env['hr.employee'].search([('department_id', '=', department_id.id)])
|
employee_ids = self.env['hr.employee'].search([('department_id', '=', department_id.id)])
|
||||||
|
# 收集有效的user_id
|
||||||
valid_user_ids.update([emp.user_id.id for emp in employee_ids if emp.user_id])
|
valid_user_ids.update([emp.user_id.id for emp in employee_ids if emp.user_id])
|
||||||
|
# 递归处理子部门
|
||||||
child_ids = self.env['hr.department'].search([('parent_id', '=', department_id.id)])
|
child_ids = self.env['hr.department'].search([('parent_id', '=', department_id.id)])
|
||||||
for child in child_ids:
|
for child in child_ids:
|
||||||
valid_user_ids.update(get_department_employees(child))
|
valid_user_ids.update(get_department_employees(child))
|
||||||
return valid_user_ids
|
return valid_user_ids
|
||||||
|
|
||||||
for notice in self:
|
for notice in self:
|
||||||
|
# 避免重复触发约束(更新users_ids时跳过本次约束检查)
|
||||||
if notice.env.context.get('skip_constrains'):
|
if notice.env.context.get('skip_constrains'):
|
||||||
continue
|
continue
|
||||||
|
# 获取部门关联的用户ID
|
||||||
department_user_ids = set()
|
department_user_ids = set()
|
||||||
for department_id in notice.department_ids:
|
for department_id in notice.department_ids:
|
||||||
department_user_ids.update(get_department_employees(department_id))
|
department_user_ids.update(get_department_employees(department_id))
|
||||||
group_role_ids = notice.users_role_group_ids
|
group_role_ids = notice.users_role_group_ids.user_role_ids
|
||||||
all_users = set(
|
all_users = set(
|
||||||
notice.employee_ids.mapped('user_id').ids +
|
notice.employee_ids.mapped('user_id').ids +
|
||||||
group_role_ids.mapped('users').ids +
|
group_role_ids.mapped('personnel_ids.user_id').ids +
|
||||||
notice.users_role_ids.mapped('users').ids +
|
notice.users_role_ids.mapped('personnel_ids.user_id').ids +
|
||||||
list(department_user_ids)
|
list(department_user_ids)
|
||||||
)
|
)
|
||||||
notice.with_context(skip_constrains=True).users_ids = [(6, 0, list(all_users))]
|
notice.with_context(skip_constrains=True).users_ids = [(6, 0, list(all_users))]
|
||||||
|
|
||||||
|
if notice.users_ids and notice.is_change_notice:
|
||||||
|
conf_old = self.env['yuthon.confirm.users'].sudo().search([('notice_id', '=', notice.id)])
|
||||||
|
old_employee_ids = conf_old.mapped('employee_id.id')
|
||||||
|
new_employee_ids = self.env['hr.employee'].sudo().search([
|
||||||
|
('user_id', 'in', notice.users_ids.ids)
|
||||||
|
]).ids
|
||||||
|
add_employee_ids = list(set(new_employee_ids) - set(old_employee_ids))
|
||||||
|
del_employee_ids = list(set(old_employee_ids) - set(new_employee_ids))
|
||||||
|
|
||||||
|
# 3. 处理删除:
|
||||||
|
if del_employee_ids:
|
||||||
|
del_conf = conf_old.filtered(lambda c: c.employee_id.id in del_employee_ids)
|
||||||
|
del_conf.unlink()
|
||||||
|
|
||||||
|
if add_employee_ids:
|
||||||
|
for emp_id in add_employee_ids:
|
||||||
|
employee = self.env['hr.employee'].sudo().browse(emp_id)
|
||||||
|
if not employee:
|
||||||
|
continue
|
||||||
|
# 防重复:如果已经存在该员工的确认记录,跳过
|
||||||
|
exist = self.env['yuthon.confirm.users'].sudo().search([
|
||||||
|
('notice_id', '=', notice.id),
|
||||||
|
('employee_id', '=', emp_id),
|
||||||
|
], limit=1)
|
||||||
|
if exist:
|
||||||
|
continue
|
||||||
|
self.env['yuthon.confirm.users'].sudo().create({
|
||||||
|
'notice_id': notice.id,
|
||||||
|
'employee_id': emp_id,
|
||||||
|
'notice_code': notice.code,
|
||||||
|
'notice_name': notice.name,
|
||||||
|
'release_date': notice.create_date1,
|
||||||
|
'state': 'to_sent',
|
||||||
|
})
|
||||||
|
|
||||||
def get_no_read_count(self):
|
def get_no_read_count(self):
|
||||||
return self.search_count([('is_notice_roger', '=', 'no'), ('notice_state', '=', 'yes')])
|
# 只统计当前用户发布范围内的未读公告,与列表 domain 保持一致
|
||||||
|
return self.search_count([
|
||||||
|
('users_ids', 'in', self.env.uid),
|
||||||
|
('is_notice_roger', '=', 'no'),
|
||||||
|
('notice_state', '=', 'yes'),
|
||||||
|
])
|
||||||
|
|
||||||
def read(self, fields=None, load='_classic_read'):
|
def read(self, fields=None, load='_classic_read'):
|
||||||
|
# 标记是否真的有未读变已读,避免没变化也触发菜单刷新
|
||||||
|
changed = False
|
||||||
for con in self:
|
for con in self:
|
||||||
con_id = self.env['yuthon.confirm.users'].search([('notice_id', '=', con.id), ('employee_id', '=', con.env.user.employee_id.id)])
|
# 只有全局状态仍是未读时才处理
|
||||||
if con_id.employee_id == con.env.user.employee_id:
|
if con.is_notice_roger == 'no':
|
||||||
con.is_notice_roger = 'yes'
|
# 查找当前用户对应的公告确认记录
|
||||||
con_id.write({'state': 'yes'})
|
con_id = self.env['yuthon.confirm.users'].search([
|
||||||
return super(YuthonNotice, self).read(fields=fields, load=load)
|
('notice_id', '=', con.id),
|
||||||
|
('employee_id', '=', con.env.user.employee_id.id)
|
||||||
|
])
|
||||||
|
# 确认是当前员工本人,再标记为已读
|
||||||
|
if con_id.employee_id == con.env.user.employee_id:
|
||||||
|
con.is_notice_roger = 'yes'
|
||||||
|
con_id.write({'state': 'yes'})
|
||||||
|
changed = True
|
||||||
|
res = super(YuthonNotice, self).read(fields=fields, load=load)
|
||||||
|
# 如果有记录从 未读 变为 已读,清除菜单缓存并通知前端刷新角标
|
||||||
|
if changed:
|
||||||
|
self.env.registry.clear_cache()
|
||||||
|
self.env['sun.badge.menu.mixin'].sudo().trigger_menu_reload()
|
||||||
|
return res
|
||||||
|
|
||||||
@api.depends('read_number')
|
@api.depends('read_number')
|
||||||
def _compute_read_number(self):
|
def _compute_read_number(self):
|
||||||
@ -134,38 +223,79 @@ class YuthonNotice(models.Model):
|
|||||||
self.sequence = 0
|
self.sequence = 0
|
||||||
|
|
||||||
def notice_publish(self):
|
def notice_publish(self):
|
||||||
if self.is_change_notice:
|
"""发布通知公告:给 yuthon.confirm.users 中尚未发送的记录发企微消息。
|
||||||
conf_old = self.env['yuthon.confirm.users'].search([('notice_id', '=', self.id), ('employee_id', 'in', self.users_ids.ids)])
|
|
||||||
for user_id in self.users_ids:
|
|
||||||
url = f'http://oa.thtzjt.com/webh5#/notice?id={self.id}'
|
|
||||||
if conf_old:
|
|
||||||
for co in conf_old:
|
|
||||||
co.write({'state': 'no'})
|
|
||||||
else:
|
|
||||||
self.env['yuthon.confirm.users'].create({
|
|
||||||
'notice_id': self.id,
|
|
||||||
'employee_id': user_id.employee_ids.id,
|
|
||||||
'notice_code': self.code,
|
|
||||||
'notice_name': self.name,
|
|
||||||
'state': 'no',
|
|
||||||
})
|
|
||||||
we_id = self.env['hr.employee'].search([('id', '=', user_id.employee_ids.id)]).wecom_userid
|
|
||||||
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', user_id=we_id, title="通知公告",
|
|
||||||
description=self.name + '发布人:' + self.users_id.name,
|
|
||||||
url=url, btntxt="详细信息")
|
|
||||||
self.write({"notice_state": 'yes'})
|
|
||||||
return {
|
|
||||||
'type': 'ir.actions.client',
|
|
||||||
'tag': 'display_notification',
|
|
||||||
'params': {
|
|
||||||
'type': 'success',
|
|
||||||
'message': '通知公告已经发布',
|
|
||||||
'sticky': False,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise UserError('该公告不允许通知,请勾选通知')
|
|
||||||
|
|
||||||
|
- 每人有且仅发一条:msg_id 为空才发,已发过的不重发
|
||||||
|
- 发送成功才写回 msg_id(企微返回的msgid)并更新状态为"待确认"
|
||||||
|
- 单条失败不影响其他记录,失败记录保持"待发送"状态
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
|
||||||
|
if self.notice_state == 'yes':
|
||||||
|
return
|
||||||
|
self.notice_state = 'yes'
|
||||||
|
|
||||||
|
if not self.is_change_notice:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 重新发布时,已发送过消息的记录重置为"待确认",等待用户重新确认(不重发消息)
|
||||||
|
self.env['yuthon.confirm.users'].sudo().search([
|
||||||
|
('notice_id', '=', self.id),
|
||||||
|
('msg_id', '!=', False),
|
||||||
|
]).write({'state': 'no'})
|
||||||
|
|
||||||
|
# 只发送尚未收到消息的记录(msg_id 为空),保证每人有且仅一条
|
||||||
|
confirm_records = self.env['yuthon.confirm.users'].sudo().search([
|
||||||
|
('notice_id', '=', self.id),
|
||||||
|
('msg_id', '=', False),
|
||||||
|
])
|
||||||
|
|
||||||
|
sent_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
for rec in confirm_records:
|
||||||
|
employee = rec.employee_id
|
||||||
|
if not employee or not employee.wecom_userid:
|
||||||
|
_logger.warning("公告[%s]确认记录%s跳过:员工或企微userid为空", self.name, rec.id)
|
||||||
|
continue
|
||||||
|
url = f'https://phone.thtzjt.com/phone/Notice/{self.id}?employee_id={employee.id}'
|
||||||
|
try:
|
||||||
|
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(
|
||||||
|
category='notice',
|
||||||
|
touser=employee.wecom_userid,
|
||||||
|
title="通知公告",
|
||||||
|
description=f"{self.name} 发布人: {self.users_id.name or ''}",
|
||||||
|
url=url,
|
||||||
|
btntxt="详细信息"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
_logger.exception("公告[%s]给员工%s发送企微消息异常", self.name, employee.name)
|
||||||
|
fail_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 发送成功:返回元组第一项为 True 且携带 msgid,才写回 msg_id 并更新状态
|
||||||
|
if result_data and result_data[0] and isinstance(result_data[1], dict) and result_data[1].get('msgid'):
|
||||||
|
rec.sudo().write({
|
||||||
|
'msg_id': result_data[1].get('msgid'),
|
||||||
|
'state': 'no',
|
||||||
|
})
|
||||||
|
sent_count += 1
|
||||||
|
else:
|
||||||
|
_logger.warning("公告[%s]给员工%s发送失败,返回:%s", self.name, employee.name, result_data)
|
||||||
|
fail_count += 1
|
||||||
|
|
||||||
|
_logger.info("公告[%s]发布完成,成功发送%s条,失败%s条", self.name, sent_count, fail_count)
|
||||||
|
|
||||||
|
# 发布完毕弹窗提醒
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'params': {
|
||||||
|
'title': '发布完毕',
|
||||||
|
'message': f'已成功发送 {sent_count} 条消息' + (f',{fail_count} 条失败' if fail_count else ''),
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def look_up_notice_users(self):
|
def look_up_notice_users(self):
|
||||||
return {
|
return {
|
||||||
@ -179,28 +309,90 @@ class YuthonNotice(models.Model):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
"""重写write方法,检测work_end字段变化,触发AI主动感知"""
|
||||||
|
result = super().write(vals)
|
||||||
|
|
||||||
|
# 检测work_end从False变为True
|
||||||
|
if 'work_end' in vals and vals['work_end']:
|
||||||
|
for record in self:
|
||||||
|
if record.work_end: # 确保当前值确实是True
|
||||||
|
self._trigger_ai_guide(record)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _trigger_ai_guide(self, record):
|
||||||
|
"""触发AI主动感知:查找匹配的规则,推送消息到聊天框"""
|
||||||
|
# 查找匹配的规则
|
||||||
|
rules = self.env['ai.rule'].sudo().search([
|
||||||
|
('rule_type', '=', 'active_guide'),
|
||||||
|
('guide_model', '=', self._name),
|
||||||
|
('guide_field', '=', 'work_end'),
|
||||||
|
('guide_field_value', '=', 'True'),
|
||||||
|
('active', '=', True),
|
||||||
|
])
|
||||||
|
|
||||||
|
for rule in rules:
|
||||||
|
# 生成提醒内容(替换变量)
|
||||||
|
message = rule.guide_message or f'{record.name} 流程已结束'
|
||||||
|
message = message.replace('{record_name}', record.name or '')
|
||||||
|
message = message.replace('{user_name}', self.env.user.name or '')
|
||||||
|
|
||||||
|
# 自动组装按钮动作(后台自动注入 record_id)
|
||||||
|
action_config = {}
|
||||||
|
if rule.guide_method_name:
|
||||||
|
action_config = {
|
||||||
|
'type': 'execute_tool',
|
||||||
|
'tool': rule.guide_method_name,
|
||||||
|
'params': {
|
||||||
|
'record_id': record.id,
|
||||||
|
'model': self._name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 查找或创建AI对话(用当前用户的默认对话)
|
||||||
|
conversation = self.env['ai.conversation'].sudo().search([
|
||||||
|
('create_uid', '=', self.env.user.id),
|
||||||
|
], limit=1, order='write_date desc')
|
||||||
|
|
||||||
|
if not conversation:
|
||||||
|
conversation = self.env['ai.conversation'].sudo().create({
|
||||||
|
'name': 'AI 对话',
|
||||||
|
})
|
||||||
|
|
||||||
|
# 创建AI消息(带action)
|
||||||
|
self.env['ai.message'].sudo().create({
|
||||||
|
'conversation_id': conversation.id,
|
||||||
|
'role': 'assistant',
|
||||||
|
'content': message,
|
||||||
|
'action': json.dumps({
|
||||||
|
'type': 'guide_button',
|
||||||
|
'label': rule.guide_button_label or '确认执行',
|
||||||
|
'action': action_config,
|
||||||
|
}, ensure_ascii=False) if action_config else None,
|
||||||
|
})
|
||||||
|
|
||||||
|
# TODO: 通过bus.bus推送到前端,让聊天框自动显示新消息
|
||||||
|
# self.env['bus.bus'].sudo()._sendone(self.env.user.partner_id, 'ai_guide_notification', {
|
||||||
|
# 'conversation_id': conversation.id,
|
||||||
|
# 'message': message,
|
||||||
|
# })
|
||||||
|
|
||||||
def stop_notice(self):
|
def stop_notice(self):
|
||||||
self.write({
|
self.write({
|
||||||
'notice_state': 'stop',
|
'notice_state': 'stop',
|
||||||
'end_date': fields.Date.today(),
|
'end_date': fields.Date.today(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def action_print_notice(self):
|
||||||
|
"""打印通知公告"""
|
||||||
|
self.ensure_one()
|
||||||
|
report = self.env.ref('yuthon_notice.action_notice_print_report')
|
||||||
|
return report.report_action(self)
|
||||||
|
|
||||||
|
|
||||||
class YuthonCsTree(models.Model):
|
class YuthonCsTree(models.Model):
|
||||||
_name = 'yuthon.cs.tree'
|
_name = 'yuthon.cs.tree'
|
||||||
_description = 'Yuthon Customer Service Tree'
|
_description = 'Yuthon Customer Service Tree'
|
||||||
|
|
||||||
name = fields.Char(string='Name', required=True)
|
name = fields.Char(string='Name', required=True)
|
||||||
|
|
||||||
def _compute_sequence(self):
|
|
||||||
for record in self:
|
|
||||||
record.sequence = 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 工作流的指定申请人逻辑?
|
|
||||||
# 转交的时候选择条件,进行优化
|
|
||||||
|
|
||||||
# 报表合同和产品业务
|
|
||||||
# 企业微信等三方平台框架--和工作流
|
|
||||||
23
yuthon_notice/report/notice_print_report.xml
Normal file
23
yuthon_notice/report/notice_print_report.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="paperformat_notice_small" model="report.paperformat">
|
||||||
|
<field name="name">Notice Small Top Margin</field>
|
||||||
|
<field name="format">A4</field>
|
||||||
|
<field name="margin_top">5</field>
|
||||||
|
<field name="orientation">Portrait</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_notice_print_report" model="ir.actions.report">
|
||||||
|
<field name="name">通知公告</field>
|
||||||
|
<field name="paperformat_id" ref="yuthon_notice.paperformat_notice_small"/>
|
||||||
|
<field name="model">yuthon.notice</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">yuthon_notice.notice_print_template</field>
|
||||||
|
<field name="report_file">yuthon_notice.notice_print_template</field>
|
||||||
|
<field name="binding_model_id" ref="model_yuthon_notice"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
<field name="print_report_name">'通知公告_%s_%s' % (object.name or '', object.code or '')</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
144
yuthon_notice/report/notice_print_template.xml
Normal file
144
yuthon_notice/report/notice_print_template.xml
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="notice_print_template">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="doc">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<style type="text/css">
|
||||||
|
/* 表格纵向版式:自适应,最大800px 居中 */
|
||||||
|
table { border-collapse: collapse; width: 100%; max-width: 800px; margin: 0 auto; table-layout: fixed; }
|
||||||
|
td { border: 1px solid #000; padding: 6px; text-align: center; vertical-align: middle; }
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
.header-cell { font-weight: bold; background-color: #d4e6f1;}
|
||||||
|
/* 附件清单左对齐 */
|
||||||
|
.attachment-list { text-align: left; }
|
||||||
|
/* 隐藏意见和审批人之间的竖边框 */
|
||||||
|
.no-right-border { border-right: none !important; }
|
||||||
|
.no-left-border { border-left: none !important; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- 流水号:页面左上角,位于标题之上,浅灰色 -->
|
||||||
|
<div style="margin-bottom:8px; text-align:left; color:#999999;">
|
||||||
|
流水号:<span t-esc="doc.code or ''"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 通知公告标题 -->
|
||||||
|
<div class="title">
|
||||||
|
通知公告
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:15%"/>
|
||||||
|
<col style="width:35%"/>
|
||||||
|
<col style="width:15%"/>
|
||||||
|
<col style="width:35%"/>
|
||||||
|
</colgroup>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>标题</strong></td>
|
||||||
|
<td colspan="3"><span t-esc="doc.name or ''"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>类型</strong></td>
|
||||||
|
<td><span t-esc="doc.notice_type_id.name or ''"/></td>
|
||||||
|
<td class="header-cell"><strong>紧急程度</strong></td>
|
||||||
|
<td>
|
||||||
|
<span t-if="doc.urgency_level == 'normal'">普件</span>
|
||||||
|
<span t-if="doc.urgency_level == 'urgent'">急件</span>
|
||||||
|
<span t-if="doc.urgency_level == 'emergency'">特急件</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>发布人</strong></td>
|
||||||
|
<td><span t-esc="doc.users_id.name or ''"/></td>
|
||||||
|
<td class="header-cell"><strong>发布部门</strong></td>
|
||||||
|
<td><span t-esc="doc.department_id.name or ''"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>发布日期</strong></td>
|
||||||
|
<td><span t-esc="doc.create_date1 and doc.create_date1.strftime('%Y-%m-%d') or ''"/></td>
|
||||||
|
<td class="header-cell"><strong>生效日期</strong></td>
|
||||||
|
<td><span t-esc="doc.start_date and doc.start_date.strftime('%Y-%m-%d') or ''"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>正文</strong></td>
|
||||||
|
<td colspan="3" class="attachment-list"><span t-field="doc.notice_text1"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>附件</strong></td>
|
||||||
|
<td colspan="3" class="attachment-list">
|
||||||
|
<t t-foreach="doc.documents_ids" t-as="att">
|
||||||
|
<div><span t-esc="att.name or ''"/></div>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- 发布范围 -->
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="background-color: #d4e6f1; text-align: center;"><strong>发布范围</strong></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>部门范围</strong></td>
|
||||||
|
<td colspan="3" class="attachment-list">
|
||||||
|
<t t-foreach="doc.department_ids" t-as="dept">
|
||||||
|
<span t-esc="dept.name"/>
|
||||||
|
<t t-if="not dept_last">、</t>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>角色组</strong></td>
|
||||||
|
<td colspan="3" class="attachment-list">
|
||||||
|
<t t-foreach="doc.users_role_group_ids" t-as="role_group">
|
||||||
|
<span t-esc="role_group.name"/>
|
||||||
|
<t t-if="not role_group_last">、</t>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>角色范围</strong></td>
|
||||||
|
<td colspan="3" class="attachment-list">
|
||||||
|
<t t-foreach="doc.users_role_ids" t-as="role">
|
||||||
|
<span t-esc="role.name"/>
|
||||||
|
<t t-if="not role_last">、</t>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="header-cell"><strong>人员范围</strong></td>
|
||||||
|
<td colspan="3" class="attachment-list">
|
||||||
|
<t t-foreach="doc.employee_ids" t-as="emp">
|
||||||
|
<span t-esc="emp.name"/>
|
||||||
|
<t t-if="not emp_last">、</t>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- 审批详情 -->
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="background-color: #d4e6f1; text-align: center;"><strong>审批详情</strong></td>
|
||||||
|
</tr>
|
||||||
|
<t t-set="logs_ordered" t-value="doc.log_ids.sorted(lambda x: x.date_create or '')"/>
|
||||||
|
<t t-foreach="logs_ordered" t-as="log">
|
||||||
|
<tr>
|
||||||
|
<td colspan="1"><span t-esc="log.name or '节点'"/></td>
|
||||||
|
<td class="no-right-border" colspan="2"><span t-esc="log.comment or ''"/></td>
|
||||||
|
<td class="no-left-border" colspan="1">
|
||||||
|
<t t-if="log.done_by">
|
||||||
|
<span t-esc="log.done_by.name"/> <span t-esc="log.lase_click_date and log.lase_click_date.strftime('%Y-%m-%d') or ''"/>
|
||||||
|
</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</table>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
@ -3,3 +3,7 @@
|
|||||||
width: 80px !important;
|
width: 80px !important;
|
||||||
max-width: 80px !important;
|
max-width: 80px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-blue {
|
||||||
|
color: #0069ff !important;
|
||||||
|
}
|
||||||
@ -1,18 +1,26 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_yuthon_confirm_users_tree" model="ir.ui.view">
|
<record id="view_yuthon_confirm_users_list" model="ir.ui.view">
|
||||||
<field name="name">yuthon.confirm.users.tree</field>
|
<field name="name">yuthon.confirm.users.list</field>
|
||||||
<field name="model">yuthon.confirm.users</field>
|
<field name="model">yuthon.confirm.users</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<list create="false" edit="false"
|
<list string="公告确认信息" js_class="custom_notice_tree_button" editable="bottom">
|
||||||
string="公告确认单" js_class="custom_notice_tree_button" editable="bottom">
|
|
||||||
<header>
|
<header>
|
||||||
<button class="oe_stat_button" name="reminders_notice" string="批量催办" type="object" icon="fa-bars"/>
|
<button name="reminders_notice" string="批量催办" type="object" class="oe_stat_button" icon="fa-bars"/>
|
||||||
|
<button name="all_withdrawn" string="批量撤回" type="object" icon="fa-bars" class="oe_stat_button" confirm="确认要撤回吗?"/>
|
||||||
|
<button name="reminders_notice_send" string="批量发送" type="object" icon="fa-bars" class="oe_stat_button"/>
|
||||||
</header>
|
</header>
|
||||||
<field name="employee_id" widget="ztree_select"/>
|
<field name="employee_id"/>
|
||||||
|
<field name="employee_number" column_invisible="1"/>
|
||||||
|
<field name="company_id" readonly="1"/>
|
||||||
|
<field name="department_id" readonly="1"/>
|
||||||
<field name="notice_id"/>
|
<field name="notice_id"/>
|
||||||
<field name="notice_code"/>
|
<field name="notice_code"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
|
<field name="release_date"/>
|
||||||
|
<field name="create_time"/>
|
||||||
|
<field name="msg_id"/>
|
||||||
|
<button name="withdrawn" string="撤回" type="object" class="oe_highlight" confirm="确认要撤回吗?"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@ -32,7 +40,7 @@
|
|||||||
<record id="yuthon_confirm_users_action" model="ir.actions.act_window">
|
<record id="yuthon_confirm_users_action" model="ir.actions.act_window">
|
||||||
<field name="name">公告确认信息</field>
|
<field name="name">公告确认信息</field>
|
||||||
<field name="res_model">yuthon.confirm.users</field>
|
<field name="res_model">yuthon.confirm.users</field>
|
||||||
<field name="view_mode">list</field>
|
<field name="view_mode">tree</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
@ -9,9 +9,9 @@
|
|||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="action_yuthon_cs_tree" model="ir.actions.act_window">
|
<record id="action_yuthon_cs_list" model="ir.actions.act_window">
|
||||||
<field name="name">Yuthon CS Tree</field>
|
<field name="name">Yuthon CS list</field>
|
||||||
<field name="res_model">yuthon.cs.tree</field>
|
<field name="res_model">yuthon.cs.list</field>
|
||||||
<field name="view_mode">list</field>
|
<field name="view_mode">list</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_yuthon_notice_type_tree" model="ir.ui.view">
|
<record id="view_yuthon_notice_type_list" model="ir.ui.view">
|
||||||
<field name="name">yuthon.notice.type.tree</field>
|
<field name="name">yuthon.notice.type.list</field>
|
||||||
<field name="model">yuthon.notice.type</field>
|
<field name="model">yuthon.notice.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<list string="公告类型" editable="bottom">
|
<list string="公告类型" editable="bottom">
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_yuthon_notice_tree" model="ir.ui.view">
|
<record id="view_yuthon_notice_list" model="ir.ui.view">
|
||||||
<field name="name">yuthon.notice.tree</field>
|
<field name="name">yuthon.notice.list</field>
|
||||||
<field name="model">yuthon.notice</field>
|
<field name="model">yuthon.notice</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<list string="公告管理" decoration-success="notice_state == 'yes'"
|
<list string="公告管理" decoration-success="notice_state == 'yes'"
|
||||||
decoration-info="notice_state == 'no'"
|
decoration-info="notice_state == 'no'"
|
||||||
decoration-muted="notice_state == 'stop'">
|
decoration-muted="notice_state == 'stop'" default_order="create_date1 desc">
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1"/>
|
||||||
<field name="notice_type_id" string="类型" class="list_ids_width"/>
|
<field name="notice_type_id" string="类型"/>
|
||||||
<field name="users_id" string="发布人" class="list_ids_width"/>
|
<field name="users_id" string="发布人"/>
|
||||||
<field name="department_id" column_invisible="1"/>
|
<field name="department_id" column_invisible="1"/>
|
||||||
<field name="users_ids" column_invisible="1"/>
|
<field name="users_ids" column_invisible="1"/>
|
||||||
<field name="create_date1"/>
|
<field name="create_date1"/>
|
||||||
<field name="end_date"/>
|
<field name="end_date"/>
|
||||||
<field name="notice_state" class="list_ids_width"/>
|
<field name="notice_state"/>
|
||||||
<button name="on_sequence" string="置顶" invisible="sequence != 0" type="object" class="btn-primary"/>
|
<button name="on_sequence" string="置顶" invisible="sequence != 0" type="object" class="btn btn-blue"/>
|
||||||
<button name="no_sequence" string="取消置顶" invisible="sequence != 1" type="object" class="btn-primary"/>
|
<button name="no_sequence" string="取消置顶" invisible="sequence != 1" type="object" class="btn btn-blue"/>
|
||||||
<!-- <button name="look_up_notice_users" string="查阅情况" type="object" class="btn-primary"/>-->
|
<button name="look_up_notice_users" string="查阅情况" type="object" class="btn btn-blue"/>
|
||||||
<button name="stop_notice" string="终止" type="object" class="btn-primary"/>
|
<button name="stop_notice" string="终止" type="object" class="btn btn-blue"/>
|
||||||
<field name="sequence" column_invisible="1"/>
|
<field name="sequence" column_invisible="1"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
@ -29,13 +29,10 @@
|
|||||||
<field name="model">yuthon.notice</field>
|
<field name="model">yuthon.notice</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="公告管理" create="0">
|
<form string="公告管理" create="0">
|
||||||
|
<header>
|
||||||
|
<button name="action_print_notice" string="打印" type="object" class="btn-primary" invisible="not work_end"/>
|
||||||
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
<widget name="web_ribbon" title="普件" bg_color="text-bg-success"
|
|
||||||
invisible="urgency_level != 'normal'"/>
|
|
||||||
<widget name="web_ribbon" title="急件" bg_color="text-bg-warning"
|
|
||||||
invisible="urgency_level != 'urgent'"/>
|
|
||||||
<widget name="web_ribbon" title="特级" bg_color="text-bg-danger"
|
|
||||||
invisible="urgency_level != 'emergency'"/>
|
|
||||||
<group col="4">
|
<group col="4">
|
||||||
<group>
|
<group>
|
||||||
<field name="code" string="流水号" readonly="1"/>
|
<field name="code" string="流水号" readonly="1"/>
|
||||||
@ -57,12 +54,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<group col="4">
|
<group col="4">
|
||||||
<group>
|
<group>
|
||||||
<field name="notice_type_id"/>
|
<!-- <field name="publish_date"/>-->
|
||||||
|
<field name="notice_type_id" required="1"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="users_id"/>
|
<field name="users_id" options="{'no_create': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
|
<field name="self_users_ids" invisible="1"/>
|
||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="work_end" invisible="1"/>
|
<field name="work_end" invisible="1"/>
|
||||||
<field name="department_id" string="发布部门" invisible="1"/>
|
<field name="department_id" string="发布部门" invisible="1"/>
|
||||||
@ -71,23 +70,18 @@
|
|||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1"/>
|
||||||
<field name="notice_text" widget="tinymce"/>
|
|
||||||
<field name="documents_ids" widget="preview_many2many"/>
|
|
||||||
</group>
|
</group>
|
||||||
<group col="4" string="发布范围">
|
<group>
|
||||||
<group>
|
<field name="notice_text" widget="tinymce" invisible="1"/>
|
||||||
<field name="department_ids" widget="many2many_tags" context="{'tree_view_ref':'yuthon_notice.view_yuthon_cs_tree'}" domain = "[('company_id', '=', company_id)] if is_this_company else []" options="{'no_create': True}"/>
|
<field name="notice_text1"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<!--<group col="3" string="发布范围">-->
|
||||||
<field name="users_role_group_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
<group string="发布范围">
|
||||||
</group>
|
<field name="department_ids" widget="many2many_tags" domain="[('company_id', '=', company_id), ('name', 'not like', '天河投资集团')] if is_this_company else [('name', 'not like', '天河投资集团')]" options="{'no_create': True}"/>
|
||||||
<group>
|
</group>
|
||||||
<field name="users_role_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
<group>
|
||||||
</group>
|
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}" domain="[('company_id', '=', company_id), ('user_id.name', '!=', 'Administrator')] if is_this_company else []"/>
|
||||||
<group>
|
<field name="users_ids" widget="many2many_tags" invisible="1"/>
|
||||||
<field name="employee_ids" widget="many2many_tags" context="{'tree_view_ref':'yuthon_notice.view_yuthon_cs_tree'}" options="{'no_create': True}" domain = "[('company_id', '=', company_id), ('user_id.name', '!=', 'Administrator')] if is_this_company else []"/>
|
|
||||||
<field name="users_ids" invisible="1" widget="many2many_tags"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
</group>
|
||||||
<group col="3">
|
<group col="3">
|
||||||
<group>
|
<group>
|
||||||
@ -105,17 +99,17 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_yuthon_notice_tree2" model="ir.ui.view">
|
<record id="view_yuthon_notice_list2" model="ir.ui.view">
|
||||||
<field name="name">yuthon.notice.tree</field>
|
<field name="name">yuthon.notice.list</field>
|
||||||
<field name="model">yuthon.notice</field>
|
<field name="model">yuthon.notice</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<list string="公告管理" edit="false" delete="false" create="false"
|
<list string="公告管理" edit="false" delete="false" create="false"
|
||||||
decoration-danger="is_notice_roger == 'no'"
|
decoration-danger="is_notice_roger == 'no'"
|
||||||
decoration-success="is_notice_roger == 'yes'"
|
decoration-success="is_notice_roger == 'yes'"
|
||||||
js_class="custom_notice_tree_button">
|
js_class="custom_notice_tree_button" default_order="create_date1 desc">
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="notice_type_id" string="类型"/>
|
<field name="notice_type_id" string="类型"/>
|
||||||
<field name="users_id" string="发布人" class="list_ids_width"/>
|
<field name="users_id" string="发布人"/>
|
||||||
<field name="is_notice_roger"/>
|
<field name="is_notice_roger"/>
|
||||||
<field name="department_id" column_invisible="1"/>
|
<field name="department_id" column_invisible="1"/>
|
||||||
<field name="users_ids" column_invisible="1"/>
|
<field name="users_ids" column_invisible="1"/>
|
||||||
@ -135,7 +129,7 @@
|
|||||||
<field name="notice_type_id" string="发布类型"/>
|
<field name="notice_type_id" string="发布类型"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="users_id"/>
|
<field name="users_id" options="{'no_create': True}"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="read_number"/>
|
<field name="read_number"/>
|
||||||
@ -153,8 +147,8 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
<field name="notice_text" widget="tinymce" string=" "/>
|
<field name="notice_text" widget="tinymce" string=" " invisible="1"/>
|
||||||
<field name="documents_ids" widget="preview_many2many"/>
|
<field name="notice_text1"/>
|
||||||
</group>
|
</group>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
@ -166,9 +160,8 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="公告查询">
|
<search string="公告查询">
|
||||||
<field string="标题" name="name"/>
|
<field string="标题" name="name"/>
|
||||||
<field string="发布人" name="users_id"/>
|
<field string="发布人" name="users_id" options="{'no_create': True}"/>
|
||||||
<field string="正文" name="notice_text"/>
|
<field string="正文" name="notice_text1"/>
|
||||||
<field string="附件" name="documents_ids"/>
|
|
||||||
<field string="部门" name="department_id"/>
|
<field string="部门" name="department_id"/>
|
||||||
<filter string="未发布" name="no " domain="[('notice_state', '=', 'no')]"/>
|
<filter string="未发布" name="no " domain="[('notice_state', '=', 'no')]"/>
|
||||||
<filter string="已发布" name="done " domain="[('notice_state', '=', 'done')]"/>
|
<filter string="已发布" name="done " domain="[('notice_state', '=', 'done')]"/>
|
||||||
@ -183,18 +176,19 @@
|
|||||||
|
|
||||||
<record id="approval_notice_action" model="ir.actions.act_window">
|
<record id="approval_notice_action" model="ir.actions.act_window">
|
||||||
<field name="name">通知公告</field>
|
<field name="name">通知公告</field>
|
||||||
|
<field name="domain">[('users_ids','in',uid),('notice_state','=','yes')]</field>
|
||||||
<field name="res_model">yuthon.notice</field>
|
<field name="res_model">yuthon.notice</field>
|
||||||
<field name="view_mode">list,form</field>
|
<field name="view_mode">list,form</field>
|
||||||
<field name="domain">[('notice_state', '=', 'yes'),('users_ids', 'in', [uid])]</field>
|
|
||||||
<field name="view_ids" eval="[(5, 0, 0),
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_notice.view_yuthon_notice_tree2')}),
|
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_notice.view_yuthon_notice_list2')}),
|
||||||
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_notice.view_yuthon_notice_form2')})]"/>
|
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_notice.view_yuthon_notice_form2')})]"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="approval_notice_action2" model="ir.actions.act_window">
|
<record id="approval_notice_action2" model="ir.actions.act_window">
|
||||||
<field name="name">通知公告管理</field>
|
<field name="name">通知公告管理</field>
|
||||||
|
<field name="domain">[('is_company', '=', True)]</field>
|
||||||
<field name="res_model">yuthon.notice</field>
|
<field name="res_model">yuthon.notice</field>
|
||||||
<field name="view_mode">list,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem id="yuthon_notice_menu_config_root"
|
<menuitem id="yuthon_notice_menu_config_root"
|
||||||
|
|||||||
@ -4,13 +4,12 @@
|
|||||||
'summary': "日程事项",
|
'summary': "日程事项",
|
||||||
'description': """""",
|
'description': """""",
|
||||||
'author': 'zhou',
|
'author': 'zhou',
|
||||||
'version': '18.0.1.0.0',
|
'version': '0.1',
|
||||||
'license': 'LGPL-3',
|
'license': 'LGPL-3',
|
||||||
'data': [
|
'data': [
|
||||||
# 'data/will_task_time_data.xml',
|
'data/task_code_data.xml',
|
||||||
# 'data/task_code_data.xml',
|
'data/mail_template.xml',
|
||||||
# 'data/mail_template.xml',
|
'data/tsak_time_data.xml',
|
||||||
# 'data/tsak_time_data.xml',
|
|
||||||
'security/yuthon_will_task_security.xml',
|
'security/yuthon_will_task_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'views/yuthon_will_task_views.xml',
|
'views/yuthon_will_task_views.xml',
|
||||||
@ -25,7 +24,7 @@
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
'demo': [],
|
'demo': [],
|
||||||
'depends': ['hr', 'mail'],
|
'depends': ['hr', 'mail', 'yuthon_hr_employee', 'room'],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': True,
|
'application': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
from . import main
|
from . import main
|
||||||
@ -1,62 +1,42 @@
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
from odoo import http, fields
|
||||||
from odoo import http, fields, Command
|
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
from odoo.osv import expression
|
from odoo.tools import float_is_zero,float_round
|
||||||
import logging
|
from odoo.exceptions import UserError
|
||||||
|
from datetime import datetime
|
||||||
_logger = logging.getLogger(__name__)
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
|
||||||
class YuthonWillTask(http.Controller):
|
class YuthonWillTask(http.Controller):
|
||||||
|
|
||||||
@http.route('/yuthon/yuthon/will/task', methods=['POST'], type='http', auth='none', csrf=False)
|
@http.route('/yuthon/yuthon/will/task', methods=['POST'], type='http', auth='none', csrf=False)
|
||||||
def index2(self, **kwargs):
|
def index2(self, **kwargs):
|
||||||
"""根据ID获取日程详情"""
|
|
||||||
id = kwargs.get('id')
|
id = kwargs.get('id')
|
||||||
|
|
||||||
data = request.env['yuthon.will.task'].sudo().search(domain=[("id", "=", int(id))])
|
data = request.env['yuthon.will.task'].sudo().search(domain = [("id", "=", int(id))])
|
||||||
request.env['yuthon.will.task'].create({
|
|
||||||
'fields': kwargs.get('id'),
|
|
||||||
})
|
|
||||||
|
|
||||||
file_list = []
|
|
||||||
pdf = ['application/pdf']
|
|
||||||
for file in data.document_ids:
|
|
||||||
base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
||||||
file_url = f"{base_url}/web/content/preview/{file.id}"
|
|
||||||
if file.mimetype in pdf:
|
|
||||||
file_url = f"{base_url}/web/content/preview/{file.id}"
|
|
||||||
file_list.append({
|
|
||||||
'file_url': file_url,
|
|
||||||
'file_name': file.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
employee_ids = []
|
employee_ids = []
|
||||||
for em in data.employee_ids:
|
for em in data.employee_ids:
|
||||||
employee_ids.append({
|
employee_ids.append({
|
||||||
'id': em.id,
|
'id':em.id,
|
||||||
'name': em.name,
|
'name':em.name,
|
||||||
'image_1920': em.image_128.decode('utf-8') if em.image_128 else '',
|
'image_1920':em.image_128.decode('utf-8') if em.image_128 else '',
|
||||||
})
|
})
|
||||||
result = {
|
result = {
|
||||||
'id': data.id,
|
'id':data.id,
|
||||||
'name': data.name,
|
'name':data.name,
|
||||||
'code': data.code,
|
'code':data.code,
|
||||||
'start_date': fields.Date.to_string(data.start_date) if data.start_date else '',
|
'start_date':fields.Date.to_string(data.start_date),
|
||||||
'request_hour_from': data.request_hour_from,
|
'request_hour_from':data.request_hour_from,
|
||||||
'request_hour_to': data.request_hour_to,
|
'request_hour_to':data.request_hour_to,
|
||||||
'priority': data.priority,
|
'priority':data.priority,
|
||||||
'user_id': data.user_id.name,
|
'user_id':data.user_id.name,
|
||||||
'user_avatar': base64.b64decode(data.user_id.avatar_128).decode('utf-8') if data.user_id.avatar_128 else '',
|
'user_avatar':base64.b64decode(data.user_id.avatar_128).decode('utf-8') if data.user_id.avatar_128 else '',
|
||||||
'remarks': data.remarks,
|
'remarks':data.remarks,
|
||||||
'address': data.address,
|
'address':data.address,
|
||||||
'employee_ids': employee_ids,
|
'employee_ids':employee_ids,
|
||||||
'file_list': file_list,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 转换 request_hour_from 和 request_hour_to 的显示值
|
# 转换 request_hour_from 和 request_hour_to 取模型 yuthon.will.task 的 request_hour_from 的selection
|
||||||
request_hour_from_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
request_hour_from_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
||||||
request_hour_to_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_to'])['request_hour_to']['selection']
|
request_hour_to_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_to'])['request_hour_to']['selection']
|
||||||
|
|
||||||
@ -68,8 +48,7 @@ class YuthonWillTask(http.Controller):
|
|||||||
result['request_hour_to'] = f[1]
|
result['request_hour_to'] = f[1]
|
||||||
|
|
||||||
# 计算 start_date 星期几
|
# 计算 start_date 星期几
|
||||||
if data.start_date:
|
result['start_date_week'] = fields.Date.from_string(result['start_date']).strftime('%A')
|
||||||
result['start_date_week'] = data.start_date.strftime('%A')
|
|
||||||
|
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
'data': result,
|
'data': result,
|
||||||
@ -77,54 +56,101 @@ class YuthonWillTask(http.Controller):
|
|||||||
'message': 'success'
|
'message': 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@http.route('/task/<int:task_id>', type='http', auth='user', website=False)
|
||||||
|
def open_task(self, task_id, **kwargs):
|
||||||
|
"""通过ID打开特定任务的详情页"""
|
||||||
|
# 查询任务数据(带权限校验)
|
||||||
|
Task = request.env['yuthon.will.task']
|
||||||
|
task = Task.sudo().browse(task_id)
|
||||||
|
if not task.exists():
|
||||||
|
return request.not_found("任务不存在或已被删除")
|
||||||
|
|
||||||
|
# 格式化时间显示(将0.5转为0:30格式)
|
||||||
|
def format_time(hour_str):
|
||||||
|
if not hour_str:
|
||||||
|
return ""
|
||||||
|
if hour_str.endswith('.5'):
|
||||||
|
return f"{int(float(hour_str))}:30"
|
||||||
|
return f"{int(float(hour_str))}:00"
|
||||||
|
|
||||||
|
# 准备模板所需数据
|
||||||
|
task_data = {
|
||||||
|
'task': task,
|
||||||
|
'format_time': format_time, # 传递格式化函数到模板
|
||||||
|
'employee_names': ', '.join(task.employee_ids.mapped('name')),
|
||||||
|
'driver_names': ', '.join(task.employee_ids2.mapped('name')) or '无',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@http.route('/yuthon/task/detail/<int:task_id>',type='http',auth='public',website=False)
|
||||||
|
def task_detail(self, task_id, **kw):
|
||||||
|
try:
|
||||||
|
task = request.env['yuthon.will.task'].sudo().browse(task_id)
|
||||||
|
if not task.exists():
|
||||||
|
raise MissingError(f"任务ID {task_id} 不存在")
|
||||||
|
|
||||||
|
participant_names = []
|
||||||
|
if task.employee_ids:
|
||||||
|
participant_names.extend(task.employee_ids.mapped('name'))
|
||||||
|
|
||||||
|
time_display = ""
|
||||||
|
if task.start_date:
|
||||||
|
time_display = str(task.start_date)
|
||||||
|
|
||||||
|
task_data = {
|
||||||
|
'name': task.name or '',
|
||||||
|
'start_date': time_display,
|
||||||
|
'address': task.address or '',
|
||||||
|
'user_id': task.user_id.name if task.user_id else '',
|
||||||
|
'employee_ids': ', '.join(participant_names) if participant_names else '无',
|
||||||
|
'code': task.code or '',
|
||||||
|
'request_hour_from': task.request_hour_from,
|
||||||
|
'request_hour_to': task.request_hour_to,
|
||||||
|
}
|
||||||
|
return request.render('yuthon_will_task.task_detail_template', task_data)
|
||||||
|
|
||||||
|
except MissingError as e:
|
||||||
|
return f"<h3>错误:{str(e)}</h3>"
|
||||||
|
except Exception as e:
|
||||||
|
return f"<h3>加载失败:{str(e)}</h3>"
|
||||||
|
|
||||||
|
|
||||||
@http.route('/will/task/list', methods=['POST'], type='http', auth='public', csrf=False)
|
@http.route('/will/task/list', methods=['POST'], type='http', auth='public', csrf=False)
|
||||||
def will_task_list(self, **kwargs):
|
def will_task_list(self, **kwargs):
|
||||||
"""获取日程列表(手机端接口)"""
|
request_hour_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
||||||
start = int(kwargs.get('start'))
|
employee_id = kwargs.get('employee_id')
|
||||||
users_id = int(kwargs.get('users_id'))
|
data = request.env['yuthon.will.task'].sudo().search(
|
||||||
_filter = kwargs.get('filter')
|
['|', ('employee_ids', 'in', [employee_id]), ('employee_id.id', '=', employee_id)])
|
||||||
state = kwargs.get('state')
|
|
||||||
order = 'task_datetime desc' if state == 'done' else 'task_datetime asc'
|
|
||||||
domain = [('users_ids', 'in', [users_id])]
|
|
||||||
|
|
||||||
today = fields.Date.today()
|
|
||||||
if _filter == 'today':
|
|
||||||
domain = expression.AND([domain, [('start_date', '=', today)]])
|
|
||||||
elif _filter == 'week':
|
|
||||||
start_date = today - timedelta(days=today.weekday())
|
|
||||||
end_date = start_date + timedelta(days=6)
|
|
||||||
domain = expression.AND([domain, [('start_date', '>=', start_date), ('start_date', '<=', end_date)]])
|
|
||||||
|
|
||||||
domain = expression.AND([domain, [('task_state', '=', state)]])
|
|
||||||
WillTask = request.env['yuthon.will.task'].sudo()
|
|
||||||
request_hour_selection = WillTask.fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
|
||||||
state_dict = dict(WillTask.fields_get(allfields=['task_state'])['task_state']['selection'])
|
|
||||||
result_list = []
|
result_list = []
|
||||||
task_ids = request.env['yuthon.will.task'].sudo().search(domain, offset=start, limit=20, order=order)
|
for task in data:
|
||||||
user_id = request.env['res.users'].sudo().search([('id', '=', users_id)])
|
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
||||||
company_id = user_id.company_id
|
file_list = []
|
||||||
for task in task_ids:
|
for attach in task.document_ids:
|
||||||
if company_id.id == task.company_id.id:
|
attach_id = request.env['ir.attachment'].sudo().search([('id', '=', attach.id)])
|
||||||
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
file_url = 'http://view.officeapps.live.com/op/view.aspx?src=' + quote(
|
||||||
result = {
|
'https://oa.thtzjt.com' + '/web/content/office_preview/' + str(attach_id.id))
|
||||||
'id': task.id,
|
file_list.append({
|
||||||
'name': task.name,
|
'file_url': file_url,
|
||||||
'employee_id': task.employee_id.name or '',
|
'file_name': attach_id.display_name,
|
||||||
'employee_ids': ', '.join(task.employee_ids.mapped('name')) if task.employee_ids else '',
|
})
|
||||||
'employee_num': ','.join(task.employee_ids.mapped('name')) if task.employee_ids else '',
|
result = {
|
||||||
'start_date': fields.Date.to_string(task.start_date) if task.start_date else '',
|
'id': task.id,
|
||||||
'weekday': weekday_str,
|
'name': task.name,
|
||||||
'address': task.address or '',
|
'employee_id': task.employee_id.name,
|
||||||
'request_hour_from': '',
|
'employee_ids': ', '.join(task.employee_ids.mapped('name')),
|
||||||
'request_hour_to': '',
|
'start_date': fields.Date.to_string(task.start_date),
|
||||||
'state': task.task_state,
|
'weekday': weekday_str,
|
||||||
'display_state': state_dict.get(task.task_state),
|
'request_hour_from': '',
|
||||||
'task_type': task.task_type
|
'request_hour_to': '',
|
||||||
}
|
'file_list': file_list,
|
||||||
hour_dict = dict(request_hour_selection)
|
}
|
||||||
result['request_hour_from'] = hour_dict.get(task.request_hour_from)
|
for f in request_hour_selection:
|
||||||
result['request_hour_to'] = hour_dict.get(task.request_hour_to)
|
if f[0] == task.request_hour_from:
|
||||||
result_list.append(result)
|
result['request_hour_from'] = f[1]
|
||||||
|
if f[0] == task.request_hour_to:
|
||||||
|
result['request_hour_to'] = f[1]
|
||||||
|
result_list.append(result)
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
'data': result_list,
|
'data': result_list,
|
||||||
'code': 200,
|
'code': 200,
|
||||||
@ -133,41 +159,33 @@ class YuthonWillTask(http.Controller):
|
|||||||
|
|
||||||
@http.route('/will/task/record', methods=['POST'], type='http', auth='public', csrf=False)
|
@http.route('/will/task/record', methods=['POST'], type='http', auth='public', csrf=False)
|
||||||
def will_task_record(self, **kwargs):
|
def will_task_record(self, **kwargs):
|
||||||
"""获取单个日程记录详情"""
|
|
||||||
task_id = kwargs.get('id')
|
task_id = kwargs.get('id')
|
||||||
task = request.env['yuthon.will.task'].sudo().search([('id', '=', task_id)])
|
task = request.env['yuthon.will.task'].sudo().search([('id', '=', task_id)], limit=1)
|
||||||
request_hour_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
request_hour_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
||||||
|
|
||||||
file_list = []
|
file_list = []
|
||||||
pdf = ['application/pdf']
|
for attach in task.document_ids:
|
||||||
for file in task.document_ids:
|
attach_id = request.env['ir.attachment'].sudo().search([('id', '=', attach.id)], limit=1)
|
||||||
base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
if attach_id:
|
||||||
file_url = f"{base_url}/web/content/preview/{file.id}"
|
file_url = 'http://view.officeapps.live.com/op/view.aspx?src=' + quote(
|
||||||
if file.mimetype in pdf:
|
'https://oa.thtzjt.com' + '/web/content/office_preview/' + str(attach_id.id))
|
||||||
file_url = f"{base_url}/web/content/preview/{file.id}"
|
file_list.append({
|
||||||
file_list.append({
|
'file_url': file_url,
|
||||||
'file_url': file_url,
|
'file_name': attach_id.display_name,
|
||||||
'file_name': file.name,
|
})
|
||||||
})
|
|
||||||
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
||||||
result = {
|
result = {
|
||||||
'id': task.id,
|
'id': task.id,
|
||||||
'name': task.name,
|
'name': task.name,
|
||||||
'employee_id': task.employee_id.name,
|
'employee_id': task.employee_id.name,
|
||||||
'employee_ids': ','.join(task.employee_ids.mapped('name')),
|
'employee_ids': ', '.join(task.employee_ids.mapped('name')),
|
||||||
'task_time_ids': ','.join(task.task_time_ids.mapped('name')),
|
'start_date': fields.Date.to_string(task.start_date),
|
||||||
'employee': task.employee_ids.read(['name']),
|
|
||||||
'task_time': task.task_time_ids.read(['name']),
|
|
||||||
'employee_num': len(task.employee_ids),
|
|
||||||
'start_date': fields.Date.to_string(task.start_date) if task.start_date else '',
|
|
||||||
'weekday': weekday_str,
|
'weekday': weekday_str,
|
||||||
'request_hour_from': task.request_hour_from,
|
'request_hour_from': '',
|
||||||
'request_hour_to': task.request_hour_to,
|
'request_hour_to': '',
|
||||||
'remarks': task.remarks,
|
|
||||||
'address': task.address,
|
|
||||||
'file_list': file_list,
|
'file_list': file_list,
|
||||||
'priority': task.priority,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hour_dict = dict(request_hour_selection)
|
hour_dict = dict(request_hour_selection)
|
||||||
result['request_hour_from'] = hour_dict.get(task.request_hour_from)
|
result['request_hour_from'] = hour_dict.get(task.request_hour_from)
|
||||||
result['request_hour_to'] = hour_dict.get(task.request_hour_to)
|
result['request_hour_to'] = hour_dict.get(task.request_hour_to)
|
||||||
@ -177,106 +195,4 @@ class YuthonWillTask(http.Controller):
|
|||||||
'message': 'success'
|
'message': 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
@http.route('/will/task/create', methods=['POST'], type='http', auth='public', csrf=False)
|
|
||||||
def will_task_create(self, **kwargs):
|
|
||||||
"""手机端创建日程接口"""
|
|
||||||
request.env['yuthon.will.task'].sudo().create({
|
|
||||||
'name': kwargs.get('name'),
|
|
||||||
'employee_id': kwargs.get('id'),
|
|
||||||
'employee_ids': kwargs.get('id'),
|
|
||||||
'start_date': kwargs.get('id'),
|
|
||||||
'request_hour_from': '',
|
|
||||||
'request_hour_to': '',
|
|
||||||
'remarks': kwargs.get('remarks'),
|
|
||||||
'address': kwargs.get('address'),
|
|
||||||
})
|
|
||||||
return json.dumps({
|
|
||||||
'code': 200,
|
|
||||||
'message': '数据创建成功'
|
|
||||||
})
|
|
||||||
|
|
||||||
@http.route('/phone/employee/self', type='http', auth="none", csrf=False, cors='*')
|
|
||||||
def phone_employee_self(self, **kw):
|
|
||||||
"""搜索员工(手机端接口)"""
|
|
||||||
value = kw.get('value', False)
|
|
||||||
domain = []
|
|
||||||
if value:
|
|
||||||
domain.append(('name', 'ilike', value))
|
|
||||||
records = request.env['hr.employee'].sudo().search(domain, limit=15)
|
|
||||||
data = records.read(['name'])
|
|
||||||
return json.dumps({'data': data, 'code': 200, 'message': ''})
|
|
||||||
|
|
||||||
@http.route('/phone/task/time', type='http', auth="none", csrf=False, cors='*')
|
|
||||||
def phone_task_time(self, **kw):
|
|
||||||
"""搜索提醒时间配置(手机端接口)"""
|
|
||||||
value = kw.get('value', False)
|
|
||||||
domain = []
|
|
||||||
if value:
|
|
||||||
domain.append(('name', 'ilike', value))
|
|
||||||
records = request.env['yuthon.task.time'].sudo().search(domain, limit=15)
|
|
||||||
data = records.read(['name'])
|
|
||||||
return json.dumps({'data': data, 'code': 200, 'message': ''})
|
|
||||||
|
|
||||||
@http.route('/phone/task/create', type='http', auth="none", csrf=False, cors='*')
|
|
||||||
def phone_task_create(self, **kw):
|
|
||||||
"""手机端创建/更新日程接口
|
|
||||||
record_id: 记录id(根据这个判断是否创建还是更新,创建的时候不传,更新的时候传)
|
|
||||||
request_hour_from: 开始时间
|
|
||||||
request_hour_to: 结束时间
|
|
||||||
priority: 优先级
|
|
||||||
name: 任务内容
|
|
||||||
remarks: 备注
|
|
||||||
employee_ids: 关联员工
|
|
||||||
task_time_ids: 提醒
|
|
||||||
address: 地址
|
|
||||||
start_date: 任务日期
|
|
||||||
"""
|
|
||||||
def str2list(value: str) -> list:
|
|
||||||
if not value:
|
|
||||||
return []
|
|
||||||
if not isinstance(value, str):
|
|
||||||
raise ValueError('value must be str')
|
|
||||||
str_list = value.split(',')
|
|
||||||
return [int(s) for s in str_list]
|
|
||||||
|
|
||||||
employee_ids = str2list(kw.get('employee_ids'))
|
|
||||||
task_time_ids = str2list(kw.get('task_time_ids'))
|
|
||||||
|
|
||||||
if not isinstance(task_time_ids, list):
|
|
||||||
return json.dumps({'data': '', 'code': 201, 'message': '提醒参数错误'})
|
|
||||||
if not isinstance(employee_ids, list):
|
|
||||||
return json.dumps({'data': '', 'code': 201, 'message': '关联员工参数错误'})
|
|
||||||
data = {
|
|
||||||
'request_hour_from': kw.get('request_hour_from', False),
|
|
||||||
'request_hour_to': kw.get('request_hour_to', False),
|
|
||||||
'priority': kw.get('priority', False),
|
|
||||||
'name': kw.get('name', False),
|
|
||||||
'remarks': kw.get('remarks', False),
|
|
||||||
'employee_ids': [(6, 0, employee_ids)],
|
|
||||||
'task_time_ids': [(6, 0, task_time_ids)],
|
|
||||||
'address': kw.get('address', False),
|
|
||||||
'start_date': kw.get('start_date', False)
|
|
||||||
}
|
|
||||||
Task = request.env['yuthon.will.task'].sudo()
|
|
||||||
record_id = kw.get('record_id', False)
|
|
||||||
if record_id:
|
|
||||||
# 更新日程
|
|
||||||
record = Task.browse(int(record_id))
|
|
||||||
record.write(data)
|
|
||||||
else:
|
|
||||||
# 创建日程
|
|
||||||
users_id = int(kw.get('users_id'))
|
|
||||||
user = request.env['res.users'].sudo().browse(int(users_id))
|
|
||||||
record = Task.with_user(user).sudo().create(data)
|
|
||||||
return json.dumps({'data': record.id, 'code': 200, 'message': ''})
|
|
||||||
|
|
||||||
@http.route('/phone/task/unlink', type='http', auth="none", csrf=False, cors='*')
|
|
||||||
def phone_task_unlink(self, **kw):
|
|
||||||
"""手机端删除日程接口"""
|
|
||||||
record_id = kw.get('record_id', False)
|
|
||||||
if record_id:
|
|
||||||
record = request.env['yuthon.will.task'].sudo().browse(int(record_id))
|
|
||||||
record.sudo().unlink()
|
|
||||||
else:
|
|
||||||
return json.dumps({'data': '', 'code': 201, 'message': '记录不存在'})
|
|
||||||
return json.dumps({'data': 'success', 'code': 200, 'message': ''})
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
from . import yuthon_will_task
|
from . import yuthon_will_task
|
||||||
from . import yuthon_task_time
|
from . import yuthon_task_time
|
||||||
|
from . import inherit_room_booking
|
||||||
from . import yuthon_dates_task
|
from . import yuthon_dates_task
|
||||||
|
|||||||
116
yuthon_will_task/models/inherit_room_booking.py
Normal file
116
yuthon_will_task/models/inherit_room_booking.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
from datetime import datetime, timedelta, date
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class InheritRoomBooking(models.Model):
|
||||||
|
_inherit = 'room.booking'
|
||||||
|
|
||||||
|
def get_no_read_count(self):
|
||||||
|
# 1. 计算各类时间参数
|
||||||
|
today = date.today() + timedelta(hours=8)
|
||||||
|
today_start = datetime.combine(today, datetime.min.time())
|
||||||
|
today_end = datetime.combine(today, datetime.max.time())
|
||||||
|
domain = [
|
||||||
|
('start_datetime', '>=', today_start),
|
||||||
|
('start_datetime', '<=', today_end),
|
||||||
|
'|',
|
||||||
|
('stop_datetime', '=', False),
|
||||||
|
('stop_datetime', '>', datetime.now())
|
||||||
|
]
|
||||||
|
matching_records = self.search(domain)
|
||||||
|
count = len(matching_records)
|
||||||
|
return count
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
new_record = super(InheritRoomBooking, self).create(vals)
|
||||||
|
employee_list = []
|
||||||
|
booking = new_record
|
||||||
|
|
||||||
|
# ① 组织者
|
||||||
|
if booking.organizer_id:
|
||||||
|
employee_list.append(booking.organizer_id.employee_id.id)
|
||||||
|
# ② 记录人
|
||||||
|
if booking.user_id.employee_id:
|
||||||
|
employee_list.append(booking.user_id.employee_id.id)
|
||||||
|
# ③ 出席人员
|
||||||
|
if booking.room_users_ids:
|
||||||
|
for user in booking.room_users_ids:
|
||||||
|
if user.employee_id:
|
||||||
|
employee_list.append(user.employee_id.id)
|
||||||
|
# ④ 议题传达人
|
||||||
|
for line in booking.issue_meeting_line_ids:
|
||||||
|
if line.employee_id:
|
||||||
|
employee_list.append(line.employee_id.id)
|
||||||
|
# ⑤ 列席人员
|
||||||
|
for line in booking.issue_meeting_line_ids:
|
||||||
|
if line.attendees_ids:
|
||||||
|
employee_list.extend(line.attendees_ids.ids)
|
||||||
|
# ⑥ 列席角色群组
|
||||||
|
for line in booking.issue_meeting_line_ids:
|
||||||
|
if line.users_role_group_ids:
|
||||||
|
role_group_roles = line.users_role_group_ids.mapped('user_role_ids')
|
||||||
|
role_employees = self.env['hr.employee'].search([
|
||||||
|
'|',
|
||||||
|
('user_role_id', 'in', role_group_roles.ids),
|
||||||
|
('user_ids', 'in', role_group_roles.ids)
|
||||||
|
])
|
||||||
|
employee_list.extend(role_employees.ids)
|
||||||
|
# ⑦ 主持人
|
||||||
|
if booking.host:
|
||||||
|
employee_list.append(booking.host.id)
|
||||||
|
|
||||||
|
# 去重
|
||||||
|
employee_list = list(set(employee_list))
|
||||||
|
|
||||||
|
def get_hour_selection(datetime_obj):
|
||||||
|
if not datetime_obj:
|
||||||
|
return None
|
||||||
|
datetime_plus_8h = datetime_obj + timedelta(hours=8)
|
||||||
|
hour = datetime_plus_8h.hour
|
||||||
|
minute = datetime_plus_8h.minute
|
||||||
|
if minute == 0:
|
||||||
|
return str(hour)
|
||||||
|
elif minute == 15:
|
||||||
|
return f"{hour}.25"
|
||||||
|
elif minute == 30:
|
||||||
|
return f"{hour}.5"
|
||||||
|
elif minute == 45:
|
||||||
|
return f"{hour}.75"
|
||||||
|
else:
|
||||||
|
nearest_minute = round(minute / 15) * 15
|
||||||
|
if nearest_minute == 60:
|
||||||
|
hour += 1
|
||||||
|
nearest_minute = 0
|
||||||
|
if nearest_minute == 0:
|
||||||
|
return str(hour % 24)
|
||||||
|
elif nearest_minute == 15:
|
||||||
|
return f"{hour % 24}.25"
|
||||||
|
elif nearest_minute == 30:
|
||||||
|
return f"{hour % 24}.5"
|
||||||
|
elif nearest_minute == 45:
|
||||||
|
return f"{hour % 24}.75"
|
||||||
|
|
||||||
|
start_datetime = new_record.start_datetime
|
||||||
|
start_date = start_datetime.date() if start_datetime else False
|
||||||
|
request_hour_from = get_hour_selection(new_record.start_datetime)
|
||||||
|
request_hour_to = get_hour_selection(new_record.stop_datetime)
|
||||||
|
|
||||||
|
task_time_vals = vals.get('task_time_ids', [])
|
||||||
|
task_id = self.env['yuthon.will.task'].sudo().create({
|
||||||
|
'name': new_record.name,
|
||||||
|
'employee_ids': [(6, 0, employee_list)],
|
||||||
|
'task_time_ids': task_time_vals,
|
||||||
|
'start_date': start_date,
|
||||||
|
'request_hour_from': request_hour_from,
|
||||||
|
'request_hour_to': request_hour_to,
|
||||||
|
'address': new_record.room_id.name if new_record.room_id else '',
|
||||||
|
'room_booking_id': new_record.id,
|
||||||
|
'task_type': 'meet',
|
||||||
|
'task_state': 'will',
|
||||||
|
})
|
||||||
|
|
||||||
|
task_id._onchange_employee_ids()
|
||||||
|
return new_record
|
||||||
@ -1,4 +1,12 @@
|
|||||||
from odoo import api, fields, models
|
# -*- 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 YuthonDatesTask(models.Model):
|
class YuthonDatesTask(models.Model):
|
||||||
@ -9,3 +17,4 @@ class YuthonDatesTask(models.Model):
|
|||||||
yuthon_will_task_id = fields.Many2one('yuthon.will.task', string='日程')
|
yuthon_will_task_id = fields.Many2one('yuthon.will.task', string='日程')
|
||||||
employee_ids = fields.Many2many('hr.employee', string='提醒人员')
|
employee_ids = fields.Many2many('hr.employee', string='提醒人员')
|
||||||
task_state = fields.Selection([('to_do', '待提醒'), ('done', '已提醒')], string='状态')
|
task_state = fields.Selection([('to_do', '待提醒'), ('done', '已提醒')], string='状态')
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,6 @@ class YuthonTaskTime(models.Model):
|
|||||||
|
|
||||||
@api.onchange('number', 'task_unit')
|
@api.onchange('number', 'task_unit')
|
||||||
def onchange_name(self):
|
def onchange_name(self):
|
||||||
"""根据数值和单位自动生成名称"""
|
|
||||||
task_unit_dict = {'min': '分钟', 'hour': '小时', 'day': '天'}
|
task_unit_dict = {'min': '分钟', 'hour': '小时', 'day': '天'}
|
||||||
for n in self:
|
for n in self:
|
||||||
if n.number and n.task_unit:
|
if n.number and n.task_unit:
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from odoo import api, fields, models
|
from odoo import api, fields, models, modules
|
||||||
from datetime import datetime, timedelta, time, date
|
import datetime
|
||||||
|
from datetime import datetime, timedelta, time
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -11,7 +12,6 @@ class YuthonWillTask(models.Model):
|
|||||||
_description = '日程'
|
_description = '日程'
|
||||||
_rec_name = 'name'
|
_rec_name = 'name'
|
||||||
_order = 'task_datetime desc'
|
_order = 'task_datetime desc'
|
||||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
||||||
|
|
||||||
time_list = [
|
time_list = [
|
||||||
('0', '0:00'), ('0.25', '0:15'), ('0.5', '0:30'), ('0.75', '0:45'),
|
('0', '0:00'), ('0.25', '0:15'), ('0.5', '0:30'), ('0.75', '0:45'),
|
||||||
@ -38,16 +38,15 @@ class YuthonWillTask(models.Model):
|
|||||||
('21', '21:00'), ('21.25', '21:15'), ('21.5', '21:30'), ('21.75', '21:45'),
|
('21', '21:00'), ('21.25', '21:15'), ('21.5', '21:30'), ('21.75', '21:45'),
|
||||||
('22', '22:00'), ('22.25', '22:15'), ('22.5', '22:30'), ('22.75', '22:45'),
|
('22', '22:00'), ('22.25', '22:15'), ('22.5', '22:30'), ('22.75', '22:45'),
|
||||||
('23', '23:00'), ('23.25', '23:15'), ('23.5', '23:30'), ('23.75', '23:45')]
|
('23', '23:00'), ('23.25', '23:15'), ('23.5', '23:30'), ('23.75', '23:45')]
|
||||||
|
|
||||||
def _default_request_hour_from(self):
|
def _default_request_hour_from(self):
|
||||||
"""开始时间默认当前小时(处理UTC+8时区)"""
|
"""开始时间默认当前小时(处理UTC+8时区)"""
|
||||||
now = fields.Datetime.now() + timedelta(hours=8)
|
now = fields.datetime.now() + timedelta(hours=8)
|
||||||
hour = now.hour
|
hour = now.hour
|
||||||
return str(hour)
|
return str(hour)
|
||||||
|
|
||||||
def _default_request_hour_to(self):
|
def _default_request_hour_to(self):
|
||||||
"""结束时间默认当前小时+1(23+1=0,处理UTC+8时区)"""
|
"""结束时间默认当前小时+1(23+1=0,处理UTC+8时区)"""
|
||||||
now = fields.Datetime.now() + timedelta(hours=8)
|
now = fields.datetime.now() + timedelta(hours=8)
|
||||||
hour = (now.hour + 1) % 24
|
hour = (now.hour + 1) % 24
|
||||||
return str(hour)
|
return str(hour)
|
||||||
|
|
||||||
@ -63,6 +62,9 @@ class YuthonWillTask(models.Model):
|
|||||||
creation_time = fields.Date(string='创建时间', default=fields.Date.today())
|
creation_time = fields.Date(string='创建时间', default=fields.Date.today())
|
||||||
employee_ids = fields.Many2many('hr.employee', 'will_task_employee_rel', 'custom_id', 'employee_id',
|
employee_ids = fields.Many2many('hr.employee', 'will_task_employee_rel', 'custom_id', 'employee_id',
|
||||||
string='人员', store=True, index=True)
|
string='人员', store=True, index=True)
|
||||||
|
employee_ids2 = fields.Many2many('hr.employee', 'will_task_employee_rel2', 'custom_id', 'employee_id',
|
||||||
|
string='司机', store=True, index=True)
|
||||||
|
user_role_ids = fields.Many2many('soong.user.role', string='角色', store=True, index=True)
|
||||||
task_time_ids = fields.Many2many('yuthon.task.time', string='提醒')
|
task_time_ids = fields.Many2many('yuthon.task.time', string='提醒')
|
||||||
document_ids = fields.Many2many('ir.attachment', string="附件")
|
document_ids = fields.Many2many('ir.attachment', string="附件")
|
||||||
address = fields.Char(string='地点')
|
address = fields.Char(string='地点')
|
||||||
@ -77,54 +79,82 @@ class YuthonWillTask(models.Model):
|
|||||||
string='用户', store=True, index=True, default=lambda self: self.env.user)
|
string='用户', store=True, index=True, default=lambda self: self.env.user)
|
||||||
|
|
||||||
self_users_ids = fields.Many2many('res.users',
|
self_users_ids = fields.Many2many('res.users',
|
||||||
relation='yuthon_will_task_current_users_rel', column1='task_id', column2='user_id', string="当前经办人", store=True, default=lambda self: [(6, 0, [self.env.user.id])]
|
relation='yuthon_will_task_current_users_rel',column1='task_id', column2='user_id', string="当前经办人", store=True, default=lambda self: [(6, 0, [self.env.user.id])]
|
||||||
)
|
)
|
||||||
|
|
||||||
employee_id = fields.Many2one('hr.employee', string="经办人", default=lambda self: self.env.user.employee_id)
|
employee_id = fields.Many2one('hr.employee', string="经办人", default=lambda self: self.env.user.employee_id)
|
||||||
task_week = fields.Char(string="周", readonly=True, compute='_compute_task_week')
|
task_week = fields.Char(string="周", readonly=True, compute='_compute_task_week')
|
||||||
task_week2 = fields.Char(string="周", readonly=True, compute='_compute_task_week2')
|
task_week2 = fields.Char(string="周", readonly=True, compute='_compute_task_week2')
|
||||||
|
msg_id = fields.Char(string="msg_id")
|
||||||
task_type = fields.Selection([
|
task_type = fields.Selection([
|
||||||
('task', '日程'),
|
('task', '日程'),
|
||||||
('meet', '会议')
|
('meet', '会议')
|
||||||
], string='类型', default='task')
|
], string='类型', default='task')
|
||||||
|
room_booking_id = fields.Many2one('room.booking', string='会议室')
|
||||||
|
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',
|
||||||
|
search='_search_part_of_company')
|
||||||
|
|
||||||
def update_task_state(self):
|
def update_task_state(self):
|
||||||
"""将选中日程标记为已办"""
|
|
||||||
will_ids = self.env["yuthon.will.task"].browse(
|
will_ids = self.env["yuthon.will.task"].browse(
|
||||||
self._context.get('active_ids', self._context.get('active_id')))
|
self._context.get('active_ids', self._context.get('active_id')))
|
||||||
for will in will_ids:
|
for will in will_ids:
|
||||||
will.task_state = 'done'
|
will.task_state = 'done'
|
||||||
|
|
||||||
|
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
|
||||||
|
if not value:
|
||||||
|
operator = '!=' if operator == '=' else '='
|
||||||
|
if operator == '=':
|
||||||
|
return [('company_id', 'in', company_ids)]
|
||||||
|
else:
|
||||||
|
return [('company_id', 'not in', company_ids)]
|
||||||
|
|
||||||
@api.onchange('start_date', 'end_date')
|
@api.onchange('start_date', 'end_date')
|
||||||
def _onchange_date_range(self):
|
def _onchange_date_range(self):
|
||||||
"""校验开始日期不能大于结束日期"""
|
|
||||||
if self.start_date and self.end_date:
|
if self.start_date and self.end_date:
|
||||||
if self.start_date > self.end_date:
|
start = fields.Datetime.from_string(self.start_date)
|
||||||
|
end = fields.Datetime.from_string(self.end_date)
|
||||||
|
if start > end:
|
||||||
raise ValidationError("开始日期必须小于结束日期,请重新设置。")
|
raise ValidationError("开始日期必须小于结束日期,请重新设置。")
|
||||||
|
|
||||||
|
def back_wecom_msg(self):
|
||||||
|
self.env['wecom.apps'].back_message(category='task', msg_id=self.msg_id)
|
||||||
|
|
||||||
def open_record(self):
|
def open_record(self):
|
||||||
"""打开当前日程记录的表单视图"""
|
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
if self.room_booking_id:
|
||||||
|
target_model = 'room.booking'
|
||||||
|
target_id = self.room_booking_id.id
|
||||||
|
view_id = self.env.ref('room.room_booking_view_form').id
|
||||||
|
else:
|
||||||
|
target_model = self._name
|
||||||
|
target_id = self.id
|
||||||
|
view_id = self.env.ref('yuthon_will_task.view_yuthon_will_task_form').id
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'name': '日程详情',
|
'name': f'{target_model} 详情',
|
||||||
'res_model': self._name,
|
'res_model': target_model,
|
||||||
'res_id': self.id,
|
'res_id': target_id,
|
||||||
'view_mode': 'form',
|
'view_mode': 'form',
|
||||||
'view_id': self.env.ref('yuthon_will_task.view_yuthon_will_task_form').id,
|
'view_type': 'form',
|
||||||
|
'view_id': view_id,
|
||||||
'target': 'current',
|
'target': 'current',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@api.depends('employee_ids')
|
@api.depends('employee_ids')
|
||||||
def _compute_employee_ids_short(self):
|
def _compute_employee_ids_short(self):
|
||||||
"""计算参与人简略显示(最多2人)"""
|
|
||||||
for record in self:
|
for record in self:
|
||||||
names = record.employee_ids.mapped('name')[:2]
|
names = record.employee_ids.mapped('name')[:2]
|
||||||
record.employee_ids_short = '、'.join(names)
|
record.employee_ids_short = '、'.join(names)
|
||||||
|
|
||||||
@api.depends('start_date')
|
@api.depends('start_date')
|
||||||
def _compute_task_week(self):
|
def _compute_task_week(self):
|
||||||
"""计算开始日期对应的星期"""
|
|
||||||
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.start_date:
|
if record.start_date:
|
||||||
@ -132,14 +162,12 @@ class YuthonWillTask(models.Model):
|
|||||||
|
|
||||||
@api.depends('end_date')
|
@api.depends('end_date')
|
||||||
def _compute_task_week2(self):
|
def _compute_task_week2(self):
|
||||||
"""计算结束日期对应的星期"""
|
|
||||||
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.end_date:
|
if record.end_date:
|
||||||
record.task_week2 = weekdays[record.end_date.weekday()]
|
record.task_week2 = weekdays[record.end_date.weekday()]
|
||||||
|
|
||||||
def one_time(self):
|
def one_time(self):
|
||||||
"""手动触发计算任务时间"""
|
|
||||||
will_ids = self.env["yuthon.will.task"].browse(
|
will_ids = self.env["yuthon.will.task"].browse(
|
||||||
self._context.get('active_ids', self._context.get('active_id')))
|
self._context.get('active_ids', self._context.get('active_id')))
|
||||||
for record in will_ids:
|
for record in will_ids:
|
||||||
@ -166,9 +194,8 @@ class YuthonWillTask(models.Model):
|
|||||||
else:
|
else:
|
||||||
record.task_datetime = False
|
record.task_datetime = False
|
||||||
|
|
||||||
@api.depends('start_date', 'task_week', 'request_hour_from', 'task_type')
|
@api.depends('start_date', 'task_week', 'request_hour_from', 'task_type', 'room_booking_id', 'room_booking_id.start_datetime', 'room_booking_id.stop_datetime')
|
||||||
def _compute_task_time(self):
|
def _compute_task_time(self):
|
||||||
"""计算任务时间显示文本"""
|
|
||||||
for record in self:
|
for record in self:
|
||||||
date_str = ""
|
date_str = ""
|
||||||
if record.start_date:
|
if record.start_date:
|
||||||
@ -201,28 +228,38 @@ class YuthonWillTask(models.Model):
|
|||||||
}
|
}
|
||||||
from_time = hour_mapping.get(record.request_hour_from, "")
|
from_time = hour_mapping.get(record.request_hour_from, "")
|
||||||
if date_str and from_time:
|
if date_str and from_time:
|
||||||
base_time = f"{date_str} {record.task_week} {from_time}"
|
# 会议类型:显示完整的开始和结束日期时间
|
||||||
record.task_time = base_time
|
if record.task_type == 'meet' and record.room_booking_id and record.room_booking_id.start_datetime and record.room_booking_id.stop_datetime:
|
||||||
|
# 转换为本地时间(UTC+8)
|
||||||
|
start_local = record.room_booking_id.start_datetime + timedelta(hours=8)
|
||||||
|
stop_local = record.room_booking_id.stop_datetime + timedelta(hours=8)
|
||||||
|
# 格式化为 "YYYY-M-D HH∶MM" 格式
|
||||||
|
start_str = start_local.strftime("%Y-%m-%d %H∶%M")
|
||||||
|
stop_str = stop_local.strftime("%Y-%m-%d %H∶%M")
|
||||||
|
record.task_time = f"{start_str} 至 {stop_str}"
|
||||||
|
else:
|
||||||
|
base_time = f"{date_str} {record.task_week} {from_time}"
|
||||||
|
record.task_time = base_time
|
||||||
else:
|
else:
|
||||||
record.task_time = ""
|
record.task_time = ""
|
||||||
|
|
||||||
@api.onchange('employee_ids', 'user_id')
|
@api.onchange('employee_ids', 'user_id')
|
||||||
def _onchange_employee_ids(self):
|
def _onchange_employee_ids(self):
|
||||||
"""人员变更时同步更新关联用户列表"""
|
|
||||||
user_ids = []
|
user_ids = []
|
||||||
if self.user_id:
|
if self.user_id:
|
||||||
user_ids.append(self.user_id.id)
|
user_ids.append(self.user_id.id)
|
||||||
for employee in self.employee_ids:
|
for employee in self.employee_ids:
|
||||||
if employee.user_id:
|
if employee.user_id:
|
||||||
user_ids.append(employee.user_id.id)
|
user_ids.append(employee.user_id.id)
|
||||||
|
for employee2 in self.employee_ids2:
|
||||||
|
if employee2.user_id:
|
||||||
|
user_ids.append(employee2.user_id.id)
|
||||||
self.users_ids = [(6, 0, user_ids)]
|
self.users_ids = [(6, 0, user_ids)]
|
||||||
|
|
||||||
def read_number(self):
|
def read_number(self):
|
||||||
"""获取当前用户的待办日程数量"""
|
|
||||||
return self.search_count([('task_state', '=', 'will'), ('users_ids', 'in', self.env.user.id)])
|
return self.search_count([('task_state', '=', 'will'), ('users_ids', 'in', self.env.user.id)])
|
||||||
|
|
||||||
def _compute_duration(self, task_time):
|
def _compute_duration(self, task_time):
|
||||||
"""根据提醒时间配置计算时间差"""
|
|
||||||
if task_time.task_unit == 'min':
|
if task_time.task_unit == 'min':
|
||||||
return timedelta(minutes=task_time.number)
|
return timedelta(minutes=task_time.number)
|
||||||
elif task_time.task_unit == 'hour':
|
elif task_time.task_unit == 'hour':
|
||||||
@ -233,8 +270,7 @@ class YuthonWillTask(models.Model):
|
|||||||
return timedelta.max
|
return timedelta.max
|
||||||
|
|
||||||
def add_done(self):
|
def add_done(self):
|
||||||
"""定时任务:将过期的待办日程自动标记为已办"""
|
today = datetime.date.today()
|
||||||
today = date.today()
|
|
||||||
records = self.search([
|
records = self.search([
|
||||||
('task_state', '=', 'will'),
|
('task_state', '=', 'will'),
|
||||||
('end_date', '<', today),
|
('end_date', '<', today),
|
||||||
@ -244,58 +280,57 @@ class YuthonWillTask(models.Model):
|
|||||||
records.write({'task_state': 'done'})
|
records.write({'task_state': 'done'})
|
||||||
|
|
||||||
def add_comment(self):
|
def add_comment(self):
|
||||||
"""日程定时任务提醒——通过 Odoo 内部消息通知"""
|
"""日程定时任务提醒"""
|
||||||
_logger.info('-----------日程任务验证--------')
|
_logger.info('-----------日程任务验证--------')
|
||||||
now = fields.Datetime.now() + timedelta(hours=8)
|
now = fields.datetime.now() + timedelta(hours=8)
|
||||||
dates_task_ids = self.env['yuthon.dates.task'].search([
|
today = fields.date.today()
|
||||||
('task_state', '=', 'to_do'),
|
dates_task_ids = self.env['yuthon.dates.task'].search([('task_state', '=', 'to_do'), ('reminder_time', '<', fields.datetime.now())])
|
||||||
('reminder_time', '<', fields.Datetime.now() + timedelta(days=1))
|
|
||||||
])
|
|
||||||
for task in dates_task_ids:
|
for task in dates_task_ids:
|
||||||
hour1 = (task.reminder_time + timedelta(hours=8)).hour
|
reminder_local = task.reminder_time + timedelta(hours=8)
|
||||||
minute1 = (task.reminder_time + timedelta(hours=8)).minute
|
hour1 = reminder_local.hour
|
||||||
_logger.info(f'-任务时间{hour1}:{minute1}当前时间{now.hour}:{now.minute}')
|
minute1 = reminder_local.minute
|
||||||
|
_logger.info(f'-任务时间{hour1}:{minute1}任务分钟{now.hour}:{now.minute}')
|
||||||
_logger.info(f'-状态{minute1 == now.minute and hour1 == now.hour}')
|
_logger.info(f'-状态{minute1 == now.minute and hour1 == now.hour}')
|
||||||
if minute1 == now.minute and hour1 == now.hour and task.reminder_time.day == fields.Date.today().day:
|
if minute1 == now.minute and hour1 == now.hour and reminder_local.date() == today:
|
||||||
task.write({'task_state': 'done'})
|
task.write({'task_state': 'done'})
|
||||||
will_id = task.yuthon_will_task_id
|
will_id = task.yuthon_will_task_id
|
||||||
will_id.task_state = 'done'
|
will_id.task_state = 'done'
|
||||||
# 构建提醒消息内容
|
for employee_id in task.employee_ids:
|
||||||
request_hour_from = dict(task.yuthon_will_task_id._fields['request_hour_from'].selection).get(
|
url = f'https://phone.thtzjt.com/phone/Schedule/{task.yuthon_will_task_id.id}/{employee_id.user_id.id}'
|
||||||
task.yuthon_will_task_id.request_hour_from, '')
|
request_hour_from = dict(task.yuthon_will_task_id._fields['request_hour_from'].selection).get(task.yuthon_will_task_id.request_hour_from, '') + '\n'
|
||||||
_weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
_weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||||
_start = task.yuthon_will_task_id.start_date
|
_start = task.yuthon_will_task_id.start_date
|
||||||
_week_str = _weekdays[_start.weekday()] if _start else ''
|
_week_str = _weekdays[_start.weekday()] if _start else ''
|
||||||
data_date = '开始时间: ' + str(_start) + ' ' + _week_str + '--' + str(request_hour_from) + '\n'
|
data_date = '开始时间:' + str(_start) + ' ' + _week_str + '--' + str(request_hour_from) + '\n'
|
||||||
data_name = "事项名称: " + task.yuthon_will_task_id.name + '\n'
|
data_name = "事项名称: " + task.yuthon_will_task_id.name + '\n'
|
||||||
data_user_ids = '参与人: ' + ', '.join(will_id.employee_ids.mapped('name')) + '\n'
|
data_user_ids = '参与人: ' + ', '.join(will_id.employee_ids.mapped('name')) + '\n'
|
||||||
data_nota = ('备注: ' + will_id.remarks + '\n') if will_id.remarks else '\n'
|
data_employee_id = ('司机:' + ', '.join(will_id.employee_ids2.mapped('name')) + '\n') if will_id.employee_ids2 else ''
|
||||||
body = data_date + data_name + data_user_ids + data_nota
|
data_nota = ('备注:' + will_id.remarks) if will_id.remarks else ('') + '\n'
|
||||||
# 使用 Odoo 内部消息通知替代企业微信推送
|
data = data_date + data_name + data_user_ids + data_employee_id + data_nota
|
||||||
will_id.message_post(
|
# url = f'https://phone.thtzjt.com/phone/Schedule/{will_id.id}/{employee_id.id}'
|
||||||
body=body,
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='task', user_id=employee_id.wecom_userid, title="日程事项提醒", description=data,
|
||||||
subject="日程事项提醒",
|
url=url, btntxt="详细信息")
|
||||||
message_type='notification',
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='task', user_id="18562027762", title="日程事项提醒", description=data,
|
||||||
subtype_xmlid='mail.mt_comment',
|
url=url, btntxt="详细信息")
|
||||||
)
|
|
||||||
|
|
||||||
def button_add_comment(self):
|
def button_add_comment(self):
|
||||||
"""手动发送日程提醒——通过 Odoo 内部消息通知"""
|
for employee_id in self.employee_ids + self.employee_ids2:
|
||||||
_weekdays2 = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
_weekdays2 = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||||
_week_str2 = _weekdays2[self.start_date.weekday()] if self.start_date else ''
|
_week_str2 = _weekdays2[self.start_date.weekday()] if self.start_date else ''
|
||||||
_hour_from = dict(self._fields['request_hour_from'].selection).get(self.request_hour_from, '')
|
_hour_from = dict(self._fields['request_hour_from'].selection).get(self.request_hour_from, '')
|
||||||
data_date = '开始时间: ' + str(self.start_date) + ' ' + _week_str2 + '--' + _hour_from + '\n'
|
data_date = '开始时间:' + str(self.start_date) + ' ' + _week_str2 + '--' + _hour_from + '\n'
|
||||||
data_name = "事项名称: " + self.name + '\n'
|
data_name = "事项名称: " + self.name + '\n'
|
||||||
data_user_ids = '参与人: ' + ', '.join(self.employee_ids.mapped('name')) + '\n'
|
data_user_ids = '参与人: ' + ', '.join(self.employee_ids.mapped('name')) + '\n'
|
||||||
data_nota = ('备注: ' + self.remarks + '\n') if self.remarks else '\n'
|
data_employee_id = ('司机:' + ', '.join(self.employee_ids2.mapped('name')) + '\n') if self.employee_ids2 else ''
|
||||||
body = data_date + data_name + data_user_ids + data_nota
|
data_nota = ('备注:' + self.remarks + '\n') if self.remarks else ('') + '\n'
|
||||||
# 使用 Odoo 内部消息通知替代企业微信推送
|
data = data_date + data_name + data_user_ids + data_nota + data_employee_id
|
||||||
self.message_post(
|
url = f'https://phone.thtzjt.com/phone/Schedule/{self.id}/{employee_id.user_id.id}'
|
||||||
body=body,
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='task', user_id=employee_id.wecom_userid,
|
||||||
subject="日程事项提醒",
|
title="日程事项提醒", description=data, url=url, btntxt="详细信息")
|
||||||
message_type='notification',
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='task', user_id="18562027762",
|
||||||
subtype_xmlid='mail.mt_comment',
|
title="日程事项提醒", description=data, url=url, btntxt="详细信息")
|
||||||
)
|
# if test_msg_id:
|
||||||
|
# self.write({'msg_id': test_msg_id[-1]['msgid']})
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.client',
|
'type': 'ir.actions.client',
|
||||||
'tag': 'display_notification',
|
'tag': 'display_notification',
|
||||||
@ -307,16 +342,15 @@ class YuthonWillTask(models.Model):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def button_add_comment_cancel(self):
|
def button_add_comment_cancel(self):
|
||||||
"""取消日程并发送内部通知"""
|
|
||||||
self.write({'task_state': 'cancel'})
|
self.write({'task_state': 'cancel'})
|
||||||
body = "事项名称: " + self.name + '\t' + '该事项已取消'
|
employee_list = [i.id for i in self.employee_ids] + [i.id for i in self.employee_ids2]
|
||||||
# 使用 Odoo 内部消息通知替代企业微信推送
|
for employee_id in self.env['hr.employee'].search([('id', 'in', employee_list)]):
|
||||||
self.message_post(
|
if not employee_id.wecom_userid:
|
||||||
body=body,
|
raise UserError("请同步{}的企业微信".format(employee_id.name))
|
||||||
subject="日程事项取消提醒",
|
url = f'https://oa.thtzjt.com/webh5#/journey?id={self.id}'
|
||||||
message_type='notification',
|
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='task', user_id=employee_id.wecom_userid,
|
||||||
subtype_xmlid='mail.mt_comment',
|
title="日程事项提醒", description="事项名称: " + self.name + '\t' + '该事项已取消',
|
||||||
)
|
url=url, btntxt="详细信息")
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.client',
|
'type': 'ir.actions.client',
|
||||||
'tag': 'display_notification',
|
'tag': 'display_notification',
|
||||||
@ -337,14 +371,13 @@ class YuthonWillTask(models.Model):
|
|||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
"""修改日程时,先删除旧提醒任务,再创建新的"""
|
"""修改日程时,先删除旧提醒任务,再创建新的"""
|
||||||
res = super(YuthonWillTask, self).write(vals)
|
res = super(YuthonWillTask, self).write(vals)
|
||||||
key_fields = ['start_date', 'request_hour_from', 'task_time_ids', 'employee_ids', 'task_type']
|
key_fields = ['start_date', 'request_hour_from', 'task_time_ids', 'employee_ids', 'employee_ids2', 'task_type', 'room_booking_id']
|
||||||
if any(field in vals for field in key_fields):
|
if any(field in vals for field in key_fields):
|
||||||
self._sync_dates_task()
|
self._sync_dates_task()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _sync_dates_task(self):
|
def _sync_dates_task(self):
|
||||||
"""根据提醒时间配置同步创建日程提醒任务"""
|
reminder_employees = self.employee_ids | self.employee_ids2 | self.employee_id
|
||||||
reminder_employees = self.employee_ids | self.employee_id
|
|
||||||
if not reminder_employees:
|
if not reminder_employees:
|
||||||
return
|
return
|
||||||
old_tasks = self.env['yuthon.dates.task'].search([('yuthon_will_task_id', '=', self.id), ('task_state', '=', 'to_do')])
|
old_tasks = self.env['yuthon.dates.task'].search([('yuthon_will_task_id', '=', self.id), ('task_state', '=', 'to_do')])
|
||||||
@ -353,7 +386,7 @@ class YuthonWillTask(models.Model):
|
|||||||
start_hour = float(self.request_hour_from)
|
start_hour = float(self.request_hour_from)
|
||||||
hours = int(start_hour)
|
hours = int(start_hour)
|
||||||
minutes = int((start_hour - hours) * 60)
|
minutes = int((start_hour - hours) * 60)
|
||||||
task_start_datetime = datetime.combine(self.start_date, time(hour=hours, minute=minutes))
|
task_start_datetime = datetime.combine(self.start_date,time(hour=hours, minute=minutes))
|
||||||
for task_time in self.task_time_ids:
|
for task_time in self.task_time_ids:
|
||||||
reminder_time_str = task_time.name
|
reminder_time_str = task_time.name
|
||||||
reminder_hours = 0.0
|
reminder_hours = 0.0
|
||||||
@ -368,7 +401,7 @@ class YuthonWillTask(models.Model):
|
|||||||
elif unit == '天':
|
elif unit == '天':
|
||||||
reminder_hours = num * 24.0
|
reminder_hours = num * 24.0
|
||||||
reminder_datetime = task_start_datetime - timedelta(hours=reminder_hours)
|
reminder_datetime = task_start_datetime - timedelta(hours=reminder_hours)
|
||||||
now = fields.Datetime.now() + timedelta(hours=8)
|
now = fields.datetime.now() + timedelta(hours=8)
|
||||||
if now > reminder_datetime:
|
if now > reminder_datetime:
|
||||||
continue
|
continue
|
||||||
existing_dates_task = self.env['yuthon.dates.task'].search([
|
existing_dates_task = self.env['yuthon.dates.task'].search([
|
||||||
@ -386,8 +419,9 @@ class YuthonWillTask(models.Model):
|
|||||||
task_vals['yuthon_will_task_id'] = self.id
|
task_vals['yuthon_will_task_id'] = self.id
|
||||||
self.env['yuthon.dates.task'].create(task_vals)
|
self.env['yuthon.dates.task'].create(task_vals)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def schedule_reminders(self):
|
def schedule_reminders(self):
|
||||||
"""查看当前日程的提醒任务列表"""
|
|
||||||
return {
|
return {
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'name': '提醒任务详情',
|
'name': '提醒任务详情',
|
||||||
@ -395,3 +429,4 @@ class YuthonWillTask(models.Model):
|
|||||||
'res_model': 'yuthon.dates.task',
|
'res_model': 'yuthon.dates.task',
|
||||||
'domain': [('yuthon_will_task_id', '=', self.id)],
|
'domain': [('yuthon_will_task_id', '=', self.id)],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_yuthon_task_time,yuthon_task_time,model_yuthon_task_time,base.group_user,1,1,1,0
|
access_yuthon_task_time,yuthon_task_time,model_yuthon_task_time,base.group_user,1,1,1,1
|
||||||
access_yuthon_task_time_manager,yuthon_task_time_manager,model_yuthon_task_time,base.group_system,1,1,1,1
|
access_yuthon_will_task,yuthon_will_task,model_yuthon_will_task,base.group_user,1,1,1,1
|
||||||
access_yuthon_will_task,yuthon_will_task,model_yuthon_will_task,base.group_user,1,1,1,0
|
access_yuthon_dates_task,yuthon_dates_task,model_yuthon_dates_task,base.group_user,1,1,1,1
|
||||||
access_yuthon_will_task_manager,yuthon_will_task_manager,model_yuthon_will_task,base.group_system,1,1,1,1
|
|
||||||
access_yuthon_dates_task,yuthon_dates_task,model_yuthon_dates_task,base.group_user,1,1,1,0
|
|
||||||
access_yuthon_dates_task_manager,yuthon_dates_task_manager,model_yuthon_dates_task,base.group_system,1,1,1,1
|
|
||||||
|
|||||||
|
@ -13,5 +13,13 @@
|
|||||||
<field name="category_id" ref="module_yuthon_will_task"/>
|
<field name="category_id" ref="module_yuthon_will_task"/>
|
||||||
<field name="implied_ids" eval="[(4, ref('group_yuthon_will_task_user'))]"/>
|
<field name="implied_ids" eval="[(4, ref('group_yuthon_will_task_user'))]"/>
|
||||||
</record>
|
</record>
|
||||||
|
<record id="group_module_room_booking" model="ir.module.category">
|
||||||
|
<field name="name">会议组</field>
|
||||||
|
</record>
|
||||||
|
<record id="group_room_booking_manager" model="res.groups">
|
||||||
|
<field name="name">会议管理员</field>
|
||||||
|
<field name="category_id" ref="group_module_room_booking"/>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
105
yuthon_will_task/static/src/css/task_detail.css
Normal file
105
yuthon_will_task/static/src/css/task_detail.css
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.o_main_content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-detail-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-align-value {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-info {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-info {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-list {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
width: 80px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #000;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-item {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-content {
|
||||||
|
line-height: 1.6;
|
||||||
|
white-space: pre-line;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back {
|
||||||
|
padding: 6px 15px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.info-label {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
.task-detail-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,6 +19,18 @@ export class CustomTaskListController extends ListController {
|
|||||||
context: {'task_state': 'will'},
|
context: {'task_state': 'will'},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
openMeetForm() {
|
||||||
|
this.actionService.doAction({
|
||||||
|
type: 'ir.actions.act_window',
|
||||||
|
res_model: 'room.booking',
|
||||||
|
name: '通知公告',
|
||||||
|
view_mode: 'form',
|
||||||
|
view_type: 'form',
|
||||||
|
views: [[false, 'form']],
|
||||||
|
target: 'current',
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.category("views").add("custom_task_tree_button", {
|
registry.category("views").add("custom_task_tree_button", {
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
<button type="button" class="btn btn-primary" style="margin-left: 10px;" t-on-click="openTaskForm">
|
<button type="button" class="btn btn-primary" style="margin-left: 10px;" t-on-click="openTaskForm">
|
||||||
新建日程
|
新建日程
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary" style="margin-left: 10px;" t-on-click="openMeetForm">
|
||||||
|
新建会议
|
||||||
|
</button>
|
||||||
</xpath>
|
</xpath>
|
||||||
</t>
|
</t>
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
70
yuthon_will_task/views/task_template.xml
Normal file
70
yuthon_will_task/views/task_template.xml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="task_detail_template" name="任务详情模板">
|
||||||
|
<t t-call="web.layout">
|
||||||
|
<t t-set="head">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||||
|
<link rel="stylesheet" type="text/css"
|
||||||
|
href="/yuthon_will_task/static/src/css/task_detail.css" />
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<div class="task-detail-container">
|
||||||
|
<h1 class="main-title">
|
||||||
|
<t t-esc="name" />
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="time-info">
|
||||||
|
<t t-set="weekday_map"
|
||||||
|
t-value="{'0':'一','1':'二','2':'三','3':'四','4':'五','5':'六','6':'日'}" />
|
||||||
|
<t t-if="start_date and start_date != 'False'">
|
||||||
|
<span t-esc="start_date" />
|
||||||
|
<t t-set="date_obj"
|
||||||
|
t-value="datetime.datetime.strptime(start_date, '%Y-%m-%d')" />
|
||||||
|
<span>(星期<t t-esc="weekday_map[str(date_obj.weekday())]" />)</span>
|
||||||
|
<t t-if="request_hour_from and request_hour_to">
|
||||||
|
<t t-set="from_time"
|
||||||
|
t-value="request_hour_from.replace('.5', ':30').replace('.0', ':00') if '.' in request_hour_from else request_hour_from + ':00'" />
|
||||||
|
<t t-set="to_time"
|
||||||
|
t-value="request_hour_to.replace('.5', ':30').replace('.0', ':00') if '.' in request_hour_to else request_hour_to + ':00'" />
|
||||||
|
<span> 时间:<t t-esc="from_time" /> - <t t-esc="to_time" /></span>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="address-info"> 地址: <t t-esc="address" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-list">
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">组织人</div>
|
||||||
|
<div class="info-value right-align-value">
|
||||||
|
<t t-esc="user_id" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">参与人</div>
|
||||||
|
<div class="info-value right-align-value ">
|
||||||
|
<t t-esc="employee_ids" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item description-item">
|
||||||
|
<div class="info-label">描述</div>
|
||||||
|
<div class="info-value ">
|
||||||
|
<t t-esc="description" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">任务编号</div>
|
||||||
|
<div class="info-value right-align-value">
|
||||||
|
<t t-esc="code" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_yuthon_dates_task_tree" model="ir.ui.view">
|
<record id="view_yuthon_dates_task_list" model="ir.ui.view">
|
||||||
<field name="name">yuthon.dates.task.tree</field>
|
<field name="name">yuthon.dates.task.list</field>
|
||||||
<field name="model">yuthon.dates.task</field>
|
<field name="model">yuthon.dates.task</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="日程任务" editable="bottom"
|
<list string="日程任务" editable="bottom"
|
||||||
decoration-success="task_state == 'to_do'"
|
decoration-success="task_state == 'to_do'"
|
||||||
decoration-danger="task_state == 'done'"
|
decoration-danger="task_state == 'done'"
|
||||||
>
|
>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||||
<field name="reminder_time"/>
|
<field name="reminder_time"/>
|
||||||
<field name="task_state"/>
|
<field name="task_state"/>
|
||||||
</tree>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="view_yuthon_dates_task_search" model="ir.ui.view">
|
<record id="view_yuthon_dates_task_search" model="ir.ui.view">
|
||||||
@ -30,8 +30,8 @@
|
|||||||
<record id="view_yuthon_dates_task_action" model="ir.actions.act_window">
|
<record id="view_yuthon_dates_task_action" model="ir.actions.act_window">
|
||||||
<field name="name">日程任务</field>
|
<field name="name">日程任务</field>
|
||||||
<field name="res_model">yuthon.dates.task</field>
|
<field name="res_model">yuthon.dates.task</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">list</field>
|
||||||
<field name="view_id" ref="view_yuthon_dates_task_tree"/>
|
<field name="view_id" ref="view_yuthon_dates_task_list"/>
|
||||||
<field name="search_view_id" ref="view_yuthon_dates_task_search"/>
|
<field name="search_view_id" ref="view_yuthon_dates_task_search"/>
|
||||||
<field name="context">{'search_default_task_state':1}</field>
|
<field name="context">{'search_default_task_state':1}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@ -4,11 +4,27 @@
|
|||||||
name='会议日程'
|
name='会议日程'
|
||||||
web_icon="yuthon_will_task,static/description/icon.png"
|
web_icon="yuthon_will_task,static/description/icon.png"
|
||||||
sequence='10'/>
|
sequence='10'/>
|
||||||
|
<!-- <menuitem id='menu_yuthon_will_task_root'-->
|
||||||
|
<!-- name='我的日程'-->
|
||||||
|
<!-- parent='menu_yuthon_will_task'-->
|
||||||
|
<!-- action='view_yuthon_will_task_action'-->
|
||||||
|
<!-- sequence='1'/>-->
|
||||||
<menuitem id='menu_yuthon_will_task_root2'
|
<menuitem id='menu_yuthon_will_task_root2'
|
||||||
name='日程管理'
|
name='日程管理'
|
||||||
parent='menu_yuthon_will_task'
|
parent='menu_yuthon_will_task'
|
||||||
action='view_yuthon_will_task_action2'
|
action='view_yuthon_will_task_action2'
|
||||||
sequence='10'/>
|
sequence='10'/>
|
||||||
|
<menuitem id="room_menu_root_will"
|
||||||
|
name="预定会议"
|
||||||
|
action="room.room_booking_action"
|
||||||
|
parent="menu_yuthon_will_task"
|
||||||
|
groups="yuthon_will_task.group_room_booking_manager"
|
||||||
|
sequence="10"/>
|
||||||
|
<menuitem id="room_booking_menu_will"
|
||||||
|
name="会议室"
|
||||||
|
action="room.room_room_action"
|
||||||
|
parent="menu_yuthon_will_task"
|
||||||
|
sequence="12"/>
|
||||||
<menuitem id="task_type_menu"
|
<menuitem id="task_type_menu"
|
||||||
name="配置"
|
name="配置"
|
||||||
parent="menu_yuthon_will_task"
|
parent="menu_yuthon_will_task"
|
||||||
|
|||||||
@ -1,21 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="yuthon_task_time_tree" model="ir.ui.view">
|
<record id="yuthon_task_time_list" model="ir.ui.view">
|
||||||
<field name="name">yuthon.task.time.tree</field>
|
<field name="name">yuthon.task.time.list</field>
|
||||||
<field name="model">yuthon.task.time</field>
|
<field name="model">yuthon.task.time</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="提醒时长配置" editable="bottom">
|
<list string="提醒时长配置" editable="bottom">
|
||||||
<field name="sequence" widget="handle"/>
|
<field name="sequence" widget="handle"/>
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1"/>
|
||||||
<field name="number" required="1"/>
|
<field name="number" required="1"/>
|
||||||
<field name="task_unit" required="1"/>
|
<field name="task_unit" required="1"/>
|
||||||
</tree>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="yuthon_task_time_setting_action" model="ir.actions.act_window">
|
<record id="yuthon_task_time_setting_action" model="ir.actions.act_window">
|
||||||
<field name="name">提醒时长配置</field>
|
<field name="name">提醒时长配置</field>
|
||||||
<field name="res_model">yuthon.task.time</field>
|
<field name="res_model">yuthon.task.time</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">list</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id='view_yuthon_will_task_tree' model='ir.ui.view'>
|
<record id='view_yuthon_will_task_list' model='ir.ui.view'>
|
||||||
<field name='name'>yuthon.will.task.tree</field>
|
<field name='name'>yuthon.will.task.list</field>
|
||||||
<field name='model'>yuthon.will.task</field>
|
<field name='model'>yuthon.will.task</field>
|
||||||
<field name='arch' type='xml'>
|
<field name='arch' type='xml'>
|
||||||
<tree string='日程'
|
<list string='日程'
|
||||||
decoration-muted="task_state == 'cancel'"
|
decoration-muted="task_state == 'cancel'"
|
||||||
decoration-danger="priority == 'very_urgent'"
|
decoration-danger="priority == 'very_urgent'"
|
||||||
decoration-info="priority == 'urgent'"
|
decoration-info="priority == 'urgent'"
|
||||||
@ -17,14 +17,22 @@
|
|||||||
<field name='employee_ids' widget="many2many_tags" required="1" options="{'no_create': True}"/>
|
<field name='employee_ids' widget="many2many_tags" required="1" options="{'no_create': True}"/>
|
||||||
<field name="address"/>
|
<field name="address"/>
|
||||||
<field name='remarks'/>
|
<field name='remarks'/>
|
||||||
|
<!-- <field name='document_ids' widget="many2many_tags"/>-->
|
||||||
|
<!-- <field name='employee_ids2' widget="many2many_tags" domain="[('user_role_ids.name', 'in', ['司机'])]" options="{'no_create': True}"/>-->
|
||||||
|
<!-- <field name='task_time_ids' widget="many2many_tags" required="1"/>-->
|
||||||
<field name='priority'/>
|
<field name='priority'/>
|
||||||
<field name='task_state' readonly="1"/>
|
<field name='task_state' readonly="1"/>
|
||||||
|
<!-- <field name='users_ids' widget="many2many_tags" column_invisible="1"/>-->
|
||||||
|
<!-- <field name='user_role_ids' widget="many2many_tags" column_invisible="1"/>-->
|
||||||
|
<!-- <field name="employee_id" optional="hide"/>-->
|
||||||
|
<!-- <field name='creation_time' optional="hide"/>-->
|
||||||
<button name="button_add_comment" string="提醒" type="object" class="btn btn-blue"
|
<button name="button_add_comment" string="提醒" type="object" class="btn btn-blue"
|
||||||
invisible="task_state in ['done', 'cancel']"/>
|
invisible="task_state in ['done', 'cancel']"/>
|
||||||
|
<!-- <button name="back_wecom_msg" string="测试撤销" type="object" class="oe_highlight"/>-->
|
||||||
<button name="button_add_comment_cancel" string="取消" type="object" class="btn btn-blue"
|
<button name="button_add_comment_cancel" string="取消" type="object" class="btn btn-blue"
|
||||||
invisible="task_state == 'cancel'"/>
|
invisible="task_state == 'cancel'"/>
|
||||||
<button name="schedule_reminders" string="提醒任务" type="object" class="btn btn-blue"/>
|
<button name="schedule_reminders" string="提醒任务" type="object" class="btn btn-blue"/>
|
||||||
</tree>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id='yuthon_will_task_search' model='ir.ui.view'>
|
<record id='yuthon_will_task_search' model='ir.ui.view'>
|
||||||
@ -111,6 +119,7 @@
|
|||||||
<field name="employee_id"/>
|
<field name="employee_id"/>
|
||||||
<field name="task_state" invisible="1"/>
|
<field name="task_state" invisible="1"/>
|
||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
|
<field name="is_company" invisible="1"/>
|
||||||
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"
|
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"
|
||||||
domain="[('company_id', '=', company_id)] if is_this_company else []"/>
|
domain="[('company_id', '=', company_id)] if is_this_company else []"/>
|
||||||
</group>
|
</group>
|
||||||
@ -121,23 +130,18 @@
|
|||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="remarks"/>
|
<field name="remarks"/>
|
||||||
<field name="document_ids" widget="many2many_binary"/>
|
<field name="document_ids" widget="preview_many2many"/>
|
||||||
<field name="creation_time" invisible="1"/>
|
<field name="creation_time" invisible="1"/>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
<div class="oe_chatter">
|
|
||||||
<field name="message_follower_ids" groups="base.group_user"/>
|
|
||||||
<field name="activity_ids"/>
|
|
||||||
<field name="message_ids"/>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id='view_yuthon_will_task_tree2' model='ir.ui.view'>
|
<record id='view_yuthon_will_task_list2' model='ir.ui.view'>
|
||||||
<field name='name'>yuthon.will.task.tree2</field>
|
<field name='name'>yuthon.will.task.list2</field>
|
||||||
<field name='model'>yuthon.will.task</field>
|
<field name='model'>yuthon.will.task</field>
|
||||||
<field name='arch' type='xml'>
|
<field name='arch' type='xml'>
|
||||||
<tree string='日程'
|
<list string='日程'
|
||||||
decoration-muted="task_state == 'cancel'"
|
decoration-muted="task_state == 'cancel'"
|
||||||
decoration-danger="priority == 'very_urgent'"
|
decoration-danger="priority == 'very_urgent'"
|
||||||
decoration-info="priority == 'urgent'"
|
decoration-info="priority == 'urgent'"
|
||||||
@ -150,13 +154,20 @@
|
|||||||
<field name='employee_ids' widget="many2many_tags" required="1" options="{'no_create': True}"/>
|
<field name='employee_ids' widget="many2many_tags" required="1" options="{'no_create': True}"/>
|
||||||
<field name="address"/>
|
<field name="address"/>
|
||||||
<field name='remarks'/>
|
<field name='remarks'/>
|
||||||
|
<!-- <field name='document_ids' widget="many2many_tags"/>-->
|
||||||
|
<!-- <field name='employee_ids2' widget="many2many_tags" domain="[('user_role_ids.name', 'in', ['司机'])]" options="{'no_create': True}"/>-->
|
||||||
|
<!-- <field name='task_time_ids' widget="many2many_tags" required="1"/>-->
|
||||||
<field name='priority'/>
|
<field name='priority'/>
|
||||||
<field name='task_state' readonly="1"/>
|
<field name='task_state' readonly="1"/>
|
||||||
|
<!-- <field name='users_ids' widget="many2many_tags" column_invisible="1"/>-->
|
||||||
|
<!-- <field name='user_role_ids' widget="many2many_tags" column_invisible="1"/>-->
|
||||||
|
<!-- <field name="employee_id" optional="hide"/>-->
|
||||||
|
<!-- <field name='creation_time' optional="hide"/>-->
|
||||||
<button name="button_add_comment" string="提醒" type="object" class="btn btn-blue"
|
<button name="button_add_comment" string="提醒" type="object" class="btn btn-blue"
|
||||||
invisible="task_state in ['done', 'cancel']"/>
|
invisible="task_state in ['done', 'cancel']"/>
|
||||||
<button name="button_add_comment_cancel" string="取消" type="object" class="btn btn-blue"
|
<button name="button_add_comment_cancel" string="取消" type="object" class="btn btn-blue"
|
||||||
invisible="task_state == 'cancel'"/>
|
invisible="task_state == 'cancel'"/>
|
||||||
</tree>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="view_yuthon_will_task_form2" model="ir.ui.view">
|
<record id="view_yuthon_will_task_form2" model="ir.ui.view">
|
||||||
@ -239,15 +250,15 @@
|
|||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="remarks"/>
|
<field name="remarks"/>
|
||||||
<field name="document_ids" widget="many2many_binary"/>
|
<field name="document_ids" widget="preview_many2many"/>
|
||||||
<field name="creation_time" invisible="1"/>
|
<field name="creation_time" invisible="1"/>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
<div class="oe_chatter">
|
<!-- <div class="oe_chatter">-->
|
||||||
<field name="message_follower_ids" groups="base.group_user"/>
|
<!-- <field name="message_follower_ids" groups="base.group_user"/>-->
|
||||||
<field name="activity_ids"/>
|
<!-- <field name="activity_ids"/>-->
|
||||||
<field name="message_ids"/>
|
<!-- <field name="message_ids"/>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@ -297,7 +308,7 @@
|
|||||||
参与人 <field name="employee_ids" widget="many2many_tags"/>共<t t-esc="record.employee_ids.raw_value.length"/>人
|
参与人 <field name="employee_ids" widget="many2many_tags"/>共<t t-esc="record.employee_ids.raw_value.length"/>人
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
<!-- 地点 -->
|
<!-- 会议室 -->
|
||||||
<t t-if="record.address.raw_value">
|
<t t-if="record.address.raw_value">
|
||||||
<div style="color: #555; font-size: 13px; margin-bottom: 4px;">
|
<div style="color: #555; font-size: 13px; margin-bottom: 4px;">
|
||||||
地点 <field name="address"/>
|
地点 <field name="address"/>
|
||||||
@ -335,22 +346,22 @@
|
|||||||
<record id='view_yuthon_will_task_action' model='ir.actions.act_window'>
|
<record id='view_yuthon_will_task_action' model='ir.actions.act_window'>
|
||||||
<field name='name'>我的日程</field>
|
<field name='name'>我的日程</field>
|
||||||
<field name='res_model'>yuthon.will.task</field>
|
<field name='res_model'>yuthon.will.task</field>
|
||||||
<field name='view_mode'>tree,kanban,calendar,form</field>
|
<field name='view_mode'>list,kanban,calendar,form</field>
|
||||||
<field name='domain'>[('users_ids', 'in', [uid]), ('task_state', '!=', 'cancel')]</field>
|
<field name='domain'>[('users_ids', 'in', [uid]), ('task_state', '!=', 'cancel')]</field>
|
||||||
<field name="context">{'search_default_next_seven_days': 1}</field>
|
<field name="context">{'search_default_next_seven_days': 1}</field>
|
||||||
<field name="view_ids" eval="[(5, 0, 0),
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_tree2')}),
|
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_list2')}),
|
||||||
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')}),
|
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')}),
|
||||||
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_form2')})]" />
|
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_form2')})]" />
|
||||||
</record>
|
</record>
|
||||||
<record id='view_yuthon_will_task_action2' model='ir.actions.act_window'>
|
<record id='view_yuthon_will_task_action2' model='ir.actions.act_window'>
|
||||||
<field name='name'>日程管理</field>
|
<field name='name'>日程管理</field>
|
||||||
<field name='res_model'>yuthon.will.task</field>
|
<field name='res_model'>yuthon.will.task</field>
|
||||||
<field name='view_mode'>tree,kanban,calendar,form</field>
|
<field name='view_mode'>list,kanban,calendar,form</field>
|
||||||
<field name='domain'>[('task_state', '!=', 'cancel'), ('is_this_company','=',True)]</field>
|
<field name='domain'>[('task_state', '!=', 'cancel'), ('is_company','=',True)]</field>
|
||||||
<field name="context">{'search_default_next_seven_days': 1}</field>
|
<field name="context">{'search_default_next_seven_days': 1}</field>
|
||||||
<field name="view_ids" eval="[(5, 0, 0),
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_tree2')}),
|
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_list2')}),
|
||||||
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')})]" />
|
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')})]" />
|
||||||
</record>
|
</record>
|
||||||
<record id="all_yuthon_will_task" model="ir.actions.server">
|
<record id="all_yuthon_will_task" model="ir.actions.server">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user