diff --git a/soong_workflow/models/soong_workflow_node.py b/soong_workflow/models/soong_workflow_node.py
index ffc3167ba..a20bd4f9c 100644
--- a/soong_workflow/models/soong_workflow_node.py
+++ b/soong_workflow/models/soong_workflow_node.py
@@ -161,7 +161,7 @@ class SoongWorkflowNode(models.Model):
"name": _("结束"),
"node_id": record.id,
"sequence": sequence + 1,
- "action_type": "end",
+ "action_type": "code",
"btn_hide": False,
"has_comment": True,
"review_type": "end",
diff --git a/yuthon_property_approval/models/inherit_yuthon_property.py b/yuthon_property_approval/models/inherit_yuthon_property.py
index aea4651c5..da1e624ce 100644
--- a/yuthon_property_approval/models/inherit_yuthon_property.py
+++ b/yuthon_property_approval/models/inherit_yuthon_property.py
@@ -758,6 +758,46 @@ class PropertyAccountsReceivable(models.Model):
record.discount_amount = record.submitted
record.discount_date = datetime(2026, 1, 1)
+ def action_clear_legacy_unpaid(self):
+ """一次性历史欠费清理(应收日期 < 2026-01-01 且 未对账,不限收款状态):
+ 1) 应收类型 = 租金 → 关闭「生成违约金」(is_liquidated_damages = False)
+ 2) 应收类型 = 违约金 → 作废 (is_invalid = True,作废日期 = 当天)
+
+ 作废后汇总视图 property.accounts.receivable.summary 与「未收明细」跳转都会自动排除。
+ """
+ cutoff = date(2026, 1, 1)
+ today = fields.Date.context_today(self)
+ base_domain = [
+ ('due_date', '<', cutoff),
+ ('is_reconciliation', '=', False),
+ ]
+
+ # 1) 租金:关闭生成违约金(只处理当前还开着的,避免无谓写入)
+ rent_records = self.search(base_domain + [
+ ('management_type_id.name', '=', '租金'),
+ ('is_liquidated_damages', '=', True),
+ ])
+ rent_records.write({'is_liquidated_damages': False})
+
+ # 2) 违约金:作废 + 填当天作废日期(已作废的不动,保留原作废日期)
+ penalty_records = self.search(base_domain + [
+ ('management_type_id.name', '=', '违约金'),
+ ('is_invalid', '=', False),
+ ])
+ penalty_records.write({'is_invalid': True, 'void_date': today})
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'type': 'success',
+ 'title': '清理完成',
+ 'message': '租金关闭「生成违约金」%s 条;违约金作废 %s 条。'
+ % (len(rent_records), len(penalty_records)),
+ 'sticky': False,
+ },
+ }
+
def verification_wizard(self):
receivable_lines = self.env['property.accounts.receivable'].search([
('contract_id', '=', self.contract_id.id),
diff --git a/yuthon_property_approval/models/property_accounts_receivable_summary.py b/yuthon_property_approval/models/property_accounts_receivable_summary.py
index f081ef17a..09c9094c8 100644
--- a/yuthon_property_approval/models/property_accounts_receivable_summary.py
+++ b/yuthon_property_approval/models/property_accounts_receivable_summary.py
@@ -54,13 +54,14 @@ class PropertyAccountsReceivableSummary(models.Model):
WHERE r.contract_id IS NOT NULL
AND r.deposit_state IN ('unpaid', 'no_paid')
AND COALESCE(r.is_reconciliation, false) = false
+ AND COALESCE(r.is_invalid, false) = false
GROUP BY r.contract_id, c.use_address, c.admin_id
HAVING COALESCE(SUM(r.submitted), 0) > 0
)
""" % {'table': self._table, 'main_cols': main_cols, 'excluded': excluded})
def action_open_unpaid_detail(self):
- """未收明细:打开该合同下 未收/收部分 且 未对账 的应收账单,
+ """未收明细:打开该合同下 未收/收部分 且 未对账 且 未作废 的应收账单,
复用 view_property_accounts_receivable_tree,不做分组,按应收类型倒序"""
self.ensure_one()
tree_view = self.env.ref('yuthon_property.view_property_accounts_receivable_tree')
@@ -77,6 +78,7 @@ class PropertyAccountsReceivableSummary(models.Model):
('contract_id', '=', self.contract_id.id),
('deposit_state', 'in', ['unpaid', 'no_paid']),
('is_reconciliation', '=', False),
+ ('is_invalid', '=', False),
],
'context': {
'order': 'management_type_id desc',
diff --git a/yuthon_property_approval/views/inherit_yuthon_property_views.xml b/yuthon_property_approval/views/inherit_yuthon_property_views.xml
index 3424f7610..74c9106ca 100644
--- a/yuthon_property_approval/views/inherit_yuthon_property_views.xml
+++ b/yuthon_property_approval/views/inherit_yuthon_property_views.xml
@@ -828,6 +828,14 @@
code
action = model.contract_context()
+
+
+ 清理2026年前旧账
+
+
+ code
+ action = model.action_clear_legacy_unpaid()
+
property.penalty.list.tree
diff --git a/yuthon_work_brand/views/receivable_type_menus.xml b/yuthon_work_brand/views/receivable_type_menus.xml
index f8814f4bc..a62907493 100644
--- a/yuthon_work_brand/views/receivable_type_menus.xml
+++ b/yuthon_work_brand/views/receivable_type_menus.xml
@@ -127,6 +127,9 @@
+
+
+