diff --git a/yuthon_property_approval/models/property_management.py b/yuthon_property_approval/models/property_management.py
index 030206dcb..bcd64c51e 100644
--- a/yuthon_property_approval/models/property_management.py
+++ b/yuthon_property_approval/models/property_management.py
@@ -5,6 +5,7 @@ from odoo import models, fields, api, exceptions, _
from datetime import timedelta
from dateutil.relativedelta import relativedelta
import calendar
+import re
from datetime import datetime, date
@@ -44,6 +45,7 @@ class PropertyManagement(models.Model):
name_title = fields.Char(default=_get_default_name_title)
document_ids = fields.Many2many('ir.attachment', string="附件")
+ is_generate_new_contract = fields.Boolean(string='是否生成新合同', default=False)
self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])])
@api.onchange('precinct_id')
@@ -80,8 +82,132 @@ class PropertyManagement(models.Model):
'tenure_type': line.tenure_type
})]
+ def _generate_and_resolve_contracts(self):
+ """
+ 如果勾选"生成新合同",为每个物业对应的合同创建新合同副本。
+ 返回 {old_contract_id: new_contract_id} 映射,没勾选返回空字典
+ """
+ new_contract_map = {}
+ if not self.is_generate_new_contract:
+ return new_contract_map
+
+ processed = set()
+ for manage_line in self.management_line2_ids:
+ if not manage_line.property_id:
+ continue
+ lease_line = self.env['property.lease.contract.line'].search([
+ ('address_id', '=', manage_line.property_id.id),
+ ('lease_id', '!=', False),
+ ], limit=1)
+ if not lease_line:
+ continue
+ old_contract = lease_line.lease_id
+ if old_contract.id in processed:
+ continue
+ processed.add(old_contract.id)
+
+ # 编号尾部数字+1:租(2023)37号-2 → 租(2023)37号-3
+ base_code = old_contract.contract_code or ''
+ match = re.match(r'^(.+)-(\d+)$', base_code)
+ if match:
+ prefix = match.group(1)
+ counter = int(match.group(2)) + 1
+ else:
+ prefix = base_code
+ counter = 1
+ new_code = f"{prefix}-{counter}"
+ while self.env['property.lease.contract'].search(
+ [('contract_code', '=', new_code)], limit=1):
+ counter += 1
+ new_code = f"{prefix}-{counter}"
+
+ new_contract = old_contract.copy({
+ 'contract_code': new_code,
+ 'contract_source_id': old_contract.id,
+ 'condition': 'new', # 进入合同审批列表
+ })
+ new_contract_map[old_contract.id] = new_contract.id
+
+ # 手动复制物业明细行(copy 未复制 One2many)
+ for old_line in old_contract.lease_ids:
+ old_line.copy({'lease_id': new_contract.id})
+
+ # 时间处理
+ day_before = self.start_time - timedelta(days=1)
+ old_contract.write({'actual_end_time': day_before})
+ new_contract.write({'actual_start_time': self.start_time,
+ 'is_child_contract': True})
+
+ # 触发原合同状态更新
+ old_contract.contract_time()
+ new_contract._onchange_action_generate_details()
+
+ return new_contract_map
+
+ def _handle_rent_account_change(self, manage_line, prev_info, new_contract_map):
+ """
+ 收租公司账号变更时触发:
+ ① 原合同 → property.bills.list 应付账单(押金)
+ ② 当前合同 → property.accounts.receivable 押金应收
+ 勾了生成新合同 → current_contract 直接取 new_contract_map 里的新合同ID
+ """
+ if prev_info.yuthon_property_id.company_id.id == self.company_id.id:
+ return
+ if prev_info.yuthon_property_id.company_id.rent_account == self.company_id.rent_account:
+ return
+
+ # 找现有合同
+ lease_line = self.env['property.lease.contract.line'].search([
+ ('address_id', '=', manage_line.property_id.id),
+ ('lease_id', '!=', False),
+ ('lease_id.contact_state', '=', 'ing'),
+ ], limit=1)
+ if not lease_line:
+ return
+
+ old_contract = lease_line.lease_id
+ # 勾了新合同 → 直接用新合同ID;没勾 → 用现有合同
+ new_cid = new_contract_map.get(old_contract.id)
+ current_contract = self.env['property.lease.contract'].browse(new_cid) if new_cid else old_contract
+ original_contract = current_contract.contract_source_id
+
+ # 获取或创建"押金"管理类型
+ deposit_type = self.env['yuthon.management.type'].search(
+ [('name', '=', '押金')], limit=1)
+ if not deposit_type:
+ deposit_type = self.env['yuthon.management.type'].create({'name': '押金'})
+
+ # ① 原合同:创建应付账单 property.bills.list
+ if original_contract and original_contract.deposit:
+ existing_bill = self.env['property.bills.list'].search([
+ ('contract_id', '=', original_contract.id),
+ ('management_type_id', '=', deposit_type.id),
+ ], limit=1)
+ if not existing_bill:
+ self.env['property.bills.list'].create({
+ 'contract_id': original_contract.id,
+ 'amount': original_contract.deposit,
+ 'due_date': self.start_time,
+ 'management_type_id': deposit_type.id,
+ })
+
+ # ② 当前合同:查找或创建押金应收 property.accounts.receivable
+ if current_contract and current_contract.deposit:
+ existing_receivable = self.env['property.accounts.receivable'].search([
+ ('contract_id', '=', current_contract.id),
+ ('management_type_id', '=', deposit_type.id),
+ ], limit=1)
+ if not existing_receivable:
+ self.env['property.accounts.receivable'].create({
+ 'contract_id': current_contract.id,
+ 'due_date': self.start_time,
+ 'original_amount': current_contract.deposit,
+ 'management_type_id': deposit_type.id,
+ })
+
def approval_end(self):
for record in self:
+ new_contract_map = record._generate_and_resolve_contracts()
for manage_line in record.management_line2_ids:
# 更新信息行的结束日期
if manage_line.information_line_id:
@@ -145,6 +271,9 @@ class PropertyManagement(models.Model):
})
lease_contract_ids.version = new_version
+ # 收租账号变更 → 应付账单 + 押金应收
+ record._handle_rent_account_change(manage_line, prev_info, new_contract_map)
+
existing_info = self.env['manage.information.line'].search([
('yuthon_property_id', '=', target_property_id),
('start_time', '=', target_start_time)
diff --git a/yuthon_property_approval/views/property_management.xml b/yuthon_property_approval/views/property_management.xml
index 73df5e8ef..748933cc6 100644
--- a/yuthon_property_approval/views/property_management.xml
+++ b/yuthon_property_approval/views/property_management.xml
@@ -56,6 +56,7 @@
+