diff --git a/app/Domains/Core/Auth/Services/AdminAppContextService.php b/app/Domains/Core/Auth/Services/AdminAppContextService.php index b62a55c3..56c20ec5 100644 --- a/app/Domains/Core/Auth/Services/AdminAppContextService.php +++ b/app/Domains/Core/Auth/Services/AdminAppContextService.php @@ -11,6 +11,9 @@ class AdminAppContextService { $tenant = $user->tenant() ->with([ + 'socialMedia', + 'addresses', + 'phoneNumbers', 'menues' => fn ($query) => $query ->whereHas( 'roles', diff --git a/app/Domains/Core/PhoneNumber/Models/PhoneNumber.php b/app/Domains/Core/PhoneNumber/Models/PhoneNumber.php index 902dcd9a..9cbb4724 100644 --- a/app/Domains/Core/PhoneNumber/Models/PhoneNumber.php +++ b/app/Domains/Core/PhoneNumber/Models/PhoneNumber.php @@ -8,7 +8,27 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; class PhoneNumber extends Model { - protected $fillable = ['label', 'number']; + protected $attributes = ['country_code' => '+54', 'mobile_prefix' => '9']; + + protected $fillable = ['label', 'country_code', 'mobile_prefix', 'area_code', 'local_number']; + + public function getNumberAttribute(?string $legacyNumber): string + { + if ($this->local_number === null) { + return $legacyNumber ?? ''; + } + + $areaCode = $this->mobile_prefix && $this->area_code + ? '('.$this->area_code.')' + : $this->area_code; + + return implode(' ', array_filter([ + $this->country_code, + $this->mobile_prefix, + $areaCode, + $this->local_number, + ], fn ($part): bool => $part !== null && $part !== '')); + } public static function dialableNumber(string $number): string { @@ -17,7 +37,18 @@ class PhoneNumber extends Model public function telUrl(): string { - return 'tel:'.self::dialableNumber($this->number); + if ($this->country_code === null || $this->local_number === null) { + return 'tel:'.self::dialableNumber($this->number); + } + + $areaCode = $this->area_code ?? ''; + if ($this->country_code === '+54') { + $areaCode = ltrim($areaCode, '0'); + } + + return 'tel:'.self::dialableNumber( + $this->country_code.($this->mobile_prefix ?? '').$areaCode.$this->local_number + ); } public function tenants(): BelongsToMany diff --git a/app/Domains/Core/PhoneNumber/Resources/PhoneNumberResource.php b/app/Domains/Core/PhoneNumber/Resources/PhoneNumberResource.php index d5d3c909..0fc30cd5 100644 --- a/app/Domains/Core/PhoneNumber/Resources/PhoneNumberResource.php +++ b/app/Domains/Core/PhoneNumber/Resources/PhoneNumberResource.php @@ -12,6 +12,10 @@ class PhoneNumberResource extends JsonResource return [ 'id' => $this->id, 'label' => $this->label, + 'country_code' => $this->country_code, + 'mobile_prefix' => $this->mobile_prefix, + 'area_code' => $this->area_code, + 'local_number' => $this->local_number, 'number' => $this->number, 'tel_url' => $this->resource->telUrl(), 'is_main' => $this->whenPivotLoaded('tenant_phone_numbers', fn () => (bool) $this->pivot->is_main), diff --git a/database/migrations/2026_09_30_000700_split_phone_number_components.php b/database/migrations/2026_09_30_000700_split_phone_number_components.php new file mode 100644 index 00000000..25898a3a --- /dev/null +++ b/database/migrations/2026_09_30_000700_split_phone_number_components.php @@ -0,0 +1,94 @@ +string('country_code', 8)->nullable()->default('+54'); + $table->string('mobile_prefix', 4)->nullable()->default('9'); + $table->string('area_code', 8)->nullable(); + $table->string('local_number')->nullable(); + }); + + DB::table('phone_numbers')->orderBy('id')->chunkById(100, function ($numbers): void { + foreach ($numbers as $phone) { + DB::table('phone_numbers')->where('id', $phone->id) + ->update($this->components($phone->number)); + } + }); + + Schema::table('phone_numbers', function (Blueprint $table): void { + $table->dropColumn('number'); + }); + } + + public function down(): void + { + Schema::table('phone_numbers', function (Blueprint $table): void { + $table->string('number')->nullable(); + }); + + DB::table('phone_numbers')->orderBy('id')->chunkById(100, function ($numbers): void { + foreach ($numbers as $phone) { + $parts = array_filter([ + $phone->country_code, + $phone->mobile_prefix, + $phone->area_code, + $phone->local_number, + ], fn ($part): bool => $part !== null && $part !== ''); + DB::table('phone_numbers')->where('id', $phone->id) + ->update(['number' => implode(' ', $parts)]); + } + }); + + Schema::table('phone_numbers', function (Blueprint $table): void { + $table->dropColumn(['country_code', 'mobile_prefix', 'area_code', 'local_number']); + }); + } + + /** @return array{country_code: ?string, mobile_prefix: ?string, area_code: ?string, local_number: string} */ + private function components(string $number): array + { + $number = trim($number); + + if (preg_match('/^(\+?\d{1,3})\s+(?:(9)\s+)?\(?(\d{2,5})\)?[\s-]+([\d\s-]+)$/', $number, $match)) { + return [ + 'country_code' => '+'.ltrim($match[1], '+'), + 'mobile_prefix' => ($match[2] ?? '') ?: null, + 'area_code' => $match[3], + 'local_number' => preg_replace('/\D/', '', $match[4]), + ]; + } + + if (preg_match('/^\(?(0\d{2,5})\)?[\s-]+([\d\s-]+)$/', $number, $match)) { + return [ + 'country_code' => null, + 'mobile_prefix' => null, + 'area_code' => $match[1], + 'local_number' => preg_replace('/\D/', '', $match[2]), + ]; + } + + if (preg_match('/^\+54(9?)(\d+)$/', $number, $match)) { + return [ + 'country_code' => '+54', + 'mobile_prefix' => $match[1] === '9' ? '9' : null, + 'area_code' => null, + 'local_number' => $match[2], + ]; + } + + return [ + 'country_code' => null, + 'mobile_prefix' => null, + 'area_code' => null, + 'local_number' => $number, + ]; + } +}; diff --git a/tests/Feature/Auth/AdminAppMeControllerTest.php b/tests/Feature/Auth/AdminAppMeControllerTest.php index 7bd0282a..54d19a50 100644 --- a/tests/Feature/Auth/AdminAppMeControllerTest.php +++ b/tests/Feature/Auth/AdminAppMeControllerTest.php @@ -2,10 +2,13 @@ namespace Tests\Feature\Auth; +use App\Domains\Core\Address\Models\Address; use App\Domains\Core\Auth\Models\User; use App\Domains\Core\Authorization\Enums\RoleCode; use App\Domains\Core\Authorization\Models\Role; use App\Domains\Core\Menu\Models\Menu; +use App\Domains\Core\PhoneNumber\Models\PhoneNumber; +use App\Domains\Core\Tenant\Models\SocialMedia; use App\Domains\Core\Tenant\Models\Tenant; use Illuminate\Foundation\Testing\RefreshDatabase; use Laravel\Sanctum\Sanctum; @@ -46,6 +49,21 @@ class AdminAppMeControllerTest extends TestCase $tenant->menues()->sync([$catalog->code, $staff->code, $storefront->code]); $otherTenant->menues()->sync([$staff->code]); + $instagram = SocialMedia::query()->create([ + 'code' => 'instagram', 'name' => 'Instagram', 'icon' => 'fa-brands fa-instagram', + ]); + $tenant->socialMedia()->attach($instagram->code, ['url' => 'https://instagram.com/acme', 'orden' => 0]); + $address = Address::query()->create(['label' => 'Principal', 'address_text' => 'San Martín 123']); + $tenant->addresses()->attach($address->id, ['is_main' => true]); + $phone = PhoneNumber::query()->create([ + 'label' => 'Principal', + 'country_code' => '+54', + 'mobile_prefix' => '9', + 'area_code' => '0341', + 'local_number' => '6658247', + ]); + $tenant->phoneNumbers()->attach($phone->id, ['is_main' => true]); + $user = User::factory()->create([ 'nombre_apellido' => 'Admin Acme', 'email' => 'admin@acme.test', @@ -59,6 +77,11 @@ class AdminAppMeControllerTest extends TestCase ->assertJsonPath('data.user.id', $user->id) ->assertJsonPath('data.user.email', 'admin@acme.test') ->assertJsonPath('data.tenant.codigo', 'acme') + ->assertJsonPath('data.tenant.social_media.0.url', 'https://instagram.com/acme') + ->assertJsonPath('data.tenant.addresses.0.address_text', 'San Martín 123') + ->assertJsonPath('data.tenant.phone_numbers.0.number', '+54 9 (0341) 6658247') + ->assertJsonPath('data.tenant.phone_numbers.0.area_code', '0341') + ->assertJsonPath('data.tenant.phone_numbers.0.local_number', '6658247') ->assertJsonCount(1, 'data.tenant.menues') ->assertJsonPath('data.tenant.menues.0.code', 'admin.catalog') ->assertJsonPath('data.tenant.menues.0.label', 'Catálogo') diff --git a/tests/Feature/Migrations/ContactPhoneNumbersTest.php b/tests/Feature/Migrations/ContactPhoneNumbersTest.php index 4068bf48..c3d2d92f 100644 --- a/tests/Feature/Migrations/ContactPhoneNumbersTest.php +++ b/tests/Feature/Migrations/ContactPhoneNumbersTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\Migrations; +use App\Domains\Core\PhoneNumber\Models\PhoneNumber; use App\Domains\Core\PhoneNumber\Resources\PhoneNumberResource; use App\Domains\Core\Tenant\Models\Tenant; use Illuminate\Database\QueryException; @@ -96,6 +97,49 @@ class ContactPhoneNumbersTest extends TestCase ]); } + public function test_it_splits_mobile_and_landline_numbers_and_builds_dialable_urls(): void + { + DB::table('phone_numbers')->insert([ + ['label' => 'Móvil', 'number' => '+54 9 (0341) 6658247'], + ['label' => 'Fijo', 'number' => '+54 341 111-1111'], + ['label' => 'Local', 'number' => '0341 1234567'], + ['label' => 'Móvil sin signo', 'number' => '54 9 (0341) 6658247'], + ]); + + $migration = require database_path('migrations/2026_09_30_000700_split_phone_number_components.php'); + $migration->up(); + + [$mobile, $landline, $local, $mobileWithoutPlus] = PhoneNumber::query()->orderBy('id')->get()->all(); + $this->assertSame('+54', $mobile->country_code); + $this->assertSame('9', $mobile->mobile_prefix); + $this->assertSame('0341', $mobile->area_code); + $this->assertSame('6658247', $mobile->local_number); + $this->assertSame('+54 9 (0341) 6658247', $mobile->number); + $this->assertSame('tel:+5493416658247', $mobile->telUrl()); + $this->assertSame('+54 341 1111111', $landline->number); + $this->assertSame('tel:+543411111111', $landline->telUrl()); + $this->assertSame('0341 1234567', $local->number); + $this->assertSame('tel:03411234567', $local->telUrl()); + $this->assertSame('+54 9 (0341) 6658247', $mobileWithoutPlus->number); + $resource = PhoneNumberResource::make($mobile)->resolve(); + $this->assertSame('9', $resource['mobile_prefix']); + $this->assertSame('0341', $resource['area_code']); + $this->assertSame('tel:+5493416658247', $resource['tel_url']); + + $created = PhoneNumber::query()->create([ + 'label' => 'Nuevo', + 'area_code' => '0341', + 'local_number' => '7777777', + ]); + $this->assertSame('+54', $created->fresh()->country_code); + $this->assertSame('9', $created->fresh()->mobile_prefix); + $this->assertSame('+54 9 (0341) 7777777', $created->number); + $this->assertSame('tel:+5493417777777', $created->telUrl()); + + $migration->down(); + $this->assertDatabaseHas('phone_numbers', ['id' => $mobile->id, 'number' => '+54 9 0341 6658247']); + } + private function contact(string $tenant, mixed $phone): void { DB::table('tenants_menues')->insert([