You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Laravel多关联查询:如何计算发票状态(未结/已付/逾期)

解决发票状态(未结/已付/逾期)的判断问题

嘿,我来帮你搞定这个发票状态计算的事儿!你之前的查询思路方向对,但whereHas的用法不对——它是用来判断关联记录是否存在的,没法直接帮你计算收款总额并和发票总金额对比。下面我给你几种实用的解决方案:

先确认模型关联

首先得保证你的Eloquent模型关联是正确的,在Invoice模型里定义和InvoicePayment的一对多关系:

// app/Models/Invoice.php
public function invoicePayments()
{
    return $this->hasMany(InvoicePayment::class);
}

方案1:批量获取发票并计算状态(推荐)

用Laravel的withSum预加载收款总额(Laravel 8+支持),避免N+1查询,然后在集合中判断状态:

use Carbon\Carbon;

$invoices = Invoice::query()
    // 预加载每个发票的收款总额,结果会存在invoice_payments_sum_amount_received字段里
    ->withSum('invoicePayments', 'amount_received')
    ->get()
    ->map(function ($invoice) {
        // 处理收款总额为0的情况(没有任何付款记录)
        $totalReceived = $invoice->invoice_payments_sum_amount_received ?? 0;

        // 判断状态逻辑
        if ($invoice->total_amount > $totalReceived) {
            // 先检查是否逾期(假设invoices表有due_date字段,没有的话可以去掉这部分)
            if (Carbon::now()->gt($invoice->due_date)) {
                $invoice->status = '逾期';
            } else {
                $invoice->status = '未结';
            }
        } elseif ($invoice->total_amount == $totalReceived) {
            $invoice->status = '已付';
        } else {
            // 收款超过发票总额的情况,可根据业务需求自定义状态
            $invoice->status = '超额收款';
        }

        return $invoice;
    });

方案2:数据库层面直接筛选特定状态的发票

如果不需要所有发票,只想筛选某一类状态(比如逾期发票),可以用join+group by的方式在数据库层面完成计算:

// 筛选逾期发票
$overdueInvoices = Invoice::query()
    ->leftJoin('invoice_payments', 'invoices.id', '=', 'invoice_payments.invoice_id')
    ->selectRaw('invoices.*, COALESCE(SUM(invoice_payments.amount_received), 0) as total_received')
    ->groupBy('invoices.id')
    // 总额大于收款总额,且已过付款期限
    ->havingRaw('invoices.total_amount > total_received')
    ->where('due_date', '<', Carbon::now())
    ->get();

// 筛选已付发票
$paidInvoices = Invoice::query()
    ->leftJoin('invoice_payments', 'invoices.id', '=', 'invoice_payments.invoice_id')
    ->selectRaw('invoices.*, COALESCE(SUM(invoice_payments.amount_received), 0) as total_received')
    ->groupBy('invoices.id')
    ->havingRaw('invoices.total_amount = total_received')
    ->get();

方案3:给Invoice模型添加状态访问器

如果想在任何地方直接通过$invoice->status获取状态,可以给模型加一个访问器:

// app/Models/Invoice.php
use Carbon\Carbon;

public function getStatusAttribute()
{
    // 优先用预加载的总额(如果用了withSum),否则实时计算
    $totalReceived = $this->invoice_payments_sum_amount_received ?? $this->invoicePayments()->sum('amount_received');

    if ($this->total_amount > $totalReceived) {
        return Carbon::now()->gt($this->due_date) ? '逾期' : '未结';
    } elseif ($this->total_amount == $totalReceived) {
        return '已付';
    }

    return '超额收款';
}

使用的时候直接调用:

$invoice = Invoice::find(1);
echo $invoice->status; // 直接输出状态

为什么你原来的查询不行?

你之前的whereHas写法只是检查发票是否有对应的付款记录,但没有计算收款总额,也没有和发票总金额做对比。whereHas的闭包是用来添加查询条件的,不是用来做汇总计算的,所以没法实现你的需求~

内容的提问来源于stack exchange,提问作者Xyrin Technologies

火山引擎 最新活动