diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index ec7ac5d17..4a3959bba 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -17,11 +17,6 @@ class SoongWorkflowMixin(models.AbstractModel): _name = "soong.workflow.mixin" _description = "Soong Workflow Mixin" - def end_approval(self): - record = self.env['soong.workflow.record'].search([('code', '=', self.code)]) - if record: - record.write({'approve_state': 'end'}) - def read(self, fields=None, load='_classic_read'): if self.log_ids: if self.log_ids[-1].done_by.id == self.env.user.id and self.log_ids[-1].approval_state == '0': @@ -518,6 +513,7 @@ class SoongWorkflowMixin(models.AbstractModel): @api.model def get_view(self, view_id=None, view_type="form", **options): res = super().get_view(view_id, view_type, **options) + active_id = self._context.get('active_id') if '
+ + + + + 结束日期 + 1 @@ -26,18 +32,12 @@ - - 0 - - - - 1 - + diff --git a/yuthon_hr_attendance/controllers/applet_attendance.py b/yuthon_hr_attendance/controllers/applet_attendance.py index 3479514f8..f290fbe30 100644 --- a/yuthon_hr_attendance/controllers/applet_attendance.py +++ b/yuthon_hr_attendance/controllers/applet_attendance.py @@ -47,13 +47,6 @@ class AppletAttendance(Controller): current_data = fields.Date.today() user_id = request.env(user=SUPERUSER_ID)["res.users"].search([('id', '=', int(kw['user_id']))]) employee_id = request.env(user=SUPERUSER_ID)["hr.employee"].search([('name', '=', user_id.name)]) - address_id = request.env(user=SUPERUSER_ID)["applet.address"].search([('address_name', '=', employee_id.company_id.name)]) - self_address = self.get_address_from_coords(kw['att_latitude'], kw['att_longitude']) - - _logger.info(f"打卡经度{kw['att_longitude']}打卡纬度{kw['att_latitude']}__________") - _logger.info(f"人员{employee_id.name}地址{self_address['formatted_address']}__________") - - card_rice = address_id.card_rice * 0.001 # 打卡范围 attendance_id = request.env(user=SUPERUSER_ID)["hr.attendance"].search([('employee_id', '=', employee_id.id), ('current_data', '=', current_data)]) check_in = attendance_id.check_in + timedelta(hours=8) if attendance_id.check_in else '' check_out = attendance_id.check_out + timedelta(hours=8) if attendance_id.check_out else '' @@ -78,25 +71,33 @@ class AppletAttendance(Controller): check_out_str = check_out.strftime('%H:%M:%S') if check_out else ' ' check_in_html = f'

{check_in_str}

' if check_in_status in ['迟到', '早退', '缺卡'] else f'

{check_in_str}

' check_out_html = f'

{check_out_str}

' if check_out_status in ['迟到', '早退', '缺卡'] else f'

{check_out_str}

' - if not self.is_within_radius_geopy(kw['att_latitude'], kw['att_longitude'], address_id.latitude, address_id.longitude, card_rice): - message = "超出范围,不允许打卡" - state = "no" - _logger.info("-------触发超出范围,不允许打卡---------") - else: - state = 'yes' - message = 'ok' - data = { - 'location': self_address['formatted_address'], - 'user_id': user_id.id, - 'class': f"{attendance_id.shift_name}{start_time_str}-{end_time_str}", - 'state_result': state_result, - 'check_in': check_in_html, - 'check_in_status': check_in_status, - 'check_out': check_out_html, - 'check_out_status': check_out_status, - 'state': state, - }, - return_data = {'data': data, 'code': '200', 'message': message, } + self_address = self.get_address_from_coords(kw['att_latitude'], kw['att_longitude']) + _logger.info(f"打卡经度{kw['att_longitude'] }打卡纬度{kw['att_latitude']}__________") + _logger.info(f"人员{employee_id.name}地址{self_address['formatted_address']}__________") + address_ids = request.env(user=SUPERUSER_ID)["applet.address"].search([('address_name', '=', employee_id.company_id.name)]) + data = {"message": "超出范围,不允许打卡", 'state': 'no', 'state_result': '超出范围'} + for address_id in address_ids: + card_rice = address_id.card_rice * 0.001 + if self.is_within_radius_geopy( + kw['att_latitude'], + kw['att_longitude'], + address_id.latitude, + address_id.longitude, + card_rice + ): + data = { + 'location': '%s' % address_id.address_name, + 'user_id': user_id.id, + 'state': 'yes', + 'class': [f"{attendance_id.shift_name}{start_time_str}-{end_time_str}"], + 'state_result': state_result, + 'check_in': check_in_html, + 'check_in_status': check_in_status, + 'check_out': check_out_html, + 'check_out_status': check_out_status, + } + break + return_data = {'data': data, 'code': '200', 'message': 'OK'} return json.dumps(return_data) def is_within_radius_geopy(self, lat1, lon1, lat2, lon2, radius_km): @@ -189,7 +190,8 @@ class AppletAttendance(Controller): } """ _logger.info("----------------/yuthon/att/att---------接口收到响应-------------------------------") - user_id = int(kw['user_id']) + user_id = request.env(user=SUPERUSER_ID)["res.users"].search([('id', '=', int(kw['user_id']))]) + employee_id = request.env(user=SUPERUSER_ID)["hr.employee"].search([('name', '=', user_id.name)]) month = kw['selected_month'] # 2015-01 year = int(month[0:4]) month = int(month[-2:]) @@ -206,7 +208,7 @@ class AppletAttendance(Controller): # 本月考勤记录 leave_days = 0 current_attendance = request.env(user=SUPERUSER_ID)['hr.attendance'].sudo().search([ - ('employee_id.user_id', '=', user_id), + ('employee_id', '=', employee_id.id), ('current_data', '>=', current_month_first.date()), ('current_data', '<=', current_month_last.date()), ('week', 'not in', ['seven', 'six']) @@ -250,7 +252,6 @@ class AppletAttendance(Controller): records = [] for att in attendances: # 获取班次信息 - shift_name = att.shift_id.name if att.shift_id else "" check_in_start = att.shift_id.template_id.check_in_start if att.shift_id and att.shift_id.template_id else 0 check_out_end = att.shift_id.template_id.check_out_end if att.shift_id and att.shift_id.template_id else 0 start_hour = int(check_in_start) @@ -277,7 +278,7 @@ class AppletAttendance(Controller): # 添加到记录列表 records.append({ 'date': att.current_data.strftime('%Y-%m-%d'), - 'class': [f"{shift_name}{standard_check_in_str}-{standard_check_out_str}"], + 'class': [f"{att.shift_name}{standard_check_in_str}-{standard_check_out_str}"], 'user_id': user_id, 'check_in': check_in_str, 'check_in_state': check_in_html, diff --git a/yuthon_hr_employee/models/yuthon_hr_employee.py b/yuthon_hr_employee/models/yuthon_hr_employee.py index 675b31ab2..a9d75cc45 100644 --- a/yuthon_hr_employee/models/yuthon_hr_employee.py +++ b/yuthon_hr_employee/models/yuthon_hr_employee.py @@ -167,6 +167,10 @@ class YuthonHrEmployee(models.Model): # 工资计算分组 salary_group_id = fields.Many2one('yuthon.salary.group', string="工资单分组", store=True) salary_date = fields.Date(string="薪资计算时间", store=True) + salary_month = fields.Selection([('1', '一月'), ('2', '二月'), ('3', '三月'), ('4', '四月'), + ('5', '五月'), ('6', '六月'), ('7', '七月'), ('8', '八月'), + ('9', '九月'), ('10', '十月'), ('11', '十一月'), + ('12', '十二月')], string='薪资计算月份') salary_year_jin_ids = fields.Many2many( 'hr.employee', # 自关联到hr.employee relation='hr_employee_salary_year_jin_rel', # 显式指定关联表名 @@ -188,6 +192,12 @@ class YuthonHrEmployee(models.Model): last_month_base_salary_deduction = fields.Float(string="上月基本薪资扣款") last_month_performance_deduction = fields.Float(string="上月绩效扣款") last_month_commuting_deduction = fields.Float(string='上月通信扣款') + # 薪资计算 基数 + salary_medical_insurance = fields.Float(string='薪资医疗费基数', compute='_compute_insurance_salary') + salary_insurance = fields.Float(string='薪资养老费基数', compute='_compute_insurance_salary') + salary_work_injury_insurance = fields.Float(string='薪资保险金基数', compute='_compute_insurance_salary') + salary_housing_fund = fields.Float(string='薪资公积金基数', compute='_compute_insurance_salary') + # 显示基数 medical_insurance_base = fields.Float(string='基本医疗费基数', compute='_compute_insurance_bases') pension_insurance_base = fields.Float(string='基本养老费基数', compute='_compute_insurance_bases') work_injury_insurance_base = fields.Float(string='失业保险金基数', compute='_compute_insurance_bases') @@ -200,6 +210,7 @@ class YuthonHrEmployee(models.Model): subsidy_amount = fields.Float(string="岗位补贴金额", compute='_compute_subsidy_amount', store=True) security_amount = fields.Float(string="安全委员会金额", compute='_compute_subsidy_amount', store=True) + quality_inspector_amount = fields.Float(string="质检员金额", compute='_compute_subsidy_amount', store=True) @api.depends('quota_issued', 'user_year') def _compute_remainder_year(self): @@ -213,6 +224,7 @@ class YuthonHrEmployee(models.Model): def _compute_subsidy_amount(self): for record in self: amount = 0 + quality_amount = 0 # 获取主角色名称 primary_role_name = record.user_role_id.name if record.user_role_id else '' # 获取所有辅助角色名称列表 @@ -244,7 +256,7 @@ class YuthonHrEmployee(models.Model): for cert in record.skills_certificates_ids ) if quality and department_name == '仓储组': - amount += 100 + quality_amount += 100 if department_name == '领导班子': record.security_amount = 0 @@ -254,7 +266,7 @@ class YuthonHrEmployee(models.Model): record.security_amount = 0 record.subsidy_amount = amount - + record.quality_inspector_amount = quality_amount @api.depends('converted', 'salary_year_jin_ids') def _compute_total_coefficients(self): for record in self: @@ -291,7 +303,7 @@ class YuthonHrEmployee(models.Model): def _compute_overtime_this_month(self): for employee in self: employee.overtime_this_month = employee.normal_overtime_hours + employee.weekend_overtime_hours + employee.holiday_overtime_hours - + # 根据当前月份来选择具体的值 @api.depends('social_base_on', 'social_base_off') def _compute_insurance_bases(self): current_month = datetime.now().month @@ -322,6 +334,39 @@ class YuthonHrEmployee(models.Model): else: setattr(record, field_name, base) + # 根据薪资计算选择的月份 来获取确定的值 + @api.depends('social_base_on', 'social_base_off', 'salary_month') + def _compute_insurance_salary(self): + insurance_names = { + '薪资医疗保险费': 'salary_medical_insurance', + '薪资养老保险费': 'salary_insurance', + '薪资保险费': 'salary_work_injury_insurance', + '薪资公积金': 'salary_housing_fund' + } + name_mapping = { + '基本医疗保险费': '薪资医疗保险费', + '基本养老保险费': '薪资养老保险费', + '失业保险费': '薪资保险费', + '住房公积金': '薪资公积金' + } + insurance_records = self.env['yuthon.payroll.insurances'].search([('name', 'in',['基本医疗保险费', '基本养老保险费', '失业保险费', '住房公积金'])]) + for record in self: + for field_name in insurance_names.values(): + setattr(record, field_name, 0) + base = record.social_base_on if 1 <= int(record.salary_month) <= 6 else record.social_base_off + for insurance in insurance_records: + insurance_name_key = name_mapping.get(insurance.name) + field_name = insurance_names.get(insurance_name_key) + if field_name == 'housing_fund_base' and record.name in ['郭爱娣', '黄炎方']: + record.housing_fund_base = 4000 + continue + if base > insurance.employee_base_ceiling: + setattr(record, field_name, insurance.employee_base_ceiling) + elif base < insurance.employee_base_floor: + setattr(record, field_name, insurance.employee_base_floor) + else: + setattr(record, field_name, base) + @api.onchange('job_id') def _onchange_job_id(self): if self.job_id: diff --git a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml index 1713d31e0..3e576d39b 100644 --- a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml +++ b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml @@ -162,6 +162,11 @@ + + + + + diff --git a/yuthon_hr_employee_approval/models/yuthon_onboard_approval.py b/yuthon_hr_employee_approval/models/yuthon_onboard_approval.py index cf7c96385..33e3f6137 100644 --- a/yuthon_hr_employee_approval/models/yuthon_onboard_approval.py +++ b/yuthon_hr_employee_approval/models/yuthon_onboard_approval.py @@ -31,7 +31,8 @@ class YuthonOnboardApproval(models.Model): work_end = fields.Boolean(string="结束") bank = fields.Char(string='开户行') use_user_type = fields.Selection(related='users_id.employee_id.use_user_type', string="用工形式") - department_id1 = fields.Many2one('hr.department', string="部门") + job = fields.Char(related='employee_name_id.name', string="岗位") + department_id1 = fields.Many2one(related='employee_name_id.department_id', string="部门") onboarding_status = fields.Selection([('none', '待入职'), ('onboarding', '已入职')], string='审批后员工状态', default='none') onboard_time = fields.Date(string='入职时间', default=fields.Datetime.now) probation_period = fields.Selection([('zero', '0个月'), ('one', '1个月'), @@ -50,7 +51,6 @@ class YuthonOnboardApproval(models.Model): is_issues = fields.Boolean(string='是否上会') issues_result = fields.Html(string="会议结果") related_issues_id = fields.Many2one('issue.library', string="关联议题") - job_id = fields.Many2one('hr.job', string="岗位") # 合同 contracted_status = fields.Selection([('first', '首' '签'), ('renew', '续签')], string='合同类型') diff --git a/yuthon_hr_employee_approval/views/yuthon_onboard_approval_views.xml b/yuthon_hr_employee_approval/views/yuthon_onboard_approval_views.xml index 4b10cae0a..4a9914e3d 100644 --- a/yuthon_hr_employee_approval/views/yuthon_onboard_approval_views.xml +++ b/yuthon_hr_employee_approval/views/yuthon_onboard_approval_views.xml @@ -75,7 +75,7 @@ - + diff --git a/yuthon_property_approval/models/property_lease_contract.py b/yuthon_property_approval/models/property_lease_contract.py index df01103b9..d5fa42503 100644 --- a/yuthon_property_approval/models/property_lease_contract.py +++ b/yuthon_property_approval/models/property_lease_contract.py @@ -52,7 +52,7 @@ class PropertyLeaseContract(models.Model): name = fields.Char(string='物业信息') code = fields.Char(string="流水号", default=lambda self: self.env['ir.sequence'].next_by_code('property_lease_contract_code')) - admin_id = fields.Many2one('res.users', string="管理员") + admin_id = fields.Many2one('res.users', string="管理员", compute='_compute_admin_id', store=True) tenure_type = fields.Selection([('self', '自有'), ('agency', '代管')], string='权属类型') contract_code = fields.Char(string="合同编号") contract_date = fields.Date(string="合同签订日期") @@ -212,6 +212,20 @@ class PropertyLeaseContract(models.Model): store=True) responsibility_book_name = fields.Char(string="安全生产责任书(住宅)名称", compute='_compute_attachment_names', store=True) + + def update_admin_id(self): + contract_ids = self.env["property.lease.contract"].browse( + self._context.get('active_ids', self._context.get('active_id'))) + for rec in contract_ids: + if rec.lease_ids: + rec.admin_id = rec.lease_ids[0].admin_line_id.id + + @api.depends('lease_ids') + def _compute_admin_id(self): + for rec in self: + if rec.lease_ids: + rec.admin_id = rec.lease_ids[0].admin_line_id.id + @api.depends( 'license_id.name', 'legal_entity_id.name', diff --git a/yuthon_property_approval/views/property_lease_contract.xml b/yuthon_property_approval/views/property_lease_contract.xml index f27e35d0a..204ef121a 100644 --- a/yuthon_property_approval/views/property_lease_contract.xml +++ b/yuthon_property_approval/views/property_lease_contract.xml @@ -809,6 +809,7 @@ 合同管理 property.lease.contract tree,form + {'search_default_group_by_admin_id': True} @@ -818,6 +819,7 @@ property.lease.contract tree,form [('condition', 'in', ['new', 'approval', 'cancel'])] + {'search_default_group_by_admin_id': True} @@ -857,6 +859,14 @@ code action = model.all_end_approval() + + 同步更新管理员 + + + code + action = model.update_admin_id() + + diff --git a/yuthon_salary/__manifest__.py b/yuthon_salary/__manifest__.py index 6114ccb8c..bc3f09971 100644 --- a/yuthon_salary/__manifest__.py +++ b/yuthon_salary/__manifest__.py @@ -34,6 +34,7 @@ 'wizard/yuthon_payroll_wizard_views.xml', 'wizard/payroll_labor_wizard_views.xml', 'wizard/payroll_retired_wizard_views.xml', + 'wizard/initialize_payroll_wizard_views.xml', 'wizard/payslip_send_wizard_views.xml', 'wizard/salary_year_wizard.xml', 'wizard/salary_compute_wizard_views.xml', diff --git a/yuthon_salary/models/inherit_hr_payslip.py b/yuthon_salary/models/inherit_hr_payslip.py index c3aa8e65f..88a11805c 100644 --- a/yuthon_salary/models/inherit_hr_payslip.py +++ b/yuthon_salary/models/inherit_hr_payslip.py @@ -119,13 +119,15 @@ class InheritHrPayslip(models.Model): leave_absence = fields.Float(string='考勤补发') # 粮储常用项 - quality_allowance = fields.Float(string='质检员津贴') + quality_allowance = fields.Float(string='质检员津贴', compute="_compute_salary", store=True) environmental_allowance = fields.Float(string='环境作业津贴') last_duty = fields.Float(string='上月值班费') high_subsidy = fields.Float(string='高温补贴') # 粮储临时项 medical_fee = fields.Float(string='超出体检费标准') + retroactive_insurance = fields.Float(string='补扣保险') + distribution_part = fields.Float(string='扣除环境作业津贴多发放部分') # 三家公司 add_year_float = fields.Float(string='补年金差额') @@ -187,6 +189,8 @@ class InheritHrPayslip(models.Model): rec.full_attendance_award = line.total if line.name == '加班费': rec.overtime_pay = line.total + if line.name == '质检员津贴': + rec.quality_allowance = line.total if line.name == '误餐费': rec.meal_misunderstanding_fee = line.total if line.name == '事假': @@ -197,6 +201,8 @@ class InheritHrPayslip(models.Model): rec.maternity_leave = line.total if line.name == '婚假': rec.marriage_leave = line.total + if line.name == '上月值班费': + rec.last_duty = line.total if line.name == '计生假': rec.family_planning_leave = line.total if line.name == '丧假': @@ -227,6 +233,31 @@ class InheritHrPayslip(models.Model): rec.commuting_deduction = line.total if line.name == '扣发基本工资': rec.base_salary_deduction = line.total + if line.name == '优秀部门经理/员工奖励': + rec.excellent_employees = line.total + if line.name == '考勤补发': + rec.leave_absence = line.total + if line.name == '环境作业津贴': + rec.environmental_allowance = line.total + if line.name == '高温补贴': + rec.high_subsidy = line.total + if line.name == '超出体检费标准': + rec.medical_fee = line.total + if line.name == '补扣保险': + rec.retroactive_insurance = line.total + if line.name == '扣除环境作业津贴多发放部分': + rec.distribution_part = line.total + # 新增字段(无重复name) + if line.name == '岗位绩效': + rec.job_performance = line.total + if line.name == '请假扣发': + rec.leave_allowance = line.total + if line.name == '值班(含节日加班)': + rec.duty = line.total + if line.name == '月加班': + rec.monthly_overtime = line.total + if line.name == '年金': + rec.annuity = line.total rec.three_one = rec.housing_provident_fund + rec.work_injury_insurance + rec.basic_endowment_insurance + rec.basic_medical_insurance + rec.unemployment_insurance + rec.social_security @@ -287,12 +318,14 @@ class InheritHrPayslip(models.Model): @api.depends('year_compute', 'year_jie', 'year_gong', 'jing_te', 'study_on', 'seniority_allowance', 'position_salary', 'quality_allowance','high_subsidy', 'environmental_allowance', 'last_duty', 'seniority_salary', 'commuting_allowance','base_salary','performance_bonuses', 'comprehensive_subsidy', 'full_attendance_award', 'overtime_pay', 'based_pay', - 'january_performance','position_subsidy', 'safety_incentives', 'temporary_performance_bonus', 'performance_deduction','commuting_deduction', - 'base_salary_deduction', 'last_year_performance_evaluation', 'safety_subsidy', 'job_performance','leave_allowance', 'medical_fee', + 'january_performance','position_subsidy', 'safety_incentives', 'temporary_performance_bonus', 'performance_deduction','commuting_deduction', 'retroactive_insurance', + 'base_salary_deduction', 'last_year_performance_evaluation', 'safety_subsidy', 'job_performance','leave_allowance', 'distribution_part', 'duty', 'monthly_overtime', 'annuity', 'excellent_employees', 'leave_absence', 'retention_previous_year') def _compute_total_before_tax(self): for record in self: record.total_before_tax = ( + record.retroactive_insurance + + record.distribution_part + record.retention_previous_year + record.position_salary + record.high_subsidy + @@ -338,7 +371,6 @@ class InheritHrPayslip(models.Model): record.performance_deduction - record.commuting_deduction - record.base_salary_deduction - - record.medical_fee - record.leave_allowance # 请假扣发 ) @@ -346,10 +378,10 @@ class InheritHrPayslip(models.Model): # 本期应收 - 三险一金 - 年金 - 应补退税 - 工会会费 - 个税退回 # total_before_tax - three_one - b_year - union_dues - tax_payable - individual_tax_refund - @api.depends('total_before_tax', 'three_one', 'b_year', 'union_dues', 'tax_payable', 'individual_tax_refund') + @api.depends('total_before_tax', 'three_one', 'b_year', 'union_dues', 'tax_payable', 'individual_tax_refund', 'medical_fee') def _compute_actual_salary(self): for record in self: - record.actual_salary = (record.total_before_tax - record.three_one - record.b_year - record.union_dues - record.tax_payable + record.individual_tax_refund) + record.actual_salary = (record.total_before_tax - record.three_one - record.b_year - record.union_dues - record.tax_payable + record.individual_tax_refund - record.medical_fee) if record.employee_id.name == '罗梦烨': _logger.info("罗梦烨实发{} 本期应:{},31:{},年金:{}应补:{}工会{}个税{}".format(record.actual_salary, record.total_before_tax, record.three_one, record.b_year, record.union_dues, @@ -500,4 +532,22 @@ class InheritHrPayslip(models.Model): unconfirmed_slips = self.search(domain) if unconfirmed_slips: unconfirmed_slips.write({'confirm_status': 'confirm'}) - return super().get_view(view_id, view_type, **options) \ No newline at end of file + return super().get_view(view_id, view_type, **options) + + def initialize_payroll_wizard(self): + payslip_ids = self.env["hr.payslip"].browse( + self._context.get('active_ids', self._context.get('active_id'))) + payslip_list = [] + for record in payslip_ids: + payslip_list.append([4, record.id]) + return { + 'name': '初始化工资单', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'initialize.payroll.wizard', + 'target': 'new', + 'context': { + 'default_payslip_ids': payslip_list + } + } + diff --git a/yuthon_salary/models/yuthon_payroll_project.py b/yuthon_salary/models/yuthon_payroll_project.py index 96ef357ee..d279e6d90 100644 --- a/yuthon_salary/models/yuthon_payroll_project.py +++ b/yuthon_salary/models/yuthon_payroll_project.py @@ -96,7 +96,7 @@ class YuthonPayrollCommon(models.Model): if ins.name == '环境作业津贴': formula_salary = "result = 0" if ins.name == '质检员津贴': - formula_salary = "result = 0" + formula_salary = "result = employee.quality_inspector_amount" if ins.name == '工会会费' and ins.company_id.name in ['集团本部', '天河物管']: formula_salary = "result = round((payslip.base_salary * 0.005), 2)" if ins.name == '工会会费' and ins.company_id.name == '天河粮储': @@ -215,19 +215,19 @@ class YuthonPayrollInsurances (models.Model): code = 'None' if ins.name == '基本医疗保险费': code = 'JBYLIAO' - formula_salary = "result = employee.medical_insurance_base * " + str(number) + formula_salary = "result = employee.salary_medical_insurance * " + str(number) if ins.name == '基本养老保险费': code = 'JBYLAO' - formula_salary = "result = employee.pension_insurance_base * " + str(number) + formula_salary = "result = employee.salary_insurance * " + str(number) if ins.name == '失业保险费': code = 'SYBX' - formula_salary = "result = employee.work_injury_insurance_base * " + str(number) + formula_salary = "result = employee.salary_work_injury_insurance * " + str(number) if ins.name == '工伤保险费': code = 'GSBX' formula_salary = "result = employee.social_base_on * " + str(number) if ins.name == '住房公积金': code = 'ZFGJJ' - formula_salary = "result = (round((employee.housing_fund_base * " + str(number) + "), 0)) if employee.is_accum_salary else 0" + formula_salary = "result = (round((employee.salary_housing_fund * " + str(number) + "), 0)) if employee.is_accum_salary else 0" if ins.name == '企业(职业)年金': if ins.company_id.name == '天河物管': code = 'WGQYNJ' diff --git a/yuthon_salary/security/ir.model.access.csv b/yuthon_salary/security/ir.model.access.csv index b6664dae9..0850fb46f 100644 --- a/yuthon_salary/security/ir.model.access.csv +++ b/yuthon_salary/security/ir.model.access.csv @@ -31,3 +31,4 @@ access_yuthon_salary_project, access_yuthon_salary_project,model_yuthon_salary_p access_yuthon_salary_asset, access_yuthon_salary_asset,model_yuthon_salary_asset,base.group_user,1,1,1,1 access_salary_payroll_distribution,access_salary_payroll_distribution,model_salary_payroll_distribution,base.group_user,1,1,1,1 access_yuthon_welfare_expenses,access_yuthon_welfare_expenses,model_yuthon_welfare_expenses,base.group_user,1,1,1,1 +access_initialize_payroll_wizard,access_initialize_payroll_wizard,model_initialize_payroll_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_salary/static/others/temporary_payslip_template.xlsx b/yuthon_salary/static/others/temporary_payslip_template.xlsx deleted file mode 100644 index 6a8333b25..000000000 Binary files a/yuthon_salary/static/others/temporary_payslip_template.xlsx and /dev/null differ diff --git a/yuthon_salary/static/others/工资项导入模板.xlsx b/yuthon_salary/static/others/工资项导入模板.xlsx new file mode 100644 index 000000000..939ca4413 Binary files /dev/null and b/yuthon_salary/static/others/工资项导入模板.xlsx differ diff --git a/yuthon_salary/views/inherit_hr_payslip_views.xml b/yuthon_salary/views/inherit_hr_payslip_views.xml index 65a1adae7..20f8b827b 100644 --- a/yuthon_salary/views/inherit_hr_payslip_views.xml +++ b/yuthon_salary/views/inherit_hr_payslip_views.xml @@ -180,6 +180,7 @@ + @@ -236,7 +237,7 @@ - + @@ -296,6 +297,8 @@ + + @@ -464,6 +467,13 @@ code action = model.action_return_salary_wizard() + + 初始化工资项 + + + code + action = model.initialize_payroll_wizard() + 批量退回薪资审定 diff --git a/yuthon_salary/wizard/__init__.py b/yuthon_salary/wizard/__init__.py index 9001f1423..4a56efcf8 100644 --- a/yuthon_salary/wizard/__init__.py +++ b/yuthon_salary/wizard/__init__.py @@ -9,4 +9,5 @@ from . import payroll_labor_wizard from . import payroll_retired_wizard from . import salary_print_wizard from . import salary_year_wizard -from . import payslip_send_wizard \ No newline at end of file +from . import payslip_send_wizard +from . import initialize_payroll_wizard \ No newline at end of file diff --git a/yuthon_salary/wizard/initialize_payroll_wizard.py b/yuthon_salary/wizard/initialize_payroll_wizard.py new file mode 100644 index 000000000..da9d317aa --- /dev/null +++ b/yuthon_salary/wizard/initialize_payroll_wizard.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api, _, SUPERUSER_ID +from odoo.exceptions import ValidationError, UserError +from pypinyin import pinyin, Style +import xlrd +import base64 +import datetime +from datetime import datetime + + +class InitializePayrollWizard(models.Model): + _name = 'initialize.payroll.wizard' + _description = '初始化工资单弹窗' + + payslip_ids = fields.Many2many('hr.payslip', string='工资单') + company_id = fields.Many2one('res.company', string="公司", index=True, default=lambda self: self.env.company) + payroll_common_ids = fields.Many2many('yuthon.payroll.common', string='常用项') + payroll_temporary_ids = fields.Many2many('yuthon.payroll.temporary', string='临时项') + payroll_insurances_ids = fields.Many2many('yuthon.payroll.insurances', string='五险一金') + + def confirm(self): + target_names = ( + [common.name for common in self.payroll_common_ids] + + [temp.name for temp in self.payroll_temporary_ids] + + [insurance.name for insurance in self.payroll_insurances_ids] + ) + for payslip in self.payslip_ids: + for line in payslip.line_ids: + if line.name in target_names: + line.write({ + 'total': 0, + 'amount': 0 + }) + payslip.with_context(recompute=True)._compute_salary() \ No newline at end of file diff --git a/yuthon_salary/wizard/initialize_payroll_wizard_views.xml b/yuthon_salary/wizard/initialize_payroll_wizard_views.xml new file mode 100644 index 000000000..634e4fcda --- /dev/null +++ b/yuthon_salary/wizard/initialize_payroll_wizard_views.xml @@ -0,0 +1,24 @@ + + + + 初始化工资单 + initialize.payroll.wizard + + + + + + + + + + + +
+
+ +
+
+
\ No newline at end of file diff --git a/yuthon_salary/wizard/salary_compute_wizard.py b/yuthon_salary/wizard/salary_compute_wizard.py index 44fa97756..f62db3d6a 100644 --- a/yuthon_salary/wizard/salary_compute_wizard.py +++ b/yuthon_salary/wizard/salary_compute_wizard.py @@ -41,7 +41,7 @@ class SalaryComputeWizard(models.Model): ('company_id', '=', self.company_id.id), ('is_salary', '=', True), ('entry_date', '<=', start_date), - ('contract_id.state', 'not in', ['draft', 'cancel']), + ('contract_id.state', 'not in', ['draft', 'cancel', 'close']), ('contract_id', '!=', False), ]) self.employee_ids = [(6, 0, employees.ids)] @@ -91,6 +91,7 @@ class SalaryComputeWizard(models.Model): ('date_to', '>=', start_date) ]) employee.salary_date = start_date + employee.salary_month = self.select_time employee.salary_year_jin_ids = [(6, 0, self.employee_ids.ids)] if existing_payslip: raise UserError(f"员工 {employee.name} 在 {start_date} 到 {end_date} 日期范围内已有工资单记录,无法创建新的工资单。") diff --git a/yuthon_salary/wizard/temporary_payslip_wizard.py b/yuthon_salary/wizard/temporary_payslip_wizard.py index 1f7ffa1ce..693e9dae5 100644 --- a/yuthon_salary/wizard/temporary_payslip_wizard.py +++ b/yuthon_salary/wizard/temporary_payslip_wizard.py @@ -14,26 +14,58 @@ class TemporaryPayslipWizard(models.Model): excel_file = fields.Binary(string="上传Excel表") excel_name = fields.Char(string="文件名") + select_time = fields.Selection([('1', '一月'), ('2', '二月'), ('3', '三月'), ('4', '四月'), + ('5', '五月'), ('6', '六月'), ('7', '七月'), ('8', '八月'), + ('9', '九月'), ('10', '十月'), ('11', '十一月'), + ('12', '十二月')], string='选择月份', + default=lambda self: self._get_default_month()) + select_year = fields.Selection(selection=[(str(i), str(i)) for i in range(2024, 2060)], string='选择年份', + default=lambda self: str(datetime.now().year)) + + def _get_default_month(self): + current_month = datetime.now().month + return str(current_month) def confirm(self): if not self.excel_file: - raise ValidationError('请上传文件。') + raise UserError("请上传文件") + book = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel_file)) sh = book.sheet_by_index(0) - for rx in range(1, sh.nrows): + + # 获取表头(第一行) + headers = [cell.value.strip() for cell in sh.row(0)] + + for rx in range(1, sh.nrows): # 从第二行开始读数据 row = sh.row(rx) - self.yuthon_payroll_data(row) + self.process_payslip_data(headers, row) - def yuthon_payroll_data(self, row): - employee_id = self.env['hr.employee'].search([('name', '=', row[0].value)]) - payslip_id = self.env['hr.payslip'].search( - [('employee_id', '=', employee_id.id)] - ) - payslip_id.write({ - 'year_compute': row[1].value, - 'year_jie': row[2].value, - 'year_gong': row[3].value, - 'jing_te': row[5].value, - 'study_on': row[4].value, - }) + def process_payslip_data(self, headers, row): + month = int(self.select_time) + month_mapping = { + 1: '一月', 2: '二月', 3: '三月', 4: '四月', + 5: '五月', 6: '六月', 7: '七月', 8: '八月', + 9: '九月', 10: '十月', 11: '十一月', 12: '十二月' + } + month_name = month_mapping.get(month, '一月') + employee_name = row[0].value if row[0].value else None + employee = self.env["hr.employee"].search([("name", "=", employee_name)], limit=1) + payslip = self.env["hr.payslip"].search([ + ('payslip_run_id.name', '=', f'{self.select_year}年-{month_name}'), + ("employee_id", "=", employee.id), + ('is_push', '=', False), + ]) + line_vals = {} + for col_idx in range(1, len(headers)): + header = headers[col_idx] + cell_value = row[col_idx].value + if cell_value in (None, "", " "): + continue + for line in payslip.line_ids: + if line.name == header: + line_vals[line.id] = {"total": float(cell_value)} + if line_vals: + for line_id, vals in line_vals.items(): + self.env["hr.payslip.line"].browse(line_id).write(vals) + payslip.with_context(recompute=True)._compute_salary() diff --git a/yuthon_salary/wizard/temporary_payslip_wizard_views.xml b/yuthon_salary/wizard/temporary_payslip_wizard_views.xml index 9e43163fb..9a528269a 100644 --- a/yuthon_salary/wizard/temporary_payslip_wizard_views.xml +++ b/yuthon_salary/wizard/temporary_payslip_wizard_views.xml @@ -3,7 +3,7 @@ 临时工资项导入模板 self - /yuthon_salary/static/others/temporary_payslip_template.xlsx + /yuthon_salary/static/others/工资项导入模板.xlsx 审批意见弹窗 @@ -17,6 +17,8 @@ + +