Files
gzth/yuthon_property_approval/models/property_lease_renewal.py
T

287 lines
15 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields, api, exceptions, _
from datetime import datetime, timedelta, date
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError
import calendar
class PropertyLeaseRenewal(models.Model):
_name = 'property.lease.renewal'
_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_lease_renewal_code'))
renewal_type = fields.Selection([('early_checkout', '合同终止'), ('lease', '承租方变更'), ('extend', '延长租期')], string='变更类型')
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
default='normal')
def _get_default_name_title(self):
return self._description if self._description else self._name
name_title = fields.Char(default=_get_default_name_title)
self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])])
# 原合同信息
lessee_id = fields.Many2one(related="contract_id.lessee_id", string='原承租方')
hide = fields.Boolean(string="隐藏")
display_name = fields.Char(string='原承租方')
contract_id = fields.Many2one('property.lease.contract', string='租赁合同')
monthly_rent = fields.Float(related="contract_id.rent", string='租金')
cycle_dict = fields.Selection(related="contract_id.cycle_dict", string='租金支付周期')
original_start_date = fields.Date(related="contract_id.term_start_time", string='原租赁期开始时间')
original_end_date = fields.Date(related="contract_id.term_end_time", string='原租赁期结束时间')
property_name = fields.Char(related="contract_id.property_name", string='物业信息')
# 变更信息
new_lessee_id = fields.Many2one("property.tenant.information", string="承租方", store=True)
contact = fields.Char(string='联系人')
phone = fields.Char(string='联系电话')
card = fields.Char(string='身份证号')
effective_date = fields.Date(string='生效日期', required=True)
entity_type = fields.Selection([('person', '个人'), ('company', '企业')], string='承租方类型')
reason = fields.Text(string='变更原因')
fee = fields.Float(string='变更费用')
document_ids = fields.Many2many('ir.attachment', string="变更附件")
early_explain = fields.Text(string="退租说明")
early_reason = fields.Selection([('normal', '正常退租'), ('agree', '协商一致'), ('settle', '违约结清'), ('other', '其他')], string="退租原因")
early_quitting_date = fields.Date(string="退租日期")
active = fields.Boolean('Active', default=True, tracking=True)
work_end = fields.Boolean(string="结束")
deposit = fields.Float(string="原押金")
is_full_refund = fields.Boolean(string="是否全额退押", default=True)
refund_amount = fields.Float(string="退款金额")
refund_remake = fields.Char(string="备注")
total_receivables = fields.Float(string="应收金额", compute="_compute_total_receivables")
total_remake = fields.Char(string="备注")
balance = fields.Float(string="余额")
balance_remake = fields.Char(string="备注")
receivables_ids = fields.One2many('early.receivables.line', 'renewal_id', string='应收明细')
# 延长租期
actual_end_date = fields.Date(string='实际结束日期')
breakdown_ids = fields.One2many('property.rent.breakdown.line', 'renewal_id', string='租金明细')
@api.depends('receivables_ids', 'receivables_ids.original_amount', 'receivables_ids.reduction_amount',
'receivables_ids.void')
def _compute_total_receivables(self):
for renewal in self:
valid_receivable_lines = renewal.receivables_ids.filtered(lambda line: not line.void and line.is_reconciliation == False)
total = sum(line.original_amount - line.reduction_amount for line in valid_receivable_lines)
renewal.total_receivables = total
def action_print_lease_renewal(self):
"""打印租赁变更审批"""
self.ensure_one()
report = self.env.ref('yuthon_property_approval.action_property_lease_renewal_report')
return report.report_action(self)
@api.onchange('refund_amount')
def _onchange_refund_amount(self):
for rec in self:
rec.balance = rec.deposit - rec.refund_amount
def end_approval(self):
for rec in self:
if rec.renewal_type == 'extend':
# 延长租期:修改原合同实际结束日期,并将租金明细写入原合同
if rec.actual_end_date:
rec.contract_id.write({
'actual_end_time': rec.actual_end_date
})
if rec.breakdown_ids:
for line in rec.breakdown_ids:
self.env['property.rent.breakdown.line'].create({
'breakdown_id': rec.contract_id.id,
'unit_price': line.unit_price,
'monthly_amount': line.monthly_amount,
'start_data': line.start_data,
'end_data': line.end_data,
'month_number': line.month_number,
'period_rent': line.period_rent,
})
elif rec.renewal_type == 'lease':
origin_contract = rec.contract_id
contract_actual_end = origin_contract.actual_end_time if origin_contract else None
effective_date = fields.Date.to_date(rec.effective_date)
last_day = calendar.monthrange(effective_date.year, effective_date.month)[1]
month_end_date = date(effective_date.year, effective_date.month, last_day)
contract_actual_end_date = fields.Date.to_date(contract_actual_end) if contract_actual_end else None
if not contract_actual_end_date or contract_actual_end_date >= month_end_date:
rec.hide = True
origin_contract = rec.contract_id
original_lessee_id = origin_contract.lessee_id.id if origin_contract.lessee_id else False
version_str = origin_contract.version.lstrip('V')
main_version = version_str.split('.')[0]
new_main_version = int(main_version) + 1
new_version = f"V{new_main_version}.0"
# 删除原承租方的物业合同信息
contract_to_unlink = origin_contract.lessee_id.property_contract_ids.filtered(
lambda line: line.lease_contract_id.id == rec.contract_id.id
)
if contract_to_unlink:
contract_to_unlink.unlink()
# 新增新承租方的物业合同信息
property_contract_list = []
origin_contract.write({
'version': new_version,
'lessee_id': rec.new_lessee_id.id
})
origin_contract.is_lessee = True
self.env['lessee.alteration.line'].create({
'contract_id': origin_contract.id,
'lessee_id': original_lessee_id,
'start_date': origin_contract.actual_start_time,
'end_date': rec.effective_date - relativedelta(days=1),
})
origin_contract._compute_contract_count()
property_contract_list.append([0, 0, {
'tenant_id': rec.new_lessee_id.id,
'lease_contract_id': rec.contract_id.id,
'rent': rec.contract_id.rent,
}])
rec.new_lessee_id.write({'property_contract_ids': property_contract_list})
# 新增一条更名应收账单
management_type_id = rec.env["yuthon.management.type"].search([('name', '=', '更名费')])
if not management_type_id:
management_type_id = rec.env["yuthon.management.type"].create({'name': '更名费'})
self.env['property.accounts.receivable'].create({
'contract_id': origin_contract.id,
'original_amount': rec.fee,
'management_type_id': management_type_id.id,
'due_date': rec.effective_date,
})
elif rec.renewal_type == 'early_checkout':
rec.contract_id.write({
'actual_end_time': rec.early_quitting_date
})
if rec.balance > rec.total_receivables:
management_type_id = self.env['yuthon.management.type'].search([
('name', '=', '违约金')
])
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'management_type_id': management_type_id.id,
'due_date': rec.early_quitting_date,
'original_amount': rec.balance - rec.total_receivables,
'remark': rec.total_remake,
})
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({
'due_date': fields.Date.today(),
'management_type_id': refund_line.management_type_id.id,
'amount': refund_line.refund_amount,
'contract_id': rec.contract_id.id,
'remake': rec.refund_remake
})
# 押金
deposit_management_type = self.env['yuthon.management.type'].search(
[('name', '=', '押金')],
limit=1
)
deposit_amount = rec.deposit if rec.is_full_refund else rec.refund_amount
if deposit_amount > 0:
bills_line_list.append({
'due_date': fields.Date.today(),
'management_type_id': deposit_management_type.id,
'amount': deposit_amount,
'contract_id': rec.contract_id.id,
'remake': rec.refund_remake
})
if not rec.is_full_refund:
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
self.env['property.bank.statements'].sudo().create({
'start_time': start_time,
'original_amount': rec.balance,
'name_account': rec.lessee_id.name,
'remake': rec.refund_remake,
})
# 创建应付账单
if bills_line_list:
self.env['property.bills.list'].create(bills_line_list)
# 原有作废/减免逻辑保留
for receivable_id in rec.receivables_ids:
if receivable_id.void:
receivable_id.receivable_id.write({
'is_invalid': True,
'void_date': fields.Date.today()
})
if receivable_id.reduction_amount:
receivable_id.receivable_id.discount_amount = receivable_id.reduction_amount
receivable_id.receivable_id.discount_date = fields.Date.today()
@api.onchange('early_quitting_date', 'contract_id')
def _onchange_early_quitting_date(self):
self.receivables_ids = [(5, 0, 0)]
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,
}])
self.receivables_ids = receivable_list
@api.onchange('contract_id', 'renewal_type')
def _onchange_name(self):
if self.contract_id and self.renewal_type:
renewal_type_text = dict(self._fields['renewal_type'].selection).get(self.renewal_type, '')
self.name = f"{self.contract_id.name} + {renewal_type_text}"
elif self.contract_id:
self.name = self.contract_id.property_name
else:
self.name = False
class PropertyEarlyQuitting(models.Model):
_name = 'property.early.quitting'
class EarlyReceivablesLine(models.Model):
_name = 'early.receivables.line'
_description = '应收明细'
renewal_id = fields.Many2one('property.lease.renewal', string='关联续租审批')
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
management_type_id = fields.Many2one(related='receivable_id.management_type_id', string='类型')
is_reconciliation = fields.Boolean(related='receivable_id.is_reconciliation', string='对账')
due_date = fields.Date(related='receivable_id.due_date', string='应收日期')
original_amount = fields.Float(related='receivable_id.original_amount', string='金额')
reduction_amount = fields.Float(string='减免金额')
refund_amount = fields.Float(string='退款金额')
void = fields.Boolean(string='作废', default=False)