499 lines
29 KiB
Python
499 lines
29 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
||
from datetime import datetime, timedelta, date
|
||
import calendar
|
||
|
||
from odoo import models, fields, api
|
||
from odoo.exceptions import UserError
|
||
|
||
|
||
class PropertyTenantCheckout(models.Model):
|
||
"""租户退租表:物业客户服务中心检查、租户签字、财务结算的多节点审批单。
|
||
各节点可见内容由工作流 state 字段驱动(form 视图按 state 显隐分组),
|
||
节点与按钮在工作流里配置,本模型只管字段与数据。"""
|
||
_name = 'property.tenant.checkout'
|
||
_description = '租户退租表'
|
||
_inherit = ['mail.thread.main.attachment', 'mail.activity.mixin', 'soong.workflow.mixin']
|
||
_rec_name = 'name'
|
||
|
||
# ===== 基础审批字段(沿用租赁变更审批的写法)=====
|
||
name = fields.Char(string='标题') # 审批单标题
|
||
user_id = fields.Many2one('res.users', string="申请人", default=lambda self: self.env.user) # 当前登录用户为申请人
|
||
department_id = fields.Many2one(related='user_id.department_id', string="部门") # 申请人所属部门
|
||
company_id = fields.Many2one(related='user_id.company_id', string="公司") # 申请人所属公司
|
||
code = fields.Char(string="编号",
|
||
default=lambda self: self.env['ir.sequence'].next_by_code('property_tenant_checkout_code')) # 流水号
|
||
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
|
||
default='normal') # 紧急程度
|
||
name_title = fields.Char(default=lambda self: self._description or self._name) # 表单标题展示(取模型描述)
|
||
self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])]) # 当前经办人
|
||
|
||
active = fields.Boolean('Active', default=True, tracking=True) # 归档标记
|
||
work_end = fields.Boolean(string="结束") # 流程是否结束
|
||
|
||
document_ids = fields.Many2many('ir.attachment', 'property_tenant_checkout_document_rel',
|
||
'checkout_id', 'attachment_id', string="附件") # 附件
|
||
|
||
# ===== 关联租赁合同 =====
|
||
contract_id = fields.Many2one('property.lease.contract', string='租赁合同') # 从合同列表「退租」按钮发起时自动带入
|
||
|
||
# ===== 合同信息(关联后自动带出,只读参考)=====
|
||
tenant_unit = fields.Char(string='租户单元') # 如:4F东边01单元(关联合同后自动带出,可改)
|
||
lessee_id = fields.Many2one(related='contract_id.lessee_id', string='承租方', readonly=True) # 承租方(关联合同自动带出)
|
||
contract_code = fields.Char(related='contract_id.contract_code', string='合同编号', readonly=True) # 合同编号
|
||
lessor_id = fields.Many2one(related='contract_id.lessor_id', string='出租方公司', readonly=True) # 出租方
|
||
contract_term_start = fields.Date(related='contract_id.term_start_time', string='合同开始', readonly=True) # 合同期限起
|
||
contract_term_end = fields.Date(related='contract_id.term_end_time', string='合同结束', readonly=True) # 合同期限止
|
||
deposit_amount = fields.Float(related='contract_id.deposit', string='合同保证金', readonly=True) # 合同保证金
|
||
rent_address = fields.Char(string='租赁地址') # 租赁地址(关联合同后自动带出,可改)
|
||
|
||
# ===== 节点一:发起申请 =====
|
||
# 退租原因(同时兼任「合同办理方式」):驱动终止逻辑与表单显隐;early_quitting_date 为终止统一日期(退租截止日期与退租日期统一使用此字段)
|
||
early_reason = fields.Selection([
|
||
('normal', '正常退租'), ('agree', '协商一致'), ('settle', '违约结清'), ('other', '其他')
|
||
], string='退租原因', default='normal')
|
||
early_quitting_date = fields.Date(string='退租日期') # 实际退租日期(终止逻辑统一使用此字段)
|
||
early_explain = fields.Text(string='退租说明') # 退租说明(进入终止/押金处理逻辑)
|
||
handling_opinions = fields.Text(string='经办意见') # 经办意见(申请人填写,沿用成交确认同名字段)
|
||
other_note = fields.Text(string='其它(补充说明)') # 其它补充说明
|
||
|
||
# ===== 节点二:工程组检查与确认 =====
|
||
inspection_ids = fields.One2many('property.tenant.checkout.inspection', 'checkout_id', string='物业检查项') # 物业检查项明细
|
||
site_photo = fields.Many2many('ir.attachment', 'property_tenant_checkout_site_photo_rel',
|
||
'checkout_id', 'attachment_id', string='现场照片') # 现场照片
|
||
engineering_sign = fields.Binary(string='工程组检查确认签字') # 工程组检查确认签字(PNG)
|
||
handler = fields.Char(string='经办人') # 经办人(工程组)
|
||
|
||
# ===== 节点三:租户签字确认 =====
|
||
result_summary = fields.Text(string='结果摘要') # 退租结果摘要
|
||
tenant_signature = fields.Binary(string='租户电子签名') # 租户电子签名(PNG:手绘/打字/上传)
|
||
signer_name = fields.Char(string='签字人姓名') # 签字人姓名
|
||
tenant_sign_date = fields.Date(string='签字确认日期') # 签字确认日期
|
||
|
||
# ===== 节点四:费用结算与复核 =====
|
||
fee_ids = fields.One2many('property.tenant.checkout.fee', 'checkout_id', string='应收费用明细') # 应收费用明细
|
||
total_fee = fields.Float(string='应收合计', compute='_compute_total_fee', store=True) # 应收明细(未对账且未作废)金额-减免-退款合计
|
||
deposit_treatment = fields.Selection([
|
||
('refund', '全额退押'), ('part_refund', '部分退租'), ('no_refund', '全额没收')
|
||
], string='保证金处理方式') # 保证金处理方式(与租赁变更审批·合同终止逻辑一致)
|
||
settlement_sign = fields.Binary(string='结算复核签字') # 结算复核签字(PNG)
|
||
|
||
# ===== 节点五:财务处理 =====
|
||
finance_handler = fields.Char(string='财务部经办人') # 财务部经办人
|
||
finance_reviewer = fields.Char(string='复核人') # 复核人
|
||
finance_person_in_charge = fields.Char(string='财务部负责人') # 财务部负责人
|
||
finance_date = fields.Date(string='财务日期') # 财务部日期
|
||
|
||
# ===== 合同终止相关(从租赁变更审批·合同终止迁移)=====
|
||
deposit = fields.Float(string='原押金') # 原押金(关联合同押金类应收汇总,onchange 带出)
|
||
refund_amount = fields.Float(string='退款金额') # 退款金额(部分退押时填写)
|
||
forfeit = fields.Float(string='没收金额') # 没收金额(= 原押金 - 退款金额)
|
||
total_receivables = fields.Float(string='应收金额', compute='_compute_total_receivables', store=True) # 应收金额(部分退押时汇总)
|
||
receivables_ids = fields.One2many('early.receivables.line', 'checkout_id', string='应收明细') # 截至退租日的应收账单(可作废/减免/退款)
|
||
|
||
# ===== 工程组检查项:默认带出字典中的检查项 =====
|
||
def _default_inspection_lines(self):
|
||
"""按检查项字典(有效项、按序号)生成明细行命令,供 default_get / create 自动带出"""
|
||
items = self.env['property.checkout.inspection.item'].search([('active', '=', True)])
|
||
return [(0, 0, {
|
||
'item_id': item.id,
|
||
'sequence': item.sequence,
|
||
'status': 'normal',
|
||
}) for item in items]
|
||
|
||
@api.model
|
||
def default_get(self, fields_list):
|
||
"""新建表单时即带出检查项默认行(未保存也可在表单上看到)"""
|
||
res = super().default_get(fields_list)
|
||
if 'inspection_ids' in fields_list and not res.get('inspection_ids'):
|
||
res['inspection_ids'] = self._default_inspection_lines()
|
||
return res
|
||
|
||
@api.model_create_multi
|
||
def create(self, vals_list):
|
||
"""程序化创建(如合同列表点「退租」发起)时,自动补齐检查项默认行"""
|
||
for vals in vals_list:
|
||
if 'inspection_ids' not in vals:
|
||
vals['inspection_ids'] = self._default_inspection_lines()
|
||
return super().create(vals_list)
|
||
|
||
@api.depends('receivables_ids', 'receivables_ids.original_amount', 'receivables_ids.reduction_amount',
|
||
'receivables_ids.refund_amount', 'receivables_ids.void', 'receivables_ids.is_reconciliation')
|
||
def _compute_total_fee(self):
|
||
"""应收合计 = 应收明细中「未对账且未作废」的(金额 - 减免金额 - 退款金额)之和"""
|
||
for rec in self:
|
||
rec.total_fee = sum(
|
||
line.original_amount - line.reduction_amount - line.refund_amount
|
||
for line in rec.receivables_ids
|
||
if not line.void and not line.is_reconciliation
|
||
)
|
||
|
||
@api.onchange('tenant_signature')
|
||
def _onchange_tenant_signature(self):
|
||
"""签名后自动填写签字日期;清除签名则清空日期(仅记录、不强制)"""
|
||
if self.tenant_signature:
|
||
self.tenant_sign_date = fields.Date.today() # 有签名则写入当天日期
|
||
else:
|
||
self.tenant_sign_date = False # 无签名则清空日期
|
||
|
||
@api.onchange('contract_id')
|
||
def _onchange_contract_id(self):
|
||
"""关联合同后自动带出公司名称/物业信息/租赁地址(可再手工修改);其余信息以 related 只读展示。
|
||
正常退租时,自动带出合同结束日期作为退租日期。"""
|
||
if self.contract_id:
|
||
self.tenant_unit = self.contract_id.property_name # 租户单元=合同物业信息
|
||
# 租赁地址=合同物业明细首行对应的物业地址名称(无物业明细则为空)
|
||
self.rent_address = self.contract_id.lease_ids[:1].address_id.name
|
||
# 正常退租:退租日期默认取合同结束日期(可再手工修改)
|
||
if self.early_reason == 'normal' and self.contract_id.term_end_time:
|
||
self.early_quitting_date = self.contract_id.term_end_time
|
||
|
||
@api.depends('receivables_ids', 'receivables_ids.original_amount', 'receivables_ids.reduction_amount',
|
||
'receivables_ids.void')
|
||
def _compute_total_receivables(self):
|
||
"""应收金额 = 未被作废且未对账的应收明细(原金额 - 减免金额)之和"""
|
||
for rec in self:
|
||
valid_lines = rec.receivables_ids.filtered(lambda line: not line.void and not line.is_reconciliation)
|
||
rec.total_receivables = sum(line.original_amount - line.reduction_amount for line in valid_lines)
|
||
|
||
@api.onchange('refund_amount')
|
||
def _onchange_forfeit(self):
|
||
"""部分退押时:没收金额 = 原押金 - 退款金额"""
|
||
for rec in self:
|
||
rec.forfeit = rec.deposit - rec.refund_amount
|
||
|
||
@api.onchange('early_quitting_date', 'contract_id')
|
||
def _onchange_early_quitting_date(self):
|
||
"""退租日期/合同变化:拉取截至退租日的应收账单生成应收明细,并汇总原押金"""
|
||
self.receivables_ids = [(5, 0, 0)]
|
||
if not self.early_quitting_date or not self.contract_id:
|
||
self.deposit = 0
|
||
return
|
||
receivable_ids = self.env['property.accounts.receivable'].search([
|
||
('due_date', '<=', self.early_quitting_date),
|
||
('contract_id', '=', self.contract_id.id),
|
||
])
|
||
deposit_receivables = self.env['property.accounts.receivable']
|
||
normal_receivables = self.env['property.accounts.receivable']
|
||
for rec in receivable_ids:
|
||
if rec.management_type_id and rec.management_type_id.name == '押金':
|
||
deposit_receivables |= rec
|
||
else:
|
||
normal_receivables |= rec
|
||
|
||
if deposit_receivables:
|
||
self.deposit = sum(rec.original_amount for rec in deposit_receivables)
|
||
|
||
receivable_list = []
|
||
for rec in normal_receivables:
|
||
receivable_list.append([0, 0, {
|
||
'receivable_id': rec.id,
|
||
'is_reconciliation': rec.is_reconciliation,
|
||
}])
|
||
self.receivables_ids = receivable_list
|
||
|
||
def end_approval(self):
|
||
"""合同终止终审逻辑(从租赁变更审批·合同终止 early_checkout 分支迁移)。
|
||
统一以 early_quitting_date 作为终止日期;按 early_reason 区分退租方式由前端/配置决定,
|
||
此处对所有退租场景统一处理:押金处理 + 应收作废/减免 + 银行流水与对账。"""
|
||
for rec in self:
|
||
contract = rec.contract_id
|
||
if not contract:
|
||
continue
|
||
workflow_record_id = self.env["soong.workflow.record"].search(
|
||
[("res_model", "=", self._name), ("res_id", "=", self.id)])
|
||
lessor_id = contract.lessor_id
|
||
if lessor_id.referred in ['天河投资', '天投城建']:
|
||
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
|
||
elif lessor_id.referred in ['天河物管']:
|
||
company_id = self.env['res.company'].browse(3)
|
||
elif lessor_id.referred in ['天河粮储']:
|
||
company_id = self.env['res.company'].search([('name', '=', '天河粮储')], limit=1)
|
||
else:
|
||
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
|
||
|
||
# 取本审批最后一条流程日志的操作时间,作为账单 due_date
|
||
operation_log = self.env['soong.workflow.log'].search([
|
||
('model', '=', self._name),
|
||
('res_id', '=', rec.id),
|
||
], order='lase_click_date DESC, id DESC', limit=1)
|
||
operation_date = operation_log.lase_click_date.date() if operation_log and operation_log.lase_click_date else fields.Date.today()
|
||
|
||
# 非押金退款明细(除押金外的退款,原逻辑保留)
|
||
if rec.receivables_ids:
|
||
refund_lines = rec.receivables_ids.filtered(
|
||
lambda l: l.refund_amount > 0 and l.management_type_id.name != '押金')
|
||
bills_line_list = []
|
||
if refund_lines:
|
||
for refund_line in refund_lines:
|
||
bills_line_list.append({
|
||
'lessor_id': contract.lessor_id.id,
|
||
'due_date': operation_date,
|
||
'management_type_id': refund_line.management_type_id.id,
|
||
'amount': refund_line.refund_amount,
|
||
'contract_id': contract.id,
|
||
'receivables_id': refund_line.id,
|
||
'remake': rec.early_explain
|
||
})
|
||
if bills_line_list:
|
||
self.env['property.bills.list'].create(bills_line_list)
|
||
|
||
# 押金处理(根据押金处理方式分三种,先判断去重再创建)
|
||
deposit_management_type = self.env['yuthon.management.type'].search(
|
||
[('name', '=', '押金')], limit=1)
|
||
|
||
if rec.deposit_treatment == 'refund':
|
||
# 全额退押金:仅创建 bills.list,金额 = 原押金 deposit
|
||
if rec.deposit > 0:
|
||
exist_bill = self.env['property.bills.list'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id', '=', deposit_management_type.id),
|
||
('amount', '=', rec.deposit),
|
||
('workflow_record_id', '=', workflow_record_id.id),
|
||
], limit=1)
|
||
if not exist_bill:
|
||
self.env['property.bills.list'].create({
|
||
'lessor_id': contract.lessor_id.id,
|
||
'due_date': operation_date,
|
||
'management_type_id': deposit_management_type.id,
|
||
'amount': rec.deposit,
|
||
'contract_id': contract.id,
|
||
'remake': rec.early_explain,
|
||
'workflow_record_id': workflow_record_id.id,
|
||
})
|
||
|
||
elif rec.deposit_treatment == 'part_refund':
|
||
# 部分退租:bills.list 金额 = refund_amount;bank.statements 和 receivable 金额 = forfeit
|
||
forfeit = rec.deposit - rec.refund_amount
|
||
if rec.refund_amount > 0:
|
||
exist_bill = self.env['property.bills.list'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id', '=', deposit_management_type.id),
|
||
('amount', '=', rec.refund_amount),
|
||
('workflow_record_id', '=', workflow_record_id.id),
|
||
], limit=1)
|
||
if not exist_bill:
|
||
self.env['property.bills.list'].create({
|
||
'lessor_id': contract.lessor_id.id,
|
||
'due_date': operation_date,
|
||
'management_type_id': deposit_management_type.id,
|
||
'amount': rec.refund_amount,
|
||
'contract_id': contract.id,
|
||
'remake': rec.early_explain,
|
||
'workflow_record_id': workflow_record_id.id,
|
||
})
|
||
if forfeit > 0:
|
||
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
|
||
existing_bank_statement = self.env['property.bank.statements'].sudo().search([
|
||
('start_time', '=', start_time),
|
||
('original_amount', '=', forfeit),
|
||
('name_account', '=', rec.lessee_id.name),
|
||
('remake', '=', rec.early_explain),
|
||
('workflow_record_id', '=', workflow_record_id.id),
|
||
], limit=1)
|
||
if not existing_bank_statement:
|
||
bank_accounts = self.env['property.bank.line'].search([
|
||
('tenant_id', '=', rec.lessee_id.id)
|
||
]).mapped('bank')
|
||
filtered_accounts = [acc for acc in bank_accounts if acc]
|
||
bank_accounts = filtered_accounts[0] if filtered_accounts else None
|
||
contract.write({
|
||
'actual_end_time': rec.early_quitting_date
|
||
})
|
||
bank_id = self.env['property.bank.statements'].sudo().create({
|
||
'start_time': start_time,
|
||
'name': bank_accounts,
|
||
'original_amount': forfeit,
|
||
'name_account': rec.lessee_id.name,
|
||
'remake': rec.early_explain,
|
||
'lending_direction': 'loan',
|
||
'workflow_record_id': workflow_record_id.id,
|
||
'company_id': company_id.id,
|
||
'your_own_unit': lessor_id.referred,
|
||
'your_own_account': lessor_id.rent_account,
|
||
})
|
||
bank_id._compute_property_employee_id()
|
||
|
||
management_type_id = self.env['yuthon.management.type'].search([
|
||
('name', '=', '违约金')
|
||
], limit=1)
|
||
exist_receivable = self.env['property.accounts.receivable'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id', '=', management_type_id.id),
|
||
('due_date', '=', rec.early_quitting_date),
|
||
('original_amount', '=', forfeit),
|
||
], limit=1)
|
||
if not exist_receivable:
|
||
self.env['property.accounts.receivable'].create({
|
||
'contract_id': contract.id,
|
||
'management_type_id': management_type_id.id,
|
||
'due_date': rec.early_quitting_date,
|
||
'original_amount': forfeit,
|
||
'remark': rec.early_explain,
|
||
})
|
||
deposit_id = self.env['property.accounts.receivable'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id.name', '=', '押金'),
|
||
], limit=1)
|
||
if deposit_id:
|
||
deposit_id.write({
|
||
'workflow_record_id': workflow_record_id.id
|
||
})
|
||
|
||
elif rec.deposit_treatment == 'no_refund':
|
||
# 全额没收:bills.list、bank.statements、receivable 金额均为 deposit
|
||
if rec.deposit > 0:
|
||
exist_bill = self.env['property.bills.list'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id', '=', deposit_management_type.id),
|
||
('amount', '=', rec.deposit),
|
||
('workflow_record_id', '=', workflow_record_id.id),
|
||
], limit=1)
|
||
if not exist_bill:
|
||
bill = self.env['property.bills.list'].create({
|
||
'lessor_id': contract.lessor_id.id,
|
||
'due_date': operation_date,
|
||
'management_type_id': deposit_management_type.id,
|
||
'amount': rec.deposit,
|
||
'contract_id': contract.id,
|
||
'remake': rec.early_explain,
|
||
'workflow_record_id': workflow_record_id.id,
|
||
})
|
||
else:
|
||
bill = exist_bill
|
||
|
||
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
|
||
existing_bank_statement = self.env['property.bank.statements'].sudo().search([
|
||
('start_time', '=', start_time),
|
||
('original_amount', '=', rec.deposit),
|
||
('name_account', '=', rec.lessee_id.name),
|
||
('remake', '=', rec.early_explain),
|
||
('workflow_record_id', '=', workflow_record_id.id),
|
||
], limit=1)
|
||
if not existing_bank_statement:
|
||
bank_accounts = self.env['property.bank.line'].search([
|
||
('tenant_id', '=', rec.lessee_id.id)
|
||
]).mapped('bank')
|
||
filtered_accounts = [acc for acc in bank_accounts if acc]
|
||
bank_accounts = filtered_accounts[0] if filtered_accounts else None
|
||
contract.write({
|
||
'actual_end_time': rec.early_quitting_date
|
||
})
|
||
bank_id = self.env['property.bank.statements'].sudo().create({
|
||
'start_time': start_time,
|
||
'name': bank_accounts,
|
||
'original_amount': rec.deposit,
|
||
'name_account': rec.lessee_id.name,
|
||
'remake': rec.early_explain,
|
||
'lending_direction': 'loan',
|
||
'workflow_record_id': workflow_record_id.id,
|
||
'company_id': company_id.id,
|
||
'your_own_unit': lessor_id.referred,
|
||
'your_own_account': lessor_id.rent_account,
|
||
})
|
||
bank_id._compute_property_employee_id()
|
||
else:
|
||
bank_id = existing_bank_statement
|
||
|
||
management_type_id = self.env['yuthon.management.type'].search([
|
||
('name', '=', '违约金')
|
||
], limit=1)
|
||
exist_receivable = self.env['property.accounts.receivable'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id', '=', management_type_id.id),
|
||
('due_date', '=', rec.early_quitting_date),
|
||
('original_amount', '=', rec.deposit),
|
||
], limit=1)
|
||
if not exist_receivable:
|
||
self.env['property.accounts.receivable'].create({
|
||
'contract_id': contract.id,
|
||
'management_type_id': management_type_id.id,
|
||
'due_date': rec.early_quitting_date,
|
||
'original_amount': rec.deposit,
|
||
'remark': rec.early_explain,
|
||
})
|
||
|
||
# ===== 触发 action_pay + button_reconciliation 的真实逻辑 =====
|
||
deposit_receivable = self.env['property.accounts.receivable'].search([
|
||
('contract_id', '=', contract.id),
|
||
('management_type_id', '=', deposit_management_type.id),
|
||
], limit=1)
|
||
|
||
# 1) 支付效果(action_pay):调用支付弹窗的真实逻辑
|
||
if bill and not bill.actual_date:
|
||
bill.amount_actual = bill.amount
|
||
pay_wizard = self.env['property.bills.wizard'].create({
|
||
'bills_list_id': bill.id,
|
||
'actual_date': bill.due_date,
|
||
'receivables_id': deposit_receivable.id if deposit_receivable else False,
|
||
})
|
||
pay_wizard._do_pay()
|
||
|
||
# 2) 对账效果(button_reconciliation):bank.statements 与押金应收对账
|
||
if bank_id and bank_id.bank_state != 'reconciled':
|
||
bank_id.bank_state = 'reconciled'
|
||
bank_id.yes_amount = bank_id.original_amount
|
||
bank_id.reconciliation_time = fields.Datetime.now() - timedelta(hours=8)
|
||
if exist_receivable:
|
||
exist_receivable.write({
|
||
'has_date': bill.due_date if bill else operation_date,
|
||
'is_reconciliation': True,
|
||
'payout_date': operation_date,
|
||
'delivered1': rec.deposit,
|
||
'deposit_state': 'paid',
|
||
'reconciliation_time': fields.Datetime.now(),
|
||
'bank_id': bank_id.id,
|
||
})
|
||
self.env['receivable.modify.line'].create({
|
||
'bank_id': bank_id.id,
|
||
'not_collected': rec.deposit,
|
||
'reconciliation_date': operation_date,
|
||
'receivable_id': exist_receivable.id,
|
||
'is_write': True,
|
||
'operation_date': fields.Date.today(),
|
||
'is_penalty': True,
|
||
'remake1': rec.early_explain,
|
||
'amount': rec.deposit,
|
||
})
|
||
contract.contact_state = 'settle'
|
||
contract.compute_is_has()
|
||
|
||
# 原有作废/减免逻辑保留
|
||
for receivable_id in rec.receivables_ids:
|
||
if receivable_id.void:
|
||
receivable_id.receivable_id.write({
|
||
'is_invalid': True,
|
||
'void_date': operation_date
|
||
})
|
||
if receivable_id.reduction_amount:
|
||
receivable_id.receivable_id.discount_amount = receivable_id.reduction_amount
|
||
receivable_id.receivable_id.discount_date = operation_date
|
||
|
||
def action_print_tenant_checkout(self):
|
||
"""打印租户退租表"""
|
||
self.ensure_one()
|
||
report = self.env.ref('yuthon_property_approval.action_property_tenant_checkout_report')
|
||
return report.report_action(self)
|
||
|
||
|
||
class PropertyLeaseContract(models.Model):
|
||
"""在租赁合同上增加「退租」入口:从合同列表直接发起租户退租表"""
|
||
_inherit = 'property.lease.contract'
|
||
|
||
def action_tenant_checkout(self):
|
||
"""合同列表「退租」按钮:打开租户退租表并自动关联当前合同"""
|
||
self.ensure_one()
|
||
return {
|
||
'name': '租户退租表',
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'property.tenant.checkout',
|
||
'view_mode': 'form',
|
||
'target': 'current',
|
||
'context': {
|
||
'default_contract_id': self.id, # 关联当前合同
|
||
'default_tenant_unit': self.property_name, # 带出租户单元(物业信息)
|
||
'default_rent_address': self.lease_ids[:1].address_id.name, # 带出租赁地址(物业地址名称)
|
||
},
|
||
}
|