212 lines
11 KiB
Python
212 lines
11 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([('lease', '承租方变更'), ('extend', '延长租期')], string='变更类型')
|
|
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
|
|
default='normal')
|
|
is_chapter = fields.Selection([('yes', '是'), ('no', '否')], string="是否用章")
|
|
type_stamp_ids = fields.Many2many('seals.type.setting', string='用章类型', domain="['&', ('start_date', '<=', context_today()), '|', ('end_date', '=', False), ('end_date', '>=', context_today())]")
|
|
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="变更附件")
|
|
|
|
|
|
active = fields.Boolean('Active', default=True, tracking=True)
|
|
work_end = fields.Boolean(string="结束")
|
|
|
|
|
|
# 延长租期
|
|
actual_end_date = fields.Date(string='实际结束日期')
|
|
breakdown_ids = fields.One2many('property.rent.breakdown.line', 'renewal_id', string='租金明细')
|
|
|
|
def _get_tenant_by_contact(self, contact):
|
|
"""按联系人或姓名获取承租方信息"""
|
|
if not contact:
|
|
return False
|
|
return self.env['property.tenant.information'].search([
|
|
'|', ('contact', '=', contact), ('name', '=', contact)
|
|
], limit=1)
|
|
|
|
def _get_employee_by_contact(self, contact):
|
|
"""按联系人姓名匹配员工,拿身份证号等信息"""
|
|
if not contact:
|
|
return False
|
|
return self.env['hr.employee'].search([('name', '=', contact)], limit=1)
|
|
|
|
@api.onchange('new_lessee_id')
|
|
def _onchange_new_lessee_id_fill_contact(self):
|
|
"""选择新承租方时,不再自动带出联系人/电话/身份证号,由联系人字段驱动。"""
|
|
# 需求调整:不做自动填充,保留用户手动输入的联系人
|
|
return
|
|
|
|
@api.onchange('contact')
|
|
def _onchange_contact_fill_info(self):
|
|
"""填写联系人后,按员工信息带出身份证号与联系电话;不再从承租方带出。"""
|
|
for rec in self:
|
|
if not rec.contact:
|
|
continue
|
|
|
|
employee = rec._get_employee_by_contact(rec.contact)
|
|
|
|
if employee:
|
|
rec.phone = employee.mobile_phone or False
|
|
rec.card = employee.identification_id or False
|
|
else:
|
|
# 未找到员工时,清空以避免沿用旧值,由用户手填
|
|
rec.phone = False
|
|
rec.card = False
|
|
|
|
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)
|
|
|
|
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': '更名费'})
|
|
if rec.fee > 0:
|
|
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,
|
|
})
|
|
|
|
@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 EarlyReceivablesLine(models.Model):
|
|
_name = 'early.receivables.line'
|
|
_description = '应收明细'
|
|
|
|
checkout_id = fields.Many2one('property.tenant.checkout', string='关联退租表', ondelete='cascade')
|
|
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
|
|
management_type_id = fields.Many2one(related='receivable_id.management_type_id', string='类型')
|
|
is_reconciliation = fields.Boolean(string='对账')
|
|
due_date = fields.Date(related='receivable_id.due_date', string='应收日期')
|
|
original_amount = fields.Float(string='金额', compute='_compute_original_amount')
|
|
reduction_amount = fields.Float(string='减免金额')
|
|
refund_amount = fields.Float(string='退款金额')
|
|
void = fields.Boolean(string='作废', default=False)
|
|
|
|
@api.depends('receivable_id')
|
|
def _compute_original_amount(self):
|
|
for line in self:
|
|
line.original_amount = line.receivable_id.original_amount - line.receivable_id.discount_amount
|
|
|
|
|