diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py index 147f93529..4c5abe957 100644 --- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py +++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py @@ -2,6 +2,7 @@ from odoo import models, fields, api, _, SUPERUSER_ID from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta from odoo.exceptions import UserError, ValidationError +import re class PropertyReconciliationWizard(models.Model): @@ -63,7 +64,6 @@ class PropertyReconciliationWizard(models.Model): 'collection_type': 'penalty', 'remake': penalty.remark, }) - start_time = self.bank_id.start_time first_day_of_last_month = (start_time.replace(day=1) - relativedelta(months=1)) last_day_of_last_month = start_time.replace(day=1) - relativedelta(days=1) @@ -107,6 +107,7 @@ class PropertyReconciliationWizard(models.Model): # 3. 添加到总列表 for vals in contract_penalties: new_lines.append((0, 0, vals)) + if new_lines: keep_lines = [ (4, line.id) for line in self.reconciliation_line_ids @@ -123,6 +124,73 @@ class PropertyReconciliationWizard(models.Model): remaining_amount = self.actual_total + rent_lines_key = set() + target_rent_months = [] + for line in self.reconciliation_line_ids: + if line.collection_type == 'rent' and line.amount == 0 and line.due_date: + # 提取到期日期的年月(作为分组依据) + due_month = fields.Date.from_string(line.due_date).strftime('%Y-%m') + line_key = ( + line.contract_id.id if line.contract_id else None, + line.property_id.id if line.property_id else None, + due_month + ) + if line_key not in rent_lines_key: + rent_lines_key.add(line_key) + target_rent_months.append({ + 'contract_id': line.contract_id, + 'property_id': line.property_id, + 'due_month': due_month # 待补违约金的年月 + }) + + existing_penalty_months = set() + for line in self.reconciliation_line_ids: + if line.collection_type == 'penalty' and line.due_date: + penalty_month = fields.Date.from_string(line.due_date).strftime('%Y-%m') + penalty_key = ( + line.contract_id.id if line.contract_id else None, + line.property_id.id if line.property_id else None, + penalty_month + ) + existing_penalty_months.add(penalty_key) + + # 3. 补全缺失的跨月违约金(按合同+物业+年月匹配,缺失则新增) + missing_penalty_lines = [] + for rent_info in target_rent_months: + contract = rent_info['contract_id'] + property_rec = rent_info['property_id'] + due_month = rent_info['due_month'] + + # 构建待检查的违约金key(合同+物业+年月) + check_key = ( + contract.id if contract else None, + property_rec.id if property_rec else None, + due_month + ) + + # 若该年月的违约金不存在,则补全 + if check_key not in existing_penalty_months: + # 计算补全违约金的日期范围:当月1号 ~ 当月最后1天 + year, month = map(int, due_month.split('-')) + first_day = datetime(year, month, 1).date() + last_day = (datetime(year, month + 1, 1).date() - relativedelta(days=1)) if month != 12 else datetime( + year, 12, 31).date() + penalty_days = (last_day - first_day).days + 1 # 当月总天数 + penalty_amount = penalty_days * round(contract.rent * float(contract_id.percentage_liquidated_damages.name), 2) if contract else 0.0 + if penalty_amount > 0: + missing_penalty_lines.append((0, 0, { + 'property_id': property_rec.id if property_rec else None, + 'contract_id': contract.id if contract else None, + 'rent_month': 0, + 'due_date': fields.Date.to_string(first_day), + 'rent': penalty_amount, + 'amount': 0, + 'collection_type': 'penalty', + 'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(last_day)},共{penalty_days}天)', + })) + if missing_penalty_lines: + self.write({ 'reconciliation_line_ids': missing_penalty_lines}) + # 检查是否有单行的rent等于实际金额 exact_match_line = next((line for line in self.reconciliation_line_ids if line.rent == remaining_amount), None) if exact_match_line: @@ -230,6 +298,11 @@ class PropertyReconciliationWizard(models.Model): # 检查是否已有相同记录 existing_record = self.env['property.accounts.receivable'].search(check_domain) is_reconciliation = line.amount == line.rent + # 用备注的的开始日期和结束日期 + date_pattern = r'(\d{4}-\d{2}-\d{2})[至-](\d{4}-\d{2}-\d{2})' + date_match = re.search(date_pattern, line.remake or '') + extract_start_date = date_match.group(1) if date_match else line.due_date + extract_end_date = date_match.group(2) if date_match else None if not existing_record: domain2 = [ ('due_date', '=', line.due_date), @@ -240,8 +313,8 @@ class PropertyReconciliationWizard(models.Model): reconciliation_id = self.env['property.reconciliation.line'].search(domain2, limit=1) self.env['property.accounts.receivable'].create({ 'is_reconciliation': is_reconciliation, - 'due_date': line.due_date, - 'due_date_end': rec.actual_date if (line.rent == line.amount and line.rent != 0) else None, + 'due_date': extract_start_date, + 'due_date_end': extract_end_date, 'penalty': reconciliation_id.rent, 'management_type_id': management_type_id.id, 'lessee_id': first_line.tenant_id.id, @@ -257,7 +330,7 @@ class PropertyReconciliationWizard(models.Model): else: existing_record.write({ 'delivered1': line.amount, - 'due_date_end': rec.actual_date if (line.rent == line.amount and line.rent != 0) else None, + 'due_date_end': extract_end_date, 'is_reconciliation': is_reconciliation, 'payout_date': rec.actual_date if (line.rent == line.amount and line.rent != 0) else None, 'remark': line.remake,