2908 lines
142 KiB
Python
2908 lines
142 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
import pandas as pd
|
||
import calendar
|
||
from dateutil.relativedelta import relativedelta
|
||
from datetime import datetime, timedelta, date
|
||
from odoo.tools.float_utils import float_round
|
||
from numpy.ma.core import default_filler
|
||
import re
|
||
from odoo import models, fields, api, exceptions, _
|
||
from odoo.exceptions import UserError, ValidationError
|
||
|
||
|
||
class PropertyContractLine(models.Model):
|
||
_name = 'property.contract.line'
|
||
_description = '物业合同明细行'
|
||
|
||
yuthon_property_id = fields.Many2one('yuthon.property', string="关联物业信息")
|
||
lease_contract_id = fields.Many2one('property.lease.contract', string="合同编号")
|
||
lessee_id = fields.Many2one('property.tenant.information', string='承租方')
|
||
rent = fields.Float(string="租金")
|
||
start_time = fields.Date(string="生效日期")
|
||
end_date = fields.Date(string="失效日期")
|
||
|
||
def open_record(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': '物业合同',
|
||
'view_mode': 'form',
|
||
'res_model': 'property.lease.contract',
|
||
"res_id": self.lease_contract_id.id,
|
||
}
|
||
|
||
@api.onchange('start_time', 'end_date', 'yuthon_property_id', 'lease_contract_id')
|
||
def _onchange_contract_dates(self):
|
||
# 1. 若未关联物业,直接返回
|
||
if not self.yuthon_property_id:
|
||
return
|
||
all_contract_lines = self.env['property.contract.line'].search([
|
||
('yuthon_property_id', '=', self.yuthon_property_id.id.origin)
|
||
])
|
||
if not all_contract_lines:
|
||
return
|
||
for contract_line in all_contract_lines:
|
||
if not all([contract_line.lease_contract_id, contract_line.start_time]):
|
||
continue
|
||
date_domain = [('meter_date', '>=', contract_line.start_time)]
|
||
if contract_line.end_date:
|
||
date_domain.append(('meter_date', '<=', contract_line.end_date))
|
||
|
||
domain = [
|
||
('yuthon_property_ids', 'in', [self.yuthon_property_id.id.origin]),
|
||
*date_domain
|
||
]
|
||
meter_models = [self.env["meter.archives.line"], self.env["meter.water.line"]]
|
||
for model in meter_models:
|
||
meter_lines = model.search(domain)
|
||
if meter_lines:
|
||
meter_lines.write({
|
||
'contract_id': contract_line.lease_contract_id.id,
|
||
})
|
||
|
||
|
||
|
||
class PropertyTenantLine(models.Model):
|
||
_name = 'property.tenant.line'
|
||
_description = '物业承租方合同明细行'
|
||
|
||
tenant_id = fields.Many2one('property.tenant.information', string="关联租户信息")
|
||
property_ids = fields.Many2many('yuthon.property', string="关联物业信息") # 数据库字段缺失 无实际意义
|
||
property_name = fields.Char(related='lease_contract_id.property_name', string="物业信息")
|
||
lease_contract_id = fields.Many2one('property.lease.contract', string="合同编号")
|
||
rent = fields.Float(related='lease_contract_id.rent', string="租金")
|
||
start_time = fields.Date(related='lease_contract_id.actual_start_time', string="生效日期")
|
||
end_date = fields.Date(related='lease_contract_id.actual_end_time',string="失效日期")
|
||
|
||
@api.model
|
||
def create(self, vals):
|
||
"""在创建记录时自动关联合同对应的物业"""
|
||
if vals.get('lease_contract_id'):
|
||
contract = self.env['property.lease.contract'].browse(vals['lease_contract_id'])
|
||
property_ids = contract.lease_ids.mapped('address_id.id')
|
||
if property_ids:
|
||
vals['property_ids'] = [(6, 0, property_ids)]
|
||
res = super(PropertyTenantLine, self).create(vals)
|
||
self.env.flush_all()
|
||
res._refresh_bank_stmt_by_tenant()
|
||
return res
|
||
|
||
def write(self, vals):
|
||
res = super().write(vals)
|
||
self.env.flush_all()
|
||
if 'tenant_id' in vals:
|
||
self._refresh_bank_stmt_by_tenant()
|
||
return res
|
||
|
||
def unlink(self):
|
||
tenants_to_refresh = self.mapped('tenant_id')
|
||
res = super().unlink()
|
||
if tenants_to_refresh:
|
||
self._refresh_bank_stmt_by_tenants(tenants_to_refresh)
|
||
return res
|
||
|
||
def _refresh_bank_stmt_by_tenant(self):
|
||
"""当 property.tenant.line 的 tenant_id 变更时,刷新关联的银行流水计算字段"""
|
||
tenant_list = self.mapped('tenant_id')
|
||
self._refresh_bank_stmt_by_tenants(tenant_list)
|
||
|
||
def _refresh_bank_stmt_by_tenants(self, tenant_list):
|
||
"""根据承租方列表,找到关联的银行流水并触发计算字段重算"""
|
||
if not tenant_list:
|
||
return
|
||
bank_lines = self.env['property.bank.line'].search([('tenant_id', 'in', tenant_list.ids)])
|
||
bank_no_list = bank_lines.mapped('bank')
|
||
if not bank_no_list:
|
||
return
|
||
stmt_recs = self.env['property.bank.statements'].search([('name', 'in', bank_no_list)])
|
||
if stmt_recs:
|
||
for stmt in stmt_recs:
|
||
stmt._compute_property_employee_id()
|
||
|
||
def _constrain_tenant_refresh_bank(self):
|
||
"""@deprecated: 请使用 _refresh_bank_stmt_by_tenant"""
|
||
self._refresh_bank_stmt_by_tenant()
|
||
|
||
class PropertyLeaseContract(models.Model):
|
||
_name = 'property.lease.contract'
|
||
_description = '租赁合同审批'
|
||
_inherit = ['mail.thread.main.attachment', 'mail.activity.mixin', 'soong.workflow.mixin']
|
||
_rec_name = 'contract_code'
|
||
|
||
property_name = fields.Char(string='物业信息')
|
||
confirm_id = fields.Many2one('property.confirm', string="关联成交确认审批")
|
||
version = fields.Char(string="合同版本", default="V1.0")
|
||
code = fields.Char(string="流水号", default=lambda self: self.env['ir.sequence'].next_by_code('property_lease_contract_code'))
|
||
admin_id = fields.Many2one('res.users', string="管理员", compute="_compute_admin_id", store=True)
|
||
tenure_type = fields.Selection([('self', '自有'), ('agency', '代管')], string='权属类型', compute="_compute_admin_id", store=True)
|
||
contract_code = fields.Char(string="合同编号")
|
||
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())]")
|
||
scope_chapter_ids = fields.Many2many('seals.scope.setting', string='用章范围')
|
||
contract_date = fields.Date(string="合同签订日期")
|
||
is_print = fields.Boolean(string="当月打印", compute ="_compute_is_print", store=True)
|
||
property_documents_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_documents_rel',
|
||
column1='property_lease_contract_id',
|
||
column2='attachment_id',
|
||
string="租赁合同文件"
|
||
)
|
||
title_address = fields.Char(string="产权地址")
|
||
# 产权地址提示:仅用于在表单上标识“哪些物业缺少产权地址”,方便后续修复;
|
||
# 不影响 title_address 原字段的取值、计算与报表使用
|
||
title_address_tip = fields.Char(string="产权地址提示", compute="_compute_title_address_tip", store=False)
|
||
|
||
@api.depends('lease_ids.address_id.title_address', 'lease_ids.address_id.name')
|
||
def _compute_title_address_tip(self):
|
||
# 遍历合同关联的每一个物业,找出缺少产权地址的物业
|
||
for rec in self:
|
||
# 缺少产权地址的物业名称列表
|
||
missing = []
|
||
# 已关联物业(有 address_id)的数量
|
||
has_address_count = 0
|
||
for lease in rec.lease_ids:
|
||
if lease.address_id:
|
||
has_address_count += 1
|
||
# 物业已关联且产权地址为空,记为缺失
|
||
if not lease.address_id.title_address:
|
||
missing.append(lease.address_id.name or "未命名物业")
|
||
if not missing:
|
||
# 所有物业都有产权地址,无需提示
|
||
rec.title_address_tip = False
|
||
elif has_address_count > 0 and len(missing) == has_address_count:
|
||
# 全部物业都缺产权地址
|
||
rec.title_address_tip = "没有产权地址"
|
||
else:
|
||
# 部分物业缺产权地址,列出具体物业名
|
||
rec.title_address_tip = "以下物业缺少产权地址:" + "、".join(missing)
|
||
receivable_ids = fields.One2many('property.accounts.receivable', 'contract_id', string="应收账单")
|
||
receivable_report_ids = fields.Many2many('property.accounts.receivable', string="应收账单(报表)")
|
||
print_date = fields.Date(string="打印日期")
|
||
total_amount = fields.Float(string="合计金额")
|
||
total_amount_caps = fields.Char(string="合计金额大写")
|
||
payment_ids = fields.One2many('property.payment.notice', 'contract_id', string="合同打印")
|
||
handling_opinions = fields.Text(string="经办意见")
|
||
# 是否停车费:勾选后,该合同推送租金时生成的应收类型为"停车费"而非"租金"
|
||
is_parking_fee = fields.Boolean(string="是否停车费", default=False)
|
||
lessor_id = fields.Many2one('yuthon.property.company', string="出租方公司")
|
||
rent_company = fields.Char(related='lessor_id.name', string="收租公司名称")
|
||
rent_account = fields.Char(related='lessor_id.rent_account', string="收租公司账号")
|
||
users_id = fields.Many2one('res.users', string="申请人", default=lambda self: self.env.user)
|
||
department_id = fields.Many2one('hr.department', string="部门", default= lambda self: self.env.user.department_id)
|
||
company_id = fields.Many2one('res.company', string="转交公司", stroe=True, compute = "_compute_company_id")
|
||
# 承租方的个人信息
|
||
lessee_id = fields.Many2one('property.tenant.information', string='承租方')
|
||
lessee_name = fields.Char(related='lessee_id.name', string="承租方姓名")
|
||
contact = fields.Char(related='lessee_id.contact', string="联系人")
|
||
phone = fields.Char(related='lessee_id.phone', string="联系电话")
|
||
email = fields.Char(related='lessee_id.email', string="电子邮箱")
|
||
card = fields.Char(related='lessee_id.card', string="身份证号")
|
||
invoicing_type = fields.Selection([('spot', '专票'), ('cape', '普票')], related='lessee_id.invoicing_type', string="开票类型")
|
||
rental_purposes = fields.Many2one('yuthon.rental.purpose', string="出租用途")
|
||
invoicing_begins = fields.Char(related='lessee_id.invoicing_begins', string="开票抬头")
|
||
tax_number = fields.Char(related='lessee_id.tax_number', string="税号")
|
||
industry_id = fields.Many2one(related='lessee_id.industry_id', string="行业")
|
||
address_for_delivery = fields.Char(string="送达地址")
|
||
precinct_id = fields.Many2one('yuthon.precinct.message', string="所属片区", compute='_compute_precinct_id')
|
||
active = fields.Boolean('Active', default=True, tracking=True)
|
||
|
||
lease_term_year = fields.Integer(string="租期(年)")
|
||
lease_term_month = fields.Integer(string="租期(月)")
|
||
lease_term_day = fields.Integer(string="租期(日)")
|
||
is_less_than_2 = fields.Boolean(string="是否小于2年")
|
||
term_start_time = fields.Date(string="合约开始时间", default=fields.Datetime.now)
|
||
term_end_time = fields.Date(string="合约结束时间")
|
||
actual_start_time = fields.Date(string="实际开始时间")
|
||
actual_end_time = fields.Date(string="实际结束时间")
|
||
end_comparison = fields.Selection([('red', '红'), ('yellow', '黄'), ('blue', '蓝')], string="结束时间对比(颜色)", compute='_compute_end_comparison', store=True)
|
||
expiration_coming = fields.Selection([('coming', '即将到期'), ('due', '已到期')], string="结束时间对比(状态)", compute='_compute_expiration_coming', store=True)
|
||
is_equal = fields.Boolean(string="是否相等", compute='_compute_end_comparison', store=True)
|
||
is_has = fields.Boolean(string='已退押金', compute="compute_is_has")
|
||
is_has_generated = fields.Boolean()
|
||
cycle_dict = fields.Selection([('monthly', '月度'), ('quarter', '季度'), ('half', '半年'),('year', '年度')],string='付款方式', default='monthly', help="应收")
|
||
cycle_mode = fields.Selection([('fixed_monthly', '合同月'), ('floating_monthly', '自然月'), ('fixed_quarter', '合同季'), ('floating_quarter', '自然季'),
|
||
('fixed_year', '合同年'), ('floating_year', '自然年'), ('is_half_year', '合同半年')], string='周期方式', default='fixed_monthly')
|
||
expected_reminders = fields.Char(string="逾期提醒", default=25)
|
||
increment_method = fields.Selection([('percentage', '百分比'),('increment_price', '递增单价'),('increment_amount', '递增金额')], string='每月递增', default='percentage')
|
||
|
||
increment_percentage = fields.Float(string="递增百分比")
|
||
increment_price = fields.Float(string="递增单价")
|
||
increment_amount = fields.Float(string="递增金额")
|
||
increment_value = fields.Float(string="递增值")
|
||
|
||
first_years_begin = fields.Integer(string="第几年开始", default=2)
|
||
increments_every_few_years = fields.Integer(string="每几年递增")
|
||
|
||
rent_free_period = fields.Datetime(string="免租期")
|
||
rent_free_period_month = fields.Integer(string="免租期")
|
||
rent_free_period_days = fields.Integer(string="免租期")
|
||
|
||
breach = fields.Integer(string="违约计算时点", default=1)
|
||
is_split = fields.Boolean(string="是否合同拆分")
|
||
|
||
def _get_percentage_liquidated_damages(self):
|
||
default_rate = self.env['yuthon.penalty.setting'].search(
|
||
[('name', '=', 0.001)], limit=1
|
||
)
|
||
return default_rate.id if default_rate else False
|
||
|
||
percentage_liquidated_damages = fields.Many2one('yuthon.penalty.setting', string="违约金比例", default=_get_percentage_liquidated_damages)
|
||
percentage_damages= fields.Float(string="违约金", compute="_compute_contract_count", store=True)
|
||
date_of_payment = fields.Selection([('five', '5'), ('fifteen', '15'), ('twenty', '20'), ('twentyfive', '25')], string="支付日期")
|
||
first_pay = fields.Date(string="首次付款日")
|
||
unit_price = fields.Float(string="单价", store=True, digits=(16, 4))
|
||
deposit_type = fields.Selection([('deposit', '押金'), ('performance', '履约保证金')], string="押金/履约保证金")
|
||
deposit_status = fields.Selection([('unpaid', '未支付'), ('paid', '已支付'), ('returned','已退'),('no_paid', '没收')], string='押金状态')
|
||
deposit = fields.Float(string="押金/履约保证金", store=True)
|
||
performance_bonds_write = fields.Char(string="履约保证金大写")
|
||
first_year_rent = fields.Float(string="首年租金")
|
||
rent = fields.Float(string="租金", help='第一笔合同的应收')
|
||
total_contract_receivables = fields.Float(string="合同总应收", compute="_compute_total_contract_receivables")
|
||
lessor_line = fields.One2many('lessor.alteration.line', 'contract_id', string="出租变更明细")
|
||
lessee_line = fields.One2many('lessee.alteration.line', 'contract_id', string="承租变更明细")
|
||
is_lessor = fields.Boolean(string="出租方变更")
|
||
is_lessee = fields.Boolean(string="承租方变更")
|
||
|
||
def _get_default_tax_rate(self):
|
||
default_rate = self.env['yuthon.tariff.setting'].search(
|
||
[('name', '=', 0.05)], limit=1
|
||
)
|
||
return default_rate.id if default_rate else False
|
||
|
||
tax_rate = fields.Many2one('yuthon.tariff.setting', string="税率", default=_get_default_tax_rate)
|
||
use_address = fields.Char(string="财务专用地址")
|
||
not_tax_amount = fields.Float(string="不含税价")
|
||
tax = fields.Float(string="税金")
|
||
is_water = fields.Boolean(string="是否有水费")
|
||
is_electricity = fields.Boolean(string="是否有电费")
|
||
is_manage = fields.Boolean(string="是否有管理费")
|
||
miscellaneous = fields.Text(string="其他约定")
|
||
number = fields.Integer(string="次数", default=0)
|
||
total_area = fields.Float(string="总面积", digits=(16, 4))
|
||
not_received = fields.Float(string="未收", compute="_compute_contract_count")
|
||
month_received = fields.Float(string="本月已收")
|
||
actual_receivable = fields.Float(string="实际应收")
|
||
now_receivable = fields.Float(string="当期应收")
|
||
total_receivable = fields.Float(string="总已收")
|
||
comparison = fields.Selection([('outweigh', '大于'), ('equal', '等于'), ('less', '小于')], string="对比", compute='_compute_comparison')
|
||
progress_percentage = fields.Float(string="合同进度", compute="_compute_progress_percentage")
|
||
condition = fields.Selection([('new', '新建'), ('approval', '审批中'), ('cancel', '作废'), ('be_executed', '待执行'), ('executed', '已执行'), ('expired', '已终止')], string="合同审批状态", default='new')
|
||
contract_type = fields.Selection([('lease_renewal', '续租'), ('interim', '临租'), ('new', '补充协议'), ('first', '首租')], string="合同类型", default='first')
|
||
contract_id = fields.Many2one('property.lease.contract', string="原合同")
|
||
performance_bonds = fields.Float(string="经营履约保证金")
|
||
utility_bills = fields.Float(string="水电费押金")
|
||
renovation_deposit = fields.Float(string="装修押金")
|
||
lease_ids = fields.One2many('property.lease.contract.line', 'lease_id',string="物业信息明细行")
|
||
subset_ids = fields.One2many('property.subset.contract', 'subset_id',string="子集明细")
|
||
|
||
breakdown_ids = fields.One2many('property.rent.breakdown.line', 'breakdown_id', string="物业租金明细行")
|
||
water_breakdown_ids = fields.One2many('property.water.breakdown.line', 'breakdown_id', string="物业水费明细行")
|
||
ele_breakdown_ids = fields.One2many('property.ele.breakdown.line', 'breakdown_id', string="物业电费明细行")
|
||
management_breakdown_ids = fields.One2many('property.management.breakdown.line', 'breakdown_id', string="物业管理费明细行")
|
||
|
||
water_fee_ids = fields.One2many('property.water.fee.line', 'contract_id', string="物业水表明细行")
|
||
electricity_fee_ids = fields.One2many('property.electric.fee.line', 'contract_id', string="物业电表明细行")
|
||
other_ids = fields.One2many('property.other.fee.line', 'other_id', string="其他费用明细行")
|
||
lease_bank_ids = fields.One2many('property.bank.line', 'contract_id', string="承租方账户明细行")
|
||
bank_statements_ids = fields.One2many('receivable.modify.line', 'contract_id', string="银行流水明细行")
|
||
contact_state = fields.Selection([('to', '待执行' ), ('ing', '执行中'), ('terminate', '中止'), ('complete', '完成'), ('early_termination', '提前终止'), ('settle', '结清')], string='合同管理状态', default='to')
|
||
# 附件
|
||
license_ids = fields.Many2many('ir.attachment',
|
||
relation='property_lease_contract_license_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="证照")
|
||
power_attorney_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_power_attorney_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="授权委托书"
|
||
)
|
||
asset_valuation_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_asset_valuation_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="资产评估结果明细表"
|
||
)
|
||
lease_contract = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_lease_contract_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="合同/协议"
|
||
)
|
||
confirmation_transaction_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_confirmation_transaction_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="成交确认书"
|
||
)
|
||
contract_signature_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_signature_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="合同签名附件"
|
||
)
|
||
responsibility_book_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_responsibility_book_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="安全生产责任书(住宅)"
|
||
)
|
||
other_ment_ids = fields.Many2many(
|
||
'ir.attachment',
|
||
relation='property_lease_contract_other_rel',
|
||
column1='contract_id',
|
||
column2='attachment_id',
|
||
string="其他"
|
||
)
|
||
handover_time = fields.Date(string="交接日期")
|
||
contract_source_id = fields.Many2one('property.lease.contract', string="合同来源")
|
||
is_child_contract = fields.Boolean(string="是否子合同")
|
||
is_contract = fields.Boolean(string="是否主合同")
|
||
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
|
||
default='normal')
|
||
work_end = fields.Boolean(string="结束")
|
||
|
||
name = fields.Char(compute='_compute_name', store=True)
|
||
self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])])
|
||
title_information = fields.Text(string="产权证信息", compute='_compute_title_information')
|
||
contract_count = fields.Integer(string="联系人数量", compute='_compute_contract_count', store=True)
|
||
reconciliation_count = fields.Integer(string="对账数量", compute='_compute_reconciliation_count')
|
||
process_count = fields.Integer(string="流程数量", compute='_compute_process_count')
|
||
is_overdue = fields.Boolean(string="是否逾期", compute='_compute_contract_count', store=True)
|
||
is_issues = fields.Boolean(string='是否上会')
|
||
issues_result = fields.Html(string="会议结果")
|
||
related_issues_id = fields.Many2one('issue.library', string="关联议题")
|
||
|
||
# 催费通知单字段
|
||
fees_date = fields.Date(string="催费日期", compute='_compute_fees_all', store=True)
|
||
fees_rent = fields.Float(string="租金", compute='_compute_fees_all', store=True)
|
||
fees_water = fields.Float(string="水费", compute='_compute_fees_all', store=True)
|
||
fees_electricity = fields.Float(string="电费", compute='_compute_fees_all', store=True)
|
||
fees_manage = fields.Float(string="管理费", compute='_compute_fees_all', store=True)
|
||
fees_expenses = fields.Float(string="杂项费", compute='_compute_fees_all', store=True)
|
||
fees_bollards = fields.Float(string="灯箱费", compute='_compute_fees_all', store=True)
|
||
betweene_tubes = fields.Float(string="管子间", compute='_compute_fees_all', store=True)
|
||
fees_total = fields.Float(string="合计", compute='_compute_fees_all', store=True)
|
||
|
||
employee_ids = fields.Many2many('hr.employee', string="当前经办人", compute='_compute_workflow')
|
||
approve_state = fields.Selection([("start", "草稿"), ("pending", "审批中"), ("end", "结束")], string="审批状态", compute='_compute_workflow')
|
||
|
||
def refund_wizard(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', [])
|
||
)
|
||
line_vals = []
|
||
for contract in contract_ids:
|
||
line_vals.append((0, 0, {
|
||
'contract_id': contract.id,
|
||
}))
|
||
payment_unit_ids = []
|
||
for contract in contract_ids:
|
||
if contract.lessor_id:
|
||
payment_unit_ids.append(contract.lessor_id.id)
|
||
|
||
wizard = self.env['refund.wizard'].create({
|
||
'refund_line_ids': line_vals,
|
||
'payment_unit_ids': [(6, 0, payment_unit_ids)],
|
||
})
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': '批量退押',
|
||
'res_model': 'refund.wizard',
|
||
'view_mode': 'form',
|
||
'res_id': wizard.id,
|
||
'target': 'new',
|
||
}
|
||
|
||
def update_lessor_line(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for contract in contract_ids:
|
||
if not contract.lease_ids:
|
||
continue
|
||
first_lease_line = contract.lease_ids[0]
|
||
address_id = first_lease_line.address_id
|
||
manage_lines = self.env['manage.information.line'].search([
|
||
('yuthon_property_id', '=', address_id.id)
|
||
])
|
||
for manage_line in manage_lines:
|
||
# 跳过公司相同的情况
|
||
if manage_line.company_id == contract.lessor_id:
|
||
continue
|
||
existing_line = self.env['lessor.alteration.line'].search([
|
||
('contract_id', '=', contract.id),
|
||
('lessor_id', '=', manage_line.company_id.id),
|
||
], limit=1)
|
||
if not existing_line:
|
||
self.env['lessor.alteration.line'].create({
|
||
'contract_id': contract.id,
|
||
'lessor_id': manage_line.company_id.id,
|
||
'start_date': contract.term_start_time,
|
||
'end_date': manage_line.end_date
|
||
})
|
||
|
||
@api.onchange('term_start_time', 'term_end_time', 'total_area')
|
||
def onchange_lessor_id(self):
|
||
if not self.term_start_time:
|
||
return
|
||
if not self.lease_ids:
|
||
return
|
||
property_id = self.lease_ids[0].address_id.id
|
||
manage_line_domain = [
|
||
('yuthon_property_id', '=', property_id),
|
||
('start_time', '<=', self.term_start_time),
|
||
'|',
|
||
('end_date', '=', False),
|
||
('end_date', '>=', self.term_start_time)
|
||
]
|
||
manage_line = self.env['manage.information.line'].search(
|
||
manage_line_domain,
|
||
order='start_time desc',
|
||
limit=1
|
||
)
|
||
if manage_line and manage_line.company_id:
|
||
self.lessor_id = manage_line.company_id.id
|
||
|
||
if not (self.term_start_time and self.term_end_time and self.management_breakdown_ids):
|
||
return
|
||
breakdown_lines = []
|
||
for line in self.management_breakdown_ids:
|
||
breakdown_lines.append((1, line.id, {
|
||
'start_data': self.term_start_time,
|
||
'end_data': self.term_end_time,
|
||
'management_monthly_amount': line.management_unit_price * self.total_area
|
||
}))
|
||
self.management_breakdown_ids = breakdown_lines
|
||
|
||
@api.depends('code')
|
||
def _compute_workflow(self):
|
||
for record in self:
|
||
record.employee_ids = False
|
||
record.approve_state = False
|
||
workflow = self.env['soong.workflow.record'].search(
|
||
[('code', '=', record.code)],
|
||
limit=1
|
||
)
|
||
if workflow:
|
||
record.employee_ids = workflow.employee_ids
|
||
record.approve_state = workflow.approve_state
|
||
|
||
@api.depends()
|
||
def _compute_fees_all(self):
|
||
receivable_obj = self.env['property.accounts.receivable']
|
||
fee_mapping = {
|
||
'fees_rent': ['租金'],
|
||
'fees_water': ['水费'],
|
||
'fees_electricity': ['电费'],
|
||
'fees_manage': ['管理费'],
|
||
'fees_bollards': ['广告灯箱费'],
|
||
'betweene_tubes': ['管子间'],
|
||
'fees_expenses': ['人工费/日光灯款/代垫用料费']
|
||
}
|
||
|
||
for contract in self:
|
||
receivable_records = receivable_obj.search([
|
||
('submitted', '!=', 0),
|
||
('contract_id', '=', contract.id)
|
||
])
|
||
contract.fees_rent = 0.0
|
||
contract.fees_water = 0.0
|
||
contract.fees_electricity = 0.0
|
||
contract.fees_manage = 0.0
|
||
contract.fees_expenses = 0.0
|
||
contract.fees_bollards = 0.0
|
||
contract.betweene_tubes = 0.0
|
||
contract.fees_date = False
|
||
contract.fees_total = 0.0
|
||
|
||
if not receivable_records:
|
||
continue
|
||
all_due_dates = []
|
||
|
||
for field_name, target_names in fee_mapping.items():
|
||
filtered_recs = receivable_records.filtered(
|
||
lambda r: r.management_type_id and r.management_type_id.name in target_names
|
||
)
|
||
if filtered_recs:
|
||
setattr(contract, field_name, sum(filtered_recs.mapped('submitted')))
|
||
valid_dates = [d for d in filtered_recs.mapped('due_date') if d]
|
||
all_due_dates.extend(valid_dates)
|
||
|
||
if all_due_dates:
|
||
contract.fees_date = max(all_due_dates)
|
||
|
||
contract.fees_total = (
|
||
contract.fees_rent + contract.fees_water + contract.fees_electricity +
|
||
contract.fees_manage + contract.fees_expenses + contract.fees_bollards +
|
||
contract.betweene_tubes
|
||
)
|
||
|
||
def name_get(self):
|
||
"""
|
||
默认保持原来的显示(各处仍然用 display_name,例如 含版本号);
|
||
只有在 context 里带了 'show_lessor_in_contract_name' 时,
|
||
才在后面拼接出租方公司名称,并且不带版本号:
|
||
例如:租(2024) 86号(天河物管)
|
||
"""
|
||
# 先拿到原始 name_get 结果,保证不破坏默认行为
|
||
names = super(PropertyLeaseContract, self).name_get()
|
||
|
||
# 没有上下文标记 → 完全按原来返回(仍然是 租(2024) 86号(V1.0) 这种)
|
||
if not self.env.context.get('show_lessor_in_contract_name'):
|
||
return names
|
||
|
||
name_map = dict(names)
|
||
result = []
|
||
for rec in self:
|
||
# 这里只用合同编号,不带版本号
|
||
# contract_code 形如 “租(2024) 86号”
|
||
base_name = rec.contract_code or name_map.get(rec.id, rec.display_name or '')
|
||
# 出租方公司在 lessor_id 上
|
||
lessor_name = rec.lessor_id.display_name if rec.lessor_id else False
|
||
name = base_name
|
||
if lessor_name:
|
||
# 拼成:租(2024) 86号(天河物管)
|
||
name = '%s(%s)' % (base_name, lessor_name)
|
||
|
||
result.append((rec.id, name))
|
||
|
||
return result
|
||
|
||
@api.depends('payment_ids.print_date', 'payment_ids.is_print')
|
||
def _compute_is_print(self):
|
||
today = date.today()
|
||
start_date = today.replace(day=1)
|
||
end_date = start_date + relativedelta(months=1) - relativedelta(days=1)
|
||
for record in self:
|
||
printed_lines = record.payment_ids.filtered(
|
||
lambda line:
|
||
line.print_date and
|
||
start_date <= line.print_date <= end_date and
|
||
line.is_print is True
|
||
)
|
||
record.is_print = bool(printed_lines)
|
||
|
||
def refund_deposit(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in contract_ids:
|
||
if len(record) > 1:
|
||
raise UserError("只能选择一个合同")
|
||
if record.is_has_generated:
|
||
raise UserError("该合同已走应付账单流程")
|
||
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1)
|
||
return {
|
||
'name': '应付账单',
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'property.bills.payable',
|
||
'view_mode': 'form',
|
||
'target': 'current',
|
||
'context': {
|
||
'default_lease_contract_id': record.id,
|
||
'default_name': f'{record.contract_code}退押金申请',
|
||
'default_amount_payable': record.deposit,
|
||
'default_actual_amount': record.deposit,
|
||
},
|
||
}
|
||
|
||
def compute_is_has(self):
|
||
receivable_id = self.env['property.accounts.receivable'].search([('contract_id', '=', self.id),
|
||
('management_type_id.name', '=', '押金')
|
||
], limit=1)
|
||
if receivable_id:
|
||
if receivable_id.has_date:
|
||
self.is_has = True
|
||
else:
|
||
self.is_has = False
|
||
else:
|
||
self.is_has = False
|
||
|
||
@api.depends('users_id')
|
||
def _compute_company_id(self):
|
||
for record in self:
|
||
if record.users_id:
|
||
record.company_id = record.users_id.company_id.id
|
||
else:
|
||
record.company_id = None
|
||
|
||
def action_view_contract(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in contract_ids:
|
||
total_month_number = 0
|
||
if record.term_start_time and record.term_end_time:
|
||
delta = relativedelta(record.term_end_time, record.term_start_time)
|
||
total_month_number = delta.years * 12 + delta.months
|
||
if delta.days > 0:
|
||
total_month_number += 1
|
||
|
||
# 生成水费默认明细行
|
||
if record.is_water and not record.water_breakdown_ids:
|
||
self.env['property.water.breakdown.line'].create({
|
||
'breakdown_id': record.id,
|
||
'start_data': record.term_start_time,
|
||
'end_data': record.term_end_time,
|
||
'water_month_number': total_month_number,
|
||
})
|
||
|
||
# 生成电费默认明细行
|
||
if record.is_electricity and not record.ele_breakdown_ids:
|
||
self.env['property.ele.breakdown.line'].create({
|
||
'breakdown_id': record.id,
|
||
'start_data': record.term_start_time,
|
||
'end_data': record.term_end_time,
|
||
'ele_month_number': total_month_number,
|
||
})
|
||
|
||
# 生成管理费默认明细行
|
||
if record.lease_ids:
|
||
management_unit_price = record.lease_ids[0].message_standard_id.amount
|
||
management_monthly_amount = record.lease_ids[0].property_area * management_unit_price
|
||
else:
|
||
management_unit_price = 0
|
||
management_monthly_amount = 0
|
||
if record.is_manage and not record.management_breakdown_ids:
|
||
self.env['property.management.breakdown.line'].create({
|
||
'breakdown_id': record.id,
|
||
'start_data': record.term_start_time,
|
||
'end_data': record.term_end_time,
|
||
'management_unit_price': management_unit_price,
|
||
'management_monthly_amount': management_monthly_amount,
|
||
'management_month_number': total_month_number,
|
||
})
|
||
|
||
@api.onchange('is_issues')
|
||
def _onchange_is_issues(self):
|
||
if self.is_issues:
|
||
issue_id = self.env['issue.library'].create({'name': self.name, 'is_invisible_workflow': True})
|
||
self.related_issues_id = issue_id.id
|
||
self.env['issue.library.approval.line'].create({
|
||
'issue_library_id': issue_id.id,
|
||
'workflow_name': self.name,
|
||
'approval_type': self.workflow_id.name,
|
||
'res_model': self._name,
|
||
'res_id': self.id,
|
||
'code': self.code,
|
||
'approval_date': fields.Datetime.now(),
|
||
})
|
||
name_title = fields.Char(string="租赁合同审批")
|
||
|
||
contract_sort = fields.Selection([('property', '物业'), ('guanggao', '广告位'),('parking', '停车场'), ('guanzi', '管子间'), ('room', '机房'), ('base_station','基站'), ('other', '其他')], string="资产类型", default='property')
|
||
|
||
@api.depends('lease_ids.address_id.title_deed')
|
||
def _compute_title_information(self):
|
||
for record in self:
|
||
seen_titles = set()
|
||
unique_titles = []
|
||
for lease in record.lease_ids:
|
||
if lease.address_id and lease.address_id.title_deed:
|
||
title = lease.address_id.title_deed.strip()
|
||
if title and title not in seen_titles:
|
||
seen_titles.add(title)
|
||
unique_titles.append(title)
|
||
record.title_information = ', '.join(unique_titles)
|
||
|
||
@api.depends('lease_ids.address_id.admin_id', 'lease_ids.address_id.admin_id.department_id', 'lease_ids.address_id.tenure_type')
|
||
def _compute_admin_id(self):
|
||
for record in self:
|
||
if record.lease_ids:
|
||
record.admin_id = record.lease_ids[0].address_id.admin_id.id
|
||
record.tenure_type = record.lease_ids[0].address_id.tenure_type
|
||
|
||
@api.depends('admin_id')
|
||
def _compute_precinct_id(self):
|
||
for rec in self:
|
||
rec.precinct_id = False
|
||
if rec.admin_id:
|
||
precinct = self.env['yuthon.precinct.message'].search([('employee_id.user_id', '=', rec.admin_id.id)], limit=1)
|
||
rec.precinct_id = precinct.id if precinct else False
|
||
|
||
def contract_signature(self):
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': '合同签订',
|
||
"target": "new",
|
||
'res_model': 'contract.signature.wizard',
|
||
'view_mode': 'form',
|
||
'context': {'default_contract_id': self.id}
|
||
}
|
||
|
||
def _compute_contract_count(self):
|
||
for record in self:
|
||
current_date = datetime.now()
|
||
current_month_start = datetime(current_date.year, current_date.month, 1)
|
||
if current_date.month == 12:
|
||
next_month = 1
|
||
next_year = current_date.year + 1
|
||
else:
|
||
next_month = current_date.month + 1
|
||
next_year = current_date.year
|
||
current_month_end = datetime(next_year, next_month, 1) - timedelta(days=1)
|
||
record.contract_count = self.env['property.accounts.receivable'].search_count([('contract_id', '=', record.id)])
|
||
overdue_domain = [
|
||
('contract_id', '=', record.id),
|
||
('due_date', '<', current_month_start),
|
||
('submitted', '!=', 0)
|
||
]
|
||
has_overdue = self.env['property.accounts.receivable'].search_count(overdue_domain) > 0
|
||
record.is_overdue = has_overdue
|
||
not_received_domain = [
|
||
('contract_id', '=', record.id),
|
||
('submitted', '!=', 0),
|
||
('due_date', '<=', current_month_end),
|
||
('management_type_id.name', '!=', '违约金')
|
||
]
|
||
receivable_records = self.env['property.accounts.receivable'].search(not_received_domain)
|
||
record.not_received = sum(receivable_records.mapped('submitted'))
|
||
|
||
percentage_damages_domain = [
|
||
('contract_id', '=', record.id),
|
||
('submitted', '!=', 0),
|
||
('management_type_id.name', '=', '违约金'),
|
||
('due_date', '<=', current_month_end),
|
||
]
|
||
receivable_records = self.env['property.accounts.receivable'].search(percentage_damages_domain)
|
||
record.percentage_damages = sum(receivable_records.mapped('submitted'))
|
||
|
||
def _compute_reconciliation_count(self):
|
||
for record in self:
|
||
record.reconciliation_count = self.env['receivable.modify.line'].search_count([('receivable_id.contract_id', '=', self.id)])
|
||
|
||
def _compute_process_count(self):
|
||
for record in self:
|
||
record.process_count = self.env['soong.workflow.record'].search_count([('code', '=', record.code)])
|
||
|
||
def action_open_contract(self):
|
||
self.ensure_one()
|
||
action = self.env["ir.actions.actions"]._for_xml_id("yuthon_property_approval.property_accounts_receivable_action")
|
||
action.update({
|
||
'domain': [('contract_id', '=', self.id)],
|
||
'context': {
|
||
'search_default_group_by_management_type_id': 1,
|
||
'group_by': 'management_type_id'
|
||
}
|
||
})
|
||
return action
|
||
|
||
def action_open_reconciliation(self):
|
||
self.ensure_one()
|
||
action = self.env["ir.actions.actions"]._for_xml_id("yuthon_work_brand.action_receivable_modify_line_wbrand")
|
||
action.update({'domain': [('receivable_id.contract_id', '=', self.id),
|
||
]
|
||
})
|
||
return action
|
||
|
||
def action_open_process(self):
|
||
self.ensure_one()
|
||
action = self.env["ir.actions.actions"]._for_xml_id("soong_workflow.soong_workflow_other_record_action")
|
||
action.update({'domain': [('code', '=', self.code)]})
|
||
return action
|
||
|
||
def update_contract(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for rec in contract_ids:
|
||
if rec.lessor_line:
|
||
rec.write({'lessor_line': [(5,)]})
|
||
|
||
def update_admin_id(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for rec in contract_ids:
|
||
if rec.is_electricity and rec.electricity_fee_ids:
|
||
for fee in rec.electricity_fee_ids:
|
||
fee.line_loss = 0.02
|
||
if rec.is_water and rec.water_fee_ids:
|
||
for fee in rec.water_fee_ids:
|
||
fee.line_loss = 0.02
|
||
|
||
@api.onchange("lease_ids")
|
||
def onchange_lease_ids(self):
|
||
for rec in self:
|
||
start = rec.term_start_time
|
||
end = rec.term_end_time
|
||
rec.is_water = False
|
||
rec.is_electricity = False
|
||
rec.water_fee_ids = [(5, 0, 0)]
|
||
rec.electricity_fee_ids = [(5, 0, 0)]
|
||
rec.management_breakdown_ids = [(5, 0, 0)]
|
||
water_fee_values = []
|
||
message_fee_values = []
|
||
electricity_fee_values = []
|
||
|
||
# 用于去重的ID集合:记录已添加的水表/电表ID
|
||
added_water_meter_ids = set()
|
||
added_electric_meter_ids = set()
|
||
|
||
if rec.lease_ids:
|
||
# 处理每个租赁记录
|
||
for lease in rec.lease_ids:
|
||
if lease.address_id:
|
||
# 处理水表:增加去重逻辑
|
||
if lease.address_id.is_meter and lease.address_id.water_meter_ids:
|
||
active_water_meters = lease.address_id.water_meter_ids.filtered(
|
||
lambda m: not m.demolition_date or m.demolition_date > fields.Date.today()
|
||
)
|
||
for meter in active_water_meters:
|
||
if meter.property_meter_water_id and meter.property_meter_water_id.id not in added_water_meter_ids:
|
||
# 记录已添加的水表ID,防止重复
|
||
added_water_meter_ids.add(meter.property_meter_water_id.id)
|
||
water_fee_values.append([0, 0, {
|
||
'contract_id': rec.id,
|
||
'property_meter_water_id': meter.property_meter_water_id.id,
|
||
}])
|
||
|
||
# 处理电表
|
||
if lease.address_id.is_electricity_meter and lease.address_id.energy_meter_ids:
|
||
active_electric_meters = lease.address_id.energy_meter_ids.filtered(
|
||
lambda m: not m.demolition_date or m.demolition_date > fields.Date.today()
|
||
)
|
||
for meter in active_electric_meters:
|
||
# 确保meter记录有效 + 未被添加过
|
||
if meter.property_meter_archives_id and meter.property_meter_archives_id.id not in added_electric_meter_ids:
|
||
# 记录已添加的电表ID,防止重复
|
||
added_electric_meter_ids.add(meter.property_meter_archives_id.id)
|
||
electricity_fee_values.append([0, 0, {
|
||
'contract_id': rec.id,
|
||
'property_meter_archives_id': meter.property_meter_archives_id.id,
|
||
}])
|
||
|
||
if lease.address_id.is_message_fee and lease.address_id.message_standard_id:
|
||
if lease.address_id.rent_area > 0:
|
||
if start and end:
|
||
total_months = (end.year - start.year) * 12 + (end.month - start.month)
|
||
else:
|
||
total_months = 0
|
||
management_month_number = max(total_months, 0)
|
||
message_fee_values.append([0, 0, {
|
||
'breakdown_id': rec.id,
|
||
'management_unit_price': lease.address_id.message_standard_id.amount,
|
||
'management_monthly_amount': lease.address_id.message_standard_id.amount * lease.address_id.rent_area,
|
||
'start_data': rec.term_start_time,
|
||
'management_month_number': management_month_number + 1,
|
||
'management_period_rent': management_month_number * lease.address_id.message_standard_id.amount * lease.address_id.rent_area,
|
||
'end_data': rec.term_end_time,
|
||
}])
|
||
|
||
rec.is_manage = bool(message_fee_values)
|
||
rec.is_water = bool(water_fee_values)
|
||
rec.is_electricity = bool(electricity_fee_values)
|
||
if water_fee_values:
|
||
rec.water_fee_ids = water_fee_values
|
||
if electricity_fee_values:
|
||
rec.electricity_fee_ids = electricity_fee_values
|
||
if message_fee_values:
|
||
rec.management_breakdown_ids = message_fee_values
|
||
|
||
def all_end_approval(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for contract in contract_ids:
|
||
contract.end_approval()
|
||
logs_to_update = self.env["soong.workflow.log"].search([
|
||
("model", "=", self._name),
|
||
("res_id", "=", contract.id),
|
||
])
|
||
if logs_to_update:
|
||
last_log = logs_to_update[-1]
|
||
last_log.write({
|
||
"comment": " ",
|
||
"approval_state": "3",
|
||
"lase_click_date": fields.Datetime.now()
|
||
})
|
||
|
||
soong_record = self.env["soong.workflow.record"].search([
|
||
("code", "=", contract.code),
|
||
], limit=1)
|
||
if soong_record:
|
||
soong_record.sudo().write({
|
||
'node_id' : False,
|
||
'employee_ids': [(5,)],
|
||
'soong_state': 'done',
|
||
'approve_state' : 'end',
|
||
})
|
||
|
||
def end_approval(self):
|
||
current_date = date.today()
|
||
# 续租审批应退押金逻辑
|
||
if self.contract_id and self.contract_id.deposit and self.contract_type == 'lease_renewal':
|
||
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1)
|
||
bills_list = self.env['property.bills.list'].search([
|
||
('contract_id', '=', self.contract_id.id,)
|
||
])
|
||
workflow_record_id = self.env["soong.workflow.record"].search(
|
||
[("res_model", "=", self._name), ("res_id", "=", self.id)])
|
||
if not bills_list:
|
||
self.env['property.bills.list'].create({
|
||
'lessor_id': self.contract_id.lessor_id.id,
|
||
'due_date': self.contract_id.actual_end_time + timedelta(days=1), # 原合同
|
||
'management_type_id': management_type_id.id,
|
||
'amount': self.contract_id.deposit,
|
||
'contract_id': self.contract_id.id,
|
||
'remake': '续签退押',
|
||
'workflow_record_id': workflow_record_id.id,
|
||
})
|
||
bank_id = self.env['property.bank.statements'].search([
|
||
('name_account', '=', self.contract_id.lessee_id.name),
|
||
('contract_ids', 'in', self.id),
|
||
('remake', '=', '续签退押'),
|
||
])
|
||
if not bank_id:
|
||
lessor_id = self.contract_id.lessor_id # 出租方
|
||
tenant_line = self.env['property.tenant.line'].search([
|
||
('tenant_id', '=', self.lessee_id.id),
|
||
('lease_contract_id', '=', self.id),
|
||
], limit=1)
|
||
if not tenant_line:
|
||
self.env['property.tenant.line'].create({
|
||
'tenant_id': self.lessee_id.id,
|
||
'lease_contract_id': self.id,
|
||
})
|
||
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
|
||
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)
|
||
bank_accounts = self.env['property.bank.line'].search([
|
||
('tenant_id', '=', self.lessee_id.id)
|
||
]).mapped('bank')
|
||
bank_accounts = [acc for acc in bank_accounts if acc][0]
|
||
end_date = self.contract_id.actual_end_time + timedelta(days=1)
|
||
start_time = f"{end_date} 09:00:00"
|
||
bank_id = self.env['property.bank.statements'].sudo().create({
|
||
'start_time': start_time,
|
||
'name': bank_accounts,
|
||
'original_amount': self.contract_id.deposit,
|
||
'name_account': self.contract_id.lessee_id.name,
|
||
'remake': '续签退押',
|
||
'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()
|
||
|
||
if self.term_start_time <= current_date <= self.term_end_time:
|
||
new_contact_state = 'ing'
|
||
old_contact_state = 'terminate'
|
||
else:
|
||
new_contact_state = 'to'
|
||
self.write({
|
||
'condition': 'be_executed',
|
||
'contact_state': new_contact_state,
|
||
'actual_start_time': self.term_start_time,
|
||
'actual_end_time': self.term_end_time,
|
||
})
|
||
old_contract_id = self.env['property.lease.contract'].search([
|
||
('contract_code', '=', self.contract_code),
|
||
('id', '!=', self.id),
|
||
])
|
||
if old_contract_id:
|
||
if old_contact_state:
|
||
old_contract_id.write({'contact_state': old_contact_state})
|
||
|
||
for contract in self:
|
||
address_ids = contract.lease_ids.mapped('address_id').filtered(lambda addr: addr.id)
|
||
if not address_ids:
|
||
continue
|
||
|
||
if not contract.actual_start_time:
|
||
continue
|
||
start_date = contract.actual_start_time
|
||
next_month = start_date.month + 1
|
||
next_year = start_date.year
|
||
if next_month > 12:
|
||
next_month = 1
|
||
next_year += 1
|
||
|
||
meter_lines = self.env['meter.archives.line'].search([
|
||
('yuthon_property_ids', 'in', address_ids.ids),
|
||
('meter_date', '>=', f'{next_year}-{next_month:02d}-01') # 下个月1号及之后
|
||
])
|
||
if not meter_lines:
|
||
continue
|
||
meter_lines.write({
|
||
'lessee_id': contract.lessee_id.id,
|
||
'contract_id': contract.id
|
||
})
|
||
contract._update_contract_lines(contract)
|
||
|
||
def open_record(self):
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'property.lease.contract',
|
||
'res_id': self.id,
|
||
'view_mode': 'form',
|
||
}
|
||
|
||
@api.depends('contract_code', 'name', 'contract_type', 'property_name')
|
||
def _compute_name(self):
|
||
contract_dict = {
|
||
'lease_renewal': '续租',
|
||
'interim': '临租',
|
||
'new': '补充协议',
|
||
'first': '首租'
|
||
}
|
||
for record in self:
|
||
if record.contract_code and record.property_name and record.contract_type:
|
||
record.name = f"{record.contract_code}{contract_dict.get(record.contract_type)}{record.property_name}"
|
||
elif record.contract_code and record.property_name:
|
||
record.name = f"{record.contract_code}{record.property_name}"
|
||
elif record.name:
|
||
record.name = record.property_name
|
||
else:
|
||
record.name = self._description
|
||
|
||
def update_property(self):
|
||
empty_contract_lines = self.env['property.contract.line'].search([('lease_contract_id', '=', False)])
|
||
if empty_contract_lines:
|
||
empty_contract_lines.unlink()
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for contract in contract_ids:
|
||
self._update_contract_lines(contract)
|
||
|
||
def _update_contract_lines(self, contract):
|
||
"""
|
||
处理单个合同的明细行更新
|
||
:param contract: 物业租赁合同记录
|
||
"""
|
||
for record in contract.lease_ids:
|
||
property_addr = record.address_id
|
||
if not property_addr:
|
||
continue
|
||
existing_line = self.env['property.contract.line'].search([
|
||
('yuthon_property_id', '=', property_addr.id),
|
||
('lease_contract_id', '=', contract.id)
|
||
], limit=1)
|
||
property_addr.lessee_id = contract.lessee_id.id
|
||
# 准备要写入的数据
|
||
contract_data = {
|
||
'yuthon_property_id': property_addr.id,
|
||
'lease_contract_id': contract.id,
|
||
'lessee_id': contract.lessee_id.id,
|
||
'rent': contract.rent,
|
||
'start_time': contract.actual_start_time,
|
||
'end_date': contract.actual_end_time,
|
||
}
|
||
|
||
if existing_line:
|
||
# 如果存在则更新
|
||
existing_line.write(contract_data)
|
||
else:
|
||
# 如果不存在则追加
|
||
property_addr.write({
|
||
'property_contract_ids': [(0, 0, contract_data)]
|
||
})
|
||
|
||
# 更新承租方合同信息
|
||
lessee_contract = self.env['property.tenant.line'].search([
|
||
('tenant_id', '=', contract.lessee_id.id),
|
||
('lease_contract_id', '=', contract.id)
|
||
], limit=1)
|
||
|
||
lessee_date = {
|
||
'tenant_id': contract.lessee_id.id,
|
||
'lease_contract_id': contract.id,
|
||
}
|
||
|
||
if not lessee_contract:
|
||
lessee_contract.write({
|
||
'property_contract_ids': [(0, 0, lessee_date)]
|
||
})
|
||
|
||
contract_lines = self.env['property.contract.line'].search([
|
||
('lease_contract_id', '=', contract.id)
|
||
])
|
||
|
||
for line in contract_lines:
|
||
# 从明细行中获取关键信息
|
||
start_time = line.start_time
|
||
end_date = line.end_date
|
||
yuthon_property_id = line.yuthon_property_id.id
|
||
|
||
if not (start_time and yuthon_property_id):
|
||
continue
|
||
|
||
# 获取当前物业记录
|
||
property_addr = self.env['yuthon.property'].browse(yuthon_property_id)
|
||
if not property_addr:
|
||
continue
|
||
|
||
date_domain = [
|
||
('meter_date', '>=', start_time)
|
||
]
|
||
if end_date:
|
||
date_domain.append(('meter_date', '<=', end_date))
|
||
|
||
# 处理水表
|
||
if property_addr.is_meter:
|
||
water_lines = self.env['meter.water.line'].search([
|
||
('yuthon_property_ids', 'in', [yuthon_property_id]),
|
||
*date_domain
|
||
])
|
||
for water_line in water_lines:
|
||
water_line.write({'lessee_id': line.lessee_id.id})
|
||
|
||
if property_addr.is_electricity_meter:
|
||
electric_lines = self.env['meter.archives.line'].search([
|
||
('yuthon_property_ids', 'in', [yuthon_property_id]),
|
||
*date_domain
|
||
])
|
||
for electric_line in electric_lines:
|
||
electric_line.write({'lessee_id': line.lessee_id.id})
|
||
|
||
@api.onchange('lessee_id')
|
||
def _onchange_lessee_id(self):
|
||
for record in self:
|
||
if record.lessee_id:
|
||
lease_bank_ids = self.env['property.bank.line'].search([('tenant_id', '=', record.lessee_id.id)])
|
||
record.lease_bank_ids = lease_bank_ids.ids
|
||
|
||
def set_actual_time_to_contract_time(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for contract in contract_ids:
|
||
contract._compute_end_comparison()
|
||
|
||
@api.depends('not_received', 'now_receivable')
|
||
def _compute_comparison(self):
|
||
for record in self:
|
||
if record.not_received and record.now_receivable:
|
||
if record.not_received > record.now_receivable:
|
||
record.comparison = 'outweigh'
|
||
elif record.not_received < record.now_receivable:
|
||
record.comparison = 'less'
|
||
else:
|
||
record.comparison = 'equal'
|
||
else:
|
||
record.comparison = None
|
||
|
||
|
||
def update_color(self):
|
||
button_ids = self.env["soong.workflow.node.button"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
|
||
@api.depends('actual_end_time', 'term_start_time', 'term_end_time')
|
||
def _compute_end_comparison(self):
|
||
for record in self:
|
||
now = date.today()
|
||
if record.actual_end_time and record.term_start_time:
|
||
if record.actual_end_time > now:
|
||
delta = record.actual_end_time - now
|
||
if 60<= delta.days < 90:
|
||
record.end_comparison = 'blue'
|
||
elif 30<= delta.days< 60:
|
||
record.end_comparison = 'yellow'
|
||
elif delta.days < 30:
|
||
record.end_comparison = 'red'
|
||
else:
|
||
record.end_comparison = None
|
||
else:
|
||
record.end_comparison = None
|
||
if record.term_end_time:
|
||
if record.actual_end_time < record.term_end_time:
|
||
record.end_comparison = 'red'
|
||
else:
|
||
record.end_comparison = None
|
||
|
||
if record.actual_end_time == record.term_start_time:
|
||
record.is_equal = True
|
||
else:
|
||
record.end_comparison = None
|
||
|
||
@api.depends('actual_end_time')
|
||
def _compute_expiration_coming(self):
|
||
for record in self:
|
||
if record.actual_end_time:
|
||
delta = record.actual_end_time - date.today()
|
||
if 0 < delta.days < 90:
|
||
record.expiration_coming = 'coming'
|
||
elif delta.days <=0:
|
||
record.expiration_coming = 'due'
|
||
else:
|
||
record.expiration_coming = None
|
||
else:
|
||
record.expiration_coming = None
|
||
|
||
|
||
@api.onchange('total_area', 'rent')
|
||
def _onchange_unit_price(self):
|
||
for i in self:
|
||
if i.total_area and i.rent:
|
||
i.unit_price = i.rent / i.total_area
|
||
|
||
@api.depends('breakdown_ids')
|
||
def _compute_total_contract_receivables(self):
|
||
for record in self:
|
||
record.total_contract_receivables = sum(line.period_rent for line in record.breakdown_ids)
|
||
|
||
# @api.onchange("term_start_time", "term_end_time",
|
||
# "increments_every_few_years", "unit_price",
|
||
# "first_years_begin", "increment_method",
|
||
# "increment_price", "increment_percentage", "increment_amount",
|
||
# "rent_free_period_year", "rent_free_period_month", "total_area")
|
||
# def onchange_first_year_rent(self):
|
||
# for record in self:
|
||
# if record.term_start_time and record.term_end_time:
|
||
# start_date = record.term_start_time
|
||
# end_date = record.term_end_time + relativedelta(days=1)
|
||
# if start_date + relativedelta(months=12) < end_date:
|
||
# end_date = start_date + relativedelta(months=12)
|
||
# all_period_rent = record.my_compute_all_period_rent(start_date, end_date)
|
||
# record.first_year_rent = all_period_rent
|
||
# all_start_date = record.term_start_time
|
||
# all_end_date = record.term_end_time + relativedelta(days=1)
|
||
# record.total_contract_receivables = record.my_compute_all_period_rent(all_start_date, all_end_date)
|
||
|
||
@api.onchange("lease_term_year", "lease_term_month", "term_start_time" , "lease_term_day")
|
||
def onchange_lease_term_year(self):
|
||
for record in self:
|
||
if record.lease_term_year < 2:
|
||
record.is_less_than_2 = True
|
||
else:
|
||
record.is_less_than_2 = False
|
||
if record.lease_term_year or record.lease_term_month:
|
||
if not record.lease_term_day:
|
||
if record.term_start_time:
|
||
year = record.lease_term_year if record.lease_term_year else 0
|
||
month = record.lease_term_month if record.lease_term_month else 0
|
||
months = year * 12 + month
|
||
record.term_end_time = record.term_start_time + relativedelta(months=months) - relativedelta(days=1)
|
||
else:
|
||
if record.term_start_time:
|
||
year = record.lease_term_year if record.lease_term_year else 0
|
||
month = record.lease_term_month if record.lease_term_month else 0
|
||
months = year * 12 + month
|
||
record.term_end_time = record.term_start_time + relativedelta(months=months) - relativedelta(days=1) + relativedelta(days=record.lease_term_day)
|
||
|
||
@api.onchange("total_contract_receivables", "tax_rate")
|
||
def onchange_total_contract_receivables(self):
|
||
for record in self:
|
||
if record.total_contract_receivables and record.tax_rate:
|
||
record.not_tax_amount = record.total_contract_receivables / (1 + record.tax_rate.ratio)
|
||
record.tax = record.total_contract_receivables - record.not_tax_amount
|
||
|
||
@staticmethod
|
||
def divide_time_range_fixed(start_time, end_time, months):
|
||
time_ranges = []
|
||
while start_time < end_time:
|
||
end_of_range = start_time + relativedelta(months=months)
|
||
if end_of_range > end_time:
|
||
end_of_range = end_time
|
||
time_range = [start_time, end_of_range]
|
||
time_ranges.append(time_range)
|
||
start_time = end_of_range
|
||
return time_ranges
|
||
|
||
@api.onchange('cycle_dict')
|
||
def onchange_cycle_dict(self):
|
||
if self.cycle_dict == 'monthly':
|
||
self.cycle_mode = 'fixed_monthly'
|
||
elif self.cycle_dict == 'quarter':
|
||
self.cycle_mode = 'fixed_quarter'
|
||
elif self.cycle_dict == 'year':
|
||
self.cycle_mode = 'fixed_year'
|
||
|
||
# @staticmethod
|
||
# def divide_time_range_floating(start_time, end_time, months):
|
||
# time_ranges = []
|
||
# while start_time < end_time:
|
||
# start_time = start_time.replace(day=1)
|
||
# end_of_range = start_time + relativedelta(months=months)
|
||
# time_range = [start_time, end_of_range - relativedelta(days=1)]
|
||
# time_ranges.append(time_range)
|
||
# start_time = end_of_range
|
||
# return time_ranges
|
||
|
||
# def my_compute_all_period_rent(self, start_date, end_date):
|
||
# first_years_begin = self.first_years_begin if self.first_years_begin else 0
|
||
# increments_every_few_years = self.increments_every_few_years
|
||
# monthly_amount = self.total_area * self.unit_price # 月租金
|
||
# unit_price = self.unit_price # 单价
|
||
# rent_free_period_month = 0
|
||
# # 先计算免租期
|
||
# # if self.rent_free_period_year:
|
||
# # rent_free_period_month += self.rent_free_period_year * 12
|
||
# if self.rent_free_period_month:
|
||
# rent_free_period_month += self.rent_free_period_month
|
||
# free_start = start_date # 免租期开始
|
||
# if rent_free_period_month:
|
||
# free_start = start_date + relativedelta(months=rent_free_period_month)
|
||
#
|
||
# all_period_rent = 0
|
||
# if not increments_every_few_years:
|
||
# # 计算剩余日期
|
||
# delta = relativedelta(end_date, free_start)
|
||
# months = delta.years * 12 + delta.months
|
||
# all_period_rent += monthly_amount * months
|
||
# else:
|
||
# # 不递增年份的计算
|
||
# increments_start = free_start # 平稳和递增开始
|
||
# all_months = 0 # 第几月份
|
||
# # 有免递增年份的计算
|
||
# if first_years_begin:
|
||
# first_index = 0 # 第几递增周期年
|
||
# divided_year_segments = self.divide_time_range_fixed(start_date, end_date, 12)
|
||
# for segment_year in divided_year_segments:
|
||
# first_index += 1
|
||
# increments_start = segment_year[1]
|
||
# if first_years_begin and first_index >= first_years_begin:
|
||
# # 进入 递增时间 跳出平稳计费极端
|
||
# break
|
||
# delta = relativedelta(segment_year[1], segment_year[0])
|
||
# months = delta.years * 12 + delta.months
|
||
# for month in range(months):
|
||
# all_months += 1
|
||
# # 判断是否是免租期
|
||
# if all_months > rent_free_period_month:
|
||
# all_period_rent += monthly_amount
|
||
# # 开始递增
|
||
# divided_increments_segments = self.divide_time_range_fixed(increments_start, end_date, increments_every_few_years * 12)
|
||
# index = 0 # 第几递增周期年
|
||
# for increments_year in divided_increments_segments:
|
||
# index += 1
|
||
# """
|
||
# increment_percentage = fields.Float(string="递增百分比")
|
||
# increment_price = fields.Float(string="递增单价")
|
||
# increment_amount = fields.Float(string="递增金额")
|
||
# """
|
||
# increments_month = 0
|
||
# if self.increment_method == "increment_price" and self.increment_value:
|
||
# unit_price = unit_price + self.increment_value
|
||
# monthly_amount = self.total_area * unit_price
|
||
# if self.increment_method == "percentage" and self.increment_value:
|
||
# monthly_amount = monthly_amount * self.increment_value
|
||
# if self.increment_method == "increment_amount" and self.increment_value:
|
||
# monthly_amount = monthly_amount + self.increment_value
|
||
# delta = relativedelta(increments_year[1], increments_year[0])
|
||
# months = delta.years * 12 + delta.months
|
||
# # 判断第几月份进入 付费租期
|
||
# for month in range(months):
|
||
# all_months += 1
|
||
# # 判断是否是免租期
|
||
# if all_months > rent_free_period_month:
|
||
# increments_month += 1
|
||
# if increments_month:
|
||
# all_period_rent += monthly_amount * increments_month
|
||
# return all_period_rent
|
||
|
||
def action_generate_rental_charges(self):
|
||
"""
|
||
跳转租金费用弹窗
|
||
:return:
|
||
"""
|
||
return {
|
||
'name': '租金费用弹窗',
|
||
'type': 'ir.actions.act_window',
|
||
'view_mode': 'form',
|
||
'res_model': 'property.rental.wizard',
|
||
'target': 'new',
|
||
'context': {
|
||
'default_lease_contract_id': self.id,
|
||
}
|
||
}
|
||
|
||
def action_open_contract_rental_wizard(self):
|
||
"""指定合同生成租金弹窗"""
|
||
property_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
return {
|
||
'name': '生成租金',
|
||
'type': 'ir.actions.act_window',
|
||
'view_mode': 'form',
|
||
'res_model': 'property.contract.rental.wizard',
|
||
'target': 'new',
|
||
'context': {
|
||
'default_contract_ids': property_ids.ids,
|
||
},
|
||
}
|
||
|
||
|
||
def action_generate_administrative_expenses(self):
|
||
"""
|
||
跳转租金费用弹窗
|
||
:return:
|
||
"""
|
||
return {
|
||
'name': '租金费用弹窗',
|
||
'type': 'ir.actions.act_window',
|
||
'view_mode': 'form',
|
||
'res_model': 'property.rental.wizard',
|
||
'target': 'new',
|
||
'context': {
|
||
'default_lease_contract_id': self.id,
|
||
}
|
||
}
|
||
|
||
@api.onchange("term_start_time", "term_end_time", "rent",
|
||
"increments_every_few_years", "unit_price",
|
||
"first_years_begin", "increment_method", "increment_value",
|
||
"rent_free_period_month", "rent_free_period_days", "total_area")
|
||
def _onchange_action_generate_details(self):
|
||
"""
|
||
生产租金明细
|
||
:return:
|
||
"""
|
||
# todo 还没有计算递增递减
|
||
# todo 还没有计算首月优惠
|
||
# todo 还没有计算开始和结束不足月份和季度的计算比例
|
||
|
||
for record in self:
|
||
if record.term_start_time and record.term_end_time:
|
||
record.write({'breakdown_ids': [(5, 0, 0)]})
|
||
start_date = record.term_start_time
|
||
end_date = record.term_end_time + relativedelta(days=1)
|
||
record.do_generate_details_core(start_date, end_date, True)
|
||
|
||
# 计算首年租金
|
||
first_year_end_date = start_date + relativedelta(months=12)
|
||
if first_year_end_date > end_date:
|
||
first_year_end_date = end_date
|
||
all_period_rent = record.do_generate_details_core(start_date, first_year_end_date, False)
|
||
record.first_year_rent = all_period_rent
|
||
|
||
def all_action_generate_details(self):
|
||
property_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in property_ids:
|
||
if record.term_start_time and record.term_end_time:
|
||
record.write({'breakdown_ids': [(5, 0, 0)]})
|
||
start_date = record.term_start_time
|
||
end_date = record.term_end_time + relativedelta(days=1)
|
||
record.do_generate_details_core(start_date, end_date, True)
|
||
# 计算首年租金
|
||
first_year_end_date = start_date + relativedelta(months=12)
|
||
if first_year_end_date > end_date:
|
||
first_year_end_date = end_date
|
||
all_period_rent = record.do_generate_details_core(start_date, first_year_end_date, False)
|
||
record.first_year_rent = all_period_rent
|
||
|
||
def do_generate_details_core(self, start_date, end_date, write_db=False):
|
||
all_period_rent = 0
|
||
|
||
unit_price = self.unit_price # 单价
|
||
unit_price = float_round(unit_price, precision_digits=2, rounding_method='HALF-UP')
|
||
monthly_amount = self.rent
|
||
monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
|
||
|
||
# 如果有免租期 月 时间进行到 start_date
|
||
free_start_dict = {}
|
||
free_start = start_date
|
||
if self.rent_free_period_month:
|
||
free_start = start_date + relativedelta(months=self.rent_free_period_month)
|
||
if free_start > end_date:
|
||
free_start = end_date
|
||
delta = relativedelta(free_start, start_date)
|
||
month_number = delta.years * 12 + delta.months
|
||
if delta.days:
|
||
month_number += 1
|
||
free_start_dict = {
|
||
"unit_price": unit_price,
|
||
"monthly_amount": monthly_amount,
|
||
"breakdown_id": self.id,
|
||
"start_data": start_date,
|
||
"end_data": free_start - relativedelta(days=1),
|
||
"month_number": month_number,
|
||
"period_rent": 0,
|
||
}
|
||
|
||
|
||
free_day_start = free_start
|
||
|
||
add_year_index = 0 # 第几年
|
||
year_month_index = 0 # 第几个月
|
||
|
||
|
||
free_start_day_dict = {}
|
||
monthly_amount_last_dict = {}
|
||
monthly_amount_new_dict = {}
|
||
|
||
# 说明存在免增期 单独计算
|
||
if start_date < end_date:
|
||
one_years_list = self.divide_time_range_fixed(start_date, end_date, 12)
|
||
for one_year in one_years_list:
|
||
add_year_index += 1
|
||
# 是否存在免增期
|
||
first_years_begin = self.first_years_begin if self.first_years_begin else 0
|
||
# 存在递增期 并且进入递增金额计算
|
||
if self.increments_every_few_years > 0 and add_year_index >= first_years_begin:
|
||
if add_year_index % self.increments_every_few_years == 0:
|
||
# 单价和金额计算
|
||
if self.increment_method == "increment_price" and self.increment_value:
|
||
unit_price = unit_price + self.increment_value
|
||
unit_price = float_round(unit_price, precision_digits=2, rounding_method='HALF-UP')
|
||
monthly_amount = self.total_area * unit_price
|
||
monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
if self.increment_method == "percentage" and self.increment_value:
|
||
unit_price = unit_price * (1 + (self.increment_value / 100))
|
||
unit_price = float_round(unit_price, precision_digits=2, rounding_method='HALF-UP')
|
||
monthly_amount = self.total_area * unit_price
|
||
monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
if self.increment_method == "increment_amount" and self.increment_value:
|
||
monthly_amount = monthly_amount + self.increment_value
|
||
monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
unit_price = monthly_amount / self.total_area
|
||
unit_price = float_round(unit_price, precision_digits=2, rounding_method='HALF-UP')
|
||
# 开始时间, 结束时间
|
||
one_year_start = one_year[0]
|
||
one_year_end = one_year[1]
|
||
one_month_list = self.divide_time_range_fixed(one_year_start, one_year_end, 1)
|
||
for one_month in one_month_list:
|
||
period_rent = round(monthly_amount)
|
||
# 所有月统计
|
||
year_month_index += 1
|
||
# 如果开始时间 是 免租期月的结束时间,那么进入免租期日
|
||
if one_month[0] == free_start:
|
||
# 如果免租期有天那么默认增加一个月
|
||
if self.rent_free_period_days:
|
||
# 免租日的截至日期
|
||
free_day_start = one_month[1]
|
||
# 计算这个月的天数
|
||
total_days = (one_month[1] - one_month[0]).days
|
||
current_no_free = (total_days - self.rent_free_period_days) / total_days
|
||
period_rent = round(monthly_amount * current_no_free) if monthly_amount * current_no_free > 0 else 0
|
||
# 计算 免租期天的比值
|
||
free_start_day_dict = {
|
||
"unit_price": unit_price,
|
||
"monthly_amount": monthly_amount,
|
||
"breakdown_id": self.id,
|
||
"start_data": one_month[0],
|
||
"end_data": one_month[1] - relativedelta(days=1),
|
||
"month_number": 1,
|
||
"period_rent": period_rent,
|
||
}
|
||
all_period_rent += period_rent
|
||
|
||
# 已经跳出 免租期了 开始计费
|
||
if one_month[0] >= free_day_start:
|
||
current_days = calendar.monthrange(one_month[0].year, one_month[0].month)[1]
|
||
total_days = (one_month[1] - one_month[0]).days
|
||
# 说明不足月 单独计算
|
||
if current_days > total_days:
|
||
current_no_free = total_days / current_days
|
||
period_rent = round(monthly_amount * current_no_free) if monthly_amount * current_no_free > 0 else 0
|
||
monthly_amount_last_dict = {
|
||
"unit_price": unit_price,
|
||
"monthly_amount": monthly_amount,
|
||
"breakdown_id": self.id,
|
||
"start_data": one_month[0],
|
||
"end_data": one_month[1] - relativedelta(days=1),
|
||
"month_number": 1,
|
||
"period_rent": period_rent,
|
||
}
|
||
# 足月的单独进行计算
|
||
else:
|
||
if str(monthly_amount) in monthly_amount_new_dict:
|
||
amount_dict = monthly_amount_new_dict.get(str(monthly_amount))
|
||
old_period_rent = amount_dict.get("period_rent")
|
||
old_start_data = amount_dict.get("start_data")
|
||
old_end_data = amount_dict.get("end_data")
|
||
|
||
start_data = min(old_start_data, one_month[0])
|
||
end_data = max(old_end_data, one_month[1])
|
||
|
||
delta = relativedelta(end_data, start_data)
|
||
month_number = delta.years * 12 + delta.months
|
||
if delta.days:
|
||
month_number += 1
|
||
|
||
monthly_amount_new_dict[str(monthly_amount)] = {
|
||
"unit_price": unit_price,
|
||
"monthly_amount": monthly_amount,
|
||
"breakdown_id": self.id,
|
||
"start_data": start_data,
|
||
"end_data": end_data - relativedelta(days=1),
|
||
"month_number": month_number,
|
||
"period_rent": old_period_rent + period_rent,
|
||
}
|
||
else:
|
||
monthly_amount_new_dict[str(monthly_amount)] = {
|
||
"unit_price": unit_price,
|
||
"monthly_amount": monthly_amount,
|
||
"breakdown_id": self.id,
|
||
"start_data": one_month[0],
|
||
"end_data": one_month[1] - relativedelta(days=1),
|
||
"month_number": 1,
|
||
"period_rent": period_rent,
|
||
}
|
||
all_period_rent += period_rent
|
||
if write_db:
|
||
# 是否免租期 月
|
||
if free_start_dict:
|
||
self.sudo().write({'breakdown_ids': [(0, 0, free_start_dict)]})
|
||
|
||
# 是否免租期 日
|
||
if free_start_day_dict:
|
||
self.sudo().write({'breakdown_ids': [(0, 0, free_start_day_dict)]})
|
||
|
||
# 开始统计月份阶段金额
|
||
for k, v in monthly_amount_new_dict.items():
|
||
self.sudo().write({'breakdown_ids': [(0, 0, v)]})
|
||
|
||
# 最后有不足月的单独计算
|
||
if monthly_amount_last_dict:
|
||
self.sudo().write({'breakdown_ids': [(0, 0, monthly_amount_last_dict)]})
|
||
|
||
return all_period_rent
|
||
|
||
# def do_generate_details_core_1(self, start_date, end_date):
|
||
# """
|
||
# 计算核心算法
|
||
# :return:
|
||
# """
|
||
# all_period_rent = 0 # 总金额
|
||
# monthly_amount = self.total_area * self.unit_price
|
||
# monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
# # 月租金
|
||
# unit_price = self.unit_price # 单价
|
||
#
|
||
# # 如果有免租期 月 时间进行到 start_date
|
||
# free_start = start_date
|
||
# if self.rent_free_period_month:
|
||
# free_start = start_date + relativedelta(months=self.rent_free_period_month)
|
||
# if free_start > end_date:
|
||
# free_start = end_date
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": start_date,
|
||
# "end_data": free_start - relativedelta(days=1),
|
||
# "month_number": self.rent_free_period_month,
|
||
# "period_rent": 0,
|
||
# })]})
|
||
#
|
||
# # 如果没有递增 那么计算全部
|
||
# if not self.increments_every_few_years:
|
||
# # 如果免租期有天那么默认增加一个月
|
||
# # 如果有免增期 日
|
||
# free_start_day = free_start
|
||
# if self.rent_free_period_days:
|
||
# free_start_day = free_start + relativedelta(months=self.rent_free_period_days)
|
||
# if free_start_day < end_date:
|
||
# # 计算这个月的天数
|
||
# current_days = calendar.monthrange(free_start.year, free_start.month)[1]
|
||
# current_no_free = (current_days - self.rent_free_period_days) / current_days
|
||
# period_rent = monthly_amount * 1 * current_no_free if monthly_amount * 1 * current_no_free > 0 else 0
|
||
# # 计算 免租期天的比值
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": free_start,
|
||
# "end_data": free_start_day - relativedelta(days=1),
|
||
# "month_number": 1,
|
||
# "period_rent": period_rent,
|
||
# })]})
|
||
# all_period_rent += period_rent
|
||
#
|
||
# # 计算 剩余日期的金额
|
||
# if free_start_day < end_date:
|
||
# delta = relativedelta(end_date, free_start_day)
|
||
# months = delta.years * 12 + delta.months
|
||
# free_start_day_end = free_start_day + relativedelta(months=months)
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": free_start_day,
|
||
# "end_data": free_start_day_end - relativedelta(days=1),
|
||
# "month_number": months,
|
||
# "period_rent": monthly_amount * months,
|
||
# })]})
|
||
# all_period_rent += monthly_amount * months
|
||
# # 说明最后还有几天
|
||
# if delta.days > 0:
|
||
# current_days = calendar.monthrange(free_start_day_end.year, free_start_day_end.month)[1]
|
||
# current_no_free = delta.days / current_days
|
||
# period_rent = monthly_amount * 1 * current_no_free if monthly_amount * 1 * current_no_free > 0 else 0
|
||
# # 计算 免租期天的比值
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": free_start_day_end,
|
||
# "end_data": end_date,
|
||
# "month_number": 1,
|
||
# "period_rent": period_rent,
|
||
# })]})
|
||
#
|
||
# # 如果有递增
|
||
# else:
|
||
# first_years_begin_end = start_date
|
||
# # 免递增年份计算
|
||
# if self.first_years_begin > 1:
|
||
# # 免递增结束时间
|
||
# first_years_begin_end = start_date + relativedelta(months=(self.first_years_begin -1) * 12)
|
||
# # 免租期结束时间 free_start
|
||
# # 计算免租期和免增期的差值, 如果免租期超过免增期那么不存在免增期
|
||
# if first_years_begin_end > free_start:
|
||
# # 如果免租期有天那么默认增加一个月
|
||
# if self.rent_free_period_days:
|
||
# # 计算这个月的天数
|
||
# current_days = calendar.monthrange(free_start.year, free_start.month)[1]
|
||
# current_no_free = (current_days - self.rent_free_period_days) / current_days
|
||
# # 计算 免租期天的比值
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": free_start,
|
||
# "end_data": free_start + relativedelta(months=1) - relativedelta(days=1),
|
||
# "month_number": 1,
|
||
# "period_rent": monthly_amount * 1 * current_no_free if monthly_amount * 1 * current_no_free > 0 else 0,
|
||
# })]})
|
||
# new_free_day_amount = monthly_amount * 1 * current_no_free if monthly_amount * 1 * current_no_free > 0 else 0
|
||
# all_period_rent += new_free_day_amount
|
||
# # 免租期后退一个月
|
||
# free_start = free_start + relativedelta(months=1)
|
||
# # 计算剩下的免增期
|
||
# delta = relativedelta(first_years_begin_end, free_start)
|
||
# first_months = delta.years * 12 + delta.months
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": free_start,
|
||
# "end_data": first_years_begin_end - relativedelta(days=1),
|
||
# "month_number": first_months,
|
||
# "period_rent": first_months * monthly_amount,
|
||
# })]})
|
||
# all_period_rent += first_months * monthly_amount
|
||
# # 免增期结束开始递增
|
||
# divided_increments_segments = self.divide_time_range_fixed(first_years_begin_end, end_date, increments_every_few_years*12)
|
||
# year_index = 0 # 第几递增周期年
|
||
# all_months = 0 # 递增的第几个月
|
||
# # 如果免租期 超过 免增期 那么递增期也需要排除
|
||
# if first_years_begin_end < free_start:
|
||
# delta = relativedelta(free_start, first_years_begin_end)
|
||
# free_first_months = delta.years * 12 + delta.months
|
||
# # 如果免租期有天那么默认增加一个月
|
||
# if self.rent_free_period_days:
|
||
# free_first_months = free_first_months + 1
|
||
# else:
|
||
# free_first_months = 0
|
||
#
|
||
# for increments_year in divided_increments_segments:
|
||
# year_index += 1
|
||
# """
|
||
# increment_percentage = fields.Float(string="递增百分比")
|
||
# increment_price = fields.Float(string="递增单价")
|
||
# increment_amount = fields.Float(string="递增金额")
|
||
# """
|
||
# increments_month = 0
|
||
# # delta = relativedelta(increments_year[1], increments_year[0])
|
||
# # months = delta.years * 12 + delta.months
|
||
# if self.increment_method == "increment_price" and self.increment_value:
|
||
# unit_price = unit_price + self.increment_value
|
||
# monthly_amount = self.total_area * unit_price
|
||
# monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
# if self.increment_method == "percentage" and self.increment_value:
|
||
# unit_price = unit_price * (1 + (self.increment_value / 100))
|
||
# monthly_amount = self.total_area * unit_price
|
||
# monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
# if self.increment_method == "increment_amount" and self.increment_value:
|
||
# monthly_amount = monthly_amount + self.increment_value
|
||
# monthly_amount = float_round(monthly_amount, precision_digits=0, rounding_method='HALF-UP') # 月租金
|
||
# unit_price = monthly_amount / self.total_area
|
||
#
|
||
# delta = relativedelta(increments_year[1], increments_year[0])
|
||
# months = delta.years * 12 + delta.months
|
||
# month_index = 0
|
||
# # 判断第几月份进入 付费租期
|
||
# for month in range(months):
|
||
# month_index += 1 # 当前第个月
|
||
# # 判断第几月份进入 付费租期
|
||
# all_months += 1
|
||
# if all_months == free_first_months:
|
||
# # 如果免租期有天那么默认增加一个月
|
||
# if self.rent_free_period_days:
|
||
# current_now = increments_year[0] + relativedelta(months=month_index)
|
||
# # 计算这个月的天数
|
||
# current_days = calendar.monthrange(current_now.year, current_now.month)[1]
|
||
# current_no_free = (current_days - self.rent_free_period_days) / current_days
|
||
# # 计算 免租期天的比值
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": current_now,
|
||
# "end_data": current_now + relativedelta(months=1) - relativedelta(days=1),
|
||
# "month_number": 1,
|
||
# "period_rent": monthly_amount * 1 * current_no_free if monthly_amount * 1 * current_no_free > 0 else 0,
|
||
# })]})
|
||
# new_free_day_amount = monthly_amount * 1 * current_no_free if monthly_amount * 1 * current_no_free > 0 else 0
|
||
# all_period_rent += new_free_day_amount
|
||
# # 判断是否是免租期
|
||
# if all_months > free_first_months:
|
||
# increments_month += 1
|
||
# if increments_month:
|
||
# self.sudo().write({'breakdown_ids': [(0, 0, {
|
||
# "unit_price": unit_price,
|
||
# "monthly_amount": monthly_amount,
|
||
# "breakdown_id": self.id,
|
||
# "start_data": increments_year[1] - relativedelta(months=increments_month),
|
||
# "end_data": increments_year[1] - relativedelta(days=1),
|
||
# "month_number": increments_month,
|
||
# "period_rent": monthly_amount * increments_month,
|
||
# })]})
|
||
# all_period_rent += monthly_amount * increments_month
|
||
# return all_period_rent
|
||
|
||
# @api.onchange("first_year_rent")
|
||
# def _onchange_first_year_rent(self):
|
||
# for rec in self:
|
||
# rec.performance_bonds = rec.first_year_rent * 2
|
||
|
||
# @api.onchange("total_area", "unit_price")
|
||
# def _onchange_deposit(self):
|
||
# for record in self:
|
||
# record.deposit = record.total_area * record.unit_price * 2 # 月租金
|
||
|
||
@api.onchange('rent')
|
||
def _onchange_deposit(self):
|
||
for record in self:
|
||
if record.rent:
|
||
record.deposit = record.rent * 2
|
||
else:
|
||
record.deposit = 0.0
|
||
|
||
def update_lease_ids(self):
|
||
property_ids = self.env["property.lease.contract"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||
for rec in property_ids:
|
||
lease_name = []
|
||
electricity_list = []
|
||
water_list = []
|
||
title_address = []
|
||
for record in rec.lease_ids:
|
||
if record.address_id and record.address_id.title_address:
|
||
lease_name.append(record.address_id.name)
|
||
title_address.append(record.address_id.title_address.name)
|
||
rec.name = ','.join(lease_name)
|
||
rec.use_address = '丶'.join(lease_name)
|
||
rec.title_address = ','.join(title_address)
|
||
property_id = self.env['yuthon.property'].search([('id', '=', record.address_id.id)])
|
||
if property_id.energy_meter_ids:
|
||
rec.is_electricity = True
|
||
for electricity_id in property_id.energy_meter_ids:
|
||
electricity_list.append([0, 0, {
|
||
'fees_id': electricity_id.standard_id.id,
|
||
'proportional': electricity_id.rate,
|
||
}])
|
||
else:
|
||
rec.is_electricity = False
|
||
if property_id.water_meter_ids:
|
||
rec.is_water = True
|
||
for water_id in property_id.water_meter_ids:
|
||
water_list.append([0, 0, {
|
||
'fees_id': water_id.standard_id.id,
|
||
'proportional': water_id.rate,
|
||
}])
|
||
else:
|
||
rec.is_water = False
|
||
|
||
rec.electricity_fee_ids = [(5, 0, 0)]
|
||
rec.admin_id = rec.lease_ids[0].admin_line_id.id if rec.lease_ids else ''
|
||
rec.tenure_type = rec.lease_ids[0].address_id.tenure_type if rec.lease_ids else ''
|
||
rec.lessor_id = rec.lease_ids[0].address_id.company_id.id if rec.lease_ids else ''
|
||
rec.total_area = sum([lease.property_area for lease in rec.lease_ids])
|
||
rec.electricity_fee_ids = electricity_list
|
||
|
||
|
||
def trigger_lease_ids(self):
|
||
property_ids = self.env["property.lease.contract"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in property_ids:
|
||
lessor_id = record.lease_ids[-1].address_id.company_id.id if record.lease_ids else ''
|
||
admin_id = record.lease_ids[-1].admin_line_id.id if record.lease_ids else ''
|
||
tenure_type = record.lease_ids[-1].address_id.tenure_type if record.lease_ids else ''
|
||
record.sudo().write({'lessor_id': lessor_id,
|
||
'admin_id': admin_id,
|
||
'tenure_type':tenure_type,})
|
||
record._compute_company_id()
|
||
|
||
def action_yuthon_property_contract_wizard(self):
|
||
return {
|
||
"type": "ir.actions.act_window",
|
||
"name": "批量导入物业合同",
|
||
"res_model": "property.contract.wizard",
|
||
"view_mode": "form",
|
||
"target": "new",
|
||
}
|
||
|
||
def action_relevance_property(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id'))
|
||
)
|
||
contract_dict = {
|
||
'lease_renewal': '续租',
|
||
'interim': '临租',
|
||
'new': '补充协议',
|
||
'first': '首租'
|
||
}
|
||
for record in contract_ids:
|
||
if record.contract_code and record.property_name and record.contract_type:
|
||
record.name = f"{record.contract_code}{contract_dict.get(record.contract_type)}{record.property_name}"
|
||
elif record.contract_code and record.property_name:
|
||
record.name = f"{record.contract_code}{record.property_name}"
|
||
elif record.name:
|
||
record.name = record.property_name
|
||
else:
|
||
record.name = self._description
|
||
|
||
lease_name = []
|
||
if record.lease_ids:
|
||
for lease in record.lease_ids:
|
||
if lease.address_id and lease.address_id.name:
|
||
lease_name.append(str(lease.address_id.name))
|
||
record.write({
|
||
'property_name': ','.join(lease_name) if lease_name else ''
|
||
})
|
||
|
||
@api.constrains("contract_code")
|
||
def _check_contract_code(self):
|
||
"""检查合同编号的唯一性(不区分中英文括号)"""
|
||
if self.env.context.get('skip_contract_code_check'):
|
||
return
|
||
for record in self:
|
||
normalized_code = self._normalize_contract_code(record.contract_code)
|
||
existing_records = self.search([
|
||
('id', '!=', record.id),
|
||
('contract_code', '!=', False),
|
||
('version', '=', record.version),
|
||
('contract_type', '=', record.contract_type),
|
||
])
|
||
for existing in existing_records:
|
||
if self._normalize_contract_code(existing.contract_code) == normalized_code:
|
||
raise ValidationError(_(
|
||
f"合同编号必须唯一!已存在编号:{existing.contract_code}",
|
||
))
|
||
|
||
@api.onchange("contract_sort")
|
||
def _onchange_contract_sort(self):
|
||
if not self.contract_sort:
|
||
return
|
||
|
||
current_year = fields.Date.today().year
|
||
# 定义各类型前缀和编号长度
|
||
type_config = {
|
||
'guanggao': {'prefix': '广', 'length': 3}, # 广告位:3位数字
|
||
'guanzi': {'prefix': '管', 'length': 3}, # 管子间:3位数字
|
||
'property': {'prefix': '租', 'length': 4}, # 物业:4位数字
|
||
'room': {'prefix': '租', 'length': 4}, # 机房:4位数字
|
||
'base_station': {'prefix': '租', 'length': 4}, # 基站:4位数字
|
||
'other': {'prefix': '租', 'length': 4} # 其他:4位数字
|
||
}
|
||
|
||
config = type_config.get(self.contract_sort)
|
||
if not config:
|
||
return
|
||
prefix = config['prefix']
|
||
num_length = config['length']
|
||
|
||
# 正则模式:前缀 + (年份) + 固定长度数字(如 租(2025)0001 或 广(2025)001)
|
||
patterns = [
|
||
re.compile(rf"^{re.escape(prefix)}\({current_year}\)(\d{{{num_length}}})$"),
|
||
re.compile(rf"^{re.escape(prefix)}({current_year})(\d{{{num_length}}})$")
|
||
]
|
||
|
||
max_num = 0
|
||
if hasattr(self.id, 'origin'):
|
||
exclude_id = self.id.origin
|
||
else:
|
||
exclude_id = self.id
|
||
same_type_contracts = self.env['property.lease.contract'].search([
|
||
('term_start_time', '>=', date(2025, 1, 1)),
|
||
('id', '!=', exclude_id)
|
||
])
|
||
for contract in same_type_contracts:
|
||
if not contract.contract_code:
|
||
continue
|
||
# 检查是否匹配两种格式中的任意一种
|
||
for pattern in patterns:
|
||
match = pattern.match(contract.contract_code)
|
||
if match:
|
||
# 提取数字部分并比较
|
||
num = int(match.group(1))
|
||
if num > max_num:
|
||
max_num = num
|
||
break
|
||
|
||
new_num = max_num + 1
|
||
formatted_num = f"{new_num:0{num_length}d}"
|
||
self.contract_code = f"{prefix}({current_year}){formatted_num}"
|
||
|
||
def _normalize_contract_code(self, code):
|
||
"""将合同编号中的括号统一为中文括号"""
|
||
if not code:
|
||
return code
|
||
|
||
# 替换中英文括号为统一格式
|
||
code = re.sub(r'[(]', '(', code) # 替换左括号
|
||
code = re.sub(r'[)]', ')', code) # 替换右括号
|
||
return code
|
||
|
||
def action_copy_record1(self):
|
||
lease_contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in lease_contract_ids:
|
||
if len(lease_contract_ids) > 1:
|
||
raise UserError("只能选择一个拆分")
|
||
record.is_contract = True
|
||
record.is_child_contract = False
|
||
record.is_split = True
|
||
record.number += 1
|
||
contract_id = self.env['property.lease.contract'].create({
|
||
'property_name': record.property_name,
|
||
'tenure_type': record.tenure_type,
|
||
'is_child_contract': True,
|
||
'is_contract': False,
|
||
'contract_source_id': record.id,
|
||
'actual_start_time': record.actual_start_time,
|
||
'actual_end_time': record.actual_end_time,
|
||
'contract_code': record.contract_code+ f'-{record.number}',
|
||
'contract_date': record.contract_date,
|
||
'lessee_id': record.lessee_id.id,
|
||
'use_address': record.use_address,
|
||
'condition':'new',
|
||
'admin_id': record.admin_id.id,
|
||
'lessor_id':record.lessor_id.id,
|
||
'rental_purposes': record.rental_purposes.id,
|
||
'address_for_delivery': record.address_for_delivery,
|
||
# 'lease_term': record.lease_term,
|
||
'lease_term_year': record.lease_term_year,
|
||
'lease_term_month': record.lease_term_month,
|
||
'term_end_time': record.term_end_time,
|
||
'cycle_dict': record.cycle_dict,
|
||
'cycle_mode': record.cycle_mode,
|
||
'increment_method': record.increment_method,
|
||
# 'increment_percentage': record.increment_percentage,
|
||
# 'increment_price': record.increment_price,
|
||
# 'increment_amount': record.increment_amount,
|
||
'increment_value': record.increment_value,
|
||
'first_years_begin': record.first_years_begin,
|
||
'increments_every_few_years': record.increments_every_few_years,
|
||
'rent_free_period': record.rent_free_period,
|
||
'rent_free_period_month': record.rent_free_period_month,
|
||
'rent_free_period_days': record.rent_free_period_days,
|
||
'breach': record.breach,
|
||
'percentage_liquidated_damages': record.percentage_liquidated_damages.id,
|
||
'first_pay': record.first_pay,
|
||
'unit_price': record.unit_price,
|
||
'deposit_type': record.deposit_type,
|
||
'performance_bonds_write': record.performance_bonds_write,
|
||
'first_year_rent': record.first_year_rent,
|
||
'deposit': record.deposit,
|
||
'rent': record.rent,
|
||
'total_contract_receivables': record.total_contract_receivables,
|
||
'tax_rate': record.tax_rate.id,
|
||
'not_tax_amount': record.not_tax_amount,
|
||
'tax': record.tax,
|
||
'is_water': record.is_water,
|
||
'is_electricity': record.is_electricity,
|
||
'miscellaneous': record.miscellaneous,
|
||
'total_area': record.total_area,
|
||
'contract_type': record.contract_type,
|
||
})
|
||
lease_list = []
|
||
for lease_id in record.lease_ids:
|
||
lease_list.append([0, 0, {
|
||
'lease_id':contract_id.id,
|
||
'address_id':lease_id.address_id.id,
|
||
'evaluate_unit_price': lease_id.evaluate_unit_price,
|
||
}])
|
||
|
||
other_list = []
|
||
if record.other_ids:
|
||
for other_id in record.other_ids:
|
||
other_list.append([0, 0, {
|
||
'other_id': contract_id.id,
|
||
'charges': other_id.charges,
|
||
'coping': other_id.coping.id,
|
||
'coping_time': other_id.coping_time,
|
||
}])
|
||
|
||
rent_list = []
|
||
if record.breakdown_ids:
|
||
rent_list.append([0, 0, {
|
||
'contract_id': contract_id.id,
|
||
}])
|
||
|
||
subset_list = []
|
||
subset_list.append([0, 0, {
|
||
'subset_id': record.id,
|
||
'contract_id': contract_id.id}])
|
||
record.subset_ids = subset_list
|
||
contract_id.write({
|
||
'lease_ids': lease_list,
|
||
'other_ids': other_list,})
|
||
contract_id.onchange_lease_ids()
|
||
return {
|
||
'type': 'ir.actions.client',
|
||
'tag': 'display_notification',
|
||
'params': {
|
||
'type': 'success',
|
||
'message': '拆分成功',
|
||
'sticky': False,
|
||
}
|
||
}
|
||
|
||
|
||
def disposable_button(self):
|
||
property_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in property_ids:
|
||
admin_id = record.admin_id
|
||
if admin_id:
|
||
record.write({
|
||
'users_id': admin_id.id,
|
||
'company_id' : admin_id.employee_id.company_id.id,
|
||
})
|
||
|
||
def choose_property_button(self):
|
||
address_ids = self.lease_ids.mapped('address_id').filtered(lambda r: r)
|
||
property_ids2 = address_ids.ids
|
||
return {
|
||
"type": "ir.actions.act_window",
|
||
"name": "批量选择物业信息",
|
||
"res_model": "choose.property.wizard",
|
||
"view_mode": "form",
|
||
"target": "new",
|
||
"context": {
|
||
'default_lease_contract_id': self.id,
|
||
'default_property_ids2': property_ids2,
|
||
}
|
||
}
|
||
|
||
@api.depends('term_start_time', 'term_end_time')
|
||
def _compute_progress_percentage(self):
|
||
for record in self:
|
||
if record.term_start_time and record.term_end_time:
|
||
# 将字段值从 Date 类型转换为 datetime 类型(默认为 00:00:00 时间)
|
||
start_time = fields.Date.from_string(record.term_start_time)
|
||
end_time = fields.Date.from_string(record.term_end_time)
|
||
current_time = fields.Date.today()
|
||
total_duration = (end_time - start_time).days
|
||
elapsed_time = (current_time - start_time).days
|
||
if current_time < start_time:
|
||
record.progress_percentage = 0
|
||
# 如果总天数差不为零,则计算进度百分比
|
||
elif total_duration > 0:
|
||
record.progress_percentage = (elapsed_time / total_duration) * 100
|
||
else:
|
||
record.progress_percentage = 0
|
||
else:
|
||
record.progress_percentage = 0
|
||
|
||
@api.onchange("name")
|
||
def _onchange_name(self):
|
||
if self.property_name:
|
||
self.address_for_delivery = self.property_name
|
||
|
||
@api.onchange("admin_id")
|
||
def _onchange_admin_id(self):
|
||
if self.admin_id:
|
||
self.users_id = self.admin_id.user_id.id
|
||
|
||
@api.onchange("actual_start_time", "actual_end_time", "term_end_time")
|
||
def _onchange_actual_start_time(self):
|
||
for record in self:
|
||
record.contract_time()
|
||
|
||
def get_property_contract_time(self):
|
||
records_only_in_tree1 = self.env['property.lease.contract'].search([('state', '!=', 'settle')])
|
||
for record in records_only_in_tree1:
|
||
record.contract_time()
|
||
|
||
def contract_time(self):
|
||
today = fields.Date.today()
|
||
for contract in self:
|
||
# 获取时间字段
|
||
actual_start = contract.actual_start_time
|
||
actual_end = contract.actual_end_time
|
||
term_end = contract.term_end_time
|
||
effective_month = None
|
||
if actual_end:
|
||
# 获取实际结束日期所在月的最后一天
|
||
last_day_of_month = calendar.monthrange(actual_end.year, actual_end.month)[1]
|
||
if actual_end.day == last_day_of_month:
|
||
# 如果是最后一天,生效月份为下月
|
||
effective_month = fields.Date.to_date(f"{actual_end.year}-{actual_end.month}-{last_day_of_month}")
|
||
else:
|
||
# 如果不是最后一天,生效月份为当月
|
||
effective_month = fields.Date.to_date(f"{actual_end.year}-{actual_end.month}-01")
|
||
# 根据时间条件更新状态
|
||
if not actual_start or today < actual_start:
|
||
# 待执行:实际开始时间未到或未设置
|
||
contract.contact_state = 'to'
|
||
elif actual_start and (not actual_end or today < actual_end):
|
||
# 执行中:已开始且未结束(或结束时间未设置)
|
||
contract.contact_state = 'ing'
|
||
elif today >= actual_end == term_end:
|
||
contract.contact_state = 'complete'
|
||
elif effective_month and today >= effective_month < term_end:
|
||
contract.contact_state = 'terminate'
|
||
|
||
def update_contract_state_task(self):
|
||
executing_contracts = self.search([('contact_state', '=', 'ing')])
|
||
for contract in executing_contracts:
|
||
contract.update_contact_state()
|
||
|
||
def update_contact_state(self):
|
||
for contract in self:
|
||
receivable_records = self.env['property.accounts.receivable'].search([
|
||
('contract_id', '=', contract.id),
|
||
])
|
||
deposit_record = receivable_records.filtered(
|
||
lambda r: r.management_type_id.name == '押金'
|
||
)
|
||
other_records = receivable_records - deposit_record
|
||
all_other_unpaid_zero = all(other.submitted == 0 for other in other_records)
|
||
|
||
if deposit_record.has_date and all_other_unpaid_zero:
|
||
contract.write({'contact_state': 'settle'})
|
||
|
||
higher_version_contract = self.search([
|
||
('contract_code', '=', contract.contract_code),
|
||
('version', '>', contract.version),
|
||
], order='version DESC', limit=1)
|
||
if higher_version_contract:
|
||
deposit_record.copy({
|
||
'contract_id': higher_version_contract.id,
|
||
'has_date': None
|
||
})
|
||
|
||
|
||
@api.onchange('actual_start_time', 'actual_end_time')
|
||
def _onchange_contact_state(self):
|
||
for contract in self:
|
||
today = fields.Date.today()
|
||
term_end = contract.term_start_time
|
||
actual_start = contract.actual_start_time
|
||
actual_end = contract.actual_end_time
|
||
effective_month = None
|
||
|
||
if actual_end:
|
||
last_day_of_month = calendar.monthrange(actual_end.year, actual_end.month)[1]
|
||
if actual_end.day == last_day_of_month:
|
||
effective_month = fields.Date.to_date(f"{actual_end.year}-{actual_end.month}-{last_day_of_month}")
|
||
if effective_month.month == 12:
|
||
effective_month = fields.Date.to_date(f"{effective_month.year + 1}-01-01")
|
||
else:
|
||
effective_month = fields.Date.to_date(f"{effective_month.year}-{effective_month.month + 1}-01")
|
||
else:
|
||
effective_month = fields.Date.to_date(f"{actual_end.year}-{actual_end.month}-01")
|
||
|
||
if not actual_start or today < actual_start:
|
||
# 待执行:实际开始时间未到或未设置
|
||
contract.contact_state = 'to'
|
||
elif actual_start and (not actual_end or today < actual_end):
|
||
# 执行中:已开始且未结束(或结束时间未设置)
|
||
contract.contact_state = 'ing'
|
||
elif today >= actual_end == term_end:
|
||
contract.contact_state = 'complete'
|
||
elif effective_month and today >= effective_month and effective_month < term_end:
|
||
contract.contact_state = 'terminate'
|
||
|
||
# 续租
|
||
def button_lease_extension(self):
|
||
lease_contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
if len(lease_contract_ids) > 1:
|
||
raise UserError("只能单个操作合同续租")
|
||
for original_contract in lease_contract_ids:
|
||
record_id = self.env['property.lease.contract'].search([('contract_id', '=', original_contract.id)])
|
||
if record_id:
|
||
raise UserError("该合同已续租,请勿重复操作")
|
||
new_contract = original_contract.with_context(skip_contract_code_check=True).copy()
|
||
new_term_start = original_contract.term_end_time + timedelta(
|
||
days=1) if original_contract.term_end_time else False
|
||
if not lease_contract_ids.contract_sort:
|
||
new_contract.write({
|
||
'contract_sort': 'property'
|
||
})
|
||
new_contract.write({
|
||
'admin_id': original_contract.admin_id,
|
||
'code': self.env['ir.sequence'].next_by_code('property_lease_contract_code'),
|
||
'contract_code': None,
|
||
'work_end': False,
|
||
'lease_term_year': None,
|
||
'term_start_time': new_term_start,
|
||
'self_users_ids': [(6, 0, self.env.user.ids)],
|
||
'term_end_time': None,
|
||
'condition':'new',
|
||
'contract_id': original_contract.id,
|
||
'contract_type': 'lease_renewal'
|
||
})
|
||
water_lines = self.env['property.water.fee.line'].search([
|
||
('contract_id', '=', original_contract.id)
|
||
])
|
||
for line in water_lines:
|
||
line.copy({'contract_id': new_contract.id})
|
||
# 复制电费明细行
|
||
electric_lines = self.env['property.electric.fee.line'].search([
|
||
('contract_id', '=', original_contract.id)
|
||
])
|
||
for line in electric_lines:
|
||
line.copy({'contract_id': new_contract.id})
|
||
# 复制物业管理费明细行
|
||
manage_lines = self.env['property.management.breakdown.line'].search([
|
||
('breakdown_id', '=', original_contract.id)
|
||
])
|
||
for line in manage_lines:
|
||
line.copy({'breakdown_id': new_contract.id})
|
||
# 复制其他费用明细行
|
||
other_lines = self.env['property.other.fee.line'].search([
|
||
('other_id', '=', original_contract.id)
|
||
])
|
||
for line in other_lines:
|
||
line.copy({'other_id': new_contract.id})
|
||
# 复制子合同明细
|
||
subset_lines = self.env['property.subset.contract'].search([
|
||
('subset_id', '=', original_contract.id)
|
||
])
|
||
for line in subset_lines:
|
||
line.copy({'subset_id': new_contract.id})
|
||
lease_lines = self.env['property.lease.contract.line'].search([
|
||
('lease_id', '=', original_contract.id)
|
||
])
|
||
for line in lease_lines:
|
||
line.copy({'lease_id': new_contract.id})
|
||
new_contract._compute_name()
|
||
new_contract._onchange_contract_sort()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': '续期合同',
|
||
'res_model': 'property.lease.contract',
|
||
'view_mode': 'form',
|
||
'res_id': new_contract.id,
|
||
'target': 'current',
|
||
}
|
||
|
||
# 合同补充
|
||
def button_lease_supplement(self):
|
||
lease_contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
if len(lease_contract_ids) > 1:
|
||
raise UserError("只能单个操作合同补充")
|
||
version_str = lease_contract_ids.version.lstrip('V')
|
||
main_version = version_str.split('.')[0]
|
||
new_main_version = int(main_version) + 1
|
||
new_version = f"V{new_main_version}.0"
|
||
record_id = self.env['property.lease.contract'].search([('contract_code', '=', lease_contract_ids.contract_code),
|
||
('version', '=', new_version)
|
||
])
|
||
if record_id:
|
||
raise UserError("该合同已有补充协议审批,请勿重复操作")
|
||
new_contract = lease_contract_ids.with_context(skip_contract_code_check=True).copy()
|
||
if not lease_contract_ids.contract_sort:
|
||
new_contract.write({
|
||
'contract_sort': 'property'
|
||
})
|
||
new_contract.write({
|
||
'work_end': False,
|
||
'version': new_version,
|
||
'condition': 'new',
|
||
'code': self.env['ir.sequence'].next_by_code('property_lease_contract_code'),
|
||
'self_users_ids': [(6, 0, [self.env.user.id])],
|
||
'contact_state': 'to',
|
||
})
|
||
water_lines = self.env['property.water.fee.line'].search([
|
||
('contract_id', '=', lease_contract_ids.id)
|
||
])
|
||
for line in water_lines:
|
||
line.copy({'contract_id': new_contract.id})
|
||
# 复制电费明细行
|
||
electric_lines = self.env['property.electric.fee.line'].search([
|
||
('contract_id', '=', lease_contract_ids.id)
|
||
])
|
||
for line in electric_lines:
|
||
line.copy({'contract_id': new_contract.id})
|
||
# 复制物业管理费明细行
|
||
manage_lines = self.env['property.management.breakdown.line'].search([
|
||
('breakdown_id', '=', lease_contract_ids.id)
|
||
])
|
||
for line in manage_lines:
|
||
line.copy({'breakdown_id': new_contract.id})
|
||
# 复制其他费用明细行
|
||
other_lines = self.env['property.other.fee.line'].search([
|
||
('other_id', '=', lease_contract_ids.id)
|
||
])
|
||
for line in other_lines:
|
||
line.copy({'other_id': new_contract.id})
|
||
# 复制子合同明细
|
||
subset_lines = self.env['property.subset.contract'].search([
|
||
('subset_id', '=', lease_contract_ids.id)
|
||
])
|
||
for line in subset_lines:
|
||
line.copy({'subset_id': new_contract.id})
|
||
lease_lines = self.env['property.lease.contract.line'].search([
|
||
('lease_id', '=', lease_contract_ids.id)
|
||
])
|
||
for line in lease_lines:
|
||
line.copy({'lease_id': new_contract.id})
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': '合同补充',
|
||
'res_model': 'property.lease.contract',
|
||
'view_mode': 'form',
|
||
'res_id': new_contract.id,
|
||
'target': 'current',
|
||
}
|
||
|
||
# 变更
|
||
def button_lease_change(self):
|
||
form_id = self.env.ref('yuthon_property_approval.view_property_lease_renewal_form').id
|
||
address_ids = self.lease_ids.mapped('address_id').ids
|
||
return {
|
||
'name': '变更申请',
|
||
"type": "ir.actions.act_window",
|
||
'view_mode': 'form',
|
||
'res_model': 'property.lease.renewal',
|
||
"views": [[form_id, "form"]],
|
||
"context": {
|
||
'default_renewal_type': 'lease',
|
||
'default_contract_id': self.id,
|
||
'default_property_ids': [(6, 0, address_ids)] if address_ids else False
|
||
},
|
||
}
|
||
|
||
def read_coming_count(self):
|
||
return self.search_count([('expiration_coming', '=', 'coming'), ('admin_id', '=', self.env.user.id)])
|
||
|
||
def read_due_count(self):
|
||
return self.search_count([('expiration_coming', '=', 'due'), ('admin_id', '=', self.env.user.id)])
|
||
|
||
def put_evaluation_plan(self):
|
||
for record in self:
|
||
property_ids = []
|
||
rent_area = 0
|
||
first_address_id = record.lease_ids[0].admin_line_id if record.lease_ids else None
|
||
for lease_id in record.lease_ids:
|
||
property_ids.append(lease_id.address_id.id)
|
||
rent_area += lease_id.address_id.rent_area
|
||
self.env['property.evaluation.plan'].create({
|
||
'property_name': record.property_name,
|
||
'property_ids': [(6, 0, property_ids)],
|
||
'evaluation_area': rent_area,
|
||
'owner_id': first_address_id.title_information_ids[0].owner_id.id if first_address_id and first_address_id.title_information_ids else None,
|
||
'registered_id': first_address_id.title_information_ids[0].registered_id.id if first_address_id and first_address_id.title_information_ids else None,
|
||
'rent_id': first_address_id.reference_ids[0].rent_id.id if first_address_id and first_address_id.reference_ids else None,
|
||
'lease_period': first_address_id.reference_ids[0].lease_period if first_address_id and first_address_id.reference_ids else None,
|
||
'annual_increase_percentage': first_address_id.reference_ids[0].annual_increase_percentage if first_address_id and first_address_id.reference_ids else None,
|
||
'remake': first_address_id.remark if first_address_id else None,
|
||
'assessment_category': 'rent_money',
|
||
})
|
||
record.write({'contact_state': 'ing'})
|
||
return {
|
||
"name": "物业评估方案",
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'property.evaluation.plan',
|
||
"view_mode": "tree",
|
||
}
|
||
|
||
def all_put_evaluation_plan(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||
for record in contract_ids:
|
||
property_ids = []
|
||
property_id = record.lease_ids[0].admin_line_id
|
||
if record.contact_state == 'done':
|
||
raise UserError(f'{record.property_name}该物业已评估,请勿重复评估')
|
||
rent_area = 0
|
||
for lease_id in record.lease_ids:
|
||
property_ids.append(lease_id.address_id.id)
|
||
rent_area += lease_id.address_id.rent_area
|
||
self.env['property.evaluation.plan'].create({
|
||
'property_name': record.property_name,
|
||
'property_ids': property_ids,
|
||
'evaluation_area': property_id.rent_area,
|
||
'owner_id': property_id.title_information_ids[0].owner_id.id if property_id.title_information_ids else None,
|
||
'registered_id': property_id.title_information_ids[0].registered_id.id if property_id.title_information_ids else None,
|
||
'rent_id': property_id.reference_ids[0].rent_id.id if property_id.reference_ids else None,
|
||
'lease_period': property_id.reference_ids[0].lease_period if property_id.reference_ids else None,
|
||
'annual_increase_percentage': property_id.reference_ids[0].annual_increase_percentage if property_id.reference_ids else None,
|
||
'remake': property_id.remark,
|
||
'assessment_category': 'rent_money',
|
||
})
|
||
record.write({'contact_state': 'ing'})
|
||
return {
|
||
"name": "物业评估方案",
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'property.evaluation.plan',
|
||
"view_mode": "tree",
|
||
}
|
||
|
||
def print_payment_notice(self):
|
||
contract_ids = self.env["property.lease.contract"].browse(
|
||
self._context.get('active_ids', self._context.get('active_id')))
|
||
return {
|
||
"name": "打印缴费通知单",
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'print.payment.wizard',
|
||
"view_mode": "form",
|
||
'target': 'new',
|
||
'context': {
|
||
'default_contract_ids': contract_ids.ids,
|
||
}
|
||
}
|
||
|
||
def print_contract(self):
|
||
return {
|
||
"name": "打印合同",
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'print.contract.wizard',
|
||
"view_mode": "form",
|
||
'target': 'new',
|
||
'context': {
|
||
'default_contract_id': self.id,
|
||
}
|
||
}
|
||
|
||
def action_print_lease_contract(self):
|
||
"""打印租赁合同审批单"""
|
||
report_ref = self.env.ref('yuthon_property_approval.action_property_lease_contract_print_report')
|
||
return report_ref.report_action(self)
|
||
|
||
def _get_property_notify_users(self, task_config):
|
||
"""从 task_config 获取需要通知的用户列表.
|
||
- user_ids: 直接指定的人
|
||
- users_role_id + users_role_group_ids: 角色组 -> 角色 -> 花名册中 user_role_id(主角色)或 user_role_ids(辅助角色)匹配的员工
|
||
"""
|
||
user_ids = set(task_config.user_ids.ids)
|
||
role_ids = set(task_config.users_role_group_ids.mapped('user_role_ids').ids)
|
||
if task_config.users_role_id:
|
||
role_ids.add(task_config.users_role_id.id)
|
||
if role_ids:
|
||
all_target_roles = self.env['soong.user.role'].browse(list(role_ids))
|
||
role_employees = self.env['hr.employee'].search([
|
||
'|',
|
||
('user_role_id', 'in', all_target_roles.ids),
|
||
('user_role_ids', 'in', all_target_roles.ids),
|
||
('user_id', '!=', False),
|
||
])
|
||
for emp in role_employees:
|
||
user_ids.add(emp.user_id.id)
|
||
return self.env['res.users'].browse(list(user_ids))
|
||
|
||
def task_property_vacant_reminder(self):
|
||
"""物业空置提醒:以物业为单位,合同实际结束日期距今已3个月或以上,或没有合同."""
|
||
today = fields.Date.today()
|
||
three_months_ago = today - timedelta(days=90)
|
||
properties = self.env['yuthon.property'].search([])
|
||
vacant_properties = self.env['yuthon.property']
|
||
for prop in properties:
|
||
contract_lines = self.env['property.lease.contract.line'].search([
|
||
('address_id', '=', prop.id),
|
||
])
|
||
if not contract_lines:
|
||
vacant_properties |= prop
|
||
continue
|
||
contracts = contract_lines.mapped('lease_id')
|
||
active_contracts = contracts.filtered(
|
||
lambda c: c.contact_state not in ['complete', 'terminate', 'early_termination', 'settle']
|
||
)
|
||
if active_contracts:
|
||
continue
|
||
ended_contracts = contracts.filtered(
|
||
lambda c: c.contact_state in ['complete', 'terminate', 'early_termination', 'settle'] and c.actual_end_time
|
||
)
|
||
if not ended_contracts:
|
||
continue
|
||
last_end_time = max(ended_contracts.mapped('actual_end_time'))
|
||
if last_end_time <= three_months_ago:
|
||
vacant_properties |= prop
|
||
if not vacant_properties:
|
||
return
|
||
task_config = self.env['yuthon.property.task'].search([('cron_id.name', '=', '定时任务:物业空置提醒')], limit=1)
|
||
if not task_config:
|
||
return
|
||
notify_users = self._get_property_notify_users(task_config)
|
||
if not notify_users:
|
||
return
|
||
count = len(vacant_properties)
|
||
for user in notify_users:
|
||
hr_employee = user.employee_ids.filtered(lambda e: e.wecom_userid)[:1]
|
||
if not hr_employee:
|
||
continue
|
||
result_data = self.sudo().env['wecom.apps'].sync_send_message_textcard(
|
||
category='oa',
|
||
user_id=hr_employee.wecom_userid,
|
||
title='物业空置提醒',
|
||
description=f"提醒:有{count}个物业已空置,请移至工作台查看。",
|
||
url='https://oa.thtzjt.com/web?debug=1#action=3584&cids=1&menu_id=9668',
|
||
btntxt='详细信息',
|
||
)
|
||
confirm = self.env['yuthon.property.task.confirm'].create({
|
||
'task_id': task_config.id,
|
||
'user_id': user.id,
|
||
'state': 'read',
|
||
})
|
||
if result_data and len(result_data) > 1:
|
||
msg_id = result_data[1].get('msgid')
|
||
if msg_id:
|
||
confirm.sudo().write({'msg_id': msg_id})
|
||
|
||
def task_property_contract_reminder(self):
|
||
"""物业合同到期提醒:距实际结束日期还有3个月."""
|
||
today = fields.Date.today()
|
||
target_date = today + timedelta(days=90)
|
||
contracts = self.search([
|
||
('actual_end_time', '=', target_date),
|
||
('contact_state', 'in', ['to', 'ing']),
|
||
])
|
||
if not contracts:
|
||
return
|
||
task_config = self.env['yuthon.property.task'].search([('cron_id.name', '=', '定时任务:物业合同到期提醒')], limit=1)
|
||
if not task_config:
|
||
return
|
||
notify_users = self._get_property_notify_users(task_config)
|
||
if not notify_users:
|
||
return
|
||
count = len(contracts)
|
||
for user in notify_users:
|
||
hr_employee = user.employee_ids.filtered(lambda e: e.wecom_userid)[:1]
|
||
if not hr_employee:
|
||
continue
|
||
result_data = self.sudo().env['wecom.apps'].sync_send_message_textcard(
|
||
category='oa',
|
||
user_id=hr_employee.wecom_userid,
|
||
title='物业合同到期提醒',
|
||
description=f"提醒:有{count}个物业即将到期,请移至工作台查看。",
|
||
url='https://oa.thtzjt.com/web?debug=1#action=3584&cids=1&menu_id=9668',
|
||
btntxt='详细信息',
|
||
)
|
||
confirm = self.env['yuthon.property.task.confirm'].create({
|
||
'task_id': task_config.id,
|
||
'user_id': user.id,
|
||
'state': 'read',
|
||
})
|
||
if result_data and len(result_data) > 1:
|
||
msg_id = result_data[1].get('msgid')
|
||
if msg_id:
|
||
confirm.sudo().write({'msg_id': msg_id})
|
||
|
||
class PropertyLeaseContractLine(models.Model):
|
||
_name = 'property.lease.contract.line'
|
||
_description = '物业列表明细行'
|
||
|
||
lease_id = fields.Many2one('property.lease.contract', string="关联凭租合同审批")
|
||
confirm_id = fields.Many2one('property.confirm', string="关联成交确认审批")
|
||
construction_id = fields.Many2one('property.construction.approval', string="关联建设工程造价咨询合同审批")
|
||
project_id = fields.Many2one('property.project.approval', string="关联工程立项")
|
||
design_id = fields.Many2one('property.design.approval', string="关联建设工程设计合同审批")
|
||
accounting_id = fields.Many2one('property.accounting.approval', string="关联建设工程委托监理合同审批")
|
||
supervising_id = fields.Many2one('property.supervising.approval', string="关联建设工程施工合同审批")
|
||
open_rent_id = fields.Many2one('property.open.rent', string="关联公开招租审批")
|
||
address_id = fields.Many2one('yuthon.property', string="物业地址")
|
||
tax = fields.Float(related='address_id.tax',string="税率")
|
||
admin_line_id = fields.Many2one(related='address_id.admin_id', string="管理员", store=True)
|
||
property_area = fields.Float(string="可租面积", compute="_compute_property_area")
|
||
message_standard_id = fields.Many2one(related='address_id.message_standard_id', string="管理费标准", store=True)
|
||
message_unit = fields.Float(related='message_standard_id.amount', string="管理费单价", store=True)
|
||
evaluate_unit_price = fields.Float(string="评估单价")
|
||
|
||
@api.depends('address_id', 'address_id.manage_information_ids')
|
||
def _compute_property_area(self):
|
||
for line in self:
|
||
line.property_area = 0.0
|
||
if line.address_id and line.address_id.manage_information_ids:
|
||
latest_line = line.address_id.manage_information_ids.sorted('id', reverse=True)[0]
|
||
line.property_area = latest_line.rent_area
|
||
|
||
def open_record(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': '物业信息',
|
||
'res_model': 'yuthon.property',
|
||
'res_id': self.address_id.id,
|
||
'view_mode': 'form',
|
||
'target': 'current',
|
||
}
|
||
|
||
|
||
class PropertyRentBreakdownLine(models.Model):
|
||
_name = 'property.rent.breakdown.line'
|
||
_description = '物业租金明细行'
|
||
|
||
breakdown_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
renewal_id = fields.Many2one('property.lease.renewal', string="关联租赁变更审批")
|
||
open_rent_id = fields.Many2one('property.open.rent', string="关联公开招租审批")
|
||
charges = fields.Char(string="收费项")
|
||
unit_price = fields.Float(string="单价")
|
||
monthly_amount = fields.Float(string="月租金", digits=(16, 2))
|
||
start_data = fields.Date(string="开始日期")
|
||
end_data = fields.Date(string="结束日期")
|
||
month_number = fields.Integer(string="期间月数")
|
||
period_rent = fields.Float(string="期间租金", digits=(16, 2))
|
||
|
||
@api.onchange('monthly_amount', 'month_number')
|
||
def _onchange_water_amount(self):
|
||
self.period_rent = self.monthly_amount * self.month_number
|
||
|
||
|
||
class PropertyWaterBreakdownLine(models.Model):
|
||
_name = 'property.water.breakdown.line'
|
||
|
||
breakdown_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
water_unit_price = fields.Float(string="单价")
|
||
water_monthly_amount = fields.Float(string="金额", digits=(16, 2))
|
||
start_data = fields.Date(string="开始日期")
|
||
end_data = fields.Date(string="结束日期")
|
||
water_month_number = fields.Integer(string="期间月数")
|
||
water_period_rent = fields.Float(string="期间费用", digits=(16, 2))
|
||
|
||
@api.onchange('water_monthly_amount', 'month_number')
|
||
def _onchange_water_amount(self):
|
||
self.water_period_rent = self.water_monthly_amount * self.water_month_number
|
||
|
||
|
||
class PropertyEleBreakdownLine(models.Model):
|
||
_name = 'property.ele.breakdown.line'
|
||
|
||
breakdown_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
|
||
ele_unit_price = fields.Float(string="单价")
|
||
ele_monthly_amount = fields.Float(string="金额", digits=(16, 2))
|
||
start_data = fields.Date(string="开始日期")
|
||
end_data = fields.Date(string="结束日期")
|
||
ele_month_number = fields.Integer(string="期间月数")
|
||
ele_period_rent = fields.Float(string="期间费用", digits=(16, 2))
|
||
|
||
@api.onchange('ele_monthly_amount', 'month_number')
|
||
def _onchange_electricity_amount(self):
|
||
self.ele_period_rent = self.ele_monthly_amount * self.ele_month_number
|
||
|
||
|
||
class PropertyManagementBreakdownLine(models.Model):
|
||
_name = 'property.management.breakdown.line'
|
||
|
||
breakdown_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
management_unit_price = fields.Float(string="单价")
|
||
management_monthly_amount = fields.Float(string="金额", digits=(16, 2))
|
||
start_data = fields.Date(string="开始日期")
|
||
end_data = fields.Date(string="结束日期")
|
||
management_month_number = fields.Float(string="期间月数", digits=(16, 2))
|
||
management_period_rent = fields.Float(string="期间费用", digits=(16, 2))
|
||
|
||
@api.onchange('start_data', 'end_data')
|
||
def _onchange_management_month_number(self):
|
||
if self.start_data and self.end_data:
|
||
if self.end_data >= self.start_data:
|
||
total_days = (self.end_data - self.start_data).days + 1
|
||
start_month_days = calendar.monthrange(self.start_data.year, self.start_data.month)[1]
|
||
self.management_month_number = round(total_days / start_month_days, 2)
|
||
else:
|
||
self.management_month_number = 0
|
||
else:
|
||
self.management_month_number = 0
|
||
|
||
@api.onchange('management_monthly_amount', 'management_month_number')
|
||
def _onchange_management_amount(self):
|
||
self.management_period_rent = round(self.management_monthly_amount * self.management_month_number, 2)
|
||
|
||
|
||
class PropertyWaterFeeLine(models.Model):
|
||
_name = 'property.water.fee.line'
|
||
_description = '物业水表库明细行'
|
||
|
||
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
property_meter_water_id = fields.Many2one('property.meter.water', string="水表库")
|
||
rate = fields.Integer(related='property_meter_water_id.gauge_magnification', string="倍率", store=True)
|
||
unit_number = fields.Char(related='property_meter_water_id.unit_number', string="单元编号/用途", store=True)
|
||
start_date = fields.Date(related='contract_id.term_start_time', string="开始日期")
|
||
end_date = fields.Date(related='contract_id.term_end_time', string="结束日期")
|
||
unit_price = fields.Float(string='固定单价', digits=(16, 4))
|
||
line_loss = fields.Float(string='固定线损', digits=(16, 4))
|
||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定线损")
|
||
is_fix_price = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定单价")
|
||
epitope_values_id = fields.Many2one(related='property_meter_water_id.epitope_values_id', string="表位置")
|
||
|
||
@api.onchange('is_fix_price')
|
||
def _onchange_is_fix_price(self):
|
||
if self.is_fix_price == 'no':
|
||
self.unit_price = 0
|
||
|
||
@api.onchange('is_fix_loss')
|
||
def _onchange_is_fix_loss(self):
|
||
if self.is_fix_loss == 'no':
|
||
self.line_loss = 0
|
||
|
||
|
||
class PropertyElectricFeeLine(models.Model):
|
||
_name = 'property.electric.fee.line'
|
||
_description = '物业电表信息明细行'
|
||
|
||
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
property_meter_archives_id = fields.Many2one('property.meter.archives', string="电表编号")
|
||
rate = fields.Integer(related='property_meter_archives_id.gauge_magnification', string="倍率", store=True)
|
||
unit_number = fields.Char(related='property_meter_archives_id.unit_number', string="单元编号/用途", store=True)
|
||
start_date = fields.Date(related='contract_id.term_start_time', string="开始日期")
|
||
end_date = fields.Date(related='contract_id.term_end_time', string="结束日期")
|
||
unit_price = fields.Float(string='固定单价', digits=(16, 4))
|
||
line_loss = fields.Float(string='固定线损', digits=(16, 4))
|
||
is_fix_loss = fields.Selection([('yes', '是'), ('no', '否')], default="no", string="是否固定线损")
|
||
is_fix_price = fields.Selection([('yes', '是'),('no', '否')], default="no", string="是否固定单价")
|
||
epitope_values_id = fields.Many2one(related='property_meter_archives_id.epitope_values_id', string="表位置")
|
||
|
||
@api.onchange('is_fix_price')
|
||
def _onchange_is_fix_price(self):
|
||
if self.is_fix_price == 'no':
|
||
self.unit_price = 0
|
||
|
||
@api.onchange('is_fix_loss')
|
||
def _onchange_is_fix_loss(self):
|
||
if self.is_fix_loss == 'no':
|
||
self.line_loss = 0
|
||
|
||
class PropertyOtherFeeLine(models.Model):
|
||
_name = 'property.other.fee.line'
|
||
_description = '物业其他费用明细行'
|
||
|
||
other_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
charges = fields.Char(string="收费项")
|
||
coping = fields.Float(string="应收")
|
||
coping_time = fields.Date(string="应收日期")
|
||
|
||
class PropertySubsetContract(models.Model):
|
||
_name = 'property.subset.contract'
|
||
_description = '物业子合同明细'
|
||
|
||
subset_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
contract_id = fields.Many2one('property.lease.contract', string="合同")
|
||
name = fields.Char(related='contract_id.name', string="物业信息")
|
||
lessee_id = fields.Many2one(related='contract_id.lessee_id', string="承租方")
|
||
rental_purposes = fields.Many2one(related='contract_id.rental_purposes', string="出租用途")
|
||
|
||
|
||
class LesseeAlterationLine(models.Model):
|
||
_name = 'lessee.alteration.line'
|
||
_description = '承租方变更明细行'
|
||
|
||
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
lessee_id = fields.Many2one('property.tenant.information', string='承租方')
|
||
start_date = fields.Date(string="生效日期", default=fields.Datetime.today())
|
||
end_date = fields.Date(string="失效日期")
|
||
|
||
|
||
class LessorAlterationLine(models.Model):
|
||
_name = 'lessor.alteration.line'
|
||
_description = '出租方变更明细行'
|
||
|
||
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
|
||
lessor_id = fields.Many2one('yuthon.property.company', string="出租方")
|
||
start_date = fields.Date(string="生效日期", default=fields.Datetime.today())
|
||
end_date = fields.Date(string="失效日期") |