Files
gzth/yuthon_property_approval/models/property_open_rent.py
T
2026-07-02 15:02:40 +08:00

295 lines
15 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import calendar
from dateutil.relativedelta import relativedelta
from odoo.tools.float_utils import float_round
from odoo import models, fields, api, exceptions, _
from odoo.exceptions import ValidationError
class PropertyOpenRent(models.Model):
_name = 'property.open.rent'
_description = '公开招租审批'
_inherit = ['mail.thread.main.attachment', 'mail.activity.mixin', 'soong.workflow.mixin']
_rec_name = 'name'
name = fields.Char(string='标题')
user_id = fields.Many2one('res.users', string='申请人', default=lambda self: self.env.user.id)
code = fields.Char(string="编号", default=lambda self: self.env['ir.sequence'].next_by_code('property_open_rent_code'))
property_documents_ids = fields.Many2many('documents.document', string="物业公开招租文件")
rental_area = fields.Float(string="出租面积")
upset = fields.Float(string="底价")
document_ids = fields.Many2many('ir.attachment', string="附件")
handling_opinions = fields.Text(string="经办意见")
type_stamp_ids = fields.Many2many('seals.type.setting', string='用章类型', domain="['&', ('start_date', '<=', context_today()), '|', ('end_date', '=', False), ('end_date', '>=', context_today())]")
is_chapter = fields.Selection([('yes', '是'), ('no', '否')], string='是否用章', default='no')
# 物业
property_name = fields.Char(string="物业信息")
lease_ids = fields.One2many('property.lease.contract.line', 'open_rent_id', string="物业信息明细行")
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_open_rent_id': self.id,
'default_property_ids2': property_ids2,
}
}
# 租期
term_start_time = fields.Date(string="租期开始")
term_end_time = fields.Date(string="租期结束")
lease_term_year = fields.Integer(string="租期(年)", store=True)
lease_term_month = fields.Integer(string="租期(月)", store=True)
lease_term_day = fields.Integer(string="租期(日)", store=True)
# 递增
first_years_begin = fields.Integer(string="第几年开始", default=2)
increments_every_few_years = fields.Integer(string="每几年递增")
increment_method = fields.Selection([
('percentage', '百分比'),
('increment_price', '递增单价'),
('increment_amount', '递增金额')
], string='递增方式', default='percentage')
increment_value = fields.Float(string="递增值")
# 月租金、单价、总租金
unit_price = fields.Float(string="单价", digits=(16, 4))
rent = fields.Float(string="月租金")
total_area = fields.Float(string="总面积", digits=(16, 4))
rent_free_period_month = fields.Integer(string="免租期(月)")
rent_free_period_days = fields.Integer(string="免租期(日)")
breakdown_ids = fields.One2many('property.rent.breakdown.line', 'open_rent_id', string="租金明细行")
total_rent = fields.Float(string="总租金", compute='_compute_total_rent', store=True)
@api.depends('term_start_time', 'term_end_time')
def _compute_lease_term(self):
for record in self:
if record.term_start_time and record.term_end_time:
start = record.term_start_time
end = record.term_end_time
years = end.year - start.year
months = end.month - start.month
days = end.day - start.day
if days < 0:
months -= 1
days += 30
if months < 0:
years -= 1
months += 12
record.lease_term_year = years
record.lease_term_month = months
record.lease_term_day = days
else:
record.lease_term_year = 0
record.lease_term_month = 0
record.lease_term_day = 0
@api.depends('breakdown_ids')
def _compute_total_rent(self):
for record in self:
record.total_rent = sum(line.period_rent for line in record.breakdown_ids)
@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):
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)
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')
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,
"open_rent_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 if self.total_area else 0
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,
"open_rent_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,
"open_rent_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,
"open_rent_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,
"open_rent_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 divide_time_range_fixed(self, start_date, end_date, months):
"""将时间段按 months 个月切分,返回 [(start, end), ...] 列表"""
result = []
current = start_date
while current < end_date:
next_date = current + relativedelta(months=months)
if next_date > end_date:
next_date = end_date
result.append((current, next_date))
current = next_date
return result
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
default='normal')
active = fields.Boolean('Active', default=True, tracking=True)
work_end = fields.Boolean(string="结束")
department_id = fields.Many2one('hr.department', string="部门")
def _get_default_name_title(self):
return self._description if self._description else self._name
name_title = fields.Char(default=_get_default_name_title)
self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])])
def action_print_property_open_rent(self):
"""打印公开招租审批"""
self.ensure_one()
report_ref = self.env.ref('yuthon_property_approval.action_property_open_rent_print_report')
return report_ref.sudo().report_action(self)
#
#
# class PropertyOpenRentLine(models.Model):
# _name = 'property.open.rent.line'
#
# property_open_rent_id = fields.Many2one('property.open.rent', string='关联审批')
# product_id = fields.Many2one('product.product', string="物业")
# property_note = fields.Text(string="物业描述")