From f845b6f94eed984ae01971ca0a1970f12361edcb Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 16:40:08 +0800
Subject: [PATCH 01/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../models/property_lease_contract.py | 16 +++++++++++++++-
.../views/property_lease_contract.xml | 8 ++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
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..75229eede 100644
--- a/yuthon_property_approval/views/property_lease_contract.xml
+++ b/yuthon_property_approval/views/property_lease_contract.xml
@@ -857,6 +857,14 @@
code
action = model.all_end_approval()
+
+ 同步更新管理员
+
+
+ code
+ action = model.update_admin_id()
+
+
From 2d0fd0d4dd60b3af76efdd15c1e6f83af70a4dc5 Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 16:59:24 +0800
Subject: [PATCH 02/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yuthon_hr_employee_approval/models/yuthon_onboard_approval.py | 4 ++--
.../views/yuthon_onboard_approval_views.xml | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
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 @@
-
+
From 547db6e7a2f90ce111b1a422d1b919cd892fdaaa Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 17:40:13 +0800
Subject: [PATCH 03/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../models/yuthon_hr_employee.py | 51 +++++++++++++++++--
.../views/yuthon_hr_employee_views.xml | 5 ++
yuthon_salary/models/inherit_hr_payslip.py | 2 +
.../models/yuthon_payroll_project.py | 10 ++--
yuthon_salary/wizard/salary_compute_wizard.py | 3 +-
5 files changed, 62 insertions(+), 9 deletions(-)
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_salary/models/inherit_hr_payslip.py b/yuthon_salary/models/inherit_hr_payslip.py
index 071fe4cd2..a96ea047c 100644
--- a/yuthon_salary/models/inherit_hr_payslip.py
+++ b/yuthon_salary/models/inherit_hr_payslip.py
@@ -182,6 +182,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 == '事假':
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/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} 日期范围内已有工资单记录,无法创建新的工资单。")
From 8530bcd29e8dd1f70cc84da0c92c84fce9b98c0c Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 21:31:56 +0800
Subject: [PATCH 04/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yuthon_salary/__manifest__.py | 1 +
yuthon_salary/models/inherit_hr_payslip.py | 22 +++++++++-
yuthon_salary/security/ir.model.access.csv | 1 +
.../others/temporary_payslip_template.xlsx | Bin 10095 -> 0 bytes
.../static/others/工资项导入模板.xlsx | Bin 0 -> 9876 bytes
.../views/inherit_hr_payslip_views.xml | 9 ++++
yuthon_salary/wizard/__init__.py | 3 +-
.../wizard/initialize_payroll_wizard.py | 33 +++++++++++++++
.../initialize_payroll_wizard_views.xml | 24 +++++++++++
.../wizard/temporary_payslip_wizard.py | 39 +++++++++++-------
.../wizard/temporary_payslip_wizard_views.xml | 2 +-
11 files changed, 116 insertions(+), 18 deletions(-)
delete mode 100644 yuthon_salary/static/others/temporary_payslip_template.xlsx
create mode 100644 yuthon_salary/static/others/工资项导入模板.xlsx
create mode 100644 yuthon_salary/wizard/initialize_payroll_wizard.py
create mode 100644 yuthon_salary/wizard/initialize_payroll_wizard_views.xml
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 a96ea047c..1e76b400c 100644
--- a/yuthon_salary/models/inherit_hr_payslip.py
+++ b/yuthon_salary/models/inherit_hr_payslip.py
@@ -119,7 +119,7 @@ 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='高温补贴')
@@ -491,4 +491,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/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 6a8333b251403608eff5a7d8ceeec512236bef2e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 10095
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
Q+7pEf_DN2O>{;*s07(sh_W%F@
diff --git a/yuthon_salary/static/others/工资项导入模板.xlsx b/yuthon_salary/static/others/工资项导入模板.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..939ca44138a671baf3e2e9e440e6b25fc49bd1ad
GIT binary patch
literal 9876
zcmaJ{1yo#162&37yXyoexH~}t!F6zVcXxLUL4!L45AF~gf`kNuyGzi(4#|EA`}g-b
z^JdPQx?SD3`&IR;DtQ@5D0r~P7Am(Y^!WWpdj|SpWNRqzU~A{dBo7k91l@r9Db~XI
zxr!Ma42%r|3=H+}V)}M=jBeIenekoHkSv(umjMr`?`>Hb$Q55a4+!Ih)pRCewO#0Z
zg`TiAal^pqM0N#hb8f4AhscUCQPSk#s(ZN6eg>Cqo1R{`9N0izlxQ#U4A)r3T9vy^
zJu4;#@C+dP+|rV?NDI!1@%yFQN(E#rK14Vc^}18^wn-FpeTYPeeVwcm?H)n6{W+aU
znKT_&&2AxhpjEnYC?W@3VUWXmJA%1IM*Y>dsj1Q!CLm)YBLsraKCmLW*R7z57JL0{
zWoPX^xORnC5c2b)w&X$JGQ*grycz$q8x9?Tf^|)pZG6WC>8gUR`LBIb6MGBL=c_-i
zPYx~^8CU3Ub}CYPw&nLAS#}^5_F-BAdeNe_y9@g9yWLeJV|~G1hjj9bcFS@_vV#?7
ziniacHtnc!-d@|BK>yCJO0dqf42WHQ5W8r9XV=iy!T6Eh-~?H@9%j_wOUbWfrvmmF
z0O)c`EWurPO3DbKeR|IY&0aYpf+TFD}Upp+x>ku??Iz#qLRr}MJ%eX7$L^~q&
z&7UFxOel^fbbvDwEL$?Q{YYz7R!CgoYj~>5X?ig$$haI7Xb15Dv4fkV%KSohh**G$
zF#~k8yESLMq#tL(Rj)FnKWMMHE(~P8hbHNrY^!%*Yp^D$Hz(=YqPe<0{GO}Jp7fyw
z3s!kEK*`3hc_ArPM2ew;RXcetd`i2;GrQ4RsOdTrMjT|sV8+?E
zS4v6WerKK+Ta_CCVjc~|JjQR#J3BhrT0e4M5HBm!%ZwUw6!1W{p0$~$^@Y(I=eCBF
z81qw`wAA#XNMaJDws!A>O^P!Xiq1I~D-+82h*6mucR(An%LHvG8!0MWpEGB%HF7rb
z9KgTxgR1jEaq{4buzC|MBh!1&JR1G4r1Zyd#*R&nHkt-j*K&iiFh1pX`EwEtW2q*Q
z&fidgpVG$_0ik$gYH8!M=HZ1*gnTC{o|kGyMc0#KHpD22a}4iaUxA-bA!#Fj+bu5L
z3lj@tHxCBnQc3r)Nl=mnC6{X!&5iOxO|y)U%2%A1eN!eFUufCjYEw6j2#V*bT+1hu
zst3Gfj#iW&p1AC8D(~5RGll3o!0I)7RV43*{lUBG%+rO{b%QTzQ6i1RCrS)a0sE>5
zCQ2ew*ZTNGvq%@4j7*YU^O>Xl88e^wj#4TaU0Kf>sC{
z3JeVQe`J3`SRR)uT4~tk3o~jf&bA&qm?#u_9kVT&F!e{8rL$#J&&_i$&q33VHH
z-fBr3#dgS(!sM=MthGJHKE6{CgkA3Dvck2L1yE7d$!skF_xJV9Z|ybaFEr>^tYM`IcCpeBbr$3v7t0M^KNRvwnhk=_t{LJV@w0_
zc}VFOwV186Wxy@zknvyP3K0T`a3
zGf`kQ&z0UqI;`9lHDX*wCS6lPwM_-?O
zL4x|sH&lp1MXu?(8B1y<6wYv-lG0pD)MYRF+H3fBIF7+~5^)liggK&=?=aRly_Uj-
zevI4cmMT@-0r5ma7bYO(XqnfQ*PKs5oZ;;>dN4Ua@c}yPV5nD)@Evwgj)fd+);CWdl8(!}csex_N;a-OAZiLn
z_f#JM(T>N6B0&y+<-q5%+XfdddwrShRN*(X5ra{
zyTuCfm5Tr>=B-la~tKl
zQYNB6DTP1i9x`ACS7%Ko4U+-&(gTk_Pg~)TN2M@SVAN#w`@vvD9&RIyGW|#x5!bJ+{DLo
ziQ5BVT<8FpMdBGEJl;+MM=|dvYL`IQP|=5D@T$>6-iGf60*Sq>XBS-degjdH)N4y%
zKs84Vw8QbKa9;<3TKh&+o{P~%k&?uPG5WwtDKAEk|UkD#P$;=rl9^c~-aE_Np4fBXP
zGTaS1!B3Y;x>EkGAadYGLWXx6wLf}2(eE4-^y)RUv9M`S@bS>s_2sJM^SudyJ9ELp
zvtDrrsX~FwmtiafpG~G!qY_3!58~MxYDmJK%VR>*yt-K)7^y(LKz0|}>38}@HZOJg
zUBZrlr^tFLfpm8`%G1Fei8ZvNjhl#~yamz8xk#6Wa&e(`6!B=mg@SLnSBo!{Zqx(g
z(0Bgb7qgTsz2b-qs$K0+pSj$g#&%KL(hc8sCMGQwC-ZtdwlCIg*K&8BM=ZH@G+US?
z&)R-qSKmZ^U@i_i7skMWt)UNjsnVab*d@zFYeY>x$A%`{4@HR56J7cVmPf`WW?<7z
zqf{-=jKyCdp*zUqL%2W*qjJhfxMQA<;kyKI8H7MR3e?W;xo9bS62x+_VXN^kSW>Hh
zRA=i>7rBKXWHTm}*=c>X!MI}5is&LFmM4s
z?RUs?QoUYmRj~0-Wkfk~iwd1GDP(`wq^EPH+G8U0R)>!V0miA{mMq8OIqeK)=u5HA
zlMa0F%ufhFH5EGd3#M>FJL)5Jyi0?muH>GSm|!z(3Q_Ge15slf>K!)q*?=+~wYnyI1DGk_OrBLpkKt_Xcq`t%d{Nre>%IhHJuG
zDvURdFnb8HpB}omU
zA!CF^R-+Xz-*~~{6T9^asa+RRmE}Ui!GJWS1wuWY{q3GL_`H06>3lP0o8{RU^t!4B
zg6LY`_t&-J4;6Y3(48=sxx;lUADtx%n2Ce}zdu0zer`k&N2O{3IfyCH=kYBGPb1ncE`LZ}4yeXo_KR6VzDa+%e8{db2uh
zYKzYyG|r*4&-oNQA`8Qs?%HEXd}{f6r4MRW;s(2|()~p~DxsNK>Yu&$@q*}EEjd;p
zuR9Jwl(}SP8F|;wP!p_}?=zczhMu=&UK-zjnx4LG+-p+SY%_raB$qBf=Nrc6xe>}a
z{gj}d7HIK&UlX`EkB2-KM(|;LrG-;7WAb6sf=N(!EA}#1@CR{==kyxQCG78$&3DJ}
zTn8PJQ9;KqlwXs5JgOPk+FJfPb!pqaWybW*u6xkC=4XkE4F=@HQ<){Fmr74Nd8QLK
zL6zxIe8GED_~Y_3>Xh7D>MX{~tFMON9GN2DI%?z`kz!%+bskQ1!MTwu_Q!(zEcFHA
zNk}VYO^MjS9izx!nOvXryu&`dAnm1l0Re>On){HB@h*W-x-{e+gpJ$_GMQ3Y+E_6a
zbhAfpDtkYZ)K{wamij{XY5$vQ=^MDds7rNohJJE17tOS>`XMoI)7
z_+F)`X%QDll?!^p)O++;M_BYLDRfjTZ-IZ87@KceGRXEPa;$2o^LVX!m*5sR?-Ww61al9-ThAPR>nWK{UXpZmOu^fKBm_fmpcFj&a|?9#HCGtu7yG+LS76)1OhYq;N&o@
zW*`%>5Pek2#p{dbX*82D4Z(hZ0(8>Ov@g
zF+XiMcARFwU)_?m*YaceMK$`O&$mO(=SUkLwlx~9)`{BQ*YXphLT+
z+g9m^KlMI0y+IfHBwDjVBhgtrF*Or{{F?4Xt*VA?+VgA6XRK7d4Ep_8?KLO~ONjet+OA+*uc36>OQJNZnKbwr;l?@0XYv6g|S7&!I23S?yk$Deu65)35DY
zU0R(@5TzsYMX-q9g#+~25wI&Ylb7MJvsT@g&Eh^3pXu!|uldCg{A=ZEQ!UJ*I`O4Vzi_?D3N+*6ddg%tV3l2l4%SstmWjgVt
z>AZzoxCgOr7h!Y;2IRWo&I+fzzt@)@2Gr|zf<0j!?X$0)yD)BhP?}Ue<|-!^mlDPB
z40m^W$vq`*4}B6`Dm_{}93m>E@5vud=`3*{Y-3@GhFOo>NfgzhRMZ%y4c9kEr_!Xu
za`>demOV2{UwDn=*HTf^e=GtF+oZf!c)v&=t^d)nhb`nYZ)HE&D&N-^&HBwv!Qn*-
zmT->LK&0IMo`3n38HRT48#nj@%KmsX&;EcGu6Nh+!k`(bdskCstZnmQG
z-WZ@>^|b;L7gwko{M6_TU-AruNXU@FM|J(mK&)k`?_eCOoW-5S3h@?er0HUPcfj|J
zcU^eN{6?i3f!is{Ql-X$SC3yiei=YTs*+gW^!F}O_4yEttfFreRqQln3IpY7
z8O(t%q8-iajWqWZ&81$pwj!Za7`tl`OR=I=Q1PWI%h0acotk0P(c69z6$d^C
zT`e`18bbSx-Mz%2H5j^g_GzlsD>vDFrRf1(=3E^zPRbftuMvh4AAZ}-xa@`d&H4TbubrP@0=ZLv
z1LWI%kvG+ugeF~ez0XBl8(mIb`x`!nBIZC922|~edWJp3P7*C9VhmM
z`Y3BFXj0I{;-d+Sj*OMh023FbT$MtUCgrq1L(6$iZooqB*n^EGJQ)@i96A!MB?cu<
zL5@wbf|vGLFVp*8&w*1IHhnR_6*&)Cr_OJEsVE#18x5XQ-n7Wq$Nq+yOrHh(hkZ=a
z&dbIkUk|qChnAs5-f0v888Zza;Hyw@-0R6zVa)Kq4}z0+_c94enBIKgU~U-qolesV6B!A
z=8cZj{PGxjm%7{QUaNxPb9nupRk@8-#%a1BBtC@;jg20b=w!+hKLT7BpqGh9aJ4>5
znFGt)9jxvU@yd_lHic?%1lY~^IL(CRly6jwSrF##S`k%2_iC%5qk5y0TtbS?zuVD3Hh5mX%Jhan+=YUZM)4Ba|Wb-B32=VIc
z?*ufa&<=9!IJ6^ennF;U(ivZPTkvEI%n9t%03
zpQxU#zml^TXW3o*Qlob%HnxcUMjhh%L|;&XW~l(<`RE6Yu^6H__sXz%_9IC&B`$9-
zf9`|sq>cTRaYn+UWkCo2TLz)9oplm)Nei+qy;R9G`JxK52W@6x_(Xy74=$@*I-Rb&
z`P4q49}%ecQ&*I=Jd>SW&2+Wv>Evl8ucgmKnt2)^c5MaV68ANrYKrX`nPoI+%u!Nw
zx>k|s7Mwidzgw^;dov!Rv%3v-*wrQuQ{j~AV!y;GX~a1}FJ+%NLY@e2yz10`QG~o!
zk~PCQAZJH$U86!aKF5b(L#1MS=rXCngWD@!Q1!X-D_E_C%AljVrj+zFd7k?@;VeZj
zMmAK0oMVq0l+F4g{ujIRBN}#Z->N0$g_-n1OO*q?hnx>usp%?G7JIPlqA#4py$Sl!
zdj}z5D}ljhe&Frn`=bJHvJol(wET2U(>?p0D@o;WjOewqRsiRXD0OuqN$iAKREm_7M0tb
z=`C?ukbqO-{H-;FV}!#CDMy`|D>_MTc~Bmit(GdqOlp(PSX^f0#YotF5@9|Li8o^p
z;Qv2R`vn8c0{2{o6hF54sQpcO2~8tYIXFzMI8zuweP4rFJl@bTP?dem+$gLa%iZ9d
z2ldTiX9vwa*#Dm?3~1=O4#rkb6Ca*jF0Bus|Gt5e9|(WSFg{9FB#cXg(o+3L0Y|8t
z?Xs>|XrG;!$oFN9y#3N%JxowzBxoG8w+SoIfBFC$`{sJ{?Kdx@7J@}m9hSnF)&OUP
z03q8!mv)6?FE{nY0Lk*U8Ax|!yKbnK-BW|xpukPOqyka1Y;l!7MF#~k
z#6cM;{9n7}m)PIK8Go<~O1L8SA4#@~Zt%)}0T(gbVT3bR;~*xLUKG@PD^~OJ^A`*(
zvR7sd`rrFdk8dq>&Sj(^rhG;*Vm|MbV|ju_(^uc0op#E
zwcb6d_8=F&27spxq5L#*r~x;{?h;|JHxVRl?p?$c)Q&E~Z_+JYQZUCuj%7HA;L!h8UqO6Jp9h^GX+X(#^sA%d3uk^P{u5P>{NyLNl%M}W0<02Sx(xPl_
zC9<_paP8~+l-I@XrKCS`E5l=Kb)CN26e$P`)n{y*LSTn=7sTb0JfQb4NV7mbd4hVees410RWk
z(1qRiw~{54QM790ZeXK?aY3CGhV1FaeuI2bAD
z$_cvs!vg;5;GZ+Lj|S^kd#ncWAo|OE{jT(>0ei&!QkwNA=H~p4O>63jfka^LN;Pt5%*;^t2A+FLW?yum7p^kE)EP@TbKDf59I>_WLjRpQQ!=
zHtENt`LFgkX?0NO732#44e&cl{lq^3e^|jMEIqM;8QhPo4K8{MRr#{t5m+uKX$gPlNJ*^~VW{QvOqj{%H?S1Dk*Kzz&+#V-J6Z
zIsc7|-
+
@@ -229,6 +230,7 @@
+
@@ -460,6 +462,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..e5b7460f5
--- /dev/null
+++ b/yuthon_salary/wizard/initialize_payroll_wizard.py
@@ -0,0 +1,33 @@
+# -*- 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
+ })
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..570966897
--- /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/temporary_payslip_wizard.py b/yuthon_salary/wizard/temporary_payslip_wizard.py
index 1f7ffa1ce..36073250f 100644
--- a/yuthon_salary/wizard/temporary_payslip_wizard.py
+++ b/yuthon_salary/wizard/temporary_payslip_wizard.py
@@ -17,23 +17,34 @@ class TemporaryPayslipWizard(models.Model):
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)]
+ def process_payslip_data(self, headers, row):
+ 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(
+ [("employee_id", "=", employee.id), ('is_push', '=', False)],
)
- 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,
- })
+ 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)
diff --git a/yuthon_salary/wizard/temporary_payslip_wizard_views.xml b/yuthon_salary/wizard/temporary_payslip_wizard_views.xml
index 9e43163fb..3fe179e00 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
审批意见弹窗
From a88b1ae167665fb36872380675a29270a451643c Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 21:34:27 +0800
Subject: [PATCH 05/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yuthon_salary/views/inherit_hr_payslip_views.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/yuthon_salary/views/inherit_hr_payslip_views.xml b/yuthon_salary/views/inherit_hr_payslip_views.xml
index 115a6d97a..a38e06bb2 100644
--- a/yuthon_salary/views/inherit_hr_payslip_views.xml
+++ b/yuthon_salary/views/inherit_hr_payslip_views.xml
@@ -230,7 +230,6 @@
-
From c94eede4306639f28ab51cd2bb427c2f3d5d4f6f Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 22:13:08 +0800
Subject: [PATCH 06/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yuthon_contract/models/inherit_hr_contract.py | 2 +-
.../views/inherit_hr_contract_views.xml | 14 +++++-----
.../wizard/initialize_payroll_wizard.py | 1 +
.../wizard/temporary_payslip_wizard.py | 27 ++++++++++++++++---
.../wizard/temporary_payslip_wizard_views.xml | 2 ++
5 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/yuthon_contract/models/inherit_hr_contract.py b/yuthon_contract/models/inherit_hr_contract.py
index b58693c73..a38ba5082 100644
--- a/yuthon_contract/models/inherit_hr_contract.py
+++ b/yuthon_contract/models/inherit_hr_contract.py
@@ -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'),
diff --git a/yuthon_contract/views/inherit_hr_contract_views.xml b/yuthon_contract/views/inherit_hr_contract_views.xml
index 7cb7b5339..92d5a0b42 100644
--- a/yuthon_contract/views/inherit_hr_contract_views.xml
+++ b/yuthon_contract/views/inherit_hr_contract_views.xml
@@ -8,10 +8,16 @@
+
+
+
+
+ 结束日期
+
1
@@ -26,18 +32,12 @@
-
- 0
-
-
-
-
1
-
+
diff --git a/yuthon_salary/wizard/initialize_payroll_wizard.py b/yuthon_salary/wizard/initialize_payroll_wizard.py
index e5b7460f5..da9d317aa 100644
--- a/yuthon_salary/wizard/initialize_payroll_wizard.py
+++ b/yuthon_salary/wizard/initialize_payroll_wizard.py
@@ -31,3 +31,4 @@ class InitializePayrollWizard(models.Model):
'total': 0,
'amount': 0
})
+ payslip.with_context(recompute=True)._compute_salary()
\ No newline at end of file
diff --git a/yuthon_salary/wizard/temporary_payslip_wizard.py b/yuthon_salary/wizard/temporary_payslip_wizard.py
index 36073250f..693e9dae5 100644
--- a/yuthon_salary/wizard/temporary_payslip_wizard.py
+++ b/yuthon_salary/wizard/temporary_payslip_wizard.py
@@ -14,6 +14,17 @@ 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:
@@ -30,11 +41,20 @@ class TemporaryPayslipWizard(models.Model):
self.process_payslip_data(headers, row)
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(
- [("employee_id", "=", employee.id), ('is_push', '=', False)],
- )
+ 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]
@@ -47,4 +67,5 @@ class TemporaryPayslipWizard(models.Model):
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 3fe179e00..9a528269a 100644
--- a/yuthon_salary/wizard/temporary_payslip_wizard_views.xml
+++ b/yuthon_salary/wizard/temporary_payslip_wizard_views.xml
@@ -17,6 +17,8 @@
+
+
From 68ce469577e6653f9e794a0d09b5936af5435fad Mon Sep 17 00:00:00 2001
From: zhou <123@>
Date: Thu, 3 Jul 2025 22:16:14 +0800
Subject: [PATCH 07/12] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yuthon_salary/wizard/initialize_payroll_wizard_views.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/yuthon_salary/wizard/initialize_payroll_wizard_views.xml b/yuthon_salary/wizard/initialize_payroll_wizard_views.xml
index 570966897..634e4fcda 100644
--- a/yuthon_salary/wizard/initialize_payroll_wizard_views.xml
+++ b/yuthon_salary/wizard/initialize_payroll_wizard_views.xml
@@ -7,8 +7,8 @@
' 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):
- data = {"message": "超出范围,不允许打卡", 'state': 'no', 'state_result': '超出范围'}
- _logger.info("-------触发超出范围,不允许打卡---------")
- else:
- data = {'location': '%s' % address_id.address_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']}__________")
+ 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}"],
@@ -91,7 +95,8 @@ class AppletAttendance(Controller):
'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)
@@ -185,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:])
@@ -202,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'])
@@ -245,7 +251,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)
@@ -270,7 +275,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_property_approval/views/property_lease_contract.xml b/yuthon_property_approval/views/property_lease_contract.xml
index 75229eede..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}