diff --git a/yuthon_property_approval/models/inherit_yuthon_property.py b/yuthon_property_approval/models/inherit_yuthon_property.py index 72b28c265..141dce0ca 100644 --- a/yuthon_property_approval/models/inherit_yuthon_property.py +++ b/yuthon_property_approval/models/inherit_yuthon_property.py @@ -292,11 +292,7 @@ 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() - rent_date = self._get_last_day('租金', record.contract_ids, dt) - deposit_date = self._get_last_day('押金', record.contract_ids, dt) - management_date = self._get_last_day('管理费', record.contract_ids, dt) - water_date = self._get_last_day('水费', record.contract_ids, dt) - electricity_date = self._get_last_day('电费', record.contract_ids, dt) + last_day = dt.replace(day=1) + relativedelta(months=1) - relativedelta(days=1) total_rent_month = 0 # 应收租金 rent_ids = self.env['property.accounts.receivable'].search([ @@ -304,8 +300,7 @@ class PropertyBankStatements(models.Model): ('contract_id.lessor_id.referred', '=', self.your_own_unit), ('management_type_id.name', '=', '租金'), ('is_reconciliation', '=', False), - ('due_date', '>=', fields.Date.to_string(rent_date)), - ('due_date', '<=', fields.Date.to_string(dt)) + ('due_date', '<=', fields.Date.to_string(last_day)), ]) # 应收押金 @@ -314,8 +309,7 @@ class PropertyBankStatements(models.Model): ('contract_id.lessor_id.referred', '=', self.your_own_unit), ('management_type_id.name', '=', '押金'), ('is_reconciliation', '=', False), - ('due_date', '>=', fields.Date.to_string(deposit_date)), - ('due_date', '<=', fields.Date.to_string(dt)) + ('due_date', '<=', fields.Date.to_string(last_day)), ]) # 应收管理费 management_ids = self.env['property.accounts.receivable'].search([ @@ -323,8 +317,7 @@ class PropertyBankStatements(models.Model): ('contract_id.lessor_id.referred', '=', self.your_own_unit), ('management_type_id.name', '=', '管理费'), ('is_reconciliation', '=', False), - ('due_date', '>=', fields.Date.to_string(management_date)), - ('due_date', '<=', fields.Date.to_string(dt)) + ('due_date', '<=', fields.Date.to_string(last_day)), ]) # 应收水费 water_ids = self.env['property.accounts.receivable'].search([ @@ -332,8 +325,7 @@ class PropertyBankStatements(models.Model): ('contract_id.lessor_id.referred', '=', self.your_own_unit), ('management_type_id.name', '=', '水费'), ('is_reconciliation', '=', False), - ('due_date', '>=', fields.Date.to_string(water_date)), - ('due_date', '<=', fields.Date.to_string(dt)) + ('due_date', '<=', fields.Date.to_string(last_day)), ]) # 应收电费 electricity_ids = self.env['property.accounts.receivable'].search([ @@ -341,8 +333,7 @@ class PropertyBankStatements(models.Model): ('contract_id.lessor_id.referred', '=', self.your_own_unit), ('management_type_id.name', '=', '电费'), ('is_reconciliation', '=', False), - ('due_date', '>=', fields.Date.to_string(electricity_date)), - ('due_date', '<=', fields.Date.to_string(dt)) + ('due_date', '<=', fields.Date.to_string(last_day)), ]) def create_reconciliation_vals(ids, collection_type_val, sequence_val): @@ -423,25 +414,6 @@ class PropertyBankStatements(models.Model): return all_vals, total_rent_month return [], 0 - def _get_last_day(self, type, contract_ids, dt): - contract_receivables = self.env['property.accounts.receivable'].search([ - ('contract_id', 'in', contract_ids.ids), - ('contract_id.lessor_id.referred', '=', self.your_own_unit), - ('is_reconciliation', '=', False), - ('payout_date', '=', False), - ('management_type_id.name', '=', f'{type}'), - ], order='due_date asc') - if contract_receivables: - earliest_receivable = contract_receivables[:1] if contract_receivables else self.env['property.accounts.receivable'] - if earliest_receivable: - due_date = earliest_receivable.due_date.replace(day=1) - else: - due_date = dt.replace(day=1) - else: - due_date = dt.replace(day=1) - - return due_date - def _get_context_and_action(self, record, all_vals, total_rent_month): """ :param record: 银行收款记录 diff --git a/yuthon_property_approval/wizard/click_reconciliation_wizard.py b/yuthon_property_approval/wizard/click_reconciliation_wizard.py index 1b0a5f2dc..4df9b4c4e 100644 --- a/yuthon_property_approval/wizard/click_reconciliation_wizard.py +++ b/yuthon_property_approval/wizard/click_reconciliation_wizard.py @@ -9,51 +9,93 @@ class ClickReconciliationWizard(models.Model): message = fields.Text(string='确认信息', readonly=True) def action_confirm(self): - receivable_ids = self.env["property.accounts.receivable"].search([ - ('is_reconciliation', '=', False), - ]) - # 直接查询符合条件的对账单 - for receivable in receivable_ids: - due_date = receivable.due_date - month_start = datetime.combine(due_date.replace(day=1), datetime.min.time()) - if due_date.month == 12: - month_end = datetime.combine( - datetime(due_date.year + 1, 1, 1), - datetime.min.time() - ) - else: - month_end = datetime.combine( - datetime(due_date.year, due_date.month + 1, 1), - datetime.min.time() - ) + target_lessee = '广州志为健康管理咨询有限公司' + unreconciled_states = ['not_reconciled', 'registered'] - statements_ids = self.env["property.bank.statements"].search([ - ('bank_state', 'in', ['not_reconciled', 'registered']), - ('name_account', '=', receivable.lessee_id.name), - ('start_time', '>=', month_start), - ('start_time', '<', month_end), - ('is_company', '=', True), - ('contract_ids', 'in', receivable.contract_id.id), - ('original_amount', '=', receivable.original_amount) - ], order='start_time asc') + bank_statements = self.env["property.bank.statements"].search([ + ('bank_state', 'in', unreconciled_states), + ('is_company', '=', True), + ], order='start_time asc') - if statements_ids: - record = statements_ids[0] - record.yes_amount = receivable.original_amount - record.bank_state = 'reconciled' - record.reconciliation_time = fields.Datetime.now() - timedelta(hours=8) - record.no_amount = 0 - receivable.write({ - 'is_reconciliation': True, - 'delivered1': receivable.original_amount, - 'reconciliation_time': fields.Datetime.now() - timedelta(hours=8), - 'payout_date': record.start_time.date(), - 'receivable_log': f'当前登录人{record.env.user.name}于{fields.Datetime.now()}对户名为{record.name_account}进行对账操作' - }) - self.env['receivable.modify.line'].create({ - 'is_write': True, - 'is_penalty': False, - 'bank_id': record.id, - 'receivable_id': receivable.id, - 'amount': receivable.original_amount, - }) \ No newline at end of file + # 3. 按“月份+合同组”分组汇总流水金额(同一月同一批合同的流水合并) + stmt_groups = {} + for stmt in bank_statements: + stmt_datetime = fields.Datetime.from_string(stmt.start_time) + stmt_date = (stmt_datetime - timedelta(hours=8)).date() + month_key = (stmt_date.year, stmt_date.month) # 月份标识(年,月) + contract_ids = tuple(stmt.contract_ids.ids) + if not contract_ids: + continue + + # 3.3 按“月份+合同组”汇总金额 + group_key = (month_key, contract_ids) + if group_key not in stmt_groups: + stmt_groups[group_key] = { + 'total_stmt_amount': 0.0, + 'statements': self.env["property.bank.statements"], # 存储该组所有流水 + } + # 累加流水金额并收集流水记录 + stmt_groups[group_key]['total_stmt_amount'] += stmt.original_amount + stmt_groups[group_key]['statements'] |= stmt + + # 4. 遍历每个分组,匹配应收单 + for group_key, group_data in stmt_groups.items(): + (month_key, contract_ids) = group_key + total_stmt_amount = group_data['total_stmt_amount'] + statements = group_data['statements'] + year, month = month_key + + # 4.1 计算该月份的时间范围 + month_start = datetime(year, month, 1).date() + month_end = datetime(year + 1, 1, 1).date() if month == 12 else datetime(year, month + 1, 1).date() + + # 4.2 查找该月份、该合同组的未对账应收单 + receivables = self.env["property.accounts.receivable"].search([ + ('is_reconciliation', '=', False), + ('contract_id', 'in', contract_ids), + ('due_date', '>=', month_start), + ('due_date', '<', month_end), + ('lessee_id.name', '=', target_lessee), + ]) + if not receivables: + continue # 无匹配应收单,跳过 + + # 4.3 计算应收总金额 + total_receivable = sum(rec.original_amount for rec in receivables) + + # 4.4 检查该合同组是否有前期未对账记录 + has_prev_unreconciled = self.env["property.accounts.receivable"].search([ + ('contract_id', 'in', contract_ids), + ('is_reconciliation', '=', False), + ('due_date', '<', month_start), + ], limit=1) + + # 4.5 核心条件:多笔流水总和 == 应收总和,且无前期未对账 + if total_stmt_amount == total_receivable and not has_prev_unreconciled: + # 4.6 更新该组所有流水为“已对账” + for stmt in statements: + stmt.write({ + 'bank_state': 'reconciled', + 'reconciliation_time': fields.Datetime.now() - timedelta(hours=8), + 'no_amount': 0, + 'yes_amount': stmt.original_amount + }) + + for rec in receivables: + rec.write({ + 'is_reconciliation': True, + 'delivered1': rec.original_amount, + 'reconciliation_time': fields.Datetime.now() - timedelta(hours=8), + 'payout_date': statements[0].start_time.date() if statements else False, # 取第一笔流水日期 + 'receivable_log': f'当前登录人{self.env.user.name}于{fields.Datetime.now()}对户名为{statements[0].name_account}进行对账操作(多笔流水合并)' + }) + + for statement in statements: + self.env['receivable.modify.line'].create({ + 'is_write': True, + 'is_penalty': False, + 'bank_id': statement.id, + 'reconciliation_date': (fields.Datetime.from_string(statement.start_time) - timedelta(hours=8)).date(), + 'receivable_id': rec.id, + 'amount': rec.original_amount, + }) \ No newline at end of file diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py index 125206d7d..41ea5ce8d 100644 --- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py +++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py @@ -22,13 +22,16 @@ class PropertyReconciliationWizard(models.Model): initial_total = fields.Float(string='初始金额', default=0.0) rent = fields.Float(string='未收总租金', compute="_compute_rent_total") penalty = fields.Float(string='未收总违约金', compute="_compute_penalty_total") - remake = fields.Text(string='备注') + remake = fields.Text(string='摘要') reconciliation_line_ids = fields.One2many('property.reconciliation.line', 'reconciliation_id', string='对账明细') @api.onchange('reconciliation_line_ids', 'actual_total') def _onchange_amount_reconciled(self): for rec in self: rec.amount_reconciled = rec.actual_total - rec.amount_been_reconciled - sum(line.amount for line in rec.reconciliation_line_ids) + for line in rec.reconciliation_line_ids: + if line.rent_month == line.rent: + line.remake = str(rec.actual_date) + '补' + str(line.due_date) @api.depends('reconciliation_line_ids') def _compute_receivable_total(self):