118 lines
4.6 KiB
PHP
118 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Events\DocumentGenerated;
|
|
use App\Models\Activity;
|
|
use App\Models\Contract;
|
|
use App\Models\Document;
|
|
use App\Models\DocumentTemplate;
|
|
use App\Services\Documents\TokenValueResolver;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Gate;
|
|
use Illuminate\Support\Str;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class ContractDocumentGenerationController extends Controller
|
|
{
|
|
public function __invoke(Request $request, Contract $contract): Response
|
|
{
|
|
if (Gate::denies('read')) { // baseline read permission required to generate
|
|
abort(403);
|
|
}
|
|
$request->validate([
|
|
'template_slug' => ['required', 'string', 'exists:document_templates,slug'],
|
|
]);
|
|
|
|
$template = DocumentTemplate::where('slug', $request->template_slug)
|
|
->where('core_entity', 'contract')
|
|
->where('active', true)
|
|
->orderByDesc('version')
|
|
->firstOrFail();
|
|
|
|
// Load related data minimally
|
|
$contract->load(['clientCase.client.person', 'clientCase.person', 'clientCase.client']);
|
|
|
|
$renderer = app(\App\Services\Documents\DocxTemplateRenderer::class);
|
|
try {
|
|
$result = $renderer->render($template, $contract, Auth::user());
|
|
} catch (\App\Services\Documents\Exceptions\UnresolvedTokensException $e) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Unresolved tokens detected.',
|
|
'tokens' => $e->unresolved ?? [],
|
|
], 422);
|
|
} catch (\Throwable $e) {
|
|
return response()->json([
|
|
'status' => 'error',
|
|
'message' => 'Generation failed.',
|
|
], 500);
|
|
}
|
|
|
|
$doc = new Document;
|
|
$doc->fill([
|
|
'uuid' => (string) Str::uuid(),
|
|
'name' => $result['fileName'],
|
|
'description' => 'Generated from template '.$template->slug.' v'.$template->version,
|
|
'user_id' => Auth::id(),
|
|
'disk' => 'public',
|
|
'path' => $result['relativePath'],
|
|
'file_name' => $result['fileName'],
|
|
'original_name' => $result['fileName'],
|
|
'extension' => 'docx',
|
|
'mime_type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
'size' => $result['size'],
|
|
'checksum' => $result['checksum'],
|
|
'is_public' => true,
|
|
'template_id' => $template->id,
|
|
'template_version' => $template->version,
|
|
]);
|
|
$contract->documents()->save($doc);
|
|
|
|
// Dispatch domain event
|
|
event(new DocumentGenerated($doc));
|
|
|
|
// Optional: create an activity if template links to action/decision
|
|
if (($template->action_id || $template->decision_id || $template->activity_note_template) && $contract->client_case_id) {
|
|
try {
|
|
$note = null;
|
|
if ($template->activity_note_template) {
|
|
// Interpolate tokens in note using existing resolver logic (non-failing policy: keep)
|
|
/** @var TokenValueResolver $resolver */
|
|
$resolver = app(TokenValueResolver::class);
|
|
$rawNote = $template->activity_note_template;
|
|
$tokens = [];
|
|
if (preg_match_all('/\{([a-zA-Z0-9_\.]+)\}/', $rawNote, $m)) {
|
|
$tokens = array_unique($m[1]);
|
|
}
|
|
$values = [];
|
|
if ($tokens) {
|
|
$resolved = $resolver->resolve($tokens, $template, $contract, Auth::user(), 'keep');
|
|
foreach ($resolved['values'] as $k => $v) {
|
|
$values['{'.$k.'}'] = $v;
|
|
}
|
|
}
|
|
$note = strtr($rawNote, $values);
|
|
}
|
|
|
|
Activity::create(array_filter([
|
|
'note' => $note,
|
|
'action_id' => $template->action_id,
|
|
'decision_id' => $template->decision_id,
|
|
'contract_id' => $contract->id,
|
|
'client_case_id' => $contract->client_case_id,
|
|
], fn ($v) => ! is_null($v) && $v !== ''));
|
|
} catch (\Throwable $e) {
|
|
// swallow activity creation errors to not block document generation
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'ok',
|
|
'document_uuid' => $doc->uuid,
|
|
'path' => $doc->path,
|
|
]);
|
|
}
|
|
}
|