14 Commits

40 changed files with 404 additions and 195 deletions

2
.gitignore vendored
View File

@ -10,3 +10,5 @@
**/.idea/
**/upload?/
**/informe?/
**/.phpunit.cache/
**/coverage/

View File

@ -9,6 +9,7 @@ RUN pecl install xdebug-3.1.3 \
&& docker-php-ext-enable xdebug
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 --from=composer /usr/bin/composer /usr/bin/composer

View File

@ -5,6 +5,11 @@ use Psr\Http\Message\UploadedFileInterface;
interface Banco
{
/**
* Process bank movements for database inserts
* @param UploadedFileInterface $file
* @return array
*/
public function process(UploadedFileInterface $file): array;
}

View File

@ -9,7 +9,7 @@ abstract class Banco extends Service implements Define\Cartola\Banco
{
public function process(UploadedFileInterface $file): array
{
$data = $this->parseFile($file);
$data = $this->handleFile($file);
$temp = [];
$columns = $this->columnMap();
foreach ($data as $row) {
@ -24,11 +24,48 @@ abstract class Banco extends Service implements Define\Cartola\Banco
}
return $temp;
}
/**
* There are banks that need some post-processing
* @param array $movimientos
* @return array
*/
public function processMovimientosDiarios(array $movimientos): array
{
return $movimientos;
}
/**
* Move the UploadedFile into a temp file from getFilename and after parseFile remove temp file
* @param UploadedFileInterface $uploadedFile
* @return array
*/
protected function handleFile(UploadedFileInterface $uploadedFile): array
{
$filename = $this->getFilename($uploadedFile);
$uploadedFile->moveTo($filename);
$data = $this->parseFile($filename);
unlink($filename);
return $data;
}
/**
* Get filename where to move UploadedFile
* @param UploadedFileInterface $uploadedFile
* @return string
*/
abstract protected function getFilename(UploadedFileInterface $uploadedFile): string;
/**
* Mapping of uploaded file data columns to database columns
* @return array
*/
abstract protected function columnMap(): array;
abstract protected function parseFile(UploadedFileInterface $uploadedFile): array;
/**
* Translate uploaded file data to database data
* @param string $filename
* @return array
*/
abstract protected function parseFile(string $filename): array;
}

View File

@ -1,5 +1,6 @@
{
"name": "incoviba/web",
"version": "2.0.0",
"type": "project",
"require": {
"berrnd/slim-blade-view": "^1.0",
@ -14,8 +15,10 @@
"slim/slim": "^4.11"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"kint-php/kint": "^5.1",
"phpunit/phpunit": "^10.2"
"phpunit/phpunit": "^10.2",
"spatie/phpunit-watcher": "^1.23"
},
"authors": [
{
@ -30,6 +33,7 @@
}
},
"config": {
"sort-packages": true
"sort-packages": true,
"process-timeout": 0
}
}

View File

@ -1,5 +1,13 @@
@extends('layout.base')
@push('page_styles')
<style>
tr.bold>th {
font-weight: bold !important;
}
</style>
@endpush
@section('page_content')
<div class="ui container">
<h1 class="ui centered header">Informe de Tesorería</h1>

View File

@ -10,11 +10,6 @@
<div class="column">
Inmobiliarias
</div>
{{--<div class="right aligned column">
<button class="ui icon button" type="button">
<i class="plus icon"></i>
</button>
</div>--}}
</h2>
<div class="ui divider"></div>
<div class="ui cards">
@ -22,9 +17,7 @@
<div class="ui card">
<div class="content">
<div class="header">
{{$inmobiliaria->abreviacion}}
{{--<a href="{{$urls->base}}/inmobiliaria/{{$inmobiliaria->rut}}">
</a>--}}
{{$inmobiliaria->abreviacion}}
</div>
<div class="description">{{$inmobiliaria->razon}} {{$inmobiliaria->tipoSociedad->descripcion}}</div>
<div class="meta">{{$inmobiliaria->rut()}}</div>

View File

@ -0,0 +1,3 @@
@push('page_scripts')
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js" integrity="sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
@endpush

View File

@ -16,28 +16,33 @@
</div>
@endsection
@include('layout.body.scripts.cryptojs')
@push('page_scripts')
<script type="text/javascript">
function encryptPassword(password) {
const passphrase = Math.floor(Math.random() * Date.now()).toString()
const encrypted = CryptoJS.AES.encrypt(password, passphrase)
return [passphrase, encrypted.toString()].join('')
}
function sendLogin(name, password) {
const data = new FormData()
data.append('name', name)
data.append('password', password)
return fetch('{{$urls->base}}/login', {
method: 'post',
headers: {
Accept: 'json'
},
body: data
}).then(response => {
const method = 'post'
const headers = {
Accept: 'json'
}
const body = new FormData()
body.append('name', name)
body.append('password', encryptPassword(password))
return fetch('{{$urls->base}}/login', {method, headers, body}).then(response => {
if (response.ok) {
return response.json()
}
}).then(data => {
if (data.login === true) {
@if(isset($redirect_uri))
window.location = '{{$redirect_uri}}'
window.location = '{{$redirect_uri}}?nocache=' + (new Date()).getTime()
@else
window.location = '{{$urls->base}}'
window.location = '{{$urls->base}}?nocache=' + (new Date()).getTime()
@endif
}
})

View File

@ -50,7 +50,7 @@
</td>
<td class="right aligned">
@if ($unidad->proyectoTipoUnidad->tipoUnidad->descripcion === 'departamento')
{{$format->number(($unidad->valor ?? $precio) / $unidad->proyectoTipoUnidad->vendible(), 2)}} UF/
{{$format->number((($unidad->valor === null or $unidad->valor === 0.0) ? $precio : $unidad->valor) / $unidad->proyectoTipoUnidad->vendible(), 2)}} UF/
@endif
</td>
<td class="center aligned">

View File

@ -1,2 +1,2 @@
<?php
//$app->add($app->getContainer()->get(Incoviba\Middleware\Authentication::class));
$app->add($app->getContainer()->get(Incoviba\Middleware\Authentication::class));

View File

@ -0,0 +1,2 @@
<?php
$app->add($app->getContainer()->get(Incoviba\Middleware\CORS::class));

View File

@ -45,7 +45,8 @@ return [
))
->register('security', $container->get(Incoviba\Service\Cartola\Security::class))
->register('itau', $container->get(Incoviba\Service\Cartola\Itau::class))
->register('santander', $container->get(Incoviba\Service\Cartola\Santander::class));
->register('santander', $container->get(Incoviba\Service\Cartola\Santander::class))
->register('bci', $container->get(Incoviba\Service\Cartola\BCI::class));
},
Incoviba\Common\Define\Contabilidad\Exporter::class => function(ContainerInterface $container) {
return $container->get(Incoviba\Service\Contabilidad\Exporter\Nubox::class);

View File

@ -12,6 +12,16 @@ class Inmobiliarias
{
use withJson;
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Repository\Inmobiliaria $inmobiliariaRepository): ResponseInterface
{
$output = [
'inmobiliarias' => []
];
try {
$output['inmobiliarias'] = $inmobiliariaRepository->fetchAll('razon');
} catch (EmptyResult) {}
return $this->withJson($response, $output);
}
public function cuentas(ServerRequestInterface $request, ResponseInterface $response,
Repository\Inmobiliaria $inmobiliariaRepository,
Repository\Inmobiliaria\Cuenta $cuentaRepository, int $inmobiliaria_rut): ResponseInterface

View File

@ -20,8 +20,8 @@ class Inmobiliarias
$inmobiliarias = [];
try {
$inmobiliarias = array_map(function($row) use ($inmobiliariaRepository) {
return $inmobiliariaRepository->load((array) $row);
}, $this->fetchRedis($redisService, $redisKey));
return $inmobiliariaRepository->load($row);
}, $this->fetchRedis($redisService, $redisKey, true));
} catch (EmptyRedis) {
try {
$inmobiliarias = $inmobiliariaRepository->fetchAll();

View File

@ -1,14 +1,16 @@
<?php
namespace Incoviba\Controller;
use Incoviba\Common\Implement\Exception\EmptyResult;
use PDOException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Incoviba\Common\Alias\View;
use Incoviba\Common\Ideal\Controller;
use Incoviba\Repository;
use Incoviba\Service;
class Login
class Login extends Controller
{
public function form(ServerRequestInterface $request, ResponseInterface $response, View $view, Service\Login $service): ResponseInterface
{
@ -20,14 +22,17 @@ class Login
public function login(ServerRequestInterface $request, ResponseInterface $response, Repository\User $userRepository, Service\Login $service): ResponseInterface
{
$body = $request->getParsedBody();
$user = $userRepository->fetchByName($body['name']);
$output = [
'name' => $user->name,
'name' => $body['name'],
'login' => false
];
if ($user->validate($body['password'])) {
$output['login'] = $service->login($user);
}
try {
$user = $userRepository->fetchByName($body['name']);
if ($service->validateUser($user, $body['password'])) {
$output['login'] = $service->login($user);
}
} catch (EmptyResult) {}
$response->getBody()->write(json_encode($output));
return $response->withHeader('Content-Type', 'application/json');
}

View File

@ -6,14 +6,15 @@ use Psr\Http\Message\ServerRequestInterface;
use Incoviba\Common\Alias\View;
use Incoviba\Common\Ideal\Controller;
use Incoviba\Repository;
use Incoviba\Service;
class Creditos extends Controller
{
public function show(ServerRequestInterface $request, ResponseInterface $response, View $view,
Repository\Venta $ventaRepository, Repository\Banco $bancoRepository,
int $venta_id): ResponseInterface
Service\Venta $ventaService, Repository\Banco $bancoRepository,
int $venta_id): ResponseInterface
{
$venta = $ventaRepository->fetchById($venta_id);
$venta = $ventaService->getById($venta_id);
$bancos = $bancoRepository->fetchAll('nombre');
return $view->render($response, 'ventas.creditos', compact('venta', 'bancos'));
}

View File

@ -6,13 +6,13 @@ use Incoviba\Service;
trait withRedis
{
public function fetchRedis(Service\Redis $redisService, string $redisKey): mixed
public function fetchRedis(Service\Redis $redisService, string $redisKey, ?bool $asArray = null): mixed
{
$jsonString = $redisService->get($redisKey);
if ($jsonString === null) {
throw new EmptyRedis($redisKey);
}
return json_decode($jsonString);
return json_decode($jsonString, $asArray);
}
public function saveRedis(Service\Redis $redisService, string $redisKey, mixed $value, ?int $expiration = null): void
{

View File

@ -45,11 +45,19 @@ class Authentication
]);
$valid_paths = [
'/',
'/'
];
if (in_array($current_path, $valid_paths, true)) {
return true;
}
$valid_subpaths = [
'/api'
];
foreach ($valid_subpaths as $path) {
if (str_starts_with($current_path, $path)) {
return true;
}
}
$valid_uris = [
$this->login_url,
];

View File

@ -0,0 +1,21 @@
<?php
namespace Incoviba\Middleware;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CORS
{
public function __construct(protected ResponseFactoryInterface $responseFactory) {}
public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($request->getMethod() === 'OPTIONS') {
return $this->responseFactory->createResponse()
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Methods', 'POST,GET,OPTIONS');
}
return $handler->handle($request);
}
}

View File

@ -10,8 +10,6 @@ class Inmobiliaria extends Model
public ?string $dv;
public ?string $razon;
public ?string $abreviacion;
public ?string $cuenta;
public ?Banco $banco;
public ?TipoSociedad $tipoSociedad;
public function rut(): string
@ -37,8 +35,6 @@ class Inmobiliaria extends Model
'rut_formateado' => $this->rut(),
'razon' => $this->razon ?? '',
'abreviacion' => $this->abreviacion ?? '',
'cuenta' => $this->cuenta ?? '',
'banco' => $this->banco ?? '',
'tipo_sociedad' => $this->tipoSociedad ?? ''
];
}

View File

@ -3,6 +3,7 @@ namespace Incoviba\Model;
use DateTimeInterface;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Model\Movimiento\Detalle;
class Movimiento extends Ideal\Model
@ -19,7 +20,11 @@ class Movimiento extends Ideal\Model
public function getDetalles(): ?Detalle
{
if (!isset($this->detalles)) {
$this->detalles = $this->runFactory('detalles');
try {
$this->detalles = $this->runFactory('detalles');
} catch (EmptyResult) {
$this->detalles = null;
}
}
return $this->detalles;
}

View File

@ -3,9 +3,6 @@ namespace Incoviba\Model;
use DateTimeInterface;
use Incoviba\Common\Ideal;
use Incoviba\Controller\Ventas;
use Incoviba\Model\Venta\FormaPago;
use Incoviba\Model\Venta\Pago;
class Venta extends Ideal\Model
{
@ -18,7 +15,7 @@ class Venta extends Ideal\Model
public bool $relacionado;
protected ?Venta\Entrega $entrega;
public float $uf;
protected ?Pago $resciliacion;
protected ?Venta\Pago $resciliacion;
public ?array $estados;
public ?Venta\EstadoVenta $currentEstado;
@ -44,6 +41,11 @@ class Venta extends Ideal\Model
}
return $this->formaPago;
}
public function setFormaPago(Venta\FormaPago $formaPago): Venta
{
$this->formaPago = $formaPago;
return $this;
}
public function entrega(): ?Venta\Entrega
{
if (!isset($this->entrega)) {
@ -94,9 +96,9 @@ class Venta extends Ideal\Model
}
return $this->valor_util;
}
public function saldo(string $moneda = Pago::UF): float
public function saldo(string $moneda = Venta\Pago::UF): float
{
$valor = $this->valor * (($moneda === Pago::UF) ? 1 : $this->uf);
$valor = $this->valor * (($moneda === Venta\Pago::UF) ? 1 : $this->uf);
return $valor - $this->formaPago()->total($moneda);
}

View File

@ -6,8 +6,7 @@ use Incoviba\Model;
class Propiedad extends Ideal\Model
{
public array $unidades;
public array $unidades = [];
protected array $departamentos;
public function departamentos(): array
{

View File

@ -65,4 +65,14 @@ class Cartola extends Ideal\Repository
->where('cuenta_id = ? AND fecha = ?');
return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d')]);
}
public function fetchLastByCuentaAndFecha(int $cuenta_id, DateTimeInterface $fecha): Model\Cartola
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('cuenta_id = ? AND fecha <= ?')
->order('fecha DESC')
->limit(1);
return $this->fetchOne($query, [$cuenta_id, $fecha->format('Y-m-d')]);
}
}

View File

@ -20,13 +20,9 @@ class Inmobiliaria extends Ideal\Repository
return 'rut';
}
public function create(?array $data = null): Define\Model
public function create(?array $data = null): Model\Inmobiliaria
{
$map = (new Implement\Repository\MapperParser(['dv', 'razon', 'abreviacion', 'cuenta']))
->register('banco', (new Implement\Repository\Mapper())
->setFunction(function($data) {
return $this->bancoRepository->fetchById($data['banco']);
}))
$map = (new Implement\Repository\MapperParser(['dv', 'razon', 'abreviacion']))
->register('sociedad', (new Implement\Repository\Mapper())
->setProperty('tipoSociedad')
->setFunction(function($data) {
@ -34,7 +30,7 @@ class Inmobiliaria extends Ideal\Repository
}));
return $this->parseData(new Model\Inmobiliaria(), $data, $map);
}
public function save(Define\Model $model): Define\Model
public function save(Define\Model $model): Model\Inmobiliaria
{
$model->rut = $this->saveNew(
['dv', 'razon', 'abreviacion', 'cuenta', 'banco', 'sociedad'],
@ -42,7 +38,7 @@ class Inmobiliaria extends Ideal\Repository
);
return $model;
}
public function edit(Define\Model $model, array $new_data): Define\Model
public function edit(Define\Model $model, array $new_data): Model\Inmobiliaria
{
return $this->update($model, ['dv', 'razon', 'abreviacion', 'cuenta', 'banco', 'sociedad'], $new_data);
}

View File

@ -132,9 +132,9 @@ class Venta extends Ideal\Repository
['propietario', 'propiedad', 'pie', 'bono_pie', 'credito', 'escritura', 'subsidio', 'escriturado',
'entrega', 'entregado', 'fecha', 'valor_uf', 'estado', 'fecha_ingreso', 'avalchile', 'agente', 'uf',
'relacionado', 'promocion', 'resciliacion', 'devolucion'],
[$model->propietario()->rut, $model->propiedad()->id, $model->formaPago()->pie?->id, $model->formaPago()->bonoPie?->id,
$model->formaPago()->credito?->id, $model->formaPago()->escritura?->id, $model->formaPago()->subsidio?->id,
$model->formaPago()->escritura !== null ? $model->formaPago()->escritura->pago->fecha->format('Y-m-d') : null,
[$model->propietario()->rut, $model->propiedad()->id, $model->formaPago()?->pie?->id, $model->formaPago()?->bonoPie?->id,
$model->formaPago()?->credito?->id, $model->formaPago()?->escritura?->id, $model->formaPago()?->subsidio?->id,
$model->formaPago()?->escritura !== null ? $model->formaPago()?->escritura->pago->fecha->format('Y-m-d') : null,
null, null, $model->fecha->format('Y-m-d'), $model->valor, 1, $model->fechaIngreso->format('Y-m-d'),
null, null, $model->uf, $model->relacionado ? 1 : 0, null, null, null]
);

View File

@ -18,22 +18,23 @@ class Propiedad extends Ideal\Repository
public function create(?array $data = null): Model\Venta\Propiedad
{
$map = (new Implement\Repository\MapperParser())
->register('unidad_principal', (new Implement\Repository\Mapper())
->setProperty('unidades')
->setFunction(function($data) {
if (isset($data['id'])) {
return $this->unidadService->getByPropiedad($data['id']);
}
return [$this->unidadService->getById($data['unidad_principal'])];
}))
->register('estado', new Implement\Repository\Mapper\Boolean('estado'));
return $this->parseData(new Model\Venta\Propiedad(), $data, $map);
}
public function load(array $data_row): Define\Model
{
$propiedad = parent::load($data_row);
if (isset($propiedad->id)) {
$propiedad->unidades = $this->unidadService->getByPropiedad($propiedad->id);
}
return $propiedad;
}
public function save(Define\Model $model): Model\Venta\Propiedad
{
$model->id = $this->saveNew(
['unidad_principal', 'estacionamientos', 'bodegas', 'estado'],
[$model->departamentos()[0]->id,
[null,
implode(',', array_map(function(Model\Venta\Unidad $unidad) {return $unidad->id;}, $model->estacionamientos())),
implode(',', array_map(function(Model\Venta\Unidad $unidad) {return $unidad->id;}, $model->bodegas())),
1]

View File

@ -121,7 +121,12 @@ class Cartola extends Service
} catch (Exception\EmptyResult $exception) {
$data['cuenta_id'] = $cuenta->id;
$movimiento = $this->movimientoRepository->create($data);
return $this->movimientoRepository->save($movimiento);
try {
return $this->movimientoRepository->save($movimiento);
} catch (\PDOException $exception) {
$this->logger->critical(var_export($data,true));
throw $exception;
}
}
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace Incoviba\Service\Cartola;
use Psr\Http\Message\UploadedFileInterface;
use PhpOffice\PhpSpreadsheet;
use Incoviba\Common\Ideal\Cartola\Banco;
class BCI extends Banco
{
protected function columnMap(): array
{
return [
'Fecha Transacción' => 'fecha',
'Cargo $ (-)' => 'cargo',
'Abono $ (+)' => 'abono',
'Descripción' => 'descripcion',
'Saldo' => 'saldo'
];
}
protected function getFilename(UploadedFileInterface $uploadedFile): string
{
return '/tmp/cartola.xlsx';
}
protected function parseFile(string $filename): array
{
$xlsx = @PhpSpreadsheet\IOFactory::load($filename);
$worksheet = $xlsx->getActiveSheet();
$rows = $worksheet->getRowIterator();
$rows->seek(3);
$row = $rows->current();
$columnIterator = $row->getColumnIterator();
$saldoFinal = 0;
foreach ($columnIterator as $column) {
if ($column->getValue() === null) {
continue;
}
if ($column->getCalculatedValue() === 'Saldo Contable') {
$columnIterator->next();
$column = $columnIterator->current();
$saldoFinal = (int) str_replace('.', '', $column->getCalculatedValue());
break;
}
}
$saldo = $saldoFinal;
$data = [];
$columns = [];
$dataFound = false;
foreach ($rows as $row) {
if (!$dataFound and $worksheet->getCell([1, $row->getRowIndex()])->getCalculatedValue() !== null
and trim($worksheet->getCell([1, $row->getRowIndex()])->getCalculatedValue()) === 'Fecha Contable') {
$dataFound = true;
$columns = $this->getRowData($row);
continue;
}
if (!$dataFound) {
continue;
}
if ($worksheet->getCell([1, $row->getRowIndex()])->getValue() === null) {
break;
}
$rowData = $this->getRowData($row);
$rowData = array_combine($columns, $rowData);
$rowData['Fecha Transacción'] = implode('-', array_reverse(explode('/', $rowData['Fecha Transacción'])));
$rowData['Cargo $ (-)'] = (int) str_replace('.', '', $rowData['Cargo $ (-)'] ?? 0);
$rowData['Abono $ (+)'] = (int) str_replace('.', '', $rowData['Abono $ (+)'] ?? 0);
$saldo = $saldo + $rowData['Cargo $ (-)'] - $rowData['Abono $ (+)'];
$rowData['Saldo'] = $saldo;
unset($rowData['']);
$data []= $rowData;
}
return $data;
}
protected function getRowData(PhpSpreadsheet\Worksheet\Row $row): array
{
$data = [];
$cells = $row->getColumnIterator();
foreach ($cells as $cell) {
$data []= $cell->getCalculatedValue();
}
return $data;
}
}

View File

@ -29,12 +29,12 @@ class Itau extends Banco
'Saldos' => 'saldo'
];
}
protected function parseFile(UploadedFileInterface $uploadedFile): array
protected function getFilename(UploadedFileInterface $uploadedFile): string
{
return '/tmp/cartola.xls';
}
protected function parseFile(string $filename): array
{
$filename = '/tmp/cartola.xls';
$uploadedFile->moveTo($filename);
$reader = PhpSpreadsheet\IOFactory::createReader('Xls');
$xlsx = $reader->load($filename);
$sheet = $xlsx->getActiveSheet();
@ -51,8 +51,6 @@ class Itau extends Banco
}
} catch (PhpSpreadsheet\Exception $exception) {
$this->logger->critical($exception);
} finally {
unlink($filename);
}
return $data;
}

View File

@ -10,7 +10,6 @@ class Santander extends Banco
{
protected function columnMap(): array
{
// MONTO DESCRIPCIÓN MOVIMIENTO FECHA N° DOCUMENTO SUCURSAL CARGO/ABONO
return [
'cargo' => 'cargo',
'abono' => 'abono',
@ -25,11 +24,13 @@ class Santander extends Banco
'Saldo Diario' => 'saldo'
];
}
protected function parseFile(UploadedFileInterface $uploadedFile): array
protected function getFilename(UploadedFileInterface $uploadedFile): string
{
$filename = '/tmp/cartola.xlsx';
$uploadedFile->moveTo($filename);
return '/tmp/cartola.xlsx';
}
protected function parseFile(string $filename): array
{
$reader = PhpSpreadsheet\IOFactory::createReader('Xlsx');
try {
$xlsx = $reader->load($filename);

View File

@ -16,14 +16,21 @@ class Security extends Banco
return $movimientos;
}
protected function parseFile(UploadedFileInterface $uploadedFile): array
protected function getFilename(UploadedFileInterface $uploadedFile): string
{
$stream = $uploadedFile->getStream();
$stream->seek(3);
if ($stream->read(strlen('table')) === 'table') {
return $this->processHtm($uploadedFile);
return '/tmp/cartola.htm';
}
return $this->processXls($uploadedFile);
return '/tmp/cartola.xls';
}
protected function parseFile(string $filename): array
{
if (str_ends_with($filename, '.htm')) {
return $this->processHtm($filename);
}
return $this->processXls($filename);
}
protected function columnMap(): array
{
@ -38,10 +45,8 @@ class Security extends Banco
];
}
private function processXls(UploadedFileInterface $file): array
private function processXls(string $filename): array
{
$filename = '/tmp/cartola.xls';
$file->moveTo($filename);
$xlsx = @PhpSpreadsheet\IOFactory::load($filename);
$worksheet = $xlsx->getActiveSheet();
$rows = $worksheet->getRowIterator(3);
@ -82,14 +87,10 @@ class Security extends Banco
$data []= $rowData;
}
}
unlink($filename);
return $data;
}
private function processHtm(UploadedFileInterface $file): array
private function processHtm(string $filename): array
{
$filename = '/tmp/cartola.htm';
$file->moveTo($filename);
$domDocument = new DOMDocument();
$domDocument->loadHTML('<body>' . file_get_contents($filename) . '</body>');
@ -122,7 +123,6 @@ class Security extends Banco
}
$data []= $rowData;
}
return $data;
}
}

View File

@ -228,12 +228,14 @@ class Tesoreria extends Ideal\Service
}
}
} catch (Implement\Exception\EmptyResult) {}
$anterior = $this->getAnterior($fecha);
try {
$cartola = $this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $fecha);
$cartola = $this->cartolaRepository->fetchLastByCuentaAndFecha($cuenta->id, $fecha);
$data->actual = $cartola->saldo;
//$anterior = $this->getAnterior($cartola->fecha);
} catch (Implement\Exception\EmptyResult) {}
try {
$cartola = $this->cartolaRepository->fetchByCuentaAndFecha($cuenta->id, $this->getAnterior($fecha));
$cartola = $this->cartolaRepository->fetchLastByCuentaAndFecha($cuenta->id, $anterior);
$data->anterior = $cartola->saldo;
} catch (Implement\Exception\EmptyResult) {}
if ($data->diferencia() !== 0) {
@ -289,5 +291,4 @@ class Tesoreria extends Ideal\Service
$this->totales->{$tipo} += $total;
return $this;
}
}

View File

@ -47,7 +47,16 @@ class Login
}
return $login->user;
}
public function validateUser(Model\User $user, string $encryptedPassword): bool
{
list($passphrase, $encrypted) = $this->splitPassword($encryptedPassword);
try {
$password = $this->cryptoJs_aes_decrypt($encrypted, $passphrase);
} catch (Exception) {
return false;
}
return $user->validate($password);
}
public function login(Model\User $user): bool
{
try {
@ -104,9 +113,12 @@ class Login
setcookie(
$this->cookie_name,
implode($this->cookie_separator, [$selector, $token]),
$expires->getTimestamp(),
$this->path,
$this->domain
[
'expires' => $expires->getTimestamp(),
'path' => $this->path,
'domain' => $this->domain,
'samesite' => 'Strict'
]
);
$this->selector = $selector;
$this->token = $token;
@ -126,10 +138,70 @@ class Login
{
return password_verify($this->token, $login->token);
}
protected function generateToken(Model\Login $login)
protected function generateToken(Model\Login $login): array
{
$selector = bin2hex(random_bytes(12));
$token = bin2hex(random_bytes(20));
return ['selector' => $selector, 'token' => $token];
}
protected function splitPassword(string $input): array
{
$ini = strpos($input, 'U');
$passphrase = substr($input, 0, $ini);
$message = substr($input, $ini);
return [$passphrase, $message];
}
protected function cryptoJs_aes_decrypt($data, $key): string
{
$data = base64_decode($data);
if (substr($data, 0, 8) != "Salted__") {
return false;
}
$salt = substr($data, 8, 8);
$keyAndIV = $this->aes_evpKDF($key, $salt);
$decrypted = openssl_decrypt(
substr($data, 16),
"aes-256-cbc",
$keyAndIV["key"],
OPENSSL_RAW_DATA, // base64 was already decoded
$keyAndIV["iv"]
);
if ($decrypted === false) {
throw new Exception();
}
return $decrypted;
}
protected function aes_evpKDF($password, $salt, $keySize = 8, $ivSize = 4, $iterations = 1, $hashAlgorithm = "md5"): array
{
$targetKeySize = $keySize + $ivSize;
$derivedBytes = "";
$numberOfDerivedWords = 0;
$block = NULL;
$hasher = hash_init($hashAlgorithm);
while ($numberOfDerivedWords < $targetKeySize) {
if ($block != NULL) {
hash_update($hasher, $block);
}
hash_update($hasher, $password);
hash_update($hasher, $salt);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
// Iterations
for ($i = 1; $i < $iterations; $i++) {
hash_update($hasher, $block);
$block = hash_final($hasher, TRUE);
$hasher = hash_init($hashAlgorithm);
}
$derivedBytes .= substr($block, 0, min(strlen($block), ($targetKeySize - $numberOfDerivedWords) * 4));
$numberOfDerivedWords += strlen($block) / 4;
}
return array(
"key" => substr($derivedBytes, 0, $keySize * 4),
"iv" => substr($derivedBytes, $keySize * 4, $ivSize * 4)
);
}
}

View File

@ -104,7 +104,7 @@ class Venta extends Service
$data['uf'] = $this->moneyService->getUF($fecha);
$propietario = $this->addPropietario($data);
$propiedad = $this->addPropiedad($data);
$forma_pago = $this->addFormaPago($data);
$formaPago = $this->addFormaPago($data);
$venta_data = [
'propietario' => $propietario->rut,
'propiedad' => $propiedad->id,
@ -116,14 +116,15 @@ class Venta extends Service
$map = ['pie', 'subsidio', 'credito', 'bono_pie'];
foreach ($map as $field) {
$name = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $field))));
if (isset($forma_pago->{$name})) {
$venta_data[$field] = $forma_pago->{$name}->id;
if (isset($formaPago->{$name})) {
$venta_data[$field] = $formaPago->{$name}->id;
}
}
try {
return $this->ventaRepository->fetchByPropietarioAndPropiedad($propietario->rut, $propiedad->id);
} catch (Implement\Exception\EmptyResult) {
$venta = $this->ventaRepository->create($venta_data);
$venta->setFormaPago($formaPago);
$venta = $this->ventaRepository->save($venta);
$tipoEstado = $this->tipoEstadoVentaRepository->fetchByDescripcion('vigente');
@ -231,7 +232,7 @@ class Venta extends Service
protected function addPropiedad(array $data): Model\Venta\Propiedad
{
$ids = array_filter($data, function($key) {
return str_contains($key, 'unidad');
return str_starts_with($key, 'unidad');
}, ARRAY_FILTER_USE_KEY);
return $this->propiedadService->addPropiedad($ids);
@ -239,85 +240,7 @@ class Venta extends Service
protected function addFormaPago(array $data): Model\Venta\FormaPago
{
return $this->formaPagoService->add($data);
/*$fields = [
'pie',
'subsidio',
'credito',
'bono_pie'
];
$forma_pago = new Model\Venta\FormaPago();
foreach ($fields as $name) {
if (isset($data["has_{$name}"])) {
$method = 'add' . str_replace(' ', '', ucwords(str_replace('_', ' ', $name)));
$obj = $this->{$method}($data);
$forma_pago->{$name} = $obj;
}
}
return $forma_pago;*/
}
/*protected function addPie(array $data): Model\Venta\Pie
{
$fields = array_fill_keys([
'fecha_venta',
'pie',
'cuotas',
'uf'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$mapped_data = array_combine([
'fecha',
'valor',
'cuotas',
'uf'
], $filtered_data);
$mapped_data['valor'] = $this->cleanValue($mapped_data['valor']);
return $this->pieService->add($mapped_data);
}
protected function addSubsidio(array $data): Model\Venta\Subsidio
{
$fields = array_fill_keys([
'fecha_venta',
'ahorro',
'subsidio',
'uf'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$mapped_data = array_combine([
'fecha',
'ahorro',
'subsidio',
'uf'
], $filtered_data);
return $this->subsidioService->add($mapped_data);
}
protected function addCredito(array $data): Model\Venta\Credito
{
$fields = array_fill_keys([
'fecha_venta',
'credito',
'uf'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$mapped_data = array_combine([
'fecha',
'valor',
'uf'
], $filtered_data);
return $this->creditoService->add($mapped_data);
}
protected function addBonoPie(array $data): Model\Venta\BonoPie
{
$fields = array_fill_keys([
'fecha_venta',
'bono_pie'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$mapped_data = array_combine([
'fecha',
'valor'
], $filtered_data);
return $this->bonoPieService->add($mapped_data);
}*/
protected function addEstado(Model\Venta $venta, Model\Venta\TipoEstadoVenta $tipoEstadoVenta, array $data): void
{
$fecha = new DateTimeImmutable($data['fecha']);

View File

@ -75,10 +75,11 @@ class Cuota
}
public function getByPie(int $pie_id): array
{
return $this->cuotaRepository->fetchByPie($pie_id);
/*return array_filter($this->cuotaRepository->fetchByPie($pie_id), function(Model\Venta\Cuota $cuota) {
return !in_array($cuota->pago->currentEstado->tipoEstadoPago->descripcion, ['anulado']);
});*/
try {
return $this->cuotaRepository->fetchByPie($pie_id);
} catch (EmptyResult) {
return [];
}
}
public function getVigenteByPie(int $pie_id): array
{

View File

@ -77,7 +77,6 @@ class FormaPago extends Ideal\Service
'uf'
], 0);
$filtered_data = array_intersect_key($data, $fields);
$this->logger->critical(var_export($filtered_data,true));
$mapped_data = array_combine([
'fecha',
'valor',

View File

@ -3,18 +3,23 @@ namespace Incoviba\Service\Venta;
use PDO;
use PDOException;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal\Service;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Repository;
use Incoviba\Model;
class Propiedad
class Propiedad extends Service
{
public function __construct(
LoggerInterface $logger,
protected Repository\Venta\Propiedad $propiedadRepository,
protected Repository\Venta\Unidad $unidadRepository,
protected Define\Connection $connection
) {}
) {
parent::__construct($logger);
}
public function addPropiedad(array $ids): Model\Venta\Propiedad
{

2
php-xdebug.ini Normal file
View File

@ -0,0 +1,2 @@
[xdebug]
xdebug.mode=coverage