Files
gzth/yuthon_property_approval/models/inherit_yuthon_property.py
T
2025-10-18 19:49:03 +08:00

1126 lines
52 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime, timedelta, date
from odoo.tools.float_utils import float_round
from numpy.ma.core import default_filler
from odoo import models, fields, api, exceptions, _
from odoo.exceptions import UserError, ValidationError
from dateutil.relativedelta import relativedelta
import calendar
class PropertyMeterWater(models.Model):
_inherit = 'property.meter.water'
contract_id = fields.Many2one('property.lease.contract', string="合同", compute='compute_contract_id')
@api.depends('yuthon_property_ids')
def compute_contract_id(self):
for record in self:
if record.yuthon_property_ids:
lease_contract = self.env['property.lease.contract.line'].search(
[('address_id', '=', record.yuthon_property_ids[0].id)], limit=1)
if lease_contract:
record.contract_id = lease_contract.lease_id.id
else:
record.contract_id = False
else:
record.contract_id = False
class PropertyMeterArchives(models.Model):
_inherit = 'property.meter.archives'
contract_id = fields.Many2one('property.lease.contract', string="合同", compute='compute_contract_id')
@api.depends('yuthon_property_ids')
def compute_contract_id(self):
for record in self:
if record.yuthon_property_ids:
lease_contract = self.env['property.lease.contract.line'].search(
[('address_id', '=', record.yuthon_property_ids[0].id)], limit=1)
if lease_contract:
record.contract_id = lease_contract.lease_id.id
else:
record.contract_id = False
else:
record.contract_id = False
class MeterArchivesLine(models.Model):
_inherit = 'meter.archives.line'
contract_id = fields.Many2one('property.lease.contract', string="合同", compute="compute_contract_id")
lessee_id = fields.Many2one(related="contract_id.lessee_id", string="承租方")
@api.depends('yuthon_property_ids')
def compute_contract_id(self):
for record in self:
if record.yuthon_property_ids:
lease_contract = self.env['property.lease.contract.line'].search([('address_id', '=', record.yuthon_property_ids[0].id)], limit=1)
if lease_contract:
record.contract_id = lease_contract.lease_id.id
else:
record.contract_id = False
else:
record.contract_id = False
@api.onchange('current_read')
def onchange_current_read(self):
if self.contract_id:
tariff_list = self.env['property.tariff.list'].search(
[('name', '=', self.archives_id.name),
('meter_date', '=', self.meter_date)
], limit=1
)
values = {
'original_amount': self.receivables,
'contract_id': self.contract_id,
'lessee_id': self.lessee_id.id,
'dosage': self.current_meter,
'tariff_meter_reading': self.current_read,
'name': self.archives_id.name,
'meter_date': self.meter_date,
'unit': self.unit,
'remarks': self.remarks,
'due_date': self.meter_date + relativedelta(months=1) - timedelta(days=1) if self.meter_date else None,
}
if tariff_list:
tariff_list.write(values)
else:
self.env['property.tariff.list'].create(values)
class MeterWaterLine(models.Model):
_inherit = 'meter.water.line'
contract_id = fields.Many2one('property.lease.contract', string="合同", compute="compute_contract_id")
lessee_id = fields.Many2one(related="contract_id.lessee_id", string="承租方")
@api.depends('yuthon_property_ids')
def compute_contract_id(self):
for record in self:
if record.yuthon_property_ids:
lease_contract = self.env['property.lease.contract.line'].search(
[('address_id', '=', record.yuthon_property_ids[0].id)], limit=1)
if lease_contract:
record.contract_id = lease_contract.lease_id.id
else:
record.contract_id = False
else:
record.contract_id = False
@api.onchange('current_read')
def onchange_current_read(self):
if self.contract_id:
tariff_list = self.env['property.water.list'].search(
[('name', '=', self.water_id.name),
('meter_date', '=', self.meter_date)
], limit=1
)
values = {
'original_amount': self.receivables,
'contract_id': self.contract_id,
'lessee_id': self.lessee_id.id,
'dosage': self.current_meter,
'water_meter_reading': self.current_read,
'name': self.water_id.name,
'meter_date': self.meter_date,
'unit': self.unit,
'remarks': self.remarks,
'due_date': self.meter_date + relativedelta(months=1) - timedelta(days=1) if self.meter_date else None,
}
if tariff_list:
tariff_list.write(values)
else:
self.env['property.water.list'].create(values)
class YuthonProperty(models.Model):
_inherit = 'yuthon.property'
property_contract_ids = fields.One2many('property.contract.line', 'yuthon_property_id', string="关联物业合同明细行")
def get_yuthon_property_time(self):
unique_property_ids = set()
for record in self.search([('property_state', '=', 'to'), ('property_contract_ids', '=', False)]):
contracts = self.env['property.evaluation.plan'].search([])
for contract in contracts:
for property_id in contract.property_ids:
if property_id.id not in unique_property_ids:
unique_property_ids.add(property_id.id)
if record.id not in unique_property_ids:
record.put_assessment_approval()
else:
continue
def update_lessee_id(self):
property_ids = self.env["yuthon.property"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in property_ids:
if rec.property_contract_ids:
rec.lessee_id = rec.property_contract_ids[-1].lessee_id
@api.onchange('property_contract_ids')
def _onchange_property_contract_ids(self):
if self.property_contract_ids:
self.lessee_id = self.property_contract_ids[-1].lessee_id
# 银行流水
class PropertyBankStatements(models.Model):
_inherit = 'property.bank.statements'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
contract_ids = fields.Many2many('property.lease.contract', string="租赁合同审批")
property_name = fields.Text(string="物业名称", compute="compute_property_name", store=True)
accounts_id = fields.Many2one('property.accounts.approval', string="关联生成应收审批单")
admin_id = fields.Many2one('res.users', string="片区管理员", compute="_compute_property_employee_id", store=True, index=True)
@api.depends('contract_ids')
def _compute_property_employee_id(self):
for emp in self:
if emp.contract_ids:
emp.admin_id = emp.contract_ids[0].admin_id.id
else:
emp.admin_id = False
def update_property_name(self):
property_ids = self.env["property.bank.statements"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in property_ids:
rec.compute_property_name()
@api.depends('contract_ids')
def compute_property_name(self):
for rec in self:
rec.property_name = ""
if rec.contract_ids:
for contract in rec.contract_ids:
if contract.property_name:
rec.property_name += contract.property_name + "\n"
def batch_revocation(self):
bank_ids = self.env["property.bank.statements"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in bank_ids:
rec.revocation()
def test_one(self):
bank_ids = self.env["property.bank.statements"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in bank_ids:
rec.start_time = rec.start_time - timedelta(hours=8)
def revocation(self):
self.ensure_one()
current_rec = self[0]
if current_rec.bank_state == 'reconciled':
domain = [
('your_own_unit', '=', current_rec.your_own_unit),
('bank_state', '=', 'reconciled'),
('name', '=', current_rec.name)
]
same_group_records = self.env[self._name].search(domain)
if same_group_records:
# 按start_time倒序排序,找到最近的记录
sorted_records = sorted(
same_group_records,
key=lambda r: r.start_time or '',
reverse=True
)
latest_record = sorted_records[0]
# 检查当前记录是否是最近的一条
if current_rec.id != latest_record.id:
raise UserError(
f"只能从最近的记录开始撤销!\n"
f"当前记录时间: {current_rec.start_time + timedelta(hours=8)}\n"
f"最近记录时间: {latest_record.start_time + timedelta(hours=8)}"
)
for modify in self.receivable_modify_ids:
if modify.is_write:
new_delivered = modify.receivable_id.delivered1 - modify.amount
modify.receivable_id.delivered1 = max(new_delivered, 0)
modify.receivable_id.is_reconciliation = False
modify.receivable_id.reconciliation_time = False
modify.receivable_id.payout_date = False
if not modify.is_penalty:
modify.receivable_id.remark = None
else:
modify.receivable_id.unlink()
if self.receivable_modify_ids:
self.receivable_modify_ids.unlink()
self.bank_state = 'not_reconciled'
self.registered = 0
self.no_amount = self.original_amount
self.yes_amount = 0
self.reconciliation_time = None
def binding_information(self):
return {
'name': '信息绑定弹窗',
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'binding.information.wizard',
'target': 'new',
'context': {
'default_bank_id': self.id,
}
}
def unbind(self):
statements_ids = self.env["property.bank.statements"].browse(
self._context.get('active_ids', self._context.get('active_id'))
)
for statement in statements_ids:
if statement.name:
bank_account = self.env['property.bank.line'].search([
('bank', '=', statement.name) # 假设statement.name存储银行账号
])
if bank_account.tenant_id:
# 获取需要删除的合同明细行
lines_to_delete = self.env['property.tenant.line'].search([
('tenant_id', '=', bank_account.tenant_id.id),
('lease_contract_id', 'in', statement.contract_ids.ids)
])
# 删除合同明细行
if lines_to_delete:
lines_to_delete.unlink()
statement.contract_ids = [(5,)]
statement.property_ids = [(5,)]
def contract_penalty(self):
statements_ids = self.env['property.bank.statements'].search([('is_put', '=', True)])
for record in statements_ids:
self._handle_contract_penalty(record)
# 违约金计算
def _handle_contract_penalty(self, record):
'''
处理合同违约金
:param record: property.bank.statements 记录
:return:
'''
for contract_id in record.contract_ids:
receivable_id = self.env['property.accounts.receivable'].search([('contract_id', '=', contract_id.id)])
if receivable_id:
dt = datetime.strptime(str(record.start_time), '%Y-%m-%d %H:%M:%S').date() # 流水交易时间
due = datetime.strptime(str(receivable_id.due_date), '%Y-%m-%d').date() + relativedelta(months=1, day=1) # 应收日期
# 判断是否违约
if receivable_id.submitted and not receivable_id.is_reconciliation or not receivable_id.is_suspend:
original_amount = (due - dt).days * (receivable_id.original_amount - record.original_amount) * int(receivable_id.percentage_liquidated_damages.name)
self.env['property.penalty.list'].create({
'contract_id': contract_id.id,
'property_id': contract_id.lease_ids[0].address_id.id,
'lessee_id': contract_id.lessee_id.id,
'original_amount': original_amount
})
receivable_id.is_reconciliation = True
def _get_reconciliation_vals(self, record):
if record.name:
dt = datetime.strptime(str(self.start_time), '%Y-%m-%d %H:%M:%S').date()
last_day = dt.replace(day=1) + relativedelta(months=1) - relativedelta(days=1)
total_rent_month = 0
# 应收租金
rent_ids = self.env['property.accounts.receivable'].search([
('contract_id', 'in', record.contract_ids.ids),
('contract_id.lessor_id.referred', '=', self.your_own_unit),
('management_type_id.name', '=', '租金'),
('is_reconciliation', '=', False),
('due_date', '<=', fields.Date.to_string(last_day)),
])
# 应收押金
deposit_ids = self.env['property.accounts.receivable'].search([
('contract_id', 'in', record.contract_ids.ids),
('contract_id.lessor_id.referred', '=', self.your_own_unit),
('management_type_id.name', '=', '押金'),
('is_reconciliation', '=', False),
('due_date', '<=', fields.Date.to_string(last_day)),
])
# 应收管理费
management_ids = self.env['property.accounts.receivable'].search([
('contract_id', 'in', record.contract_ids.ids),
('contract_id.lessor_id.referred', '=', self.your_own_unit),
('management_type_id.name', '=', '管理费'),
('is_reconciliation', '=', False),
('due_date', '<=', fields.Date.to_string(last_day)),
])
# 应收水费
water_ids = self.env['property.accounts.receivable'].search([
('contract_id', 'in', record.contract_ids.ids),
('contract_id.lessor_id.referred', '=', self.your_own_unit),
('management_type_id.name', '=', '水费'),
('is_reconciliation', '=', False),
('due_date', '<=', fields.Date.to_string(last_day)),
])
# 应收电费
electricity_ids = self.env['property.accounts.receivable'].search([
('contract_id', 'in', record.contract_ids.ids),
('contract_id.lessor_id.referred', '=', self.your_own_unit),
('management_type_id.name', '=', '电费'),
('is_reconciliation', '=', False),
('due_date', '<=', fields.Date.to_string(last_day)),
])
def create_reconciliation_vals(ids, collection_type_val, sequence_val):
"""
:param ids:费用记录集
:param collection_type_val: 费用类型(如 'rent', 'deposit')
:return: 格式化后的费用行列表
"""
nonlocal total_rent_month
vals_list = []
current_sequence = sequence_val
# 特殊处理:押金类型需要去重相同合同的记录
if collection_type_val == 'deposit':
# 使用字典来跟踪已处理的合同ID
processed_contracts = set()
for sub_record in ids:
contract_id = sub_record.contract_id.id if sub_record.contract_id else False
original_amount = sub_record.original_amount if sub_record.original_amount else 0
submitted = sub_record.submitted if sub_record.submitted else 0
# 跳过已处理的合同
if contract_id and contract_id in processed_contracts:
continue
# 获取合同的实际结束日期作为到期日
due_date = False
if contract_id:
due_date = sub_record.contract_id.actual_start_time
# 构建对账行数据
vals = {
'contract_id': contract_id,
'rent_month': original_amount,
'rent': submitted,
'collection_type': collection_type_val,
'sequence': current_sequence,
'due_date': due_date, # 押金类型使用合同实际开始日期
}
total_rent_month += original_amount # 累加总金额
vals_list.append((0, 0, vals))
# 标记此合同已处理
if contract_id:
processed_contracts.add(contract_id)
current_sequence += 10
else:
# 非押金类型:正常处理每条记录
for sub_record in ids:
contract_id = sub_record.contract_id.id if sub_record.contract_id else False
original_amount = sub_record.original_amount if sub_record.original_amount else 0
submitted = sub_record.submitted if sub_record.submitted else 0
due_date = sub_record.due_date if sub_record.due_date else False
vals = {
'contract_id': contract_id,
'rent_month': original_amount,
'rent': submitted,
'collection_type': collection_type_val,
'sequence': current_sequence,
'due_date': due_date,
}
total_rent_month += original_amount # 累加总金额
vals_list.append((0, 0, vals))
current_sequence += 10
return vals_list, current_sequence
all_vals = []
rent_vals, next_seq = create_reconciliation_vals(rent_ids, 'rent', 1)
deposit_vals, next_seq = create_reconciliation_vals(deposit_ids, 'deposit', 100)
management_vals, next_seq = create_reconciliation_vals(management_ids, 'management_fee', 200)
water_vals, next_seq = create_reconciliation_vals(water_ids, 'water_fee', 300)
electricity_vals, next_seq = create_reconciliation_vals(electricity_ids, 'electricity_fee', 400)
all_vals.extend(rent_vals)
all_vals.extend(deposit_vals)
all_vals.extend(management_vals)
all_vals.extend(water_vals)
all_vals.extend(electricity_vals)
return all_vals, total_rent_month
return [], 0
def _get_context_and_action(self, record, all_vals, total_rent_month):
"""
:param record: 银行收款记录
:param all_vals: 应收费用列表
:param total_rent_month: 应收总金额
:return:
"""
start_time = self.start_time + relativedelta(hours=8)
context = {
"default_actual_date": fields.Date.to_date(start_time),
"default_bank_id": self.id,
"default_bank_branch": self.bank_branch,
"default_account": record.name,
"default_actual_total": record.original_amount,
"default_reconciliation_line_ids": all_vals, # 租金,应收日期排序、管理费、电、水、押金
'default_amount_reconciled': self.no_amount,
"default_receivable_total": total_rent_month,
"dialog_size": "extra-large",
"default_order": "due_date desc",
}
return {
"type": "ir.actions.act_window",
"name": "对账弹窗",
"res_model": "property.reconciliation.wizard",
"view_mode": "form",
"target": "new",
"dialog_size": "extra-large",
"context": context,
}
def button_reconciliation(self):
for record in self:
all_vals, total_rent_month = self._get_reconciliation_vals(record)
return self._get_context_and_action(record, all_vals, total_rent_month)
def put_button(self):
form_id = self.env.ref('yuthon_property_approval.view_property_accounts_approval_form').id
bank_list = []
bank_list.append((0, 0, {
'start_time': self.start_time,
'company_id': self.company_id.id if self.company_id else False,
'name': self.name,
'bank_name': self.bank_name,
'name_account': self.name_account,
'purpose': self.purpose,
'journal_date': self.journal_date,
'bank_branch': self.bank_branch,
'extract': self.extract,
'account_code': self.account_code,
'is_put': self.is_put,
'remake': self.remake,
'original_amount': self.original_amount,
'contract_account': self.contract_account,
'contract_penalty': self.contract_penalty,
'property_ids': [(6, 0, self.property_ids.ids)] if self.property_ids else [],
'contract_ids': [(6, 0, self.contract_ids.ids)] if self.contract_ids else [],
'bank_number': self.bank_number,
'message': self.message,
'number': self.number,
'voucher_type': self.voucher_type
}))
return {
"type": "ir.actions.act_window",
"name": "应收账单生成审批单",
"res_model": "property.accounts.approval",
"views": [[form_id, "form"]],
"context": {
"default_bank_ids": bank_list,}}
class PropertyBankLine(models.Model):
_inherit = 'property.bank.line'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
# 应收账单
class PropertyAccountsReceivable(models.Model):
_inherit = 'property.accounts.receivable'
_rec_name = 'contract_id'
property_ids = fields.Many2many('yuthon.property', string='物业', compute='_compute_property_ids', store=True)
contract_accounts_id = fields.Many2one('contract.accounts.receivable', string="关联应收账单(合同)")
property_name = fields.Text(string='物业', compute="_compute_property_name")
is_rent = fields.Boolean(string='是否租金', default=True, compute = '_compute_is_rent', store=True)
management_area = fields.Float(related='contract_id.total_area', string='面积')
title_deed = fields.Char(string="产权证号", compute='_compute_title_deed')
lessee_id = fields.Many2one(related='contract_id.lessee_id', string='承租方', store=True)
company_id = fields.Many2one(related='contract_id.lessor_id', string="出租方公司", store=True)
# company_id1 = fields.Many2one(related='contract_id.admin_id.company_id', string="管理员公司", store=True)
company_id2 = fields.Many2one("res.company", string="公司", compute='_compute_company_id2', store=True)
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
contract_sort = fields.Selection(related='contract_id.contract_sort', string="资产类型", store=True)
admin_id = fields.Many2one(related='contract_id.admin_id', string="片区管理员", store=True)
tenure_type = fields.Selection(related='contract_id.tenure_type', string="权属类型", store=True)
precinct_id = fields.Many2one(related='contract_id.precinct_id', string="所属片区", store=True)
penalty = fields.Float(string="违约金基数")
penalty_date = fields.Date(string="违约金日期")
invoice_type = fields.Selection(
[('ordinary_invoice', '普通发票'), ('vat_invoice', '增值税发票')], string='发票类型', default='ordinary_invoice')
# 报表新增字段
rent_owed = fields.Float(string="欠租金额")
per_day = fields.Float(string="每天违约金(元/天)")
last_month_table = fields.Float(string="上月表数")
this_month_table = fields.Float(string="本月表数")
ratio = fields.Float(string="倍率")
billable_quantity = fields.Float(string="计费数量")
pre_collection = fields.Float(string="预收金额")
actual_coping = fields.Float(string="实际应付", compute='_compute_actual_coping', store=True)
@api.depends('company_id')
def _compute_company_id2(self):
for rec in self:
if not rec.company_id:
continue
if rec.company_id:
if rec.company_id.referred == '天河物管':
company = self.env['res.company'].search([('name', '=', '天河物管')], limit=1)
rec.company_id2 = company.id if company else False
elif rec.company_id.referred in ('天河投资', '天投城建'):
company = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
rec.company_id2 = company.id if company else False
@api.depends('original_amount', 'pre_collection')
def _compute_actual_coping(self):
for record in self:
if record.original_amount:
record.actual_coping = record.original_amount - record.pre_collection
else:
record.actual_coping = 0
def update_lessee_id(self):
# 1. 获取选中的应收账单记录
accounts_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id'))
)
if not accounts_ids:
return
unique_contracts = {
account.contract_id for account in accounts_ids
if account.contract_id
}
for contract in unique_contracts:
contract_account = self.env['contract.accounts.receivable'].search([
('contract_id', '=', contract.id)
], limit=1)
if not contract_account:
contract_account = self.env['contract.accounts.receivable'].create({
'contract_id': contract.id,
})
contract_account.onchange_contract_id()
else:
contract_account.onchange_contract_id()
@api.depends('management_type_id')
def _compute_is_rent(self):
for rec in self:
if rec.management_type_id.name == '租金':
rec.is_rent = True
else:
rec.is_rent = False
def penalty_wizard(self):
return {
'name': '合同违约金明细',
'type': 'ir.actions.act_window',
'res_model': 'penalty.line.wizard',
'view_mode': 'form',
'target': 'new',
}
@api.depends('contract_id')
def _compute_property_name(self):
for record in self:
record.property_name = record.contract_id.property_name
@api.depends('property_ids')
def _compute_title_deed(self):
for record in self:
title_deed = ""
if record.property_ids:
first_property = record.property_ids[0]
title_deed = first_property.title_deed or ""
record.title_deed = title_deed
def invalid(self):
for record in self:
# 处理多对多物业名称,用逗号拼接
property_names = ",".join(record.property_ids.mapped('name')) if record.property_ids else "未知物业"
receivable_log = (
f'当前登录人{self.env.user.name}于{fields.Datetime.now()}将物业为 {property_names}的应收账单状态修改为作废')
original_log = record.receivable_log or ''
new_log = f"{original_log}\n{receivable_log}" if original_log else receivable_log
record.write({
'is_invalid': True,
'receivable_log': new_log
})
return True
# 红冲
def all_red(self):
receivable_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id', []))
)
for receivable in receivable_ids:
property_names = ",".join(receivable.property_ids.mapped('name')) if receivable.property_ids else "未知物业"
receivable_log = (
f'当前登录人{self.env.user.name}于{fields.Datetime.now()}将物业为 {property_names}的应收账单红冲')
original_log = receivable.receivable_log or ''
new_log = f"{original_log}\n{receivable_log}" if original_log else receivable_log
receivable.write({
'is_red': True,
'red_state_date': fields.Date.today(),
'receivable_log': new_log
})
return True
@api.depends('contract_id', 'contract_id.lease_ids', 'contract_id.lease_ids.address_id')
def _compute_property_ids(self):
for record in self:
property_ids = self.env['yuthon.property']
if record.contract_id and record.contract_id.lease_ids:
property_ids = record.contract_id.lease_ids.mapped('address_id')
record.property_ids = property_ids
def update_property_ids(self):
receivable_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for receivable in receivable_ids:
receivable._compute_property_ids()
def de_duplicate(self):
# 查询所有记录并按 id 升序排列
records = self.search([], order='id ASC')
# 用于存储已存在的组合
unique_combinations = set()
records_to_delete = self.browse() # 创建一个空记录集
# 遍历所有记录,找出重复项
for record in records:
combination = (
record.contract_id.id,
record.property_id.id,
record.lessee_id.id,
record.due_date
)
# 如果组合已存在,则添加到删除列表
if combination in unique_combinations:
records_to_delete += record
else:
# 否则添加到唯一组合集合
unique_combinations.add(combination)
# 删除重复记录
if records_to_delete:
records_to_delete.unlink()
def initialize_reconciliation(self):
receivable_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for receivable in receivable_ids:
receivable.delivered1 = receivable.submitted
receivable.submitted = 0
receivable.is_reconciliation = True
due_year = receivable.due_date.year
due_month = receivable.due_date.month
# 构建日期范围条件
import calendar
last_day = calendar.monthrange(due_year, due_month)[1]
# 构建日期范围条件(使用正确的最后一天)
start_date = f"{due_year}-{due_month:02d}-01"
end_date = f"{due_year}-{due_month:02d}-{last_day}"
# 搜索条件使用大于等于当月第一天,小于等于当月最后一天
statements = self.env['property.bank.statements'].search([
('name_account', '=', receivable.lessee_id.name),
('bank_state', '=', 'reconciled'),
('start_time', '>=', start_date),
('start_time', '<=', end_date),
])
if statements:
for statement in statements:
receivable.payout_date = statement.start_time.date()
else:
receivable.payout_date = receivable.due_date
@api.model_create_multi
def create(self, vals_list):
records = super().create(vals_list)
for record in records:
if record.lessee_id and record.contract_id:
lease_address_ids = record.contract_id.lease_ids.mapped('address_id.id')
property_ids_value = [(6, 0, lease_address_ids)] if lease_address_ids else [(6, 0, [])]
statements = self.env['property.bank.statements'].search([
('name_account', '=', record.lessee_id.name),
('bank_state', '=', 'not_reconciled'),
])
contract_id_value = [(4, record.contract_id.id)] if record.contract_id else []
for statement in statements:
statement.write({
'property_ids': property_ids_value,
'contract_ids': contract_id_value,
})
# contract_account_map = {}
# for vals in vals_list:
# if 'contract_id' in vals and vals['contract_id']:
# contract_id = vals['contract_id']
# if contract_id not in contract_account_map:
# contract_account = self.env['contract.accounts.receivable'].search([
# ('contract_id', '=', contract_id)
# ], limit=1)
# if not contract_account:
# contract_account = self.env['contract.accounts.receivable'].create({
# 'contract_id': contract_id
# })
# contract_account.onchange_contract_id()
return records
@api.onchange('penalty_date')
def _onchange_penalty_date(self):
for record in self:
record.write({
'is_reconciliation': True
})
def all_penalty_date(self):
receivable_ids= self.env["property.accounts.receivable"].browse(self._context.get('active_ids', self._context.get('active_id')))
for receivable in receivable_ids:
if receivable.receivable_id.create_time and receivable.penalty_date:
create_time = receivable.receivable_id.create_time
dt = datetime.strptime(str(create_time), '%Y-%m-%d').date()
new_month = dt.month + 1
new_year = dt.year
if new_month > 12:
new_month = 1
new_year += 1
new_date = dt.replace(year=new_year, month=new_month, day=1)
penalty_date = datetime.strptime(str(receivable.penalty_date), '%Y-%m-%d').date()
if penalty_date.year == datetime.now().year - 1:
raise UserWarning("选择的日期为去年的年份,请重新选择!")
days_difference = (penalty_date - new_date).days
penalty = days_difference * receivable.receivable_id.difference_amount
receivable.receivable_id.write({'penalty': penalty})
# 批量撤销
def batch_receivable_return(self):
archives_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in archives_ids:
# 银行流水对应的已对账改为未对账
due_year = rec.due_date.year
due_month = rec.due_date.month
import calendar
last_day = calendar.monthrange(due_year, due_month)[1]
# 构建日期范围条件(使用正确的最后一天)
start_date = f"{due_year}-{due_month:02d}-01"
end_date = f"{due_year}-{due_month:02d}-{last_day}"
# 搜索条件使用大于等于当月第一天,小于等于当月最后一天
statements = self.env['property.bank.statements'].search([
('name_account', '=', rec.lessee_id.name),
('bank_state', '=', 'reconciled'),
('start_time', '>=', start_date),
('start_time', '<=', end_date),
])
rec.is_reconciliation = False
for statement in statements:
statement.write({'bank_state': 'not_reconciled'})
# 批量退回
def action_refuse_all(self):
model_type_data = {
'水费': 'meter.archives.line',
'电费': 'meter.water.line',
'押金': 'property.deposit.list',
'租金': 'property.rent.list'
}
archives_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in archives_ids:
model_type = model_type_data.get(rec.management_type_id.name)
if model_type:
model_id = self.env[model_type].search([('contract_id', '=', rec.contract_id.id)])
if model_id:
model_id.write({'is_push': False})
rec.unlink()
def button_accounts_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "操作弹窗",
"res_model": "accounts.wizard",
"view_mode": "form",
"target": "new",
"context": {
"default_accounts_id": self.id
}
}
# 租金
class PropertyRentList(models.Model):
_inherit = 'property.rent.list'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
tenure_type = fields.Selection(related='contract_id.tenure_type', string="权属类型", store=True)
property_address = fields.Text(string="物业信息", compute='get_property_address')
contract_sort = fields.Selection(related='contract_id.contract_sort', string="资产类型", store=True)
company_id = fields.Many2one(related='contract_id.lessor_id', string="出租方公司", store=True)
@api.depends('contract_id')
def get_property_address(self):
for rec in self:
rec.property_address = rec.contract_id.property_name
def clear_the_data(self):
archives_ids = self.env["property.rent.list"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in archives_ids:
first_day_of_month = rec.due_date.replace(day=1)
last_day_of_month = first_day_of_month + relativedelta(months=1) - timedelta(days=1)
existing_receivable = self.env['property.accounts.receivable'].search([
('lessee_id', '=', rec.lessee_id.id),
('due_date', '>=', first_day_of_month),
('due_date', '<=', last_day_of_month),
('contract_id', '=', rec.id),
], limit=1)
if existing_receivable:
rec.unlink()
def push_rent(self):
current_user = self.env.user
is_admin = current_user.has_group('base.group_system') or current_user.has_group('base.group_admin')
for rec in self:
if not is_admin and rec.admin_id.id != current_user.id:
raise UserError(f"您只能推送自己记录")
management_type = self.env['yuthon.management.type'].search([('name', '=', '租金')], limit=1)
if not management_type:
management_type = self.env['yuthon.management.type'].create({'name': '租金'})
due_date = rec.due_date
due_date_end = False
theoretical_end = False
contract = rec.contract_id
if contract and contract.cycle_mode and due_date:
due_date_obj = fields.Date.from_string(due_date)
contract_actual_end = fields.Date.from_string(
contract.actual_end_time) if contract.actual_end_time else None
# 处理合同月
if contract.cycle_mode == 'fixed_monthly':
# 计算理论应收结束日(当月最后一天)
next_month_first = (due_date_obj+ relativedelta(months=1))
theoretical_end = next_month_first - timedelta(days=1)
# 处理自然月
elif contract.cycle_mode == 'floating_monthly':
breakdown_line = self.env['property.rent.breakdown.line'].search([
('breakdown_id', '=', contract.id),
('start_data', '<=', due_date),
('end_data', '>=', due_date)
], limit=1)
if breakdown_line:
start_data_obj = fields.Date.from_string(breakdown_line.start_data)
if not (due_date_obj.year == start_data_obj.year and
due_date_obj.month == start_data_obj.month):
due_date = fields.Date.to_string(due_date_obj.replace(day=1))
due_date_obj = fields.Date.from_string(due_date)
last_day = calendar.monthrange(due_date_obj.year, due_date_obj.month)[1]
theoretical_end = date(due_date_obj.year, due_date_obj.month, last_day)
if contract_actual_end:
due_date_end = fields.Date.to_string(min(theoretical_end, contract_actual_end))
else:
due_date_end = fields.Date.to_string(theoretical_end)
# 创建应收记录
self.env['property.accounts.receivable'].create({
'contract_id': contract.id if contract else False,
'lessee_id': contract.lessee_id.id if contract and contract.lessee_id else None,
'original_amount': rec.original_amount,
'due_date': due_date,
'acc_type': rec.acc_type,
'due_date_end': due_date_end,
'create_time': datetime.now(),
'management_type_id': management_type.id,
})
rec.is_push = True
self.reconcile_with_bank_statements(rec)
def batch_push_rent(self):
archives_ids = self.env["property.rent.list"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rec in archives_ids:
rec.push_rent()
def reconcile_with_bank_statements(self, rec):
bank_accounts = self.env['property.bank.line'].search([
('tenant_id', '=', rec.contract_id.lessee_id.id)
])
if not bank_accounts:
return
if not rec.property_id:
return
# 构建银行对账单搜索条件
bank_domain = [
('name', 'in', [account.bank for account in bank_accounts]),
]
# 查找匹配的银行对账单
matching_statements = self.env['property.bank.statements'].search(bank_domain)
# 更新银行对账单,关联合同和物业
for statement in matching_statements:
# 添加合同ID(如果不存在)
if rec.contract_id.id not in statement.contract_ids.ids:
statement.write({
'contract_ids': [(4, rec.contract_id.id)], # 添加关系
})
# 添加物业ID(如果不存在)
if rec.property_id.id not in statement.property_ids.ids:
statement.write({
'property_ids': [(4, rec.property_id.id)], # 添加关系
})
# 押金
class PropertyDepositList(models.Model):
_inherit = 'property.deposit.list'
contract_id = fields.Many2one('property.lease.contract', string="合同")
due_date = fields.Date(related='contract_id.actual_start_time', string='应收日期', store=True)
posit_type = fields.Selection([('deposit', '押金'), ('performance_bonds', '经营履约保证金'),
('utility_bills', '水电费'), ('decorate', '装修')], string="保证金类型")
original_amount = fields.Float(string="金额")
tenure_type = fields.Selection(related='contract_id.tenure_type', string="权属类型", store=True)
company_id = fields.Many2one(related='contract_id.lessor_id', string="出租方公司", store=True)
def push_deposit(self):
for rec in self:
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1)
if not management_type_id:
management_type_id = self.env['yuthon.management.type'].create({'name': '押金'})
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'due_date' : rec.due_date,
'lessee_id':rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount' : rec.original_amount,
'create_time': datetime.now(),
'management_type_id':management_type_id.id,
})
rec.is_push = True
def batch_push_deposit(self):
archives_ids = self.env["property.deposit.list"].browse(
self._context.get('active_ids', self._context.get('active_id')))
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1)
for rec in archives_ids:
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'due_date': rec.due_date,
'lessee_id': rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount': rec.original_amount,
'create_time': datetime.now(),
'management_type_id': management_type_id.id,
})
rec.is_push = True
# 管理费
class PropertyManagementList(models.Model):
_inherit = 'property.management.list'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
company_id = fields.Many2one(related='contract_id.lessor_id', string="出租方公司", store=True)
tenure_type = fields.Selection(related='contract_id.tenure_type', string="权属类型", store=True)
def push_management(self):
for rec in self:
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '管理费')], limit=1)
if not management_type_id:
management_type_id = self.env['yuthon.management.type'].create({'name': '管理费'})
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'lessee_id':rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount' : rec.original_amount,
'due_date':rec.due_date,
'create_time': datetime.now(),
'management_type_id':management_type_id.id,
})
rec.is_push = True
def batch_push_management(self):
archives_ids = self.env["property.management.list"].browse(
self._context.get('active_ids', self._context.get('active_id')))
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '电费')], limit=1)
for rec in archives_ids:
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'lessee_id': rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount': rec.original_amount,
'create_time': datetime.now(),
'management_type_id': management_type_id.id,
})
rec.is_push = True
# 电费
class PropertyTariffList(models.Model):
_inherit = 'property.tariff.list'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
admin_id = fields.Many2one(related='contract_id.admin_id', string="片区管理员", store=True)
property_name = fields.Char(related='contract_id.property_name', string="物业信息", store=True)
company_id = fields.Many2one(related='contract_id.lessor_id', string="出租方公司", store=True)
tenure_type = fields.Selection(related='contract_id.tenure_type', string="权属类型", store=True)
def push_tariff(self):
for rec in self:
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '电费')], limit=1)
if not management_type_id:
management_type_id = self.env['yuthon.management.type'].create({'name': '电费'})
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'lessee_id':rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount' : rec.original_amount,
'due_date': rec.due_date,
'create_time': datetime.now(),
'management_type_id':management_type_id.id,
})
rec.is_push = True
def batch_push_tariff(self):
archives_ids = self.env["property.tariff.list"].browse(
self._context.get('active_ids', self._context.get('active_id')))
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '电费')], limit=1)
for rec in archives_ids:
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'lessee_id': rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount': rec.original_amount,
'due_date': rec.due_date,
'create_time': datetime.now(),
'management_type_id': management_type_id.id,
})
rec.is_push = True
# 水费
class PropertyWaterList(models.Model):
_inherit = 'property.water.list'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")
admin_id = fields.Many2one(related='contract_id.admin_id', string="片区管理员", store=True)
property_name = fields.Char(related='contract_id.property_name', string="物业信息")
company_id = fields.Many2one(related='contract_id.lessor_id', string="出租方公司", store=True)
tenure_type = fields.Selection(related='contract_id.tenure_type', string="权属类型", store=True)
def push_water(self):
for rec in self:
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '水费')], limit=1)
if not management_type_id:
management_type_id = self.env['yuthon.management.type'].create({'name': '水费'})
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'lessee_id': rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount': rec.original_amount,
'create_time': datetime.now(),
'management_type_id': management_type_id.id,
})
rec.is_push = True
def batch_push_water(self):
archives_ids = self.env["property.water.list"].browse(
self._context.get('active_ids', self._context.get('active_id')))
management_type_id = self.env['yuthon.management.type'].search([('name', '=', '水费')], limit=1)
for rec in archives_ids:
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'lessee_id': rec.contract_id.lessee_id.id if rec.contract_id.lessee_id else None,
'original_amount': rec.original_amount,
'create_time': datetime.now(),
'management_type_id': management_type_id.id,
})
rec.is_push = True
class PropertyPenaltyList(models.Model):
_inherit = 'property.penalty.list'
contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同审批")