473 lines
20 KiB
Python
473 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
||
from odoo import fields, models
|
||
import json
|
||
from odoo.tools import date_utils
|
||
from datetime import datetime, timedelta
|
||
from odoo.exceptions import UserError
|
||
|
||
|
||
class ExportDateWizard(models.TransientModel):
|
||
_name = 'export.date.wizard'
|
||
_description = '导出日期选择向导'
|
||
|
||
year = fields.Integer(string='结转年份', required=True, default=lambda self: datetime.now().year)
|
||
month = fields.Selection([
|
||
('1', '1月'), ('2', '2月'), ('3', '3月'), ('4', '4月'),
|
||
('5', '5月'), ('6', '6月'), ('7', '7月'), ('8', '8月'),
|
||
('9', '9月'), ('10', '10月'), ('11', '11月'), ('12', '12月'),
|
||
], string='结转月份', required=True, default=lambda self: str(datetime.now().month))
|
||
|
||
export_type = fields.Char(string='导出类型')
|
||
model_name = fields.Char(string='模型名称')
|
||
active_ids = fields.Char(string='活动记录ID')
|
||
|
||
def action_confirm_export(self):
|
||
"""确认导出"""
|
||
self.ensure_one()
|
||
|
||
start_date = datetime(int(self.year), int(self.month), 1)
|
||
if int(self.month) == 12:
|
||
end_date = datetime(int(self.year) + 1, 1, 1)
|
||
else:
|
||
end_date = datetime(int(self.year), int(self.month) + 1, 1)
|
||
|
||
if self.export_type == 'leave':
|
||
return self._export_leave(start_date, end_date)
|
||
elif self.export_type == 'leave_filing':
|
||
return self._export_leave_filing(start_date, end_date)
|
||
elif self.export_type == 'overtime':
|
||
return self._export_overtime(start_date, end_date)
|
||
elif self.export_type == 'overtime_detail':
|
||
return self._export_overtime_detail(start_date, end_date)
|
||
elif self.export_type == 'overtime_active':
|
||
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)
|
||
elif self.export_type == 'overtime_carryover':
|
||
return self._export_overtime_carryover(start_date, end_date)
|
||
|
||
else:
|
||
raise UserError('未知的导出类型')
|
||
|
||
def _export_leave(self, start_date, end_date):
|
||
"""导出请假情况"""
|
||
active_ids = json.loads(self.active_ids) if self.active_ids else []
|
||
|
||
domain = [('date_from', '>=', start_date), ('date_from', '<', end_date), ('state', '!=', 'refuse')]
|
||
if active_ids:
|
||
domain = [('id', 'in', active_ids)] + domain
|
||
|
||
filtered_records = self.env['hr.leave'].search(domain)
|
||
|
||
if not filtered_records:
|
||
raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的请假记录')
|
||
|
||
filtered_ids = filtered_records.ids
|
||
|
||
fields_name = ['employee_id', 'department_id', 'holiday_status_id',
|
||
'date_from', 'date_to', 'length_leave', 'application_reason', 'mobile_phone']
|
||
field_label = ['发起人', '部门', '休假类型', '开始时间', '结束时间',
|
||
'请假时长', '申请原因', '手机号']
|
||
|
||
data = {
|
||
'report_name': '请假情况报表',
|
||
'model_name': 'hr.leave',
|
||
'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',
|
||
}
|
||
|
||
def _export_leave_filing(self, start_date, end_date):
|
||
"""导出归档请假情况(is_filing=True)"""
|
||
active_ids = json.loads(self.active_ids) if self.active_ids else []
|
||
|
||
domain = [
|
||
('date_from', '>=', start_date),
|
||
('date_from', '<', end_date),
|
||
('is_filing', '=', True)
|
||
]
|
||
if active_ids:
|
||
domain = [('id', 'in', active_ids)] + domain
|
||
|
||
filtered_records = self.env['hr.leave'].search(domain)
|
||
|
||
if not filtered_records:
|
||
raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的归档请假记录')
|
||
|
||
filtered_ids = filtered_records.ids
|
||
|
||
fields_name = ['employee_id', 'department_id', 'holiday_status_id',
|
||
'date_from', 'date_to', 'length_leave', 'application_reason', 'mobile_phone']
|
||
field_label = ['发起人', '部门', '休假类型', '开始时间', '结束时间',
|
||
'请假时长', '申请原因', '手机号']
|
||
|
||
data = {
|
||
'report_name': '归档请假情况报表',
|
||
'model_name': 'hr.leave',
|
||
'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',
|
||
}
|
||
|
||
def _export_overtime(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),
|
||
'|', ('workflow_record_id', '!=', False), ('is_initial_number', '=', True),
|
||
('update_type', '=', 'add'), ('is_filing', '=', False), ('is_visible', '=', False)]
|
||
if active_ids:
|
||
domain = [('id', 'in', active_ids)] + domain
|
||
|
||
filtered_records = self.env['vacation.over.line'].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', 'carry_forward_date',
|
||
'duration', 'carry_result']
|
||
field_label = ['员工', '部门', '加班时间', '结转日期', '结转时长', '结转结果']
|
||
|
||
data = {
|
||
'report_name': '加班明细报表',
|
||
'model_name': 'vacation.over.line',
|
||
'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',
|
||
}
|
||
|
||
def _export_overtime_detail(self, start_date, end_date):
|
||
"""导出加班详情 - 对应 vacation.over.line.overtime.tree 视图字段"""
|
||
active_ids = json.loads(self.active_ids) if self.active_ids else []
|
||
|
||
carry_forward_state_map = {
|
||
'pending': '待结转',
|
||
'completed': '已结转',
|
||
'partial': '部分结转',
|
||
}
|
||
|
||
if active_ids:
|
||
records = self.env['vacation.over.line'].browse(active_ids)
|
||
else:
|
||
domain = [('start_time', '>=', start_date), ('start_time', '<', end_date)]
|
||
records = self.env['vacation.over.line'].search(domain)
|
||
|
||
if not records:
|
||
raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的加班记录')
|
||
|
||
export_data = []
|
||
for rec in records:
|
||
export_data.append({
|
||
'overtime_name': rec.overtime_name or '',
|
||
'employee_id': rec.employee_id.name if rec.employee_id else '',
|
||
'department_id': rec.department_id.name if rec.department_id else '',
|
||
'start_time': (rec.start_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if rec.start_time else '',
|
||
'end_time': (rec.end_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if rec.end_time else '',
|
||
'duration': rec.duration,
|
||
'carry_forward_quantity': rec.carry_forward_quantity,
|
||
'carry_forward_date': rec.carry_forward_date,
|
||
'carry_forward_state': carry_forward_state_map.get(rec.carry_forward_state, rec.carry_forward_state or ''),
|
||
'remaining_quantity': rec.remaining_quantity,
|
||
'validity': rec.validity or '',
|
||
})
|
||
|
||
fields_name = ['overtime_name', 'employee_id', 'department_id', 'start_time', 'end_time',
|
||
'duration', 'carry_forward_quantity', 'carry_forward_date',
|
||
'carry_forward_state', 'remaining_quantity', 'validity']
|
||
field_label = ['标题', '员工', '部门', '开始时间', '结束时间',
|
||
'加班时长(小时)', '结转时长(小时)', '结转日期',
|
||
'结转状态', '剩余数', '有效期']
|
||
|
||
data = {
|
||
'report_name': '加班详情报表',
|
||
'model_name': 'vacation.over.line',
|
||
'fields_name': fields_name,
|
||
'field_label': field_label,
|
||
'export_data': export_data,
|
||
'date_field': None,
|
||
}
|
||
|
||
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 _export_overtime_active(self, start_date, end_date):
|
||
"""导出加班归档明细
|
||
规则:弹窗填N年M月 → 查 select_year/select_month = N年(M-1)月 的 vacation.overtime.active 记录(每人一行),
|
||
对每条归档记录复用 open_record 的 domain + 累减逻辑,汇总为每人一行后导出。
|
||
"""
|
||
import datetime as dt
|
||
|
||
year = int(self.year)
|
||
month = int(self.month)
|
||
|
||
# 往前推一个月
|
||
if month == 1:
|
||
prev_year, prev_month = year - 1, 12
|
||
else:
|
||
prev_year, prev_month = year, month - 1
|
||
|
||
carry_forward_state_map = {
|
||
'pending': '待结转',
|
||
'completed': '已结转',
|
||
'partial': '部分结转',
|
||
}
|
||
|
||
active_records = self.env['vacation.overtime.active'].search([
|
||
('select_year', '=', str(prev_year)),
|
||
('select_month', '=', str(prev_month)),
|
||
('select_revoke', '=', False)
|
||
])
|
||
|
||
if not active_records:
|
||
raise UserError(f'在 {prev_year} 年 {prev_month} 月没有找到加班归档记录')
|
||
|
||
export_data = []
|
||
for active in active_records:
|
||
# 复用 open_record 的 domain
|
||
final_domain = [
|
||
('over_line_id.vacation_id.employee_id', '=', active.employee_id.id),
|
||
('is_void', '=', False),
|
||
('carryover_type', '=', 'fee'),
|
||
('carry_forward_date', '=', active.carry_forward_date),
|
||
]
|
||
carryover_records = self.env['overtime.carryover'].search(final_domain, order='start_time asc')
|
||
if not carryover_records:
|
||
continue
|
||
|
||
# 复用 open_record 的累减逻辑,实时计算每条的 last_month/this_month
|
||
current_remaining = active.vacation_sum
|
||
for carry in carryover_records:
|
||
last_month_val = current_remaining
|
||
this_month_val = round(current_remaining - carry.carry_forward_quantity, 3)
|
||
current_remaining = this_month_val
|
||
|
||
over_line = carry.over_line_id
|
||
export_data.append({
|
||
'employee_id': active.employee_id.name if active.employee_id else '',
|
||
'department_id': carry.department_id.name if carry.department_id else '',
|
||
'start_time': (over_line.start_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if over_line and over_line.start_time else '',
|
||
'end_time': (over_line.end_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if over_line and over_line.end_time else '',
|
||
'carry_forward_date': carry.carry_forward_date,
|
||
'carry_forward_quantity1': carry.carry_forward_quantity,
|
||
'carry_forward_state': carry_forward_state_map.get(carry.carry_forward_state, carry.carry_forward_state or ''),
|
||
})
|
||
|
||
if not export_data:
|
||
raise UserError(f'在 {prev_year} 年 {prev_month} 月没有找到符合条件的加班归档记录')
|
||
|
||
fields_name = ['employee_id', 'department_id', 'start_time', 'end_time',
|
||
'carry_forward_date', 'carry_forward_quantity1', 'carry_forward_state']
|
||
field_label = ['姓名', '部门', '加班开始时间', '加班结束时间',
|
||
'结转日期', '结转时长(小时)', '结转状态']
|
||
|
||
data = {
|
||
'report_name': '加班归档明细报表',
|
||
'model_name': 'overtime.carryover',
|
||
'fields_name': fields_name,
|
||
'field_label': field_label,
|
||
'export_data': export_data,
|
||
'date_field': None,
|
||
}
|
||
|
||
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 _export_overtime_carryover(self, start_date, end_date):
|
||
"""导出加班结转明细(overtime.carryover.tree1 视图数据)"""
|
||
active_ids = json.loads(self.active_ids) if self.active_ids else []
|
||
|
||
if active_ids:
|
||
records = self.env['overtime.carryover'].browse(active_ids)
|
||
else:
|
||
records = self.env['overtime.carryover'].search([
|
||
('carry_forward_date', '>=', start_date.date()),
|
||
('carry_forward_date', '<', end_date.date()),
|
||
])
|
||
|
||
if not records:
|
||
raise UserError(f'在 {self.year} 年 {self.month} 月没有找到符合条件的加班结转记录')
|
||
|
||
export_data = []
|
||
for record in records:
|
||
over_line = record.over_line_id
|
||
export_data.append({
|
||
'employee_id': record.employee_id.name if record.employee_id else '',
|
||
'department_id': record.department_id.name if record.department_id else '',
|
||
'start_time': (over_line.start_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if over_line and over_line.start_time else '',
|
||
'end_time': (over_line.end_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if over_line and over_line.end_time else '',
|
||
'carry_forward_date': record.carry_forward_date,
|
||
'last_month': record.last_month,
|
||
'carry_forward_quantity1': record.carry_forward_quantity1,
|
||
'this_month': record.this_month,
|
||
'carry_result': '成功',
|
||
})
|
||
|
||
fields_name = ['employee_id', 'department_id', 'start_time', 'end_time',
|
||
'carry_forward_date', 'last_month', 'carry_forward_quantity1', 'this_month', 'carry_result']
|
||
field_label = ['姓名', '部门', '加班开始时间', '加班结束时间',
|
||
'结转日期', '上期剩余', '结转时长', '本期结余', '结转结果']
|
||
|
||
data = {
|
||
'report_name': '加班结转明细报表',
|
||
'model_name': 'overtime.carryover',
|
||
'fields_name': fields_name,
|
||
'field_label': field_label,
|
||
'export_data': export_data,
|
||
'date_field': None,
|
||
}
|
||
|
||
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 _export_meal(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)]
|
||
if active_ids:
|
||
domain = [('id', 'in', active_ids)] + domain
|
||
|
||
filtered_records = self.env['vacation.meal.line'].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.line',
|
||
'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',
|
||
}
|
||
|
||
def _export_meal_active(self, start_date, end_date):
|
||
"""导出误餐归档明细"""
|
||
active_ids = json.loads(self.active_ids) if self.active_ids else []
|
||
|
||
domain = [('carry_forward_date', '>=', start_date.date()), ('carry_forward_date', '<', end_date.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} 月没有找到符合条件的误餐归档记录')
|
||
|
||
export_data = []
|
||
for record in filtered_records:
|
||
export_data.append({
|
||
'employee_id': record.employee_id.name if record.employee_id else '',
|
||
'department_id': record.employee_id.department_id.name if record.employee_id and record.employee_id.department_id else '',
|
||
'workflow_id': record.workflow_id.name if record.workflow_id else '',
|
||
'start_time': (record.start_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if record.start_time else '',
|
||
'end_time': (record.end_time + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if record.end_time else '',
|
||
'duration': record.duration,
|
||
'update_lines': record.update_lines,
|
||
'reasons': record.reasons or '',
|
||
'meal': record.meal,
|
||
})
|
||
|
||
fields_name = ['employee_id', 'department_id', 'workflow_id', 'start_time',
|
||
'end_time', 'duration', 'update_lines', 'reasons', 'meal']
|
||
field_label = ['发起人', '部门', '审批类型', '开始时间', '结束时间',
|
||
'时长', '额度', '事由', '加班餐费']
|
||
|
||
data = {
|
||
'report_name': '误餐归档明细报表',
|
||
'model_name': 'vacation.meal.active',
|
||
'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',
|
||
}
|