!5845 添加功能

Merge pull request !5845 from 李鹏宇/dev_zhou
This commit is contained in:
李鹏宇
2025-10-21 09:22:40 +00:00
committed by Gitee
7 changed files with 148 additions and 15 deletions
@@ -205,6 +205,7 @@ class PropertyBankStatements(models.Model):
if accounts:
rec.reconciliation_time = accounts[0].reconciliation_time
class ReceivableModifyLine(models.Model):
_name = 'receivable.modify.line'
_description = '修改应收账单明细'
@@ -212,7 +213,8 @@ class ReceivableModifyLine(models.Model):
bank_id = fields.Many2one('property.bank.statements', string='关联银行对账单')
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
is_write = fields.Boolean()
is_penalty = fields.Boolean()
reconciliation_date = fields.Date(string='对账日期')
is_penalty = fields.Boolean(string='是否为违约金')
amount = fields.Float(string='修改金额')
@@ -4,7 +4,9 @@
<field name="name">property.bank.statements.tree</field>
<field name="model">property.bank.statements</field>
<field name="arch" type="xml">
<tree string="对账" editable="bottom" js_class="custom_statements_list_button" edit="false" delete="false" limit="15">
<tree string="对账" editable="bottom" js_class="custom_statements_list_button" edit="false" delete="false" limit="15"
decoration-warning="bank_state == 'registered'"
>
<field name="your_own_unit"/>
<field name="start_time" />
<field name="name_account" />
@@ -21,7 +23,9 @@
<field name="name">property.bank.statements.tree</field>
<field name="model">property.bank.statements</field>
<field name="arch" type="xml">
<tree string="银行流水" editable="bottom" js_class="custom_bank_statements_button" delete="false" default_order="start_time desc" limit="15">
<tree string="银行流水" editable="bottom" js_class="custom_bank_statements_button" delete="false" default_order="start_time desc" limit="15"
decoration-warning="bank_state == 'registered'"
>
<field name="your_own_account" optional="hide"/>
<field name="your_own_unit" optional="hide"/>
<header>
@@ -54,6 +58,8 @@
<field name="receivable_modify_ids">
<tree editable="bottom">
<field name="bank_id" column_invisible="1"/>
<field name="reconciliation_date"/>
<field name="is_penalty"/>
<field name="receivable_id"/>
<field name="amount"/>
</tree>
@@ -88,7 +94,7 @@
<record id="view_property_bank_statements_action1" model="ir.actions.act_window">
<field name="name">银行流水</field>
<field name="res_model">property.bank.statements</field>
<field name="view_mode">tree,search</field>
<field name="view_mode">tree,form,search</field>
<field name="domain">[('is_company', '=', True)]</field>
<field name="context">{'group_by': 'bank_state'}</field>
<field name="view_ids" eval="[(5, 0, 0),
@@ -598,12 +598,26 @@ class PropertyAccountsReceivable(models.Model):
rec.is_rent = False
def penalty_wizard(self):
self.ensure_one()
penalty_lines = []
penalty_lines.append((0, 0, {
'date': self.due_date,
'remake': f'开始计算违约金,应收金额: ¥{self.original_amount}',
'days_overdue': 0,
'remaining_principal': self.original_amount,
'penalty': 0,
}))
return {
'name': '合同违约金明细',
'type': 'ir.actions.act_window',
'res_model': 'penalty.line.wizard',
'view_mode': 'form',
'target': 'new',
'name': '合同违约金明细',
'type': 'ir.actions.act_window',
'res_model': 'penalty.line.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_receivable_id': self.id,
'default_penalty_line_ids': penalty_lines,
'default_rent_collected': self.submitted,
}
}
@api.depends('contract_id')
@@ -627,14 +627,13 @@
<button name="other" type="object" class="oe_highlight" string="其他"/>
</header>
</xpath>
<xpath expr="//field[@name='property_ids']" position="before">
<field name="contract_ids" string="合同编号" widget="many2many_tags" readonly="1" domain="[('lessor_id.name', '=', your_own_unit)]"/>
<field name="property_name" string="物业信息"/>
<field name="admin_id"/>
</xpath>
<xpath expr="//field[@name='bank_state']" position="after">
<button name="button_reconciliation" type="object" string="对账" class="btn btn-primary" invisible="bank_state != 'not_reconciled'"/>
<button name="button_reconciliation" type="object" string="对账" class="btn btn-primary" invisible="bank_state == 'reconciled'"/>
<button name="binding_information" type="object" string="绑定信息" class="btn btn-primary"/>
</xpath>
</field>
@@ -2,10 +2,11 @@ from email.policy import default
from odoo import models, fields, api, _, SUPERUSER_ID
from odoo.exceptions import ValidationError, UserError
import calendar
import xlrd
import base64
import datetime
from datetime import datetime
from datetime import datetime, timedelta, date
import pandas as pd
@@ -14,6 +15,7 @@ class PenaltyLineWizard(models.Model):
_description = '合同违约金明细'
rent_collected = fields.Float(string='未收租金')
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
days_overdue = fields.Integer(string='逾期天数')
total_penalty = fields.Float(string='违约金总额')
collected_penalty = fields.Float(string='已收违约金')
@@ -25,6 +27,114 @@ class PenaltyLineWizard(models.Model):
penalty_line_ids = fields.One2many('penalty.compute.line.wizard', 'penalty_id', string='违约金明细')
pay_line_ids = fields.One2many('penalty.pay.line', 'penalty_id', string='违约金支付明细')
@api.onchange('receivable_id')
def _onchange_penalty_line_ids(self):
receivable = self.receivable_id
due_date = receivable.due_date
if not due_date:
return
rent_pay_count = 0 # 租金支付次数
penalty_pay_count = 0 # 违约金支付次数
# 2. 计算关键日期:应收月、应收月最后一天、次月1号(违约金起算日)
due_year = due_date.year
due_month = due_date.month
# 应收月最后一天(如2025-10月最后一天是2025-10-31)
last_day_of_due_month = date(due_year, due_month, calendar.monthrange(due_year, due_month)[1])
# 次月1号(违约金开始计算的日期)
next_month = due_month + 1 if due_month < 12 else 1
next_year = due_year if due_month < 12 else due_year + 1
penalty_start_date = date(next_year, next_month, 1)
modify_lines = self.env['receivable.modify.line'].search([
('receivable_id', '=', receivable.id),
('reconciliation_date', '!=', False),
('is_penalty', '=', False)
], order='reconciliation_date asc')
initial_line = self.penalty_line_ids[0]
prev_date = initial_line.date # 初始日期
prev_principal = initial_line.remaining_principal # 初始剩余本金
new_penalty_lines = []
new_pay_lines = []
rent_type = self.env['yuthon.management.type'].search([('name', '=', '租金')], limit=1)
penalty_type = self.env['yuthon.management.type'].search([('name', '=', '违约金')], limit=1)
for pay_line in modify_lines:
pay_amount = pay_line.amount # 付款金额
pay_date = pay_line.reconciliation_date # 付款日期
is_penalty = pay_line.is_penalty # 是否是违约金
if is_penalty:
penalty_pay_count += 1
current_idx = penalty_pay_count
type_name = "违约金"
else:
rent_pay_count += 1
current_idx = rent_pay_count
type_name = "租金"
if prev_date <= last_day_of_due_month:
days_overdue = 0
current_penalty = 0.0
else:
days_diff = (pay_date - penalty_start_date).days - 1
days_overdue = max(0, days_diff)
current_penalty = prev_principal * days_overdue * 0.001 if days_overdue > 0 else 0.0 # 违约金
# 计算剩余本金
remaining_principal = max(0, prev_principal - pay_amount)
new_penalty_lines.append((0, 0, {
'date': pay_date,
'days_overdue': days_overdue,
'remaining_principal': remaining_principal,
'penalty': round(current_penalty, 2),
}))
prev_date = pay_date
prev_principal = remaining_principal
management_type = penalty_type if is_penalty else rent_type
pay_remake = f'第{current_idx}次支付{type_name},金额:¥{pay_amount}'
new_pay_lines.append((0, 0, {
'date': pay_date,
'amount': pay_amount,
'management_type_id': management_type.id if management_type else False,
'remake': pay_remake,
}))
if new_penalty_lines:
last_line_dict = new_penalty_lines[-1][2]
last_line_principal = last_line_dict.get('remaining_principal', 0)
else:
last_line_principal = 0
if last_line_principal > 0:
current_date = date.today()
last_pay_date = last_line_dict.get('date')
# 计算逾期天数:当前日期 - 最后一次支付日期 - 1(避免计算当天)
if last_pay_date and current_date > last_pay_date:
days_overdue = (current_date - last_pay_date).days - 1
days_overdue = max(0, days_overdue) # 确保非负
current_penalty = last_line_principal * days_overdue * 0.001
# 生成补充明细行
new_penalty_lines.append((0, 0, {
'date': current_date,
'days_overdue': days_overdue,
'remaining_principal': last_line_principal,
'remake': '当前未支付金额违约金',
'penalty': round(current_penalty, 2),
}))
self.penalty_line_ids = new_penalty_lines
self.pay_line_ids = new_pay_lines
class PenaltyComputeLineWizard(models.Model):
_name = 'penalty.compute.line.wizard'
@@ -36,7 +146,7 @@ class PenaltyComputeLineWizard(models.Model):
remake = fields.Text(string='说明')
days_overdue = fields.Integer(string='逾期天数')
remaining_principal = fields.Float(string='剩余本金')
calculation_formula = fields.Text(string='计算公式')
penalty = fields.Float(string='违约金')
class PenaltyPayLine(models.Model):
@@ -8,6 +8,7 @@
<sheet>
<group col="2">
<group>
<field name="receivable_id" column_invisible="1"/>
<field name="rent_collected"/>
<field name="days_overdue"/>
<field name="total_penalty"/>
@@ -33,7 +34,7 @@
<field name="remake"/>
<field name="days_overdue"/>
<field name="remaining_principal"/>
<field name="calculation_formula"/>
<field name="penalty"/>
</tree>
</field>
</page>
@@ -28,7 +28,6 @@ class PropertyReconciliationWizard(models.Model):
@api.onchange('reconciliation_line_ids', 'actual_total')
def _onchange_amount_reconciled(self):
for rec in self:
rec.amount_been_reconciled = sum(line.amount for line in rec.reconciliation_line_ids)
rec.amount_reconciled = rec.actual_total - rec.amount_been_reconciled
@api.depends('reconciliation_line_ids')
@@ -409,6 +408,7 @@ class PropertyReconciliationWizard(models.Model):
})
self.env['receivable.modify.line'].create({
'bank_id': rec.bank_id.id,
'reconciliation_date': rec.actual_date if line.amount != 0 else None,
'receivable_id': receivable.id,
'is_write': True,
'is_penalty': False,
@@ -465,6 +465,7 @@ class PropertyReconciliationWizard(models.Model):
'remark': line.remake,
'bank_id': rec.bank_id.id if line.amount != 0 else None,
'is_write': True if line.amount != 0 else None,
'reconciliation_date': rec.actual_date if line.amount != 0 else None,
'reconciliation_time': fields.Datetime.now(),
})
self.env['receivable.modify.line'].create({