diff --git a/soong_workflow/__init__.py b/soong_workflow/__init__.py
index cde864bae..3616b3002 100644
--- a/soong_workflow/__init__.py
+++ b/soong_workflow/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
+from . import wizard
\ No newline at end of file
diff --git a/soong_workflow/__manifest__.py b/soong_workflow/__manifest__.py
index 4ed811fde..0f2db46f0 100644
--- a/soong_workflow/__manifest__.py
+++ b/soong_workflow/__manifest__.py
@@ -17,7 +17,8 @@
"views/soong_workflow_node_views.xml",
"views/soong_workflow_link_views.xml",
"views/soong_workflow_views.xml",
- 'views/soong_workflow_record.xml'
+ 'views/soong_workflow_record.xml',
+ 'wizard/approval_notice_wizard_views.xml'
],
"installable": True,
"auto_install": False,
diff --git a/soong_workflow/models/soong_approval_users.py b/soong_workflow/models/soong_approval_users.py
index 95ee95b51..d91d0b388 100644
--- a/soong_workflow/models/soong_approval_users.py
+++ b/soong_workflow/models/soong_approval_users.py
@@ -26,7 +26,7 @@ class SoongApprovalUsers(models.Model):
approval_user = fields.Many2one('soong.user.role', string='审批角色')
approval_user_datetime = fields.Datetime(string='审批时间')
approval_user_notice = fields.Char(string='审批意见')
- approval_user_sign = fields.Char(string='审批签署')
+ approval_user_sign = fields.Binary(string='审批签署')
approval_user_state = fields.Selection([('will', '待审批')], string='状态')
@api.model_create_multi
diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py
index 368efbc68..3265b298a 100644
--- a/soong_workflow/models/soong_workflow_mixin.py
+++ b/soong_workflow/models/soong_workflow_mixin.py
@@ -148,18 +148,18 @@ class SoongWorkflowMixin(models.AbstractModel):
@api.model
def get_view(self, view_id=None, view_type="form", **options):
res = super().get_view(view_id, view_type, **options)
-
- if view_type in ["tree", "form"]:
- if "field" in res["arch"]:
- # res = self._remove_default_header(view_type, res)
- res = self._remove_default_modifiers(view_type, res)
- res = self._load_buttons_view(view_type, res)
- res = self._load_button_box_view(view_type, res)
- res = self._load_state_view(view_type, res)
- res = self._load_one2many_views(view_type, res)
- res = self._update_fields_view(view_type, res)
- # 列表视图增加状态字段
- # res = self._add_state_tree_view(view_type, res)
+ if 'create="false"' not in res['arch']: # 针对一个模型多个form临时做的区分
+ if view_type in ["tree", "form"]:
+ if "field" in res["arch"]:
+ # res = self._remove_default_header(view_type, res)
+ res = self._remove_default_modifiers(view_type, res)
+ res = self._load_buttons_view(view_type, res)
+ res = self._load_button_box_view(view_type, res)
+ res = self._load_state_view(view_type, res)
+ res = self._load_one2many_views(view_type, res)
+ res = self._update_fields_view(view_type, res)
+ # 列表视图增加状态字段
+ # res = self._add_state_tree_view(view_type, res)
return res
def _remove_default_header(self, view_type, res):
diff --git a/soong_workflow/security/ir.model.access.csv b/soong_workflow/security/ir.model.access.csv
index e7766b223..22fa7a293 100644
--- a/soong_workflow/security/ir.model.access.csv
+++ b/soong_workflow/security/ir.model.access.csv
@@ -17,4 +17,6 @@ access_soong_workflow_link_manager,soong_workflow_link_manager,model_soong_workf
access_soong_workflow_record_user,soong_workflow_record_user,model_soong_workflow_record,soong_workflow.group_soong_workflow_user,1,1,1,1
access_soong_user_role,soong_user_role,model_soong_user_role,soong_workflow.group_soong_workflow_user,1,1,1,1
access_soong_approval_users,soong_approval_users,model_soong_approval_users,soong_workflow.group_soong_workflow_user,1,1,1,1
-access_soong_note,soong_note,model_soong_note,soong_workflow.group_soong_workflow_user,1,1,1,1
\ No newline at end of file
+access_soong_note,soong_note,model_soong_note,soong_workflow.group_soong_workflow_user,1,1,1,1
+
+access_soong_approval_notice_wizard,soong_approval_notice_wizard,model_soong_approval_notice_wizard,base.group_user,1,1,1,1
diff --git a/soong_workflow/views/soong_approval_users_views.xml b/soong_workflow/views/soong_approval_users_views.xml
index fe5ef2886..fb76a2936 100644
--- a/soong_workflow/views/soong_approval_users_views.xml
+++ b/soong_workflow/views/soong_approval_users_views.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/soong_workflow/wizard/__init__.py b/soong_workflow/wizard/__init__.py
new file mode 100644
index 000000000..f048f7d3d
--- /dev/null
+++ b/soong_workflow/wizard/__init__.py
@@ -0,0 +1,5 @@
+#
+
+from . import approval_notice_wizard
+
+
diff --git a/soong_workflow/wizard/approval_notice_wizard.py b/soong_workflow/wizard/approval_notice_wizard.py
new file mode 100644
index 000000000..c439f2e7f
--- /dev/null
+++ b/soong_workflow/wizard/approval_notice_wizard.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from odoo import api, fields, models, tools, _
+
+
+class SoongApprovalNoticeWizard(models.TransientModel):
+ _name = 'soong.approval.notice.wizard'
+ _description = """ 审批意见弹窗 """
+
+ # approval_res_id = fields.Char(string="审批明细行")
+ # approval_res_model = fields.Char(string="审批明细行")
+
+ approval_user_notice = fields.Char(string='审批意见')
+ approval_user_sign = fields.Binary(string="审批签署", copy=False)
+
+ def confirm(self):
+ self.ensure_one()
+ active_model = self.env.context.get("active_model")
+ active_id = self.env.context.get("active_id")
+ self.env["soong.approval.users"].create({
+ 'res_model_id': self.env['ir.model']._get(active_model).id,
+ 'res_id': active_id,
+ 'approval_user_notice': self.approval_user_notice,
+ 'approval_user_id': self.env.user.id,
+ 'approval_user_datetime': fields.datetime.now(),
+ 'approval_user_sign': self.approval_user_sign,
+ })
diff --git a/soong_workflow/wizard/approval_notice_wizard_views.xml b/soong_workflow/wizard/approval_notice_wizard_views.xml
new file mode 100644
index 000000000..f2da5adb7
--- /dev/null
+++ b/soong_workflow/wizard/approval_notice_wizard_views.xml
@@ -0,0 +1,34 @@
+
+
+ 审批意见弹窗
+ soong.approval.notice.wizard
+
+
+
+
+
+ 审批意见弹窗
+ ir.actions.act_window
+ soong.approval.notice.wizard
+
+
+
+
+
+ form
+ new
+
+
\ No newline at end of file
diff --git a/web_editor_tinymce/__manifest__.py b/web_editor_tinymce/__manifest__.py
new file mode 100644
index 000000000..947706809
--- /dev/null
+++ b/web_editor_tinymce/__manifest__.py
@@ -0,0 +1,19 @@
+{
+ 'name': 'TinyMCE Editor',
+ 'summary': 'TinyMCE Editor for Html Field',
+ 'author': 'Hoang Minh Hieu',
+ 'version': '17.0.1.0.0',
+ 'license': 'OPL-1',
+ 'support ': 'hieuhoangminh1996@gmail.com',
+ 'depends': ['web'],
+ 'assets': {
+ 'web.assets_backend': [
+ 'web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss',
+ 'web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml',
+ 'web_editor_tinymce/static/src/field_tinymce/field_tinymce.js',
+ ],
+ },
+ 'images': ['static/description/main_screenshot.png'],
+ 'price': 35,
+ 'currency': 'USD',
+}
diff --git a/web_editor_tinymce/static/description/icon.png b/web_editor_tinymce/static/description/icon.png
new file mode 100644
index 000000000..15e5ed47f
Binary files /dev/null and b/web_editor_tinymce/static/description/icon.png differ
diff --git a/web_editor_tinymce/static/description/index.html b/web_editor_tinymce/static/description/index.html
new file mode 100644
index 000000000..32e00ac75
--- /dev/null
+++ b/web_editor_tinymce/static/description/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+ This module replaces Odoo html editor with TinyMCE editor.
+
+
+
+
+
+ This module replaces Odoo html editor with TinyMCE editor.
+
+
+
+ Screenshot
+
+
+ Usage
+
+
+
+
+
diff --git a/web_editor_tinymce/static/description/main_screenshot.png b/web_editor_tinymce/static/description/main_screenshot.png
new file mode 100644
index 000000000..2e999d5ca
Binary files /dev/null and b/web_editor_tinymce/static/description/main_screenshot.png differ
diff --git a/web_editor_tinymce/static/description/usage.png b/web_editor_tinymce/static/description/usage.png
new file mode 100644
index 000000000..25524eedc
Binary files /dev/null and b/web_editor_tinymce/static/description/usage.png differ
diff --git a/web_editor_tinymce/static/src/field_tinymce/field_tinymce.js b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.js
new file mode 100644
index 000000000..946769557
--- /dev/null
+++ b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.js
@@ -0,0 +1,130 @@
+/** @odoo-module **/
+
+import {_t} from '@web/core/l10n/translation';
+import {Component, onWillStart, onMounted, onPatched, onWillUnmount, useEffect} from '@odoo/owl';
+import {standardFieldProps} from '@web/views/fields/standard_field_props';
+import {registry} from '@web/core/registry';
+import {TranslationButton} from '@web/views/fields/translation_button';
+import {loadJS} from '@web/core/assets';
+import {cookie} from '@web/core/browser/cookie';
+
+export class FieldTinyMCE extends Component {
+ static template = 'web_editor_tinymce.TinyMCE';
+ static components = {
+ TranslationButton,
+ };
+ static props = {
+ ...standardFieldProps,
+ placeholder: {type: String, optional: true},
+ dynamicPlaceholder: {type: Boolean, optional: true},
+ dynamicPlaceholderModelReferenceField: {type: String, optional: true},
+ };
+ static defaultProps = {
+ dynamicPlaceholder: false,
+ };
+
+ setup() {
+ super.setup();
+ this.editorId = `tinymce_${this.props.name}`
+ const useDarkMode = this.theme === 'dark';
+ this.defaultTinymceConfig = {
+ selector: `#${this.editorId}`,
+ plugins: 'autoresize preview importcss searchreplace autolink directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons accordion',
+ editimage_cors_hosts: ['picsum.photos'],
+ image_advtab: true,
+ importcss_append: true,
+ height: 'auto',
+ image_caption: true,
+ quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
+ noneditable_class: 'mceNonEditable',
+ toolbar_mode: 'sliding',
+ contextmenu: 'link image table',
+ skin: useDarkMode ? 'oxide-dark' : 'oxide',
+ content_css: useDarkMode ? 'dark' : 'default',
+ content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }',
+ placeholder: this.props.placeholder,
+ }
+ onWillStart(async () => {
+ await loadJS('web_editor_tinymce/static/lib/tinymce/tinymce.min.js');
+ });
+ onMounted(async () => {
+ await this.initEditor();
+ })
+ useEffect(() => {
+ this.updateEditor();
+ })
+ onWillUnmount(async () => {
+ this.removeEditor()
+ })
+ }
+
+ get value() {
+ return this.props.record.data[this.props.name] || '';
+ }
+
+ get theme() {
+ return cookie.get('color_scheme');
+ }
+
+ get isTranslatable() {
+ return this.props.record.fields[this.props.name].translate;
+ }
+
+ get tinymceConfig() {
+ return {
+ ...this.defaultTinymceConfig,
+ menubar: !this.props.readonly && 'file edit view insert format tools table help',
+ toolbar: !this.props.readonly && 'undo redo | accordion accordionremove | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | code fullscreen preview | save print | pagebreak anchor codesample | ltr rtl',
+ readonly: this.props.readonly,
+ setup: this.setupEditor.bind(this),
+ init_instance_callback: this.initCallback.bind(this),
+ }
+ }
+
+ setupEditor(editor) {
+ editor.on('change', e => {
+ const value = tinymce.activeEditor.getContent();
+ if (value !== this.value) {
+ this.props.record.update({[this.props.name]: value})
+ }
+ });
+ }
+
+ removeEditor() {
+ if (this.editor) {
+ this.editor.remove()
+ }
+ }
+
+ async initEditor() {
+ this.removeEditor()
+ const config = this.tinymceConfig;
+ this.editor = (await tinymce.init(config))[0];
+ }
+
+ initCallback(editor) {
+ editor.setContent(this.value);
+ }
+
+ updateEditor() {
+ if (!this.editor || this.props.record.dirty) {
+ return
+ }
+ this.editor.setContent(this.value);
+ }
+
+}
+
+export const fieldTinyMCE = {
+ component: FieldTinyMCE,
+ displayName: _t('TinyMCE'),
+ supportedTypes: ['html', 'text'],
+ extractProps: ({attrs, options}) => ({
+ placeholder: attrs.placeholder,
+ dynamicPlaceholder: options?.dynamic_placeholder || false,
+ dynamicPlaceholderModelReferenceField:
+ options?.dynamic_placeholder_model_reference_field || '',
+ }),
+}
+
+registry.category('fields').add('tinymce', fieldTinyMCE);
diff --git a/web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss
new file mode 100644
index 000000000..ed817fb39
--- /dev/null
+++ b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.scss
@@ -0,0 +1,3 @@
+.o_field_tinymce {
+ width: 100%;
+}
diff --git a/web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml
new file mode 100644
index 000000000..301ffd7e9
--- /dev/null
+++ b/web_editor_tinymce/static/src/field_tinymce/field_tinymce.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yuthon_notice/models/yuthon_notice.py b/yuthon_notice/models/yuthon_notice.py
index ca327e2c6..c319ace04 100644
--- a/yuthon_notice/models/yuthon_notice.py
+++ b/yuthon_notice/models/yuthon_notice.py
@@ -21,18 +21,19 @@ class YuthonNotice(models.Model):
start_date = fields.Date(string="生效日期")
end_date = fields.Date(string="终止日期")
sequence = fields.Integer(string="顺序", default=1)
- statu = fields.Selection([('new', '草稿'), ('approval', '审批中'), ('no', '未发布'), ('done', '已发布')], default='new', string="公告状态")
- # pub_range = fields.Char(string="发布范围")
+ statu = fields.Selection(
+ [('new', '草稿'), ('approval', '审批中'), ('no', '未发布'), ('done', '已发布'), ('stop', '已终止')],
+ default='new', string="公告状态")
notice_documents_ids = fields.Many2many('documents.document', string="通知公告文件")
- # is_confirm = fields.Boolean(string="是否需要人员确认")
- is_notice_roger = fields.Boolean(string="已读")
+ is_notice_roger = fields.Selection([('yes', '已读'), ('no', '未读')], default='no', string="确认状态")
notice_text = fields.Html(string="正文")
@api.onchange('is_notice_roger')
def onchange_is_notice_roger(self):
- roger_id = self.env['yuthon.confirm.users'].search([('notice_id', '=', self.id), ('user_id', '=', self.env.user.id)])
+ roger_id = self.env['yuthon.confirm.users'].search(
+ [('notice_id', '=', self.id), ('user_id', '=', self.env.user.id)])
if not roger_id:
- self.is_notice_roger = False
+ self.is_notice_roger = 'yes'
def on_sequence(self):
self.sequence = 1
@@ -40,28 +41,6 @@ class YuthonNotice(models.Model):
def no_sequence(self):
self.sequence = 0
- # @api.onchange('department_ids', 'employee_ids')
- # def onchange_pub_range(self):
- # for pub in self:
- # data_str = ''
- # de_str = [de.name for de in pub.department_ids] if pub.department_ids else data_str
- # if len(de_str) == 1:
- # de_str = de_str[0]
- # else:
- # de_str = ','.join(de_str)
- # em_str = [em.name for em in pub.employee_ids] if pub.employee_ids else data_str
- # if len(em_str) == 1:
- # em_str = em_str[0]
- # else:
- # em_str = ','.join(em_str)
- # if de_str and em_str:
- # pub.pub_range = '部门:' + de_str + ' 人员:' + em_str
- # elif de_str and not em_str:
- # pub.pub_range = '部门:' + de_str
- # elif em_str and not de_str:
- # pub.pub_range = '人员:' + em_str
- # else:
- # pub.pub_range = data_str
def notice_publish(self):
users_role_list = department_users_list = []
for depart in self.department_ids:
@@ -82,13 +61,14 @@ class YuthonNotice(models.Model):
'state': 'yes',
})
# todo 普通用户权限是否打开,可能需要配置权限
- partner_ids = self.sudo().env['res.users'].search([('id', 'in', publish_user_list)]).mapped('partner_id').ids
- self.sudo().with_context(lang=self.env.lang)._message_auto_subscribe_notify(partner_ids, 'yuthon_notice.message_yuthon_confirm_users_data')
+ partner_ids = self.sudo().env['res.users'].search([('id', 'in', publish_user_list)]).mapped(
+ 'partner_id').ids
+ self.sudo().with_context(lang=self.env.lang)._message_auto_subscribe_notify(partner_ids,
+ 'yuthon_notice.message_yuthon_confirm_users_data')
self.write({"statu": 'done'})
def confirm_notice(self):
- self.write({'is_notice_roger': True})
- # 用write
+ self.write({'is_notice_roger': 'yes'})
def look_up_notice_users(self):
return {
@@ -101,3 +81,9 @@ class YuthonNotice(models.Model):
"search_default_group_state": True,
},
}
+
+ def stop_notice(self):
+ self.write({
+ 'statu': 'stop',
+ 'end_date': fields.Datetime.now(),
+ })
diff --git a/yuthon_notice/report/notice_report.xml b/yuthon_notice/report/notice_report.xml
index 670c7811c..0b3784d5b 100644
--- a/yuthon_notice/report/notice_report.xml
+++ b/yuthon_notice/report/notice_report.xml
@@ -21,6 +21,11 @@
+
diff --git a/yuthon_notice/views/yuthon_ntice_views.xml b/yuthon_notice/views/yuthon_ntice_views.xml
index ef40fda03..ac4ee5edf 100644
--- a/yuthon_notice/views/yuthon_ntice_views.xml
+++ b/yuthon_notice/views/yuthon_ntice_views.xml
@@ -10,7 +10,6 @@
-
@@ -18,6 +17,7 @@
+
@@ -32,8 +32,6 @@
-
-
@@ -50,9 +48,7 @@
-
-
@@ -67,7 +63,7 @@
-
+
@@ -107,12 +103,101 @@
+
+ yuthon.notice.tree
+ yuthon.notice
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ yuthon.notice.form2
+ yuthon.notice
+
+
+
+
+
+
+ yuthon.notice.search
+ yuthon.notice
+
+
+
+
+
+
+
+
+
+
+ 我的通知公告
+ yuthon.notice
+ tree,form
+
+
+ {'search_default_group_by_is_notice_roger': True,}
+
+
+
+