From 301cd54c3c43ee57042249296dafe25070ed088e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?=
<9664676+pengyuthon@user.noreply.gitee.com>
Date: Wed, 19 Nov 2025 11:53:53 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../models/property_accounts_receivable.py | 16 ++++
.../models/contract_accounts_receivable.py | 86 +++++++++++++++----
.../models/inherit_yuthon_property.py | 1 +
.../models/property_lease_contract.py | 1 +
.../models/property_lease_renewal.py | 3 +-
.../contract_accounts_receivable_viwes.xml | 7 ++
.../views/inherit_yuthon_property_views.xml | 4 +
.../views/property_lease_contract.xml | 18 ++++
.../views/property_lease_renewal.xml | 1 +
9 files changed, 117 insertions(+), 20 deletions(-)
diff --git a/yuthon_property/models/property_accounts_receivable.py b/yuthon_property/models/property_accounts_receivable.py
index b04afd19f..dc510fe2f 100644
--- a/yuthon_property/models/property_accounts_receivable.py
+++ b/yuthon_property/models/property_accounts_receivable.py
@@ -22,6 +22,7 @@ class PropertyAccountsReceivable(models.Model):
('12', '十二月')], string='选择月份')
original_amount = fields.Float(string='当期金额')
discount_amount = fields.Float(string='减免金额')
+ discount_date = fields.Date(string='减免日期')
multiplier = fields.Integer(string='季度')
remark = fields.Text(string='备注')
red_date = fields.Date(string='红冲日期')
@@ -50,6 +51,21 @@ class PropertyAccountsReceivable(models.Model):
receivable_log = fields.Text(string='应收日志')
+ @api.onchange('is_invalid')
+ def _onchange_is_invalid(self):
+ if self.is_invalid:
+ self.void_date = fields.Date.today()
+ else:
+ self.void_date = False
+
+ @api.depends('discount_amount')
+ def _onchange_discount_amount(self):
+ for record in self:
+ if record.discount_amount:
+ record.discount_date = fields.Date.today()
+ else:
+ record.discount_date = False
+
def update_submitted(self):
record_ids = self.env["property.accounts.receivable"].browse(
self._context.get("active_ids", self._context.get("active_id"))
diff --git a/yuthon_property_approval/models/contract_accounts_receivable.py b/yuthon_property_approval/models/contract_accounts_receivable.py
index 3608398c1..96e66a3ea 100644
--- a/yuthon_property_approval/models/contract_accounts_receivable.py
+++ b/yuthon_property_approval/models/contract_accounts_receivable.py
@@ -1,4 +1,5 @@
from odoo import models, fields, api
+from datetime import datetime, timedelta, date
import json
@@ -10,6 +11,7 @@ class ContractAccountsReceivable(models.Model):
lessee_id = fields.Many2one(related="contract_id.lessee_id", string='承租方')
property_name = fields.Char(related="contract_id.property_name", string='物业信息')
contract_accounts_ids = fields.One2many('property.accounts.receivable', 'contract_accounts_id', string='关联应收账单')
+ contract_date = fields.Date(string="日期")
is_print = fields.Boolean(string="打印")
json_display = fields.Char(
@@ -18,26 +20,72 @@ class ContractAccountsReceivable(models.Model):
store=True
)
- # @api.onchange('contract_id')
- # def onchange_contract_id(self):
- # accounts = self.env['property.accounts.receivable'].search([('contract_id', '=', self.contract_id.id)])
- # receivable_list = []
- # for account in accounts:
- # receivable_list.append([0, 0, {
- # 'contract_accounts_id': account.id,
- # 'management_type_id': account.management_type_id.id,
- # 'last_month_table': account.last_month_table,
- # 'this_month_table': account.this_month_table,
- # 'ratio': account.ratio,
- # 'billable_quantity': account.billable_quantity,
- # 'due_date': account.due_date,
- # 'due_date_end': account.due_date_end,
- # 'original_amount': account.original_amount,
- # 'pre_collection': account.pre_collection,
- # 'remark': account.remark,
- # }])
- # self.contract_accounts_ids = receivable_list
+ def batch_create_by_contract_and_month(self):
+ """
+ 按“合同+年月”分组创建账单,创建时即设置contract_date为分组最早due_date
+ """
+ all_receivables = self.env['property.accounts.receivable'].search([
+ ('contract_id', '!=', False),
+ ('due_date', '!=', False),
+ ('is_invalid', '=', False),
+ ('is_reconciliation', '=', True)
+ ])
+ # 按“合同ID+年月”分组
+ grouped_data = {}
+ for receivable in all_receivables:
+ year_month = receivable.due_date.strftime('%Y-%m')
+ key = (receivable.contract_id.id, year_month)
+ if key not in grouped_data:
+ grouped_data[key] = self.env['property.accounts.receivable']
+ grouped_data[key] += receivable
+ for (contract_id, year_month), receivables in grouped_data.items():
+ if not contract_id:
+ continue
+ earliest_due_date = min(receivables.mapped('due_date'))
+ start_date, end_date = self._get_month_date_range(year_month)
+ existing_bill = self.search([
+ ('contract_id', '=', contract_id),
+ ('contract_date', '>=', start_date),
+ ('contract_date', '<=', end_date)
+ ], limit=1)
+ if not existing_bill:
+ existing_bill = self.create({
+ 'contract_id': contract_id,
+ 'contract_date': earliest_due_date,
+ 'is_print': False
+ })
+ accounts = self.env['property.accounts.receivable'].search([('contract_id', '=', contract_id),
+ ('due_date', '>=', start_date),
+ ('due_date', '<', end_date),
+ ('is_invalid', '=', False),
+ ('is_reconciliation', '=', True)
+ ])
+ receivable_list = []
+ for account in accounts:
+ receivable_list.append([0, 0, {
+ 'contract_accounts_id': existing_bill.id,
+ 'management_type_id': account.management_type_id.id,
+ 'last_month_table': account.last_month_table,
+ 'this_month_table': account.this_month_table,
+ 'ratio': account.ratio,
+ 'billable_quantity': account.billable_quantity,
+ 'due_date': account.due_date,
+ 'due_date_end': account.due_date_end,
+ 'original_amount': account.original_amount,
+ 'pre_collection': account.pre_collection,
+ 'remark': account.remark,
+ }])
+ existing_bill.contract_accounts_ids = receivable_list
+
+
+ def _get_month_date_range(self, year_month):
+ """获取年月对应的起止日期"""
+ year, month = map(int, year_month.split('-'))
+ start_date = fields.Date.from_string(f"{year}-{month}-01")
+ next_month = f"{year + 1}-01-01" if month == 12 else f"{year}-{month + 1}-01"
+ end_date = fields.Date.from_string(next_month) - timedelta(days=1)
+ return start_date, end_date
@api.depends(
'contract_accounts_ids.management_type_id',
diff --git a/yuthon_property_approval/models/inherit_yuthon_property.py b/yuthon_property_approval/models/inherit_yuthon_property.py
index 1f10de41b..39ceb4509 100644
--- a/yuthon_property_approval/models/inherit_yuthon_property.py
+++ b/yuthon_property_approval/models/inherit_yuthon_property.py
@@ -623,6 +623,7 @@ class PropertyAccountsReceivable(models.Model):
_rec_name = 'contract_id'
property_ids = fields.Many2many('yuthon.property', string='物业', compute='_compute_property_ids', store=True)
+ lease_contract_id = fields.Many2one('property.lease.contract', string="关联租赁合同")
contract_accounts_id = fields.Many2one('contract.accounts.receivable', string="合同")
property_name = fields.Text(string='物业', compute="_compute_property_name")
is_penalty = fields.Boolean(string='是否违约金', default=True, compute = '_compute_is_penalty', store=True)
diff --git a/yuthon_property_approval/models/property_lease_contract.py b/yuthon_property_approval/models/property_lease_contract.py
index 363ae0e39..c83d93d82 100644
--- a/yuthon_property_approval/models/property_lease_contract.py
+++ b/yuthon_property_approval/models/property_lease_contract.py
@@ -107,6 +107,7 @@ class PropertyLeaseContract(models.Model):
string="租赁合同文件"
)
title_address = fields.Char(string="产权地址")
+ receivable_ids = fields.One2many('property.accounts.receivable', 'lease_contract_id', string="应收账单")
handling_opinions = fields.Text(string="经办意见")
lessor_id = fields.Many2one('yuthon.property.company', string="出租方公司")
rent_company = fields.Char(related='lessor_id.name', string="收租公司名称")
diff --git a/yuthon_property_approval/models/property_lease_renewal.py b/yuthon_property_approval/models/property_lease_renewal.py
index dbe2f43ee..e5e8e5a7c 100644
--- a/yuthon_property_approval/models/property_lease_renewal.py
+++ b/yuthon_property_approval/models/property_lease_renewal.py
@@ -143,4 +143,5 @@ class EarlyReceivablesLine(models.Model):
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
management_type_id = fields.Many2one(related='receivable_id.management_type_id', string='类型')
due_date = fields.Date(related='receivable_id.due_date', string='应收日期')
- original_amount = fields.Float(related='receivable_id.original_amount', string='金额')
\ No newline at end of file
+ original_amount = fields.Float(related='receivable_id.original_amount', string='金额')
+ void = fields.Boolean(string='作废', default=False)
\ No newline at end of file
diff --git a/yuthon_property_approval/views/contract_accounts_receivable_viwes.xml b/yuthon_property_approval/views/contract_accounts_receivable_viwes.xml
index 6097e8708..becdf6558 100644
--- a/yuthon_property_approval/views/contract_accounts_receivable_viwes.xml
+++ b/yuthon_property_approval/views/contract_accounts_receivable_viwes.xml
@@ -47,6 +47,13 @@
+
+
+ 更新
+
+
+ code
+ action = model.batch_create_by_contract_and_month()
应收账单(合同)
diff --git a/yuthon_property_approval/views/inherit_yuthon_property_views.xml b/yuthon_property_approval/views/inherit_yuthon_property_views.xml
index 8c45e0f40..8f1b070d5 100644
--- a/yuthon_property_approval/views/inherit_yuthon_property_views.xml
+++ b/yuthon_property_approval/views/inherit_yuthon_property_views.xml
@@ -403,6 +403,10 @@
+
+
+
+
diff --git a/yuthon_property_approval/views/property_lease_contract.xml b/yuthon_property_approval/views/property_lease_contract.xml
index 5828ab609..952985e0c 100644
--- a/yuthon_property_approval/views/property_lease_contract.xml
+++ b/yuthon_property_approval/views/property_lease_contract.xml
@@ -839,6 +839,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yuthon_property_approval/views/property_lease_renewal.xml b/yuthon_property_approval/views/property_lease_renewal.xml
index b67ad84d3..f2b29af0e 100644
--- a/yuthon_property_approval/views/property_lease_renewal.xml
+++ b/yuthon_property_approval/views/property_lease_renewal.xml
@@ -99,6 +99,7 @@
+