From 8e7f43e487b555a120c6b19feea9861feb65a9de Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Wed, 17 Jul 2024 22:33:33 -0400 Subject: [PATCH] Subir cartolas procesadas --- Dockerfile | 1 + app/common/Implement/Repository/Mapper.php | 11 +- .../routes/api/contabilidad/cartolas.php | 1 + .../routes/api/contabilidad/tesoreria.php | 6 + .../routes/contabilidad/cartolas.php | 1 + .../contabilidad/informes/tesoreria.php | 2 + .../contabilidad/cartolas/diaria.blade.php | 291 ++++++++------ .../contabilidad/cartolas/import.blade.php | 300 ++++++++++++++ .../contabilidad/tesoreria/import.blade.php | 53 +++ .../body/header/menu/contabilidad.blade.php | 8 +- .../views/layout/body/scripts/rut.blade.php | 2 +- app/setup/setups/services.php | 4 +- .../Controller/API/Contabilidad/Cartolas.php | 60 +++ .../API/Contabilidad/Movimientos.php | 31 +- .../Controller/API/Contabilidad/Tesoreria.php | 29 ++ app/src/Controller/Contabilidad.php | 22 +- app/src/Controller/Contabilidad/Informes.php | 2 +- app/src/Controller/Contabilidad/Tesoreria.php | 14 + app/src/Model/Contabilidad/Movimiento.php | 6 +- .../Model/Contabilidad/Movimiento/Detalle.php | 6 +- app/src/Repository/Contabilidad/Banco.php | 9 + .../Repository/Contabilidad/Movimiento.php | 6 +- .../Contabilidad/Movimiento/Detalle.php | 11 +- app/src/Service/Contabilidad.php | 2 +- app/src/Service/Contabilidad/Cartola.php | 3 +- app/src/Service/Contabilidad/Cartola/BCI.php | 8 +- app/src/Service/Contabilidad/Cartola/Itau.php | 9 +- .../Contabilidad/Cartola/Santander.php | 8 +- .../Service/Contabilidad/Cartola/Security.php | 9 +- .../Contabilidad/Informe/Tesoreria.php | 369 +----------------- .../Contabilidad/Informe/Tesoreria/Input.php | 36 ++ .../Informe/Tesoreria/Input/Data/DAPyFFMM.php | 12 + .../Tesoreria/Input/Data/SaldosContables.php | 152 ++++++++ .../Informe/Tesoreria/Input/Excel.php | 35 ++ .../Tesoreria/Input/Excel/DAPyFFMM.php | 51 +++ .../Tesoreria/Input/Excel/DeudaBanco.php | 34 ++ .../Tesoreria/Input/Excel/SaldosCuentas.php | 109 ++++++ .../Informe/Tesoreria/Input/Excel/Sheet.php | 73 ++++ .../Contabilidad/Informe/Tesoreria/Output.php | 226 +++++++++++ .../Informe/Tesoreria/Output/Data/Cuenta.php | 21 + .../Tesoreria/Output/Data/Movimientos.php | 87 +++++ .../Tesoreria/Output/Data/Sociedad.php | 34 ++ .../Informe/Tesoreria/Output/Data/Totales.php | 36 ++ .../Informe/Tesoreria/{ => Output}/Excel.php | 8 +- .../Informe/Tesoreria/{ => Output}/PDF.php | 2 +- app/src/Service/Contabilidad/Movimiento.php | 20 +- php-memory.ini | 2 + php.ini.bak | Bin 0 -> 57266 bytes 48 files changed, 1692 insertions(+), 530 deletions(-) create mode 100644 app/resources/routes/api/contabilidad/tesoreria.php create mode 100644 app/resources/views/contabilidad/cartolas/import.blade.php create mode 100644 app/resources/views/contabilidad/tesoreria/import.blade.php create mode 100644 app/src/Controller/API/Contabilidad/Tesoreria.php create mode 100644 app/src/Controller/Contabilidad/Tesoreria.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/DAPyFFMM.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/SaldosContables.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DAPyFFMM.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DeudaBanco.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/SaldosCuentas.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/Sheet.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Output.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Cuenta.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Movimientos.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Sociedad.php create mode 100644 app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Totales.php rename app/src/Service/Contabilidad/Informe/Tesoreria/{ => Output}/Excel.php (99%) rename app/src/Service/Contabilidad/Informe/Tesoreria/{ => Output}/PDF.php (66%) create mode 100644 php-memory.ini create mode 100644 php.ini.bak diff --git a/Dockerfile b/Dockerfile index c8dd985..4641cd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ RUN pecl install xdebug-3.1.3 \ COPY ./php-errors.ini /usr/local/etc/php/conf.d/docker-php-errors.ini COPY ./php-xdebug.ini /usr/local/etc/php/conf.d/docker-php-xdebug.ini +COPY ./php-memory.ini /usr/local/etc/php/conf.d/docker-php-memory.ini COPY --from=composer /usr/bin/composer /usr/bin/composer diff --git a/app/common/Implement/Repository/Mapper.php b/app/common/Implement/Repository/Mapper.php index a8dc82b..d17aeaf 100644 --- a/app/common/Implement/Repository/Mapper.php +++ b/app/common/Implement/Repository/Mapper.php @@ -4,6 +4,7 @@ namespace Incoviba\Common\Implement\Repository; use Error; use Closure; use Incoviba\Common\Define; +use Incoviba\Common\Implement\Exception\EmptyResult; class Mapper implements Define\Repository\Mapper { @@ -68,7 +69,15 @@ class Mapper implements Define\Repository\Mapper $value = $data[$column]; if ($this->hasFunction()) { if ($value !== null) { - $value = ($this->function)($data); + try { + $value = ($this->function)($data); + } catch (EmptyResult $exception) { + if ($this->hasDefault()) { + $value = $this->default; + } else { + throw $exception; + } + } } elseif ($this->hasDefault()) { $value = $this->default; } diff --git a/app/resources/routes/api/contabilidad/cartolas.php b/app/resources/routes/api/contabilidad/cartolas.php index 52cdf3c..9bc2f80 100644 --- a/app/resources/routes/api/contabilidad/cartolas.php +++ b/app/resources/routes/api/contabilidad/cartolas.php @@ -3,6 +3,7 @@ use Incoviba\Controller\API\Contabilidad\Cartolas; $app->group('/cartolas', function($app) { $app->post('/procesar[/]', [Cartolas::class, 'procesar']); + $app->post('/importar[/]', [Cartolas::class, 'importar']); }); $app->group('/cartola', function($app) { $app->group('/diaria', function($app) { diff --git a/app/resources/routes/api/contabilidad/tesoreria.php b/app/resources/routes/api/contabilidad/tesoreria.php new file mode 100644 index 0000000..238e85d --- /dev/null +++ b/app/resources/routes/api/contabilidad/tesoreria.php @@ -0,0 +1,6 @@ +group('/tesoreria', function($app) { + $app->post('/import[/]', [Tesoreria::class, 'import']); +}); diff --git a/app/resources/routes/contabilidad/cartolas.php b/app/resources/routes/contabilidad/cartolas.php index 7bd0058..fe7a14e 100644 --- a/app/resources/routes/contabilidad/cartolas.php +++ b/app/resources/routes/contabilidad/cartolas.php @@ -3,4 +3,5 @@ use Incoviba\Controller\Contabilidad; $app->group('/cartolas', function($app) { $app->get('/diaria[/]', [Contabilidad::class, 'diaria']); + $app->get('/importar[/]', [Contabilidad::class, 'importar']); }); diff --git a/app/resources/routes/contabilidad/informes/tesoreria.php b/app/resources/routes/contabilidad/informes/tesoreria.php index 9cbca00..20c447e 100644 --- a/app/resources/routes/contabilidad/informes/tesoreria.php +++ b/app/resources/routes/contabilidad/informes/tesoreria.php @@ -1,6 +1,8 @@ group('/tesoreria', function($app) { + $app->get('/import[/]', [Tesoreria::class, 'import']); $app->get('[/[{fecha}[/]]]', [Contabilidad::class, 'tesoreria']); }); diff --git a/app/resources/views/contabilidad/cartolas/diaria.blade.php b/app/resources/views/contabilidad/cartolas/diaria.blade.php index 316246b..2988a76 100644 --- a/app/resources/views/contabilidad/cartolas/diaria.blade.php +++ b/app/resources/views/contabilidad/cartolas/diaria.blade.php @@ -52,8 +52,7 @@ Cargo Abono Saldo - Centro de Costo - Detalle + Orden @@ -115,10 +114,85 @@ + @endsection @include('layout.head.styles.datatables') @include('layout.body.scripts.datatables') +@include('layout.body.scripts.rut') @push('page_scripts') +@endpush diff --git a/app/resources/views/contabilidad/tesoreria/import.blade.php b/app/resources/views/contabilidad/tesoreria/import.blade.php new file mode 100644 index 0000000..512eba1 --- /dev/null +++ b/app/resources/views/contabilidad/tesoreria/import.blade.php @@ -0,0 +1,53 @@ +@extends('layout.base') + +@section('page_content') +
+

Importar Informe de Tesorería

+ +
+
+ +
+ + +
+
+ +
+
+@endsection + +@push('page_scripts') + +@endpush diff --git a/app/resources/views/layout/body/header/menu/contabilidad.blade.php b/app/resources/views/layout/body/header/menu/contabilidad.blade.php index fa8c613..86f94a3 100644 --- a/app/resources/views/layout/body/header/menu/contabilidad.blade.php +++ b/app/resources/views/layout/body/header/menu/contabilidad.blade.php @@ -16,7 +16,13 @@ Asignar en Cartola - Cartola Diaria +
+ + Cartola Diaria + +
Depósitos a Plazo Movimientos diff --git a/app/resources/views/layout/body/scripts/rut.blade.php b/app/resources/views/layout/body/scripts/rut.blade.php index d0e099f..3a93ebe 100644 --- a/app/resources/views/layout/body/scripts/rut.blade.php +++ b/app/resources/views/layout/body/scripts/rut.blade.php @@ -15,7 +15,7 @@ if (rut.length === 0) { return '' } - rut.replace(/\./g, '') + rut.replace(/\D/g, '') return rut.replace(/^(\d{1,2})(\d{3})(\d{3})$/, '$1.$2.$3') } static validar(rut, digito) { diff --git a/app/setup/setups/services.php b/app/setup/setups/services.php index 2864326..0c14f39 100644 --- a/app/setup/setups/services.php +++ b/app/setup/setups/services.php @@ -72,8 +72,8 @@ return [ ) ->register('xlsx', Incoviba\Service\Informe\Excel::class); }, - Incoviba\Service\Contabilidad\Informe\Tesoreria\Excel::class => function(ContainerInterface $container) { - return new Incoviba\Service\Contabilidad\Informe\Tesoreria\Excel( + \Incoviba\Service\Contabilidad\Informe\Tesoreria\Output\Excel::class => function(ContainerInterface $container) { + return new \Incoviba\Service\Contabilidad\Informe\Tesoreria\Output\Excel( $container->get(Psr\Log\LoggerInterface::class), $container->get('folders')->get('informes'), $container->get(Incoviba\Service\UF::class), diff --git a/app/src/Controller/API/Contabilidad/Cartolas.php b/app/src/Controller/API/Contabilidad/Cartolas.php index 179c7e2..b8ad3e0 100644 --- a/app/src/Controller/API/Contabilidad/Cartolas.php +++ b/app/src/Controller/API/Contabilidad/Cartolas.php @@ -5,10 +5,14 @@ use DateTimeImmutable; use Incoviba\Common\Ideal\Controller; use Incoviba\Common\Implement\Exception\EmptyResult; use Incoviba\Controller\API\withJson; +use Incoviba\Model\Contabilidad\Banco; +use Incoviba\Model\Inmobiliaria; use Incoviba\Repository; use Incoviba\Service; +use PhpParser\Node\Stmt\TryCatch; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Log\LoggerInterface; class Cartolas extends Controller { @@ -95,4 +99,60 @@ class Cartolas extends Controller } catch (EmptyResult) {} return $this->withJson($response, $output); } + public function importar(ServerRequestInterface $request, ResponseInterface $response, + LoggerInterface $logger, + Repository\Inmobiliaria $inmobiliariaRepository, + Repository\Contabilidad\Banco $bancoRepository, + Repository\Inmobiliaria\Cuenta $cuentaRepository, + Service\Contabilidad\Cartola $cartolaService, + Service\Contabilidad\Movimiento $movimientoService): ResponseInterface + { + $body = $request->getParsedBody(); + $files = $request->getUploadedFiles(); + + $output = [ + 'input' => $body, + 'movimientos' => [] + ]; + if (is_array($files['file'])) { + foreach ($files['file'] as $i => $file) { + if ($file->getError() !== UPLOAD_ERR_OK) { + continue; + } + try { + $inmobiliaria = $inmobiliariaRepository->fetchById($body['sociedad_rut'][$i]); + $banco = $bancoRepository->fetchById($body['banco_id'][$i]); + $movimientos = $cartolaService->process($inmobiliaria, $banco, new DateTimeImmutable($body['mes'][$i]), $file); + $cuenta = $cuentaRepository->fetchByInmobiliariaAndBanco($inmobiliaria->rut, $banco->id); + $this->addMovimientos($movimientoService, $cuenta, $movimientos); + $output['movimientos'] = array_merge($output['movimientos'], $movimientos); + } catch (EmptyResult) {} + } + } else { + try { + $inmobiliaria = $inmobiliariaRepository->fetchById($body['sociedad_rut']); + $banco = $bancoRepository->fetchById($body['banco_id']); + $movimientos = $cartolaService->process($inmobiliaria, $banco, new DateTimeImmutable($body['mes']), $files['file']); + $cuenta = $cuentaRepository->fetchByInmobiliariaAndBanco($inmobiliaria->rut, $banco->id); + $this->addMovimientos($movimientoService, $cuenta, $movimientos); + $output['movimientos'] = $movimientos; + } catch (EmptyResult) {} + } + return $this->withJson($response, $output); + } + + protected function addMovimientos(Service\Contabilidad\Movimiento $movimientoService, Inmobiliaria\Cuenta $cuenta, array $movimientos): void + { + foreach ($movimientos as $dataMovimiento) { + $dataMovimiento['cuenta_id'] = $cuenta->id; + $dataMovimiento['centro_costo_id'] = $dataMovimiento['centro_costo']; + $dataMovimiento['fecha'] = new DateTimeImmutable($dataMovimiento['fecha']); + if (array_key_exists('rut', $dataMovimiento)) { + list($rut, $digito) = explode('-', $dataMovimiento['rut']); + $dataMovimiento['rut'] = preg_replace('/\D+/', '', $rut); + $dataMovimiento['digito'] = $digito; + } + $movimientoService->add($dataMovimiento); + } + } } diff --git a/app/src/Controller/API/Contabilidad/Movimientos.php b/app/src/Controller/API/Contabilidad/Movimientos.php index 3daeb2d..c5030cb 100644 --- a/app/src/Controller/API/Contabilidad/Movimientos.php +++ b/app/src/Controller/API/Contabilidad/Movimientos.php @@ -23,29 +23,30 @@ class Movimientos extends Ideal\Controller 'movimiento_id' => $movimiento_id, 'input' => $body, 'status' => false, - 'movimiento' => null, - 'centro' => null, - 'detalle' => '' + 'movimiento' => null ]; try { $movimiento = $movimientoService->getById($movimiento_id); $output['movimiento'] = $this->movimientosToArray([$movimiento])[0]; $data = []; - if (isset($body['centro_id'])) { - $centro = $centroCostoRepository->fetchById($body['centro_id']); - $data['centro_costo_id'] = $centro->id; - } - if (isset($body['detalle'])) { - $data['detalle'] = $body['detalle']; + $fieldMap = [ + 'centro_costo_id', + 'categoria', + 'detalle', + 'rut', + 'digito', + 'nombres', + 'identificador' + ]; + foreach ($fieldMap as $field) { + if (key_exists($field, $body)) { + $data[$field] = $body[$field]; + } } + $movimientoService->setDetalles($movimiento, $data); $output['movimiento'] = $this->movimientosToArray([$movimientoService->getById($movimiento->id)])[0]; - if (isset($body['centro_id'])) { - $output['centro'] = $centro; - } - if (isset($body['detalle'])) { - $output['detalle'] = $body['detalle']; - } + $output['status'] = true; } catch (EmptyResult) {} return $this->withJson($response, $output); } diff --git a/app/src/Controller/API/Contabilidad/Tesoreria.php b/app/src/Controller/API/Contabilidad/Tesoreria.php new file mode 100644 index 0000000..651cd2f --- /dev/null +++ b/app/src/Controller/API/Contabilidad/Tesoreria.php @@ -0,0 +1,29 @@ +getUploadedFiles(); + $output = []; + foreach ($files['file'] as $file) { + if ($file->getError() !== UPLOAD_ERR_OK) { + continue; + } + $data = $tesoreriaService->getInput()->loadFromExcel($file); + $tesoreriaService->getInput()->load($data); + $output['informes'] []= $data; + } + return $this->withJson($response, $output); + } +} diff --git a/app/src/Controller/Contabilidad.php b/app/src/Controller/Contabilidad.php index 2cd4e1c..9b4160c 100644 --- a/app/src/Controller/Contabilidad.php +++ b/app/src/Controller/Contabilidad.php @@ -34,6 +34,20 @@ class Contabilidad extends Controller $centrosCostos = $centroCostoRepository->fetchAll(); return $view->render($response, 'contabilidad.cartolas.diaria', compact('inmobiliarias', 'centrosCostos')); } + public function importar(ServerRequestInterface $request, ResponseInterface $response, View $view, + Repository\Inmobiliaria $inmobiliariaRepository, + Repository\Contabilidad\Banco $bancoRepository): ResponseInterface + { + $inmobiliarias = []; + try { + $inmobiliarias = $inmobiliariaRepository->fetchAll('razon'); + } catch (EmptyResult) {} + $bancos = []; + try { + $bancos = $bancoRepository->fetchAll('nombre'); + } catch (EmptyResult) {} + return $view->render($response, 'contabilidad.cartolas.import', compact('inmobiliarias', 'bancos')); + } public function depositos(ServerRequestInterface $request, ResponseInterface $response, View $view, Service\Redis $redisService, Repository\Inmobiliaria $inmobiliariaRepository, @@ -68,7 +82,7 @@ class Contabilidad extends Controller string $fecha = 'today'): ResponseInterface { $fecha = new DateTimeImmutable($fecha); - $anterior = $contabilidadService->getAnterior($fecha); + $anterior = $contabilidadService->getOutput()->getAnterior($fecha); $yesterday = new DateTimeImmutable('yesterday'); $siguiente = null; if ($yesterday > $fecha) { @@ -77,16 +91,16 @@ class Contabilidad extends Controller $siguiente = $fecha->add(new DateInterval('P3D')); } } - $informes = $contabilidadService->build($fecha); + $informes = $contabilidadService->getOutput()->build($fecha); $filename = "Informe de Tesorería {$fecha->format('d.m.Y')}"; return $view->render($response, 'contabilidad.informes.tesoreria', compact('fecha', 'anterior', 'siguiente', 'informes', 'filename')); } - public function semanal(ServerRequestInterface $request, ResponseInterface $response, View $view, + /*public function semanal(ServerRequestInterface $request, ResponseInterface $response, View $view, Service\Contabilidad\Informe\Semanal $semanalService, string $fecha = 'today'): ResponseInterface { - } + }*/ public function cuadratura(ServerRequestInterface $request, ResponseInterface $response, View $view, Repository\Inmobiliaria $inmobiliariaRepository): ResponseInterface { diff --git a/app/src/Controller/Contabilidad/Informes.php b/app/src/Controller/Contabilidad/Informes.php index 6f3dc14..203dcf5 100644 --- a/app/src/Controller/Contabilidad/Informes.php +++ b/app/src/Controller/Contabilidad/Informes.php @@ -13,7 +13,7 @@ class Informes extends Ideal\Controller { $fecha = new DateTimeImmutable($fecha); - $tesoreriaService->buildInforme($fecha, $tesoreriaService->build($fecha)); + $tesoreriaService->getOutput()->buildInforme($fecha, $tesoreriaService->getOutput()->build($fecha)); $response->getBody()->write(file_get_contents('php://output')); return $response; } diff --git a/app/src/Controller/Contabilidad/Tesoreria.php b/app/src/Controller/Contabilidad/Tesoreria.php new file mode 100644 index 0000000..fe3924a --- /dev/null +++ b/app/src/Controller/Contabilidad/Tesoreria.php @@ -0,0 +1,14 @@ +render($response, 'contabilidad.tesoreria.import'); + } +} diff --git a/app/src/Model/Contabilidad/Movimiento.php b/app/src/Model/Contabilidad/Movimiento.php index 0ae380b..1f4adbf 100644 --- a/app/src/Model/Contabilidad/Movimiento.php +++ b/app/src/Model/Contabilidad/Movimiento.php @@ -26,14 +26,14 @@ class Movimiento extends Ideal\Model return $this->detalles; } - protected ?array $auxiliares; + /*protected ?array $auxiliares; public function getAuxiliares(): ?array { if (!isset($this->auxiliares)) { $this->auxiliares = $this->runFactory('auxiliares'); } return $this->auxiliares; - } + }*/ public function jsonSerialize(): mixed { @@ -46,7 +46,7 @@ class Movimiento extends Ideal\Model 'abono' => $this->abono, 'saldo' => $this->saldo, 'detalles' => $this->getDetalles(), - 'auxiliares' => $this->getAuxiliares() + //'auxiliares' => $this->getAuxiliares() ]); } } diff --git a/app/src/Model/Contabilidad/Movimiento/Detalle.php b/app/src/Model/Contabilidad/Movimiento/Detalle.php index 0afa4c3..8fabd68 100644 --- a/app/src/Model/Contabilidad/Movimiento/Detalle.php +++ b/app/src/Model/Contabilidad/Movimiento/Detalle.php @@ -10,9 +10,10 @@ class Detalle extends Ideal\Model public ?Model\Contabilidad\CentroCosto $centroCosto; public ?int $rut; public ?string $digito; - public ?string $nombre; + public ?string $nombres; public ?string $categoria; public ?string $detalle; + public ?string $identificador; public function rut(): string { @@ -30,9 +31,10 @@ class Detalle extends Ideal\Model 'centro_costo' => $this->centroCosto, 'rut' => $this->rut, 'digito' => $this->digito, - 'nombre' => $this->nombre, + 'nombres' => $this->nombres, 'categoria' => $this->categoria, 'detalle' => $this->detalle, + 'identificador' => $this->identificador, 'rutFormatted' => $this->rutFormatted() ]; } diff --git a/app/src/Repository/Contabilidad/Banco.php b/app/src/Repository/Contabilidad/Banco.php index af6c350..111732a 100644 --- a/app/src/Repository/Contabilidad/Banco.php +++ b/app/src/Repository/Contabilidad/Banco.php @@ -31,4 +31,13 @@ class Banco extends Ideal\Repository { return $this->update($model, ['nombre'], $new_data); } + + public function fetchByNombre(string $nombre): Model\Contabilidad\Banco + { + $query = $this->connection->getQueryBuilder() + ->select() + ->from($this->getTable()) + ->where('nombre = :nombre'); + return $this->fetchOne($query, ['nombre' => $nombre]); + } } diff --git a/app/src/Repository/Contabilidad/Movimiento.php b/app/src/Repository/Contabilidad/Movimiento.php index 4f36d3c..d45d8ca 100644 --- a/app/src/Repository/Contabilidad/Movimiento.php +++ b/app/src/Repository/Contabilidad/Movimiento.php @@ -62,13 +62,13 @@ class Movimiento extends Ideal\Repository ->where('cuenta_id = ? AND fecha = ?'); return $this->fetchMany($query, [$cuenta_id, $fecha->format('Y-m-d')]); } - public function fetchByCuentaAndFechaAndCargoAndAbonoAndSaldo(int $cuenta_id, DateTimeInterface $fecha, int $cargo, int $abono, int $saldo): Model\Contabilidad\Movimiento + public function fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo(int $cuenta_id, DateTimeInterface $fecha, string $glosa, int $cargo, int $abono, int $saldo): Model\Contabilidad\Movimiento { $query = $this->connection->getQueryBuilder() ->select() ->from($this->getTable()) - ->where('cuenta_id = ? AND fecha = ? AND cargo = ? AND abono = ? AND saldo = ?'); - return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d'), $cargo, $abono, $saldo]); + ->where('cuenta_id = ? AND fecha = ? AND glosa = ? AND cargo = ? AND abono = ? AND saldo = ?'); + return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d'), $glosa, $cargo, $abono, $saldo]); } public function fetchAmountStartingFrom(int $start, int $amount): array { diff --git a/app/src/Repository/Contabilidad/Movimiento/Detalle.php b/app/src/Repository/Contabilidad/Movimiento/Detalle.php index b1ef0da..ba526c2 100644 --- a/app/src/Repository/Contabilidad/Movimiento/Detalle.php +++ b/app/src/Repository/Contabilidad/Movimiento/Detalle.php @@ -19,7 +19,7 @@ class Detalle extends Ideal\Repository public function create(?array $data = null): Model\Contabilidad\Movimiento\Detalle { - $map = (new Implement\Repository\MapperParser(['detalle'])) + $map = (new Implement\Repository\MapperParser(['rut', 'digito', 'nombres', 'categoria', 'detalle', 'identificador'])) ->register('movimiento_id', (new Implement\Repository\Mapper()) ->setProperty('movimiento') ->setFunction(function(array $data) { @@ -29,20 +29,21 @@ class Detalle extends Ideal\Repository ->setProperty('centroCosto') ->setFunction(function(array $data) { return $this->centroCostoRepository->fetchById($data['centro_costo_id']); - })); + }) + ->setDefault(null)); return $this->parseData(new Model\Contabilidad\Movimiento\Detalle(), $data, $map); } public function save(Define\Model $model): Model\Contabilidad\Movimiento\Detalle { $this->saveNew( - ['movimiento_id', 'centro_costo_id', 'detalle'], - [$model->movimiento->id, $model->centroCosto->id, $model->detalles] + ['movimiento_id', 'centro_costo_id', 'rut', 'digito', 'nombres', 'categoria', 'detalle', 'identificador'], + [$model->movimiento->id, $model->centroCosto?->id, $model->rut, $model->digito, $model->nombres, $model->categoria, $model->detalle, $model->identificador] ); return $model; } public function edit(Define\Model $model, array $new_data): Model\Contabilidad\Movimiento\Detalle { - return $this->update($model, ['movimiento_id', 'centro_costo_id', 'detalle'], $new_data); + return $this->update($model, ['movimiento_id', 'centro_costo_id', 'rut', 'digito', 'nombres', 'categoria', 'detalle', 'identificador'], $new_data); } public function fetchByMovimiento(int $movimiento_id): Model\Contabilidad\Movimiento\Detalle diff --git a/app/src/Service/Contabilidad.php b/app/src/Service/Contabilidad.php index a5e87fd..692fe72 100644 --- a/app/src/Service/Contabilidad.php +++ b/app/src/Service/Contabilidad.php @@ -15,6 +15,6 @@ class Contabilidad extends Ideal\Controller public function tesoreria(DateTimeInterface $fecha): array { - return $this->tesoreriaService->build($fecha); + return $this->tesoreriaService->getOutput()->build($fecha); } } diff --git a/app/src/Service/Contabilidad/Cartola.php b/app/src/Service/Contabilidad/Cartola.php index 8b3cb76..cd19242 100644 --- a/app/src/Service/Contabilidad/Cartola.php +++ b/app/src/Service/Contabilidad/Cartola.php @@ -121,9 +121,10 @@ class Cartola extends Service { try { return $this->movimientoRepository - ->fetchByCuentaAndFechaAndCargoAndAbonoAndSaldo( + ->fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo( $cuenta->id, new DateTimeImmutable($data['fecha']), + $data['glosa'], $data['cargo'] ?? 0, $data['abono'] ?? 0, $data['saldo'] diff --git a/app/src/Service/Contabilidad/Cartola/BCI.php b/app/src/Service/Contabilidad/Cartola/BCI.php index dc3b8ab..54ee427 100644 --- a/app/src/Service/Contabilidad/Cartola/BCI.php +++ b/app/src/Service/Contabilidad/Cartola/BCI.php @@ -14,7 +14,13 @@ class BCI extends Banco 'Cargo $ (-)' => 'cargo', 'Abono $ (+)' => 'abono', 'Descripción' => 'glosa', - 'Saldo' => 'saldo' + 'Saldo' => 'saldo', + 'Categoría' => 'categoria', + 'Centro costos' => 'centro_costo', + 'Detalle' => 'detalle', + 'Factura Boleta' => 'identificador', + 'RUT' => 'rut', + 'Nombres' => 'nombres', ]; } protected function getFilename(UploadedFileInterface $uploadedFile): string diff --git a/app/src/Service/Contabilidad/Cartola/Itau.php b/app/src/Service/Contabilidad/Cartola/Itau.php index 4639ad5..17bc111 100644 --- a/app/src/Service/Contabilidad/Cartola/Itau.php +++ b/app/src/Service/Contabilidad/Cartola/Itau.php @@ -26,7 +26,14 @@ class Itau extends Banco 'Giros o cargos' => 'cargo', 'Documentos' => 'documento', 'Movimientos' => 'glosa', - 'Saldos' => 'saldo' + 'Saldos' => 'saldo', + 'Categoría' => 'categoria', + 'Centro costo' => 'centro_costo', + 'Detalle' => 'detalle', + 'Factura Boleta' => 'identificador', + 'RUT' => 'rut', + 'Nombres' => 'nombres', + 'Depto' => 'identificador', ]; } protected function getFilename(UploadedFileInterface $uploadedFile): string diff --git a/app/src/Service/Contabilidad/Cartola/Santander.php b/app/src/Service/Contabilidad/Cartola/Santander.php index 8b6c7ab..d2f6d8b 100644 --- a/app/src/Service/Contabilidad/Cartola/Santander.php +++ b/app/src/Service/Contabilidad/Cartola/Santander.php @@ -20,7 +20,13 @@ class Santander extends Banco 'Cargo ($)' => 'cargo', 'Abono ($)' => 'abono', 'Descripcin' => 'glosa', - 'Saldo Diario' => 'saldo' + 'Saldo Diario' => 'saldo', + 'Categoría' => 'categoria', + 'Centro costos' => 'centro_costo', + 'Detalle' => 'detalle', + 'Factura Boleta' => 'identificador', + 'RUT' => 'rut', + 'Nombres' => 'nombres', ]; } protected function getFilename(UploadedFileInterface $uploadedFile): string diff --git a/app/src/Service/Contabilidad/Cartola/Security.php b/app/src/Service/Contabilidad/Cartola/Security.php index f2798b4..c36c9a9 100644 --- a/app/src/Service/Contabilidad/Cartola/Security.php +++ b/app/src/Service/Contabilidad/Cartola/Security.php @@ -41,7 +41,14 @@ class Security extends Banco 'nº documento' => 'documento', 'cargos' => 'cargo', 'abonos' => 'abono', - 'saldos' => 'saldo' + 'saldos' => 'saldo', + 'categoría' => 'categoria', + 'centro costos' => 'centro_costo', + 'detalle' => 'detalle', + 'factura boleta' => 'identificador', + 'rut' => 'rut', + 'nombres' => 'nombres', + 'depto' => 'identificador', ]; } diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria.php b/app/src/Service/Contabilidad/Informe/Tesoreria.php index 5bc8e56..937ae59 100644 --- a/app/src/Service/Contabilidad/Informe/Tesoreria.php +++ b/app/src/Service/Contabilidad/Informe/Tesoreria.php @@ -1,378 +1,23 @@ movimientos = new class(self::ORDEN_SOCIEDADES) { - public function __construct(protected array $ordenSociedades) - { - $this->dap = new class() - { - public array $ingresos = []; - public array $egresos = []; - }; - } - public object $dap; - public array $ingresos = []; - public array $egresos = []; - - const INGRESOS = 'ingresos'; - const EGRESOS = 'egresos'; - public function addDap(string $tipo, array $movimientos) - { - foreach ($movimientos as $movimiento) { - $this->dap->{$tipo} []= $movimiento; - } - return $this; - } - public function updateDap(object $movimiento): void - { - foreach ($this->ingresos as $ingreso) { - if ($movimiento->cuenta->inmobiliaria->rut !== $ingreso->cuenta->inmobiliaria->rut) { - continue; - } - if ($movimiento->fecha->format('Y-m-d') !== $ingreso->fecha->format('Y-m-d')) { - continue; - } - if ($movimiento->documento !== $ingreso->documento) { - continue; - } - $ingreso->glosa = $movimiento->glosa; - break; - } - } - public function build(): array - { - $this->dap->ingresos = $this->sortBySociedades($this->dap->ingresos); - $this->dap->egresos = $this->sortBySociedades($this->dap->egresos); - $this->ingresos = $this->sortBySociedades($this->ingresos); - $this->egresos = $this->sortBySociedades($this->egresos); - return [ - 'capital dap' => [ - 'ingresos' => $this->dap->ingresos, - 'egresos' => $this->dap->egresos - ], - 'ingresos' => $this->ingresos, - 'egresos' => $this->egresos - ]; - } - - private function sortBySociedades(array $movimientos): array - { - $temp = []; - foreach ($this->ordenSociedades as $sociedad_rut) { - $date = null; - foreach ($movimientos as $movimiento) { - if ($date === null) { - $date = $movimiento->fecha; - } - if ($movimiento->fecha !== $date) { - if ($movimiento->cuenta->inmobiliaria->rut === $sociedad_rut) { - $temp []= $movimiento; - } - $date = $movimiento->fecha; - continue; - } - if ($movimiento->cuenta->inmobiliaria->rut === $sociedad_rut) { - $temp []= $movimiento; - } - } - } - foreach ($movimientos as $movimiento) { - if (!in_array($movimiento, $temp)) { - $temp []= $movimiento; - } - } - return $temp; - } - }; - $this->totales = new class() { - public int $anterior = 0; - public int $actual = 0; - public int $ffmm = 0; - public int $deposito = 0; - public int $saldo = 0; - - public function diferencia(): int - { - return $this->actual - $this->anterior; - } - public function saldo(): int - { - return $this->diferencia() + $this->ffmm + $this->deposito; - } - public function cuentas(): int - { - return $this->actual; - } - public function ffmms(): int - { - return $this->ffmm; - } - public function depositos(): int - { - return $this->deposito; - } - public function caja(): int - { - return $this->cuentas() + $this->ffmms() + $this->depositos(); - } - }; } - - const DAP_INGRESOS = 'dap->ingresos'; - const DAP_EGRESOS = 'dap->egresos'; - const INGRESOS = 'ingresos'; - const EGRESOS = 'egresos'; - const TOTAL_ANTERIOR = 'anterior'; - const TOTAL_ACTUAL = 'actual'; - const TOTAL_FFMM = 'ffmm'; - const TOTAL_DAP = 'deposito'; - - protected DateTimeInterface $anterior; - protected object $totales; - protected object $movimientos; - - public function getAnterior(DateTimeInterface $fecha): DateTimeInterface + public function getOutput(): Tesoreria\Output { - if (!isset($this->anterior)) { - $this->anterior = $fecha->sub(new DateInterval('P1D')); - if ($this->anterior->format('N') === '7') { - $this->anterior = $fecha->sub(new DateInterval('P3D')); - } - } - return $this->anterior; + return $this->outputService; } - public function build(DateTimeInterface $fecha): array + public function getInput(): Tesoreria\Input { - try { - $inmobiliarias = $this->inmobiliariaRepository->fetchAll(); - } catch (Implement\Exception\EmptyResult) { - return []; - } - $temp = []; - foreach (self::ORDEN_SOCIEDADES as $sociedad_rut) { - foreach ($inmobiliarias as $inmobiliaria) { - if ($inmobiliaria->rut === $sociedad_rut) { - $temp []= $inmobiliaria; - } - } - } - foreach ($inmobiliarias as $inmobiliaria) { - if (!in_array($inmobiliaria, $temp)) { - $temp []= $inmobiliaria; - } - } - $informe = ['inmobiliarias' => []]; - foreach ($temp as $inmobiliaria) { - $informe['inmobiliarias'][$inmobiliaria->rut] = $this->buildInmobiliaria($inmobiliaria, $fecha); - } - $informe['movimientos'] = $this->buildMovimientos(); - $informe['totales'] = $this->buildTotales(); - - //$this->buildInforme($fecha, $informe); - - return $informe; - } - public function buildInforme(DateTimeInterface $fecha, array $data, string $type = 'Xlsx', ?string $filename = 'php://output'): void - { - $informe = $this->excelService->build($fecha, $data); - $this->excelService->save($fecha, $informe, $type, $filename); - } - - protected function buildInmobiliaria(Model\Inmobiliaria $inmobiliaria, DateTimeInterface $fecha): object - { - $dataInmobiliaria = new class() { - public Model\Inmobiliaria $inmobiliaria; - public array $cuentas = []; - public function total(): int - { - return array_reduce($this->cuentas, function(int $sum, $cuenta) { - return $sum + $cuenta->actual; - }, 0); - } - public function ffmm(): int - { - return array_reduce($this->cuentas, function(int $sum, $cuenta) { - return $sum + $cuenta->ffmm; - }, 0); - } - public function deposito(): int - { - return array_reduce($this->cuentas, function(int $sum, $cuenta) { - return $sum + $cuenta->deposito; - }, 0); - } - public function caja(): int - { - return array_reduce($this->cuentas, function(int $sum, $cuenta) { - return $sum + $cuenta->saldo(); - }, 0); - } - }; - $dataInmobiliaria->inmobiliaria = $inmobiliaria; - try { - $cuentas = $this->cuentaService->getAllActiveByInmobiliaria($inmobiliaria->rut); - } catch (Implement\Exception\EmptyResult) { - return $dataInmobiliaria; - } - foreach ($cuentas as $cuenta) { - $data = new class() { - public string $banco; - public string $numero; - public int $anterior = 0; - public int $actual = 0; - public int $ffmm = 0; - public int $deposito = 0; - - public function diferencia(): int - { - return $this->actual - $this->anterior; - } - public function saldo(): int - { - return $this->diferencia() + $this->ffmm + $this->deposito; - } - }; - $data->banco = $cuenta->banco->nombre; - $data->numero = $cuenta->cuenta; - try { - $depositos = $this->depositoRepository->fetchByCuenta($cuenta->id); - foreach ($depositos as $deposito) { - if ($deposito->termino < $fecha) { - continue; - } - $data->deposito += $deposito->capital; - $this->addTotal(self::TOTAL_DAP, $deposito->capital); - $this->totales->saldo += $deposito->capital; - - if ($deposito->inicio->format('Y-m-d') === $fecha->format('Y-m-d')) { - $this->addMovimientos(self::DAP_EGRESOS, [(object) [ - 'cuenta' => $deposito->cuenta, - 'fecha' => $deposito->inicio, - 'cargo' => - $deposito->capital, - 'abono' => 0, - 'saldo' => - $deposito->capital, - 'glosa' => 'INVERSION DAP' - ]]); - } - if ($deposito->termino->format('Y-m-d') === $fecha->format('Y-m-d')) { - $data->deposito -= $deposito->capital; - $this->addTotal(self::TOTAL_DAP, -$deposito->capital); - - $this->addMovimientos(self::INGRESOS, [(object) [ - 'cuenta' => $deposito->cuenta, - 'fecha' => $deposito->termino, - 'cargo' => 0, - 'abono' => $deposito->futuro - $deposito->capital, - 'saldo' => $deposito->futuro - $deposito->capital, - 'glosa' => 'RESCATE DAP', - 'documento' => $deposito->id - ]]); - } - } - } catch (Implement\Exception\EmptyResult) {} - $anterior = $this->getAnterior($fecha); - try { - $cartola = $this->cartolaRepository->fetchLastByCuentaAndFecha($cuenta->id, $fecha); - $data->actual = $cartola->saldo; - //$anterior = $this->getAnterior($cartola->fecha); - } catch (Implement\Exception\EmptyResult) {} - try { - $cartola = $this->cartolaRepository->fetchLastByCuentaAndFecha($cuenta->id, $anterior); - $data->anterior = $cartola->saldo; - $this->totales->saldo += $cartola->saldo; - } catch (Implement\Exception\EmptyResult) {} - if ($data->diferencia() !== 0) { - try { - $movimientos = $this->movimientoRepository->fetchByCuentaAndFecha($cuenta->id, $fecha); - $this->addMovimientos(self::INGRESOS, - array_filter($movimientos, function(Model\Contabilidad\Movimiento $movimiento) { - return $movimiento->abono > 0; - })); - $this->addMovimientos(self::EGRESOS, - array_filter($movimientos, function(Model\Contabilidad\Movimiento $movimiento) { - return $movimiento->cargo > 0; - })); - } catch (Implement\Exception\EmptyResult) {} - } - $dataInmobiliaria->cuentas []= $data; - - $this->addTotal( - [self::TOTAL_ANTERIOR, self::TOTAL_ACTUAL], - [$data->anterior, $data->actual] - ); - } - - return $dataInmobiliaria; - } - protected function buildMovimientos(): array - { - return $this->movimientos->build(); - } - protected function buildTotales(): object - { - return $this->totales; - } - protected function addMovimientos(string $tipo, array $movimientos): Tesoreria - { - if (str_starts_with($tipo, 'dap')) { - list($d, $t) = explode('->', $tipo); - $this->movimientos->addDap($t, $movimientos); - return $this; - } - foreach ($movimientos as $movimiento) { - if ($tipo === 'ingresos' and str_contains(strtolower($movimiento->glosa), ' dap ')) { - $this->movimientos->updateDap($movimiento); - continue; - } - $this->movimientos->{$tipo} []= $movimiento; - } - return $this; - } - protected function addTotal(string|array $tipo, int|array $total): Tesoreria - { - if (is_array($tipo)) { - foreach ($tipo as $i => $t) { - $this->addTotal($t, $total[$i]); - } - return $this; - } - $this->totales->{$tipo} += $total; - return $this; + return $this->inputService; } } diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input.php new file mode 100644 index 0000000..4b65e1f --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input.php @@ -0,0 +1,36 @@ +getSize() === 0) { + return []; + } + $tmpFile = '/tmp/' . $uploadedFile->getClientFilename(); + $uploadedFile->moveTo($tmpFile); + $data = $this->excelService->load($tmpFile); + unlink($tmpFile); + + return $data; + } + + public function load(array $data): void + { + $this->saldosContablesService->load($data['saldosContables']); + $this->dapYFFMMService->load($data['dapyffmm']); + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/DAPyFFMM.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/DAPyFFMM.php new file mode 100644 index 0000000..fb5b57c --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/DAPyFFMM.php @@ -0,0 +1,12 @@ +logger->error('Cargando DAP y FFMM', $data); + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/SaldosContables.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/SaldosContables.php new file mode 100644 index 0000000..12f1b61 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Data/SaldosContables.php @@ -0,0 +1,152 @@ +data = $data['movimientos']; + foreach ($data['movimientos'] as $type => $dataMovimiento) { + switch ($type) { + case 'total': + $this->saldoInicial = $dataMovimiento; + break; + case 'depositos': + $this->loadDepositos($dataMovimiento); + break; + case 'egresos': + case 'ingresos': + $this->loadMovimiento($dataMovimiento); + break; + } + } + } + + protected array $sociedades; + protected int $saldoInicial; + protected array $data; + + protected function loadDepositos(array $data): void + { + $this->logger->error('Cargando depositos', $data); + } + protected function loadMovimiento(array $data): void + { + $sociedades = $this->getSociedades(); + $unmatched = []; + foreach ($data as $dataMovimiento) { + try { + $sociedad_rut = $this->matchSociedad($sociedades, $dataMovimiento->empresa); + $banco = $this->bancoRepository->fetchByNombre($dataMovimiento->banco); + $cuenta = $this->cuentaRepository->fetchByInmobiliariaAndBanco($sociedad_rut, $banco->id); + $data = [ + 'cuenta_id' => $cuenta->id, + 'fecha' => $dataMovimiento->fecha->format('Y-m-d'), + 'glosa' => mb_convert_encoding($dataMovimiento->{'descripción'}, 'UTF-8'), + 'documento' => $dataMovimiento->{'n°Doc'}, + 'cargo' => -$dataMovimiento->egresos, + 'abono' => $dataMovimiento->ingresos, + 'saldo' => $this->getSaldo($dataMovimiento) + ]; + try { + $movimiento = $this->movimientoRepository->fetchByCuentaAndFechaAndCargoAndAbonoAndSaldo($cuenta->id, $dataMovimiento->fecha, $data['cargo'], $data['abono'], $data['saldo']); + } catch (Implement\Exception\EmptyResult) { + $movimiento = $this->movimientoRepository->create($data); + $movimiento = $this->movimientoRepository->save($movimiento); + } + list($rut, $digito) = explode('-', $dataMovimiento->rut); + $data = [ + 'movimiento_id' => $movimiento->id, + 'centro_costo_id' => $dataMovimiento->cc, + 'rut' => (int) preg_replace('/\D*/', '', $rut), + 'digito' => $digito, + 'nombre' => $dataMovimiento->nombres, + 'categoria' => $dataMovimiento->categoria, + 'detalle' => $dataMovimiento->detalle, + ]; + if ($data['centro_costo_id'] === 0 and ($data['rut'] === 0 or $data['nombre'] === '' or $data['categoria'] === '' or $data['detalle'] === '')) { + continue; + } + try { + $detalles = $this->detalleRepository->fetchByMovimiento($movimiento->id); + $this->detalleRepository->edit($detalles, $data); + } catch (Implement\Exception\EmptyResult) { + $detalles = $this->detalleRepository->create($data); + $this->detalleRepository->save($detalles); + } + } catch (Exception $exception) { + $this->logger->error($exception); + $unmatched []= $dataMovimiento; + } + } + + if (count($unmatched) > 0) { + $this->logger->error('Movimientos no asociados', $unmatched); + } + } + + protected function getSociedades(): array + { + if (!isset($this->sociedades)) { + $this->sociedades = []; + try { + $this->sociedades = $this->inmobiliariaRepository->fetchAll(); + } catch (Implement\Exception\EmptyResult $e) { + $this->logger->error($e->getMessage()); + } + } + return $this->sociedades; + } + protected function getSaldo($movimiento): int + { + $saldo = $this->saldoInicial; + foreach ($this->data as $type => $dataMovimientos) { + if (in_array($type, ['total', 'depositos'])) { + continue; + } + foreach ($dataMovimientos as $dataMovimiento) { + if ($dataMovimiento !== $movimiento) { + continue; + } + $saldo += $dataMovimiento->ingresos + $dataMovimiento->egresos; + } + } + return $saldo; + } + protected function matchSociedad(array $sociedades, string $name): int + { + foreach ($sociedades as $sociedad) { + $abreviacion = $sociedad->abreviacion; + if ($abreviacion === 'Incoviba') { + if (preg_match('/(Vial Balmaceda)+/', $name) === 1) { + return $sociedad->rut; + } + continue; + } + if (str_contains($abreviacion, 'Optimus')) { + $abreviacion = 'Óptimus'; + } + if (preg_match("/({$abreviacion})+/", $name) === 1) { + return $sociedad->rut; + } + } + + throw new Exception("No se encontró la sociedad asociada al nombre '{$name}'"); + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel.php new file mode 100644 index 0000000..e9262d2 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel.php @@ -0,0 +1,35 @@ +logger->error($e->getMessage()); + return []; + } + $excel = $reader->load($filename); + + $data = []; + $data['saldosContables'] = $this->saldosCuentasService->load($excel); + $data['dapyffmm'] = $this->dapyFFMMService->load($excel); + $data['deudaBanco'] = $this->deudaBancoService->load($excel); + + return $data; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DAPyFFMM.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DAPyFFMM.php new file mode 100644 index 0000000..c6623a0 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DAPyFFMM.php @@ -0,0 +1,51 @@ +sheetNameExists('DAP y FFMM')) { + return []; + } + $sheet = $excel->getSheetByName('DAP y FFMM'); + $data = []; + + $rowIndex = 1; + while ($sheet->getCell("B{$rowIndex}")->getValue() !== 'EMPRESA') { + $rowIndex ++; + } + $titleRow = $rowIndex; + $columnOffset = 2; + $titles = $this->getTableTitles($sheet, $titleRow, $columnOffset, 20, 'UF', ['(']); + + $rowIndex ++; + while ($sheet->getCell("B{$rowIndex}")->getValue() !== 'TOTAL') { + if ($sheet->getCell("B{$rowIndex}")->getValue() !== null) { + $data []= $this->getRow($sheet, $titles, $titleRow, $rowIndex, $columnOffset); + /* + $dap = []; + $columnIndex = 0; + $dap[$titles[$columnIndex ++]] = $sheet->getCell("B{$rowIndex}")->getValue(); + $dap[$titles[$columnIndex ++]] = $sheet->getCell("D{$rowIndex}")->getValue(); + $dap[$titles[$columnIndex ++]] = $sheet->getCell("F{$rowIndex}")->getValue(); + $dap[$titles[$columnIndex ++]] = $this->getCalculatedValue($sheet, 'H', $rowIndex); + $dap[$titles[$columnIndex ++]] = $this->getCalculatedValue($sheet, 'I', $rowIndex); + $dap[$titles[$columnIndex ++]] = $this->getCalculatedValue($sheet, 'J', $rowIndex); + $dap[$titles[$columnIndex ++]] = $this->getCalculatedValue($sheet, 'K', $rowIndex); + $dap[$titles[$columnIndex ++]] = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($this->getCalculatedValue($sheet, 'L', $rowIndex)); + $dap[$titles[$columnIndex ++]] = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($this->getCalculatedValue($sheet, 'M', $rowIndex)); + $dap[$titles[$columnIndex ++]] = $this->getCalculatedValue($sheet, 'N', $rowIndex); + $dap[$titles[$columnIndex]] = $this->getCalculatedValue($sheet, 'O', $rowIndex); + + $data []= (object) $dap;*/ + } + $rowIndex ++; + } + + return $data; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DeudaBanco.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DeudaBanco.php new file mode 100644 index 0000000..764b2a3 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/DeudaBanco.php @@ -0,0 +1,34 @@ +sheetNameExists('Deuda Banco')) { + return []; + } + $sheet = $excel->getSheetByName('Deuda Banco'); + $data = []; + + $rowIndex = 1; + $columnOffset = 2; + while ($sheet->getCell([$columnOffset, $rowIndex])->getValue() !== 'EMPRESA') { + $rowIndex ++; + } + $titleRow = $rowIndex; + $titles = $this->getTableTitles($sheet, $titleRow, $columnOffset, 13, 'UF', ['(']); + $rowIndex ++; + + while ($sheet->getCell([$columnOffset, $rowIndex])->getValue() !== 'TOTAL GRUPO EMPRESAS') { + if ($sheet->getCell([$columnOffset, $rowIndex])->getValue() !== null) { + $data []= $this->getRow($sheet, $titles, $titleRow, $rowIndex, $columnOffset); + } + $rowIndex ++; + } + + return $data; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/SaldosCuentas.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/SaldosCuentas.php new file mode 100644 index 0000000..041ef60 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/SaldosCuentas.php @@ -0,0 +1,109 @@ +sheetNameExists('Saldos cuentas')) { + return []; + } + $sheet = $excel->getSheetByName('Saldos cuentas'); + $data = []; + $data['anterior'] = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($this->getCalculatedValue($sheet, 'C4')); + $data['actual'] = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($this->getCalculatedValue($sheet, 'C5')); + $data['uf'] = $this->getCalculatedValue($sheet, 'F4'); + $data['usd'] = $this->getCalculatedValue($sheet, 'F5'); + + $titleRow = 7; + $data['sociedades'] = $this->getSociedades($sheet, $titleRow); + + $titleRow += count($data['sociedades']); + while ($sheet->getCell("B{$titleRow}")->getValue() !== 'EMPRESA') { + $titleRow ++; + } + $data['movimientos'] = $this->getMovimientos($sheet, $titleRow); + + return $data; + } + + protected function getSociedades(PhpSpreadsheet\Worksheet\Worksheet $sheet, int $titleRow = 7, int $startColumn = 2): array + { + $totalsColumn = $startColumn; + while ($sheet->getCell([$totalsColumn, $titleRow])->getValue() !== null && $sheet->getCell([$totalsColumn, $titleRow])->getValue() !== 'Total Cuentas') { + $totalsColumn ++; + } + $rowIndex = $titleRow + 1; + $sociedades = []; + while ($rowIndex < 100 and $sheet->getCell("B{$rowIndex}")->getValue() !== 'TOTAL') { + $sociedad = new stdClass(); + $sociedad->razon = $sheet->getCell("B{$rowIndex}")->getValue(); + $sociedad->cuentas = []; + $cuentaColumnIndex = 3; + $cuenta = new stdClass(); + while ($cuentaColumnIndex < $totalsColumn) { + $cuenta->banco = $sheet->getCell([$cuentaColumnIndex ++, $rowIndex])->getValue() ?? ''; + $cuenta->numero = $sheet->getCell([$cuentaColumnIndex ++, $rowIndex])->getValue() ?? ''; + $cuenta->anterior = $this->getCalculatedValue($sheet, $cuentaColumnIndex ++, $rowIndex); + $cuenta->actual = $this->getCalculatedValue($sheet, $cuentaColumnIndex ++, $rowIndex); + $cuenta->diferencia = $this->getCalculatedValue($sheet, $cuentaColumnIndex ++, $rowIndex); + $cuenta->ffmm = $this->getCalculatedValue($sheet, $cuentaColumnIndex ++, $rowIndex); + $cuenta->deposito = $this->getCalculatedValue($sheet, $cuentaColumnIndex ++, $rowIndex); + $cuenta->saldo = $this->getCalculatedValue($sheet, $cuentaColumnIndex ++, $rowIndex); + + $sociedad->cuentas []= $cuenta; + + if ($sheet->getCell([$cuentaColumnIndex, $rowIndex])->getValue() === null) { + break; + } + $cuenta = new stdClass(); + } + $sociedades []= $sociedad; + $rowIndex ++; + } + return $sociedades; + } + protected function getMovimientos(PhpSpreadsheet\Worksheet\Worksheet $sheet, int $titleRow, int $startColumn = 2): array + { + $data = []; + $titles = $this->getTableTitles($sheet, $titleRow, $startColumn, 15); + + $totalRow = $titleRow + 2; + $data['total'] = $this->getCalculatedValue($sheet, "V{$totalRow}"); + $rowIndex = $titleRow + 4; + $depositos = []; + while ($sheet->getCell("B{$rowIndex}")->getValue() !== 'INGRESOS') { + if ( $sheet->getCell("B{$rowIndex}")->getValue() !== null) { + $depositos []= $this->getRow($sheet, $titles, $titleRow, $rowIndex, $startColumn); + } + $rowIndex ++; + } + $data['depositos'] = $depositos; + + $rowIndex ++; + $ingresos = []; + while ($sheet->getCell("B{$rowIndex}")->getValue() !== 'EGRESOS') { + if ($sheet->getCell("B{$rowIndex}")->getValue() !== null) { + $ingresos []= $this->getRow($sheet, $titles, $titleRow, $rowIndex, $startColumn); + } + $rowIndex ++; + } + $data['ingresos'] = $ingresos; + + $rowIndex ++; + $egresos = []; + while ($sheet->getCell("B{$rowIndex}")->getValue() !== 'TOTAL') { + if ($sheet->getCell("B{$rowIndex}")->getValue() === null) { + break; + } + $egresos [] = $this->getRow($sheet, $titles, $titleRow, $rowIndex, $startColumn); + $rowIndex ++; + } + $data['egresos'] = $egresos; + + return $data; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/Sheet.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/Sheet.php new file mode 100644 index 0000000..a0293ef --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Input/Excel/Sheet.php @@ -0,0 +1,73 @@ +getCell($columnIndex)->getCalculatedValue() ?? $default; + } + return $sheet->getCell("{$columnIndex}{$rowIndex}")->getCalculatedValue() ?? $default; + } + return $sheet->getCell([$columnIndex, $rowIndex])->getCalculatedValue() ?? $default; + } catch (PhpSpreadsheet\Calculation\Exception $e) { + $this->logger->error($e->getMessage()); + return $default; + } + } + protected function getTableTitles(PhpSpreadsheet\Worksheet\Worksheet $sheet, int $titleRow, int $columnOffset, int $maxColumns, string $duplicateSuffix = '', array $cutoffCharacters = []): array + { + $titles = []; + $columnIndex = $columnOffset; + while ($columnIndex < $maxColumns or $sheet->getCell([$columnIndex, $titleRow])->getValue() !== null) { + if ($sheet->getCell([$columnIndex, $titleRow])->getValue() !== null) { + $title = $this->transformTitle($sheet->getCell([$columnIndex, $titleRow])->getValue(), $cutoffCharacters); + if (in_array($title, $titles)) { + $title .= $duplicateSuffix; + } + $titles []= $title; + } + $columnIndex ++; + } + return $titles; + } + protected function getRow(PhpSpreadsheet\Worksheet\Worksheet $sheet, array $titles, int $titleRow, int $rowIndex, int $columnOffset): object + { + $row = []; + $columnIndex = $columnOffset; + $titleIndex = 0; + $currentTitle = $this->transformTitle($sheet->getCell([$columnIndex, $titleRow])->getValue()); + while ($columnIndex < count($titles) * 3 and $currentTitle !== last($titles)) { + if ($sheet->getCell([$columnIndex, $titleRow])->getValue() === null) { + $columnIndex ++; + continue; + } + if (str_contains($titles[$titleIndex], 'fecha')) { + $row[$titles[$titleIndex ++]] = PhpSpreadsheet\Shared\Date::excelToDateTimeObject($this->getCalculatedValue($sheet, $columnIndex ++, $rowIndex)); + continue; + } + $row[$titles[$titleIndex ++]] = $this->getCalculatedValue($sheet, $columnIndex ++, $rowIndex); + } + + return (object) $row; + } + protected function transformTitle(string $title, array $cutoffCharacters = []): string + { + $title = str_replace(' ', '', ucwords(mb_strtolower($title))); + $title = strtolower(substr($title, 0, 1)) . substr($title, 1); + foreach ($cutoffCharacters as $cutoffCharacter) { + if (str_contains($title, $cutoffCharacter)) { + $title = substr($title, 0, strpos($title, $cutoffCharacter)); + } + } + return $title; + } + + abstract public function load(PhpSpreadsheet\Spreadsheet $excel): array; +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Output.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output.php new file mode 100644 index 0000000..c3568fb --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output.php @@ -0,0 +1,226 @@ +ingresos'; + const DAP_EGRESOS = 'dap->egresos'; + const INGRESOS = 'ingresos'; + const EGRESOS = 'egresos'; + const TOTAL_ANTERIOR = 'anterior'; + const TOTAL_ACTUAL = 'actual'; + const TOTAL_FFMM = 'ffmm'; + const TOTAL_DAP = 'deposito'; + + protected DateTimeInterface $anterior; + protected object $totales; + protected object $movimientos; + + public function __construct(LoggerInterface $logger, + protected Repository\Inmobiliaria $inmobiliariaRepository, + protected Repository\Contabilidad\Deposito $depositoRepository, + protected Repository\Contabilidad\Cartola $cartolaRepository, + protected Repository\Contabilidad\Movimiento $movimientoRepository, + protected Service\Inmobiliaria\Cuenta $cuentaService, + protected Output\Excel $excelService, + protected Output\PDF $pdfService) + { + parent::__construct($logger); + + $this->movimientos = new Output\Data\Movimientos(self::ORDEN_SOCIEDADES); + $this->totales = new Output\Data\Totales(); + } + + public function getAnterior(DateTimeInterface $fecha): DateTimeInterface + { + if (!isset($this->anterior)) { + $this->anterior = $fecha->sub(new DateInterval('P1D')); + if ($this->anterior->format('N') === '7') { + $this->anterior = $fecha->sub(new DateInterval('P3D')); + } + } + return $this->anterior; + } + public function build(DateTimeInterface $fecha): array + { + try { + $inmobiliarias = $this->inmobiliariaRepository->fetchAll(); + } catch (Implement\Exception\EmptyResult) { + return []; + } + $data = $this->sortBySociedad($inmobiliarias); + $informe = ['sociedades' => []]; + foreach ($data as $sociedad) { + $informe['sociedades'][$sociedad->rut] = $this->buildInmobiliaria($sociedad, $fecha); + } + $informe['movimientos'] = $this->buildMovimientos(); + $informe['totales'] = $this->buildTotales(); + + //$this->buildInforme($fecha, $informe); + + return $informe; + } + public function buildInforme(DateTimeInterface $fecha, array $data, string $type = 'Xlsx', ?string $filename = 'php://output'): void + { + $informe = $this->excelService->build($fecha, $data); + $this->excelService->save($fecha, $informe, $type, $filename); + } + + protected function buildInmobiliaria(Model\Inmobiliaria $inmobiliaria, DateTimeInterface $fecha): object + { + $dataInmobiliaria = new Output\Data\Sociedad(); + $dataInmobiliaria->sociedad = $inmobiliaria; + try { + $cuentas = $this->cuentaService->getAllActiveByInmobiliaria($inmobiliaria->rut); + } catch (Implement\Exception\EmptyResult) { + return $dataInmobiliaria; + } + foreach ($cuentas as $cuenta) { + $data = new Output\Data\Cuenta(); + $data->banco = $cuenta->banco->nombre; + $data->numero = $cuenta->cuenta; + try { + $depositos = $this->depositoRepository->fetchByCuenta($cuenta->id); + foreach ($depositos as $deposito) { + if ($deposito->termino < $fecha) { + continue; + } + $data->deposito += $deposito->capital; + $this->addTotal(self::TOTAL_DAP, $deposito->capital); + $this->totales->saldo += $deposito->capital; + + if ($deposito->inicio->format('Y-m-d') === $fecha->format('Y-m-d')) { + $this->addMovimientos(self::DAP_EGRESOS, [(object) [ + 'cuenta' => $deposito->cuenta, + 'fecha' => $deposito->inicio, + 'cargo' => - $deposito->capital, + 'abono' => 0, + 'saldo' => - $deposito->capital, + 'glosa' => 'INVERSION DAP' + ]]); + } + if ($deposito->termino->format('Y-m-d') === $fecha->format('Y-m-d')) { + $data->deposito -= $deposito->capital; + $this->addTotal(self::TOTAL_DAP, -$deposito->capital); + + $this->addMovimientos(self::DAP_INGRESOS, [(object) [ + 'cuenta' => $deposito->cuenta, + 'fecha' => $deposito->termino, + 'cargo' => 0, + 'abono' => $deposito->futuro - $deposito->capital, + 'saldo' => $deposito->futuro - $deposito->capital, + 'glosa' => 'RESCATE DAP', + 'documento' => $deposito->id + ]]); + } + } + } catch (Implement\Exception\EmptyResult) {} + $anterior = $this->getAnterior($fecha); + try { + $cartola = $this->cartolaRepository->fetchLastByCuentaAndFecha($cuenta->id, $fecha); + $data->actual = $cartola->saldo; + //$anterior = $this->getAnterior($cartola->fecha); + } catch (Implement\Exception\EmptyResult) {} + try { + $cartola = $this->cartolaRepository->fetchLastByCuentaAndFecha($cuenta->id, $anterior); + $data->anterior = $cartola->saldo; + $this->totales->saldo += $cartola->saldo; + } catch (Implement\Exception\EmptyResult) {} + if ($data->diferencia() !== 0) { + try { + $movimientos = $this->movimientoRepository->fetchByCuentaAndFecha($cuenta->id, $fecha); + $this->addMovimientos(self::INGRESOS, + array_filter($movimientos, function(Model\Contabilidad\Movimiento $movimiento) { + return $movimiento->abono > 0; + })); + $this->addMovimientos(self::EGRESOS, + array_filter($movimientos, function(Model\Contabilidad\Movimiento $movimiento) { + return $movimiento->cargo > 0; + })); + } catch (Implement\Exception\EmptyResult) {} + } + $dataInmobiliaria->cuentas []= $data; + + $this->addTotal( + [self::TOTAL_ANTERIOR, self::TOTAL_ACTUAL], + [$data->anterior, $data->actual] + ); + } + + return $dataInmobiliaria; + } + protected function buildMovimientos(): array + { + return $this->movimientos->build(); + } + protected function buildTotales(): object + { + return $this->totales; + } + protected function addMovimientos(string $tipo, array $movimientos): Output + { + if (str_starts_with($tipo, 'dap')) { + list($d, $t) = explode('->', $tipo); + $this->movimientos->addDap($t, $movimientos); + return $this; + } + foreach ($movimientos as $movimiento) { + if ($tipo === 'ingresos' and str_contains(strtolower($movimiento->glosa), ' dap ')) { + $this->movimientos->updateDap($movimiento); + continue; + } + $this->movimientos->{$tipo} []= $movimiento; + } + return $this; + } + protected function addTotal(string|array $tipo, int|array $total): Output + { + if (is_array($tipo)) { + foreach ($tipo as $i => $t) { + $this->addTotal($t, $total[$i]); + } + return $this; + } + $this->totales->{$tipo} += $total; + return $this; + } + protected function sortBySociedad(array $data): array + { + $temp = []; + foreach (self::ORDEN_SOCIEDADES as $sociedad_rut) { + foreach ($data as $inmobiliaria) { + if ($inmobiliaria->rut === $sociedad_rut) { + $temp []= $inmobiliaria; + } + } + } + foreach ($data as $inmobiliaria) { + if (!in_array($inmobiliaria, $temp)) { + $temp []= $inmobiliaria; + } + } + return $temp; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Cuenta.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Cuenta.php new file mode 100644 index 0000000..fe951f2 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Cuenta.php @@ -0,0 +1,21 @@ +actual - $this->anterior; + } + public function saldo(): int + { + return $this->diferencia() + $this->ffmm + $this->deposito; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Movimientos.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Movimientos.php new file mode 100644 index 0000000..00131e3 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Movimientos.php @@ -0,0 +1,87 @@ +dap = new class() + { + public array $ingresos = []; + public array $egresos = []; + }; + } + public object $dap; + public array $ingresos = []; + public array $egresos = []; + + const INGRESOS = 'ingresos'; + const EGRESOS = 'egresos'; + public function addDap(string $tipo, array $movimientos) + { + foreach ($movimientos as $movimiento) { + $this->dap->{$tipo} []= $movimiento; + } + return $this; + } + public function updateDap(object $movimiento): void + { + foreach ($this->ingresos as $ingreso) { + if ($movimiento->cuenta->inmobiliaria->rut !== $ingreso->cuenta->inmobiliaria->rut) { + continue; + } + if ($movimiento->fecha->format('Y-m-d') !== $ingreso->fecha->format('Y-m-d')) { + continue; + } + if ($movimiento->documento !== $ingreso->documento) { + continue; + } + $ingreso->glosa = $movimiento->glosa; + break; + } + } + public function build(): array + { + $this->dap->ingresos = $this->sortBySociedades($this->dap->ingresos); + $this->dap->egresos = $this->sortBySociedades($this->dap->egresos); + $this->ingresos = $this->sortBySociedades($this->ingresos); + $this->egresos = $this->sortBySociedades($this->egresos); + return [ + 'capital dap' => [ + 'ingresos' => $this->dap->ingresos, + 'egresos' => $this->dap->egresos + ], + 'ingresos' => $this->ingresos, + 'egresos' => $this->egresos + ]; + } + + private function sortBySociedades(array $movimientos): array + { + $temp = []; + foreach ($this->ordenSociedades as $sociedad_rut) { + $date = null; + foreach ($movimientos as $movimiento) { + if ($date === null) { + $date = $movimiento->fecha; + } + if ($movimiento->fecha !== $date) { + if ($movimiento->cuenta->inmobiliaria->rut === $sociedad_rut) { + $temp []= $movimiento; + } + $date = $movimiento->fecha; + continue; + } + if ($movimiento->cuenta->inmobiliaria->rut === $sociedad_rut) { + $temp []= $movimiento; + } + } + } + foreach ($movimientos as $movimiento) { + if (!in_array($movimiento, $temp)) { + $temp []= $movimiento; + } + } + return $temp; + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Sociedad.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Sociedad.php new file mode 100644 index 0000000..e28ae8e --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Sociedad.php @@ -0,0 +1,34 @@ +cuentas, function(int $sum, $cuenta) { + return $sum + $cuenta->actual; + }, 0); + } + public function ffmm(): int + { + return array_reduce($this->cuentas, function(int $sum, $cuenta) { + return $sum + $cuenta->ffmm; + }, 0); + } + public function deposito(): int + { + return array_reduce($this->cuentas, function(int $sum, $cuenta) { + return $sum + $cuenta->deposito; + }, 0); + } + public function caja(): int + { + return array_reduce($this->cuentas, function(int $sum, $cuenta) { + return $sum + $cuenta->saldo(); + }, 0); + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Totales.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Totales.php new file mode 100644 index 0000000..b59c865 --- /dev/null +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Data/Totales.php @@ -0,0 +1,36 @@ +actual - $this->anterior; + } + public function saldo(): int + { + return $this->diferencia() + $this->ffmm + $this->deposito; + } + public function cuentas(): int + { + return $this->actual; + } + public function ffmms(): int + { + return $this->ffmm; + } + public function depositos(): int + { + return $this->deposito; + } + public function caja(): int + { + return $this->cuentas() + $this->ffmms() + $this->depositos(); + } +} diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/Excel.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Excel.php similarity index 99% rename from app/src/Service/Contabilidad/Informe/Tesoreria/Excel.php rename to app/src/Service/Contabilidad/Informe/Tesoreria/Output/Excel.php index fe83b77..d40ea0d 100644 --- a/app/src/Service/Contabilidad/Informe/Tesoreria/Excel.php +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/Excel.php @@ -1,8 +1,8 @@ fillColumns($sheet, $columns, $styles, $startRow); $rowIndex = $startRow + 1; - $sociedades = $data['inmobiliarias']; + $sociedades = $data['sociedades']; foreach ($sociedades as $dataSociedad) { $rowIndex += $this->fillSociedad($sheet, $dataSociedad, $rowIndex); } @@ -461,7 +461,7 @@ class Excel extends Ideal\Service protected function fillSociedad(PhpSpreadsheet\Worksheet\Worksheet $sheet, object $dataSociedad, int $baseRowIndex): int { $rowIndex = $baseRowIndex; - $sheet->getCell("B{$rowIndex}")->setValue($dataSociedad->inmobiliaria->razon); + $sheet->getCell("B{$rowIndex}")->setValue($dataSociedad->sociedad->razon); foreach ($dataSociedad->cuentas as $cuentaRowIndex => $cuenta) { $this->fillCuenta($sheet, $cuenta, 3, $baseRowIndex + $cuentaRowIndex); } diff --git a/app/src/Service/Contabilidad/Informe/Tesoreria/PDF.php b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/PDF.php similarity index 66% rename from app/src/Service/Contabilidad/Informe/Tesoreria/PDF.php rename to app/src/Service/Contabilidad/Informe/Tesoreria/Output/PDF.php index 7357299..2c050e6 100644 --- a/app/src/Service/Contabilidad/Informe/Tesoreria/PDF.php +++ b/app/src/Service/Contabilidad/Informe/Tesoreria/Output/PDF.php @@ -1,5 +1,5 @@ movimientoRepository->fetchAmountBySociedadAndMes($sociedad_rut, $mes, $amount)); } + public function getByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo(int $cuenta_id, DateTimeInterface $fecha, string $glosa, int $cargo, int $abono, int $saldo): Model\Contabilidad\Movimiento + { + return $this->process($this->movimientoRepository->fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo($cuenta_id, $fecha, $glosa, $cargo, $abono, $saldo)); + } + public function add(array $data): Model\Contabilidad\Movimiento + { + try { + $movimiento = $this->movimientoRepository->fetchByCuentaAndFechaAndGlosaAndCargoAndAbonoAndSaldo($data['cuenta_id'], $data['fecha'], $data['glosa'], $data['cargo'] ?? 0, $data['abono'] ?? 0, $data['saldo']); + } catch (Implement\Exception\EmptyResult) { + $data['fecha'] = $data['fecha']->format('Y-m-d'); + $movimiento = $this->movimientoRepository->create($data); + $movimiento = $this->movimientoRepository->save($movimiento); + } + $movimiento = $this->setDetalles($movimiento, $data); + return $this->process($movimiento); + } public function setDetalles(Model\Contabilidad\Movimiento $movimiento, array $data): Model\Contabilidad\Movimiento { try { @@ -58,11 +74,11 @@ class Movimiento extends Service } }) ->setArgs(['movimiento_id' => $movimiento->id])); - $movimiento->addFactory('auxiliares', (new Implement\Repository\Factory()) + /*$movimiento->addFactory('auxiliares', (new Implement\Repository\Factory()) ->setCallable(function(int $movimiento_id) { return $this->auxiliarService->getByMovimiento($movimiento_id); }) - ->setArgs(['movimiento_id' => $movimiento->id])); + ->setArgs(['movimiento_id' => $movimiento->id]));*/ return $movimiento; } } diff --git a/php-memory.ini b/php-memory.ini new file mode 100644 index 0000000..50601e5 --- /dev/null +++ b/php-memory.ini @@ -0,0 +1,2 @@ +memory_limit = 512M +max_execution_time = 300 diff --git a/php.ini.bak b/php.ini.bak new file mode 100644 index 0000000000000000000000000000000000000000..b3d2738269f59b8bb3c729d408e0331f43805ee5 GIT binary patch literal 57266 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%0ACiP^QJRPV7JG)~ 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 literal 0 HcmV?d00001