From 46feba2df7439939e975f61844fc69cd64c6dded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Pocrnji=C4=8D?= Date: Thu, 6 Nov 2025 22:30:17 +0100 Subject: [PATCH] meta data za SMS --- .../Controllers/Admin/PackageController.php | 51 ++++++++++++++++++ app/Http/Controllers/ClientCaseContoller.php | 50 ++++++++++++++++- app/Jobs/PackageItemSmsJob.php | 53 +++++++++++++++++-- resources/js/Components/PersonInfoGrid.vue | 45 ++++++++++++++++ 4 files changed, 195 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Admin/PackageController.php b/app/Http/Controllers/Admin/PackageController.php index 4c5d28b..d2f9eab 100644 --- a/app/Http/Controllers/Admin/PackageController.php +++ b/app/Http/Controllers/Admin/PackageController.php @@ -98,6 +98,10 @@ public function show(Package $package, SmsService $sms): Response 'start_date' => (string) ($c->start_date ?? ''), 'end_date' => (string) ($c->end_date ?? ''), ]; + // Include contract.meta as flattened key-value pairs + if (is_array($c->meta) && ! empty($c->meta)) { + $vars['contract']['meta'] = $this->flattenMeta($c->meta); + } if ($c->account) { $initialRaw = (string) $c->account->initial_amount; $balanceRaw = (string) $c->account->balance_amount; @@ -157,6 +161,10 @@ public function show(Package $package, SmsService $sms): Response 'start_date' => (string) ($c->start_date ?? ''), 'end_date' => (string) ($c->end_date ?? ''), ]; + // Include contract.meta as flattened key-value pairs + if (is_array($c->meta) && ! empty($c->meta)) { + $vars['contract']['meta'] = $this->flattenMeta($c->meta); + } if ($c->account) { $initialRaw = (string) $c->account->initial_amount; $balanceRaw = (string) $c->account->balance_amount; @@ -479,4 +487,47 @@ public function storeFromContracts(StorePackageFromContractsRequest $request, Ph return back()->with('success', 'Package created from contracts'); } + + /** + * Flatten nested meta structure into dot-notation key-value pairs. + * Extracts 'value' from objects with {title, value, type} structure. + * Also creates direct access aliases for nested fields (skipping numeric keys). + */ + private function flattenMeta(array $meta, string $prefix = ''): array + { + $result = []; + foreach ($meta as $key => $value) { + $newKey = $prefix === '' ? $key : "{$prefix}.{$key}"; + + if (is_array($value)) { + // Check if it's a structured meta entry with 'value' field + if (isset($value['value'])) { + $result[$newKey] = $value['value']; + // If parent key is numeric, also create direct alias without the number + if ($prefix !== '' && is_numeric($key)) { + $result[$key] = $value['value']; + } + } else { + // Recursively flatten nested arrays + $nested = $this->flattenMeta($value, $newKey); + $result = array_merge($result, $nested); + + // If current key is numeric, also flatten without it for easier access + if (is_numeric($key)) { + $directNested = $this->flattenMeta($value, $prefix); + foreach ($directNested as $dk => $dv) { + // Only add if not already set (prefer first occurrence) + if (! isset($result[$dk])) { + $result[$dk] = $dv; + } + } + } + } + } else { + $result[$newKey] = $value; + } + } + + return $result; + } } diff --git a/app/Http/Controllers/ClientCaseContoller.php b/app/Http/Controllers/ClientCaseContoller.php index 1476525..fd8f004 100644 --- a/app/Http/Controllers/ClientCaseContoller.php +++ b/app/Http/Controllers/ClientCaseContoller.php @@ -1836,7 +1836,7 @@ public function listContracts(ClientCase $clientCase) { $contracts = $clientCase->contracts() ->with('account.type') - ->select('id', 'uuid', 'reference', 'active', 'start_date', 'end_date') + ->select('id', 'uuid', 'reference', 'active', 'start_date', 'end_date', 'meta') ->latest('id') ->get() ->map(function ($c) { @@ -1852,6 +1852,7 @@ public function listContracts(ClientCase $clientCase) 'active' => (bool) $c->active, 'start_date' => (string) ($c->start_date ?? ''), 'end_date' => (string) ($c->end_date ?? ''), + 'meta' => is_array($c->meta) && ! empty($c->meta) ? $this->flattenMeta($c->meta) : null, 'account' => $acc ? [ 'reference' => $acc->reference, 'type' => $acc->type?->name, @@ -1894,6 +1895,10 @@ public function previewSms(ClientCase $clientCase, Request $request, SmsService 'start_date' => (string) ($contract->start_date ?? ''), 'end_date' => (string) ($contract->end_date ?? ''), ]; + // Include contract.meta as flattened key-value pairs + if (is_array($contract->meta) && ! empty($contract->meta)) { + $vars['contract']['meta'] = $this->flattenMeta($contract->meta); + } if ($contract->account) { $initialRaw = (string) $contract->account->initial_amount; $balanceRaw = (string) $contract->account->balance_amount; @@ -1917,4 +1922,47 @@ public function previewSms(ClientCase $clientCase, Request $request, SmsService 'variables' => $vars, ]); } + + /** + * Flatten nested meta structure into dot-notation key-value pairs. + * Extracts 'value' from objects with {title, value, type} structure. + * Also creates direct access aliases for nested fields (skipping numeric keys). + */ + private function flattenMeta(array $meta, string $prefix = ''): array + { + $result = []; + foreach ($meta as $key => $value) { + $newKey = $prefix === '' ? $key : "{$prefix}.{$key}"; + + if (is_array($value)) { + // Check if it's a structured meta entry with 'value' field + if (isset($value['value'])) { + $result[$newKey] = $value['value']; + // If parent key is numeric, also create direct alias without the number + if ($prefix !== '' && is_numeric($key)) { + $result[$key] = $value['value']; + } + } else { + // Recursively flatten nested arrays + $nested = $this->flattenMeta($value, $newKey); + $result = array_merge($result, $nested); + + // If current key is numeric, also flatten without it for easier access + if (is_numeric($key)) { + $directNested = $this->flattenMeta($value, $prefix); + foreach ($directNested as $dk => $dv) { + // Only add if not already set (prefer first occurrence) + if (! isset($result[$dk])) { + $result[$dk] = $dv; + } + } + } + } + } else { + $result[$newKey] = $value; + } + } + + return $result; + } } diff --git a/app/Jobs/PackageItemSmsJob.php b/app/Jobs/PackageItemSmsJob.php index d1ac66a..b6426ce 100644 --- a/app/Jobs/PackageItemSmsJob.php +++ b/app/Jobs/PackageItemSmsJob.php @@ -9,8 +9,8 @@ use App\Models\SmsSender; use App\Models\SmsTemplate; use App\Services\Sms\SmsService; -use Illuminate\Bus\Queueable; use Illuminate\Bus\Batchable; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; @@ -19,7 +19,7 @@ class PackageItemSmsJob implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable; + use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct(public int $packageItemId) { @@ -70,6 +70,10 @@ public function handle(SmsService $sms): void 'start_date' => (string) ($contract->start_date ?? ''), 'end_date' => (string) ($contract->end_date ?? ''), ]; + // Include contract.meta as flattened key-value pairs for template access + if (is_array($contract->meta) && ! empty($contract->meta)) { + $variables['contract']['meta'] = $this->flattenMeta($contract->meta); + } if ($contract->account) { // Preserve raw values and provide EU-formatted versions for SMS rendering $initialRaw = (string) $contract->account->initial_amount; @@ -193,7 +197,7 @@ public function handle(SmsService $sms): void if ($newStatus === 'sent' && $template && ($template->action_id || $template->decision_id)) { if (! empty($target['contract_id'])) { $contract = Contract::query()->with('clientCase')->find($target['contract_id']); - + if ($contract && $contract->client_case_id) { \App\Models\Activity::create(array_filter([ 'client_case_id' => $contract->client_case_id, @@ -234,4 +238,47 @@ public function handle(SmsService $sms): void $sendClosure(); } } + + /** + * Flatten nested meta structure into dot-notation key-value pairs. + * Extracts 'value' from objects with {title, value, type} structure. + * Also creates direct access aliases for nested fields (skipping numeric keys). + */ + private function flattenMeta(array $meta, string $prefix = ''): array + { + $result = []; + foreach ($meta as $key => $value) { + $newKey = $prefix === '' ? $key : "{$prefix}.{$key}"; + + if (is_array($value)) { + // Check if it's a structured meta entry with 'value' field + if (isset($value['value'])) { + $result[$newKey] = $value['value']; + // If parent key is numeric, also create direct alias without the number + if ($prefix !== '' && is_numeric($key)) { + $result[$key] = $value['value']; + } + } else { + // Recursively flatten nested arrays + $nested = $this->flattenMeta($value, $newKey); + $result = array_merge($result, $nested); + + // If current key is numeric, also flatten without it for easier access + if (is_numeric($key)) { + $directNested = $this->flattenMeta($value, $prefix); + foreach ($directNested as $dk => $dv) { + // Only add if not already set (prefer first occurrence) + if (! isset($result[$dk])) { + $result[$dk] = $dv; + } + } + } + } + } else { + $result[$newKey] = $value; + } + } + + return $result; + } } diff --git a/resources/js/Components/PersonInfoGrid.vue b/resources/js/Components/PersonInfoGrid.vue index 2d52b89..f36e8aa 100644 --- a/resources/js/Components/PersonInfoGrid.vue +++ b/resources/js/Components/PersonInfoGrid.vue @@ -250,6 +250,41 @@ const formatEu = (value, decimals = 2) => { }).format(isNaN(num) ? 0 : num); }; +// Flatten meta structure on the client side (mirrors backend logic) +const flattenMeta = (meta, prefix = "") => { + if (!meta || typeof meta !== "object") return {}; + const result = {}; + for (const [key, value] of Object.entries(meta)) { + const newKey = prefix === "" ? key : `${prefix}.${key}`; + if (value && typeof value === "object") { + // Check if it's a structured meta entry with 'value' field + if ("value" in value) { + result[newKey] = value.value; + // If parent key is numeric, also create direct alias + if (prefix !== "" && /^\d+$/.test(key)) { + result[key] = value.value; + } + } else { + // Recursively flatten nested objects + const nested = flattenMeta(value, newKey); + Object.assign(result, nested); + // If current key is numeric, also flatten without it + if (/^\d+$/.test(key)) { + const directNested = flattenMeta(value, prefix); + for (const [dk, dv] of Object.entries(directNested)) { + if (!(dk in result)) { + result[dk] = dv; + } + } + } + } + } else { + result[newKey] = value; + } + } + return result; +}; + const renderTokens = (text, vars) => { if (!text) return ""; const resolver = (obj, path) => { @@ -355,6 +390,7 @@ const buildVarsFromSelectedContract = () => { if (!uuid) return {}; const c = (contractsForCase.value || []).find((x) => x.uuid === uuid); if (!c) return {}; + const vars = { contract: { uuid: c.uuid, @@ -363,6 +399,15 @@ const buildVarsFromSelectedContract = () => { end_date: c.end_date || "", }, }; + // Include contract.meta - flatten if needed (in case server returns nested structure) + if (c.meta && typeof c.meta === "object") { + // Check if already flattened (no nested objects with 'value' property) + const hasStructuredMeta = Object.values(c.meta).some( + (v) => v && typeof v === "object" && "value" in v + ); + vars.contract.meta = hasStructuredMeta ? flattenMeta(c.meta) : c.meta; + } + if (c.account) { vars.account = { reference: c.account.reference,