diff --git a/app/Http/Controllers/App/InventoryController.php b/app/Http/Controllers/App/InventoryController.php index 872694b..d517be1 100644 --- a/app/Http/Controllers/App/InventoryController.php +++ b/app/Http/Controllers/App/InventoryController.php @@ -4,9 +4,16 @@ use App\Http\Controllers\Controller; use App\Http\Requests\App\InventoryStoreRequest; use App\Http\Requests\App\InventoryUpdateRequest; +use App\Http\Requests\App\InventoryImportRequest; use App\Services\ProductService; +use App\Imports\ProductsImport; +use Illuminate\Http\Request; use Notsoweb\ApiResponse\Enums\ApiResponse; - +use Maatwebsite\Excel\Facades\Excel; +use Maatwebsite\Excel\Validators\ValidationException; +use Maatwebsite\Excel\Concerns\FromArray; +use Maatwebsite\Excel\Concerns\WithHeadings; +use Maatwebsite\Excel\Concerns\Exportable; class InventoryController extends Controller { @@ -14,11 +21,20 @@ public function __construct( protected ProductService $productService ) {} - public function index() + public function index(Request $request) { $products = Inventory::with(['category', 'price']) - ->where('is_active', true) - ->orderBy('name') + ->where('is_active', true); + + + if ($request->has('q') && $request->q) { + $products->where(function($query) use ($request) { + $query->where('name', 'like', "%{$request->q}%") + ->orWhere('sku', 'like', "%{$request->q}%"); + }); + } + + $products = $products->orderBy('name') ->paginate(config('app.pagination')); return ApiResponse::OK->response([ @@ -58,4 +74,102 @@ public function destroy(Inventory $inventario) return ApiResponse::OK->response(); } + /** + * Importar productos desde Excel + */ + public function import(InventoryImportRequest $request) + { + try { + $import = new ProductsImport(); + + Excel::import($import, $request->file('file')); + + $stats = $import->getStats(); + + return ApiResponse::OK->response([ + 'message' => 'Importación completada exitosamente.', + 'imported' => $stats['imported'], + 'skipped' => $stats['skipped'], + 'errors' => $stats['errors'], + ]); + } catch (ValidationException $e) { + $failures = $e->failures(); + $errors = []; + + foreach ($failures as $failure) { + $errors[] = [ + 'row' => $failure->row(), + 'attribute' => $failure->attribute(), + 'errors' => $failure->errors(), + 'values' => $failure->values(), + ]; + } + + return ApiResponse::BAD_REQUEST->response([ + 'message' => 'Error de validación en el archivo.', + 'errors' => $errors, + ]); + } catch (\Exception $e) { + return ApiResponse::INTERNAL_ERROR->response([ + 'message' => 'Error al importar productos: ' . $e->getMessage(), + ]); + } + } + + /** + * Descargar plantilla de Excel para importación + */ + public function downloadTemplate() + { + $headers = [ + 'nombre', + 'sku', + 'categoria', + 'stock', + 'costo', + 'precio_venta', + 'impuesto' + ]; + + $exampleData = [ + [ + 'nombre' => 'Producto Ejemplo 1', + 'sku' => 'PROD-001', + 'categoria' => 'Electrónica', + 'stock' => 10, + 'costo' => 100.00, + 'precio_venta' => 150.00, + 'impuesto' => 16 + ], + [ + 'nombre' => 'Producto Ejemplo 2', + 'sku' => 'PROD-002', + 'categoria' => 'Alimentos', + 'stock' => 50, + 'costo' => 25.50, + 'precio_venta' => 35.00, + 'impuesto' => 0 + ], + ]; + + return Excel::download( + new class($headers, $exampleData) implements FromArray, WithHeadings { + use Exportable; + + public function __construct(private array $headers, private array $data) {} + + public function array(): array + { + return $this->data; + } + + public function headings(): array + { + return $this->headers; + } + }, + 'plantilla_productos.xlsx' + ); + } + } diff --git a/app/Http/Controllers/App/SaleController.php b/app/Http/Controllers/App/SaleController.php index ca9a955..ec2aeba 100644 --- a/app/Http/Controllers/App/SaleController.php +++ b/app/Http/Controllers/App/SaleController.php @@ -6,6 +6,7 @@ use App\Http\Requests\App\SaleStoreRequest; use App\Services\SaleService; use App\Models\Sale; +use Illuminate\Http\Request; use Notsoweb\ApiResponse\Enums\ApiResponse; class SaleController extends Controller @@ -14,18 +15,32 @@ public function __construct( protected SaleService $saleService ) {} - public function index() + public function index(Request $request) { $sales = Sale::with(['details.inventory', 'user']) - ->orderBy('created_at', 'desc') - ->paginate(config('app.pagination')); + ->orderBy('created_at', 'desc'); + + if ($request->has('q') && $request->q) { + $sales->where('invoice_number', 'like', "%{$request->q}%") + ->orWhereHas('user', fn($query) => + $query->where('name', 'like', "%{$request->q}%") + ); + } + + if ($request->has('cash_register_id')) { + $sales->where('cash_register_id', $request->cash_register_id); + } + + if ($request->has('status')) { + $sales->where('status', $request->status); + } return ApiResponse::OK->response([ - 'sales' => $sales, + 'sales' => $sales->paginate(config('app.pagination')), ]); } - public function show( Sale $sale) + public function show(Sale $sale) { return ApiResponse::OK->response([ 'model' => $sale->load(['details.inventory', 'user']) diff --git a/app/Http/Requests/App/InventoryImportRequest.php b/app/Http/Requests/App/InventoryImportRequest.php new file mode 100644 index 0000000..f117ba7 --- /dev/null +++ b/app/Http/Requests/App/InventoryImportRequest.php @@ -0,0 +1,86 @@ + [ + 'required', + 'file', + 'mimes:xlsx,xls,csv', + 'max:10240', // 10MB máximo + ], + ]; + } + + /** + * Get custom messages for validator errors. + */ + public function messages(): array + { + return [ + 'file.required' => 'Debe seleccionar un archivo para importar.', + 'file.file' => 'El archivo no es válido.', + 'file.mimes' => 'El archivo debe ser de tipo Excel (.xlsx, .xls) o CSV (.csv).', + 'file.max' => 'El archivo no debe superar los 10MB.', + ]; + } + + /** + * Reglas de validación para cada fila del Excel + */ + public static function rowRules(): array + { + return [ + 'nombre' => ['required', 'string', 'max:100'], + 'sku' => ['nullable', 'string', 'max:50', 'unique:inventories,sku'], + 'categoria' => ['nullable', 'string', 'max:100'], + 'stock' => ['required', 'integer', 'min:0'], + 'costo' => ['required', 'numeric', 'min:0'], + 'precio_venta' => ['required', 'numeric', 'min:0', 'gt:costo'], + 'impuesto' => ['nullable', 'numeric', 'min:0', 'max:100'], + ]; + } + + /** + * Mensajes personalizados de validación para las filas del Excel + */ + public static function rowMessages(): array + { + return [ + 'nombre.required' => 'El nombre del producto es requerido.', + 'nombre.max' => 'El nombre no debe exceder los 100 caracteres.', + 'sku.unique' => 'El SKU ya existe en el sistema.', + 'sku.max' => 'El SKU no debe exceder los 50 caracteres.', + 'stock.required' => 'El stock es requerido.', + 'stock.integer' => 'El stock debe ser un número entero.', + 'stock.min' => 'El stock no puede ser negativo.', + 'costo.required' => 'El costo es requerido.', + 'costo.numeric' => 'El costo debe ser un número.', + 'costo.min' => 'El costo no puede ser negativo.', + 'precio_venta.required' => 'El precio de venta es requerido.', + 'precio_venta.numeric' => 'El precio de venta debe ser un número.', + 'precio_venta.min' => 'El precio de venta no puede ser negativo.', + 'precio_venta.gt' => 'El precio de venta debe ser mayor que el costo.', + 'impuesto.numeric' => 'El impuesto debe ser un número.', + 'impuesto.min' => 'El impuesto no puede ser negativo.', + 'impuesto.max' => 'El impuesto no puede exceder el 100%.', + ]; + } +} diff --git a/app/Imports/ProductsImport.php b/app/Imports/ProductsImport.php new file mode 100644 index 0000000..34c9887 --- /dev/null +++ b/app/Imports/ProductsImport.php @@ -0,0 +1,122 @@ + trim($row['categoria'])], + ['is_active' => true] + ); + $categoryId = $category->id; + } + + // Crear el producto en inventario + $inventory = Inventory::create([ + 'name' => trim($row['nombre']), + 'sku' => !empty($row['sku']) ? trim($row['sku']) : null, + 'category_id' => $categoryId, + 'stock' => (int) $row['stock'], + 'is_active' => true, + ]); + + // Crear el precio del producto + Price::create([ + 'inventory_id' => $inventory->id, + 'cost' => (float) $row['costo'], + 'retail_price' => (float) $row['precio_venta'], + 'tax' => !empty($row['impuesto']) ? (float) $row['impuesto'] : 0, + ]); + + $this->imported++; + + return $inventory; + } catch (\Exception $e) { + $this->skipped++; + $this->errors[] = "Error en fila: " . $e->getMessage(); + return null; + } + } + + /** + * Reglas de validación para cada fila + */ + public function rules(): array + { + return InventoryImportRequest::rowRules(); + } + + /** + * Mensajes personalizados de validación + */ + public function customValidationMessages() + { + return InventoryImportRequest::rowMessages(); + } + + /** + * Batch insert size + */ + public function batchSize(): int + { + return 100; + } + + /** + * Chunk size for reading + */ + public function chunkSize(): int + { + return 100; + } + + /** + * Obtener estadísticas de la importación + */ + public function getStats(): array + { + return [ + 'imported' => $this->imported, + 'skipped' => $this->skipped, + 'errors' => $this->errors, + ]; + } +} 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..054a481 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.1", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "682f1098a8fddbaf43edac2306a691c7ad508ec5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/682f1098a8fddbaf43edac2306a691c7ad508ec5", + "reference": "682f1098a8fddbaf43edac2306a691c7ad508ec5", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "ext-zlib": "*", + "php-64bit": "^8.3" + }, + "require-dev": { + "brianium/paratest": "^7.7", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.86", + "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.1" + }, + "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + } + ], + "time": "2025-12-10T09:58:31+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", @@ -10494,5 +11083,5 @@ "php": "^8.3" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/routes/api.php b/routes/api.php index 4c7bbda..2579c9f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -27,6 +27,8 @@ //INVENTARIO Route::resource('inventario', InventoryController::class); + Route::post('inventario/import', [InventoryController::class, 'import']); + Route::get('inventario/template/download', [InventoryController::class, 'downloadTemplate']); //CATEGORIAS Route::resource('categorias', CategoryController::class); @@ -34,7 +36,7 @@ //PRECIOS Route::resource('precios', PriceController::class); - // Rutas que debes agregar en routes/api.php + //VENTAS Route::resource('/sales', SaleController::class); Route::put('/sales/{sale}/cancel', [SaleController::class, 'cancel']);