@@ -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 '<form create="false"' not in res['arch']:
|
||||
if view_type in ["tree", "form"]:
|
||||
params = {}
|
||||
@@ -746,6 +742,8 @@ class SoongWorkflowMixin(models.AbstractModel):
|
||||
id_el.set("name", "log_ids")
|
||||
id_el.set("invisible", "1")
|
||||
invisible += " or not log_ids "
|
||||
if button.name == "结束" :
|
||||
invisible += " or True"
|
||||
# invisible += " or not work_end "
|
||||
btn_exec_action_el.set("invisible", invisible)
|
||||
btn_exec_action_el.set("context", "{'btn_key':'%s','active_model':'%s'}" % (button.btn_key, self._name),)
|
||||
|
||||
@@ -22,7 +22,7 @@ class HrContract(models.Model):
|
||||
salary_group_id = fields.Many2one('yuthon.salary.group', string="工资单分组", store=True, index=True)
|
||||
is_internship = fields.Boolean(string="实习期", compute='_compute_name', store=True)
|
||||
date_end = fields.Date(string="合同结束时间", compute='_compute_date_end', store=True)
|
||||
termination_date = fields.Date(string="合同中止时间")
|
||||
termination_date = fields.Date(string="中止时间")
|
||||
work_entry_source = fields.Selection([
|
||||
('calendar', '考勤安排'),
|
||||
('attendance', 'Attendances'),
|
||||
|
||||
@@ -8,10 +8,16 @@
|
||||
<xpath expr="//field[@name='date_end']" position="after">
|
||||
<field name="date_of_entry"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_end']" position="before">
|
||||
<field name="termination_date"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='employee_id']" position="after">
|
||||
<field name="contract_type_id" string="用工形式"/>
|
||||
<field name="is_company" string="是否本公司" optional="hide"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_end']" position="attributes">
|
||||
<attribute name="string">结束日期</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='name']" position="attributes">
|
||||
<attribute name="column_invisible">1</attribute>
|
||||
</xpath>
|
||||
@@ -26,18 +32,12 @@
|
||||
<xpath expr="//field[@name='employee_id']" position="after">
|
||||
<field name="code"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_end']" position="attributes">
|
||||
<attribute name="readonly">0</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_end']" position="after">
|
||||
<field name="termination_date"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='other']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='contract_type_id']" position="after">
|
||||
<field name="is_probation"/>
|
||||
<field name="termination_date"/>
|
||||
<field name="is_probation"/>
|
||||
<field name="salary_day" invisible="not is_internship"/>
|
||||
<field name="salary_group_id" options="{'no_create': True}"/>
|
||||
<field name="is_internship" invisible="1"/>
|
||||
|
||||
@@ -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'<p style="color:red">{check_in_str}</p>' if check_in_status in ['迟到', '早退', '缺卡'] else f'<p style="color:green">{check_in_str}</p>'
|
||||
check_out_html = f'<p style="color:red">{check_out_str}</p>' if check_out_status in ['迟到', '早退', '缺卡'] else f'<p style="color:green">{check_out_str}</p>'
|
||||
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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -162,6 +162,11 @@
|
||||
<field name="pension_insurance_base"/>
|
||||
<field name="work_injury_insurance_base"/>
|
||||
<field name="housing_fund_base"/>
|
||||
<field name="salary_month" invisible="1"/>
|
||||
<field name="salary_medical_insurance" invisible="1"/>
|
||||
<field name="salary_insurance" invisible="1"/>
|
||||
<field name="salary_work_injury_insurance" invisible="1"/>
|
||||
<field name="salary_housing_fund" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="converted"/>
|
||||
|
||||
@@ -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='合同类型')
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="job_id"/>
|
||||
<field name="job"/>
|
||||
<field name="department_id1"/>
|
||||
</group>
|
||||
<group>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -809,6 +809,7 @@
|
||||
<field name="name">合同管理</field>
|
||||
<field name="res_model">property.lease.contract</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'search_default_group_by_admin_id': True}</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property_approval.view_property_lease_contract_tree1')}),
|
||||
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property_approval.view_property_lease_contract_form1')})]"/>
|
||||
@@ -818,6 +819,7 @@
|
||||
<field name="res_model">property.lease.contract</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('condition', 'in', ['new', 'approval', 'cancel'])]</field>
|
||||
<field name="context">{'search_default_group_by_admin_id': True}</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property_approval.view_property_lease_contract_tree')}),
|
||||
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property_approval.view_property_lease_contract_form')})]"/>
|
||||
@@ -857,6 +859,14 @@
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.all_end_approval()</field>
|
||||
</record>
|
||||
<record id="update_admin_id" model="ir.actions.server">
|
||||
<field name="name">同步更新管理员</field>
|
||||
<field name="model_id" ref="yuthon_property_approval.model_property_lease_contract"/>
|
||||
<field name="binding_model_id" ref="yuthon_property_approval.model_property_lease_contract"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.update_admin_id()</field>
|
||||
</record>
|
||||
|
||||
<!-- <record id="all_data_action_use_admin" model="ir.actions.server">-->
|
||||
<!-- <field name="name">调换</field>-->
|
||||
<!-- <field name="model_id" ref="yuthon_property_approval.model_property_lease_contract"/>-->
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
Binary file not shown.
Binary file not shown.
@@ -180,6 +180,7 @@
|
||||
<field name="seniority_salary" invisible="1" required="0"/>
|
||||
<field name="union_dues" invisible="1"/>
|
||||
<field name="overtime_pay" invisible="1"/>
|
||||
<field name="b_year" invisible="1"/>
|
||||
<field name="meal_misunderstanding_fee" invisible="1"/>
|
||||
<field name="personal_leave" invisible="1"/>
|
||||
<field name="sick_leave" invisible="1"/>
|
||||
@@ -236,7 +237,7 @@
|
||||
<field name="date_of_entry" optional="show"/>
|
||||
<field name="struct_id" string="职级" optional="show" column_invisible="1"/>
|
||||
<field name="bank_account_id" optional="show" column_invisible="1"/>
|
||||
<field name="actual_salary" decoration-danger="1" decoration-bf="1" optional="show" column_invisible="1"/>
|
||||
<field name="actual_salary" decoration-danger="1" decoration-bf="1" optional="show"/>
|
||||
<field name="total_before_tax" decoration-danger="1" decoration-bf="1" optional="show"/>
|
||||
<field name="b_year" decoration-danger="1" decoration-bf="1" optional="show"/>
|
||||
<field name="tax_payable" decoration-danger="1" decoration-bf="1" readonly="1" optional="show"/>
|
||||
@@ -296,6 +297,8 @@
|
||||
<field name="study_on" decoration-danger="1" optional="show" groups="yuthon_salary.group_jtbb_user"/>
|
||||
<field name="excellent_employees" decoration-danger="1" groups="yuthon_salary.group_wg_user" optional="show"/>
|
||||
<field name="medical_fee" decoration-danger="1" groups="yuthon_salary.group_lc_user" optional="show"/>
|
||||
<field name="retroactive_insurance" decoration-danger="1" groups="yuthon_salary.group_lc_user" optional="show"/>
|
||||
<field name="distribution_part" decoration-danger="1" groups="yuthon_salary.group_lc_user" optional="show"/>
|
||||
<field name="commuting_deduction" decoration-danger="1" optional="show" groups="yuthon_salary.group_jtbb_user"/>
|
||||
<field name="performance_deduction" decoration-danger="1" optional="show" groups="yuthon_salary.group_jtbb_user"/>
|
||||
<field name="retention_previous_year" decoration-danger="1" optional="show" groups="yuthon_salary.group_jtbb_user"/>
|
||||
@@ -464,6 +467,13 @@
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.action_return_salary_wizard()</field>
|
||||
</record>
|
||||
<record id="all_initialize_payroll_wizard" model="ir.actions.server">
|
||||
<field name="name">初始化工资项</field>
|
||||
<field name="model_id" ref="hr_payroll.model_hr_payslip"/>
|
||||
<field name="binding_model_id" ref="hr_payroll.model_hr_payslip"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.initialize_payroll_wizard()</field>
|
||||
</record>
|
||||
<record id="all_data_return_release" model="ir.actions.server">
|
||||
<field name="name">批量退回薪资审定</field>
|
||||
<field name="model_id" ref="hr_payroll.model_hr_payslip"/>
|
||||
|
||||
@@ -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
|
||||
from . import payslip_send_wizard
|
||||
from . import initialize_payroll_wizard
|
||||
@@ -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()
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="initialize_payroll_wizard_form" model="ir.ui.view">
|
||||
<field name="name">初始化工资单</field>
|
||||
<field name="model">initialize.payroll.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="payslip_ids" widget="many2many_tags" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="payroll_common_ids" widget="many2many_tags" domain="[('company_id', '=', company_id)]"/>
|
||||
<field name="payroll_temporary_ids" widget="many2many_tags" domain="[('company_id', '=', company_id)]"/>
|
||||
<field name="payroll_insurances_ids" widget="many2many_tags" domain="[('company_id', '=', company_id)]"/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button type="object" name="confirm" string="确认" class="oe_highlight"/>
|
||||
<button special="cancel" string="取消" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -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} 日期范围内已有工资单记录,无法创建新的工资单。")
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<record id="temporary_template_downloads_tian_he_action" model="ir.actions.act_url">
|
||||
<field name="name">临时工资项导入模板</field>
|
||||
<field name="target">self</field>
|
||||
<field name="url">/yuthon_salary/static/others/temporary_payslip_template.xlsx</field>
|
||||
<field name="url">/yuthon_salary/static/others/工资项导入模板.xlsx</field>
|
||||
</record>
|
||||
<record id="temporary_payslip_wizard_form" model="ir.ui.view">
|
||||
<field name="name">审批意见弹窗</field>
|
||||
@@ -17,6 +17,8 @@
|
||||
</header>
|
||||
<group>
|
||||
<group>
|
||||
<field name="select_time"/>
|
||||
<field name="select_year"/>
|
||||
<field name="excel_file" filename="excel_name"/>
|
||||
<field name="excel_name" invisible="1"/>
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user