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 0000000..b3d2738 Binary files /dev/null and b/php.ini.bak differ