!2569 添加功能

Merge pull request !2569 from 李鹏宇/dev_zhou
This commit is contained in:
李鹏宇
2025-04-23 13:13:19 +00:00
committed by Gitee
8 changed files with 285 additions and 26 deletions
+241 -13
View File
@@ -366,6 +366,34 @@ class ReportExcel(models.Model):
'align': 'center',
'valign': 'vcenter'
})
# 定义备注格式
note_format = workbook.add_format({
'font_size': 10,
'border': 1,
'align': 'left',
'valign': 'top',
'text_wrap': True
})
# 定义审批字段格式
approval_format = workbook.add_format({
'font_size': 10,
'border': 1,
'align': 'center',
'valign': 'vcenter'
})
# 定义靠右无边框显示的格式
right_align_format = workbook.add_format({
'font_size': 10,
'align': 'right',
'valign': 'vcenter'
})
# 定义靠右有边框显示的格式
right_format = workbook.add_format({
'font_size': 10,
'align': 'right',
'valign': 'vcenter',
'border': 1,
})
header_format = workbook.add_format({'font_size': 11, 'bold': True, 'border': 1})
data_format = workbook.add_format({'font_size': 10, 'border': 1})
domain = self._get_payslip_domain(data)
@@ -375,6 +403,7 @@ class ReportExcel(models.Model):
for record in records:
group_id = record.salary_group_id.id if record.salary_group_id else None
grouped_records[group_id].append(record)
for group_id, group_records in grouped_records.items():
if group_id:
group_name = self.env['yuthon.salary.group'].browse(group_id).name
@@ -383,21 +412,107 @@ class ReportExcel(models.Model):
# 创建新的工作表
sheet = workbook.add_worksheet(group_name[:31])
# 写入公司和年月信息
sheet.merge_range(0, 0, 0, len(data['field_label']) - 1, f'员工{data["current_year"]}年{data["current_month"]}工资表({company_name})', title_format)
headers = ['序号'] + data['field_label']
columns_data = [[] for _ in range(len(headers) - 1)]
float_totals = [0] * (len(headers) - 1)
current_year = int(data['current_year'])
current_month = int(data['current_month'])
valid_columns = []
for index, field_name in enumerate(data['fields_name']):
field = self.env[data['model_name']]._fields.get(field_name)
if not field:
continue
# 检查字段是否存在于 yuthon.payroll.common 和 yuthon.payroll.temporary 模型中
common_model = self.env['yuthon.payroll.common']
temp_model = self.env['yuthon.payroll.temporary']
common_records = common_model.search([('name', '=', field.string)])
temp_records = temp_model.search([('name', '=', field.string)])
if not common_records and not temp_records:
# 若两个模型中都没找到该字段,跳过日期判断,直接认为有效
valid_columns.append(index)
continue
valid = False
if field.type == 'float':
# 分别处理 common_records
for rec in common_records:
start_date = rec.start_date
end_date = rec.end_date
if not end_date:
end_date = datetime.date(current_year + 99, current_month, 1)
if start_date and end_date:
start_year_month = (start_date.year, start_date.month)
end_year_month = (end_date.year, end_date.month)
current_year_month = (current_year, current_month)
if start_year_month <= current_year_month <= end_year_month:
valid = True
break
# 如果 common_records 未找到有效记录,再处理 temp_records
if not valid:
for rec in temp_records:
start_date = rec.start_date
end_date = rec.end_date
if not end_date:
end_date = datetime.date(current_year + 99, current_month, 1)
if start_date and end_date:
start_year_month = (start_date.year, start_date.month)
end_year_month = (end_date.year, end_date.month)
current_year_month = (current_year, current_month)
if start_year_month <= current_year_month <= end_year_month:
valid = True
break
else:
valid = True
if valid:
valid_columns.append(index)
for record in group_records:
col_num = 1
for index, field_name in enumerate(data['fields_name']):
if index in valid_columns and index < len(columns_data):
value = record[field_name]
columns_data[index].append(value)
field = self.env[data['model_name']]._fields.get(field_name)
if field and field.type == 'float':
float_totals[index] += float(value) if value else 0
col_num += 1
# 记录实际显示的列索引
visible_cols = [0]
first_float_col_index = None
for index in range(len(headers) - 1):
if index in valid_columns and not all([x == 0 for x in columns_data[index]]):
visible_cols.append(index + 1)
if first_float_col_index is None and self.env[data['model_name']]._fields[
data['fields_name'][index]].type == 'float':
first_float_col_index = len(visible_cols) - 1
actual_header_length = len(visible_cols)
# 写入大表头,根据实际显示列数确定合并范围
company_year_month_text = f'员工{data["current_year"]}年{data["current_month"]}工资表({company_name})'
sheet.merge_range(0, 0, 0, actual_header_length - 1, company_year_month_text, title_format)
# 添加单位和日期
current_date_str = datetime.datetime.now().strftime('%Y年%m月%d日')
sheet.write(1, actual_header_length - 1, f"单位:元 日期:{current_date_str}", right_align_format)
# 写入表头
headers = ['序号']+data['field_label']
col_num = 0
for label in headers:
sheet.write(1, col_num, label, header_format)
for index in visible_cols:
sheet.write(2, col_num, headers[index], header_format)
col_num += 1
row_num = 2
# 写入数据
row_num = 3
serial_number = 1
for record in group_records:
col_num = 0
sheet.write(row_num, col_num, serial_number, serial_number_format)
col_num += 1
for field_name in data['fields_name']:
for index in visible_cols[1:]:
field_name = data['fields_name'][index - 1]
field = self.env[data['model_name']]._fields.get(field_name)
value = record[field_name]
if isinstance(value, datetime.date):
@@ -421,6 +536,45 @@ class ReportExcel(models.Model):
col_num += 1
row_num += 1
serial_number += 1 # 序号递增
if first_float_col_index is not None:
# 合并从首列到首个float类型字段所在列的前一列的单元格
sheet.merge_range(row_num, 0, row_num, first_float_col_index - 1, "总计", serial_number_format)
# 调整起始列索引
start_col = first_float_col_index
for col_index, visible_col in enumerate(visible_cols[first_float_col_index:]):
field_name = data['fields_name'][visible_col - 1]
field = self.env[data['model_name']]._fields.get(field_name)
if field and field.type == 'float':
# 计算正确的列索引
target_col = start_col + col_index
sheet.write(row_num, target_col, float_totals[visible_col - 1], right_format)
else:
# 计算正确的列索引
target_col = start_col + col_index
sheet.write(row_num, target_col, "", right_format)
# 写入备注,根据实际显示列数确定合并范围
start_row = row_num + 1
end_row = start_row + 4
note_text = "备注"
sheet.merge_range(start_row, 0, end_row, actual_header_length - 1, note_text, note_format)
col_num = 0
# 记录表头实际占据的列数
header_column_count = len(visible_cols)
for index in visible_cols:
sheet.write(2, col_num, headers[index], header_format)
col_num += 1
# 添加审批字段行
approval_row = end_row + 1
approval_fields = ["领导审批:", "财务审核:", "部门审核:", "制表:"]
approval_text = " ".join(approval_fields)
# 合并单元格,使其宽度和表头一致
sheet.merge_range(approval_row, 0, approval_row, header_column_count - 1, approval_text, approval_format)
# 创建劳务信息工作表
labor_sheet = workbook.add_worksheet('劳务信息')
labor_fields = ['employee_id', 'labor_amount']
@@ -431,20 +585,39 @@ class ReportExcel(models.Model):
labor_sheet.merge_range(0, 0, 0, len(labor_headers) - 1, labor_title, title_format)
title_width = len(labor_title) + 2 # 适当增加一些宽度余量
labor_sheet.set_column(0, len(labor_headers) - 1, title_width)
# 添加单位和日期信息
current_time = datetime.datetime.now().strftime('%Y-%m-%d')
info_text = f"单位:元 日期:{current_time}"
labor_sheet.merge_range(1, 0, 1, len(labor_headers) - 1, info_text, right_align_format)
# 写入表头
col_num = 0
for label in labor_headers:
labor_sheet.write(1, col_num, label, header_format)
labor_sheet.write(2, col_num, label, header_format)
col_num += 1
labor_records = self.env['yuthon.payroll.labor'].search([
('current_year', '=', data['current_year']),
('current_month', '=', data['current_month'])
])
row_num = 2
row_num = 3
serial_number = 1
column_totals = [0] * len(labor_headers)
first_float_col_index = None
# 找出首个 float 类型字段所在列的索引
for col_index, field_name in enumerate(['serial_number'] + labor_fields):
field = self.env['yuthon.payroll.labor']._fields.get(field_name) if field_name in labor_fields else None
if field and field.type == 'float':
first_float_col_index = col_index
break
for labor_record in labor_records:
col_num = 0
labor_sheet.write(row_num, col_num, serial_number, serial_number_format)
if first_float_col_index is not None and col_num >= first_float_col_index:
try:
column_totals[col_num] += float(serial_number)
except (ValueError, TypeError):
pass
col_num += 1
for field_name in labor_fields:
field = self.env['yuthon.payroll.labor']._fields.get(field_name)
@@ -465,9 +638,28 @@ class ReportExcel(models.Model):
selection_options = dict(field.selection)
value = selection_options.get(value, value)
labor_sheet.write(row_num, col_num, value, data_format)
if first_float_col_index is not None and col_num >= first_float_col_index:
try:
column_totals[col_num] += float(value)
except (ValueError, TypeError):
pass
col_num += 1
row_num += 1
serial_number += 1
# 写入总计行
total_row = row_num
if first_float_col_index is not None:
labor_sheet.merge_range(total_row, 0, total_row, first_float_col_index - 1, '合计', serial_number_format)
for col_num in range(first_float_col_index, len(labor_headers)):
labor_sheet.write(total_row, col_num, column_totals[col_num], data_format)
end_row = total_row # 假设 end_row 是总计行
approval_row = end_row + 1
approval_fields = ["领导审批:", "财务审核:", "部门审核:", "制表:"]
approval_text = " ".join(approval_fields)
# 合并单元格,使其宽度和表头一致
labor_sheet.merge_range(approval_row, 0, approval_row, len(labor_headers) - 1, approval_text, approval_format)
# 创建退休人员费用工作表
retired_sheet = workbook.add_worksheet('退休人员费用')
@@ -479,20 +671,38 @@ class ReportExcel(models.Model):
retired_sheet.merge_range(0, 0, 0, len(labor_headers) - 1, retired_title, title_format)
title_width = len(retired_title) + 2 # 适当增加一些宽度余量
retired_sheet.set_column(0, len(retired_headers) - 1, title_width)
# 添加单位和日期信息
current_time = datetime.datetime.now().strftime('%Y-%m-%d')
info_text = f"单位:元 日期:{current_time}"
retired_sheet.merge_range(1, 0, 1, len(retired_headers) - 1, info_text, right_align_format)
# 写入表头
col_num = 0
for label in retired_headers:
retired_sheet.write(1, col_num, label, header_format)
retired_sheet.write(2, col_num, label, header_format)
col_num += 1
retired_records = self.env['yuthon.payroll.retired'].search([
('current_year', '=', data['current_year']),
('current_month', '=', data['current_month'])
])
row_num = 2
row_num = 3
serial_number = 1
column_totals = [0] * len(retired_headers)
first_float_col_index = None
for col_index, field_name in enumerate(['serial_number'] + retired_fields):
field = self.env['yuthon.payroll.retired']._fields.get(field_name) if field_name in retired_fields else None
if field and field.type == 'float':
first_float_col_index = col_index
break
for retired_record in retired_records:
col_num = 0
retired_sheet.write(row_num, col_num, serial_number, serial_number_format)
if first_float_col_index is not None and col_num >= first_float_col_index:
try:
column_totals[col_num] += float(serial_number)
except (ValueError, TypeError):
pass
col_num += 1
for field_name in retired_fields:
field = self.env['yuthon.payroll.retired']._fields.get(field_name)
@@ -513,9 +723,28 @@ class ReportExcel(models.Model):
selection_options = dict(field.selection)
value = selection_options.get(value, value)
retired_sheet.write(row_num, col_num, value, data_format)
if first_float_col_index is not None and col_num >= first_float_col_index:
try:
column_totals[col_num] += float(value)
except (ValueError, TypeError):
pass
col_num += 1
row_num += 1
serial_number += 1
# 写入总计行
total_row = row_num
if first_float_col_index is not None:
retired_sheet.merge_range(total_row, 0, total_row, first_float_col_index - 1, '合计', serial_number_format)
for col_num in range(first_float_col_index, len(retired_headers)):
retired_sheet.write(total_row, col_num, column_totals[col_num], data_format)
end_row = total_row # 假设 end_row 是总计行
approval_row = end_row + 1
approval_fields = ["领导审批:", "财务审核:", "部门审核:", "制表:"]
approval_text = " ".join(approval_fields)
# 合并单元格,使其宽度和表头一致
retired_sheet.merge_range(approval_row, 0, approval_row, len(labor_headers) - 1, approval_text, approval_format)
# 关闭工作簿
workbook.close()
@@ -548,7 +777,6 @@ class ReportExcel(models.Model):
chinese_month = month_mapping.get(current_month)
target_name = f"{current_year}年-{chinese_month}"
return [
('is_salary_push', '=', True),
('is_salary_release', '=', True),
('is_push', '=', False),
('payslip_run_id.name', '=', target_name)
]
]
@@ -390,6 +390,19 @@
</xpath>
</field>
</record>
<!--通讯录 -->
<record id="view_employee_contacts_tree" model="ir.ui.view">
<field name="name">hr.employee.inherit.del.form</field>
<field name="model">hr.employee</field>
<field name="arch" type="xml">
<tree string="通讯录">
<field name="name"/>
<field name="mobile_phone"/>
<field name="department_id"/>
<field name="job_id"/>
</tree>
</field>
</record>
<record id="view_employee_inherit_filter" model="ir.ui.view">
<field name="name">hr.employee.inherit.search</field>
<field name="model">hr.employee</field>
@@ -408,7 +421,14 @@
<field name="state">code</field>
<field name="code">action = model.action_hr_employee()</field>
</record>
<record id="all_employee_contacts_action" model="ir.actions.act_window">
<field name="name">通讯录</field>
<field name="res_model">hr.employee</field>
<field name="view_mode">tree,kanban,form,search</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_employee.view_employee_contacts_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('hr.view_employee_form')})]"/>
</record>
<record id="hr.open_view_employee_list_my" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,activity,graph,pivot,hierarchy</field>
</record>
@@ -8,6 +8,7 @@ class InheritHrPayslip(models.Model):
company_id = fields.Many2one('res.company', string="公司", default=lambda self: self.env.company)
employee_id = fields.Many2one('hr.employee', string="姓名")
date_of_entry = fields.Date(related='employee_id.entry_date', string="入职时间", store=True)
base_salary = fields.Float(string='基本工资', compute='_compute_salary', store=True)
bank_account_id = fields.Many2one(string='银行账号', related='employee_id.bank_account_id', store=True)
tax_payable = fields.Float(string='应补(退)税额', store=True)
@@ -7,6 +7,7 @@ class YuthonPayrollLabor(models.Model):
employee_id = fields.Many2one('hr.employee', string='姓名')
current_year = fields.Integer(string='年份')
date_of_entry = fields.Date(related='employee_id.entry_date', string="入职时间", store=True)
current_month = fields.Selection([
('1', '1月份'),
('2', '2月份'),
@@ -7,9 +7,14 @@
<field name="inherit_id" ref="hr_payroll.view_hr_payslip_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="js_class">custom_documents_button</attribute>
<attribute name="js_class">custom_salary_validation_button</attribute>
<attribute name="default_order">employee_id</attribute>
</xpath>
<xpath expr="//tree" position="after">
<header>
<button name="button_salary_print" string="打印" type="object" class="oe_highlight"/>
</header>
</xpath>
<xpath expr="//field[@name='basic_wage']" position="attributes">
<attribute name="column_invisible">1</attribute>
</xpath>
@@ -32,6 +37,7 @@
<attribute name="column_invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='net_wage']" position="after">
<field name="date_of_entry"/>
<field name="struct_id" string="职级"/>
<field name="bank_account_id"/>
<field name="actual_salary" decoration-danger="1"/>
@@ -85,12 +91,10 @@
<field name="name">hr.payslip.tree</field>
<field name="model">hr.payslip</field>
<field name="arch" type="xml">
<tree string="薪资发放" default_order="employee_id" js_class="custom_salary_validation_button">
<header>
<button name="button_salary_print" type="object" string="打印" class="btn-primary"/>
</header>
<tree string="薪资发放" default_order="employee_id">
<field name="employee_id" string="姓名" widget="ztree_select"/>
<field name="company_id" string="公司"/>
<field name="date_of_entry"/>
<field name="company_id" string="公司" optional="hide"/>
<field name="struct_id" string="职级"/>
<field name="bank_account_id"/>
<field name="actual_salary" decoration-danger="1"/>
@@ -241,6 +245,7 @@
<button name="action_refresh_from_work_entries" string="重新计算" type="object" class="oe_highlight"/>
</header>
<field name="employee_id" string="姓名" widget="ztree_select"/>
<field name="date_of_entry"/>
<field name="total_before_tax" decoration-danger="1" decoration-bf="1"/>
<field name="company_id" optional="hide" required="1"/>
<field name="base_salary"/>
@@ -286,6 +291,7 @@
<button name="action_salary_payslip_wizard" string="导入累计专项" type="object" class="oe_highlight"/>
</header>
<field name="employee_id" string="姓名" widget="ztree_select"/>
<field name="date_of_entry"/>
<field name="tax_payable" decoration-danger="1" decoration-bf="1"/>
<field name="struct_id" string="职级"/>
<field name="post_salary"/>
@@ -354,9 +360,6 @@
<field name="view_mode">tree,form,search</field>
<field name="domain">[('is_salary_push', '=', True), ('is_salary_release', '=', False)]</field>
<field name="context">{'search_default_group_by_employee': 1}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('hr_payroll.view_hr_payslip_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('hr_payroll.view_hr_payslip_form')})]"/>
</record>
<record id="action_view_hr_payslip_salary_release" model="ir.actions.act_window">
<field name="name">薪资发放</field>
@@ -6,6 +6,7 @@
<field name="arch" type="xml">
<tree string="劳务费" editable="bottom" js_class="custom_labor_button">
<field name="employee_id" domain="[('use_user_type', '=', 'labor')]"/>
<field name="date_of_entry"/>
<field name="current_year" optional="hide"/>
<field name="current_month" optional="hide"/>
<field name="company_id" required="1" optional="hide"/>
+3 -3
View File
@@ -28,6 +28,7 @@ class SalaryPrintWizard(models.Model):
def print_fixed_report(self):
fields_list = [
'employee_id',
'date_of_entry',
'company_id',
'struct_id',
'bank_account_id',
@@ -66,7 +67,6 @@ class SalaryPrintWizard(models.Model):
'continuing_education',
'infant_care',
'meal_misunderstanding_fee',
'salary_state'
]
data = {
'report_name': '薪资',
@@ -75,6 +75,7 @@ class SalaryPrintWizard(models.Model):
'current_year': self.current_year,
'fields_name': [
'employee_id',
'date_of_entry',
'company_id',
'struct_id',
'bank_account_id',
@@ -113,10 +114,10 @@ class SalaryPrintWizard(models.Model):
'continuing_education',
'infant_care',
'meal_misunderstanding_fee',
'salary_state'
],
'field_label': [
'姓名',
'入职时间',
'公司',
'职级',
'银行账号',
@@ -155,7 +156,6 @@ class SalaryPrintWizard(models.Model):
'累计继续教育',
'累计3岁以下婴幼儿照护',
'误餐费',
'状态'
],
'field_order': str([i for i in range(1, len(fields_list) + 1)])
}
+5
View File
@@ -114,6 +114,11 @@
parent="human_resources_menu_root_board"
action="hr.open_view_employee_list_my"
sequence="1"/>
<menuitem id="employee_contacts_menu"
name="通讯录"
parent="human_resources_menu_root_board"
action="yuthon_hr_employee.all_employee_contacts_action"
sequence="2"/>
<menuitem id="hr_menu_root_board"
name="人事管理"
parent="human_resources_menu_root_board"