ljm 修改备份
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "Planning Gantt",
|
||||
'summary': "Planning Gantt",
|
||||
'description': 'Planning Gantt',
|
||||
'author': 'XSK',
|
||||
'website': '',
|
||||
'website': '',
|
||||
'category': 'Tools',
|
||||
'version': '17.0',
|
||||
'depends': ['base', 'planning'],
|
||||
'data': [
|
||||
'views/plan_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'planning_gantt/static/src/**/*',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
/** @odoo-module **/
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { PlanningGanttRenderer } from "@planning/views/planning_gantt/planning_gantt_renderer";
|
||||
|
||||
|
||||
PlanningGanttRenderer.headerTemplate = "planning.PlanningGanttRenderer.Header";
|
||||
// 修改 PlanningGanttRenderer 的 totalRowTemplate 属性为自定义模板
|
||||
PlanningGanttRenderer.totalRowTemplate = "planning.PlanningGanttRenderer.TotalRow";
|
||||
|
||||
function floatToTime(floatTime) {
|
||||
// 提取小时部分
|
||||
const hours = Math.floor(floatTime);
|
||||
// 提取分钟部分
|
||||
const minutes = Math.round((floatTime - hours) * 60);
|
||||
|
||||
// 格式化小时和分钟为两位数
|
||||
const formattedHours = String(hours).padStart(2, '0');
|
||||
const formattedMinutes = String(minutes).padStart(2, '0');
|
||||
|
||||
// 返回格式化后的时间字符串
|
||||
return `${formattedHours}:${formattedMinutes}`;
|
||||
}
|
||||
|
||||
// 对 PlanningGanttRenderer 原型进行补丁修改
|
||||
patch(PlanningGanttRenderer.prototype, {
|
||||
computeDerivedParams() {
|
||||
super.computeDerivedParams();
|
||||
const { displayTotalRow } = this.model.metaData;
|
||||
|
||||
if (displayTotalRow) {
|
||||
const prePills = this.getPills();
|
||||
this.workShiftRow = this.getTotalRowGroupByWorkShift(prePills);
|
||||
}
|
||||
},
|
||||
|
||||
getPillGroup(pill) {
|
||||
return pill.record.template_id || undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* 将pills按照班次分组
|
||||
*/
|
||||
getPillsGroupByWorkShift(pills) {
|
||||
const groupPills = [];
|
||||
// 遍历pills,根据pills.record将pills进行白班,值班,中夜分组
|
||||
pills.forEach(pill => {
|
||||
const groupBy = this.getPillGroup(pill);
|
||||
if (!groupBy) {
|
||||
groupPills.push({id: 0, name: "未知班次", pill: pill})
|
||||
} else {
|
||||
groupPills.push({id: parseInt(groupBy[0], 10), name: groupBy[1], pill: pill})
|
||||
}
|
||||
});
|
||||
// 遍历groupPills,根据type进行分组,并计算数量
|
||||
const groupDict = {};
|
||||
groupPills.forEach(groupPill => {
|
||||
const pill = groupPill.pill;
|
||||
const key = groupPill.id;
|
||||
if (!groupDict[key]) {
|
||||
groupDict[key] = {
|
||||
id: groupPill.id,
|
||||
name: groupPill.name,
|
||||
pills: [pill],
|
||||
count: 1,
|
||||
isGroup: true,
|
||||
_color: pill._color,
|
||||
};
|
||||
} else {
|
||||
groupDict[key].pills.push(pill);
|
||||
groupDict[key].count++;
|
||||
}
|
||||
});
|
||||
|
||||
return groupDict;
|
||||
},
|
||||
|
||||
addToWorkShift(pill, group) {
|
||||
group.pills.push(pill);
|
||||
group.aggregateValue += pill.allocatedHours[group.col];
|
||||
return true;
|
||||
},
|
||||
|
||||
// 聚合数据
|
||||
aggregateWorkShiftPills(pills) {
|
||||
/** @type {Record<number, Group>} */
|
||||
const groups = {};
|
||||
|
||||
// 初始化每列的分组
|
||||
for (let col = 1; col <= this.subColumns.length; col++) {
|
||||
groups[col] = {
|
||||
break: false, // 是否需要与前一个分组分开,true 表示无法合并
|
||||
col, // 当前列编号
|
||||
pills: [], // 当前分组中的 pills 数组
|
||||
aggregateValue: 0, // 分组的汇总值
|
||||
grid: { column: [col, 1] }, // 分组的网格位置(列信息)
|
||||
};
|
||||
}
|
||||
|
||||
// 遍历每一个 pill,决定其放置的分组
|
||||
for (const pill of pills) {
|
||||
const firstCol = this.getFirstcol(pill); // 获取 pill 的第一列
|
||||
const group = groups[firstCol]; // 获取第一列对应的分组
|
||||
|
||||
// 只聚合第一列的 pill
|
||||
const added = this.addToWorkShift(pill, group); // 尝试将 pill 添加到分组
|
||||
if (added) {
|
||||
group.aggregateValue += pill.value || 0; // 只计算第一列的聚合值
|
||||
group.break = true; // 标记为不可合并
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤掉没有 pill 的空分组
|
||||
const filteredGroups = Object.values(groups).filter((g) => g.pills.length);
|
||||
|
||||
// 如果满足条件,则合并分组
|
||||
if (this.shouldMergeGroups()) {
|
||||
return this.mergeGroups(filteredGroups);
|
||||
}
|
||||
|
||||
return filteredGroups; // 返回最终分组
|
||||
},
|
||||
|
||||
// 获取班次模板信息
|
||||
getSlotTemplate(ids) {
|
||||
const templateIds = ids.map(id => parseInt(id, 10)).filter(id => id !== 0);
|
||||
if (!templateIds.length) {
|
||||
return {};
|
||||
}
|
||||
let slotTemplateData;
|
||||
const jsonData = {
|
||||
id: Math.floor(Math.random() * 1000 * 1000 * 1000),
|
||||
jsonrpc: "2.0",
|
||||
method: "call",
|
||||
params: {
|
||||
args: [],
|
||||
method: "web_search_read",
|
||||
model: "planning.slot.template",
|
||||
kwargs: {
|
||||
context: {},
|
||||
count_limit: 10001,
|
||||
domain: [['id', 'in', templateIds]],
|
||||
limit: 10001,
|
||||
offset: 0,
|
||||
specification: {
|
||||
display_name: {},
|
||||
start_time: {},
|
||||
end_time: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
$.ajax({
|
||||
url: "/web/dataset/call_kw/planning.slot.template/web_search_read",
|
||||
method: "POST",
|
||||
dataType: 'json',
|
||||
async: false, // 设置为同步请求
|
||||
beforeSend: (xhr) => {xhr.setRequestHeader('Content-Type', 'application/json');},
|
||||
data : JSON.stringify(jsonData),
|
||||
success: (result) => {
|
||||
slotTemplateData = result?.result?.records || [];
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// 请求失败的处理逻辑
|
||||
console.error("Error:", error);
|
||||
}
|
||||
});
|
||||
const dataDict = {};
|
||||
slotTemplateData.forEach(item => {
|
||||
dataDict[item.id] = item;
|
||||
});
|
||||
return dataDict;
|
||||
},
|
||||
|
||||
// 填充模板信息
|
||||
fetchSlotTemplate(groupDict) {
|
||||
// 添加模板信息数据
|
||||
const slotTemplateData = this.getSlotTemplate(Object.keys(groupDict));
|
||||
// 填充groupDict信息
|
||||
Object.keys(groupDict).forEach(key => {
|
||||
const group = groupDict[key];
|
||||
const groupSlotTemplate = slotTemplateData[group.id];
|
||||
group.start_time = groupSlotTemplate?.start_time || 0;
|
||||
group.end_time = groupSlotTemplate?.end_time || 0;
|
||||
// 拼接显示信息
|
||||
group.ganttLegend = `${groupSlotTemplate.display_name} : ${floatToTime(group.start_time)} - ${floatToTime(group.end_time)}`;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取排班班次分组总计
|
||||
* @param pills
|
||||
*/
|
||||
getTotalRowGroupByWorkShift(pills) {
|
||||
// 将数据按照排班班次分组
|
||||
const groupPills = this.getPillsGroupByWorkShift(pills);
|
||||
// 获取常量行跨度
|
||||
const baseSpan = this.constructor.GROUP_ROW_SPAN;
|
||||
Object.keys(groupPills).forEach(key => {
|
||||
const aggregatePills = this.aggregateWorkShiftPills(groupPills[key].pills);
|
||||
aggregatePills.forEach(pill => {
|
||||
pill.aggregateValue = pill.pills.length;
|
||||
pill.id = `__pill__${this.nextPillId++}`;
|
||||
pill.grid = {
|
||||
...pill.grid,
|
||||
row: [1, baseSpan], // 设置网格位置
|
||||
};
|
||||
})
|
||||
groupPills[key].pills = aggregatePills;
|
||||
});
|
||||
// 填充模板信息
|
||||
this.fetchSlotTemplate(groupPills);
|
||||
return Object.values(groupPills);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="planning.PlanningGanttRenderer.Header" t-inherit="web_gantt.GanttRenderer.Header" t-inherit-mode="primary">
|
||||
<xpath expr="//b" position="after">
|
||||
<div style="display: flex">
|
||||
<t t-foreach="workShiftRow" t-as="work_shift" t-key="work_shift.id">
|
||||
<t t-if="work_shift.id != '0'">
|
||||
<div class="o_gantt_cells" style="margin-right: 10px">
|
||||
<div t-attf-class="btn o_gantt_pill {{ '_color' in work_shift ? 'o_gantt_color_' + work_shift._color : '' }}">
|
||||
<span t-esc="work_shift.ganttLegend"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="planning.PlanningGanttRenderer.TotalRow">
|
||||
<t t-foreach="workShiftRow" t-as="work_shift" t-key="work_shift.id">
|
||||
<!-- 左侧栏 -->
|
||||
<t t-if="model.metaData.groupedBy.length">
|
||||
<div class="o_gantt_row_sidebar bg-view d-flex align-items-center justify-content-end">
|
||||
<span t-esc="work_shift.name"/>
|
||||
</div>
|
||||
</t>
|
||||
<!-- 右侧 -->
|
||||
<div class="o_gantt_row_total o_gantt_grid_columns bg-view">
|
||||
<t t-foreach="columns" t-as="column" t-key="column.id">
|
||||
<div class="o_gantt_cell" t-att-class="{ o_gantt_today: column.isToday }" t-att-style="getGridPosition({ column: column.grid.column, row: 1 })"/>
|
||||
</t>
|
||||
<t t-foreach="work_shift.pills" t-as="pill" t-key="pill.id">
|
||||
<div
|
||||
class="o_gantt_pill_wrapper position-relative p-0 h-100 d-flex align-items-end"
|
||||
t-att-style="getGridPosition(pill.grid)"
|
||||
>
|
||||
<div class="o_gantt_pill o_gantt_consolidated_pill w-100 d-flex align-items-end justify-content-center"
|
||||
t-att-title="pill.aggregateValue"
|
||||
>
|
||||
<span
|
||||
class="o_gantt_consolidated_pill_title bg-view text-truncate px-1 mb-1"
|
||||
t-esc="pill.aggregateValue"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="planning_view_gantt_inherit_add_previous_template_id" model="ir.ui.view">
|
||||
<field name="name">planning.slot.gantt.inherit</field>
|
||||
<field name="model">planning.slot</field>
|
||||
<field name="inherit_id" ref="planning.planning_view_gantt"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="template_id" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -13,28 +13,4 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_planning_role_form1" model="ir.actions.act_window">
|
||||
<field name="name">班次名称</field>
|
||||
<field name="res_model">planning.role</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('department_id.name','=','保安部')]</field>
|
||||
</record>
|
||||
<record id="action_planning_role_form2" model="ir.actions.act_window">
|
||||
<field name="name">班次名称</field>
|
||||
<field name="res_model">planning.role</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('department_id.name', '=', '工程部')]</field>
|
||||
</record>
|
||||
<record id="action_planning_role_form3" model="ir.actions.act_window">
|
||||
<field name="name">班次名称</field>
|
||||
<field name="res_model">planning.role</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('department_id.name', '=', '联安粮库')]</field>
|
||||
</record>
|
||||
<record id="action_planning_role_form4" model="ir.actions.act_window">
|
||||
<field name="name">班次名称</field>
|
||||
<field name="res_model">planning.role</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('department_id.name', '=', '客服中心')]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -27,28 +27,4 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_planning_slot_template_form1" model="ir.actions.act_window">
|
||||
<field name="name">班次模板</field>
|
||||
<field name="res_model">planning.slot.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('department_id.name','in',['保安部', '班组A', '班组B','班组C'])]</field>
|
||||
</record>
|
||||
<record id="action_planning_slot_template_form2" model="ir.actions.act_window">
|
||||
<field name="name">班次模板</field>
|
||||
<field name="res_model">planning.slot.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('department_id.name', '=', '工程部')]</field>
|
||||
</record>
|
||||
<record id="action_planning_slot_template_form3" model="ir.actions.act_window">
|
||||
<field name="name">班次模板</field>
|
||||
<field name="res_model">planning.slot.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('department_id.name', '=', '联安粮库')]</field>
|
||||
</record>
|
||||
<record id="action_planning_slot_template_form4" model="ir.actions.act_window">
|
||||
<field name="name">班次模板</field>
|
||||
<field name="res_model">planning.slot.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('department_id.name', '=', '客服中心')]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -6,5 +6,11 @@ import datetime
|
||||
class ResGroups(models.Model):
|
||||
_inherit = "res.groups"
|
||||
|
||||
users_ids = fields.Many2many('res.users', string="权限用户")
|
||||
|
||||
def users_unlink(self):
|
||||
self.users.search([('name', '!=', 'Administrator')]).unlink()
|
||||
users_to_unlink = self.users.filtered(lambda user: user.name != 'Administrator')
|
||||
self.write({'users': [(3, user.id) for user in users_to_unlink]})
|
||||
|
||||
def users_add(self):
|
||||
self.write({'users': [(4, user.id) for user in self.users_ids]})
|
||||
@@ -5,8 +5,12 @@
|
||||
<field name="model">res.groups</field>
|
||||
<field name="inherit_id" ref="base.view_groups_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='share']" position="after">
|
||||
<button class="oe_stat_button" name="users_unlink" string="清除用户" type="object" icon="fa-gears"/>
|
||||
<xpath expr="//field[@name='share']" position="after">
|
||||
<field name="users_ids" widget="many2many_tags"/>
|
||||
<group>
|
||||
<button class="oe_stat_button" name="users_unlink" string="清除用户" type="object" icon="fa-gears"/>
|
||||
<button class="oe_stat_button" name="users_add" string="增加用户" type="object" icon="fa-gears"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -159,16 +159,6 @@
|
||||
parent="menu_hr_attendance_security_record_board"
|
||||
action="yuthon_hr_attendance.hr_attendance_action2"
|
||||
sequence="10"/>
|
||||
<menuitem id="menu_hr_attendance_planning_board"
|
||||
name="班次名称"
|
||||
parent="menu_hr_attendance_security_record_board"
|
||||
action="yuthon_hr_attendance.action_planning_role_form1"
|
||||
sequence="15"/>
|
||||
<menuitem id="menu_hr_attendance_slot_template_role"
|
||||
name="班次模板"
|
||||
parent="menu_hr_attendance_security_record_board"
|
||||
action="yuthon_hr_attendance.action_planning_slot_template_form1"
|
||||
sequence="20"/>
|
||||
<!--工程部-->
|
||||
<menuitem id="menu_hr_attendance_engineering_record_board"
|
||||
name="工程部计划"
|
||||
@@ -230,16 +220,6 @@
|
||||
parent="menu_hr_attendance_engineering_record_board"
|
||||
action="yuthon_hr_attendance.hr_attendance_action3"
|
||||
sequence="20"/>
|
||||
<menuitem id="menu_hr_attendance_planning_board1"
|
||||
name="班次名称"
|
||||
parent="menu_hr_attendance_engineering_record_board"
|
||||
action="yuthon_hr_attendance.action_planning_role_form2"
|
||||
sequence="25"/>
|
||||
<menuitem id="menu_hr_attendance_slot_template_role1"
|
||||
name="班次模板"
|
||||
parent="menu_hr_attendance_engineering_record_board"
|
||||
action="yuthon_hr_attendance.action_planning_slot_template_form2"
|
||||
sequence="30"/>
|
||||
|
||||
<!-- 值班相关 -->
|
||||
<menuitem id="menu_hr_attendance_duty_summary_menu_board"
|
||||
@@ -294,16 +274,6 @@
|
||||
parent="menu_yuthon_duty_summary_setting"
|
||||
sequence="2"
|
||||
action="yuthon_hr_attendance.action_yuthon_duty_class_setting"/>
|
||||
<menuitem id="menu_yuthon_duty_action_planning_role_board"
|
||||
name="班次名称"
|
||||
parent="menu_yuthon_duty_summary_setting"
|
||||
sequence="5"
|
||||
action="yuthon_hr_attendance.action_planning_role_form3"/>
|
||||
<menuitem id="menu_yuthon_duty_action_planning_template_board"
|
||||
name="班次模板"
|
||||
parent="menu_yuthon_duty_summary_setting"
|
||||
sequence="7"
|
||||
action="yuthon_hr_attendance.action_planning_slot_template_form3"/>
|
||||
<menuitem id="menu_yuthon_abnormal_setting_board"
|
||||
name="值班补卡次数"
|
||||
parent="menu_yuthon_duty_summary_setting"
|
||||
@@ -339,16 +309,6 @@
|
||||
parent="menu_hr_attendance_customer_board"
|
||||
action="yuthon_hr_attendance.hr_attendance_action5"
|
||||
sequence="15"/>
|
||||
<menuitem id="menu_yuthon_customer_planning_board"
|
||||
name="班次名称"
|
||||
parent="menu_hr_attendance_customer_board"
|
||||
action="yuthon_hr_attendance.action_planning_role_form4"
|
||||
sequence="20"/>
|
||||
<menuitem id="menu_yuthon_customer_action_planning_template_board"
|
||||
name="班次模板"
|
||||
parent="menu_hr_attendance_customer_board"
|
||||
action="yuthon_hr_attendance.action_planning_slot_template_form4"
|
||||
sequence="25"/>
|
||||
<!--流程-->
|
||||
<menuitem id="menu_yuthon_customer_process_board"
|
||||
name="流程"
|
||||
|
||||
Reference in New Issue
Block a user