diff --git a/app/Http/Controllers/Netbien/SimCardController.php b/app/Http/Controllers/Netbien/SimCardController.php index a99b778..7f0d58f 100644 --- a/app/Http/Controllers/Netbien/SimCardController.php +++ b/app/Http/Controllers/Netbien/SimCardController.php @@ -1,4 +1,7 @@ - 0, + 'assigned' => 0, + 'packages_created' => 0, + 'clients_created' => 0, + 'errors' => [], + ]; + + public function import(Request $request) + { + $request->validate([ + 'file' => 'required|mimes:xlsx,xls,csv|max:10240', + ]); + + try { + $file = $request->file('file'); + + $spreadsheet = IOFactory::load($file->getRealPath()); + $sheet = $spreadsheet->getActiveSheet(); + $rows = $sheet->toArray(); + + foreach ($rows as $index => $row) { + if ($index === 0) continue; + + $this->processRow([ + 'iccid' => $row[4] ?? null, + 'msisdn' => $row[5] ?? null, + 'estado_de_la_sim' => $row[9] ?? null, + 'usuario' => $row[8] ?? null, + ]); + } + + return ApiResponse::OK->response([ + 'success' => true, + 'message' => 'Importación completada', + 'stats' => $this->stats, + 'packages_created' => array_values(array_map(fn($p) => [ + 'name' => $p->name, + 'price' => $p->price + ], $this->packageCache)), + ]); + } catch (\Exception $e) { + return ApiResponse::BAD_REQUEST->response([ + 'success' => false, + 'message' => 'Error en la importación', + 'error' => $e->getMessage(), + 'line' => $e->getLine(), + ], 500); + } + } + + private function processRow(array $row) + { + // Validar campos requeridos + if (empty($row['iccid']) || empty($row['msisdn'])) { + return; + } + + try { + DB::transaction(function () use ($row) { + // Buscar o crear la SIM + $sim = SimCard::where('iccid', $row['iccid'])->first(); + + if (!$sim) { + // No existe, crearla + $sim = SimCard::create([ + 'iccid' => $row['iccid'], + 'msisdn' => $row['msisdn'], + 'status' => SimCardStatus::AVAILABLE, + ]); + + $this->stats['created']++; + } + + $this->processPackageFromText($sim, $row); + // Asignar cliente + $this->assignToClient($sim, $row); + }); + } catch (\Exception $e) { + $this->stats['errors'][] = [ + 'iccid' => $row['iccid'] ?? 'N/A', + 'error' => $e->getMessage(), + ]; + } + } + + private function processPackageFromText(SimCard $sim, array $row) + { + $estadoSim = trim($row['estado_de_la_sim'] ?? ''); + + if (empty($estadoSim)) { + return; + } + + $packageInfo = $this->parsePackageText($estadoSim); + + if (!$packageInfo) { + $this->stats['errors'][] = [ + 'iccid' => $sim->iccid, + 'estado_sim' => $estadoSim, + 'reason' => 'No se pudo parsear el paquete' + ]; + return; + } + + $package = $this->getOrCreatePackage( + $packageInfo['type'], + $packageInfo['price'] + ); + + $sim->packages()->attach($package->id, [ + 'activated_at' => now(), + 'is_active' => true, + ]); + } + + private function parsePackageText(string $text): ?array + { + $text = strtolower($text); + + $type = null; + if (str_contains($text, 'precarga') || str_contains($text, 'pre carga')) { + $type = 'Precarga'; + } elseif (str_contains($text, 'prepago') || str_contains($text, 'pre pago')) { + $type = 'Prepago'; + } + + if (!$type) { + return null; + } + + preg_match('/\$?\s*(\d+)(?:\.\d+)?/', $text, $matches); + $price = isset($matches[1]) ? (float) $matches[1] : 0; + + return ['type' => $type, 'price' => $price]; + } + + private function getOrCreatePackage(string $type, float $price): Packages + { + $cacheKey = "{$type}_{$price}"; + + if (isset($this->packageCache[$cacheKey])) { + return $this->packageCache[$cacheKey]; + } + + $package = Packages::firstOrCreate( + ['name' => $type, 'price' => $price], + ['period' => 0, 'data_limit' => 0] + ); + + if ($package->wasRecentlyCreated) { + $this->stats['packages_created']++; + } + + $this->packageCache[$cacheKey] = $package; + + return $package; + } + + private function assignToClient(SimCard $sim, array $row) + { + $usuario = trim($row['usuario'] ?? ''); + + if (empty($usuario) || strtolower($usuario) === 'si' || strtolower($usuario) === 'no') { + return; + } + + $client = Client::where('full_name', $usuario)->first() + ?? Client::where('full_name', 'LIKE', "%{$usuario}%")->first() + ?? Client::where(function ($query) use ($usuario) { + $query->whereRaw("CONCAT(name, ' ', IFNULL(paternal,''), ' ', IFNULL(maternal,'')) LIKE ?", ["%{$usuario}%"]); + })->first(); + + if (!$client) { + $nameParts = $this->splitFullName($usuario); + + try { + $client = Client::create([ + 'full_name' => $usuario, + 'name' => $nameParts['name'], + 'paternal' => $nameParts['paternal'], + 'maternal' => $nameParts['maternal'], + ]); + + $this->stats['clients_created']++; + } catch (\Exception $e) { + $this->stats['errors'][] = [ + 'usuario' => $usuario, + 'error' => 'Error al crear cliente: ' . $e->getMessage() + ]; + return; + } + } + + $existingRelation = ClientSim::where('client_id', $client->id) + ->where('sim_card_id', $sim->id) + ->where('is_active', true) + ->exists(); + + if ($existingRelation) { + return; + } + + try { + ClientSim::create([ + 'client_id' => $client->id, + 'sim_card_id' => $sim->id, + 'assigned_at' => now(), + 'is_active' => true, + ]); + + $sim->update(['status' => SimCardStatus::ASSIGNED]); + + $this->stats['assigned']++; + } catch (\Exception $e) { + $this->stats['errors'][] = [ + 'iccid' => $sim->iccid, + 'usuario' => $usuario, + 'error' => 'Error al asignar cliente: ' . $e->getMessage() + ]; + } + } + + private function splitFullName(string $fullName): array + { + $parts = array_filter(explode(' ', trim($fullName))); + $parts = array_values($parts); + + return [ + 'name' => $parts[0] ?? '', + 'paternal' => $parts[1] ?? '', + 'maternal' => $parts[2] ?? '', + ]; + } } diff --git a/app/Models/Client.php b/app/Models/Client.php index df3baa0..1373811 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -20,6 +20,7 @@ class Client extends Model 'name', 'paternal', 'maternal', + 'full_name', 'email', 'phone', 'rfc', diff --git a/composer.json b/composer.json index e82bd60..db002a8 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "laravel/pulse": "^1.4", "laravel/reverb": "^1.4", "laravel/tinker": "^2.10", + "maatwebsite/excel": "^3.1", "notsoweb/laravel-core": "dev-main", "spatie/laravel-permission": "^6.16", "tightenco/ziggy": "^2.5" diff --git a/composer.lock b/composer.lock index 850dce6..93abaad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0cdf26aa072a7f833793cfba72e94e81", + "content-hash": "9f4c9c1c470ced74308c732439eb5b3d", "packages": [ { "name": "brick/math", @@ -265,6 +265,162 @@ ], "time": "2025-01-03T16:18:33+00:00" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, { "name": "defuse/php-encryption", "version": "v2.4.0", @@ -809,6 +965,67 @@ }, "time": "2023-08-08T05:53:35+00:00" }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.19.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + }, + "time": "2025-10-17T16:34:55+00:00" + }, { "name": "firebase/php-jwt", "version": "v6.11.1", @@ -2968,6 +3185,272 @@ ], "time": "2025-04-12T22:26:52+00:00" }, + { + "name": "maatwebsite/excel", + "version": "3.1.67", + "source": { + "type": "git", + "url": "https://github.com/SpartnerNL/Laravel-Excel.git", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/e508e34a502a3acc3329b464dad257378a7edb4d", + "reference": "e508e34a502a3acc3329b464dad257378a7edb4d", + "shasum": "" + }, + "require": { + "composer/semver": "^3.3", + "ext-json": "*", + "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", + "php": "^7.0||^8.0", + "phpoffice/phpspreadsheet": "^1.30.0", + "psr/simple-cache": "^1.0||^2.0||^3.0" + }, + "require-dev": { + "laravel/scout": "^7.0||^8.0||^9.0||^10.0", + "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0", + "predis/predis": "^1.1" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Excel": "Maatwebsite\\Excel\\Facades\\Excel" + }, + "providers": [ + "Maatwebsite\\Excel\\ExcelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Maatwebsite\\Excel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Patrick Brouwers", + "email": "patrick@spartner.nl" + } + ], + "description": "Supercharged Excel exports and imports in Laravel", + "keywords": [ + "PHPExcel", + "batch", + "csv", + "excel", + "export", + "import", + "laravel", + "php", + "phpspreadsheet" + ], + "support": { + "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.67" + }, + "funding": [ + { + "url": "https://laravel-excel.com/commercial-support", + "type": "custom" + }, + { + "url": "https://github.com/patrickbrouwers", + "type": "github" + } + ], + "time": "2025-08-26T09:13:16+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/9712d8fa4cdf9240380b01eb4be55ad8dcf71416", + "reference": "9712d8fa4cdf9240380b01eb4be55ad8dcf71416", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.3" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.16", + "guzzlehttp/guzzle": "^7.5", + "mikey179/vfsstream": "^1.6", + "php-coveralls/php-coveralls": "^2.5", + "phpunit/phpunit": "^12.0", + "vimeo/psalm": "^6.0" + }, + "suggest": { + "guzzlehttp/psr7": "^2.4", + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-07-17T11:15:13+00:00" + }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, { "name": "monolog/monolog", "version": "3.9.0", @@ -3841,6 +4324,112 @@ }, "time": "2024-09-04T12:51:01+00:00" }, + { + "name": "phpoffice/phpspreadsheet", + "version": "1.30.1", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "fa8257a579ec623473eabfe49731de5967306c4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fa8257a579ec623473eabfe49731de5967306c4c", + "reference": "fa8257a579ec623473eabfe49731de5967306c4c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "ezyang/htmlpurifier": "^4.15", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": ">=7.4.0 <8.5.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.3", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^8.5 || ^9.0", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.1" + }, + "time": "2025-10-26T16:01:04+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.3", diff --git a/database/migrations/2025_11_12_123948_update_clients_email_nullable.php b/database/migrations/2025_11_12_123948_update_clients_email_nullable.php new file mode 100644 index 0000000..ece7689 --- /dev/null +++ b/database/migrations/2025_11_12_123948_update_clients_email_nullable.php @@ -0,0 +1,28 @@ +string('email')->nullable()->change(); + $table->string('phone')->nullable()->change(); + $table->string('rfc', 13)->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/database/migrations/2025_11_12_130504_add_full_name_to_clients_table.php b/database/migrations/2025_11_12_130504_add_full_name_to_clients_table.php new file mode 100644 index 0000000..a1b852a --- /dev/null +++ b/database/migrations/2025_11_12_130504_add_full_name_to_clients_table.php @@ -0,0 +1,25 @@ +string('full_name')->after('maternal')->nullable(); + }); + } + + public function down(): void + { + Schema::table('clients', function (Blueprint $table) { + $table->dropColumn('full_name'); + }); + } +}; diff --git a/routes/api.php b/routes/api.php index 880187a..e1fe90b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -24,6 +24,7 @@ /** Rutas protegidas (requieren autenticación) */ Route::middleware('auth:api')->group(function() { + Route::post('import', [SimCardController::class, 'import']); Route::resource('sim-cards', SimCardController::class); Route::resource('packages', PackagesController::class);