CaiWuXiTong/app/Business/ThirdPartyWarehouseQuoteRepository.php

153 lines
5.5 KiB
PHP

<?php
namespace App\Business;
use App\Models\Business\ThirdPartyWarehouseQuote;
use Carbon\CarbonInterface;
use Illuminate\Database\QueryException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema;
/**
* 三方仓仓储报价的唯一读写入口。
*
* 结算只需按仓库和结算期查找生效日不晚于该期间的最新启用记录,
* 因此每次价格变化都新增版本,不覆盖旧报价。
*/
class ThirdPartyWarehouseQuoteRepository
{
public function ready(): bool
{
try {
return Schema::hasTable((new ThirdPartyWarehouseQuote())->getTable());
} catch (QueryException) {
return false;
}
}
/** @return array{ready:bool,rows:Collection,summary:array<string,int>} */
public function pageData(): array
{
if (! $this->ready()) {
return $this->emptyPageData();
}
try {
$quotes = ThirdPartyWarehouseQuote::query()
->with('owner')
->orderByDesc('effective_at')
->orderByDesc('id')
->get();
} catch (QueryException) {
return $this->emptyPageData();
}
return [
'ready' => true,
'rows' => $quotes->map(fn (ThirdPartyWarehouseQuote $quote): array => $this->present($quote)),
'summary' => [
'total' => $quotes->count(),
'active' => $quotes->where('status', ThirdPartyWarehouseQuote::STATUS_ACTIVE)->count(),
'warehouses' => $quotes->pluck('warehouse_name')->filter()->unique()->count(),
],
];
}
/**
* Return active quote versions that may apply on or before the settlement end date.
*
* @return Collection<int,array<string,mixed>>
*/
public function settlementQuotes(string $warehouseName, CarbonInterface|string $throughDate): Collection
{
if (! $this->ready()) {
return collect();
}
$normalizedName = preg_replace('/^\s*三方-/u', '', trim($warehouseName)) ?: trim($warehouseName);
$aliases = array_values(array_unique(array_filter([
trim($warehouseName),
$normalizedName,
'三方-' . $normalizedName,
])));
$date = $throughDate instanceof CarbonInterface
? $throughDate->toDateString()
: trim($throughDate);
try {
return ThirdPartyWarehouseQuote::query()
->whereIn('warehouse_name', $aliases)
->where('status', ThirdPartyWarehouseQuote::STATUS_ACTIVE)
->whereDate('effective_at', '<=', $date)
->orderBy('effective_at')
->orderBy('id')
->get()
->map(fn (ThirdPartyWarehouseQuote $quote): array => $this->present($quote))
->values();
} catch (QueryException) {
return collect();
}
}
/** @param array<string,mixed> $data */
public function create(array $data, int $userId): ThirdPartyWarehouseQuote
{
return ThirdPartyWarehouseQuote::query()->create([
'company_id' => company_id(),
'warehouse_name' => trim((string) $data['warehouse_name']),
'inbound_rate_per_ton' => $data['inbound_rate_per_ton'],
'outbound_rate_per_order' => $data['outbound_rate_per_order'],
'pieces_per_pallet' => $data['pieces_per_pallet'],
'pallet_area_sqm' => $data['pallet_area_sqm'],
'storage_rate_per_sqm_month' => $data['storage_rate_per_sqm_month'],
'effective_at' => $data['effective_at'],
'status' => ThirdPartyWarehouseQuote::STATUS_ACTIVE,
'remark' => trim((string) ($data['remark'] ?? '')) ?: null,
'created_by' => $userId,
'updated_by' => $userId,
]);
}
public function setStatus(ThirdPartyWarehouseQuote $quote, string $status, int $userId): ThirdPartyWarehouseQuote
{
abort_unless(in_array($status, [
ThirdPartyWarehouseQuote::STATUS_ACTIVE,
ThirdPartyWarehouseQuote::STATUS_INACTIVE,
], true), 422, '报价状态不合法。');
$quote->forceFill(['status' => $status, 'updated_by' => $userId])->save();
return $quote->fresh();
}
/** @return array<string,mixed> */
private function present(ThirdPartyWarehouseQuote $quote): array
{
return [
'id' => 'WHQ-' . str_pad((string) $quote->id, 4, '0', STR_PAD_LEFT),
'db_id' => $quote->id,
'warehouse_name' => $quote->warehouse_name,
'inbound_rate_per_ton' => (float) $quote->inbound_rate_per_ton,
'outbound_rate_per_order' => (float) $quote->outbound_rate_per_order,
'pieces_per_pallet' => (float) $quote->pieces_per_pallet,
'pallet_area_sqm' => (float) $quote->pallet_area_sqm,
'storage_rate_per_sqm_month' => (float) $quote->storage_rate_per_sqm_month,
'effective_at' => optional($quote->effective_at)->format('Y-m-d'),
'status_code' => $quote->status,
'status' => $quote->statusLabel(),
'remark' => $quote->remark,
'creator' => optional($quote->owner)->name ?: '-',
];
}
/** @return array{ready:false,rows:Collection,summary:array<string,int>} */
private function emptyPageData(): array
{
return [
'ready' => false,
'rows' => collect(),
'summary' => ['total' => 0, 'active' => 0, 'warehouses' => 0],
];
}
}