添加文件
This commit is contained in:
@@ -1351,3 +1351,82 @@ class ReportExcel(models.Model):
|
||||
output.seek(0)
|
||||
response.stream.write(output.read())
|
||||
output.close()
|
||||
|
||||
def get_xlsx_meal_active_report(self, data, response):
|
||||
"""误餐归档明细报表的自定义 Excel 样式"""
|
||||
output = io.BytesIO()
|
||||
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||
sheet = workbook.add_worksheet('误餐归档明细')
|
||||
tz = self.env.user.tz or 'Asia/Shanghai'
|
||||
from pytz import timezone
|
||||
user_tz = timezone(tz)
|
||||
header_format = workbook.add_format({'font_size': 11, 'bold': True, 'border': 1, 'align': 'center', 'valign': 'vcenter', 'bg_color': '#D3D3D3'})
|
||||
data_format = workbook.add_format({'font_size': 10, 'border': 1, 'align': 'center', 'valign': 'vcenter'})
|
||||
datetime_format = workbook.add_format({'font_size': 10, 'border': 1, 'align': 'center', 'valign': 'vcenter', 'num_format': 'yyyy-mm-dd hh:mm'})
|
||||
date_format = workbook.add_format({'font_size': 10, 'border': 1, 'align': 'center', 'valign': 'vcenter', 'num_format': 'yyyy-mm-dd'})
|
||||
col = 0
|
||||
for label in data['field_label']:
|
||||
sheet.write(0, col, label, header_format)
|
||||
if col == 0:
|
||||
sheet.set_column(col, col, 15)
|
||||
elif col == 1:
|
||||
sheet.set_column(col, col, 18)
|
||||
elif col == 2:
|
||||
sheet.set_column(col, col, 18)
|
||||
elif col in [3, 4]:
|
||||
sheet.set_column(col, col, 20)
|
||||
elif col == 6:
|
||||
sheet.set_column(col, col, 40)
|
||||
else:
|
||||
sheet.set_column(col, col, 15)
|
||||
col += 1
|
||||
records = []
|
||||
active_ids = data.get('active_ids')
|
||||
if active_ids:
|
||||
records = self.env[data['model_name']].browse(active_ids)
|
||||
else:
|
||||
records = self.env[data['model_name']].search([])
|
||||
row = 1
|
||||
for rec in records:
|
||||
col = 0
|
||||
for field_name in data['fields_name']:
|
||||
field = rec._fields.get(field_name)
|
||||
value = rec[field_name]
|
||||
if isinstance(value, datetime.datetime):
|
||||
if value:
|
||||
from pytz import UTC
|
||||
value_utc = UTC.localize(value) if value.tzinfo is None else value
|
||||
value_local = value_utc.astimezone(user_tz)
|
||||
sheet.write_datetime(row, col, value_local.replace(tzinfo=None), datetime_format)
|
||||
else:
|
||||
sheet.write(row, col, '', data_format)
|
||||
elif isinstance(value, datetime.date):
|
||||
sheet.write_datetime(row, col, value, date_format)
|
||||
elif field:
|
||||
if field.type == 'many2one':
|
||||
value = value.display_name if value else ''
|
||||
sheet.write(row, col, value, data_format)
|
||||
elif field.type == 'many2many':
|
||||
value = ', '.join(value.mapped('display_name')) if value else ''
|
||||
sheet.write(row, col, value, data_format)
|
||||
elif field.type == 'selection':
|
||||
selection_dict = dict(field.selection)
|
||||
value = selection_dict.get(value, value or '')
|
||||
sheet.write(row, col, value, data_format)
|
||||
elif field.type == 'float':
|
||||
sheet.write(row, col, value or 0, data_format)
|
||||
elif field.type == 'text':
|
||||
sheet.write(row, col, value or '', data_format)
|
||||
else:
|
||||
sheet.write(row, col, value or '', data_format)
|
||||
else:
|
||||
sheet.write(row, col, value or '', data_format)
|
||||
col += 1
|
||||
row += 1
|
||||
if row > 1:
|
||||
sheet.autofilter(0, 0, row - 1, len(data['field_label']) - 1)
|
||||
sheet.freeze_panes(1, 0)
|
||||
workbook.close()
|
||||
output.seek(0)
|
||||
response.stream.write(output.read())
|
||||
output.close()
|
||||
|
||||
@@ -113,6 +113,8 @@
|
||||
'yuthon_hr_attendance/static/src/xml/button_overtime_export.xml',
|
||||
'yuthon_hr_attendance/static/src/js/button_overtime_active_export.js',
|
||||
'yuthon_hr_attendance/static/src/xml/button_overtime_active_export.xml',
|
||||
'yuthon_hr_attendance/static/src/js/button_meal_active_export.js',
|
||||
'yuthon_hr_attendance/static/src/xml/button_meal_active_export.xml',
|
||||
'yuthon_hr_attendance/static/src/js/button_vacation_put.js',
|
||||
'yuthon_hr_attendance/static/src/xml/button_vacation_put.xml',
|
||||
'yuthon_hr_attendance/static/src/planning_gantt/planning_gantt_controller.xml',
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/** @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 CustomMealActiveExportController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
async exportMealActiveExcel() {
|
||||
const selectedIds = await this.getSelectedResIds();
|
||||
|
||||
return this.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'export.date.wizard',
|
||||
name: '打印薪资',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
default_export_type: 'meal_active',
|
||||
default_model_name: 'vacation.meal.active',
|
||||
default_active_ids: JSON.stringify(selectedIds),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("views").add("custom_meal_active_export_button", {
|
||||
...listView,
|
||||
Controller: CustomMealActiveExportController,
|
||||
buttonTemplate: "meal_active_export_module.ListView.Buttons",
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
/** @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 CustomOvertimeActiveExportController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
async exportOvertimeActiveExcel() {
|
||||
// 获取选中的记录 ID
|
||||
const selectedIds = await this.getSelectedResIds();
|
||||
|
||||
// 直接打开向导,不经过 Python 方法
|
||||
return this.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'export.date.wizard',
|
||||
name: '打印薪资',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
default_export_type: 'overtime_active',
|
||||
default_model_name: 'overtime.active.line',
|
||||
default_active_ids: JSON.stringify(selectedIds),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("views").add("custom_overtime_active_export_button", {
|
||||
...listView,
|
||||
Controller: CustomOvertimeActiveExportController,
|
||||
buttonTemplate: "overtime_active_export_module.ListView.Buttons",
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/** @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 CustomOvertimeExportController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
async exportOvertimeExcel() {
|
||||
// 获取当前选中的记录 ID(如果有选中)
|
||||
const selectedIds = await this.getSelectedResIds();
|
||||
|
||||
return this.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'export.date.wizard',
|
||||
name: '打印薪资',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
default_export_type: 'overtime',
|
||||
default_model_name: 'vacation.over.line',
|
||||
default_active_ids: JSON.stringify(selectedIds),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async exportOvertimeDetailExcel() {
|
||||
// 导出加班详情(包含审批类型、开始结束时间、事由、餐费等)
|
||||
const selectedIds = await this.getSelectedResIds();
|
||||
|
||||
return this.actionService.doAction({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'export.date.wizard',
|
||||
name: '打印薪资',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
default_export_type: 'overtime_detail',
|
||||
default_model_name: 'vacation.over.line',
|
||||
default_active_ids: JSON.stringify(selectedIds),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registry.category("views").add("custom_overtime_export_button", {
|
||||
...listView,
|
||||
Controller: CustomOvertimeExportController,
|
||||
buttonTemplate: "overtime_export_module.ListView.Buttons",
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
<t t-name="meal_active_export_module.ListView.Buttons" t-inherit="web.ListView.Buttons">
|
||||
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
|
||||
<button type="button" class="btn btn-success" style="margin-left: 10px;" t-on-click="exportMealActiveExcel">
|
||||
导出误餐归档明细
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
<t t-name="overtime_active_export_module.ListView.Buttons" t-inherit="web.ListView.Buttons">
|
||||
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
|
||||
<button type="button" class="btn btn-success" style="margin-left: 10px;" t-on-click="exportOvertimeActiveExcel">
|
||||
导出加班归档明细
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates>
|
||||
<t t-name="overtime_export_module.ListView.Buttons" t-inherit="web.ListView.Buttons">
|
||||
<xpath expr="//div[hasclass('o_list_buttons')]" position="after">
|
||||
<button type="button" class="btn btn-success" style="margin-left: 10px;" t-on-click="exportOvertimeExcel">
|
||||
导出加班明细
|
||||
</button>
|
||||
<button type="button" class="btn btn-info" style="margin-left: 10px;" t-on-click="exportOvertimeDetailExcel">
|
||||
导出加班详情
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -4,7 +4,7 @@
|
||||
<field name="name">vacation.meal.active.tree</field>
|
||||
<field name="model">vacation.meal.active</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="误餐记录" editable="bottom">
|
||||
<tree string="误餐记录" editable="bottom" js_class="custom_meal_active_export_button">
|
||||
<header>
|
||||
<button name="revoke" type="object" string="批量撤销" dclass="oe_highlight"/>
|
||||
</header>
|
||||
|
||||
@@ -43,6 +43,8 @@ class ExportDateWizard(models.TransientModel):
|
||||
return self._export_overtime_active(start_date, end_date)
|
||||
elif self.export_type == 'meal':
|
||||
return self._export_meal(start_date, end_date)
|
||||
elif self.export_type == 'meal_active':
|
||||
return self._export_meal_active(start_date, end_date)
|
||||
else:
|
||||
raise UserError('未知的导出类型')
|
||||
|
||||
@@ -282,3 +284,42 @@ class ExportDateWizard(models.TransientModel):
|
||||
},
|
||||
'report_type': 'xlsx',
|
||||
}
|
||||
|
||||
def _export_meal_active(self, start_date, end_date):
|
||||
"""导出误餐归档明细"""
|
||||
active_ids = json.loads(self.active_ids) if self.active_ids else []
|
||||
|
||||
domain = [('start_time', '>=', start_date), ('start_time', '<', end_date)]
|
||||
if active_ids:
|
||||
domain = [('id', 'in', active_ids)] + domain
|
||||
|
||||
filtered_records = self.env['vacation.meal.active'].search(domain)
|
||||
|
||||
if not filtered_records:
|
||||
raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的误餐归档记录')
|
||||
|
||||
filtered_ids = filtered_records.ids
|
||||
|
||||
fields_name = ['employee_id', 'department_id', 'workflow_id', 'start_time',
|
||||
'end_time', 'duration', 'reasons', 'meal']
|
||||
field_label = ['发起人', '部门', '审批类型', '加班开始时间', '加班结束时间',
|
||||
'加班时长', '事由', '加班餐费']
|
||||
|
||||
data = {
|
||||
'report_name': '误餐归档明细报表',
|
||||
'model_name': 'vacation.meal.active',
|
||||
'fields_name': fields_name,
|
||||
'field_label': field_label,
|
||||
'active_ids': filtered_ids,
|
||||
}
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.report',
|
||||
'data': {
|
||||
'model': 'report.excel',
|
||||
'options': json.dumps(data, default=date_utils.json_default),
|
||||
'output_format': 'xlsx',
|
||||
'report_name': '误餐归档明细报表',
|
||||
},
|
||||
'report_type': 'xlsx',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user