SMS service
This commit is contained in:
@@ -1343,6 +1343,22 @@ function ($p) {
|
||||
'segments' => $case->segments()->wherePivot('active', true)->get(['segments.id', 'segments.name']),
|
||||
'all_segments' => \App\Models\Segment::query()->where('active', true)->get(['id', 'name']),
|
||||
'current_segment' => $currentSegment,
|
||||
// SMS helpers for per-case sending UI
|
||||
'sms_profiles' => \App\Models\SmsProfile::query()
|
||||
->select(['id', 'name', 'default_sender_id'])
|
||||
->where('active', true)
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
'sms_senders' => \App\Models\SmsSender::query()
|
||||
->select(['id', 'profile_id'])
|
||||
->addSelect(\DB::raw('sname as name'))
|
||||
->addSelect(\DB::raw('phone_number as phone'))
|
||||
->orderBy('sname')
|
||||
->get(),
|
||||
'sms_templates' => \App\Models\SmsTemplate::query()
|
||||
->select(['id', 'name', 'content', 'allow_custom_body'])
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1665,4 +1681,124 @@ public function emergencyCreatePerson(ClientCase $clientCase, Request $request)
|
||||
'person_uuid' => $newPerson?->uuid,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMS to a specific phone that belongs to the client case person.
|
||||
*/
|
||||
public function sendSmsToPhone(ClientCase $clientCase, Request $request, int $phone_id)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'message' => ['required', 'string', 'max:1000'],
|
||||
'delivery_report' => ['sometimes', 'boolean'],
|
||||
'template_id' => ['sometimes', 'nullable', 'integer', 'exists:sms_templates,id'],
|
||||
'profile_id' => ['sometimes', 'nullable', 'integer', 'exists:sms_profiles,id'],
|
||||
'sender_id' => ['sometimes', 'nullable', 'integer', 'exists:sms_senders,id'],
|
||||
]);
|
||||
|
||||
// Ensure the phone belongs to the person of this case
|
||||
/** @var \App\Models\Person\PersonPhone|null $phone */
|
||||
$phone = \App\Models\Person\PersonPhone::query()
|
||||
->where('id', $phone_id)
|
||||
->where('person_id', $clientCase->person_id)
|
||||
->first();
|
||||
|
||||
if (! $phone) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
// Resolve explicit profile/sender if provided; otherwise fallback to first active profile and its default sender
|
||||
/** @var \App\Models\SmsProfile|null $profile */
|
||||
$profile = null;
|
||||
/** @var \App\Models\SmsSender|null $sender */
|
||||
$sender = null;
|
||||
|
||||
if (! empty($validated['sender_id']) && empty($validated['profile_id'])) {
|
||||
// Infer profile from sender if not explicitly provided
|
||||
$sender = \App\Models\SmsSender::query()->find($validated['sender_id']);
|
||||
if ($sender) {
|
||||
$profile = \App\Models\SmsProfile::query()->find($sender->profile_id);
|
||||
}
|
||||
}
|
||||
if (! empty($validated['profile_id'])) {
|
||||
$profile = \App\Models\SmsProfile::query()->where('id', $validated['profile_id'])->first();
|
||||
if (! $profile) {
|
||||
return back()->with('error', 'Izbran SMS profil ne obstaja.');
|
||||
}
|
||||
if (property_exists($profile, 'active') && ! $profile->active) {
|
||||
return back()->with('error', 'Izbran SMS profil ni aktiven.');
|
||||
}
|
||||
}
|
||||
if (! empty($validated['sender_id'])) {
|
||||
$sender = \App\Models\SmsSender::query()->find($validated['sender_id']);
|
||||
if (! $sender) {
|
||||
return back()->with('error', 'Izbran pošiljatelj ne obstaja.');
|
||||
}
|
||||
if ($profile && (int) $sender->profile_id !== (int) $profile->id) {
|
||||
return back()->with('error', 'Izbran pošiljatelj ne pripada izbranemu profilu.');
|
||||
}
|
||||
}
|
||||
if (! $profile) {
|
||||
$profile = \App\Models\SmsProfile::query()
|
||||
->where('active', true)
|
||||
->orderByRaw('CASE WHEN default_sender_id IS NULL THEN 1 ELSE 0 END')
|
||||
->orderBy('id')
|
||||
->first();
|
||||
}
|
||||
if (! $profile) {
|
||||
return back()->with('warning', 'Ni aktivnega SMS profila.');
|
||||
}
|
||||
if (! $sender && ! empty($profile->default_sender_id)) {
|
||||
$sender = \App\Models\SmsSender::query()->find($profile->default_sender_id);
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var \App\Services\Sms\SmsService $sms */
|
||||
$sms = app(\App\Services\Sms\SmsService::class);
|
||||
// Check available credits before enqueueing (fail-closed)
|
||||
try {
|
||||
$raw = (string) $sms->getCreditBalance($profile);
|
||||
$num = null;
|
||||
if ($raw !== '') {
|
||||
$normalized = str_replace(',', '.', trim($raw));
|
||||
if (preg_match('/-?\d+(?:\.\d+)?/', $normalized, $m)) {
|
||||
$num = (float) ($m[0] ?? null);
|
||||
}
|
||||
}
|
||||
if (! is_null($num) && $num <= 0.0) {
|
||||
return back()->with('error', 'No credits left.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
\Log::warning('SMS credit balance check failed', [
|
||||
'error' => $e->getMessage(),
|
||||
'profile_id' => $profile->id,
|
||||
]);
|
||||
|
||||
return back()->with('error', 'Unable to verify SMS credits.');
|
||||
}
|
||||
|
||||
// Queue the SMS send; activity will be created in the job on success if a template is provided
|
||||
\App\Jobs\SendSmsJob::dispatch(
|
||||
profileId: $profile->id,
|
||||
to: (string) $phone->nu,
|
||||
content: (string) $validated['message'],
|
||||
senderId: $sender?->id,
|
||||
countryCode: $phone->country_code ?: null,
|
||||
deliveryReport: (bool) ($validated['delivery_report'] ?? false),
|
||||
clientReference: null,
|
||||
templateId: $validated['template_id'] ?? null,
|
||||
clientCaseId: $clientCase->id,
|
||||
userId: optional($request->user())->id,
|
||||
);
|
||||
|
||||
return back()->with('success', 'SMS je bil dodan v čakalno vrsto.');
|
||||
} catch (\Throwable $e) {
|
||||
\Log::warning('SMS enqueue failed', [
|
||||
'error' => $e->getMessage(),
|
||||
'case_id' => $clientCase->id,
|
||||
'phone_id' => $phone_id,
|
||||
]);
|
||||
|
||||
return back()->with('error', 'SMS ni bil dodan v čakalno vrsto.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user