diff --git a/excel_report_designer/models/report_excel.py b/excel_report_designer/models/report_excel.py index aa14f078b..b15ccbef6 100644 --- a/excel_report_designer/models/report_excel.py +++ b/excel_report_designer/models/report_excel.py @@ -1032,6 +1032,7 @@ class ReportExcel(models.Model): output.seek(0) response.stream.write(output.read()) output.close() + def get_xlsx_overtime_report(self, data, response): """加班明细报表的自定义 Excel 样式""" output = io.BytesIO() @@ -1155,17 +1156,94 @@ class ReportExcel(models.Model): response.stream.write(output.read()) output.close() + def _export_overtime_active(self, start_date, end_date): + """导出加班归档明细""" + active_ids = json.loads(self.active_ids) if self.active_ids else [] + domain = [ + ('carry_forward_date', '>=', start_date), + ('carry_forward_date', '<', end_date), + ('is_void', '=', False), + ('carryover_type', '=', 'fee') + ] + if active_ids: + domain = [('id', 'in', active_ids)] + domain + filtered_records = self.env['overtime.carryover'].search(domain) + + if not filtered_records: + raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的加班归档记录') + + export_data = [] + for record in filtered_records: + over_line = record.over_line_id + # 提前处理时区转换,避免在Excel函数中重复处理 + tz = self.env.user.tz or 'Asia/Shanghai' + user_tz = timezone(tz) + + # 处理开始时间时区转换 + start_time = '' + if over_line and over_line.start_time: + start_time_utc = UTC.localize( + over_line.start_time) if over_line.start_time.tzinfo is None else over_line.start_time + start_time = start_time_utc.astimezone(user_tz).replace(tzinfo=None) + + # 处理结束时间时区转换 + end_time = '' + if over_line and over_line.end_time: + end_time_utc = UTC.localize( + over_line.end_time) if over_line.end_time.tzinfo is None else over_line.end_time + end_time = end_time_utc.astimezone(user_tz).replace(tzinfo=None) + + row_data = { + 'employee_id': over_line.employee_id.name if over_line and over_line.employee_id else '', + 'department_id': over_line.department_id.name if over_line and over_line.department_id else '', + 'start_time': start_time, + 'end_time': end_time, + 'carry_forward_date': record.carry_forward_date, + 'carry_forward_quantity': record.carry_forward_quantity or 0, + 'carry_forward_state': dict(record._fields.get('carry_forward_state').selection).get( + record.carry_forward_state, '') if hasattr(record, 'carry_forward_state') else '' + } + export_data.append(row_data) + + fields_name = ['employee_id', 'department_id', 'start_time', 'end_time', + 'carry_forward_date', 'carry_forward_quantity', 'carry_forward_state'] + field_label = ['姓名', '部门', '加班开始时间', '加班结束时间', '结转日期', + '结转时长', '结转结果'] + + data = { + 'report_name': '加班归档明细报表', + 'model_name': 'overtime.carryover', + 'fields_name': fields_name, + 'field_label': field_label, + 'export_data': export_data, # 传递预处理好的数据 + } + + 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', + } + def get_xlsx_overtime_active_report(self, data, response): """加班归档明细报表的自定义 Excel 样式""" + import io + import xlsxwriter + from pytz import timezone, UTC + from datetime import datetime, date + 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, @@ -1198,8 +1276,8 @@ class ReportExcel(models.Model): 'valign': 'vcenter', 'num_format': 'yyyy-mm-dd' }) - - # 写表头 + + # 写表头(保持原有样式) col = 0 for label in data['field_label']: sheet.write(0, col, label, header_format) @@ -1213,61 +1291,36 @@ class ReportExcel(models.Model): 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([]) - + + # ========== 核心修复:使用预处理的 export_data ========== + records_data = data.get('export_data', []) + # 写数据行 row = 1 - for rec in records: + for rec_data in records_data: 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): + value = rec_data.get(field_name, '') + + # 根据值类型应用对应格式 + if isinstance(value, datetime): + sheet.write_datetime(row, col, value, datetime_format) + elif isinstance(value, 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) - else: - sheet.write(row, col, value or '', data_format) + elif isinstance(value, float): + sheet.write(row, col, value, 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()) diff --git a/yuthon_hr_attendance/wizard/export_date_wizard.py b/yuthon_hr_attendance/wizard/export_date_wizard.py index e69f89fe5..27cea0147 100644 --- a/yuthon_hr_attendance/wizard/export_date_wizard.py +++ b/yuthon_hr_attendance/wizard/export_date_wizard.py @@ -212,31 +212,48 @@ class ExportDateWizard(models.TransientModel): def _export_overtime_active(self, start_date, end_date): """导出加班归档明细""" active_ids = json.loads(self.active_ids) if self.active_ids else [] - - domain = [('carry_forward_date', '>=', start_date), ('carry_forward_date', '<', end_date), ('is_void','=', False), ('carry_forward_quantity', '>', 0)] + domain = [ + ('carry_forward_date', '>=', start_date), + ('carry_forward_date', '<', end_date), + ('is_void', '=', False), + ('carryover_type', '=', 'fee') + ] if active_ids: domain = [('id', 'in', active_ids)] + domain - - filtered_records = self.env['overtime.active.line'].search(domain) - + filtered_records = self.env['overtime.carryover'].search(domain) + if not filtered_records: raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的加班归档记录') - - filtered_ids = filtered_records.ids - - fields_name = ['employee_id', 'department_id', 'start_time', 'end_time', + export_data = [] + for record in filtered_records: + over_line = record.over_line_id + carry_forward_state_value = '' + if over_line and over_line.carry_forward_state: + selection_dict = dict(over_line._fields.get('carry_forward_state').selection) + carry_forward_state_value = selection_dict.get(over_line.carry_forward_state, '') + row_data = { + 'employee_id': over_line.employee_id.name if over_line and over_line.employee_id else '', + 'department_id': over_line.department_id.name if over_line and over_line.department_id else '', + 'start_time': over_line.start_time if over_line else '', + 'end_time': over_line.end_time if over_line else '', + 'carry_forward_date': record.carry_forward_date, + 'carry_forward_quantity': record.carry_forward_quantity, + 'carry_forward_state': carry_forward_state_value + } + export_data.append(row_data) + fields_name = ['employee_id', 'department_id', 'start_time', 'end_time', 'carry_forward_date', 'carry_forward_quantity', 'carry_forward_state'] - field_label = ['姓名', '部门', '加班开始时间', '加班结束时间', '结转日期', + field_label = ['姓名', '部门', '加班开始时间', '加班结束时间', '结转日期', '结转时长', '结转结果'] - + data = { 'report_name': '加班归档明细报表', - 'model_name': 'overtime.active.line', + 'model_name': 'overtime.carryover', 'fields_name': fields_name, 'field_label': field_label, - 'active_ids': filtered_ids, + 'export_data': export_data, } - + return { 'type': 'ir.actions.report', 'data': {