Files
xin-procurement/app/Services/StatementGenerateService.php
T
2026-07-23 20:41:25 +08:00

101 lines
4.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services;
use App\Exceptions\RepositoryException;
use App\Models\StatementItemModel;
use App\Models\StatementModel;
use App\Models\StoreModel;
use App\Models\StoreOrderItemModel;
use App\Models\StoreOrderModel;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
/**
* 门店对账单生成(小程序端自助生成)
*
* 流程(事务内):
* 1. 拉取门店周期内的订单明细(排除已取消订单,按 order_item 去重防止重复入账)
* 2. 快照当前 payment_cycle_dayssettlement_date = period_end + cycle 天
* 3. 明细快照商品名/单价/数量/重量/金额,statement_no = ST…
*/
class StatementGenerateService
{
public function __construct(private readonly BillNumberService $billNumberService)
{
}
/**
* @param StoreModel $store 门店(回款周期从此快照)
* @param string $periodStart 周期开始(Y-m-d
* @param string $periodEnd 周期结束(Y-m-d
*/
public function generate(StoreModel $store, string $periodStart, string $periodEnd): StatementModel
{
return DB::transaction(function () use ($store, $periodStart, $periodEnd) {
$orderItems = StoreOrderItemModel::query()
->join('store_order', 'store_order.id', '=', 'store_order_item.order_id')
->where('store_order_item.store_id', $store->id)
->whereDate('store_order.order_date', '>=', $periodStart)
->whereDate('store_order.order_date', '<=', $periodEnd)
->where('store_order.status', '<>', StoreOrderModel::STATUS_CANCELLED)
->select('store_order_item.*')
->get();
if ($orderItems->isEmpty()) {
throw new RepositoryException('周期内本店无订单数据,无法生成对账单');
}
// 防重复入账:剔除已计入过对账单的订单明细
$usedItemIds = StatementItemModel::query()
->whereIn('statement_id', StatementModel::where('store_id', $store->id)->pluck('id'))
->pluck('order_item_id');
$orderItems = $orderItems->reject(fn ($item) => $usedItemIds->contains($item->id));
if ($orderItems->isEmpty()) {
throw new RepositoryException('周期内的订单明细均已生成过对账单');
}
// 快照回款周期 → 应结算日期
$cycleDays = (int) $store->payment_cycle_days;
$totalAmount = $orderItems->reduce(
static fn (string $carry, $item): string => bcadd($carry, (string) $item->amount, 2),
'0'
);
$statement = StatementModel::create([
'statement_no' => $this->billNumberService->make('ST'),
'store_id' => $store->id,
'period_start' => $periodStart,
'period_end' => $periodEnd,
'total_amount' => $totalAmount,
'payment_cycle_days' => $cycleDays,
'settlement_date' => Carbon::parse($periodEnd)->addDays($cycleDays)->toDateString(),
'status' => StatementModel::STATUS_PENDING,
]);
$rows = [];
$now = now();
foreach ($orderItems as $item) {
$rows[] = [
'statement_id' => $statement->id,
'order_id' => $item->order_id,
'order_item_id' => $item->id,
'product_id' => $item->product_id,
'product_name' => $item->product_name,
'price' => $item->price,
'quantity' => $item->quantity,
'weight' => $item->weight,
'amount' => $item->amount,
'is_reconciled' => StatementItemModel::NOT_RECONCILED,
'store_remark' => '',
'created_at' => $now,
'updated_at' => $now,
];
}
StatementItemModel::insert($rows);
return $statement;
});
}
}