ljm 修改备份

This commit is contained in:
李鹏宇
2024-12-24 11:36:19 +08:00
parent 0a05eccecb
commit 3792140bc0
10 changed files with 318 additions and 91 deletions
+1
View File
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
+21
View File
@@ -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>
+16
View File
@@ -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="流程"