diff --git a/app/GrupoDeCompra.php b/app/GrupoDeCompra.php index bb13a9f..782d16c 100644 --- a/app/GrupoDeCompra.php +++ b/app/GrupoDeCompra.php @@ -11,10 +11,11 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use League\Csv\Exception; class GrupoDeCompra extends Model { - protected $fillable = ["nombre", "region", "devoluciones_habilitadas"]; + protected $fillable = ["nombre", "region", "devoluciones_habilitadas", "saldo"]; protected $table = 'grupos_de_compra'; public function subpedidos(): HasMany @@ -69,11 +70,17 @@ class GrupoDeCompra extends Model return $total; } - public function totalATransferir() + public function totalDePedido() { return $this->totalCentralesQueNoPaganTransporte() + $this->totalCentralesQuePaganTransporte() - + $this->totalTransporte(); + + $this->totalTransporte() + ; + } + + public function totalATransferir() + { + return $this->totalDePedido() - $this->saldo; } public function totalCentralesQueNoPaganTransporte() @@ -159,6 +166,10 @@ class GrupoDeCompra extends Model } //Asume que los productos están gruadados en orden de fila + + /** + * @throws Exception + */ public static function obtenerTemplateDeFilasVacias(int $columns): array { $productosFilaID = Producto::productosFilaID(); @@ -176,6 +187,9 @@ class GrupoDeCompra extends Model return $template; } + /** + * @throws Exception + */ public function exportarPedidoEnCSV() { $records = $this->generarColumnaCantidades(); @@ -184,6 +198,9 @@ class GrupoDeCompra extends Model CsvHelper::generarCsv('csv/exports/' . $this->nombre . '-' . $fecha . '.csv', $records); } + /** + * @throws Exception + */ public function generarColumnaCantidades(): array { $productos_en_pedido = $this->productosPedidos(); @@ -206,6 +223,9 @@ class GrupoDeCompra extends Model return $records; } + /** + * @throws Exception + */ public function exportarPedidoConNucleosEnCSV() { $productos_en_pedido = $this->productosPedidos(); @@ -284,4 +304,9 @@ class GrupoDeCompra extends Model ->get() ->keyBy('producto_id'); } + + public function setSaldo(float $saldo) { + $this->saldo = $saldo; + $this->save(); + } } diff --git a/app/Helpers/CanastaHelper.php b/app/Helpers/CanastaHelper.php index bda7f71..d8884b1 100644 --- a/app/Helpers/CanastaHelper.php +++ b/app/Helpers/CanastaHelper.php @@ -9,6 +9,7 @@ use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; use Illuminate\Support\Str; +use League\Csv\Exception; class CanastaHelper { @@ -25,7 +26,8 @@ class CanastaHelper $log = CanastaLog::where('descripcion', self::CANASTA_CARGADA) ->orderBy('created_at', 'desc') ->first(); - $nombre = str_replace("csv/canastas/", "", $log->path); + $nombre = str_replace(storage_path(), "", $log->path); + $nombre = str_replace("/csv/canastas/", "", $nombre); $result["nombre"] = str_replace(".csv", "", $nombre); $result["fecha"] = $log->created_at; return $result; @@ -38,17 +40,22 @@ class CanastaHelper $nombre = $data->getClientOriginalName(); - $data->move(storage_path($path), $nombre); + $storage_path = storage_path($path); + $data->move($storage_path, $nombre); - self::log($path . $nombre, self::ARCHIVO_SUBIDO); + self::log($storage_path . $nombre, self::ARCHIVO_SUBIDO); return $nombre; } + /** + * @throws Exception + */ public static function cargarCanasta($archivo) { self::limpiarTablas(); - $registros = CsvHelper::getRecords($archivo); + $registros = CsvHelper::getRecords($archivo, "No se pudo leer el archivo."); + $toInsert = []; $categoria = ''; diff --git a/app/Helpers/CsvHelper.php b/app/Helpers/CsvHelper.php index 5686b96..c27ff6d 100644 --- a/app/Helpers/CsvHelper.php +++ b/app/Helpers/CsvHelper.php @@ -13,8 +13,11 @@ use League\Csv\Writer; class CsvHelper { - public static function getRecords($filePath): Iterator { - $csv = Reader::createFromPath(storage_path($filePath)); + /** + * @throws Exception + */ + public static function getRecords($filePath, $message): Iterator { + $csv = Reader::createFromPath($filePath); try { $csv->setDelimiter("|"); $csv->setEnclosure("'"); @@ -22,7 +25,7 @@ class CsvHelper return $csv->getRecords(); } catch (InvalidArgument|Exception $e) { Log::error($e->getMessage()); - return null; + throw new Exception($message, $e); } } diff --git a/app/Helpers/TransporteHelper.php b/app/Helpers/TransporteHelper.php index 49ec37f..046b4b8 100644 --- a/app/Helpers/TransporteHelper.php +++ b/app/Helpers/TransporteHelper.php @@ -4,6 +4,7 @@ namespace App\Helpers; use App\CanastaLog; use Illuminate\Support\Facades\Log; +use League\Csv\Exception; class TransporteHelper { @@ -20,6 +21,9 @@ class TransporteHelper return self::cantidadTransporte($monto) * self::COSTO_TRANSPORTE; } + /** + * @throws Exception + */ public static function filaTransporte() { $ultimaCanasta = CanastaLog::where('descripcion', CanastaHelper::CANASTA_CARGADA) @@ -27,12 +31,14 @@ class TransporteHelper ->pluck('path') ->first(); - $registros = CsvHelper::getRecords($ultimaCanasta); + $registros = CsvHelper::getRecords($ultimaCanasta, "No se encontró la ultima canasta."); + $error = 'No hay fila de tipo T en la planilla: ' . $ultimaCanasta; foreach ($registros as $key => $registro) - if ($registro[CanastaHelper::TIPO] == 'T') return $key; + if ($registro[CanastaHelper::TIPO] == 'T') + return $key; - Log::error('No hay fila de tipo T en la planilla: ' . $ultimaCanasta); - return null; + Log::error($error); + throw new Exception($error); } } diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 3073578..a280c84 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\GrupoDeCompra; -use Symfony\Component\HttpFoundation\BinaryFileResponse; +use League\Csv\Exception; class AdminController extends Controller { @@ -20,9 +20,13 @@ class AdminController extends Controller $gdc->exportarPedidosAPdf(); } - public function exportarPedidoACSV(GrupoDeCompra $gdc): BinaryFileResponse + public function exportarPedidoACSV(GrupoDeCompra $gdc) { - $gdc->exportarPedidoEnCSV(); + try { + $gdc->exportarPedidoEnCSV(); + } catch (Exception $e) { + return response()->json(['message' => $e->getMessage()]); + } $pattern = storage_path('csv/exports/'. $gdc->nombre . '-*.csv'); $files = glob($pattern); @@ -33,9 +37,13 @@ class AdminController extends Controller return response()->download($files[0]); } - public function exportarPedidoConNucleosACSV(GrupoDeCompra $gdc): BinaryFileResponse + public function exportarPedidoConNucleosACSV(GrupoDeCompra $gdc) { - $gdc->exportarPedidoConNucleosEnCSV(); + try { + $gdc->exportarPedidoConNucleosEnCSV(); + } catch (Exception $e) { + return response()->json(['message' => $e->getMessage()]); + } $pattern = storage_path('csv/exports/'.$gdc->nombre.'-completo-*.csv'); $files = glob($pattern); diff --git a/app/Http/Controllers/Api/GrupoDeCompraController.php b/app/Http/Controllers/Api/GrupoDeCompraController.php index 754b1ae..8ede935 100644 --- a/app/Http/Controllers/Api/GrupoDeCompraController.php +++ b/app/Http/Controllers/Api/GrupoDeCompraController.php @@ -4,9 +4,8 @@ namespace App\Http\Controllers\Api; use App\GrupoDeCompra; use App\Http\Controllers\Controller; -use App\Http\Resources\GrupoDeCompraReducido; +use App\Http\Resources\GrupoDeCompraComisionesResource; use App\Http\Resources\GrupoDeCompraResource; -use http\Env\Request; class GrupoDeCompraController extends Controller { @@ -32,4 +31,18 @@ class GrupoDeCompraController extends Controller GrupoDeCompra::find($gdc)->toggleDevoluciones(); return response()->noContent(); } + + public function setSaldo(int $gdc) { + $valid = request()->validate([ + 'saldo' => ['required', 'min:0'], + ]); + $grupoDeCompra = GrupoDeCompra::find($gdc); + $grupoDeCompra->setSaldo($valid['saldo']); + return response()->noContent(); + } + + public function saldos() + { + return GrupoDeCompraComisionesResource::collection(GrupoDeCompra::all()); + } } diff --git a/app/Http/Controllers/ComisionesController.php b/app/Http/Controllers/ComisionesController.php index 9ffd3f1..6c5d555 100644 --- a/app/Http/Controllers/ComisionesController.php +++ b/app/Http/Controllers/ComisionesController.php @@ -4,23 +4,32 @@ namespace App\Http\Controllers; use App\GrupoDeCompra; use App\Helpers\CanastaHelper; +use App\Helpers\CsvHelper; +use App\Http\Resources\GrupoDeCompraResource; use App\Producto; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use League\Csv\Exception; use Symfony\Component\HttpFoundation\BinaryFileResponse; class ComisionesController { const CANASTAS_PATH = 'csv/canastas/'; + const BARRIO = "Barrio"; + const SALDO = "Saldo"; public function show() { return view('auth/login'); } - public function descargarPedidos(): BinaryFileResponse + public function descargarPedidos() { - Producto::planillaTotales(); + try { + Producto::planillaTotales(); + } catch (Exception $e) { + return response()->json(['message' => $e->getMessage()], 500); + } $pattern = storage_path('csv/exports/pedidos-por-barrio-*.csv'); $files = glob($pattern); @@ -55,7 +64,11 @@ class ComisionesController ]); $nombre = CanastaHelper::guardarCanasta($request->file('data'), self::CANASTAS_PATH); - CanastaHelper::cargarCanasta(self::CANASTAS_PATH . $nombre); + try { + CanastaHelper::cargarCanasta(storage_path(self::CANASTAS_PATH . $nombre)); + } catch (Exception $e) { + return response()->json(['message' => $e->getMessage()], 500); + } return response()->json([ 'message' => 'Canasta cargada exitosamente', @@ -64,7 +77,31 @@ class ComisionesController public function descargarCanastaEjemplo(): BinaryFileResponse { - $file = storage_path('csv/productos.csv'); + $file = resource_path('csv/productos.csv'); return response()->download($file); } + + public function cargarSaldos(Request $request): JsonResponse + { + $request->validate([ + 'data' => 'required|file|mimes:csv,txt|max:2048', + ]); + + $file = $request->file('data')->getPathname(); + + try { + $records = CsvHelper::getRecords($file, "No se pudo leer el archivo."); + } catch (Exception $e) { + return response()->json(['message' => $e->getMessage()], 500); + } + + foreach ($records as $record) { + $barrio = $record[self::BARRIO]; + $saldo = $record[self::SALDO]; + GrupoDeCompra::where('nombre', $barrio) + ->update(['saldo' => $saldo]); + } + + return response()->json(GrupoDeCompraResource::collection(GrupoDeCompra::all())); + } } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index d85e473..62243c5 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\GrupoDeCompra; -use App\Http\Resources\GrupoDeCompraReducido; +use App\Http\Resources\GrupoDeCompraPedidoResource; use App\Http\Resources\GrupoDeCompraResource; use App\UserRole; use Illuminate\Http\Request; @@ -22,7 +22,7 @@ class UserController extends Controller $grupo_de_compra = GrupoDeCompra::find($user->grupo_de_compra_id); switch (UserRole::findOrFail($user->role_id)->nombre) { case 'barrio': - $result['grupo_de_compra'] = new GrupoDeCompraReducido($grupo_de_compra); + $result['grupo_de_compra'] = new GrupoDeCompraPedidoResource($grupo_de_compra); break; case 'admin_barrio': $result['grupo_de_compra'] = new GrupoDeCompraResource($grupo_de_compra); diff --git a/app/Http/Resources/GrupoDeCompraComisionesResource.php b/app/Http/Resources/GrupoDeCompraComisionesResource.php new file mode 100644 index 0000000..537971e --- /dev/null +++ b/app/Http/Resources/GrupoDeCompraComisionesResource.php @@ -0,0 +1,22 @@ + $this->id, + 'nombre' => $this->nombre, + 'saldo' => $this->saldo, + ]; + } +} diff --git a/app/Http/Resources/GrupoDeCompraReducido.php b/app/Http/Resources/GrupoDeCompraPedidoResource.php similarity index 89% rename from app/Http/Resources/GrupoDeCompraReducido.php rename to app/Http/Resources/GrupoDeCompraPedidoResource.php index b81ac02..ea725f7 100644 --- a/app/Http/Resources/GrupoDeCompraReducido.php +++ b/app/Http/Resources/GrupoDeCompraPedidoResource.php @@ -4,7 +4,7 @@ namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; -class GrupoDeCompraReducido extends JsonResource +class GrupoDeCompraPedidoResource extends JsonResource { /** * Transform the resource into an array. diff --git a/app/Http/Resources/GrupoDeCompraResource.php b/app/Http/Resources/GrupoDeCompraResource.php index ef16fb5..b67e14b 100644 --- a/app/Http/Resources/GrupoDeCompraResource.php +++ b/app/Http/Resources/GrupoDeCompraResource.php @@ -21,9 +21,11 @@ class GrupoDeCompraResource extends JsonResource 'devoluciones_habilitadas' => $this->devoluciones_habilitadas, 'pedidos' => SubpedidoResource::collection($this->subpedidos), 'total_a_recaudar' => number_format($this->totalARecaudar(),2), + 'saldo' => number_format($this->saldo, 2, ".", ""), 'total_sin_devoluciones' => number_format($this->totalSinDevoluciones(),2), 'total_barrial' => number_format($this->totalBarrial(),2), 'total_devoluciones' => number_format($this->totalDevoluciones(),2), + 'total_de_pedido' => number_format($this->totalDePedido(),2), 'total_a_transferir' => number_format($this->totalATransferir(),2), 'total_transporte' => number_format($this->totalTransporte()), 'cantidad_transporte' => number_format($this->cantidadTransporte()), diff --git a/app/Producto.php b/app/Producto.php index 505909d..5617617 100644 --- a/app/Producto.php +++ b/app/Producto.php @@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use League\Csv\Exception; class Producto extends Model { @@ -75,6 +76,9 @@ class Producto extends Model ->get(); } + /** + * @throws Exception + */ static public function planillaTotales() { $headers = ['Producto']; diff --git a/database/migrations/2025_06_14_172643_agregar_saldos_a_barrios.php b/database/migrations/2025_06_14_172643_agregar_saldos_a_barrios.php new file mode 100644 index 0000000..bffe955 --- /dev/null +++ b/database/migrations/2025_06_14_172643_agregar_saldos_a_barrios.php @@ -0,0 +1,34 @@ +double('saldo', 10, 2)->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Remover columna 'saldo' de la tabla 'grupos_de_compra' + Schema::table('grupos_de_compra', function (Blueprint $table) { + $table->dropColumn('saldo'); + }); + } +} diff --git a/database/seeds/CanastaSeeder.php b/database/seeds/CanastaSeeder.php index 167fa8f..19ed268 100644 --- a/database/seeds/CanastaSeeder.php +++ b/database/seeds/CanastaSeeder.php @@ -11,9 +11,10 @@ class CanastaSeeder extends Seeder * Run the database seeds. * * @return void + * @throws \League\Csv\Exception */ public function run() { - CanastaHelper::cargarCanasta(self::ARCHIVO_DEFAULT); + CanastaHelper::cargarCanasta(resource_path(self::ARCHIVO_DEFAULT)); } } diff --git a/database/seeds/GrupoDeCompraSeeder.php b/database/seeds/GrupoDeCompraSeeder.php index ae06746..366b25d 100644 --- a/database/seeds/GrupoDeCompraSeeder.php +++ b/database/seeds/GrupoDeCompraSeeder.php @@ -13,10 +13,11 @@ class GrupoDeCompraSeeder extends Seeder * Run the database seeds. * * @return void + * @throws \League\Csv\Exception */ public function run() { - $registros = CsvHelper::getRecords('csv/barrios.csv'); + $registros = CsvHelper::getRecords(resource_path('csv/barrios.csv'), 'No se pudo leer la planilla de barrios.'); $gdcToInsert = []; $usersToInsert = []; $roles = UserRole::where('nombre', 'barrio')->orWhere('nombre', 'admin_barrio')->get(); diff --git a/resources/js/components/admin/TablaPedidos.vue b/resources/js/components/admin/TablaPedidos.vue index 9907ac7..1aae42c 100644 --- a/resources/js/components/admin/TablaPedidos.vue +++ b/resources/js/components/admin/TablaPedidos.vue @@ -43,7 +43,15 @@ $ {{ total_transporte }} - Total a depositar: + Total de pedido: + $ {{ total_de_pedido }} + + + Saldo a favor: + - $ {{ saldo }} + + + Total a transferir: $ {{ total_a_transferir }} @@ -67,7 +75,9 @@ export default { "total_devoluciones", "cantidad_transporte", "total_transporte", + "total_de_pedido", "total_a_transferir", + "saldo", ]), ...mapGetters('admin', ['pedidosAprobados']), }, diff --git a/resources/js/components/comisiones/Body.vue b/resources/js/components/comisiones/Body.vue index 20573ec..56287e4 100644 --- a/resources/js/components/comisiones/Body.vue +++ b/resources/js/components/comisiones/Body.vue @@ -1,44 +1,22 @@ @@ -46,22 +24,29 @@ diff --git a/resources/js/components/comisiones/CanastaInput.vue b/resources/js/components/comisiones/canasta/CanastaInput.vue similarity index 100% rename from resources/js/components/comisiones/CanastaInput.vue rename to resources/js/components/comisiones/canasta/CanastaInput.vue diff --git a/resources/js/components/comisiones/canasta/CanastaSeccion.vue b/resources/js/components/comisiones/canasta/CanastaSeccion.vue new file mode 100644 index 0000000..b138ea9 --- /dev/null +++ b/resources/js/components/comisiones/canasta/CanastaSeccion.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/resources/js/components/comisiones/saldos/SaldoRow.vue b/resources/js/components/comisiones/saldos/SaldoRow.vue new file mode 100644 index 0000000..d5ba174 --- /dev/null +++ b/resources/js/components/comisiones/saldos/SaldoRow.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/resources/js/components/comisiones/saldos/SaldosSeccion.vue b/resources/js/components/comisiones/saldos/SaldosSeccion.vue new file mode 100644 index 0000000..c9b413a --- /dev/null +++ b/resources/js/components/comisiones/saldos/SaldosSeccion.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/resources/js/components/comisiones/saldos/TablaSaldos.vue b/resources/js/components/comisiones/saldos/TablaSaldos.vue new file mode 100644 index 0000000..46130b1 --- /dev/null +++ b/resources/js/components/comisiones/saldos/TablaSaldos.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/resources/js/components/comunes/InputFileButton.vue b/resources/js/components/comunes/InputFileButton.vue new file mode 100644 index 0000000..a9f70a4 --- /dev/null +++ b/resources/js/components/comunes/InputFileButton.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/resources/js/components/pedidos/Body.vue b/resources/js/components/pedidos/Body.vue index 706adc2..7e85e18 100644 --- a/resources/js/components/pedidos/Body.vue +++ b/resources/js/components/pedidos/Body.vue @@ -1,5 +1,5 @@