diff --git a/room/security/ir_rule.xml b/room/security/ir_rule.xml index c20ade81a..7b2e0524c 100644 --- a/room/security/ir_rule.xml +++ b/room/security/ir_rule.xml @@ -11,6 +11,11 @@ [('company_id', 'in', company_ids)] + + 预定会议多公司 + + [('company_id', 'in', company_ids)] + diff --git a/soong_workflow/models/__init__.py b/soong_workflow/models/__init__.py index f27b21a33..4d5b4814e 100644 --- a/soong_workflow/models/__init__.py +++ b/soong_workflow/models/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- - +# 1.双向删除 +# 2. from . import soong_workflow from . import soong_workflow_node from . import soong_workflow_link diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 28068a7f3..436b4e525 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -332,7 +332,7 @@ class SoongWorkflowMixin(models.AbstractModel): index = 20 # 防止死循环 break current_department = self.department_id - if btn_rec.link_id and btn_rec.link_id in current_node.out_link_ids: + if btn_rec.link_id in current_node.out_link_ids and len(current_node.out_link_ids) == 1: matched_link = btn_rec.link_id next_node = matched_link.node_to matched_key = next_node.name @@ -341,14 +341,27 @@ class SoongWorkflowMixin(models.AbstractModel): # 筛选与当前节点部门匹配的后续节点 valid_nodes = [] for link in current_node.out_link_ids: - target_node = link.node_to - # 如果目标节点有部门限制,且与当前节点部门匹配 - if target_node.department_id and any( - dept in current_department or - current_department in self.get_all_sub_departments(dept) - for dept in target_node.department_id - ): - valid_nodes.append((target_node, link)) + if self.name_title == '业务审批': + category_names = self.information_categories_ids.mapped('name') + target_name = link.name.split('->')[-1].strip() + if '工会活动' in category_names: + if target_name == '工会主席': + target_node = link.node_to + valid_nodes.append((target_node, link)) + else: + if target_name == '复核': + target_node = link.node_to + valid_nodes.append((target_node, link)) + + else: + target_node = link.node_to + # 如果目标节点有部门限制,且与当前节点部门匹配 + if target_node.department_id and any( + dept in current_department or + current_department in self.get_all_sub_departments(dept) + for dept in target_node.department_id + ): + valid_nodes.append((target_node, link)) # 尝试父部门匹配 if not valid_nodes and current_department and current_department.parent_id: @@ -734,8 +747,11 @@ class SoongWorkflowMixin(models.AbstractModel): btn_exec_action_el.set("class", "oe_highlight") if button.has_icon: btn_exec_action_el.set("icon", button.icon) - btn_exec_action_el.set("invisible", "state != '%s'" % node.node_name) - btn_exec_action_el.set("context", "{'btn_key':'%s','active_model':'%s'}" % (button.btn_key, self._name),) + invisible_condition = "state != '%s'" % node.node_name + if model == 'purchase.order': + invisible_condition = invisible_condition.replace("询价", "draft") + btn_exec_action_el.set("invisible", invisible_condition) + btn_exec_action_el.set("context","{'btn_key':'%s','active_model':'%s'}" % (button.btn_key, self._name), ) else: is_apostille = False if button.btn_hide: @@ -811,6 +827,10 @@ class SoongWorkflowMixin(models.AbstractModel): # if button.name == "结束": # invisible += " or True" invisible += " or work_end" + + if model == 'purchase.order': + invisible = invisible.replace("询价", "draft") + btn_exec_action_el.set("invisible", invisible) btn_exec_action_el.set("context", "{'btn_key':'%s','active_model':'%s'}" % (button.btn_key, self._name),) res["arch"] = etree.tostring(arch, encoding="unicode").replace("\t", "") diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 150fcf4e9..37e7870d7 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -62,6 +62,18 @@ class SoongWorkflowRecord(models.Model): res.append_date2(res) return res + def log(self): + log = self.env['soong.workflow.log'].search([("model", "=", self.res_model), + ("res_id", "=", self.res_id)]) + return { + 'type': 'ir.actions.act_window', + 'name': '日志', + 'view_mode': 'tree', + 'res_model': 'soong.workflow.log', + 'domain': [('id', 'in', log.ids)], + } + + def restore(self): workflow_ids = self.env["soong.workflow.record"].browse( self._context.get('active_ids', self._context.get('active_id', []))) diff --git a/soong_workflow/views/soong_workflow_record.xml b/soong_workflow/views/soong_workflow_record.xml index 61edd8feb..3f04bee28 100644 --- a/soong_workflow/views/soong_workflow_record.xml +++ b/soong_workflow/views/soong_workflow_record.xml @@ -90,12 +90,15 @@ + - + - + + - diff --git a/yuthon_salary/__manifest__.py b/yuthon_salary/__manifest__.py index 49be4f687..a2511b537 100644 --- a/yuthon_salary/__manifest__.py +++ b/yuthon_salary/__manifest__.py @@ -35,6 +35,7 @@ 'wizard/yuthon_salary_wizard_views.xml', 'wizard/salary_party_wizard_views.xml', 'wizard/temporary_payslip_wizard_views.xml', + 'wizard/salary_import_wizard_views.xml', 'wizard/yuthon_payroll_wizard_views.xml', 'wizard/basic_pension_wizard_views.xml', 'wizard/payroll_labor_wizard_views.xml', @@ -61,8 +62,10 @@ 'yuthon_salary/static/src/js/salary_retired_button.js', 'yuthon_salary/static/src/js/salary_validation_button.js', 'yuthon_salary/static/src/js/salary_year_button.js', + 'yuthon_salary/static/src/js/salary_import_button.js', 'yuthon_salary/static/src/xml/salary_create_button.xml', 'yuthon_salary/static/src/xml/salary_labor_button.xml', + 'yuthon_salary/static/src/xml/salary_import_button.xml', 'yuthon_salary/static/src/xml/salary_retired_button.xml', 'yuthon_salary/static/src/xml/salary_payslip_button.xml', 'yuthon_salary/static/src/xml/salary_documents_button.xml', diff --git a/yuthon_salary/models/inherit_hr_payslip.py b/yuthon_salary/models/inherit_hr_payslip.py index 474afc099..2361a99c1 100644 --- a/yuthon_salary/models/inherit_hr_payslip.py +++ b/yuthon_salary/models/inherit_hr_payslip.py @@ -385,23 +385,23 @@ class InheritHrPayslip(models.Model): res = super(InheritHrPayslip, self).create(vals_list) return res - @api.model - def create(self, vals): - """ - 为了解决部分薪资生成未生效,单独处理计算公式 - 工会会费:round(abs(payslip.base_salary * 0.005) * -1, 2) --union_dues - :param vals: - :return: - """ - record = super().create(vals) - employee_id = self.env['hr.employee'].search([('id', '=', vals['employee_id'])]) - payroll = self.env['hr.contract'].search([('employee_id', '=', employee_id.id), ('state', '=', 'open')])[-1].structure_type_id.default_struct_id - if payroll.rule_ids.search([('name', '=', '基本工资'), ('struct_id', '=', payroll.id)]): - base_salary = payroll.rule_ids.search([('name', '=', '基本工资'), ('struct_id', '=', payroll.id)])[-1].amount_fix - # if record.worked_days_line_ids.search([('name', '=', '工会会费')]): - # record.line_ids.search([('name', '=', '工会会费')]).total = round(abs(base_salary * 0.005) * -1, 2) - record.write({'union_dues': round(abs(base_salary * 0.005) * -1, 2)}) - return record + # @api.model + # def create(self, vals): + # """ + # 为了解决部分薪资生成未生效,单独处理计算公式 + # 工会会费:round(abs(payslip.base_salary * 0.005) * -1, 2) --union_dues + # :param vals: + # :return: + # """ + # record = super().create(vals) + # employee_id = self.env['hr.employee'].search([('id', '=', vals['employee_id'])]) + # payroll = self.env['hr.contract'].search([('employee_id', '=', employee_id.id), ('state', '=', 'open')])[-1].structure_type_id.default_struct_id + # if payroll.rule_ids.search([('name', '=', '基本工资'), ('struct_id', '=', payroll.id)]): + # base_salary = payroll.rule_ids.search([('name', '=', '基本工资'), ('struct_id', '=', payroll.id)])[-1].amount_fix + # # if record.worked_days_line_ids.search([('name', '=', '工会会费')]): + # # record.line_ids.search([('name', '=', '工会会费')]).total = round(abs(base_salary * 0.005) * -1, 2) + # record.write({'union_dues': round(abs(base_salary * 0.005) * -1, 2)}) + # return record @api.ondelete(at_uninstall=False) def _unlink_if_draft_or_cancel(self): diff --git a/yuthon_salary/security/ir.model.access.csv b/yuthon_salary/security/ir.model.access.csv index b6690aef6..bfa5c3a25 100644 --- a/yuthon_salary/security/ir.model.access.csv +++ b/yuthon_salary/security/ir.model.access.csv @@ -36,4 +36,5 @@ access_rank_log_line,access_rank_log_line,model_rank_log_line,base.group_user,1, access_rank_log_wizard,access_rank_log_wizard,model_rank_log_wizard,base.group_user,1,1,1,1 access_basic_pension_wizard,access_basic_pension_wizard,model_basic_pension_wizard,base.group_user,1,1,1,1 access_yuthon_salary_party,access_yuthon_salary_party,model_yuthon_salary_party,base.group_user,1,1,1,1 -access_salary_party_wizard,access_salary_party_wizard,model_salary_party_wizard,base.group_user,1,1,1,1 \ No newline at end of file +access_salary_party_wizard,access_salary_party_wizard,model_salary_party_wizard,base.group_user,1,1,1,1 +access_salary_import_wizard,access_salary_import_wizard,model_salary_import_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_salary/static/src/js/salary_import_button.js b/yuthon_salary/static/src/js/salary_import_button.js new file mode 100644 index 000000000..b3407e9c0 --- /dev/null +++ b/yuthon_salary/static/src/js/salary_import_button.js @@ -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 CustomSalaryImportController extends ListController { + setup() { + super.setup(); + } + + openSalaryImportWizard() { + this.actionService.doAction({ + type: 'ir.actions.act_window', + res_model: 'salary.import.wizard', + name: '薪资导入', + view_mode: 'form', + view_type: 'form', + views: [[false, 'form']], + target: 'new', // 以弹窗形式打开 + context: {}, + }); + } +} + +registry.category("views").add("custom_salary_import_button", { + ...listView, + Controller: CustomSalaryImportController, + buttonTemplate: "salary_import_module.ListView.Buttons", +}); \ No newline at end of file diff --git a/yuthon_salary/static/src/xml/salary_import_button.xml b/yuthon_salary/static/src/xml/salary_import_button.xml new file mode 100644 index 000000000..aade38590 --- /dev/null +++ b/yuthon_salary/static/src/xml/salary_import_button.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/yuthon_salary/views/inherit_hr_payslip_views.xml b/yuthon_salary/views/inherit_hr_payslip_views.xml index 878747f4c..b0d7e4399 100644 --- a/yuthon_salary/views/inherit_hr_payslip_views.xml +++ b/yuthon_salary/views/inherit_hr_payslip_views.xml @@ -242,7 +242,7 @@ hr.payslip.tree.validation.jtbb hr.payslip - +
+ + + + + + + + + +
+
+ +
+ + diff --git a/yuthon_will_task/controllers/main.py b/yuthon_will_task/controllers/main.py index 853e24822..266e9ebec 100644 --- a/yuthon_will_task/controllers/main.py +++ b/yuthon_will_task/controllers/main.py @@ -1,9 +1,8 @@ import base64 import json - from odoo import http, fields from odoo.http import request -from odoo.tools import float_is_zero,float_round +from urllib.parse import quote class YuthonWillTask(http.Controller): @@ -56,19 +55,79 @@ class YuthonWillTask(http.Controller): 'message':'success' }) - @http.route('/will/taks/list', methods=['POST'], type='http', auth='none', csrf=False) - def task_list(self, **kwargs): - employee_id = kwargs.get('id') - data = request.env['yuthon.will.task'].sudo().search([]) + @http.route('/will/task/list', methods=['POST'], type='http', auth='public', csrf=False) + def will_task_list(self, **kwargs): + employee_id = kwargs.get('employee_id') + start = kwargs.get('start') + request_hour_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection'] result_list = [] - for emp in data: + # for task in request.env['yuthon.will.task'].sudo().search(['|', ('employee_ids', 'in', [employee_id]), ('employee_id.id', '=', employee_id)]): + task_ids = request.env['yuthon.will.task'].sudo().search([], offset=start, limit=7) + for task in task_ids: + weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()] result = { - 'name': emp.name, - 'code': emp.code, - } + 'id': task.id, + 'name': task.name, + 'employee_id': task.employee_id.name, + 'employee_ids': ', '.join(task.employee_ids.mapped('name')), + 'employee_num': len(task.employee_ids), + 'start_date': fields.Date.to_string(task.start_date), + 'weekday': weekday_str, + 'request_hour_from': '', + 'request_hour_to': '', + } + 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 = [] + for attach in task.document_ids: + attach_id = request.env['ir.attachment'].sudo().search([('id', '=', attach.id)]) + if 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}) + 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')), + 'employee_num': len(task.employee_ids), + 'start_date': fields.Date.to_string(task.start_date), + 'weekday': weekday_str, + 'request_hour_from': '', + 'request_hour_to': '', + 'remarks': task.remarks, + 'address': task.address, + # 'file_list': file_list, + 'file_list': [{ + 'file_url':'https://view.officeapps.live.com/op/view.aspx?src=https%3A//oa.thtzjt.com/web/content/office_preview/13672', + 'file_name':'excel', + }, + { + 'file_url':'https://oa.thtzjt.com/web/content/preview/13673', + 'file_name':'pdf', + } + ], + } + 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' + }) diff --git a/yuthon_will_task/models/inherit_room_booking.py b/yuthon_will_task/models/inherit_room_booking.py index 95c6b86f2..7dc660b58 100644 --- a/yuthon_will_task/models/inherit_room_booking.py +++ b/yuthon_will_task/models/inherit_room_booking.py @@ -1,10 +1,28 @@ from odoo import api, fields, models from datetime import datetime, timedelta, date +import logging +_logger = logging.getLogger(__name__) class InheritRoomBooking(models.Model): _inherit = 'room.booking' + def get_no_read_count(self): + # 1. 计算各类时间参数 + today = date.today() + timedelta(hours=8) + today_start = datetime.combine(today, datetime.min.time()) + today_end = datetime.combine(today, datetime.max.time()) + domain = [ + ('start_datetime', '>=', today_start), + ('start_datetime', '<=', today_end), + '|', + ('stop_datetime', '=', False), + ('stop_datetime', '>', datetime.now()) + ] + matching_records = self.search(domain) + count = len(matching_records) + return count + @api.model def create(self, vals): task_time_ids = self.env['yuthon.task.time'].search([('name', '=', '30分钟')]) diff --git a/yuthon_will_task/models/yuthon_will_task.py b/yuthon_will_task/models/yuthon_will_task.py index de03c4bf7..3241a3d9e 100644 --- a/yuthon_will_task/models/yuthon_will_task.py +++ b/yuthon_will_task/models/yuthon_will_task.py @@ -82,7 +82,7 @@ class YuthonWillTask(models.Model): document_ids = fields.Many2many('ir.attachment', string="附件") address = fields.Char(string='地点') start_date = fields.Date(string='开始日期', default=fields.Date.today) - task_time = fields.Char(string='任务时间', compute='_compute_task_time') + task_time = fields.Char(string='任务时间') is_this_company = fields.Boolean(string='本公司', default=True) task_state = fields.Selection([('will', '待办'), ('done', '已办'), ('cancel', '取消')], default='will', string='状态', store=True) @@ -106,42 +106,58 @@ class YuthonWillTask(models.Model): store=True, default=lambda self: [(6, 0, [self.env.user.id])] ) - @api.depends('start_date', 'request_hour_from', 'request_hour_to') - def _compute_task_time(self): + 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') + + @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()] + + 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._check_task_time() + + @api.constrains('start_date', 'request_hour_from', 'request_hour_to') + def _check_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.5': '0:30', - '1': '01:00', '1.5': '01:30', - '2': '02:00', '2.5': '02:30', - '3': '03:00', '3.5': '03:30', - '4': '04:00', '4.5': '04:30', - '5': '05:00', '5.5': '05:30', - '6': '06:00', '6.5': '06:30', - '7': '07:00', '7.5': '07:30', - '8': '08:00', '8.5': '08:30', - '9': '09:00', '9.5': '09:30', - '10': '10:00', '10.5': '10:30', - '11': '11:00', '11.5': '11:30', - '12': '12:00', '12.5': '12:30', - '13': '13:00', '13.5': '13:30', - '14': '14:00', '14.5': '14:30', - '15': '15:00', '15.5': '15:30', - '16': '16:00', '16.5': '16:30', - '17': '17:00', '17.5': '17:30', - '18': '18:00', '18.5': '18:30', - '19': '19:00', '19.5': '19:30', - '20': '20:00', '20.5': '20:30', - '21': '21:00', '21.5': '21:30', - '22': '22:00', '22.5': '22:30', - '23': '23:00', '23.5': '23:30' + '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, "") to_time = hour_mapping.get(record.request_hour_to, "") if date_str and from_time and to_time: - record.task_time = f"{date_str} {from_time}" + record.task_time = f"{date_str} {record.task_week} {from_time}" else: record.task_time = "" diff --git a/yuthon_will_task/security/yuthon_will_task_security.xml b/yuthon_will_task/security/yuthon_will_task_security.xml index d8e1b26a8..0056a273e 100644 --- a/yuthon_will_task/security/yuthon_will_task_security.xml +++ b/yuthon_will_task/security/yuthon_will_task_security.xml @@ -13,5 +13,13 @@ + + 会议组 + + + 会议管理员 + + + \ No newline at end of file diff --git a/yuthon_will_task/views/yuthon_task_menu.xml b/yuthon_will_task/views/yuthon_task_menu.xml index 1b7b5861e..c83c199c5 100644 --- a/yuthon_will_task/views/yuthon_task_menu.xml +++ b/yuthon_will_task/views/yuthon_task_menu.xml @@ -23,6 +23,7 @@ name="预定会议" action="room.room_booking_action" parent="menu_yuthon_will_task" + groups="yuthon_will_task.group_room_booking_manager" sequence="10"/> + decoration-success="priority == 'important'" limit="15"> - - - + + + - - - - + + + +