134 lines
4.9 KiB
PHP
134 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
/**
|
|
* 固定月度顺序:先生成商品月加权平均单价,再同步商品成本表。
|
|
*/
|
|
class AutoMonthlyWeightedAverageAndProductCost extends Command
|
|
{
|
|
protected $signature = 'finance:monthly-weighted-product-cost:auto
|
|
{--month= : 指定月份;默认取最新已成功的进销存月份}
|
|
{--result-json= : 将两阶段结果写入指定 JSON 文件}';
|
|
|
|
protected $description = '先计算商品月加权平均单价,再自动同步商品成本表';
|
|
|
|
public function handle(): int
|
|
{
|
|
$connection = (string) config('upstream_mirrors.connection', 'business_mirror');
|
|
$month = $this->targetMonth($connection);
|
|
if ($month === null) {
|
|
return $this->writeResult([
|
|
'status' => 'blocked',
|
|
'message' => '未找到已成功导入的进销存月份。',
|
|
]);
|
|
}
|
|
|
|
$weightedPath = tempnam(sys_get_temp_dir(), 'weighted-result-') . '.json';
|
|
$costPath = tempnam(sys_get_temp_dir(), 'product-cost-result-') . '.json';
|
|
try {
|
|
$weightedExit = $this->call('product:monthly-weighted-average:auto', [
|
|
'--month' => $month->format('Y-m'),
|
|
'--result-json' => $weightedPath,
|
|
]);
|
|
$weighted = $this->readJson($weightedPath);
|
|
if (
|
|
$weightedExit !== self::SUCCESS
|
|
|| in_array($weighted['status'] ?? 'failed', ['blocked', 'failed'], true)
|
|
) {
|
|
return $this->writeResult([
|
|
'status' => 'failed',
|
|
'report_month' => $month->toDateString(),
|
|
'message' => '商品月加权平均单价未完成,商品成本未执行。',
|
|
'weighted_average' => $weighted,
|
|
'product_cost' => null,
|
|
]);
|
|
}
|
|
|
|
$costExit = $this->call('store-receipt:product-cost:sync', [
|
|
'month' => $month->format('Y-m'),
|
|
'--result-json' => $costPath,
|
|
]);
|
|
$cost = $this->readJson($costPath);
|
|
$status = $costExit !== self::SUCCESS
|
|
? 'failed'
|
|
: ((int) ($cost['pending_cost_rows'] ?? 0) > 0 ? 'review_required' : 'success');
|
|
|
|
return $this->writeResult([
|
|
'status' => $status,
|
|
'report_month' => $month->toDateString(),
|
|
'message' => match ($status) {
|
|
'success' => '月加权平均单价与商品成本已按顺序完成。',
|
|
'review_required' => '月加权平均单价与商品成本已完成,存在待核验成本。',
|
|
default => '商品成本同步失败。',
|
|
},
|
|
'weighted_average' => $weighted,
|
|
'product_cost' => $cost,
|
|
]);
|
|
} finally {
|
|
@unlink($weightedPath);
|
|
@unlink($costPath);
|
|
}
|
|
}
|
|
|
|
private function targetMonth(string $connection): ?CarbonImmutable
|
|
{
|
|
$requested = trim((string) $this->option('month'));
|
|
if ($requested !== '') {
|
|
return CarbonImmutable::parse($requested)->startOfMonth();
|
|
}
|
|
|
|
$latest = DB::connection($connection)->table('inventory_report_authoritative_months')
|
|
->where('dataset_key', 'jiansun.inventory-report.authoritative-month')
|
|
->where('status', 'succeeded')
|
|
->whereNotNull('active_generation')
|
|
->orderByDesc('source_month')
|
|
->value('source_month');
|
|
|
|
return $latest === null ? null : CarbonImmutable::parse((string) $latest)->startOfMonth();
|
|
}
|
|
|
|
/** @return array<string,mixed> */
|
|
private function readJson(string $path): array
|
|
{
|
|
if (! is_file($path)) {
|
|
return ['status' => 'failed', 'message' => '子任务未生成结果文件。'];
|
|
}
|
|
|
|
$decoded = json_decode((string) file_get_contents($path), true);
|
|
|
|
return is_array($decoded)
|
|
? $decoded
|
|
: ['status' => 'failed', 'message' => '子任务结果文件无法解析。'];
|
|
}
|
|
|
|
/** @param array<string,mixed> $result */
|
|
private function writeResult(array $result): int
|
|
{
|
|
$path = trim((string) $this->option('result-json'));
|
|
if ($path !== '') {
|
|
File::ensureDirectoryExists(dirname($path));
|
|
File::put(
|
|
$path,
|
|
json_encode(
|
|
$result,
|
|
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT,
|
|
) . PHP_EOL,
|
|
);
|
|
}
|
|
|
|
if (isset($result['message'])) {
|
|
$this->line((string) $result['message']);
|
|
}
|
|
|
|
return in_array($result['status'] ?? 'failed', ['success', 'review_required'], true)
|
|
? self::SUCCESS
|
|
: self::FAILURE;
|
|
}
|
|
}
|