From a302eaa1d7b4ce2bc8f716f6675a2c8c8af9b151 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, 29 Oct 2025 11:15:46 +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/inherit_yuthon_property.py | 87 ++++++++++++++++++- .../views/inherit_yuthon_property_views.xml | 3 + .../wizard/property_reconciliation_wizard.py | 34 +++++++- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/yuthon_property_approval/models/inherit_yuthon_property.py b/yuthon_property_approval/models/inherit_yuthon_property.py index e7a5935b3..32994dfd7 100644 --- a/yuthon_property_approval/models/inherit_yuthon_property.py +++ b/yuthon_property_approval/models/inherit_yuthon_property.py @@ -342,7 +342,6 @@ class PropertyBankStatements(models.Model): def _get_reconciliation_vals(self, record): if record.name: - dt = datetime.strptime(str(self.start_time), '%Y-%m-%d %H:%M:%S').date() total_rent_month = 0 # 应收租金 rent_ids = self.env['property.accounts.receivable'].search([ @@ -366,6 +365,21 @@ class PropertyBankStatements(models.Model): ('management_type_id.name', '=', '管理费'), ('is_reconciliation', '=', False), ]) + # 应收灯箱 + lightbox_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), + ]) + # 应收管子间 + guanzi_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), + ]) + # 应收水费 water_ids = self.env['property.accounts.receivable'].search([ ('contract_id', 'in', record.contract_ids.ids), @@ -447,11 +461,15 @@ class PropertyBankStatements(models.Model): 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) + lightbox_vals, next_seq = create_reconciliation_vals(lightbox_ids, 'lightbox', 220) + guanzi_vals, next_seq = create_reconciliation_vals(guanzi_ids, 'guanzijian', 230) 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(lightbox_vals) + all_vals.extend(guanzi_vals) all_vals.extend(management_vals) all_vals.extend(water_vals) all_vals.extend(electricity_vals) @@ -598,6 +616,36 @@ class PropertyAccountsReceivable(models.Model): pre_collection = fields.Float(string="预收金额") actual_coping = fields.Float(string="实际应付", compute='_compute_actual_coping', store=True) + charging_range = fields.Text(string="收费区间") + + def update_charging_range(self): + accounts = self.env["property.accounts.receivable"].browse( + self._context.get('active_ids', self._context.get('active_id', [])) + ) + for account in accounts: + type_name = account.management_type_id.name or "" + def get_charging_range(start, end): + if not start or not end: + return "" # 日期不全时返回空 + return f"{start.strftime('%Y-%m-%d')} 到 {end.strftime('%Y-%m-%d')}" + + # 按类型计算收费区间 + if type_name == "租金": + if account.due_date_end: + contract_actual_end = account.contract_id.actual_end_time + min_end_date = min(contract_actual_end, account.due_date_end) if contract_actual_end else account.due_date_end + account.charging_range = get_charging_range(account.due_date, min_end_date) + + elif type_name == "押金": + start_date = account.due_date + if start_date: + end_date = start_date + relativedelta(months=1) + account.charging_range = get_charging_range(start_date, end_date) + + elif type_name == "违约金": + if account.due_date_end: + account.charging_range = get_charging_range(account.due_date, account.due_date_end) + @api.depends('company_id') def _compute_company_id2(self): @@ -1123,11 +1171,48 @@ class PropertyManagementList(models.Model): ) if not management_type_id: management_type_id = self.env['yuthon.management.type'].create({'name': target_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': 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, + 'due_date_end': due_date_end, 'create_time': datetime.now(), 'management_type_id': management_type_id.id, }) diff --git a/yuthon_property_approval/views/inherit_yuthon_property_views.xml b/yuthon_property_approval/views/inherit_yuthon_property_views.xml index 159e3f57a..086b1a765 100644 --- a/yuthon_property_approval/views/inherit_yuthon_property_views.xml +++ b/yuthon_property_approval/views/inherit_yuthon_property_views.xml @@ -405,6 +405,9 @@ + + + diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py index 9dfd1636b..0b09e5bb5 100644 --- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py +++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py @@ -366,6 +366,8 @@ class PropertyReconciliationWizard(models.Model): collection_type_mapping = { 'deposit': '押金', 'rent': '租金', + 'lightbox': '灯箱', + 'guanzijian': '管子间', 'water_fee': '水费', 'electricity_fee': '电费', 'penalty': '违约金', @@ -430,6 +432,27 @@ class PropertyReconciliationWizard(models.Model): domain.append(('contract_id', '=', line.contract_id.id)) receivable_ids = self.env['property.accounts.receivable'].search(domain) for receivable in receivable_ids: + charging_range = receivable.charging_range + type_name = receivable.management_type_id.name or "" + if receivable.due_date_end: + def get_range(start, end): + if start and end: + return f"{start.strftime('%Y-%m-%d')} 到 {end.strftime('%Y-%m-%d')}" + return "" + if type_name in ["租金", "管理费", '灯箱', '管子间']: + contract_actual_end = receivable.contract_id.actual_end_time + min_end = min(contract_actual_end, receivable.due_date_end) if contract_actual_end else receivable.due_date_end + charging_range = get_range(receivable.due_date, min_end) + elif type_name == "押金": + if receivable.due_date: + end_date = receivable.due_date + relativedelta(months=1) + charging_range = get_range(receivable.due_date, end_date) + elif type_name in ["水费", "电费"]: + if receivable.due_date: + start_date = (receivable.due_date - relativedelta(months=2)).replace(day=16) + end_date = (start_date + relativedelta(months=1)).replace(day=15) + charging_range = get_range(start_date, end_date) + if line.rent == line.amount: is_reconciliation = True else: @@ -440,13 +463,16 @@ class PropertyReconciliationWizard(models.Model): final_remark = line.remake if line.collection_type != 'penalty': receivable.delivered1 = receivable.delivered1 + line.amount - receivable.write({ + write_vals = { 'is_reconciliation': is_reconciliation, 'payout_date': rec.actual_date, 'remark': final_remark, 'reconciliation_time': fields.Datetime.now(), 'receivable_log': f'当前登录人{rec.env.user.name}于{fields.Datetime.now()}对户名为{rec.bank_id.name_account}进行对账操作,对账应收金额为{rec.receivable_total},实际金额为{rec.amount_water}' - }) + } + if charging_range: + write_vals['charging_range'] = charging_range + receivable.write(write_vals) self.env['receivable.modify.line'].create({ 'bank_id': rec.bank_id.id, 'not_collected': line.rent, @@ -489,6 +515,7 @@ class PropertyReconciliationWizard(models.Model): 'payout_date': rec.actual_date if (line.rent == line.amount and line.rent != 0) else None, 'original_amount': line.rent, 'remark': line.remake, + 'charging_range': f"{extract_start_date.strftime('%Y-%m-%d')} 到 {extract_end_date.strftime('%Y-%m-%d')}", 'reconciliation_time': fields.Datetime.now(), }) else: @@ -542,6 +569,7 @@ class PropertyReconciliationWizard(models.Model): rec.contract_ids = [(6, 0, remaining_contract_ids)] rec.contract_ids1 = [(6, 0, removed_contract_ids)] + class PropertyReconciliationLine(models.Model): _name = 'property.reconciliation.line' _description = '对账明细' @@ -556,7 +584,7 @@ class PropertyReconciliationLine(models.Model): due_date = fields.Date(string='应收日期') rent = fields.Float(string='未收') amount = fields.Float(string='金额', store=True) - collection_type = fields.Selection([('deposit', '押金'), ('rent', '租金'), ('water_fee', '水费'), + collection_type = fields.Selection([('deposit', '押金'), ('rent', '租金'), ('water_fee', '水费'), ('lightbox', '灯箱'), ('guanzijian', '管子间'), ('electricity_fee', '电费'), ('penalty', '违约金'), ('management_fee', '管理费'), ('repair_fee', '维修费'), ('other', '其他')], string='收款类型', store=True) remake = fields.Text(string='备注')