Compare commits
3 Commits
6588d9f1a8
...
feature/sh
| Author | SHA1 | Date | |
|---|---|---|---|
| f3e226130f | |||
| 77dd0e2715 | |||
| 917f2dff09 |
@@ -11,6 +11,9 @@ class AdminAppContextService
|
||||
{
|
||||
$tenant = $user->tenant()
|
||||
->with([
|
||||
'socialMedia',
|
||||
'addresses',
|
||||
'phoneNumbers',
|
||||
'menues' => fn ($query) => $query
|
||||
->whereHas(
|
||||
'roles',
|
||||
|
||||
61
app/Domains/Core/PhoneNumber/Models/PhoneNumber.php
Normal file
61
app/Domains/Core/PhoneNumber/Models/PhoneNumber.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\PhoneNumber\Models;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class PhoneNumber extends Model
|
||||
{
|
||||
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
|
||||
{
|
||||
return preg_replace('/[^0-9+]/', '', $number);
|
||||
}
|
||||
|
||||
public function telUrl(): string
|
||||
{
|
||||
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
|
||||
{
|
||||
return $this->belongsToMany(Tenant::class, 'tenant_phone_numbers', 'phone_number_id', 'tenant_codigo', 'id', 'codigo')
|
||||
->using(TenantPhoneNumber::class)
|
||||
->withPivot(['id', 'is_main'])
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
31
app/Domains/Core/PhoneNumber/Models/TenantPhoneNumber.php
Normal file
31
app/Domains/Core/PhoneNumber/Models/TenantPhoneNumber.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\PhoneNumber\Models;
|
||||
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class TenantPhoneNumber extends Pivot
|
||||
{
|
||||
protected $table = 'tenant_phone_numbers';
|
||||
|
||||
public $incrementing = true;
|
||||
|
||||
protected $fillable = ['tenant_codigo', 'phone_number_id', 'is_main'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['is_main' => 'boolean'];
|
||||
}
|
||||
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class, 'tenant_codigo', 'codigo');
|
||||
}
|
||||
|
||||
public function phoneNumber(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PhoneNumber::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Core\PhoneNumber\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class PhoneNumberResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
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),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ use App\Domains\Commerce\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Commerce\Catalog\Models\Category;
|
||||
use App\Domains\Core\Address\Models\Address;
|
||||
use App\Domains\Core\Address\Models\TenantAddress;
|
||||
use App\Domains\Core\PhoneNumber\Models\PhoneNumber;
|
||||
use App\Domains\Core\PhoneNumber\Models\TenantPhoneNumber;
|
||||
use App\Domains\Core\Client\Models\Client;
|
||||
use App\Domains\Core\Menu\Models\Menu;
|
||||
use App\Domains\Core\Menu\Models\TenantMenu;
|
||||
@@ -328,6 +330,22 @@ class Tenant extends Model
|
||||
->where('tenant_addresses.is_main', true);
|
||||
}
|
||||
|
||||
public function phoneNumbers(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(PhoneNumber::class, 'tenant_phone_numbers', 'tenant_codigo', 'phone_number_id', 'codigo', 'id')
|
||||
->using(TenantPhoneNumber::class)
|
||||
->withPivot(['id', 'is_main'])
|
||||
->withTimestamps()
|
||||
->orderByPivot('is_main', 'desc')
|
||||
->orderByPivot('id');
|
||||
}
|
||||
|
||||
public function mainPhoneNumber(): HasOneThrough
|
||||
{
|
||||
return $this->hasOneThrough(PhoneNumber::class, TenantPhoneNumber::class, 'tenant_codigo', 'id', 'codigo', 'phone_number_id')
|
||||
->where('tenant_phone_numbers.is_main', true);
|
||||
}
|
||||
|
||||
public function menues(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Core\Tenant\Resources;
|
||||
use App\Domains\Commerce\Catalog\Models\Category;
|
||||
use App\Domains\Core\Address\Resources\AddressResource;
|
||||
use App\Domains\Core\Menu\Models\Menu;
|
||||
use App\Domains\Core\PhoneNumber\Resources\PhoneNumberResource;
|
||||
use App\Domains\Core\Tenant\Models\StorefrontWebsiteType;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticketing\Event\Models\Event;
|
||||
@@ -45,6 +46,8 @@ class TenantResource extends JsonResource
|
||||
'addresses' => $this->whenLoaded('addresses', fn () => TenantAddressResource::collection($this->addresses)),
|
||||
'main_address' => $this->whenLoaded('mainAddress', fn () => AddressResource::make($this->mainAddress)),
|
||||
'phone' => $this->phone,
|
||||
'phone_numbers' => $this->whenLoaded('phoneNumbers', fn () => PhoneNumberResource::collection($this->phoneNumbers)),
|
||||
'main_phone_number' => $this->whenLoaded('mainPhoneNumber', fn () => PhoneNumberResource::make($this->mainPhoneNumber)),
|
||||
'favicon' => $this->favicon?->getTemporaryUrl(1440),
|
||||
'primary_color' => $this->primary_color,
|
||||
'secondary_color' => $this->secondary_color,
|
||||
@@ -230,7 +233,7 @@ class TenantResource extends JsonResource
|
||||
'route' => $menu->route,
|
||||
];
|
||||
|
||||
if ($menu->pivot?->static_content !== null) {
|
||||
if ($menu->content_type === Menu::CONTENT_TYPE_STATIC && $menu->pivot?->static_content !== null) {
|
||||
$formatted['static_content'] = $menu->pivot->static_content;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ class TenantInformationService
|
||||
'socialMedia',
|
||||
'addresses',
|
||||
'mainAddress',
|
||||
'phoneNumbers',
|
||||
'mainPhoneNumber',
|
||||
'websiteExtras.websiteTypeExtra',
|
||||
'activeEvent.dates',
|
||||
'activeEvent.socialMedia',
|
||||
|
||||
@@ -12,6 +12,7 @@ Es la raíz del modelo multi-tenant. Gestiona organizaciones/sitios, tipos de we
|
||||
- `StorefrontWebsiteTypeExtra`: definición de un extra y su configuración admitida.
|
||||
- `WebsiteExtra`: valor resuelto y estado del extra para un tenant.
|
||||
- `SocialMedia`: catálogo de redes sociales asociables.
|
||||
- `phoneNumbers` y `mainPhoneNumber`: teléfonos públicos del tenant, definidos en el dominio `Core/PhoneNumber` y vinculados por `tenant_phone_numbers`. Solo un vínculo por tenant puede ser principal.
|
||||
|
||||
## Servicios
|
||||
|
||||
@@ -31,3 +32,5 @@ El par `(dominio, base_path)` es único. Un mismo dominio puede alojar el tenant
|
||||
## Dependencias y reglas
|
||||
|
||||
Usa `Attachable` para logos y archivos. Es referenciado por casi todos los dominios para aislamiento. El `codigo` es clave de ruta y clave foránea heredada; no debe sustituirse por `id` sin una migración integral.
|
||||
|
||||
Los resources exponen `phone_numbers` (incluye `is_main`) y `main_phone_number`, con `id`, `label`, `number` y `tel_url`. Contacto muestra la colección y el footer usa el principal. La migración copia primero `tenants.phone` como principal y luego el teléfono de `help.contact`, si es distinto; si no existe teléfono del tenant, el del menú pasa a ser principal. Los duplicados por formato se comparan dentro de cada tenant sin inferir país ni código de área. Los campos anteriores se conservan como datos legacy, pero la tienda consume las relaciones nuevas. No hay endpoints de edición de teléfonos ni cambios del adminapp en esta etapa.
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$driver = DB::connection()->getDriverName();
|
||||
if (! in_array($driver, ['mysql', 'mariadb', 'sqlite', 'pgsql', 'sqlsrv'], true)) {
|
||||
throw new RuntimeException("Unsupported phone number database driver: {$driver}");
|
||||
}
|
||||
|
||||
Schema::create('phone_numbers', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->string('number');
|
||||
$table->timestamps();
|
||||
});
|
||||
Schema::create('tenant_phone_numbers', function (Blueprint $table) use ($driver): void {
|
||||
$table->id();
|
||||
$table->string('tenant_codigo');
|
||||
$table->foreignId('phone_number_id')->constrained('phone_numbers')->cascadeOnDelete();
|
||||
$table->boolean('is_main')->default(false);
|
||||
$table->timestamps();
|
||||
$table->foreign('tenant_codigo')->references('codigo')->on('tenants')->cascadeOnDelete();
|
||||
$table->unique(['tenant_codigo', 'phone_number_id']);
|
||||
if (in_array($driver, ['mysql', 'mariadb'], true)) {
|
||||
$table->string('main_tenant_codigo')->nullable()
|
||||
->virtualAs('CASE WHEN is_main = 1 THEN tenant_codigo ELSE NULL END');
|
||||
$table->unique('main_tenant_codigo', 'tenant_phones_one_main');
|
||||
}
|
||||
});
|
||||
if (! in_array($driver, ['mysql', 'mariadb'], true)) {
|
||||
$condition = $driver === 'pgsql' ? 'is_main = true' : 'is_main = 1';
|
||||
DB::statement("CREATE UNIQUE INDEX tenant_phones_one_main ON tenant_phone_numbers (tenant_codigo) WHERE {$condition}");
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tenant_phone_numbers');
|
||||
Schema::dropIfExists('phone_numbers');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
DB::transaction(function (): void {
|
||||
DB::table('tenants')->orderBy('id')->chunkById(100, function ($tenants): void {
|
||||
foreach ($tenants as $tenant) {
|
||||
$this->copyPhone($tenant->codigo, $tenant->phone, true);
|
||||
}
|
||||
});
|
||||
DB::table('tenants_menues')->where('menu_code', 'help.contact')
|
||||
->whereNotNull('static_content')->orderBy('id')->chunkById(100, function ($menus): void {
|
||||
foreach ($menus as $menu) {
|
||||
$content = json_decode($menu->static_content, true, flags: JSON_THROW_ON_ERROR);
|
||||
if ($content === null) {
|
||||
continue;
|
||||
}
|
||||
if (! is_array($content)) {
|
||||
throw new RuntimeException("Invalid contact content for tenant {$menu->tenant_code}");
|
||||
}
|
||||
$this->copyPhone($menu->tenant_code, $content['phone'] ?? null, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function copyPhone(string $tenantCode, mixed $number, bool $fromTenant): void
|
||||
{
|
||||
if ($number === null || (is_string($number) && trim($number) === '')) {
|
||||
return;
|
||||
}
|
||||
if (! is_string($number) || mb_strlen($number) > 255 || ! preg_match('/[0-9]/', $number)) {
|
||||
throw new RuntimeException("Invalid phone number for tenant {$tenantCode}");
|
||||
}
|
||||
$numbers = DB::table('phone_numbers')
|
||||
->join('tenant_phone_numbers', 'phone_numbers.id', '=', 'tenant_phone_numbers.phone_number_id')
|
||||
->where('tenant_codigo', $tenantCode)->get(['phone_numbers.number']);
|
||||
$dialable = preg_replace('/[^0-9]/', '', $number);
|
||||
foreach ($numbers as $existing) {
|
||||
if (preg_replace('/[^0-9]/', '', $existing->number) === $dialable) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$isMain = ! DB::table('tenant_phone_numbers')->where('tenant_codigo', $tenantCode)->where('is_main', true)->exists();
|
||||
$now = now();
|
||||
$id = DB::table('phone_numbers')->insertGetId([
|
||||
'label' => $fromTenant ? 'Principal' : 'Contacto',
|
||||
'number' => trim($number), 'created_at' => $now, 'updated_at' => $now,
|
||||
]);
|
||||
DB::table('tenant_phone_numbers')->insert([
|
||||
'tenant_codigo' => $tenantCode, 'phone_number_id' => $id, 'is_main' => $isMain,
|
||||
'created_at' => $now, 'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// Preserve copies that may have been edited. The legacy sources were not changed.
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
DB::table('menues')->where('code', 'help.contact')->update([
|
||||
'content_type' => 'dynamic',
|
||||
'static_content_schema' => null,
|
||||
]);
|
||||
// Preserve legacy pivot content for rollback; dynamic menus do not expose it.
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::table('menues')->where('code', 'help.contact')->update([
|
||||
'content_type' => 'static',
|
||||
'static_content_schema' => json_encode([
|
||||
'whatsapp' => 'required|array',
|
||||
'whatsapp.whatsapp_url' => 'required|url',
|
||||
'whatsapp.whatsapp_label' => 'required|string',
|
||||
'phone' => 'required|string',
|
||||
'locations' => 'required|array|min:1',
|
||||
'locations.*.label' => 'required|string',
|
||||
'locations.*.addresses' => 'required|array|min:1',
|
||||
'locations.*.addresses.*' => 'required|array:label,address,coordinates',
|
||||
'locations.*.addresses.*.label' => 'required|string',
|
||||
'locations.*.addresses.*.address' => 'required|string',
|
||||
'locations.*.addresses.*.coordinates' => 'required|array|size:2',
|
||||
'locations.*.addresses.*.coordinates.0' => 'required|numeric|between:-90,90',
|
||||
'locations.*.addresses.*.coordinates.1' => 'required|numeric|between:-180,180',
|
||||
'map_locations' => 'prohibited',
|
||||
], JSON_THROW_ON_ERROR),
|
||||
]);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('phone_numbers', function (Blueprint $table): void {
|
||||
$table->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,
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -224,23 +224,8 @@ class MenuSeeder extends Seeder
|
||||
'code' => 'help.contact',
|
||||
'label' => 'Contacto',
|
||||
'parent_menu_code' => 'help',
|
||||
'content_type' => Menu::CONTENT_TYPE_STATIC,
|
||||
'static_content_schema' => [
|
||||
'whatsapp' => 'required|array',
|
||||
'whatsapp.whatsapp_url' => 'required|url',
|
||||
'whatsapp.whatsapp_label' => 'required|string',
|
||||
'phone' => 'required|string',
|
||||
'locations' => 'required|array|min:1',
|
||||
'locations.*.label' => 'required|string',
|
||||
'locations.*.addresses' => 'required|array|min:1',
|
||||
'locations.*.addresses.*' => 'required|array:label,address,coordinates',
|
||||
'locations.*.addresses.*.label' => 'required|string',
|
||||
'locations.*.addresses.*.address' => 'required|string',
|
||||
'locations.*.addresses.*.coordinates' => 'required|array|size:2',
|
||||
'locations.*.addresses.*.coordinates.0' => 'required|numeric|between:-90,90',
|
||||
'locations.*.addresses.*.coordinates.1' => 'required|numeric|between:-180,180',
|
||||
'map_locations' => 'prohibited',
|
||||
],
|
||||
'content_type' => Menu::CONTENT_TYPE_DYNAMIC,
|
||||
'static_content_schema' => null,
|
||||
'route' => '/ayuda/contacto',
|
||||
],
|
||||
[
|
||||
@@ -399,30 +384,6 @@ class MenuSeeder extends Seeder
|
||||
'is_active' => false,
|
||||
],
|
||||
];
|
||||
$contactContent = [
|
||||
'whatsapp' => [
|
||||
'whatsapp_url' => 'https://wa.me/543412602222',
|
||||
'whatsapp_label' => 'Chatea con nosotros',
|
||||
],
|
||||
'phone' => '+54 9 (0341) 6658247',
|
||||
'locations' => [
|
||||
'rosario' => [
|
||||
'label' => 'Rosario',
|
||||
'addresses' => [
|
||||
[
|
||||
'label' => 'Gigante de Arroyito',
|
||||
'address' => 'Av. Génova 640, Rosario',
|
||||
'coordinates' => [-32.913997, -60.674567],
|
||||
],
|
||||
[
|
||||
'label' => 'Telepagos',
|
||||
'address' => 'Rioja 1150, piso 12, dpto. 3, Rosario',
|
||||
'coordinates' => [-32.946820, -60.639320],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$mutualSmepFrequentlyAskedQuestions = [
|
||||
[
|
||||
'pregunta' => '¿Hay algún límite de compra?',
|
||||
@@ -440,25 +401,6 @@ class MenuSeeder extends Seeder
|
||||
'is_active' => false,
|
||||
],
|
||||
];
|
||||
$mutualSmepContactContent = [
|
||||
'whatsapp' => [
|
||||
'whatsapp_url' => 'https://wa.me/5493412474530',
|
||||
'whatsapp_label' => 'Chateá con Mutual SMEP',
|
||||
],
|
||||
'phone' => '+54 9 341 247-4530',
|
||||
'locations' => [
|
||||
'rosario' => [
|
||||
'label' => 'Rosario',
|
||||
'addresses' => [
|
||||
[
|
||||
'label' => 'Mutual SMEP',
|
||||
'address' => 'San Lorenzo 1543, Rosario, Santa Fe',
|
||||
'coordinates' => [-32.9431184, -60.6437991],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
$menuCodes = $allMenus;
|
||||
@@ -511,11 +453,6 @@ class MenuSeeder extends Seeder
|
||||
? $mutualSmepFrequentlyAskedQuestions
|
||||
: $frequentlyAskedQuestions,
|
||||
]);
|
||||
$tenant->menues()->updateExistingPivot('help.contact', [
|
||||
'static_content' => $tenant->codigo === 'mutual_smep'
|
||||
? $mutualSmepContactContent
|
||||
: $contactContent,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
150
tests/Feature/Migrations/ContactPhoneNumbersTest.php
Normal file
150
tests/Feature/Migrations/ContactPhoneNumbersTest.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ContactPhoneNumbersTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Schema::create('tenants', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('codigo')->unique();
|
||||
$table->string('phone')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
Schema::create('tenants_menues', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('tenant_code');
|
||||
$table->string('menu_code');
|
||||
$table->json('static_content')->nullable();
|
||||
});
|
||||
(require database_path('migrations/2026_09_30_000400_create_phone_numbers_and_tenant_phone_numbers_tables.php'))->up();
|
||||
}
|
||||
|
||||
public function test_tenant_phone_wins_and_copy_is_idempotent_and_scoped(): void
|
||||
{
|
||||
DB::table('tenants')->insert([
|
||||
['codigo' => 'a', 'phone' => '+54 341 111-1111'],
|
||||
['codigo' => 'b', 'phone' => '+54 341 111-1111'],
|
||||
['codigo' => 'c', 'phone' => null],
|
||||
['codigo' => 'empty', 'phone' => ''],
|
||||
]);
|
||||
$this->contact('a', '+54 (341) 222-2222');
|
||||
$this->contact('b', '54 (341) 1111111');
|
||||
$this->contact('c', '+54 341 3333333');
|
||||
$copy = require database_path('migrations/2026_09_30_000500_copy_tenant_and_contact_phone_numbers.php');
|
||||
$copy->up();
|
||||
$copy->up();
|
||||
$this->assertDatabaseCount('phone_numbers', 4);
|
||||
$a = Tenant::where('codigo', 'a')->firstOrFail();
|
||||
$b = Tenant::where('codigo', 'b')->firstOrFail();
|
||||
$this->assertSame('+54 341 111-1111', $a->mainPhoneNumber->number);
|
||||
$this->assertCount(2, $a->phoneNumbers);
|
||||
$this->assertCount(1, $b->phoneNumbers);
|
||||
$this->assertNotSame($a->mainPhoneNumber->id, $b->mainPhoneNumber->id);
|
||||
$this->assertSame('+54 341 3333333', Tenant::where('codigo', 'c')->firstOrFail()->mainPhoneNumber->number);
|
||||
$this->assertNull(Tenant::where('codigo', 'empty')->firstOrFail()->mainPhoneNumber);
|
||||
$this->assertSame('tel:+543411111111', $a->mainPhoneNumber->telUrl());
|
||||
$mainResource = PhoneNumberResource::make($a->mainPhoneNumber)->resolve();
|
||||
$this->assertSame('+54 341 111-1111', $mainResource['number']);
|
||||
$this->assertSame('tel:+543411111111', $mainResource['tel_url']);
|
||||
$resources = PhoneNumberResource::collection($a->phoneNumbers)->resolve();
|
||||
$this->assertTrue($resources[0]['is_main']);
|
||||
$this->assertFalse($resources[1]['is_main']);
|
||||
$copy->down();
|
||||
$this->assertDatabaseCount('phone_numbers', 4);
|
||||
$this->assertDatabaseHas('tenants', ['codigo' => 'a', 'phone' => '+54 341 111-1111']);
|
||||
}
|
||||
|
||||
public function test_database_rejects_two_main_numbers(): void
|
||||
{
|
||||
DB::table('tenants')->insert(['codigo' => 'a', 'phone' => '+54 341 1111111']);
|
||||
$this->contact('a', '+54 341 2222222');
|
||||
(require database_path('migrations/2026_09_30_000500_copy_tenant_and_contact_phone_numbers.php'))->up();
|
||||
$this->expectException(QueryException::class);
|
||||
DB::table('tenant_phone_numbers')->where('tenant_codigo', 'a')->update(['is_main' => true]);
|
||||
}
|
||||
|
||||
public function test_invalid_contact_rolls_back_the_whole_copy(): void
|
||||
{
|
||||
DB::table('tenants')->insert(['codigo' => 'a', 'phone' => '+54 341 1111111']);
|
||||
$this->contact('a', ['invalid']);
|
||||
try {
|
||||
(require database_path('migrations/2026_09_30_000500_copy_tenant_and_contact_phone_numbers.php'))->up();
|
||||
$this->fail('Invalid legacy numbers must not be silently discarded.');
|
||||
} catch (\RuntimeException $exception) {
|
||||
$this->assertStringContainsString('Invalid phone number', $exception->getMessage());
|
||||
$this->assertDatabaseCount('phone_numbers', 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_a_number_cannot_be_linked_to_a_missing_tenant(): void
|
||||
{
|
||||
$id = DB::table('phone_numbers')->insertGetId(['label' => 'Contacto', 'number' => '123456']);
|
||||
$this->expectException(QueryException::class);
|
||||
DB::table('tenant_phone_numbers')->insert([
|
||||
'tenant_codigo' => 'missing', 'phone_number_id' => $id, 'is_main' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
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([
|
||||
'tenant_code' => $tenant, 'menu_code' => 'help.contact',
|
||||
'static_content' => json_encode(['phone' => $phone], JSON_THROW_ON_ERROR),
|
||||
]);
|
||||
}
|
||||
}
|
||||
48
tests/Feature/Migrations/DynamicContactMenuTest.php
Normal file
48
tests/Feature/Migrations/DynamicContactMenuTest.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Migrations;
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
class DynamicContactMenuTest extends TestCase
|
||||
{
|
||||
public function test_it_converts_only_contact_and_preserves_legacy_data_for_rollback(): void
|
||||
{
|
||||
Schema::create('menues', function (Blueprint $table): void {
|
||||
$table->string('code')->primary();
|
||||
$table->string('content_type');
|
||||
$table->json('static_content_schema')->nullable();
|
||||
});
|
||||
Schema::create('tenants_menues', function (Blueprint $table): void {
|
||||
$table->string('tenant_code');
|
||||
$table->string('menu_code');
|
||||
$table->json('static_content')->nullable();
|
||||
});
|
||||
DB::table('menues')->insert([
|
||||
['code' => 'help.contact', 'content_type' => 'static', 'static_content_schema' => '{}'],
|
||||
['code' => 'help.faq', 'content_type' => 'static', 'static_content_schema' => '{}'],
|
||||
]);
|
||||
$legacy = json_encode(['whatsapp' => ['whatsapp_url' => 'https://wa.me/123']], JSON_THROW_ON_ERROR);
|
||||
DB::table('tenants_menues')->insert([
|
||||
'tenant_code' => 'test', 'menu_code' => 'help.contact', 'static_content' => $legacy,
|
||||
]);
|
||||
$migration = require database_path('migrations/2026_09_30_000600_make_contact_menu_dynamic.php');
|
||||
$migration->up();
|
||||
$migration->up();
|
||||
|
||||
$this->assertDatabaseHas('menues', [
|
||||
'code' => 'help.contact', 'content_type' => 'dynamic', 'static_content_schema' => null,
|
||||
]);
|
||||
$this->assertDatabaseHas('menues', ['code' => 'help.faq', 'content_type' => 'static', 'static_content_schema' => '{}']);
|
||||
$this->assertDatabaseHas('tenants_menues', ['menu_code' => 'help.contact', 'static_content' => $legacy]);
|
||||
|
||||
$migration->down();
|
||||
$this->assertDatabaseHas('menues', ['code' => 'help.contact', 'content_type' => 'static']);
|
||||
$schema = json_decode(DB::table('menues')->where('code', 'help.contact')->value('static_content_schema'), true, flags: JSON_THROW_ON_ERROR);
|
||||
$this->assertSame('required|url', $schema['whatsapp.whatsapp_url']);
|
||||
$this->assertDatabaseHas('tenants_menues', ['menu_code' => 'help.contact', 'static_content' => $legacy]);
|
||||
}
|
||||
}
|
||||
@@ -338,26 +338,8 @@ class MenuSeederTest extends TestCase
|
||||
], $faq->static_content_schema);
|
||||
|
||||
$contact = Menu::query()->where('code', 'help.contact')->firstOrFail();
|
||||
$this->assertSame(Menu::CONTENT_TYPE_STATIC, $contact->content_type);
|
||||
$this->assertSame('required|url', $contact->static_content_schema['whatsapp.whatsapp_url']);
|
||||
$this->assertSame('required|string', $contact->static_content_schema['phone']);
|
||||
$this->assertSame(
|
||||
'required|array|min:1',
|
||||
$contact->static_content_schema['locations.*.addresses']
|
||||
);
|
||||
$this->assertSame(
|
||||
'required|array|size:2',
|
||||
$contact->static_content_schema['locations.*.addresses.*.coordinates']
|
||||
);
|
||||
$this->assertSame(
|
||||
'required|string',
|
||||
$contact->static_content_schema['locations.*.addresses.*.address']
|
||||
);
|
||||
$this->assertSame(
|
||||
'required|numeric|between:-90,90',
|
||||
$contact->static_content_schema['locations.*.addresses.*.coordinates.0']
|
||||
);
|
||||
$this->assertSame('prohibited', $contact->static_content_schema['map_locations']);
|
||||
$this->assertSame(Menu::CONTENT_TYPE_DYNAMIC, $contact->content_type);
|
||||
$this->assertNull($contact->static_content_schema);
|
||||
}
|
||||
|
||||
public function test_it_assigns_help_to_the_supported_tenants_with_faq_content(): void
|
||||
@@ -385,30 +367,6 @@ class MenuSeederTest extends TestCase
|
||||
'is_active' => false,
|
||||
],
|
||||
];
|
||||
$expectedContact = [
|
||||
'whatsapp' => [
|
||||
'whatsapp_url' => 'https://wa.me/543412602222',
|
||||
'whatsapp_label' => 'Chatea con nosotros',
|
||||
],
|
||||
'phone' => '+54 9 (0341) 6658247',
|
||||
'locations' => [
|
||||
'rosario' => [
|
||||
'label' => 'Rosario',
|
||||
'addresses' => [
|
||||
[
|
||||
'label' => 'Gigante de Arroyito',
|
||||
'address' => 'Av. Génova 640, Rosario',
|
||||
'coordinates' => [-32.913997, -60.674567],
|
||||
],
|
||||
[
|
||||
'label' => 'Telepagos',
|
||||
'address' => 'Rioja 1150, piso 12, dpto. 3, Rosario',
|
||||
'coordinates' => [-32.946820, -60.639320],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ([$sonder, $fiesta] as $tenant) {
|
||||
$helpMenus = $tenant->menues()
|
||||
@@ -422,10 +380,7 @@ class MenuSeederTest extends TestCase
|
||||
$expectedQuestions,
|
||||
$helpMenus->firstWhere('code', 'help.faq')->pivot->static_content
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expectedContact,
|
||||
$helpMenus->firstWhere('code', 'help.contact')->pivot->static_content
|
||||
);
|
||||
$this->assertNull($helpMenus->firstWhere('code', 'help.contact')->pivot->static_content);
|
||||
}
|
||||
|
||||
$this->assertFalse(
|
||||
|
||||
@@ -510,7 +510,7 @@ class BootstrapTenantControllerTest extends TestCase
|
||||
$tenant->menues()->sync([
|
||||
$parent->code,
|
||||
$staticChild->code => ['static_content' => $staticContent],
|
||||
$dynamicChild->code,
|
||||
$dynamicChild->code => ['static_content' => ['legacy' => 'must not be returned']],
|
||||
]);
|
||||
$userRole->menus()->sync([
|
||||
$parent->code,
|
||||
|
||||
54
tests/Unit/Bootstrap/TenantContactResourceTest.php
Normal file
54
tests/Unit/Bootstrap/TenantContactResourceTest.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Bootstrap;
|
||||
|
||||
use App\Domains\Core\Menu\Models\Menu;
|
||||
use App\Domains\Core\Menu\Models\TenantMenu;
|
||||
use App\Domains\Core\Tenant\Models\SocialMedia;
|
||||
use App\Domains\Core\Tenant\Models\Tenant;
|
||||
use App\Domains\Core\Tenant\Resources\TenantResource;
|
||||
use App\Domains\Ticketing\Event\Models\Event;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TenantContactResourceTest extends TestCase
|
||||
{
|
||||
public function test_dynamic_contact_omits_legacy_content_and_prioritizes_active_event_social_media(): void
|
||||
{
|
||||
$tenant = new Tenant(['codigo' => 'test']);
|
||||
foreach (['favicon', 'headerLogo', 'footerLogo', 'headerBackgroundImage', 'footerBackgroundImage', 'storefrontWebsiteType'] as $relation) {
|
||||
$tenant->setRelation($relation, null);
|
||||
}
|
||||
$contact = new Menu([
|
||||
'code' => 'help.contact', 'label' => 'Contacto', 'content_type' => 'dynamic', 'route' => '/ayuda/contacto',
|
||||
]);
|
||||
$contact->setRelation('pivot', new TenantMenu(['static_content' => ['whatsapp' => 'legacy']]));
|
||||
$faq = new Menu(['code' => 'help.faq', 'label' => 'FAQ', 'content_type' => 'static']);
|
||||
$faq->setRelation('pivot', new TenantMenu(['static_content' => ['question' => 'Example']]));
|
||||
$tenant->setRelation('menues', collect([$contact, $faq]));
|
||||
$tenantSocial = new SocialMedia(['code' => 'whatsapp', 'name' => 'WhatsApp', 'icon' => 'fa-brands fa-whatsapp']);
|
||||
$tenantSocial->setRelation('pivot', new Pivot(['url' => 'https://wa.me/111']));
|
||||
$eventSocial = new SocialMedia(['code' => 'whatsapp', 'name' => 'WhatsApp', 'icon' => 'fa-brands fa-whatsapp']);
|
||||
$eventSocial->setRelation('pivot', new Pivot(['url' => 'https://wa.me/222']));
|
||||
$tenant->setRelation('socialMedia', collect([$tenantSocial]));
|
||||
$event = new Event;
|
||||
$event->setRelation('socialMedia', collect([$eventSocial]));
|
||||
$event->setRelation('address', null);
|
||||
$event->setRelation('dates', collect());
|
||||
$tenant->setRelation('activeEvent', $event);
|
||||
|
||||
$data = TenantResource::make($tenant)->resolve(request());
|
||||
$this->assertSame('dynamic', $data['menues'][0]['content_type']);
|
||||
$this->assertArrayNotHasKey('static_content', $data['menues'][0]);
|
||||
$this->assertSame(['question' => 'Example'], $data['menues'][1]['static_content']);
|
||||
$this->assertSame('https://wa.me/222', $data['social_media'][0]['url']);
|
||||
|
||||
$event->setRelation('socialMedia', collect());
|
||||
$data = TenantResource::make($tenant)->resolve(request());
|
||||
$this->assertCount(0, $data['social_media']);
|
||||
|
||||
$tenant->setRelation('activeEvent', null);
|
||||
$data = TenantResource::make($tenant)->resolve(request());
|
||||
$this->assertSame('https://wa.me/111', $data['social_media'][0]['url']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user