62 lines
4.1 KiB
PHP
62 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use App\Models\Business\CourierQuoteChange;
|
|
use App\Models\Business\CourierQuoteChangeEvent;
|
|
use App\Models\Business\CourierQuoteSignature;
|
|
use App\Models\Business\CourierQuoteVersion;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
final class CourierQuotePeriodCorrection
|
|
{
|
|
public function available(CourierQuoteChange $change): bool
|
|
{
|
|
if ($change->isStockQuote() || $change->status !== CourierQuoteChange::STATUS_APPROVED
|
|
|| !$change->version || $change->version->status !== CourierQuoteVersion::STATUS_APPROVED) return false;
|
|
// Once any attempt has entered the external signing flow, its execution terms are immutable.
|
|
return !CourierQuoteSignature::withTrashed()->disableCache()->where('quote_version_id', $change->version->id)
|
|
->get()->contains(static fn ($signature) => !$signature->canSend()
|
|
|| filled($signature->esign_file_id) || $signature->sent_at !== null || filled($signature->signed_pdf_path));
|
|
}
|
|
|
|
public function correct(CourierQuoteChange $change, array $data, int $userId): CourierQuoteChange
|
|
{
|
|
app(CourierQuoteWorkflowPermissions::class)->assertReviewer($change, $userId);
|
|
$data = CourierQuotePeriods::normalize($data);
|
|
return DB::transaction(function () use ($change, $data, $userId) {
|
|
$change = CourierQuoteChange::query()->disableCache()->lockForUpdate()->findOrFail($change->id);
|
|
$version = CourierQuoteVersion::query()->disableCache()->where('source_change_id', $change->id)->lockForUpdate()->firstOrFail();
|
|
$signatures = CourierQuoteSignature::withTrashed()->disableCache()->where('quote_version_id', $version->id)->lockForUpdate()->get();
|
|
$change->setRelation('version', $version);
|
|
if (!$this->available($change)) {
|
|
throw ValidationException::withMessages(['periods' => '仅已审核且从未推送签署的变价可更正执行区间;已进入签署流程的原文须保留。']);
|
|
}
|
|
$old = ['periods' => $change->executionPeriods(), 'documents' => $signatures->map->only([
|
|
'id', 'source_pdf_path', 'source_pdf_sha256', 'status',
|
|
])->all()];
|
|
$retrospective = $data['effective_date'] < today()->toDateString();
|
|
$change->update([
|
|
'effective_date' => $data['effective_date'], 'end_date' => $data['end_date'],
|
|
'quote_payload' => CourierQuotePeriods::payload($change->quote_payload, $data['execution_periods']),
|
|
'is_retrospective' => $retrospective,
|
|
'recalculation_status' => $retrospective ? CourierQuoteChange::RECALCULATION_BLOCKED : CourierQuoteChange::RECALCULATION_NOT_REQUIRED,
|
|
'recalculation_note' => $retrospective ? '执行区间已更正;尚未执行历史运费补差核算,原运费未改动。' : null,
|
|
]);
|
|
$version->update(['effective_date' => $data['effective_date'], 'end_date' => $data['end_date']]);
|
|
// A new private file is generated; the old document remains stored and is referenced by the audit.
|
|
$signature = app(CourierQuoteSignatureService::class)->prepareVersion($version, $userId, today()->toDateString());
|
|
CourierQuoteChangeEvent::create([
|
|
'company_id' => $change->company_id, 'quote_change_id' => $change->id,
|
|
'event_type' => 'periods_corrected', 'from_status' => $change->status, 'to_status' => $change->status,
|
|
'note' => '审核人更正未推送报价的执行区间:' . $change->periodLabel() . '。区间外沿用原长期报价;价格不变,待签文件已重新生成。',
|
|
'details_json' => json_encode(['before' => $old, 'after' => ['periods' => $change->executionPeriods(),
|
|
'source_pdf_path' => $signature->source_pdf_path, 'source_pdf_sha256' => $signature->source_pdf_sha256]], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
|
|
'created_by' => $userId,
|
|
]);
|
|
return $change->fresh(['version.signature', 'events']);
|
|
});
|
|
}
|
|
}
|