This commit is contained in:
李鹏宇
2026-06-18 19:25:09 +08:00
parent 3864458de3
commit e38108db6b
+27 -52
View File
@@ -13,18 +13,14 @@ class PreArrearsReport(models.Model):
@api.model
def get_pre_arrears_report(self, params):
"""
前期欠收报表(数据源:receivable.modify.line):
- 查询条件的日期对应应收账单的 due_date(应收日期在查询月之前,不包含查询月)
- 欠收条件(二选一):
- 收款日期(payout_date)为空: 从未收款 → 欠收
- 收款日期所在月 >= 查询月 且 收款日期所在月 > 应收日期所在月: 收款跨月且查询月才收到 → 欠收
- 同月收款不算欠收(如1月15日收1月1日应收的款)
- 金额取值:
- 如果 not_collected > 0,取 not_collected (当前还欠的金额)
- 否则取 amount (已对金额)
- 按应收账单聚合,每张账单只出一行(汇总该账单下所有欠收收款金额)
- 补充: 对于真正从未对过账(在 modify.line 表中无任何记录)但 submitted > 0 的应收账单,
直接从 accounts.receivable 取数补齐
前期欠收报表:
- Domain: due_date(应收开始)所在月 < 查询月(不含查询月)
- 数据源分两部分:
1. receivable.modify.line: 有 reconciliation_date 且 reconciliation_date月 > due_date月
且 reconciliation_date月 >= 查询月 → 欠收金额取 amount
2. property.accounts.receivable: is_reconciliation=False 且 due_date月 < 查询月
→ 从未对过账,欠收金额取 submitted
- 按应收账单聚合,每张账单只出一行
"""
today = datetime.date.today()
this_month_first = today.replace(day=1)
@@ -88,19 +84,16 @@ class PreArrearsReport(models.Model):
_logger.info("query_month_start=%s, query_month_end=%s", query_month_start, query_month_end)
_logger.info("Part1 domain: %s", domain)
lines = self.sudo().search(domain, order="receivable_id asc, payout_date asc")
lines = self.sudo().search(domain, order="receivable_id asc, reconciliation_date asc")
_logger.info("Part1 search found %s modify.line records", len(lines))
# 欠收条件(二选一):
# 1. 没有收款日期(从未收款)→ 欠收
# 2. 收款日期所在月 >= 查询月 且 收款日期所在月 > 应收日期所在月 → 欠收
# 即:收款跨月(交晚了)且收款在查询月或之后才到
# 同月收款不算欠收(如1月15日收1月1日应收的款)
# 欠收条件:reconciliation_date 有值 且 所在月 > due_date月 且 >= 查询月
# (交晚了且查询月才收到,取amount)
due_month_start = query_month_start.replace(day=1)
lines = lines.filtered(
lambda l: not l.payout_date or (
l.payout_date.replace(day=1) >= query_month_start and
l.payout_date.replace(day=1) > l.receivable_id.due_date.replace(day=1)
)
lambda l: l.reconciliation_date and
l.reconciliation_date.replace(day=1) > l.receivable_id.due_date.replace(day=1) and
l.reconciliation_date.replace(day=1) >= due_month_start
)
_logger.info("Part1 after欠收filter: %s records remain", len(lines))
@@ -110,44 +103,25 @@ class PreArrearsReport(models.Model):
rid = line.receivable_id.id
if rid not in rec_map:
rec_map[rid] = {'rec': line.receivable_id, 'amount': 0.0, 'payout_dates': [], 'discount_amount': 0.0}
# 金额取值:有未收金额取not_collected,否则取amount
rec_map[rid]['amount'] += (line.not_collected if (line.not_collected and line.not_collected > 0) else line.amount) or 0.0
# 收集收款日期
if line.payout_date:
rec_map[rid]['payout_dates'].append(line.payout_date)
# 有 reconciliation_date(交晚了),欠收金额取 amount
rec_map[rid]['amount'] += line.amount or 0.0
rec_map[rid]['payout_dates'].append(line.reconciliation_date)
# 应收金额 = original_amount - discount_amount(取 receivable_id 的值)
rec_map[rid]['discount_amount'] = line.receivable_id.discount_amount or 0.0
# ============================================================
# 第二部分:补充真正从未对过账 but submitted > 0 的应收账单
# Part 1 只能查到有 modify.line 记录且 payout_date > query_month_end 的数据。
# 对于那些真正从未对过账(在 modify.line 表中无任何记录)的应收账单,
# 直接从 property.accounts.receivable 取数补齐。
#
# 关键:需要排除所有在 modify.line 中出现过的 receivable_id,
# 不仅仅是 Part 1 覆盖的那些。否则,有 modify.line 但 payout_date
# 早于查询月的 receivable 会被错误地纳入欠收。
# 第二部分:从 property.accounts.receivable 取 is_reconciliation=False 的记录
# 即:未对过账的应收账单,due_date月 < 查询月,欠收金额取 submitted
# ============================================================
covered_ids = list(rec_map.keys())
# 找出所有在 modify.line 中有记录的 receivable_id(不管 payout_date 是什么时候)
# 优化:用 read_group 只取 distinct receivable_id,避免全表加载到内存
all_line_receivable_groups = self.sudo().read_group(
[('receivable_id', '!=', False)],
['receivable_id'],
['receivable_id'],
)
all_line_receivable_ids = set(g['receivable_id'][0] for g in all_line_receivable_groups if g['receivable_id'])
# Part 2 只查真正"从未对过账"的 receivable
exclude_ids = all_line_receivable_ids # 包含 covered_ids 和 payout_date 被过滤掉的
receivable_domain = [
('is_reconciliation', '=', False),
('due_date', '<', query_month_start),
('is_invalid', '!=', True),
('submitted', '>', 0),
]
if exclude_ids:
receivable_domain.append(('id', 'not in', list(exclude_ids)))
if covered_ids:
receivable_domain.append(('id', 'not in', list(covered_ids)))
# 同样的筛选条件(去掉 receivable_id. 前缀,直接查 accounts.receivable 自身字段)
if keyword:
@@ -175,12 +149,13 @@ class PreArrearsReport(models.Model):
else:
receivable_domain.append(('management_type_id.name', 'not in', ['押金', '违约金']))
uncovered_receivables = self.env['property.accounts.receivable'].sudo().search(
unreconciled_receivables = self.env['property.accounts.receivable'].sudo().search(
receivable_domain, order="due_date asc")
_logger.info("Part2 从 accounts.receivable(is_reconciliation=False) 补充 %d 条", len(unreconciled_receivables))
for rec in uncovered_receivables:
for rec in unreconciled_receivables:
rid = rec.id
# 从未对过账:欠收金额 = submitted(未收金额),无收款日期
# 未对过账的应收账单,欠收金额 = submitted
rec_map[rid] = {
'rec': rec,
'amount': rec.submitted or 0.0,