diff --git a/app/common/Ideal/Cartola/Banco.php b/app/common/Ideal/Cartola/Banco.php index 16d88e2..253a952 100644 --- a/app/common/Ideal/Cartola/Banco.php +++ b/app/common/Ideal/Cartola/Banco.php @@ -2,9 +2,10 @@ namespace Incoviba\Common\Ideal\Cartola; use Incoviba\Common\Define; +use Incoviba\Common\Ideal\Service; use Psr\Http\Message\UploadedFileInterface; -abstract class Banco implements Define\Cartola\Banco +abstract class Banco extends Service implements Define\Cartola\Banco { public function process(UploadedFileInterface $file): array { @@ -14,6 +15,9 @@ abstract class Banco implements Define\Cartola\Banco foreach ($data as $row) { $r = []; foreach ($columns as $old => $new) { + if (!isset($row[$old])) { + continue; + } $r[$new] = $row[$old]; } $temp []= $r; diff --git a/app/resources/routes/api/contabilidad/cartolas.php b/app/resources/routes/api/contabilidad/cartolas.php index f81baa7..52cdf3c 100644 --- a/app/resources/routes/api/contabilidad/cartolas.php +++ b/app/resources/routes/api/contabilidad/cartolas.php @@ -1,9 +1,13 @@ group('/cartolas', function($app) { - $app->post('/procesar[/]', [Contabilidad::class, 'procesarCartola']); + $app->post('/procesar[/]', [Cartolas::class, 'procesar']); }); $app->group('/cartola', function($app) { - $app->post('/exportar[/]', [Contabilidad::class, 'exportarCartola']); + $app->group('/diaria', function($app) { + $app->post('/ayer[/]', [Cartolas::class, 'ayer']); + $app->post('/procesar[/]', [Cartolas::class, 'diaria']); + }); + $app->post('/exportar[/]', [Cartolas::class, 'exportar']); }); diff --git a/app/resources/routes/contabilidad/cartolas.php b/app/resources/routes/contabilidad/cartolas.php new file mode 100644 index 0000000..7bd0058 --- /dev/null +++ b/app/resources/routes/contabilidad/cartolas.php @@ -0,0 +1,6 @@ +group('/cartolas', function($app) { + $app->get('/diaria[/]', [Contabilidad::class, 'diaria']); +}); diff --git a/app/resources/views/contabilidad/cartolas/diaria.blade.php b/app/resources/views/contabilidad/cartolas/diaria.blade.php new file mode 100644 index 0000000..26b4d66 --- /dev/null +++ b/app/resources/views/contabilidad/cartolas/diaria.blade.php @@ -0,0 +1,323 @@ +@extends('layout.base') + +@section('page_content') +
+

+ Cartola Diaria +

+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+
+
+ + + +
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + +
HoyÚltimo SaldoSaldo ActualDiferencia
+
+
+ + + + + + + + + + + + + +
FechaGlosaDocumentoCargoAbonoSaldoOrden
+
+@endsection + +@include('layout.head.styles.datatables') +@include('layout.body.scripts.datatables') + +@push('page_scripts') + +@endpush diff --git a/app/setup/setups/services.php b/app/setup/setups/services.php index 485eda8..d0e986c 100644 --- a/app/setup/setups/services.php +++ b/app/setup/setups/services.php @@ -32,8 +32,12 @@ return [ }, Incoviba\Service\Cartola::class => function(ContainerInterface $container) { return (new Incoviba\Service\Cartola( + $container->get(Psr\Log\LoggerInterface::class), $container->get(Psr\Http\Message\StreamFactoryInterface::class), - $container->get(Incoviba\Common\Define\Contabilidad\Exporter::class) + $container->get(Incoviba\Common\Define\Contabilidad\Exporter::class), + $container->get(Incoviba\Repository\Inmobiliaria\Cuenta::class), + $container->get(Incoviba\Repository\Movimiento::class), + $container->get(Incoviba\Repository\Cartola::class) )) ->register('security', $container->get(Incoviba\Service\Cartola\Security::class)) ->register('itau', $container->get(Incoviba\Service\Cartola\Itau::class)) @@ -48,6 +52,7 @@ return [ }, Incoviba\Service\Contabilidad\Nubox::class => function(ContainerInterface $container) { return new Incoviba\Service\Contabilidad\Nubox( + $container->get(Psr\Log\LoggerInterface::class), $container->get(Incoviba\Repository\Nubox::class), $container->get(Incoviba\Service\Redis::class), new GuzzleHttp\Client(), diff --git a/app/src/Controller/API/Contabilidad.php b/app/src/Controller/API/Contabilidad.php index bf35b84..c99ce31 100644 --- a/app/src/Controller/API/Contabilidad.php +++ b/app/src/Controller/API/Contabilidad.php @@ -2,51 +2,16 @@ namespace Incoviba\Controller\API; use DateTimeImmutable; +use Incoviba\Common\Ideal\Controller; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Incoviba\Common\Implement\Exception\EmptyResult; use Incoviba\Repository; use Incoviba\Service; -class Contabilidad +class Contabilidad extends Controller { use withJson; - public function procesarCartola(ServerRequestInterface $request, ResponseInterface $response, - Repository\Inmobiliaria $inmobiliariaRepository, - Repository\Banco $bancoRepository, - Service\Cartola $cartolaService): ResponseInterface - { - $body = $request->getParsedBody(); - $output = [ - 'input' => $body, - 'movimientos' => [] - ]; - try { - $inmobiliaria = $inmobiliariaRepository->fetchById($body['inmobiliaria']); - $banco = $bancoRepository->fetchById($body['banco']); - $mes = new DateTimeImmutable($body['mes']); - $file = $request->getUploadedFiles()['file']; - $output['movimientos'] = $cartolaService->process($inmobiliaria, $banco, $mes, $file); - } catch (EmptyResult) {} - return $this->withJson($response, $output); - } - public function exportarCartola(ServerRequestInterface $request, ResponseInterface $response, - Repository\Inmobiliaria $inmobiliariaRepository, - Repository\Banco $bancoRepository, - Service\Cartola $cartolaService): ResponseInterface - { - $body = $request->getParsedBody(); - $output = [ - 'input' => $body, - 'filename' => '' - ]; - try { - $inmobiliaria = $inmobiliariaRepository->fetchById($body['inmobiliaria']); - $banco = $bancoRepository->fetchById($body['banco']); - $mes = new DateTimeImmutable($body['mes']); - $output['filename'] = $cartolaService->export($inmobiliaria, $banco, $mes, json_decode($body['movimientos'])); - } catch (EmptyResult) {} - return $this->withJson($response, $output); - } + } diff --git a/app/src/Controller/API/Contabilidad/Cartolas.php b/app/src/Controller/API/Contabilidad/Cartolas.php new file mode 100644 index 0000000..299c011 --- /dev/null +++ b/app/src/Controller/API/Contabilidad/Cartolas.php @@ -0,0 +1,98 @@ +getParsedBody(); + $output = [ + 'input' => $body, + 'movimientos' => [] + ]; + try { + $inmobiliaria = $inmobiliariaRepository->fetchById($body['inmobiliaria']); + $banco = $bancoRepository->fetchById($body['banco']); + $mes = new DateTimeImmutable($body['mes']); + $file = $request->getUploadedFiles()['file']; + $output['movimientos'] = $cartolaService->process($inmobiliaria, $banco, $mes, $file); + } catch (EmptyResult) {} + return $this->withJson($response, $output); + } + public function exportar(ServerRequestInterface $request, ResponseInterface $response, + Repository\Inmobiliaria $inmobiliariaRepository, + Repository\Banco $bancoRepository, + Service\Cartola $cartolaService): ResponseInterface + { + $body = $request->getParsedBody(); + $output = [ + 'input' => $body, + 'filename' => '' + ]; + try { + $inmobiliaria = $inmobiliariaRepository->fetchById($body['inmobiliaria']); + $banco = $bancoRepository->fetchById($body['banco']); + $mes = new DateTimeImmutable($body['mes']); + $output['filename'] = $cartolaService->export($inmobiliaria, $banco, $mes, json_decode($body['movimientos'])); + } catch (EmptyResult) {} + return $this->withJson($response, $output); + } + public function diaria(ServerRequestInterface $request, ResponseInterface $response, + Repository\Inmobiliaria $inmobiliariaRepository, + Repository\Banco $bancoRepository, + Service\Cartola $cartolaService): ResponseInterface + { + $body = $request->getParsedBody(); + $output = [ + 'input' => $body, + 'cartola' => [] + ]; + try { + $inmobiliaria = $inmobiliariaRepository->fetchById($body['inmobiliaria_rut']); + $banco = $bancoRepository->fetchById($body['banco_id']); + $fecha = new DateTimeImmutable($body['fecha']); + $file = $request->getUploadedFiles()['file']; + $output['cartola'] = $cartolaService->diaria($inmobiliaria, $banco, $fecha, $file); + } catch (EmptyResult $exception) { + $this->logger->debug($exception); + } + return $this->withJson($response, $output); + } + public function ayer(ServerRequestInterface $request, ResponseInterface $response, + Repository\Inmobiliaria $inmobiliariaRepository, + Repository\Banco $bancoRepository, + Repository\Inmobiliaria\Cuenta $cuentaRepository, + Repository\Cartola $cartolaRepository): ResponseInterface + { + $body = $request->getParsedBody(); + $output = [ + 'input' => $body, + 'cartola' => [] + ]; + try { + $inmobiliaria = $inmobiliariaRepository->fetchById($body['inmobiliaria_rut']); + $banco = $bancoRepository->fetchById($body['banco_id']); + $cuenta = $cuentaRepository->fetchByInmobiliariaAndBanco($inmobiliaria->rut, $banco->id); + $fecha = new DateTimeImmutable($body['fecha']); + $output['cartola'] = $cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $fecha); + } catch (EmptyResult $exception) { + $this->logger->critical($exception); + } + return $this->withJson($response, $output); + } +} diff --git a/app/src/Controller/Contabilidad.php b/app/src/Controller/Contabilidad.php new file mode 100644 index 0000000..8653d8c --- /dev/null +++ b/app/src/Controller/Contabilidad.php @@ -0,0 +1,31 @@ +fetchRedis($redisService, $redisKey); + } catch (EmptyRedis) { + try { + $inmobiliarias = $inmobiliariaRepository->fetchAll(); + $this->saveRedis($redisService, $redisKey, $inmobiliarias, 30 * 24 * 60 * 60); + } catch (EmptyResult) {} + } + return $view->render($response, 'contabilidad.cartolas.diaria', compact('inmobiliarias')); + } +} diff --git a/app/src/Model/Cartola.php b/app/src/Model/Cartola.php new file mode 100644 index 0000000..49363b6 --- /dev/null +++ b/app/src/Model/Cartola.php @@ -0,0 +1,26 @@ + $this->cuenta->id, + 'fecha' => $this->fecha->format('Y-m-d'), + 'cargos' => $this->cargos, + 'abonos' => $this->abonos, + 'saldo' => $this->saldo + ]); + } +} diff --git a/app/src/Model/Movimiento.php b/app/src/Model/Movimiento.php new file mode 100644 index 0000000..6ca1a36 --- /dev/null +++ b/app/src/Model/Movimiento.php @@ -0,0 +1,29 @@ + $this->cuenta->id, + 'fecha' => $this->fecha->format('Y-m-d'), + 'glosa' => $this->glosa, + 'documento' => $this->documento, + 'cargo' => $this->cargo, + 'abono' => $this->abono, + 'saldo' => $this->saldo + ]); + } +} diff --git a/app/src/Repository/Cartola.php b/app/src/Repository/Cartola.php new file mode 100644 index 0000000..be03b12 --- /dev/null +++ b/app/src/Repository/Cartola.php @@ -0,0 +1,60 @@ +setTable('cartolas'); + } + + public function create(?array $data = null): Model\Cartola + { + $map = (new Implement\Repository\MapperParser(['cargos', 'abonos', 'saldo'])) + ->register('fecha', new Implement\Repository\Mapper\DateTime('fecha')) + ->register('cuenta_id', (new Implement\Repository\Mapper()) + ->setProperty('cuenta') + ->setFunction(function($data) { + return $this->cuentaRepository->fetchById($data['cuenta_id']); + })); + return $this->parseData(new Model\Cartola(), $data, $map); + } + public function save(Define\Model $model): Model\Cartola + { + $model->id = $this->saveNew([ + 'cuenta_id', + 'fecha', + 'cargos', + 'abonos', + 'saldo' + ], [ + $model->cuenta->id, + $model->fecha->format('Y-m-d'), + $model->cargos, + $model->abonos, + $model->saldo + ]); + return $model; + } + public function edit(Define\Model $model, array $new_data): Model\Cartola + { + return $this->update($model, ['cuenta_id', 'fecha', 'cargos', 'abonos', 'saldo'], $new_data); + } + + public function fetchByCuentaAndFecha(int $cuenta_id, DateTimeInterface $fecha): Model\Cartola + { + $query = $this->connection->getQueryBuilder() + ->select() + ->from($this->getTable()) + ->where('cuenta_id = ? AND fecha = ?'); + return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d')]); + } +} diff --git a/app/src/Repository/Inmobiliaria/Cuenta.php b/app/src/Repository/Inmobiliaria/Cuenta.php index 71e3b5d..5005723 100644 --- a/app/src/Repository/Inmobiliaria/Cuenta.php +++ b/app/src/Repository/Inmobiliaria/Cuenta.php @@ -6,6 +6,7 @@ use Incoviba\Common\Ideal; use Incoviba\Repository; use Incoviba\Model; use Incoviba\Common\Implement; +use PhpParser\Node\Expr\BinaryOp\Mod; class Cuenta extends Ideal\Repository { @@ -49,4 +50,12 @@ class Cuenta extends Ideal\Repository ->where('inmobiliaria = ?'); return $this->fetchMany($query, [$inmobiliaria_rut]); } + public function fetchByInmobiliariaAndBanco(int $inmobiliaria_rut, int $banco_id): Model\Inmobiliaria\Cuenta + { + $query = $this->connection->getQueryBuilder() + ->select() + ->from($this->getTable()) + ->where('inmobiliaria = ? AND banco = ?'); + return $this->fetchOne($query, [$inmobiliaria_rut, $banco_id]); + } } diff --git a/app/src/Repository/Movimiento.php b/app/src/Repository/Movimiento.php new file mode 100644 index 0000000..8e11f06 --- /dev/null +++ b/app/src/Repository/Movimiento.php @@ -0,0 +1,64 @@ +setTable('movimientos'); + } + + public function create(?array $data = null): Model\Movimiento + { + $map = (new Implement\Repository\MapperParser(['cargo', 'abono', 'saldo', 'glosa', 'documento'])) + ->register('fecha', new Implement\Repository\Mapper\DateTime('fecha')) + ->register('cuenta_id', (new Implement\Repository\Mapper()) + ->setProperty('cuenta') + ->setFunction(function($data) { + return $this->cuentaRepository->fetchById($data['cuenta_id']); + }) + ); + return $this->parseData(new Model\Movimiento(), $data, $map); + } + public function save(Define\Model $model): Model\Movimiento + { + $model->id = $this->saveNew([ + 'cuenta_id', + 'fecha', + 'glosa', + 'documento', + 'cargo', + 'abono', + 'saldo' + ], [ + $model->cuenta->id, + $model->fecha->format('Y-m-d'), + $model->glosa, + $model->documento, + $model->cargo, + $model->abono, + $model->saldo + ]); + return $model; + } + public function edit(Define\Model $model, array $new_data): Model\Movimiento + { + return $this->update($model, ['cuenta_id', 'fecha', 'glosa', 'documento', 'cargo', 'abono', 'saldo'], $new_data); + } + + public function fetchByCuentaAndFechaAndMonto(int $cuenta_id, DateTimeInterface $fecha, int $monto): Model\Movimiento + { + $query = $this->connection->getQueryBuilder() + ->select() + ->from($this->getTable()) + ->where('cuenta_id = ? AND fecha = ? AND (cargo = ? OR abono = ?)'); + return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d'), $monto, $monto]); + } +} diff --git a/app/src/Service/Cartola.php b/app/src/Service/Cartola.php index 231ba7a..ae7f927 100644 --- a/app/src/Service/Cartola.php +++ b/app/src/Service/Cartola.php @@ -2,15 +2,26 @@ namespace Incoviba\Service; use DateTimeInterface; +use DateTimeImmutable; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UploadedFileInterface; +use Psr\Log\LoggerInterface; +use Incoviba\Common\Ideal\Service; use Incoviba\Common\Define\Cartola\Banco; use Incoviba\Common\Define\Contabilidad\Exporter; +use Incoviba\Common\Implement\Exception; use Incoviba\Model; +use Incoviba\Repository; -class Cartola +class Cartola extends Service { - public function __construct(protected StreamFactoryInterface $streamFactory, protected Exporter $exporter) {} + public function __construct(LoggerInterface $logger, + protected StreamFactoryInterface $streamFactory, protected Exporter $exporter, + protected Repository\Inmobiliaria\Cuenta $cuentaRepository, + protected Repository\Movimiento $movimientoRepository, + protected Repository\Cartola $cartolaRepository) { + parent::__construct($logger); + } protected array $bancos; public function register(string $name, Banco $banco): Cartola @@ -26,4 +37,75 @@ class Cartola { return $this->exporter->export($inmobiliaria, $banco, $mes, $movimientos); } + public function diaria(Model\Inmobiliaria $inmobiliaria, Model\Banco $banco, DateTimeInterface $fecha, UploadedFileInterface $file): array + { + $cuenta = $this->cuentaRepository->fetchByInmobiliariaAndBanco($inmobiliaria->rut, $banco->id); + + $ms = $this->getMovimientosDiarios($banco, $file); + + $cartolaData = [ + 'cargos' => 0, + 'abonos' => 0, + 'saldo' => 0 + ]; + $movimientos = []; + foreach ($ms as $m) { + $movimiento = $this->buildMovimiento($cuenta, $m); + + if ($movimiento->fecha->getTimestamp() === $fecha->getTimestamp()) { + $movimientos []= $movimiento; + $cartolaData['cargos'] += $movimiento->cargo; + $cartolaData['abonos'] += $movimiento->abono; + } + $cartolaData['saldo'] = $movimiento->saldo; + } + $cartola = $this->buildCartola($cuenta, $fecha, $cartolaData); + return compact('cartola', 'movimientos'); + } + + protected function getMovimientosDiarios(Model\Banco $banco, UploadedFileInterface $file): array + { + $movimientos = $this->bancos[strtolower($banco->nombre)]->process($file); + switch (strtolower($banco->nombre)) { + case 'security': + $movimientos = $this->processMovimientosDiariosSecurity($movimientos); + break; + case 'itau': + case 'santander': + break; + } + return $movimientos; + } + protected function processMovimientosDiariosSecurity(array $movimientos): array + { + $movimientos = array_reverse($movimientos); + array_shift($movimientos); + return $movimientos; + } + protected function buildCartola(Model\Inmobiliaria\Cuenta $cuenta, DateTimeInterface $fecha, array $data): Model\Cartola + { + try { + return $this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $fecha); + } catch (Exception\EmptyResult) { + $data['cuenta_id'] = $cuenta->id; + $data['fecha'] = $fecha->format('Y-m-d'); + $cartola = $this->cartolaRepository->create($data); + return $this->cartolaRepository->save($cartola); + } + } + protected function buildMovimiento(Model\Inmobiliaria\Cuenta $cuenta, array $data): Model\Movimiento + { + try { + return $this->movimientoRepository + ->fetchByCuentaAndFechaAndMonto( + $cuenta->id, + new DateTimeImmutable($data['fecha']), + $data['cargo'] ?? $data['abono'] + ); + } catch (Exception\EmptyResult) { + $data['cuenta_id'] = $cuenta->id; + $movimiento = $this->movimientoRepository->create($data); + return $this->movimientoRepository->save($movimiento); + } + } } diff --git a/app/src/Service/Cartola/Security.php b/app/src/Service/Cartola/Security.php index 28323a2..c00861c 100644 --- a/app/src/Service/Cartola/Security.php +++ b/app/src/Service/Cartola/Security.php @@ -24,8 +24,10 @@ class Security extends Banco 'fecha' => 'fecha', 'descripción' => 'glosa', 'número de documentos' => 'documento', + 'nº documento' => 'documento', 'cargos' => 'cargo', - 'abonos' => 'abono' + 'abonos' => 'abono', + 'saldos' => 'saldo' ]; } @@ -33,10 +35,9 @@ class Security extends Banco { $filename = '/tmp/cartola.xls'; $file->moveTo($filename); - $reader = PhpSpreadsheet\IOFactory::createReader('Xls'); - $xlsx = $reader->load($filename); + $xlsx = @PhpSpreadsheet\IOFactory::load($filename); $worksheet = $xlsx->getActiveSheet(); - $rows = $worksheet->getRowIterator(); + $rows = $worksheet->getRowIterator(3); $dataFound = false; $columns = []; $data = []; @@ -44,10 +45,10 @@ class Security extends Banco $cells = $row->getCellIterator(); $rowData = []; foreach ($cells as $cell) { - if ($cell->getColumn() === 'A' and $cell->getCalculatedValue() === "fecha ") { + if ($cell->getColumn() === 'A' and $cell->getCalculatedValue() !== null and strtolower($cell->getCalculatedValue()) === "fecha ") { $cols = $row->getColumnIterator(); foreach ($cols as $col) { - $columns[$col->getColumn()] = trim($col->getCalculatedValue()); + $columns[$col->getColumn()] = trim(strtolower($col->getCalculatedValue()), '  '); } $dataFound = true; break; @@ -62,7 +63,11 @@ class Security extends Banco $col = $columns[$cell->getColumn()]; $value = $cell->getCalculatedValue(); if ($col === 'fecha') { - $value = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($cell->getValue(), 'America/Santiago')->format('Y-m-d'); + if ((int) $cell->getValue() !== $cell->getValue()) { + $value = implode('-', array_reverse(explode('-', $cell->getValue()))); + } else { + $value = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($cell->getValue(), 'America/Santiago')->format('Y-m-d'); + } } $rowData[$col] = $value; }