Package system sms

This commit is contained in:
Simon Pocrnjič
2025-10-26 12:57:09 +01:00
parent 266af6595e
commit 369af34ad4
29 changed files with 2639 additions and 330 deletions
+55
View File
@@ -0,0 +1,55 @@
<?php
use App\Jobs\PackageItemSmsJob;
use App\Models\Package;
use App\Models\SmsLog;
use App\Models\SmsProfile;
use App\Services\Sms\SmsService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
uses(RefreshDatabase::class);
it('processes a queued package item and updates counters', function () {
$package = Package::create([
'uuid' => (string) Str::uuid(),
'type' => Package::TYPE_SMS,
'status' => Package::STATUS_DRAFT,
'name' => 'Test SMS Package',
'total_items' => 1,
]);
$profile = SmsProfile::factory()->create();
$item = $package->items()->create([
'status' => 'queued',
'target_json' => ['number' => '+38640123456'],
'payload_json' => ['profile_id' => $profile->id, 'body' => 'Hello world'],
]);
// Mock SmsService to return a successful log
$log = new SmsLog([
'status' => 'sent',
'provider_message_id' => 'abc123',
'cost' => 0.0100,
'currency' => 'EUR',
'meta' => ['parts' => 1],
]);
$this->mock(SmsService::class)
->shouldReceive('sendFromTemplate')->zeroOrMoreTimes()
->andReturn($log);
$this->mock(SmsService::class)
->shouldReceive('sendRaw')->zeroOrMoreTimes()
->andReturn($log);
$job = new PackageItemSmsJob($item->id);
$job->handle(app(SmsService::class));
$item->refresh();
$package->refresh();
expect($item->status)->toBe('sent');
expect($package->sent_count)->toBe(1);
expect($package->failed_count)->toBe(0);
});
+53
View File
@@ -0,0 +1,53 @@
<?php
use App\Models\Person\Person;
use App\Models\Person\PersonPhone;
use App\Services\Contact\PhoneSelector;
it('selects validated mobile first', function () {
$person = Person::factory()->create();
// Non-mobile validated
PersonPhone::factory()->for($person, 'person')->landline()->validated()->create();
// Mobile not validated
PersonPhone::factory()->for($person, 'person')->mobile()->notValidated()->create();
// Mobile validated (should win)
$best = PersonPhone::factory()->for($person, 'person')->mobile()->validated()->create();
$selector = new PhoneSelector;
$result = $selector->selectForPerson($person->fresh('phones'));
expect($result['phone'])->not->toBeNull();
expect($result['phone']->id)->toBe($best->id);
expect($result['reason'])->toBeNull();
});
it('falls back to validated any type then mobile', function () {
$person = Person::factory()->create();
$validatedAny = PersonPhone::factory()->for($person, 'person')->landline()->validated()->create();
$mobile = PersonPhone::factory()->for($person, 'person')->mobile()->notValidated()->create();
$selector = new PhoneSelector;
$result = $selector->selectForPerson($person->fresh('phones'));
expect($result['phone']->id)->toBe($validatedAny->id);
});
it('returns first active when no better option', function () {
$person = Person::factory()->create();
$first = PersonPhone::factory()->for($person, 'person')->landline()->notValidated()->create();
PersonPhone::factory()->for($person, 'person')->landline()->notValidated()->create();
$selector = new PhoneSelector;
$result = $selector->selectForPerson($person->fresh('phones'));
expect($result['phone']->id)->toBe($first->id);
});
it('returns reason when no phones', function () {
$person = Person::factory()->create();
$selector = new PhoneSelector;
$result = $selector->selectForPerson($person->fresh('phones'));
expect($result['phone'])->toBeNull();
expect($result['reason'])->toBe('no_active_phones');
});
+38
View File
@@ -0,0 +1,38 @@
<?php
use App\Models\SmsProfile;
use App\Services\Sms\SmsClient;
use App\Services\Sms\SmsMessage;
use App\Services\Sms\SmsResult;
use App\Services\Sms\SmsService;
it('formats amounts to EU style', function () {
$client = new class implements SmsClient
{
public function send(SmsProfile $profile, SmsMessage $message): SmsResult
{
throw new RuntimeException('not used');
}
public function getCreditBalance(SmsProfile $profile): int
{
return 0;
}
public function getPriceQuotes(SmsProfile $profile): array
{
return [];
}
};
$sms = new SmsService($client);
expect($sms->formatAmountEu(0))->toBe('0,00');
expect($sms->formatAmountEu('1'))->toBe('1,00');
expect($sms->formatAmountEu('12.3'))->toBe('12,30');
expect($sms->formatAmountEu('1234.56'))->toBe('1.234,56');
expect($sms->formatAmountEu('9876543.21'))->toBe('9.876.543,21');
expect($sms->formatAmountEu('-42.5'))->toBe('-42,50');
// Accept EU input too
expect($sms->formatAmountEu('1.234,56'))->toBe('1.234,56');
});