From 1a646109679454e9bfc5af8cc74ab104bbe50362 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?=
<9664676+pengyuthon@user.noreply.gitee.com>
Date: Sat, 6 Sep 2025 16:05:44 +0800
Subject: [PATCH 1/4] =?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
---
.../wizard/property_reconciliation_wizard.py | 51 ++++++--
yuthon_salary/models/inherit_hr_payslip.py | 12 +-
.../models/yuthon_payroll_project.py | 8 +-
yuthon_salary/wizard/salary_compute_wizard.py | 121 +++++++++++++-----
yuthon_stock/models/inherit_stock_picking.py | 2 +-
yuthon_stock/views/inherit_stock_picking.xml | 2 +-
6 files changed, 136 insertions(+), 60 deletions(-)
diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py
index 6b3327731..cfdbcc66c 100644
--- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py
+++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py
@@ -198,6 +198,18 @@ class PropertyReconciliationWizard(models.Model):
# 3. 补全缺失的跨月违约金(按合同+物业+年月匹配,缺失则新增)
missing_penalty_lines = []
+ year, month = map(int, due_month.split('-'))
+ first_day = datetime(year, month, 1).date()
+ penalty_days = (self.actual_date - first_day).days + 1
+
+ if month == 1:
+ last_month_year = year - 1
+ last_month = 12
+ else:
+ last_month_year = year
+ last_month = month - 1
+ last_month_str = f"{last_month_year}-{last_month:02d}"
+
for rent_info in target_rent_months:
contract = rent_info['contract_id']
property_rec = rent_info['property_id']
@@ -212,18 +224,6 @@ class PropertyReconciliationWizard(models.Model):
if check_key not in existing_penalty_months:
# 计算补全违约金的日期范围:当月1号 ~ 当月最后1天
- year, month = map(int, due_month.split('-'))
- first_day = datetime(year, month, 1).date()
- penalty_days = (self.actual_date - first_day).days + 1
-
- if month == 1:
- last_month_year = year - 1
- last_month = 12
- else:
- last_month_year = year
- last_month = month - 1
- last_month_str = f"{last_month_year}-{last_month:02d}"
-
# 查找上个月的租金明细行
last_month_rent_line = next((
line for line in self.reconciliation_line_ids
@@ -233,7 +233,6 @@ class PropertyReconciliationWizard(models.Model):
and line.due_date # 有到期日期
and fields.Date.from_string(line.due_date).strftime('%Y-%m') == last_month_str
), None)
-
# 提取上个月租金的rent金额
last_month_rent = last_month_rent_line.rent if last_month_rent_line else 0.0
penalty_amount = penalty_days * round(
@@ -252,6 +251,32 @@ class PropertyReconciliationWizard(models.Model):
'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days}天',
}))
+ # 查找上个月应收是否有为交全的租金
+ unpaid_rent_recs = self.env['property.accounts.receivable'].search([
+ ('management_type_id.name', '=', '租金'),
+ ('contract_id', '=', contract_id.id),
+ ('property_id', '=', property_id),
+ ('submitted', '!=', 0),
+ ('due_date', '=like', f'{last_month_str}%')
+ ])
+ if unpaid_rent_recs:
+ first_day = (unpaid_rent_recs.due_date_end + timedelta(days=1))
+ last_month_rent = unpaid_rent_recs.submitted
+ penalty_amount = penalty_days * round(last_month_rent * float(contract.percentage_liquidated_damages.name), 2
+ ) if contract else 0.0
+ if penalty_amount > 0:
+ missing_penalty_lines.append((0, 0, {
+ 'property_id': property_rec.id if property_rec else None,
+ 'contract_id': contract.id if contract else None,
+ 'rent_month': 0,
+ 'due_date': fields.Date.to_string(first_day),
+ 'rent': penalty_amount,
+ 'amount': 0,
+ 'collection_type': 'penalty',
+ 'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days}天)',
+ }))
+
+
if missing_penalty_lines:
self.write({ 'reconciliation_line_ids': missing_penalty_lines})
self.create_reconciliation_vals()
diff --git a/yuthon_salary/models/inherit_hr_payslip.py b/yuthon_salary/models/inherit_hr_payslip.py
index 870fc1539..2d7ab777d 100644
--- a/yuthon_salary/models/inherit_hr_payslip.py
+++ b/yuthon_salary/models/inherit_hr_payslip.py
@@ -167,8 +167,8 @@ class InheritHrPayslip(models.Model):
total_payable = fields.Float(string='应缴金额合计')
performance_deduction = fields.Float(string='扣发绩效奖金')
- commuting_deduction = fields.Float(string='扣发通信补贴', compute='_compute_deduction', store=True)
- base_salary_deduction = fields.Float(string='扣发基本工资', compute='_compute_deduction', store=True)
+ commuting_deduction = fields.Float(string='扣发通信补贴', store=True)
+ base_salary_deduction = fields.Float(string='扣发基本工资', store=True)
specialized_deduction = fields.Float(string='累计专项扣除')
@api.onchange('minus_att', 'individual_income_tax', 'minus_annuity_difference')
@@ -564,14 +564,6 @@ class InheritHrPayslip(models.Model):
}
- @api.depends('employee_id')
- def _compute_deduction(self):
- for rec in self:
- employee_id = self.env['hr.employee'].search([('id', '=', rec.employee_id.id)])
- rec.performance_deduction = employee_id.last_month_performance_deduction
- rec.commuting_deduction = employee_id.last_month_commuting_deduction
- rec.base_salary_deduction = employee_id.last_month_base_salary_deduction
-
def put_payslip(self):
menu_id = self.env['ir.ui.menu'].search([('name', '=', '工资条'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=hr.payslip&view_type=list&menu_id={menu_id.id}'
diff --git a/yuthon_salary/models/yuthon_payroll_project.py b/yuthon_salary/models/yuthon_payroll_project.py
index a2d620194..833c270cb 100644
--- a/yuthon_salary/models/yuthon_payroll_project.py
+++ b/yuthon_salary/models/yuthon_payroll_project.py
@@ -56,11 +56,11 @@ class YuthonPayrollCommon(models.Model):
rule_list = []
formula_salary = ''
if ins.name == '扣发绩效奖金':
- formula_salary = "result = employee.last_month_performance_deduction"
+ formula_salary = "result = (employee.last_month_performance_deduction) * -1"
if ins.name == '扣发通信补贴':
- formula_salary = "result = employee.last_month_commuting_deduction"
+ formula_salary = "result = (employee.last_month_commuting_deduction) * -1"
if ins.name == '扣发基本工资':
- formula_salary = "result = employee.last_month_base_salary_deduction"
+ formula_salary = "result = (employee.last_month_base_salary_deduction) * -1"
if ins.name == '工龄工资' and ins.company_id.name == '天河物管':
formula_salary = "result = employee.company_old_year * 10 if employee.company_old_year >= 1 and employee.department_id.name != '领导班子' else 0"
if ins.name == '工龄工资' and ins.company_id.name == '集团本部':
@@ -74,7 +74,7 @@ class YuthonPayrollCommon(models.Model):
if ins.name == '全勤奖' and ins.company_id.name == '天河物管':
formula_salary = "result = 100 if employee.last_attendance else 0"
if ins.name == '加班费':
- formula_salary = "result = c if (employee.is_annual_salary or employee.contract_id.structure_type_id.name == '工勤返聘') else (round(((employee.all_overtime_hours - 40) * (payslip.base_salary / 21.75 / 8) * 1.5), 0) if employee.all_overtime_hours > 40 else 0)"
+ formula_salary = "result = c if (employee.is_annual_salary or employee.contract_id.structure_type_id.name == '工勤返聘') else (round(((employee.last_month_overtime - 40) * (payslip.base_salary / 21.75 / 8) * 1.5), 0) if employee.last_month_overtime > 40 else 0)"
if ins.name == '岗位工资':
formula_salary = "result = 0"
if ins.name == '安全生产管理补贴':
diff --git a/yuthon_salary/wizard/salary_compute_wizard.py b/yuthon_salary/wizard/salary_compute_wizard.py
index 4a58cc3af..74582104f 100644
--- a/yuthon_salary/wizard/salary_compute_wizard.py
+++ b/yuthon_salary/wizard/salary_compute_wizard.py
@@ -55,10 +55,10 @@ class SalaryComputeWizard(models.Model):
('contract_id.termination_date', '=', False),
('contract_id.date_end', '=', False)
])
- self.employee_ids = [(6, 0, employees.ids)]
- self.employee_number = len(employees)
- for employee in employees:
- print(f"员工姓名: {employee.name}")
+ # self.employee_ids = [(6, 0, employees.ids)]
+ # self.employee_number = len(employees)
+ # for employee in employees:
+ # print(f"员工姓名: {employee.name}")
@api.onchange('employee_ids')
def onchange_employee_ids(self):
@@ -97,8 +97,7 @@ class SalaryComputeWizard(models.Model):
'state': 'draft',
'payslip_count': 0
})
- query_date = datetime(year, month, 1).date()
-
+ query_date = datetime(year, month, 1).date()
vacation_report = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', employee.id),
('vacation_date', '=', query_date)
@@ -144,6 +143,11 @@ class SalaryComputeWizard(models.Model):
}
# 更新员工花名册记录
employee.write(update_vals)
+
+ # 加班时长
+ employee.last_month_overtime = vacation_report.done_working_overtime
+ employee.last_attendance = vacation_report.should_attendance == vacation_report.actual_attendance
+
employee.salary_date = start_date
employee.salary_month = self.select_time
employee.salary_year_jin_ids = [(6, 0, self.employee_ids.ids)]
@@ -185,11 +189,6 @@ class SalaryComputeWizard(models.Model):
contract = employee.contract_id
year = int(self.select_year)
month = int(self.select_time)
- # 更改扣发绩效奖金
- performance_deduction = self.salary_performance_deduction(employee, self.select_year, self.select_time, internship_id.performance_bonuses)
- internship_id.write({
- 'performance_deduction': performance_deduction,
- })
# 获取离职日期
turnover_date = contract.date_end
start_date = fields.Date.from_string(start_date)
@@ -321,12 +320,29 @@ class SalaryComputeWizard(models.Model):
setattr(internship_id, field_name, result)
line.write({'total': result})
+ # 计算扣发绩效奖金
+ performance_deduction = self.salary_performance_deduction(vacation_report, internship_id.performance_bonuses)
+ employee.write({
+ 'last_month_performance_deduction': performance_deduction,
+ })
+ # 计算扣发基本薪资
+ basic_salary_deduction = self.salary_basic_salary_deduction(vacation_report, internship_id.base_salary)
+ employee.write({
+ 'last_month_base_salary_deduction': basic_salary_deduction,
+ })
+ # 计算扣发通讯
+ commuting_deduction = self.salary_commuting_deduction(vacation_report, internship_id.commuting_allowance)
+ employee.write({
+ 'last_month_commuting_deduction': commuting_deduction,
+ })
+
year = int(self.select_year)
month = int(self.select_time)
- for rec2 in self.env['hr.payslip'].search([('is_push', '=', False)]):
- if not self._is_employee_turnover(rec2.employee_id, year, month) and not self._is_employee_in_probation(
- rec2.employee_id, year, month):
- rec2.compute_sheet()
+ if not self._is_employee_turnover(existing_payslip.employee_id, year, month) and not self._is_employee_in_probation(
+ existing_payslip.employee_id, year, month):
+ existing_payslip.compute_sheet()
+
+
def _get_chinese_month(self, month):
chinese_months = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
@@ -429,19 +445,62 @@ class SalaryComputeWizard(models.Model):
selected_date = fields.Date.to_date(f"{year}-{month:02d}-01")
return contract.start_date <= selected_date <= contract.end_date
- def salary_performance_deduction(self, employee, selected_year, selected_month, performance_bonuses):
- attendance_month_obj = self.env["hr.attendance.month.detail"].search([
- ('employee_id', '=', employee.id),
- ('current_year', '=', selected_year),
- ('current_month', '=', selected_month),
- ('state', '=', 'confirmed')
- ], limit=1)
- if attendance_month_obj:
- year = int(selected_year)
- month = int(selected_month)
- last_day = calendar.monthrange(year, month)[1]
- total_days = last_day
- result = (performance_bonuses / total_days) * attendance_month_obj.maternity_leave if total_days != 0 else 0.0
- return round(result, 2)
- else:
- return 0.00
\ No newline at end of file
+ def salary_performance_deduction(self, vacation_report, performance_bonuses):
+ if not (vacation_report and performance_bonuses):
+ return 0.00
+ deduction_leave_fields = [
+ 'marriage_leave', # 婚假
+ 'maternity_leave', # 产假
+ 'paternity_leave', # 陪产假
+ 'family_planning_leave', # 计生假
+ 'bereavement_leave', # 丧假
+ 'home_visit_leave', # 探亲假
+ 'general_sick_leave', # 病假(普通)
+ 'hospital_sick_leave', # 病假(住院期)
+ 'recovery_sick_leave', # 病假(出院恢复期)
+ 'exam_leave', # 考试假
+ 'parents_meeting_leave', # 家长会假
+ 'leave_of_absence' # 事假
+ ]
+ total_leave_days = 0.0
+ for field in deduction_leave_fields:
+ leave_days = getattr(vacation_report, field, 0.0)
+ total_leave_days += leave_days if leave_days > 0 else 0.0
+ if total_leave_days <= 0:
+ return 0.00
+ return round((performance_bonuses / 21.75) * total_leave_days, 2)
+
+ def salary_basic_salary_deduction(self, vacation_report, base_salary):
+ if not (vacation_report and base_salary > 0):
+ return 0.00
+
+ deduction_rules = {
+ 'general_sick_leave': 1.0, # 普通病假:1%
+ 'recovery_sick_leave': 1.0, # 出院恢复期病假:1%
+ 'leave_of_absence': 2.5 # 事假:2.5%
+ }
+ total_deduction = 0.0
+ for leave_field, ratio in deduction_rules.items():
+ leave_days = getattr(vacation_report, leave_field, 0.0)
+ if leave_days > 0:
+ single_deduction = base_salary * leave_days * (ratio / 100)
+ total_deduction += single_deduction
+
+ return round(total_deduction, 2)
+
+ def salary_commuting_deduction(self, vacation_report, communication_subsidy):
+ if not (vacation_report and communication_subsidy > 0):
+ return 0.00
+ maternity_days = vacation_report.maternity_leave # 产假天数
+ paternity_days = vacation_report.paternity_leave # 陪产假天数
+ family_planning_days = vacation_report.family_planning_leave # 计生假天数
+ total_days = sum([
+ maternity_days if maternity_days > 0 else 0,
+ paternity_days if paternity_days > 0 else 0,
+ family_planning_days if family_planning_days > 0 else 0
+ ])
+ # 通讯补贴 / 21.75 * 总请假天数
+ if total_days <= 0:
+ return 0.00
+ deduction_amount = (communication_subsidy / 21.75) * total_days
+ return round(deduction_amount, 2)
\ No newline at end of file
diff --git a/yuthon_stock/models/inherit_stock_picking.py b/yuthon_stock/models/inherit_stock_picking.py
index e498f509b..03a4e4b57 100644
--- a/yuthon_stock/models/inherit_stock_picking.py
+++ b/yuthon_stock/models/inherit_stock_picking.py
@@ -7,7 +7,7 @@ class StockPicking(models.Model):
_inherit = 'stock.picking'
document_ids = fields.Many2many('ir.attachment', string="附件")
- employee_id = fields.Many2one('hr.employee', string="领用人")
+ employee_id = fields.Many2one('hr.employee', string="验收人")
user_id = fields.Many2one('res.users', string="采购人员", compute="_compute_origin")
json_display = fields.Char(
string="物品详情",
diff --git a/yuthon_stock/views/inherit_stock_picking.xml b/yuthon_stock/views/inherit_stock_picking.xml
index 7f98ce37a..731545098 100644
--- a/yuthon_stock/views/inherit_stock_picking.xml
+++ b/yuthon_stock/views/inherit_stock_picking.xml
@@ -12,7 +12,7 @@
-
+
From 9a586e3395f55844e38a5f3bcfcf40069740d631 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?=
<9664676+pengyuthon@user.noreply.gitee.com>
Date: Sat, 6 Sep 2025 18:34:05 +0800
Subject: [PATCH 2/4] =?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_meter_archives.py | 52 ++++++++++----
.../models/property_meter_water.py | 52 ++++++++++----
.../views/property_meter_archives_views.xml | 70 +++++++++++++++++--
.../views/property_meter_water_views.xml | 69 ++++++++++++++++--
.../wizard/property_meter_wizard.py | 66 +++++++++++------
.../wizard/property_water_wizard.py | 64 +++++++++++------
6 files changed, 287 insertions(+), 86 deletions(-)
diff --git a/yuthon_property/models/property_meter_archives.py b/yuthon_property/models/property_meter_archives.py
index 10656dd19..beb3ccbfb 100644
--- a/yuthon_property/models/property_meter_archives.py
+++ b/yuthon_property/models/property_meter_archives.py
@@ -8,15 +8,16 @@ class PropertyMeterArchives(models.Model):
_description = '电表库'
name = fields.Char(string='仪表编号')
- unit_number = fields.Char(string='单元编号')
+ unit_number = fields.Char(string='单元编号/用途')
installation_location = fields.Char(string='安装位置')
is_state = fields.Boolean(string='是否启用')
type = fields.Selection([('public', '公共'), ('fees', '收费')], string='类型')
gauge_magnification = fields.Integer(string='仪表倍率', default=1)
- maximum_count = fields.Integer(string='标准用量', default=999999)
- lessee_id = fields.Many2one(related='yuthon_property_id.lessee_id', string='承租方', store=True)
+ maximum_count = fields.Integer(string='最大位数')
+ lessee_id = fields.Many2one('property.tenant.information', string='承租方', store=True, compute='_compute_lessee_id')
employee_ids = fields.Many2many('hr.employee', string='抄表人')
- meter_type = fields.Selection([('electric', '电表'), ('water', '水表')], string='仪表类型')
+ epitope_values = fields.Char(string='表位置')
+ meter_type = fields.Selection([('electric', '电表'), ('water', '水表'), ('smart', '智能电表')], string='仪表类型')
other = fields.Char(string='其他')
meter_date = fields.Date(string='任务日期')
last_dosage = fields.Float(string='上次用量')
@@ -33,7 +34,15 @@ class PropertyMeterArchives(models.Model):
receivables = fields.Float(string="应收", compute='_compute_receivables', store=True)
last_reading_time = fields.Date(string="最后读数时间")
standards_id = fields.Many2one('yuthon.electricity.setting', string='标准')
- yuthon_property_id = fields.Many2one('yuthon.property', string='关联物业信息')
+ yuthon_property_ids = fields.Many2many('yuthon.property', string='物业信息')
+
+ @api.depends('yuthon_property_ids')
+ def _compute_lessee_id(self):
+ for record in self:
+ if record.yuthon_property_ids:
+ record.lessee_id = record.yuthon_property_ids[0].lessee_id
+ else:
+ record.lessee_id = False
@api.depends('line_loss', 'current_meter', 'unit')
def _compute_receivables(self):
@@ -52,6 +61,14 @@ class PropertyMeterArchives(models.Model):
record.write({'maximum_count': 99999,
})
+ def open_record(self):
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'property.meter.archives',
+ 'res_id': self.id,
+ 'view_mode': 'form',
+ }
+
@api.depends('lessee_id')
def _compute_archives_state(self):
for record in self:
@@ -95,19 +112,24 @@ class PropertyMeterArchives(models.Model):
if record.current_read > record.last_read:
record.current_meter = record.current_read - record.last_read
else:
- record.current_meter = record.maximum_count - record.last_read + record.current_read
+ if record.maximum_count > 0:
+ max_value = 10 ** record.maximum_count - 1
+ record.current_meter = max_value - record.last_read + record.current_read
+ else:
+ record.current_meter = 0
- @api.onchange('yuthon_property_id')
+ @api.onchange('yuthon_property_ids')
def _onchange_property(self):
- if self.yuthon_property_id:
+ if self.yuthon_property_ids:
meter_list = []
- property_id = self.env['yuthon.property'].search([('id', '=', self.yuthon_property_id.id)])
- property_id.is_electricity_meter = True
- meter_list.append((0, 0, {
- 'yuthon_property_id':property_id.id,
- 'property_meter_archives_id':self.id.origin,
- }))
- property_id.energy_meter_ids = meter_list
+ for yuthon_property_id in self.yuthon_property_ids:
+ property_id = self.env['yuthon.property'].search([('id', '=', yuthon_property_id.origin.id)])
+ property_id.is_meter = True
+ meter_list.append((0, 0, {
+ 'yuthon_property_id': property_id.id,
+ 'property_meter_water_id': self.id.origin,
+ }))
+ property_id.water_meter_ids = meter_list
def action_property_meter_wizard(self):
return {
diff --git a/yuthon_property/models/property_meter_water.py b/yuthon_property/models/property_meter_water.py
index 37f1f80c6..22b47945d 100644
--- a/yuthon_property/models/property_meter_water.py
+++ b/yuthon_property/models/property_meter_water.py
@@ -7,15 +7,16 @@ class PropertyMeterWater(models.Model):
_description = '水表库'
name = fields.Char(string='仪表编号')
- unit_number = fields.Char(string='单元编号')
+ unit_number = fields.Char(string='单元编号/用途')
installation_location = fields.Char(string='安装位置')
is_state = fields.Boolean(string='是否启用')
type = fields.Selection([('public', '公共'), ('fees', '收费')], string='类型')
gauge_magnification = fields.Integer(string='仪表倍率', default=1)
- maximum_count = fields.Integer(string='标准用量', default=99999)
- lessee_id = fields.Many2one(related='yuthon_property_id.lessee_id', string='承租方', store=True)
+ maximum_count = fields.Integer(string='最大位数')
+ lessee_id = fields.Many2one('property.tenant.information', store=True, compute='_compute_lessee_id', string="承租方")
employee_ids = fields.Many2many('hr.employee', string='抄表人')
meter_type = fields.Selection([('electric', '电表'), ('water', '水表')], string='仪表类型')
+ epitope_values = fields.Char(string='表位置')
other = fields.Char(string='其他')
meter_date = fields.Date(string='任务日期')
last_dosage = fields.Float(string='上次用量')
@@ -32,7 +33,23 @@ class PropertyMeterWater(models.Model):
last_reading_time = fields.Date(string="最后读数时间")
water_state = fields.Selection([('stop', '停用'), ('use', '使用'), ('rent', '出租')], string='状态', compute='_compute_water_state', store=True)
standards_id = fields.Many2one('yuthon.electricity.setting', string='标准')
- yuthon_property_id = fields.Many2one('yuthon.property', string='关联物业信息')
+ yuthon_property_ids = fields.Many2many('yuthon.property', string='物业信息')
+
+ def open_record(self):
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'property.meter.water',
+ 'res_id': self.id,
+ 'view_mode': 'form',
+ }
+
+ @api.depends('yuthon_property_ids')
+ def _compute_lessee_id(self):
+ for record in self:
+ if record.yuthon_property_ids:
+ record.lessee_id = record.yuthon_property_ids[0].lessee_id
+ else:
+ record.lessee_id = False
@api.depends('line_loss', 'current_meter', 'unit')
def _compute_receivables(self):
@@ -95,26 +112,31 @@ class PropertyMeterWater(models.Model):
if record.current_read > record.last_read:
record.current_meter = record.current_read - record.last_read
else:
- record.current_meter = record.maximum_count - record.last_read + record.current_read
+ if record.maximum_count > 0:
+ max_value = 10 ** record.maximum_count - 1
+ record.current_meter = max_value - record.last_read + record.current_read
+ else:
+ record.current_meter = 0
- @api.onchange('yuthon_property_id')
+ @api.onchange('yuthon_property_ids')
def _onchange_property(self):
- if self.yuthon_property_id:
+ if self.yuthon_property_ids:
meter_list = []
- property_id = self.env['yuthon.property'].search([('id', '=', self.yuthon_property_id.id)])
- property_id.is_meter = True
- meter_list.append((0, 0, {
- 'yuthon_property_id': property_id.id,
- 'property_meter_water_id': self.id.origin,
- }))
- property_id.water_meter_ids = meter_list
+ for yuthon_property_id in self.yuthon_property_ids:
+ property_id = self.env['yuthon.property'].search([('id', '=', yuthon_property_id.id)])
+ property_id.is_meter = True
+ meter_list.append((0, 0, {
+ 'yuthon_property_id': property_id.id,
+ 'property_meter_water_id': self.id.origin,
+ }))
+ property_id.water_meter_ids = meter_list
def action_property_water_wizard(self):
return {
"type": "ir.actions.act_window",
- "name": "批量导入物业电表",
+ "name": "批量导入物业水表",
"res_model": "property.water.wizard",
"view_mode": "form",
"target": "new",
diff --git a/yuthon_property/views/property_meter_archives_views.xml b/yuthon_property/views/property_meter_archives_views.xml
index c9d3d4d4f..00af49e11 100644
--- a/yuthon_property/views/property_meter_archives_views.xml
+++ b/yuthon_property/views/property_meter_archives_views.xml
@@ -10,9 +10,11 @@
+
+
-
+
@@ -30,14 +32,66 @@
+
+
+ property.meter.archives.form
+ property.meter.archives
+
+
+
property.meter.archives.tree
property.meter.archives
+
-
+
+
@@ -60,7 +114,7 @@
property.meter.archives
-
+
@@ -68,17 +122,19 @@
电表库
property.meter.archives
- tree,search
+ tree,form,search
+ (0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree')}),
+ (0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_archives_form')})]"/>
电表任务
property.meter.archives
- tree,search
+ tree,form,search
[('is_push', '=', True)]
+ (0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree1')}),
+ (0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_archives_form')})]"/>
导入电表
diff --git a/yuthon_property/views/property_meter_water_views.xml b/yuthon_property/views/property_meter_water_views.xml
index b96a07c9b..c74cc1936 100644
--- a/yuthon_property/views/property_meter_water_views.xml
+++ b/yuthon_property/views/property_meter_water_views.xml
@@ -11,9 +11,11 @@
+
+
-
+
@@ -32,14 +34,64 @@
+
+
+ property.meter.water.form
+ property.meter.water
+
+
+
property.meter.water.tree
property.meter.water
+
-
+
@@ -62,7 +114,7 @@
property.meter.water
-
+
@@ -70,23 +122,26 @@
水表库
property.meter.water
- tree,search
+ tree,form,search
+ (0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree')}),
+ (0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_water_form')})]"/>
水表任务
property.meter.water
- tree,search
+ tree,form,search
[('is_push', '=', True)]
+ (0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree1')}),
+ (0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_water_form')})]"/>
导入水表
code
+ action = model.action_property_water_wizard()
更新状态
diff --git a/yuthon_property/wizard/property_meter_wizard.py b/yuthon_property/wizard/property_meter_wizard.py
index 4cff40255..6b7ae2981 100644
--- a/yuthon_property/wizard/property_meter_wizard.py
+++ b/yuthon_property/wizard/property_meter_wizard.py
@@ -26,36 +26,60 @@ class PropertyMeterWizard(models.Model):
row = sh.row(rx)
self.property_meter_data(row)
- def property_meter_data(self,row):
- name = row[0].value
+ def property_meter_data(self, row):
+ epitope_values = row[0].value
+ name = row[3].value
+ type_dict = {'水表': 'water', '电表': 'electric', '电表(电子表)': 'smart', '': ''}
+ meter_type = type_dict.get(row[4].value)
unit_number = row[1].value
installation_location = row[2].value
- if row[4].value:
- gauge_magnification = float(row[4].value)
+ if row[5].value:
+ gauge_magnification = float(row[5].value)
else:
gauge_magnification = None
- property_id = self.env['yuthon.property'].search([('name', '=', row[8].value)])
- meter_list = []
- type_dict = {'公共':'public', '收费':'fees', '':''}
- type = type_dict.get(row[6].value)
- state_dict = {'启用': 1, '停用': 0, '': ''}
- is_state = state_dict.get(row[7].value)
+ maximum_count = float(row[6].value) if row[6].value else None
+ type_dict = {'公共': 'public', '收费': 'fees', '': ''}
+ type = type_dict.get(row[7].value)
+ property_id = None
+ if row[2].value:
+ property_search = self.env['yuthon.property'].search([('name', '=', row[2].value)])
+ if property_search:
+ property_id = property_search[0]
meter_data = {
'name': name,
+ 'meter_type': meter_type,
+ 'epitope_values': epitope_values,
+ 'maximum_count': maximum_count,
'unit_number': unit_number,
'installation_location': installation_location,
'type': type,
'gauge_magnification': gauge_magnification,
- 'yuthon_property_id':property_id.id if property_id else None,
- 'is_state':is_state
}
- meter_id = self.env['property.meter.archives'].create(meter_data)
if property_id:
- meter_list.append((0, 0, {
- 'property_meter_archives_id': meter_id.id,
- }))
- property_id.write({
- 'is_electricity_meter' : True,
- })
- property_id.energy_meter_ids = [(5, 0, 0)]
- property_id.energy_meter_ids = meter_list
+ meter_data['yuthon_property_ids'] = [(4, property_id.id)]
+
+ meter_id = self.env['property.meter.archives'].search([('name', '=', row[3].value)], limit=1)
+ if meter_id:
+ meter_id.write(meter_data)
+ else:
+ meter_id = self.env['property.meter.archives'].create(meter_data)
+
+ if property_id:
+ # 检查物业是否已关联该电表
+ existing_meter = next((
+ meter for meter in property_id.energy_meter_ids
+ if meter.property_meter_archives_id.id == meter_id.id
+ ), None)
+
+ if not existing_meter:
+ existing_entries = [(4, meter.id) for meter in property_id.energy_meter_ids]
+ new_meter_entry = (0, 0, {
+ 'property_meter_archives_id': meter_id.id,
+ })
+ updated_meters = existing_entries + [new_meter_entry]
+ property_id.write({
+ 'is_electricity_meter': True,
+ 'energy_meter_ids': updated_meters
+ })
+
+
diff --git a/yuthon_property/wizard/property_water_wizard.py b/yuthon_property/wizard/property_water_wizard.py
index 94b264313..4149489e3 100644
--- a/yuthon_property/wizard/property_water_wizard.py
+++ b/yuthon_property/wizard/property_water_wizard.py
@@ -27,35 +27,57 @@ class PropertyWaterWizard(models.Model):
self.property_water_data(row)
def property_water_data(self,row):
- name = row[0].value
+ epitope_values = row[0].value
+ name = row[3].value
+ type_dict = {'水表': 'water', '电表': 'electric', '电表(电子表)': 'smart', '': ''}
+ meter_type = type_dict.get(row[4].value)
unit_number = row[1].value
installation_location = row[2].value
- if row[4].value:
- gauge_magnification = float(row[4].value)
+ if row[5].value:
+ gauge_magnification = float(row[5].value)
else:
gauge_magnification = None
- property_id = self.env['yuthon.property'].search([('name', '=', row[8].value)])
- water_list = []
- type_dict = {'公共':'public', '收费':'fees', '':''}
- type = type_dict.get(row[6].value)
- state_dict = {'启用':1, '停用':0, '':''}
- is_state = state_dict.get(row[7].value)
- water_data = {
+ maximum_count = float(row[6].value) if row[6].value else None
+ type_dict = {'公共': 'public', '收费': 'fees', '': ''}
+ type = type_dict.get(row[7].value)
+ property_id = None
+ if row[2].value:
+ property_search = self.env['yuthon.property'].search([('name', '=', row[2].value)])
+ if property_search:
+ property_id = property_search[0]
+ meter_data = {
'name': name,
+ 'meter_type': meter_type,
+ 'epitope_values': epitope_values,
+ 'maximum_count': maximum_count,
'unit_number': unit_number,
'installation_location': installation_location,
'type': type,
'gauge_magnification': gauge_magnification,
- 'yuthon_property_id':property_id.id if property_id else None,
- 'is_state':is_state
}
- water_id = self.env['property.meter.water'].create(water_data)
if property_id:
- water_list.append((0, 0, {
- 'property_meter_water_id': water_id.id,
- }))
- property_id.write({
- 'is_meter' : True,
- })
- property_id.water_meter_ids = [(5, 0, 0)]
- property_id.water_meter_ids = water_list
+ meter_data['yuthon_property_ids'] = [(4, property_id.id)]
+
+ meter_id = self.env['property.meter.water'].search([('name', '=', row[3].value)], limit=1)
+ if meter_id:
+ meter_id.write(meter_data)
+ else:
+ meter_id = self.env['property.meter.water'].create(meter_data)
+
+ if property_id:
+ # 检查物业是否已关联该水表
+ existing_meter = next((
+ meter for meter in property_id.water_meter_ids
+ if meter.property_meter_water_id.id == meter_id.id
+ ), None)
+ if not existing_meter:
+ existing_entries = [(4, meter.id) for meter in property_id.water_meter_ids]
+ new_meter_entry = (0, 0, {
+ 'property_meter_water_id': meter_id.id,
+ })
+ updated_meters = existing_entries + [new_meter_entry]
+ property_id.write({
+ 'is_meter': True,
+ 'water_meter_ids': updated_meters
+ })
+
From bd879d343c8003791946e60632e7a5168ac4cc81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?=
<9664676+pengyuthon@user.noreply.gitee.com>
Date: Sat, 6 Sep 2025 19:03:37 +0800
Subject: [PATCH 3/4] =?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/inherit_yuthon_property.py | 2 +-
.../wizard/property_reconciliation_wizard.py | 34 ++++++++++---------
2 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/yuthon_property_approval/models/inherit_yuthon_property.py b/yuthon_property_approval/models/inherit_yuthon_property.py
index 10c71aa98..705664e32 100644
--- a/yuthon_property_approval/models/inherit_yuthon_property.py
+++ b/yuthon_property_approval/models/inherit_yuthon_property.py
@@ -125,7 +125,7 @@ class PropertyBankStatements(models.Model):
('lessee_id', '=', tenant_id),
('contract_id', 'in', rec.contract_ids.ids),
('property_id', 'in', rec.property_ids.ids),
- ('original_amount', '=', rec.original_amount),
+ ('payout_date', '!=', None),
('management_type_id', '!=', penalty_type.id),
])
if accounts:
diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py
index cfdbcc66c..ac8ba0b63 100644
--- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py
+++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py
@@ -257,24 +257,26 @@ class PropertyReconciliationWizard(models.Model):
('contract_id', '=', contract_id.id),
('property_id', '=', property_id),
('submitted', '!=', 0),
+ ('payout_date', '!=', None),
('due_date', '=like', f'{last_month_str}%')
])
if unpaid_rent_recs:
- first_day = (unpaid_rent_recs.due_date_end + timedelta(days=1))
- last_month_rent = unpaid_rent_recs.submitted
- penalty_amount = penalty_days * round(last_month_rent * float(contract.percentage_liquidated_damages.name), 2
- ) if contract else 0.0
- if penalty_amount > 0:
- missing_penalty_lines.append((0, 0, {
- 'property_id': property_rec.id if property_rec else None,
- 'contract_id': contract.id if contract else None,
- 'rent_month': 0,
- 'due_date': fields.Date.to_string(first_day),
- 'rent': penalty_amount,
- 'amount': 0,
- 'collection_type': 'penalty',
- 'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days}天)',
- }))
+ first_day = (unpaid_rent_recs.payout_date + timedelta(days=1))
+ last_month_rent = unpaid_rent_recs.submitted
+ penalty_days = self.actual_date - first_day
+ penalty_amount = penalty_days.days * round(last_month_rent * float(contract.percentage_liquidated_damages.name), 2
+ ) if contract else 0.0
+ if penalty_amount > 0:
+ missing_penalty_lines.append((0, 0, {
+ 'property_id': property_rec.id if property_rec else None,
+ 'contract_id': contract.id if contract else None,
+ 'rent_month': 0,
+ 'due_date': fields.Date.to_string(first_day),
+ 'rent': penalty_amount,
+ 'amount': 0,
+ 'collection_type': 'penalty',
+ 'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days.days}天)',
+ }))
if missing_penalty_lines:
@@ -394,7 +396,7 @@ class PropertyReconciliationWizard(models.Model):
receivable.write({
'is_reconciliation': is_reconciliation,
'delivered1': line.amount,
- 'payout_date': rec.actual_date if line.rent == line.amount else None,
+ 'payout_date': rec.actual_date,
'remark': line.remake,
'reconciliation_time': fields.Datetime.now(),
'receivable_log': f'当前登录人{rec.env.user.name}于{fields.Datetime.now()}对户名为{rec.bank_id.name_account}进行对账操作,对账应收金额为{rec.receivable_total},实际金额为{rec.actual_total}'
From b30430f0feda38a47cbd1c3a6763ea07ac4c32be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?=
<9664676+pengyuthon@user.noreply.gitee.com>
Date: Sat, 6 Sep 2025 19:23:44 +0800
Subject: [PATCH 4/4] =?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
---
.../wizard/property_reconciliation_wizard.py | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py
index ac8ba0b63..c1c3c96f5 100644
--- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py
+++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py
@@ -264,7 +264,7 @@ class PropertyReconciliationWizard(models.Model):
first_day = (unpaid_rent_recs.payout_date + timedelta(days=1))
last_month_rent = unpaid_rent_recs.submitted
penalty_days = self.actual_date - first_day
- penalty_amount = penalty_days.days * round(last_month_rent * float(contract.percentage_liquidated_damages.name), 2
+ penalty_amount = (penalty_days.days +1) * round(last_month_rent * float(contract.percentage_liquidated_damages.name), 2
) if contract else 0.0
if penalty_amount > 0:
missing_penalty_lines.append((0, 0, {
@@ -275,7 +275,7 @@ class PropertyReconciliationWizard(models.Model):
'rent': penalty_amount,
'amount': 0,
'collection_type': 'penalty',
- 'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days.days}天)',
+ 'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days.days + 1}天)',
}))
@@ -442,13 +442,7 @@ class PropertyReconciliationWizard(models.Model):
'remark': line.remake,
'reconciliation_time': fields.Datetime.now(),
})
- rec.bank_id.write({
- 'create_receivable_ids': [(4, receivable_id.id)]
- })
- # if receivable_id:
- # rec.bank_id.write({
- # 'create_receivable_ids': [(4, receivable_id.id)]
- # })
+
else:
existing_record.write({
'delivered1': existing_record.delivered1 + line.amount,
@@ -460,9 +454,6 @@ class PropertyReconciliationWizard(models.Model):
'is_write': True if line.amount != 0 else None,
'reconciliation_time': fields.Datetime.now(),
})
- # rec.bank_id.write({
- # 'write_receivable_ids': [(4, existing_record.id)]
- # })
# 计算已对账和未对账金额
rec.amount_been_reconciled += line.amount