CaiWuXiTong/app/Business/ThirdPartyWarehouseProductWeightRepository.php
limeng 0b00f01566 feat: 三方仓仓储费一键核算+导出核算明细+每期存档
在「第三方仓储报价」页新增仓储费核算:选月份一键按当期生效报价核算
(入库管理费/出库费用/每日仓储费),结果存档并可导出汇总/每日/逐商品三表。

- 新增计算服务 ThirdPartyWarehouseStorageFeeCalculator(口径与报价页公式一致,
  重量取普通商品资料 ops_jst_sku.weight)
- 新增存档表 business_third_party_warehouse_storage_settlements + 模型
- 新增 maatwebsite/excel 三表导出 ThirdPartyWarehouseStorageSettlementExport
- 报价仓库新增 effectiveFor(按仓库+结算月取当期生效报价)
- Livewire 组件与报价页新增核算/导出/历史 UI
- 新增计算服务测试(附 2026-08 真实验收基准 1538.20/6099.60/8376.48/16014.28)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DVQ6NsLsbtxm5Ng7Eew3s9
2026-09-15 09:21:05 +08:00

90 lines
2.8 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
declare(strict_types=1);
namespace App\Business;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Facades\Schema;
use Throwable;
/**
* 三方仓核算的商品重量来源。
*
* 重量取【普通商品资料】主档「重量(kg)」,即业务镜像表 ops_jst_sku 的
* weight 列,按商品编码(sku_id)关联。字段与表定义与
* Modules\EcommerceFinance\Repositories\ProductCatalogMirrorRepository 一致
* (dataset operations_replica.rpt.jst_sku → ops_jst_sku,见 config/upstream_mirrors.php)。
* 同一编码可能有多条历史行,取最新 autoid 的重量。
*/
class ThirdPartyWarehouseProductWeightRepository
{
/** 普通商品资料主档在业务镜像中的落地表。 */
private const PRODUCT_TABLE = 'ops_jst_sku';
private readonly ConnectionInterface $connection;
public function __construct(DatabaseManager $databases)
{
$this->connection = $databases->connection(
(string) config('upstream_mirrors.connection', 'business_mirror'),
);
}
/**
* 按商品编码批量取重量(kg)。
*
* @param list<string> $skuCodes
* @return array<string,float> 商品编码 => 重量(kg)
*/
public function weightsBySkuCodes(array $skuCodes): array
{
$codes = [];
foreach ($skuCodes as $code) {
$code = trim((string) $code);
if ($code !== '') {
$codes[$code] = $code;
}
}
$codes = array_values($codes);
if ($codes === [] || ! $this->available()) {
return [];
}
try {
$latest = $this->connection->table(self::PRODUCT_TABLE)
->where('sku_type', 'normal')
->whereIn('sku_id', $codes)
->groupBy('sku_id')
->select('sku_id')
->selectRaw('MAX(autoid) AS autoid');
return $this->connection->table(self::PRODUCT_TABLE . ' as s')
->joinSub($latest, 'latest', function ($join): void {
$join->on('latest.sku_id', '=', 's.sku_id')
->on('latest.autoid', '=', 's.autoid');
})
->pluck('s.weight', 's.sku_id')
->map(static fn (mixed $weight): float => (float) $weight)
->all();
} catch (Throwable) {
return [];
}
}
private function available(): bool
{
try {
$schema = Schema::connection($this->connection->getName());
return $schema->hasTable(self::PRODUCT_TABLE)
&& $schema->hasColumn(self::PRODUCT_TABLE, 'weight')
&& $schema->hasColumn(self::PRODUCT_TABLE, 'sku_id');
} catch (Throwable) {
return false;
}
}
}