1
This commit is contained in:
commit
782bea26af
3
yuthon_notice/__init__.py
Normal file
3
yuthon_notice/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
from . import controllers
|
||||
33
yuthon_notice/__manifest__.py
Normal file
33
yuthon_notice/__manifest__.py
Normal file
@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Yuthon notice",
|
||||
'summary': "通知公告",
|
||||
'description': """
|
||||
|
||||
""",
|
||||
'author': 'pengyuthon',
|
||||
'website': 'https://www.pengyuthon.com',
|
||||
'category': 'Human Resources/Attendances',
|
||||
'version': '0.1',
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['base', 'hr', 'mail'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/notice_code_data.xml',
|
||||
'data/notice_cron_data.xml',
|
||||
'data/notice_type_data.xml',
|
||||
'views/yuthon_notice_views.xml',
|
||||
'views/yuthon_notice_type_views.xml',
|
||||
'views/yuthon_cs_tree_views.xml',
|
||||
'views/yuthon_confirm_users_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'yuthon_notice/static/src/js/custom_create_button.js',
|
||||
'yuthon_notice/static/src/xml/custom_create_button.xml',
|
||||
'yuthon_notice/static/scss/notice_scss.scss',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': True,
|
||||
}
|
||||
1
yuthon_notice/controllers/__init__.py
Normal file
1
yuthon_notice/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import main
|
||||
133
yuthon_notice/controllers/main.py
Normal file
133
yuthon_notice/controllers/main.py
Normal file
@ -0,0 +1,133 @@
|
||||
import base64
|
||||
import json
|
||||
|
||||
from odoo import http, fields
|
||||
from odoo.http import request
|
||||
from urllib.parse import quote
|
||||
|
||||
|
||||
class YuthonNotice(http.Controller):
|
||||
|
||||
@http.route('/yuthon/yuthon/notice', methods=['POST'], type='http', auth='none', csrf=False)
|
||||
def index(self, **kwargs):
|
||||
id = kwargs.get('id')
|
||||
|
||||
data = request.env['yuthon.notice'].sudo().search([("id", "=", int(id))])
|
||||
documents = []
|
||||
for document in data.documents_ids:
|
||||
documents.append({
|
||||
'id': document.id,
|
||||
'name': document.name,
|
||||
'url': document.url,
|
||||
'type': document.type,
|
||||
'file_size': document.file_size,
|
||||
'file_size_display':document.file_size_display,
|
||||
'mimetype': document.mimetype,
|
||||
'datas': document.datas.decode('utf-8') if document.datas else None,
|
||||
})
|
||||
|
||||
result = {
|
||||
'id': data.id,
|
||||
'title': data.name,
|
||||
'code': data.code,
|
||||
'create_date1': fields.Date.to_string(data.create_date1),
|
||||
'notice_text': data.notice_text,
|
||||
'documents':documents,
|
||||
'user_name':data.users_id.name,
|
||||
'read_number': data.read_number,
|
||||
}
|
||||
return json.dumps({
|
||||
'data':result,
|
||||
'code':200,
|
||||
'message':'success'
|
||||
})
|
||||
|
||||
@http.route('/yuthon/notice/list', methods=['POST'], type='http', auth='none', csrf=False)
|
||||
def yuthon_notice_list(self, **kwargs):
|
||||
user_id = kwargs.get('users_id') # 按创建人筛选
|
||||
domain = []
|
||||
if user_id:
|
||||
user_id_int = int(user_id)
|
||||
domain.append(('users_id', '=', user_id_int))
|
||||
notice_data = request.env['yuthon.notice'].sudo().search(domain)
|
||||
result_list = []
|
||||
urgency_map = {'normal': '普件', 'urgent': '急件', 'emergency': '特急件'}
|
||||
for notice in notice_data:
|
||||
department_list = []
|
||||
file_list = []
|
||||
for attach in notice.documents_ids:
|
||||
file_url = 'http://view.officeapps.live.com/op/view.aspx?src=' + quote('https://oa.thtzjt.com' + '/web/content/office_preview/' + str(attach.id))
|
||||
file_list.append({'file_url': file_url, 'file_name': attach.display_name})
|
||||
result = {
|
||||
'id': notice.id,
|
||||
'title': notice.name,
|
||||
'code': notice.code,
|
||||
'notice_text': notice.notice_text,
|
||||
'create_date1': fields.Date.to_string(notice.create_date1),
|
||||
'user_name': notice.users_id.name,
|
||||
'read_number': notice.read_number or 0,
|
||||
'department_ids': department_list,
|
||||
'file_list': file_list,
|
||||
'urgency_level': notice.urgency_level,
|
||||
'urgency_name': urgency_map.get(notice.urgency_level, ''),
|
||||
}
|
||||
result_list.append(result)
|
||||
return json.dumps({
|
||||
'data': result_list,
|
||||
'code': 200,
|
||||
'message': 'success'
|
||||
})
|
||||
|
||||
|
||||
@http.route('/yuthon/notice/record', methods=['POST'], type='http', auth='none', csrf=False)
|
||||
def yuthon_notice_record(self, **kwargs):
|
||||
id = kwargs.get('id')
|
||||
|
||||
data = request.env['yuthon.notice'].sudo().search([("id", "=", int(id))])
|
||||
|
||||
department_list = []
|
||||
for dept in data.department_ids:
|
||||
department_list.append({
|
||||
'id': dept.id,
|
||||
'name': dept.name,
|
||||
})
|
||||
|
||||
role_group_list = [
|
||||
{'id': group.id, 'name': group.name} # res.groups的name字段是角色组名称
|
||||
for group in data.users_role_group_ids
|
||||
]
|
||||
|
||||
file_list = []
|
||||
for attach in data.documents_ids:
|
||||
attach_id = request.env['ir.attachment'].sudo().search([('id', '=', attach.id)])
|
||||
file_url = 'http://view.officeapps.live.com/op/view.aspx?src=' + quote('https://oa.thtzjt.com' + '/web/content/office_preview/' + str(attach_id.id))
|
||||
file_list.append({
|
||||
'file_url': file_url,
|
||||
'file_name': attach_id.display_name,
|
||||
})
|
||||
|
||||
urgency_map = {'normal': '普件','urgent': '急件','emergency': '特急件',}
|
||||
|
||||
urgency_name = urgency_map.get(data.urgency_level)
|
||||
|
||||
result = {
|
||||
'id': data.id,
|
||||
'title': data.name,
|
||||
'code': data.code,
|
||||
'notice_text': data.notice_text,
|
||||
'create_date1': fields.Date.to_string(data.create_date1),
|
||||
'user_name': data.users_id.name,
|
||||
'read_number': data.read_number,
|
||||
'department_ids': department_list,
|
||||
'users_role_group_ids': role_group_list,
|
||||
'file_list': file_list,
|
||||
'urgency_name': urgency_name,
|
||||
}
|
||||
return json.dumps({
|
||||
'data':result,
|
||||
'code':200,
|
||||
'message':'success'
|
||||
})
|
||||
|
||||
|
||||
|
||||
24
yuthon_notice/data/notice_code_data.xml
Normal file
24
yuthon_notice/data/notice_code_data.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="notice_code_sequence" model="ir.sequence">
|
||||
<field name="name">通知公告编号</field>
|
||||
<field name="code">notice_code</field>
|
||||
<field name="prefix">TZ%(y)s%(month)s%(day)s</field>
|
||||
<field name="padding">3</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<template id="message_yuthon_confirm_users_data">
|
||||
<p style="margin: 0px;">
|
||||
<span>尊敬的用户,</span><br />
|
||||
<span style="margin-top: 8px;">您有一条新的公告确认请求
|
||||
<t t-esc="object.name"/> 请登录系统查看详情。
|
||||
</span>
|
||||
</p>
|
||||
<p style="margin-top: 24px; margin-bottom: 16px;">
|
||||
<a t-att-href="access_link" t-att-data-oe-model="object._name" t-att-data-oe-id="object.id" style="background-color:#2C65F7; padding: 10px; text-decoration: none; color: #fff; border-radius: 5px;">
|
||||
查看 <t t-esc="model_description or 'document'"/>
|
||||
</a>
|
||||
</p>
|
||||
</template>
|
||||
</odoo>
|
||||
15
yuthon_notice/data/notice_cron_data.xml
Normal file
15
yuthon_notice/data/notice_cron_data.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record forcecreate="True" id="ir_cron_yuthon_confirm_users" model="ir.cron">
|
||||
<field name="name">公告未确认通知</field>
|
||||
<field name="model_id" ref="model_yuthon_confirm_users"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.sync_confirm_users_notices()</field>
|
||||
<field eval="False" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
7
yuthon_notice/data/notice_type_data.xml
Normal file
7
yuthon_notice/data/notice_type_data.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="yuthon_notice_type1" model="yuthon.notice.type"><field name="name">通知</field></record>
|
||||
<record id="yuthon_notice_type2" model="yuthon.notice.type"><field name="name">公告</field></record>
|
||||
<record id="yuthon_notice_type3" model="yuthon.notice.type"><field name="name">决定</field></record>
|
||||
<record id="yuthon_notice_type4" model="yuthon.notice.type"><field name="name">其他</field></record>
|
||||
</odoo>
|
||||
6
yuthon_notice/models/__init__.py
Normal file
6
yuthon_notice/models/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import yuthon_notice
|
||||
from . import yuthon_notice_type
|
||||
from . import yuthon_confirm_users
|
||||
|
||||
38
yuthon_notice/models/yuthon_confirm_users.py
Normal file
38
yuthon_notice/models/yuthon_confirm_users.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class YuthonConfirmUsers(models.Model):
|
||||
_name = "yuthon.confirm.users"
|
||||
_description = "公告确认信息"
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string='员工')
|
||||
notice_id = fields.Many2one('yuthon.notice', string="公告名称")
|
||||
notice_code = fields.Char(string="公告编号")
|
||||
notice_name = fields.Char(string="公告名称")
|
||||
state = fields.Selection([('no', '未确认'), ('yes', '已确认')], string="状态")
|
||||
|
||||
def sync_confirm_users_notices(self):
|
||||
menu_id = self.env['ir.ui.menu'].search([('name', '=', '通知公告')])
|
||||
for record in self.search([('state', '=', 'no')]):
|
||||
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')
|
||||
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}'
|
||||
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', user_id=record.employee_id.wecom_userid, title="通知公告",
|
||||
description="您有待确认的通知公告请尽快确认", url=url, btntxt="详细信息")
|
||||
|
||||
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')))
|
||||
for rec in confirm_noice_ids:
|
||||
menu_id = self.env['ir.ui.menu'].search([('name', '=', '通知公告')])
|
||||
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}'
|
||||
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='notice', user_id=rec.employee_id.wecom_userid,
|
||||
title="通知公告(催办)", description="您有待确认的通知公告请尽快确认",
|
||||
url=url, btntxt="详细信息")
|
||||
partner_ids = rec.employee_id.user_id.partner_id.id
|
||||
rec.with_context(lang=rec.env.lang)._message_auto_subscribe_notify(partner_ids,
|
||||
'yuthon_notice.message_yuthon_confirm_users_data')
|
||||
206
yuthon_notice/models/yuthon_notice.py
Normal file
206
yuthon_notice/models/yuthon_notice.py
Normal file
@ -0,0 +1,206 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class YuthonNotice(models.Model):
|
||||
_name = "yuthon.notice"
|
||||
_description = '通知公告'
|
||||
_inherit = ['mail.thread.main.attachment', 'mail.activity.mixin']
|
||||
_rec_name = 'name'
|
||||
_order = "sequence desc"
|
||||
|
||||
name = fields.Char(string='标题')
|
||||
code = fields.Char(string="编号", default=lambda self: self.env['ir.sequence'].next_by_code('notice_code'))
|
||||
users_id = fields.Many2one('res.users', string="发布人", default=lambda self: self.env.user)
|
||||
company_id = fields.Many2one(related="users_id.employee_id.company_id", string="公司", store=True)
|
||||
department_id = fields.Many2one(related='users_id.employee_id.department_id', string="部门")
|
||||
notice_type_id = fields.Many2one('yuthon.notice.type', string="类型")
|
||||
create_date1 = fields.Date(string="发布日期", default=fields.Date.today())
|
||||
start_date = fields.Date(string="生效日期", default=fields.Date.today())
|
||||
end_date = fields.Date(string="终止日期")
|
||||
sequence = fields.Integer(string="顺序",compute='_compute_sequence',store=True)
|
||||
employee_ids = fields.Many2many('hr.employee', 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="公告状态")
|
||||
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||||
is_notice_roger = fields.Selection([('yes', '已读'), ('no', '未读')], default='no', string="确认状态")
|
||||
is_change_notice = fields.Boolean(string="是否发送通知", default=True)
|
||||
attn_notice = fields.Html(string="经办人意见")
|
||||
notice_text = fields.Html(string="正文")
|
||||
read_number = fields.Integer(string='阅读量', compute="_compute_read_number", store=True)
|
||||
is_this_company = fields.Boolean(string='本公司', default=True)
|
||||
approval_user_id = fields.Many2one('res.users', string="审核人")
|
||||
approval_date = fields.Datetime(string="审核日期")
|
||||
is_sticky = fields.Boolean(string="是否置顶")
|
||||
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度', default='normal')
|
||||
active = fields.Boolean('Active', default=True, tracking=True)
|
||||
|
||||
work_end = fields.Boolean(string="结束")
|
||||
users_ids = fields.Many2many(
|
||||
'res.users',
|
||||
relation='yuthon_notice_users_rel',
|
||||
column1='notice_id',
|
||||
column2='user_id',
|
||||
string='用户'
|
||||
)
|
||||
self_users_ids = fields.Many2many(
|
||||
'res.users',
|
||||
relation='yuthon_notice_current_users_rel',
|
||||
column1='notice_id',
|
||||
column2='user_id',
|
||||
string="当前经办人",
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for i in vals_list:
|
||||
if i['department_ids'] == i['employee_ids'] == i['users_role_ids'] == i['users_role_group_ids']:
|
||||
raise UserError('请填写发布的范围')
|
||||
return super().create(vals_list)
|
||||
|
||||
def _get_default_name_title(self):
|
||||
return self._description if self._description else self._name
|
||||
|
||||
name_title = fields.Char(default=_get_default_name_title)
|
||||
|
||||
@api.constrains('employee_ids', 'department_ids', 'users_role_ids', 'users_role_group_ids')
|
||||
def _constrains_employee_related_fields(self):
|
||||
|
||||
def get_department_employees(department_id):
|
||||
valid_user_ids = set()
|
||||
employee_ids = self.env['hr.employee'].search([('department_id', '=', department_id.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)])
|
||||
for child in child_ids:
|
||||
valid_user_ids.update(get_department_employees(child))
|
||||
return valid_user_ids
|
||||
|
||||
for notice in self:
|
||||
if notice.env.context.get('skip_constrains'):
|
||||
continue
|
||||
|
||||
department_user_ids = set()
|
||||
for department_id in notice.department_ids:
|
||||
department_user_ids.update(get_department_employees(department_id))
|
||||
group_role_ids = notice.users_role_group_ids
|
||||
all_users = set(
|
||||
notice.employee_ids.mapped('user_id').ids +
|
||||
group_role_ids.mapped('users').ids +
|
||||
notice.users_role_ids.mapped('users').ids +
|
||||
list(department_user_ids)
|
||||
)
|
||||
notice.with_context(skip_constrains=True).users_ids = [(6, 0, list(all_users))]
|
||||
|
||||
def get_no_read_count(self):
|
||||
return self.search_count([('is_notice_roger', '=', 'no'), ('notice_state', '=', 'yes')])
|
||||
|
||||
def read(self, fields=None, load='_classic_read'):
|
||||
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:
|
||||
con.is_notice_roger = 'yes'
|
||||
con_id.write({'state': 'yes'})
|
||||
return super(YuthonNotice, self).read(fields=fields, load=load)
|
||||
|
||||
@api.depends('read_number')
|
||||
def _compute_read_number(self):
|
||||
notice_confirm_ids = self.env['yuthon.confirm.users'].search(
|
||||
[('notice_id.id', '=', self.id), ('state', '=', 'yes')])
|
||||
for read in self:
|
||||
if notice_confirm_ids:
|
||||
read.read_number = len(notice_confirm_ids)
|
||||
else:
|
||||
read.read_number = 0
|
||||
|
||||
def on_sequence(self):
|
||||
self.sequence = 1
|
||||
|
||||
@api.onchange('is_sticky')
|
||||
def _compute_sequence(self):
|
||||
for i in self:
|
||||
if i.is_sticky:
|
||||
i.sequence = 1
|
||||
else:
|
||||
i.sequence = 0
|
||||
|
||||
def no_sequence(self):
|
||||
self.sequence = 0
|
||||
|
||||
def notice_publish(self):
|
||||
if self.is_change_notice:
|
||||
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('该公告不允许通知,请勾选通知')
|
||||
|
||||
|
||||
def look_up_notice_users(self):
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "通知公告确认信息",
|
||||
"domain": [("notice_id", "=", self.id)],
|
||||
"res_model": "yuthon.confirm.users",
|
||||
"view_mode": "tree",
|
||||
"context": {
|
||||
"search_default_group_state": True,
|
||||
},
|
||||
}
|
||||
|
||||
def stop_notice(self):
|
||||
self.write({
|
||||
'notice_state': 'stop',
|
||||
'end_date': fields.Date.today(),
|
||||
})
|
||||
|
||||
|
||||
class YuthonCsTree(models.Model):
|
||||
_name = 'yuthon.cs.tree'
|
||||
_description = 'Yuthon Customer Service Tree'
|
||||
|
||||
name = fields.Char(string='Name', required=True)
|
||||
|
||||
def _compute_sequence(self):
|
||||
for record in self:
|
||||
record.sequence = 1
|
||||
|
||||
|
||||
|
||||
|
||||
# 工作流的指定申请人逻辑?
|
||||
# 转交的时候选择条件,进行优化
|
||||
|
||||
# 报表合同和产品业务
|
||||
# 企业微信等三方平台框架--和工作流
|
||||
11
yuthon_notice/models/yuthon_notice_type.py
Normal file
11
yuthon_notice/models/yuthon_notice_type.py
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
|
||||
|
||||
class YuthonNoticeType(models.Model):
|
||||
_name = "yuthon.notice.type"
|
||||
_description = "通知公告类型管理"
|
||||
|
||||
name = fields.Char(string="类型名称")
|
||||
note = fields.Char(string="说明")
|
||||
6
yuthon_notice/security/ir.model.access.csv
Normal file
6
yuthon_notice/security/ir.model.access.csv
Normal file
@ -0,0 +1,6 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_yuthon_notice,yuthon_notice,model_yuthon_notice,,1,1,1,1
|
||||
access_yuthon_notice_type,yuthon_notice_type,model_yuthon_notice_type,base.group_user,1,1,1,1
|
||||
access_yuthon_confirm_users,yuthon_confirm_users,model_yuthon_confirm_users,base.group_user,1,1,1,1
|
||||
access_yuthon_cs_tree,yuthon_cs_tree,model_yuthon_cs_tree,base.group_user,1,1,1,1
|
||||
|
||||
|
BIN
yuthon_notice/static/description/icon.png
Normal file
BIN
yuthon_notice/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
5
yuthon_notice/static/scss/notice_scss.scss
Normal file
5
yuthon_notice/static/scss/notice_scss.scss
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
.list_ids_width{
|
||||
width: 80px !important;
|
||||
max-width: 80px !important;
|
||||
}
|
||||
29
yuthon_notice/static/src/js/custom_create_button.js
Normal file
29
yuthon_notice/static/src/js/custom_create_button.js
Normal file
@ -0,0 +1,29 @@
|
||||
/** @odoo-module */
|
||||
import { ListController } from "@web/views/list/list_controller";
|
||||
import { registry } from '@web/core/registry';
|
||||
import { listView } from '@web/views/list/list_view';
|
||||
|
||||
export class CustomNoticeListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
}
|
||||
openNoticeForm() {
|
||||
this.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'yuthon.notice',
|
||||
name: '创建通知公告',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']], // 让 Odoo 找到默认的 form 视图
|
||||
target: 'current', // 在当前窗口打开
|
||||
context: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("views").add("custom_notice_tree_button", {
|
||||
...listView,
|
||||
Controller: CustomNoticeListController,
|
||||
buttonTemplate: "yuthon_notice.ListView.Buttons",
|
||||
});
|
||||
|
||||
10
yuthon_notice/static/src/xml/custom_create_button.xml
Normal file
10
yuthon_notice/static/src/xml/custom_create_button.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
<t t-name="yuthon_notice.ListView.Buttons" t-inherit="web.ListView.Buttons">
|
||||
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
|
||||
<button type="button" class="btn btn-primary" style="margin-left: 10px;" t-on-click="openNoticeForm">
|
||||
新建
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
38
yuthon_notice/views/yuthon_confirm_users_views.xml
Normal file
38
yuthon_notice/views/yuthon_confirm_users_views.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_yuthon_confirm_users_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.confirm.users.tree</field>
|
||||
<field name="model">yuthon.confirm.users</field>
|
||||
<field name="arch" type="xml">
|
||||
<list create="false" edit="false"
|
||||
string="公告确认单" js_class="custom_notice_tree_button" editable="bottom">
|
||||
<header>
|
||||
<button class="oe_stat_button" name="reminders_notice" string="批量催办" type="object" icon="fa-bars"/>
|
||||
</header>
|
||||
<field name="employee_id" widget="ztree_select"/>
|
||||
<field name="notice_id"/>
|
||||
<field name="notice_code"/>
|
||||
<field name="state"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="yuthon_confirm_users_filter" model="ir.ui.view">
|
||||
<field name="name">yuthon.confirm.users.search</field>
|
||||
<field name="model">yuthon.confirm.users</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="公告查询">
|
||||
<group expand="0">
|
||||
<filter name="group_state" string="确认状态" context="{'group_by': 'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="yuthon_confirm_users_action" model="ir.actions.act_window">
|
||||
<field name="name">公告确认信息</field>
|
||||
<field name="res_model">yuthon.confirm.users</field>
|
||||
<field name="view_mode">list</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
17
yuthon_notice/views/yuthon_cs_tree_views.xml
Normal file
17
yuthon_notice/views/yuthon_cs_tree_views.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_yuthon_cs_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.cs.tree</field>
|
||||
<field name="model">yuthon.cs.tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Yuthon CS Tree">
|
||||
<field name="name"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_yuthon_cs_tree" model="ir.actions.act_window">
|
||||
<field name="name">Yuthon CS Tree</field>
|
||||
<field name="res_model">yuthon.cs.tree</field>
|
||||
<field name="view_mode">list</field>
|
||||
</record>
|
||||
</odoo>
|
||||
33
yuthon_notice/views/yuthon_notice_type_views.xml
Normal file
33
yuthon_notice/views/yuthon_notice_type_views.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_yuthon_notice_type_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.notice.type.tree</field>
|
||||
<field name="model">yuthon.notice.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="公告类型" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="note"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="approval_notice_type_action" model="ir.actions.act_window">
|
||||
<field name="name">公告类型管理</field>
|
||||
<field name="res_model">yuthon.notice.type</field>
|
||||
<field name="view_mode">list</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="yuthon_notice_type_setting"
|
||||
parent="yuthon_notice_menu_config_root"
|
||||
name="配置"
|
||||
sequence="7"/>
|
||||
|
||||
<menuitem id="yuthon_notice_type_menu"
|
||||
parent="yuthon_notice_type_setting"
|
||||
name="公告类型"
|
||||
action="approval_notice_type_action"
|
||||
sequence="1"/>
|
||||
|
||||
</odoo>
|
||||
216
yuthon_notice/views/yuthon_notice_views.xml
Normal file
216
yuthon_notice/views/yuthon_notice_views.xml
Normal file
@ -0,0 +1,216 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_yuthon_notice_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.notice.tree</field>
|
||||
<field name="model">yuthon.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="公告管理" decoration-success="notice_state == 'yes'"
|
||||
decoration-info="notice_state == 'no'"
|
||||
decoration-muted="notice_state == 'stop'">
|
||||
<field name="name" required="1"/>
|
||||
<field name="notice_type_id" string="类型" class="list_ids_width"/>
|
||||
<field name="users_id" string="发布人" class="list_ids_width"/>
|
||||
<field name="department_id" column_invisible="1"/>
|
||||
<field name="users_ids" column_invisible="1"/>
|
||||
<field name="create_date1"/>
|
||||
<field name="end_date"/>
|
||||
<field name="notice_state" class="list_ids_width"/>
|
||||
<button name="on_sequence" string="置顶" invisible="sequence != 0" type="object" class="btn-primary"/>
|
||||
<button name="no_sequence" string="取消置顶" invisible="sequence != 1" type="object" class="btn-primary"/>
|
||||
<!-- <button name="look_up_notice_users" string="查阅情况" type="object" class="btn-primary"/>-->
|
||||
<button name="stop_notice" string="终止" type="object" class="btn-primary"/>
|
||||
<field name="sequence" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_yuthon_notice_form" model="ir.ui.view">
|
||||
<field name="name">yuthon.notice.form</field>
|
||||
<field name="model">yuthon.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="公告管理" create="0">
|
||||
<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>
|
||||
<field name="code" string="流水号" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="urgency_level" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_this_company"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="start_date"/>
|
||||
</group>
|
||||
</group>
|
||||
<div class="col-12 text-center">
|
||||
<h1>
|
||||
<field name="name_title" readonly="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group col="4">
|
||||
<group>
|
||||
<field name="notice_type_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="users_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="work_end" invisible="1"/>
|
||||
<field name="department_id" string="发布部门" invisible="1"/>
|
||||
<field name="create_date1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="name" required="1"/>
|
||||
<field name="notice_text" widget="tinymce"/>
|
||||
<field name="documents_ids" widget="preview_many2many"/>
|
||||
</group>
|
||||
<group col="4" string="发布范围">
|
||||
<group>
|
||||
<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}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="users_role_group_ids" widget="many2many_tags" 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" 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 col="3">
|
||||
<group>
|
||||
<field name="is_sticky" string="置顶"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_change_notice"/>
|
||||
</group>
|
||||
<group>
|
||||
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_yuthon_notice_tree2" model="ir.ui.view">
|
||||
<field name="name">yuthon.notice.tree</field>
|
||||
<field name="model">yuthon.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="公告管理" edit="false" delete="false" create="false"
|
||||
decoration-danger="is_notice_roger == 'no'"
|
||||
decoration-success="is_notice_roger == 'yes'"
|
||||
js_class="custom_notice_tree_button">
|
||||
<field name="name"/>
|
||||
<field name="notice_type_id" string="类型"/>
|
||||
<field name="users_id" string="发布人" class="list_ids_width"/>
|
||||
<field name="is_notice_roger"/>
|
||||
<field name="department_id" column_invisible="1"/>
|
||||
<field name="users_ids" column_invisible="1"/>
|
||||
<field name="create_date1"/>
|
||||
<field name="end_date" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_yuthon_notice_form2" model="ir.ui.view">
|
||||
<field name="name">yuthon.notice.form2</field>
|
||||
<field name="model">yuthon.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<form create="false" edit="false" delete="false" string="公告管理">
|
||||
<group col="5">
|
||||
<group>
|
||||
<field name="notice_type_id" string="发布类型"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="users_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="read_number"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="start_date"/>
|
||||
</group>
|
||||
</group>
|
||||
<div class="col-12 text-center">
|
||||
<h1>
|
||||
<field name="name" required="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="notice_text" widget="tinymce" string=" "/>
|
||||
<field name="documents_ids" widget="preview_many2many"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_yuthon_notice_filter" model="ir.ui.view">
|
||||
<field name="name">yuthon.notice.search</field>
|
||||
<field name="model">yuthon.notice</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="公告查询">
|
||||
<field string="标题" name="name"/>
|
||||
<field string="发布人" name="users_id"/>
|
||||
<field string="正文" name="notice_text"/>
|
||||
<field string="附件" name="documents_ids"/>
|
||||
<field string="部门" name="department_id"/>
|
||||
<filter string="未发布" name="no " domain="[('notice_state', '=', 'no')]"/>
|
||||
<filter string="已发布" name="done " domain="[('notice_state', '=', 'done')]"/>
|
||||
<group expand="0">
|
||||
<filter name="group_statu" string="发布状态" context="{'group_by': 'notice_state'}"/>
|
||||
<filter name="group_type" string="类型" context="{'group_by': 'notice_type_id'}"/>
|
||||
<filter name="group_date" string="发布日期" context="{'group_by': 'create_date1'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="approval_notice_action" model="ir.actions.act_window">
|
||||
<field name="name">通知公告</field>
|
||||
<field name="res_model">yuthon.notice</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),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_notice.view_yuthon_notice_tree2')}),
|
||||
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_notice.view_yuthon_notice_form2')})]"/>
|
||||
</record>
|
||||
|
||||
<record id="approval_notice_action2" model="ir.actions.act_window">
|
||||
<field name="name">通知公告管理</field>
|
||||
<field name="res_model">yuthon.notice</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="yuthon_notice_menu_config_root"
|
||||
name="通知公告"
|
||||
web_icon="yuthon_notice,static/description/icon.png"
|
||||
sequence="1"/>
|
||||
|
||||
<!-- <menuitem id="yuthon_notice_menu1"-->
|
||||
<!-- parent="yuthon_notice_menu_config_root"-->
|
||||
<!-- name="通知公告"-->
|
||||
<!-- action="approval_notice_action"-->
|
||||
<!-- sequence="1"/>-->
|
||||
|
||||
<menuitem id="yuthon_notice_my_menu"
|
||||
parent="yuthon_notice_menu_config_root"
|
||||
name="通知公告管理"
|
||||
action="approval_notice_action2"
|
||||
sequence="2"/>
|
||||
</odoo>
|
||||
2
yuthon_will_task/__init__.py
Normal file
2
yuthon_will_task/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
31
yuthon_will_task/__manifest__.py
Normal file
31
yuthon_will_task/__manifest__.py
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "日程",
|
||||
'summary': "日程事项",
|
||||
'description': """""",
|
||||
'author': 'zhou',
|
||||
'version': '18.0.1.0.0',
|
||||
'license': 'LGPL-3',
|
||||
'data': [
|
||||
# 'data/will_task_time_data.xml',
|
||||
# 'data/task_code_data.xml',
|
||||
# 'data/mail_template.xml',
|
||||
# 'data/tsak_time_data.xml',
|
||||
'security/yuthon_will_task_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/yuthon_will_task_views.xml',
|
||||
'views/yuthon_dates_task_views.xml',
|
||||
'views/yuthon_task_time_views.xml',
|
||||
'views/yuthon_task_menu.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'yuthon_will_task/static/src/js/task_create_button.js',
|
||||
'yuthon_will_task/static/src/xml/task_create_button.xml',
|
||||
],
|
||||
},
|
||||
'demo': [],
|
||||
'depends': ['hr', 'mail'],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
}
|
||||
1
yuthon_will_task/controllers/__init__.py
Normal file
1
yuthon_will_task/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import main
|
||||
282
yuthon_will_task/controllers/main.py
Normal file
282
yuthon_will_task/controllers/main.py
Normal file
@ -0,0 +1,282 @@
|
||||
import base64
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from odoo import http, fields, Command
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class YuthonWillTask(http.Controller):
|
||||
|
||||
@http.route('/yuthon/yuthon/will/task', methods=['POST'], type='http', auth='none', csrf=False)
|
||||
def index2(self, **kwargs):
|
||||
"""根据ID获取日程详情"""
|
||||
id = kwargs.get('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 = []
|
||||
for em in data.employee_ids:
|
||||
employee_ids.append({
|
||||
'id': em.id,
|
||||
'name': em.name,
|
||||
'image_1920': em.image_128.decode('utf-8') if em.image_128 else '',
|
||||
})
|
||||
result = {
|
||||
'id': data.id,
|
||||
'name': data.name,
|
||||
'code': data.code,
|
||||
'start_date': fields.Date.to_string(data.start_date) if data.start_date else '',
|
||||
'request_hour_from': data.request_hour_from,
|
||||
'request_hour_to': data.request_hour_to,
|
||||
'priority': data.priority,
|
||||
'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 '',
|
||||
'remarks': data.remarks,
|
||||
'address': data.address,
|
||||
'employee_ids': employee_ids,
|
||||
'file_list': file_list,
|
||||
}
|
||||
|
||||
# 转换 request_hour_from 和 request_hour_to 的显示值
|
||||
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']
|
||||
|
||||
for f in request_hour_from_selection:
|
||||
if f[0] == data.request_hour_from:
|
||||
result['request_hour_from'] = f[1]
|
||||
for f in request_hour_to_selection:
|
||||
if f[0] == data.request_hour_to:
|
||||
result['request_hour_to'] = f[1]
|
||||
|
||||
# 计算 start_date 星期几
|
||||
if data.start_date:
|
||||
result['start_date_week'] = data.start_date.strftime('%A')
|
||||
|
||||
return json.dumps({
|
||||
'data': result,
|
||||
'code': 200,
|
||||
'message': 'success'
|
||||
})
|
||||
|
||||
@http.route('/will/task/list', methods=['POST'], type='http', auth='public', csrf=False)
|
||||
def will_task_list(self, **kwargs):
|
||||
"""获取日程列表(手机端接口)"""
|
||||
start = int(kwargs.get('start'))
|
||||
users_id = int(kwargs.get('users_id'))
|
||||
_filter = kwargs.get('filter')
|
||||
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 = []
|
||||
task_ids = request.env['yuthon.will.task'].sudo().search(domain, offset=start, limit=20, order=order)
|
||||
user_id = request.env['res.users'].sudo().search([('id', '=', users_id)])
|
||||
company_id = user_id.company_id
|
||||
for task in task_ids:
|
||||
if company_id.id == task.company_id.id:
|
||||
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
||||
result = {
|
||||
'id': task.id,
|
||||
'name': task.name,
|
||||
'employee_id': task.employee_id.name or '',
|
||||
'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 '',
|
||||
'start_date': fields.Date.to_string(task.start_date) if task.start_date else '',
|
||||
'weekday': weekday_str,
|
||||
'address': task.address or '',
|
||||
'request_hour_from': '',
|
||||
'request_hour_to': '',
|
||||
'state': task.task_state,
|
||||
'display_state': state_dict.get(task.task_state),
|
||||
'task_type': task.task_type
|
||||
}
|
||||
hour_dict = dict(request_hour_selection)
|
||||
result['request_hour_from'] = hour_dict.get(task.request_hour_from)
|
||||
result['request_hour_to'] = hour_dict.get(task.request_hour_to)
|
||||
result_list.append(result)
|
||||
return json.dumps({
|
||||
'data': result_list,
|
||||
'code': 200,
|
||||
'message': 'success'
|
||||
})
|
||||
|
||||
@http.route('/will/task/record', methods=['POST'], type='http', auth='public', csrf=False)
|
||||
def will_task_record(self, **kwargs):
|
||||
"""获取单个日程记录详情"""
|
||||
task_id = kwargs.get('id')
|
||||
task = request.env['yuthon.will.task'].sudo().search([('id', '=', task_id)])
|
||||
request_hour_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
||||
file_list = []
|
||||
pdf = ['application/pdf']
|
||||
for file in task.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,
|
||||
})
|
||||
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
||||
result = {
|
||||
'id': task.id,
|
||||
'name': task.name,
|
||||
'employee_id': task.employee_id.name,
|
||||
'employee_ids': ','.join(task.employee_ids.mapped('name')),
|
||||
'task_time_ids': ','.join(task.task_time_ids.mapped('name')),
|
||||
'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,
|
||||
'request_hour_from': task.request_hour_from,
|
||||
'request_hour_to': task.request_hour_to,
|
||||
'remarks': task.remarks,
|
||||
'address': task.address,
|
||||
'file_list': file_list,
|
||||
'priority': task.priority,
|
||||
}
|
||||
|
||||
hour_dict = dict(request_hour_selection)
|
||||
result['request_hour_from'] = hour_dict.get(task.request_hour_from)
|
||||
result['request_hour_to'] = hour_dict.get(task.request_hour_to)
|
||||
return json.dumps({
|
||||
'data': result,
|
||||
'code': 200,
|
||||
'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': ''})
|
||||
18
yuthon_will_task/data/mail_template.xml
Normal file
18
yuthon_will_task/data/mail_template.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<template id="mail_action_yuthon_will_task_add_comment">
|
||||
<p style="margin: 0px;">
|
||||
<span>日程事项提醒,</span><br />
|
||||
<t t-set="name" t-value="'%s' % (object.name)"/>
|
||||
<span style="margin-top: 8px;">请注意你的日程事项 <t t-esc="name" />!
|
||||
</span>
|
||||
</p>
|
||||
<p style="margin-top: 8px; margin-bottom: 10px;">
|
||||
<a t-att-href="access_link" t-att-data-oe-model="object._name" t-att-data-oe-id="object.id" style="background-color:#2C65F7; padding: 5px; text-decoration: none; color: #fff; border-radius: 5px;">
|
||||
详细信息 <t t-esc="model_description or '详情'"/>
|
||||
</a>
|
||||
</p>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
10
yuthon_will_task/data/task_code_data.xml
Normal file
10
yuthon_will_task/data/task_code_data.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="task_code_sequence" model="ir.sequence">
|
||||
<field name="name">日程编号</field>
|
||||
<field name="code">task_code</field>
|
||||
<field name="prefix">RC%(y)s%(month)s%(day)s</field>
|
||||
<field name="padding">3</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
</odoo>
|
||||
28
yuthon_will_task/data/tsak_time_data.xml
Normal file
28
yuthon_will_task/data/tsak_time_data.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="yuthon_task_time0" model="yuthon.task.time">
|
||||
<field name="name">15分钟</field>
|
||||
<field name="number">15</field>
|
||||
<field name="task_unit">min</field>
|
||||
</record>
|
||||
<record id="yuthon_task_time1" model="yuthon.task.time">
|
||||
<field name="name">30分钟</field>
|
||||
<field name="number">30</field>
|
||||
<field name="task_unit">min</field>
|
||||
</record>
|
||||
<record id="yuthon_task_time2" model="yuthon.task.time">
|
||||
<field name="name">1小时</field>
|
||||
<field name="number">1</field>
|
||||
<field name="task_unit">hour</field>
|
||||
</record>
|
||||
<record id="yuthon_task_time3" model="yuthon.task.time">
|
||||
<field name="name">2小时</field>
|
||||
<field name="number">2</field>
|
||||
<field name="task_unit">hour</field>
|
||||
</record>
|
||||
<record id="yuthon_task_time4" model="yuthon.task.time">
|
||||
<field name="name">1天</field>
|
||||
<field name="number">1</field>
|
||||
<field name="task_unit">day</field>
|
||||
</record>
|
||||
</odoo>
|
||||
31
yuthon_will_task/data/will_task_time_data.xml
Normal file
31
yuthon_will_task/data/will_task_time_data.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record forcecreate="True" id="will_task_time_data" model="ir.cron">
|
||||
<field name="name">定时任务:日程任务提醒</field>
|
||||
<field name="model_id" ref="model_yuthon_will_task"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.add_comment()</field>
|
||||
<field eval="False" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="nextcall" eval="(DateTime.now() + timedelta(minutes=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
<record forcecreate="True" id="add_done_time_data" model="ir.cron">
|
||||
<field name="name">定时任务:日程待办自动转为已办</field>
|
||||
<field name="model_id" ref="model_yuthon_will_task"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.add_done()</field>
|
||||
<field eval="False" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 00:00:00')"/>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
3
yuthon_will_task/models/__init__.py
Normal file
3
yuthon_will_task/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from . import yuthon_will_task
|
||||
from . import yuthon_task_time
|
||||
from . import yuthon_dates_task
|
||||
11
yuthon_will_task/models/yuthon_dates_task.py
Normal file
11
yuthon_will_task/models/yuthon_dates_task.py
Normal file
@ -0,0 +1,11 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class YuthonDatesTask(models.Model):
|
||||
_name = "yuthon.dates.task"
|
||||
_description = "日程任务"
|
||||
|
||||
reminder_time = fields.Datetime(string='提醒时间')
|
||||
yuthon_will_task_id = fields.Many2one('yuthon.will.task', string='日程')
|
||||
employee_ids = fields.Many2many('hr.employee', string='提醒人员')
|
||||
task_state = fields.Selection([('to_do', '待提醒'), ('done', '已提醒')], string='状态')
|
||||
20
yuthon_will_task/models/yuthon_task_time.py
Normal file
20
yuthon_will_task/models/yuthon_task_time.py
Normal file
@ -0,0 +1,20 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class YuthonTaskTime(models.Model):
|
||||
_name = 'yuthon.task.time'
|
||||
_description = '提醒时间配置'
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char(string='名称')
|
||||
number = fields.Integer(string="数值")
|
||||
task_unit = fields.Selection([('min', '分钟'), ('hour', '小时'), ('day', '天')], string="单位")
|
||||
sequence = fields.Integer(string="序号")
|
||||
|
||||
@api.onchange('number', 'task_unit')
|
||||
def onchange_name(self):
|
||||
"""根据数值和单位自动生成名称"""
|
||||
task_unit_dict = {'min': '分钟', 'hour': '小时', 'day': '天'}
|
||||
for n in self:
|
||||
if n.number and n.task_unit:
|
||||
n.name = str(n.number) + task_unit_dict[n.task_unit]
|
||||
397
yuthon_will_task/models/yuthon_will_task.py
Normal file
397
yuthon_will_task/models/yuthon_will_task.py
Normal file
@ -0,0 +1,397 @@
|
||||
from odoo import api, fields, models
|
||||
from datetime import datetime, timedelta, time, date
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class YuthonWillTask(models.Model):
|
||||
_name = 'yuthon.will.task'
|
||||
_description = '日程'
|
||||
_rec_name = 'name'
|
||||
_order = 'task_datetime desc'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
time_list = [
|
||||
('0', '0:00'), ('0.25', '0:15'), ('0.5', '0:30'), ('0.75', '0:45'),
|
||||
('1', '01:00'), ('1.25', '01:15'), ('1.5', '01:30'), ('1.75', '01:45'),
|
||||
('2', '02:00'), ('2.25', '02:15'), ('2.5', '02:30'), ('2.75', '02:45'),
|
||||
('3', '03:00'), ('3.25', '03:15'), ('3.5', '03:30'), ('3.75', '03:45'),
|
||||
('4', '04:00'), ('4.25', '04:15'), ('4.5', '04:30'), ('4.75', '04:45'),
|
||||
('5', '05:00'), ('5.25', '05:15'), ('5.5', '05:30'), ('5.75', '05:45'),
|
||||
('6', '06:00'), ('6.25', '06:15'), ('6.5', '06:30'), ('6.75', '06:45'),
|
||||
('7', '07:00'), ('7.25', '07:15'), ('7.5', '07:30'), ('7.75', '07:45'),
|
||||
('8', '08:00'), ('8.25', '08:15'), ('8.5', '08:30'), ('8.75', '08:45'),
|
||||
('9', '09:00'), ('9.25', '09:15'), ('9.5', '09:30'), ('9.75', '09:45'),
|
||||
('10', '10:00'), ('10.25', '10:15'), ('10.5', '10:30'), ('10.75', '10:45'),
|
||||
('11', '11:00'), ('11.25', '11:15'), ('11.5', '11:30'), ('11.75', '11:45'),
|
||||
('12', '12:00'), ('12.25', '12:15'), ('12.5', '12:30'), ('12.75', '12:45'),
|
||||
('13', '13:00'), ('13.25', '13:15'), ('13.5', '13:30'), ('13.75', '13:45'),
|
||||
('14', '14:00'), ('14.25', '14:15'), ('14.5', '14:30'), ('14.75', '14:45'),
|
||||
('15', '15:00'), ('15.25', '15:15'), ('15.5', '15:30'), ('15.75', '15:45'),
|
||||
('16', '16:00'), ('16.25', '16:15'), ('16.5', '16:30'), ('16.75', '16:45'),
|
||||
('17', '17:00'), ('17.25', '17:15'), ('17.5', '17:30'), ('17.75', '17:45'),
|
||||
('18', '18:00'), ('18.25', '18:15'), ('18.5', '18:30'), ('18.75', '18:45'),
|
||||
('19', '19:00'), ('19.25', '19:15'), ('19.5', '19:30'), ('19.75', '19:45'),
|
||||
('20', '20:00'), ('20.25', '20:15'), ('20.5', '20:30'), ('20.75', '20: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'),
|
||||
('23', '23:00'), ('23.25', '23:15'), ('23.5', '23:30'), ('23.75', '23:45')]
|
||||
|
||||
def _default_request_hour_from(self):
|
||||
"""开始时间默认当前小时(处理UTC+8时区)"""
|
||||
now = fields.Datetime.now() + timedelta(hours=8)
|
||||
hour = now.hour
|
||||
return str(hour)
|
||||
|
||||
def _default_request_hour_to(self):
|
||||
"""结束时间默认当前小时+1(23+1=0,处理UTC+8时区)"""
|
||||
now = fields.Datetime.now() + timedelta(hours=8)
|
||||
hour = (now.hour + 1) % 24
|
||||
return str(hour)
|
||||
|
||||
request_hour_from = fields.Selection(time_list, string='开始时间', default=_default_request_hour_from)
|
||||
request_hour_to = fields.Selection(time_list, string='结束时间', default=_default_request_hour_to)
|
||||
priority = fields.Selection([('important', '重要不紧急'), ('urgent', '紧急不重要'), ('very_urgent', '重要且紧急')], string='优先级')
|
||||
code = fields.Char(string="任务编号", default=lambda self: self.env['ir.sequence'].next_by_code('task_code'))
|
||||
user_id = fields.Many2one('res.users', string="创建人", default=lambda self: self.env.user)
|
||||
company_id = fields.Many2one(related="employee_id.company_id", string="公司", store=True)
|
||||
name = fields.Text(string='任务内容')
|
||||
remarks = fields.Text(string='备注')
|
||||
employee_ids_short = fields.Char(string='参与人简略', compute='_compute_employee_ids_short')
|
||||
creation_time = fields.Date(string='创建时间', default=fields.Date.today())
|
||||
employee_ids = fields.Many2many('hr.employee', 'will_task_employee_rel', 'custom_id', 'employee_id',
|
||||
string='人员', store=True, index=True)
|
||||
task_time_ids = fields.Many2many('yuthon.task.time', string='提醒')
|
||||
document_ids = fields.Many2many('ir.attachment', string="附件")
|
||||
address = fields.Char(string='地点')
|
||||
start_date = fields.Date(string='开始日期', default=fields.Date.today)
|
||||
end_date = fields.Date(string='结束日期', default=fields.Date.today)
|
||||
task_time = fields.Char(string='任务时间', compute='_compute_task_time', store=True)
|
||||
task_datetime = fields.Datetime(string='任务时间排序', compute='_compute_task_datetime', store=True)
|
||||
is_this_company = fields.Boolean(string='本公司', default=True)
|
||||
task_state = fields.Selection([('will', '待办'), ('done', '已办'), ('cancel', '取消')], default='will', string='状态', store=True)
|
||||
|
||||
users_ids = fields.Many2many('res.users', relation='yuthon_will_task_users_rel', column1='task_id', column2='user_id',
|
||||
string='用户', store=True, index=True, default=lambda self: self.env.user)
|
||||
|
||||
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])]
|
||||
)
|
||||
|
||||
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_week2 = fields.Char(string="周", readonly=True, compute='_compute_task_week2')
|
||||
task_type = fields.Selection([
|
||||
('task', '日程'),
|
||||
('meet', '会议')
|
||||
], string='类型', default='task')
|
||||
|
||||
def update_task_state(self):
|
||||
"""将选中日程标记为已办"""
|
||||
will_ids = self.env["yuthon.will.task"].browse(
|
||||
self._context.get('active_ids', self._context.get('active_id')))
|
||||
for will in will_ids:
|
||||
will.task_state = 'done'
|
||||
|
||||
@api.onchange('start_date', 'end_date')
|
||||
def _onchange_date_range(self):
|
||||
"""校验开始日期不能大于结束日期"""
|
||||
if self.start_date and self.end_date:
|
||||
if self.start_date > self.end_date:
|
||||
raise ValidationError("开始日期必须小于结束日期,请重新设置。")
|
||||
|
||||
def open_record(self):
|
||||
"""打开当前日程记录的表单视图"""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': '日程详情',
|
||||
'res_model': self._name,
|
||||
'res_id': self.id,
|
||||
'view_mode': 'form',
|
||||
'view_id': self.env.ref('yuthon_will_task.view_yuthon_will_task_form').id,
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
@api.depends('employee_ids')
|
||||
def _compute_employee_ids_short(self):
|
||||
"""计算参与人简略显示(最多2人)"""
|
||||
for record in self:
|
||||
names = record.employee_ids.mapped('name')[:2]
|
||||
record.employee_ids_short = '、'.join(names)
|
||||
|
||||
@api.depends('start_date')
|
||||
def _compute_task_week(self):
|
||||
"""计算开始日期对应的星期"""
|
||||
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
for record in self:
|
||||
if record.start_date:
|
||||
record.task_week = weekdays[record.start_date.weekday()]
|
||||
|
||||
@api.depends('end_date')
|
||||
def _compute_task_week2(self):
|
||||
"""计算结束日期对应的星期"""
|
||||
weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
|
||||
for record in self:
|
||||
if record.end_date:
|
||||
record.task_week2 = weekdays[record.end_date.weekday()]
|
||||
|
||||
def one_time(self):
|
||||
"""手动触发计算任务时间"""
|
||||
will_ids = self.env["yuthon.will.task"].browse(
|
||||
self._context.get('active_ids', self._context.get('active_id')))
|
||||
for record in will_ids:
|
||||
record._compute_task_time()
|
||||
|
||||
@api.depends('start_date', 'request_hour_from')
|
||||
def _compute_task_datetime(self):
|
||||
"""将 start_date + request_hour_from 拼合为 Datetime,用于排序"""
|
||||
for record in self:
|
||||
if record.start_date and record.request_hour_from:
|
||||
try:
|
||||
hour_float = float(record.request_hour_from)
|
||||
hours = int(hour_float)
|
||||
minutes = int(round((hour_float - hours) * 60))
|
||||
# 存储时转为UTC(界面显示是UTC+8,存储需减8小时)
|
||||
record.task_datetime = datetime(
|
||||
record.start_date.year,
|
||||
record.start_date.month,
|
||||
record.start_date.day,
|
||||
hours, minutes, 0
|
||||
) - timedelta(hours=8)
|
||||
except Exception:
|
||||
record.task_datetime = False
|
||||
else:
|
||||
record.task_datetime = False
|
||||
|
||||
@api.depends('start_date', 'task_week', 'request_hour_from', 'task_type')
|
||||
def _compute_task_time(self):
|
||||
"""计算任务时间显示文本"""
|
||||
for record in self:
|
||||
date_str = ""
|
||||
if record.start_date:
|
||||
date_str = record.start_date.strftime("%Y/%m/%d")
|
||||
hour_mapping = {
|
||||
'0': '0:00', '0.25': '0:15', '0.5': '0:30', '0.75': '0:45',
|
||||
'1': '01:00', '1.25': '01:15', '1.5': '01:30', '1.75': '01:45',
|
||||
'2': '02:00', '2.25': '02:15', '2.5': '02:30', '2.75': '02:45',
|
||||
'3': '03:00', '3.25': '03:15', '3.5': '03:30', '3.75': '03:45',
|
||||
'4': '04:00', '4.25': '04:15', '4.5': '04:30', '4.75': '04:45',
|
||||
'5': '05:00', '5.25': '05:15', '5.5': '05:30', '5.75': '05:45',
|
||||
'6': '06:00', '6.25': '06:15', '6.5': '06:30', '6.75': '06:45',
|
||||
'7': '07:00', '7.25': '07:15', '7.5': '07:30', '7.75': '07:45',
|
||||
'8': '08:00', '8.25': '08:15', '8.5': '08:30', '8.75': '08:45',
|
||||
'9': '09:00', '9.25': '09:15', '9.5': '09:30', '9.75': '09:45',
|
||||
'10': '10:00', '10.25': '10:15', '10.5': '10:30', '10.75': '10:45',
|
||||
'11': '11:00', '11.25': '11:15', '11.5': '11:30', '11.75': '11:45',
|
||||
'12': '12:00', '12.25': '12:15', '12.5': '12:30', '12.75': '12:45',
|
||||
'13': '13:00', '13.25': '13:15', '13.5': '13:30', '13.75': '13:45',
|
||||
'14': '14:00', '14.25': '14:15', '14.5': '14:30', '14.75': '14:45',
|
||||
'15': '15:00', '15.25': '15:15', '15.5': '15:30', '15.75': '15:45',
|
||||
'16': '16:00', '16.25': '16:15', '16.5': '16:30', '16.75': '16:45',
|
||||
'17': '17:00', '17.25': '17:15', '17.5': '17:30', '17.75': '17:45',
|
||||
'18': '18:00', '18.25': '18:15', '18.5': '18:30', '18.75': '18:45',
|
||||
'19': '19:00', '19.25': '19:15', '19.5': '19:30', '19.75': '19:45',
|
||||
'20': '20:00', '20.25': '20:15', '20.5': '20:30', '20.75': '20: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',
|
||||
'23': '23:00', '23.25': '23:15', '23.5': '23:30', '23.75': '23:45'
|
||||
}
|
||||
from_time = hour_mapping.get(record.request_hour_from, "")
|
||||
if date_str and from_time:
|
||||
base_time = f"{date_str} {record.task_week} {from_time}"
|
||||
record.task_time = base_time
|
||||
else:
|
||||
record.task_time = ""
|
||||
|
||||
@api.onchange('employee_ids', 'user_id')
|
||||
def _onchange_employee_ids(self):
|
||||
"""人员变更时同步更新关联用户列表"""
|
||||
user_ids = []
|
||||
if self.user_id:
|
||||
user_ids.append(self.user_id.id)
|
||||
for employee in self.employee_ids:
|
||||
if employee.user_id:
|
||||
user_ids.append(employee.user_id.id)
|
||||
self.users_ids = [(6, 0, user_ids)]
|
||||
|
||||
def read_number(self):
|
||||
"""获取当前用户的待办日程数量"""
|
||||
return self.search_count([('task_state', '=', 'will'), ('users_ids', 'in', self.env.user.id)])
|
||||
|
||||
def _compute_duration(self, task_time):
|
||||
"""根据提醒时间配置计算时间差"""
|
||||
if task_time.task_unit == 'min':
|
||||
return timedelta(minutes=task_time.number)
|
||||
elif task_time.task_unit == 'hour':
|
||||
return timedelta(hours=task_time.number)
|
||||
elif task_time.task_unit == 'day':
|
||||
return timedelta(days=task_time.number)
|
||||
else:
|
||||
return timedelta.max
|
||||
|
||||
def add_done(self):
|
||||
"""定时任务:将过期的待办日程自动标记为已办"""
|
||||
today = date.today()
|
||||
records = self.search([
|
||||
('task_state', '=', 'will'),
|
||||
('end_date', '<', today),
|
||||
('end_date', '!=', False),
|
||||
])
|
||||
if records:
|
||||
records.write({'task_state': 'done'})
|
||||
|
||||
def add_comment(self):
|
||||
"""日程定时任务提醒——通过 Odoo 内部消息通知"""
|
||||
_logger.info('-----------日程任务验证--------')
|
||||
now = fields.Datetime.now() + timedelta(hours=8)
|
||||
dates_task_ids = self.env['yuthon.dates.task'].search([
|
||||
('task_state', '=', 'to_do'),
|
||||
('reminder_time', '<', fields.Datetime.now() + timedelta(days=1))
|
||||
])
|
||||
for task in dates_task_ids:
|
||||
hour1 = (task.reminder_time + timedelta(hours=8)).hour
|
||||
minute1 = (task.reminder_time + timedelta(hours=8)).minute
|
||||
_logger.info(f'-任务时间{hour1}:{minute1}当前时间{now.hour}:{now.minute}')
|
||||
_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:
|
||||
task.write({'task_state': 'done'})
|
||||
will_id = task.yuthon_will_task_id
|
||||
will_id.task_state = 'done'
|
||||
# 构建提醒消息内容
|
||||
request_hour_from = dict(task.yuthon_will_task_id._fields['request_hour_from'].selection).get(
|
||||
task.yuthon_will_task_id.request_hour_from, '')
|
||||
_weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
_start = task.yuthon_will_task_id.start_date
|
||||
_week_str = _weekdays[_start.weekday()] if _start else ''
|
||||
data_date = '开始时间: ' + str(_start) + ' ' + _week_str + '--' + str(request_hour_from) + '\n'
|
||||
data_name = "事项名称: " + task.yuthon_will_task_id.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'
|
||||
body = data_date + data_name + data_user_ids + data_nota
|
||||
# 使用 Odoo 内部消息通知替代企业微信推送
|
||||
will_id.message_post(
|
||||
body=body,
|
||||
subject="日程事项提醒",
|
||||
message_type='notification',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
|
||||
def button_add_comment(self):
|
||||
"""手动发送日程提醒——通过 Odoo 内部消息通知"""
|
||||
_weekdays2 = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
||||
_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, '')
|
||||
data_date = '开始时间: ' + str(self.start_date) + ' ' + _week_str2 + '--' + _hour_from + '\n'
|
||||
data_name = "事项名称: " + self.name + '\n'
|
||||
data_user_ids = '参与人: ' + ', '.join(self.employee_ids.mapped('name')) + '\n'
|
||||
data_nota = ('备注: ' + self.remarks + '\n') if self.remarks else '\n'
|
||||
body = data_date + data_name + data_user_ids + data_nota
|
||||
# 使用 Odoo 内部消息通知替代企业微信推送
|
||||
self.message_post(
|
||||
body=body,
|
||||
subject="日程事项提醒",
|
||||
message_type='notification',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'success',
|
||||
'message': '已发送提醒消息',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
|
||||
def button_add_comment_cancel(self):
|
||||
"""取消日程并发送内部通知"""
|
||||
self.write({'task_state': 'cancel'})
|
||||
body = "事项名称: " + self.name + '\t' + '该事项已取消'
|
||||
# 使用 Odoo 内部消息通知替代企业微信推送
|
||||
self.message_post(
|
||||
body=body,
|
||||
subject="日程事项取消提醒",
|
||||
message_type='notification',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
)
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'success',
|
||||
'message': '已发送取消提醒消息',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
"""创建日程时,同步创建提醒任务"""
|
||||
will_task = super(YuthonWillTask, self).create(vals)
|
||||
will_task._sync_dates_task()
|
||||
return will_task
|
||||
|
||||
def write(self, vals):
|
||||
"""修改日程时,先删除旧提醒任务,再创建新的"""
|
||||
res = super(YuthonWillTask, self).write(vals)
|
||||
key_fields = ['start_date', 'request_hour_from', 'task_time_ids', 'employee_ids', 'task_type']
|
||||
if any(field in vals for field in key_fields):
|
||||
self._sync_dates_task()
|
||||
return res
|
||||
|
||||
def _sync_dates_task(self):
|
||||
"""根据提醒时间配置同步创建日程提醒任务"""
|
||||
reminder_employees = self.employee_ids | self.employee_id
|
||||
if not reminder_employees:
|
||||
return
|
||||
old_tasks = self.env['yuthon.dates.task'].search([('yuthon_will_task_id', '=', self.id), ('task_state', '=', 'to_do')])
|
||||
if old_tasks:
|
||||
old_tasks.unlink()
|
||||
start_hour = float(self.request_hour_from)
|
||||
hours = int(start_hour)
|
||||
minutes = int((start_hour - hours) * 60)
|
||||
task_start_datetime = datetime.combine(self.start_date, time(hour=hours, minute=minutes))
|
||||
for task_time in self.task_time_ids:
|
||||
reminder_time_str = task_time.name
|
||||
reminder_hours = 0.0
|
||||
import re
|
||||
match = re.match(r'(\d+)(分钟|小时|天)', reminder_time_str.strip())
|
||||
num = int(match.group(1))
|
||||
unit = match.group(2)
|
||||
if unit == '小时':
|
||||
reminder_hours = num
|
||||
elif unit == '分钟':
|
||||
reminder_hours = num / 60.0
|
||||
elif unit == '天':
|
||||
reminder_hours = num * 24.0
|
||||
reminder_datetime = task_start_datetime - timedelta(hours=reminder_hours)
|
||||
now = fields.Datetime.now() + timedelta(hours=8)
|
||||
if now > reminder_datetime:
|
||||
continue
|
||||
existing_dates_task = self.env['yuthon.dates.task'].search([
|
||||
('yuthon_will_task_id', '=', self.id),
|
||||
('reminder_time', '=', reminder_datetime)
|
||||
])
|
||||
task_vals = {
|
||||
'reminder_time': reminder_datetime - timedelta(hours=8),
|
||||
'employee_ids': [(6, 0, reminder_employees.ids)],
|
||||
'task_state': 'to_do',
|
||||
}
|
||||
if existing_dates_task:
|
||||
existing_dates_task.write(task_vals)
|
||||
else:
|
||||
task_vals['yuthon_will_task_id'] = self.id
|
||||
self.env['yuthon.dates.task'].create(task_vals)
|
||||
|
||||
def schedule_reminders(self):
|
||||
"""查看当前日程的提醒任务列表"""
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': '提醒任务详情',
|
||||
'view_mode': 'tree',
|
||||
'res_model': 'yuthon.dates.task',
|
||||
'domain': [('yuthon_will_task_id', '=', self.id)],
|
||||
}
|
||||
7
yuthon_will_task/security/ir.model.access.csv
Normal file
7
yuthon_will_task/security/ir.model.access.csv
Normal file
@ -0,0 +1,7 @@
|
||||
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_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,0
|
||||
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
|
||||
|
17
yuthon_will_task/security/yuthon_will_task_security.xml
Normal file
17
yuthon_will_task/security/yuthon_will_task_security.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="module_yuthon_will_task" model="ir.module.category">
|
||||
<field name="name">日程组</field>
|
||||
</record>
|
||||
<record id="group_yuthon_will_task_user" model="res.groups">
|
||||
<field name="name">日程-用户</field>
|
||||
<field name="category_id" ref="module_yuthon_will_task"/>
|
||||
</record>
|
||||
<record id="group_yuthon_will_task_leader" model="res.groups">
|
||||
<field name="name">日程-管理员</field>
|
||||
<field name="category_id" ref="module_yuthon_will_task"/>
|
||||
<field name="implied_ids" eval="[(4, ref('group_yuthon_will_task_user'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
BIN
yuthon_will_task/static/description/icon.png
Normal file
BIN
yuthon_will_task/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
28
yuthon_will_task/static/src/js/task_create_button.js
Normal file
28
yuthon_will_task/static/src/js/task_create_button.js
Normal file
@ -0,0 +1,28 @@
|
||||
/** @odoo-module */
|
||||
import { ListController } from "@web/views/list/list_controller";
|
||||
import { registry } from '@web/core/registry';
|
||||
import { listView } from '@web/views/list/list_view';
|
||||
|
||||
export class CustomTaskListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
}
|
||||
openTaskForm() {
|
||||
this.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'yuthon.will.task',
|
||||
name: '日程会议',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']],
|
||||
target: 'current',
|
||||
context: {'task_state': 'will'},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("views").add("custom_task_tree_button", {
|
||||
...listView,
|
||||
Controller: CustomTaskListController,
|
||||
buttonTemplate: "yuthon_task.ListView.Buttons",
|
||||
});
|
||||
4
yuthon_will_task/static/src/scss/yuthon_will_task.scss
Normal file
4
yuthon_will_task/static/src/scss/yuthon_will_task.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.button_width{
|
||||
width: 100px !important;
|
||||
max-width: 100px !important;
|
||||
}
|
||||
10
yuthon_will_task/static/src/xml/task_create_button.xml
Normal file
10
yuthon_will_task/static/src/xml/task_create_button.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
<t t-name="yuthon_task.ListView.Buttons" t-inherit="web.ListView.Buttons">
|
||||
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
|
||||
<button type="button" class="btn btn-primary" style="margin-left: 10px;" t-on-click="openTaskForm">
|
||||
新建日程
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
38
yuthon_will_task/views/yuthon_dates_task_views.xml
Normal file
38
yuthon_will_task/views/yuthon_dates_task_views.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_yuthon_dates_task_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.dates.task.tree</field>
|
||||
<field name="model">yuthon.dates.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="日程任务" editable="bottom"
|
||||
decoration-success="task_state == 'to_do'"
|
||||
decoration-danger="task_state == 'done'"
|
||||
>
|
||||
<field name="yuthon_will_task_id"/>
|
||||
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||
<field name="reminder_time"/>
|
||||
<field name="task_state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_dates_task_search" model="ir.ui.view">
|
||||
<field name="name">yuthon.dates.task.search</field>
|
||||
<field name="model">yuthon.dates.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="日程任务">
|
||||
<field name="yuthon_will_task_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="task_state" string="状态" context="{'group_by':'task_state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_dates_task_action" model="ir.actions.act_window">
|
||||
<field name="name">日程任务</field>
|
||||
<field name="res_model">yuthon.dates.task</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_yuthon_dates_task_tree"/>
|
||||
<field name="search_view_id" ref="view_yuthon_dates_task_search"/>
|
||||
<field name="context">{'search_default_task_state':1}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
26
yuthon_will_task/views/yuthon_task_menu.xml
Normal file
26
yuthon_will_task/views/yuthon_task_menu.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<menuitem id='menu_yuthon_will_task'
|
||||
name='会议日程'
|
||||
web_icon="yuthon_will_task,static/description/icon.png"
|
||||
sequence='10'/>
|
||||
<menuitem id='menu_yuthon_will_task_root2'
|
||||
name='日程管理'
|
||||
parent='menu_yuthon_will_task'
|
||||
action='view_yuthon_will_task_action2'
|
||||
sequence='10'/>
|
||||
<menuitem id="task_type_menu"
|
||||
name="配置"
|
||||
parent="menu_yuthon_will_task"
|
||||
sequence="100"/>
|
||||
<menuitem id="yuthon_task_time_setting_menu"
|
||||
name="提醒时间"
|
||||
parent="task_type_menu"
|
||||
action="yuthon_task_time_setting_action"
|
||||
sequence="5"/>
|
||||
<menuitem id="yuthon_dates_task_menu"
|
||||
name="日程任务"
|
||||
parent="task_type_menu"
|
||||
action="view_yuthon_dates_task_action"
|
||||
sequence="5"/>
|
||||
</odoo>
|
||||
21
yuthon_will_task/views/yuthon_task_time_views.xml
Normal file
21
yuthon_will_task/views/yuthon_task_time_views.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="yuthon_task_time_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.task.time.tree</field>
|
||||
<field name="model">yuthon.task.time</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="提醒时长配置" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name" required="1"/>
|
||||
<field name="number" required="1"/>
|
||||
<field name="task_unit" required="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="yuthon_task_time_setting_action" model="ir.actions.act_window">
|
||||
<field name="name">提醒时长配置</field>
|
||||
<field name="res_model">yuthon.task.time</field>
|
||||
<field name="view_mode">tree</field>
|
||||
</record>
|
||||
</odoo>
|
||||
371
yuthon_will_task/views/yuthon_will_task_views.xml
Normal file
371
yuthon_will_task/views/yuthon_will_task_views.xml
Normal file
@ -0,0 +1,371 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id='view_yuthon_will_task_tree' model='ir.ui.view'>
|
||||
<field name='name'>yuthon.will.task.tree</field>
|
||||
<field name='model'>yuthon.will.task</field>
|
||||
<field name='arch' type='xml'>
|
||||
<tree string='日程'
|
||||
decoration-muted="task_state == 'cancel'"
|
||||
decoration-danger="priority == 'very_urgent'"
|
||||
decoration-info="priority == 'urgent'"
|
||||
decoration-success="priority == 'important'" limit="15" action="open_record" type="object"
|
||||
>
|
||||
<field name="task_type"/>
|
||||
<field name='start_date' column_invisible="1"/>
|
||||
<field name='task_time'/>
|
||||
<field name='name' required="1"/>
|
||||
<field name='employee_ids' widget="many2many_tags" required="1" options="{'no_create': True}"/>
|
||||
<field name="address"/>
|
||||
<field name='remarks'/>
|
||||
<field name='priority'/>
|
||||
<field name='task_state' readonly="1"/>
|
||||
<button name="button_add_comment" string="提醒" type="object" class="btn btn-blue"
|
||||
invisible="task_state in ['done', 'cancel']"/>
|
||||
<button name="button_add_comment_cancel" string="取消" type="object" class="btn btn-blue"
|
||||
invisible="task_state == 'cancel'"/>
|
||||
<button name="schedule_reminders" string="提醒任务" type="object" class="btn btn-blue"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id='yuthon_will_task_search' model='ir.ui.view'>
|
||||
<field name='name'>yuthon.will.task.search</field>
|
||||
<field name='model'>yuthon.will.task</field>
|
||||
<field name='arch' type='xml'>
|
||||
<search string='日程'>
|
||||
<field name="name"/>
|
||||
<filter name="next_seven_days" string="周内事项" domain="[('start_date', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('start_date', '<=', (context_today() + relativedelta(days=7)).strftime('%Y-%m-%d'))]"/>
|
||||
<filter name="next_thirty_days" string="月内事项" domain="[('start_date', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('start_date', '<=', (context_today() + relativedelta(days=30)).strftime('%Y-%m-%d'))]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_will_task_form" model="ir.ui.view">
|
||||
<field name="name">yuthon.will.task.form</field>
|
||||
<field name="model">yuthon.will.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="日程">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="重要不紧急" bg_color="text-bg-success"
|
||||
invisible="priority != 'important'"/>
|
||||
<widget name="web_ribbon" title="紧急不重要" bg_color="text-bg-warning"
|
||||
invisible="priority != 'urgent'"/>
|
||||
<widget name="web_ribbon" title="重要且紧急" bg_color="text-bg-danger"
|
||||
invisible="priority != 'very_urgent'"/>
|
||||
<group col="3">
|
||||
<group>
|
||||
<field name="code" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_this_company" string="本公司"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="priority"/>
|
||||
<field name="task_type" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1>
|
||||
<field name="name" required="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group col="2">
|
||||
<group>
|
||||
<label for="start_date" string="开始日期"/>
|
||||
<div class="d-inline-flex w-100">
|
||||
<field name="start_date" string="开始日期" class="me-2"
|
||||
style="max-width: 6rem !important;"/>
|
||||
<field name="task_week" string="" class="me-2" style="max-width: 4rem !important;"/>
|
||||
<field name="request_hour_from" string=" " class="ms-2"
|
||||
style="max-width: 5rem !important;"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group col="2">
|
||||
<group invisible="task_type != 'meet'">
|
||||
<label for="end_date" string="结束日期"/>
|
||||
<div class="d-inline-flex w-100">
|
||||
<field name="end_date" string="结束日期" class="me-2"
|
||||
style="max-width: 6rem !important;"/>
|
||||
<field name="task_week" string="" class="me-2" style="max-width: 4rem !important;"/>
|
||||
<field name="request_hour_to" string=" " class="ms-2"
|
||||
style="max-width: 5rem !important;"/>
|
||||
</div>
|
||||
</group>
|
||||
<group invisible="task_type == 'meet'">
|
||||
<label for="end_date" string="结束日期"/>
|
||||
<div class="d-inline-flex w-100">
|
||||
<field name="end_date" string="结束日期" class="me-2"
|
||||
style="max-width: 6rem !important;"/>
|
||||
<field name="task_week2" string="" class="me-2" style="max-width: 4rem !important;"/>
|
||||
<field name="request_hour_to" string=" " class="ms-2"
|
||||
style="max-width: 5rem !important;"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="task_time_ids" widget="many2many_tags" required="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="employee_id"/>
|
||||
<field name="task_state" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"
|
||||
domain="[('company_id', '=', company_id)] if is_this_company else []"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<group>
|
||||
<field name="address"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="remarks"/>
|
||||
<field name="document_ids" widget="many2many_binary"/>
|
||||
<field name="creation_time" invisible="1"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" groups="base.group_user"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id='view_yuthon_will_task_tree2' model='ir.ui.view'>
|
||||
<field name='name'>yuthon.will.task.tree2</field>
|
||||
<field name='model'>yuthon.will.task</field>
|
||||
<field name='arch' type='xml'>
|
||||
<tree string='日程'
|
||||
decoration-muted="task_state == 'cancel'"
|
||||
decoration-danger="priority == 'very_urgent'"
|
||||
decoration-info="priority == 'urgent'"
|
||||
decoration-success="priority == 'important'" create="false" edit="false"
|
||||
js_class="custom_task_tree_button" limit="15" default_order="task_datetime desc">
|
||||
<field name='task_datetime' column_invisible="1"/>
|
||||
<field name='start_date' column_invisible="1"/>
|
||||
<field name='task_time'/>
|
||||
<field name='name' required="1"/>
|
||||
<field name='employee_ids' widget="many2many_tags" required="1" options="{'no_create': True}"/>
|
||||
<field name="address"/>
|
||||
<field name='remarks'/>
|
||||
<field name='priority'/>
|
||||
<field name='task_state' readonly="1"/>
|
||||
<button name="button_add_comment" string="提醒" type="object" class="btn btn-blue"
|
||||
invisible="task_state in ['done', 'cancel']"/>
|
||||
<button name="button_add_comment_cancel" string="取消" type="object" class="btn btn-blue"
|
||||
invisible="task_state == 'cancel'"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_will_task_form2" model="ir.ui.view">
|
||||
<field name="name">yuthon.will.task.message.form</field>
|
||||
<field name="model">yuthon.will.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="日程" create="false">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" title="重要不紧急" bg_color="text-bg-success"
|
||||
invisible="priority != 'important'"/>
|
||||
<widget name="web_ribbon" title="紧急不重要" bg_color="text-bg-warning"
|
||||
invisible="priority != 'urgent'"/>
|
||||
<widget name="web_ribbon" title="重要且紧急" bg_color="text-bg-danger"
|
||||
invisible="priority != 'very_urgent'"/>
|
||||
<group col="3">
|
||||
<group>
|
||||
<field name="code" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_this_company" string="本公司"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="priority"/>
|
||||
<field name="task_type" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1>
|
||||
<field name="name" required="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group col="2">
|
||||
<group>
|
||||
<label for="start_date" string="开始日期"/>
|
||||
<div class="d-inline-flex w-100">
|
||||
<field name="start_date" string="开始日期" class="me-2"
|
||||
style="max-width: 6rem !important;"/>
|
||||
<field name="task_week" string="" class="me-2" style="max-width: 4rem !important;"/>
|
||||
<field name="request_hour_from" string=" " class="ms-2"
|
||||
style="max-width: 5rem !important;"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group col="2">
|
||||
<group invisible="task_type != 'meet'">
|
||||
<label for="end_date" string="结束日期"/>
|
||||
<div class="d-inline-flex w-100">
|
||||
<field name="end_date" string="结束日期" class="me-2"
|
||||
style="max-width: 6rem !important;"/>
|
||||
<field name="task_week" string="" class="me-2" style="max-width: 4rem !important;"/>
|
||||
<field name="request_hour_to" string=" " class="ms-2"
|
||||
style="max-width: 5rem !important;"/>
|
||||
</div>
|
||||
</group>
|
||||
<group invisible="task_type == 'meet'">
|
||||
<label for="end_date" string="结束日期"/>
|
||||
<div class="d-inline-flex w-100">
|
||||
<field name="end_date" string="结束日期" class="me-2"
|
||||
style="max-width: 6rem !important;"/>
|
||||
<field name="task_week2" string="" class="me-2" style="max-width: 4rem !important;"/>
|
||||
<field name="request_hour_to" string=" " class="ms-2"
|
||||
style="max-width: 5rem !important;"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="task_time_ids" widget="many2many_tags" required="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="employee_id" domain="[('company_id', '=', company_id)]"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="employee_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||
<field name='users_ids' widget="many2many_tags" invisible="1"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<group>
|
||||
<field name="address"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="remarks"/>
|
||||
<field name="document_ids" widget="many2many_binary"/>
|
||||
<field name="creation_time" invisible="1"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" groups="base.group_user"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_will_task_kanban" model="ir.ui.view">
|
||||
<field name="name">yuthon.will.task.kanban</field>
|
||||
<field name="model">yuthon.will.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<field name="name"/>
|
||||
<field name="task_state"/>
|
||||
<field name="start_date"/>
|
||||
<field name="end_date"/>
|
||||
<field name="task_week"/>
|
||||
<field name="request_hour_from"/>
|
||||
<field name="request_hour_to"/>
|
||||
<field name="employee_ids"/>
|
||||
<field name="address"/>
|
||||
<field name="remarks"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click" style="border-radius: 8px; padding: 12px; margin-bottom: 8px;">
|
||||
<!-- 标题行:任务名称 + 状态标签 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 6px;">
|
||||
<div style="font-weight: bold; font-size: 14px; line-height: 1.5; flex: 1; margin-right: 8px;">
|
||||
<field name="name"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="task_state" widget="badge"
|
||||
decoration-success="task_state == 'done'"
|
||||
decoration-info="task_state == 'will'"
|
||||
decoration-muted="task_state == 'cancel'"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 时间行 -->
|
||||
<div style="color: #555; font-size: 13px; margin-bottom: 4px;">
|
||||
<field name="start_date"/>
|
||||
<t t-if="record.task_week.raw_value">
|
||||
<t t-esc="record.task_week.raw_value"/>
|
||||
</t>
|
||||
<t t-if="record.request_hour_from.value and record.request_hour_to.value">
|
||||
<t t-esc="record.request_hour_from.value"/> - <t t-esc="record.request_hour_to.value"/>
|
||||
</t>
|
||||
</div>
|
||||
<!-- 参与人 -->
|
||||
<t t-if="record.employee_ids.raw_value and record.employee_ids.raw_value.length">
|
||||
<div style="color: #555; font-size: 13px; margin-bottom: 4px;">
|
||||
参与人 <field name="employee_ids" widget="many2many_tags"/>共<t t-esc="record.employee_ids.raw_value.length"/>人
|
||||
</div>
|
||||
</t>
|
||||
<!-- 地点 -->
|
||||
<t t-if="record.address.raw_value">
|
||||
<div style="color: #555; font-size: 13px; margin-bottom: 4px;">
|
||||
地点 <field name="address"/>
|
||||
</div>
|
||||
</t>
|
||||
<!-- 备注 -->
|
||||
<t t-if="record.remarks.raw_value">
|
||||
<div style="color: #555; font-size: 13px;">
|
||||
备注:<field name="remarks"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_calendar_event_calendar" model="ir.ui.view">
|
||||
<field name="name">yuthon.will.task.calendar</field>
|
||||
<field name="model">yuthon.will.task</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="日程"
|
||||
date_start="start_date"
|
||||
event_open_popup="true"
|
||||
event_limit="5"
|
||||
quick_create="false"
|
||||
quick_create_view_id="%(yuthon_will_task.view_yuthon_will_task_form)d"
|
||||
color="employee_ids">
|
||||
<field name="priority"/>
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
<record id='view_yuthon_will_task_action' model='ir.actions.act_window'>
|
||||
<field name='name'>我的日程</field>
|
||||
<field name='res_model'>yuthon.will.task</field>
|
||||
<field name='view_mode'>tree,kanban,calendar,form</field>
|
||||
<field name='domain'>[('users_ids', 'in', [uid]), ('task_state', '!=', 'cancel')]</field>
|
||||
<field name="context">{'search_default_next_seven_days': 1}</field>
|
||||
<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': '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')})]" />
|
||||
</record>
|
||||
<record id='view_yuthon_will_task_action2' model='ir.actions.act_window'>
|
||||
<field name='name'>日程管理</field>
|
||||
<field name='res_model'>yuthon.will.task</field>
|
||||
<field name='view_mode'>tree,kanban,calendar,form</field>
|
||||
<field name='domain'>[('task_state', '!=', 'cancel'), ('is_this_company','=',True)]</field>
|
||||
<field name="context">{'search_default_next_seven_days': 1}</field>
|
||||
<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': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')})]" />
|
||||
</record>
|
||||
<record id="all_yuthon_will_task" model="ir.actions.server">
|
||||
<field name="name">更新</field>
|
||||
<field name="model_id" ref="yuthon_will_task.model_yuthon_will_task"/>
|
||||
<field name="binding_model_id" ref="yuthon_will_task.model_yuthon_will_task"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.one_time()</field>
|
||||
</record>
|
||||
<record id="all_update_task_state" model="ir.actions.server">
|
||||
<field name="name">状态改为已办</field>
|
||||
<field name="model_id" ref="yuthon_will_task.model_yuthon_will_task"/>
|
||||
<field name="binding_model_id" ref="yuthon_will_task.model_yuthon_will_task"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.update_task_state()</field>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_system'))]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user