14 Commits

Author SHA1 Message Date
430e29eaec Fixed TipoCategoria select and url importar in menu 2021-12-20 23:31:05 -03:00
79c7d5ad63 Fixes 2021-12-20 23:30:13 -03:00
e6ebb2c279 FIX: url importar 2021-12-20 23:29:40 -03:00
45952bb3ac FIX: Select cuentas 2021-12-20 23:14:23 -03:00
6b03d62ce0 Merge branch 'develop' into release 2021-12-20 22:54:14 -03:00
894cc26b21 Upload files 2021-12-20 22:51:15 -03:00
64ffb53f0c Composer for UI 2021-12-20 22:45:00 -03:00
42310ef0e4 Formatting 2021-12-20 22:44:47 -03:00
a6362a6770 FIX: Empty results threw errors 2021-12-20 22:44:29 -03:00
e9c63abc3a PHP info 2021-12-20 22:43:43 -03:00
9f47c8a85f Default value for seed 2021-12-20 22:43:32 -03:00
34eedb93d7 FIX: .env not loaded in ui 2021-12-20 21:35:47 -03:00
0e5714edc8 FIX: cuentas 2021-12-07 09:13:20 -03:00
f33bddfbea Added Docker profiles 2021-12-06 22:22:54 -03:00
20 changed files with 252 additions and 72 deletions

View File

@ -1,3 +1,4 @@
COMPOSE_PROFILES=
MYSQL_HOST= MYSQL_HOST=
MYSQL_ROOT_PASSWORD= MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE= MYSQL_DATABASE=

View File

@ -24,4 +24,11 @@ class Base {
$key = urlencode(base64_encode($signature)); $key = urlencode(base64_encode($signature));
return $this->withJson($response, ['key' => $key]); return $this->withJson($response, ['key' => $key]);
} }
public function info(Request $request, Response $response): Response {
ob_start();
phpinfo();
$data = ob_get_clean();
$response->getBody()->write($data);
return $response;
}
} }

View File

@ -13,33 +13,35 @@ class Categorias {
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response { public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
$categorias = $factory->find(Categoria::class)->many(); $categorias = $factory->find(Categoria::class)->many();
array_walk($categorias, function(&$item) use ($service) { if ($categorias !== null) {
$arr = $item->toArray(); array_walk($categorias, function(&$item) use ($service) {
$arr['cuentas'] = array_map(function($item) { $arr = $item->toArray();
return $item->toArray(); if ($item->cuentas()) {
}, $item->cuentas()); $arr['cuentas'] = array_map(function($item) {
$maps = ['activo', 'pasivo', 'ganancia', 'perdida']; return $item->toArray();
foreach ($maps as $m) { }, $item->cuentas());
$p = $m . 's';
$t = ucfirst($m);
$cuentas = $item->getCuentasOf($t);
if ($cuentas === false or $cuentas === null) {
$arr[$p] = 0;
continue;
} }
$arr[$p] = array_reduce($cuentas, function($sum, $item) use($service) { $maps = ['activo', 'pasivo', 'ganancia', 'perdida'];
return $sum + $item->saldo($service, true); foreach ($maps as $m) {
}); $p = $m . 's';
} $t = ucfirst($m);
$item = $arr; $cuentas = $item->getCuentasOf($t);
}); if ($cuentas === false or $cuentas === null) {
if ($categorias) { $arr[$p] = 0;
usort($categorias, function($a, $b) { continue;
return strcmp($a['nombre'], $b['nombre']); }
}); $arr[$p] = array_reduce($cuentas, function($sum, $item) use($service) {
return $sum + $item->saldo($service, true);
});
}
$item = $arr;
});
usort($categorias, function($a, $b) {
return strcmp($a['nombre'], $b['nombre']);
});
} }
$output = [ $output = [
'categorias' => $categorias 'categorias' => $categorias
]; ];
return $this->withJson($response, $output); return $this->withJson($response, $output);
} }

View File

@ -12,8 +12,13 @@ class Cuentas {
use Json; use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response { public function __invoke(Request $request, Response $response, Factory $factory): Response {
$cuentas = $factory->find(Cuenta::class)->array(); $cuentas = $factory->find(Cuenta::class)->many();
if ($cuentas) { if ($cuentas) {
array_walk($cuentas, function (&$item) {
$arr = $item->toArray();
$arr['categoria'] = $item->categoria()->toArray();
$item = $arr;
});
usort($cuentas, function($a, $b) { usort($cuentas, function($a, $b) {
$t = strcmp($a['tipo']['descripcion'], $b['tipo']['descripcion']); $t = strcmp($a['tipo']['descripcion'], $b['tipo']['descripcion']);
if ($t != 0) { if ($t != 0) {

View File

@ -3,19 +3,47 @@ namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ResponseInterface as Response;
use Psr\Container\ContainerInterface as Container;
use ProVM\Common\Define\Controller\Json; use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory; use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Common\Service\DocumentHandler as Handler; use Contabilidad\Common\Service\DocumentHandler as Handler;
use Contabilidad\Cuenta;
class Import { class Import {
use Json; use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response { public function __invoke(Request $request, Response $response, Factory $factory, Container $container): Response {
$post = $request->getParsedBody(); $post =$request->getParsedBody();
return $this->withJson($response, $post); $cuenta = $factory->find(Cuenta::class)->one($post['cuenta']);
$file = $request->getUploadedFiles()['archivo'];
$valid_media = [
'text/csv' => 'csvs',
'application/pdf' => 'pdfs',
'application/vnd.ms-excel' => 'xlss',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlss',
'application/json' => 'jsons'
];
if ($file->getError() === 0 and in_array($file->getClientMediaType(), array_keys($valid_media))) {
$filenfo = new \SplFileInfo($file->getClientFilename());
$new_name = implode('.', [implode(' - ', [$cuenta->nombre, $cuenta->categoria()->nombre, $post['fecha']]), $filenfo->getExtension()]);
$to = implode(DIRECTORY_SEPARATOR, [$container->get('folders')->uploads, $valid_media[$file->getClientMediaType()], $new_name]);
$file->moveTo($to);
$status = file_exists($to);
}
$output = [
'input' => [
'name' => $file->getClientFilename(),
'type' => $file->getClientMediaType(),
'size' => $file->getSize(),
'error' => $file->getError()
],
'new_name' => $new_name,
'uploaded' => $status
];
return $this->withJson($response, $output);
} }
public function uploads(Request $request, Response $response, Handler $handler): Response { public function uploads(Request $request, Response $response, Handler $handler): Response {
$output = $handler->handle(); $output = $handler->handle();
return $this->withJson($response, $output); return $this->withJson($response, $output);
} }
} }

View File

@ -13,31 +13,34 @@ class TiposCategorias {
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response { public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
$tipos = $factory->find(TipoCategoria::class)->many(); $tipos = $factory->find(TipoCategoria::class)->many();
array_walk($tipos, function(&$item) use ($service) { if ($tipos !== null) {
$arr = $item->toArray(); array_walk($tipos, function(&$item) use ($service) {
$arr['categorias'] = array_map(function($item) { $arr = $item->toArray();
return $item->toArray(); $arr['categorias'] = $item->categorias();
}, $item->categorias()); if ($arr['categorias'] !== null) {
$arr['saldo'] = abs($item->saldo($service)); $arr['categorias'] = array_map(function($item) {
$maps = ['activo', 'pasivo', 'ganancia', 'perdida']; return $item->toArray();
foreach ($maps as $m) { }, $item->categorias());
$p = $m . 's';
$t = ucfirst($m);
$cuentas = $item->getCuentasOf($t);
if ($cuentas === false or $cuentas === null) {
$arr[$p] = 0;
continue;
} }
$arr[$p] = array_reduce($cuentas, function($sum, $item) use($service) { $arr['saldo'] = abs($item->saldo($service));
return $sum + $item->saldo($service, true); $maps = ['activo', 'pasivo', 'ganancia', 'perdida'];
}); foreach ($maps as $m) {
} $p = $m . 's';
$item = $arr; $t = ucfirst($m);
}); $cuentas = $item->getCuentasOf($t);
if ($tipos) { if ($cuentas === false or $cuentas === null) {
usort($tipos, function($a, $b) { $arr[$p] = 0;
return strcmp($a['descripcion'], $b['descripcion']); continue;
}); }
$arr[$p] = array_reduce($cuentas, function($sum, $item) use($service) {
return $sum + $item->saldo($service, true);
});
}
$item = $arr;
});
usort($tipos, function($a, $b) {
return strcmp($a['descripcion'], $b['descripcion']);
});
} }
$output = [ $output = [
'tipos' => $tipos 'tipos' => $tipos

View File

@ -20,7 +20,7 @@ final class TipoCuenta extends AbstractMigration
{ {
$this->table('tipos_cuenta') $this->table('tipos_cuenta')
->addColumn('descripcion', 'string') ->addColumn('descripcion', 'string')
->addColumn('color', 'string', ['length' => 6]) ->addColumn('color', 'string', ['length' => 6, 'default' => 'ffffff'])
->create(); ->create();
} }
} }

View File

@ -5,6 +5,8 @@ server {
access_log /var/log/nginx/access.log; access_log /var/log/nginx/access.log;
root /app/public; root /app/public;
client_max_body_size 50M;
location / { location / {
try_files $uri $uri/ /index.php?$query_string; try_files $uri $uri/ /index.php?$query_string;
} }

View File

@ -1,2 +1,4 @@
log_errors = true log_errors = true
error_log = /var/log/php/error.log error_log = /var/log/php/error.log
upload_max_filesize = 50M
max_input_vars = 5000

View File

@ -3,4 +3,5 @@ use Contabilidad\Common\Controller\Base;
$app->get('/key/generate[/]', [Base::class, 'generate_key']); $app->get('/key/generate[/]', [Base::class, 'generate_key']);
$app->get('/balance[/]', [Contabilidad\Common\Controller\TiposCategorias::class, 'balance']); $app->get('/balance[/]', [Contabilidad\Common\Controller\TiposCategorias::class, 'balance']);
$app->get('/info', [Base::class, 'info']);
$app->get('/', Base::class); $app->get('/', Base::class);

View File

@ -2,9 +2,9 @@
use Psr\Container\ContainerInterface as Container; use Psr\Container\ContainerInterface as Container;
return [ return [
GuzzleHttp\Client::class => function(Container $c) { GuzzleHttp\Client::class => function(Container $c) {
return new GuzzleHttp\Client(); return new GuzzleHttp\Client();
}, },
Contabilidad\Common\Service\Auth::class => function(Container $c) { Contabilidad\Common\Service\Auth::class => function(Container $c) {
return new Contabilidad\Common\Service\Auth($c->get('api_key')); return new Contabilidad\Common\Service\Auth($c->get('api_key'));
}, },

View File

@ -89,4 +89,10 @@ class Categoria extends Model {
} }
return $this->saldo; return $this->saldo;
} }
public function toArray(): array {
$arr = parent::toArray();
$arr['tipo'] = $this->tipo()->toArray();
return $arr;
}
} }

View File

@ -10,20 +10,20 @@ use Contabilidad\Common\Service\TiposCambios as Service;
* @property int $activo * @property int $activo
*/ */
class TipoCategoria extends Model { class TipoCategoria extends Model {
public static $_table = 'tipos_categoria'; public static $_table = 'tipos_categoria';
protected static $fields = ['descripcion', 'activo']; protected static $fields = ['descripcion', 'activo'];
protected $categorias; protected $categorias;
public function categorias() { public function categorias() {
if ($this->categorias === null) { if ($this->categorias === null) {
$this->categorias = $this->parentOf(Categoria::class, [Model::CHILD_KEY => 'tipo_id']); $this->categorias = $this->parentOf(Categoria::class, [Model::CHILD_KEY => 'tipo_id']);
}
return $this->categorias;
} }
return $this->categorias;
}
public function getCuentasOf($tipo) { public function getCuentasOf($tipo) {
return $this->factory->find(Cuenta::class) return $this->factory->find(Cuenta::class)
->select([['cuentas', '*']]) ->select('cuentas.*')
->join([ ->join([
['tipos_cuenta', 'tipos_cuenta.id', 'cuentas.tipo_id'], ['tipos_cuenta', 'tipos_cuenta.id', 'cuentas.tipo_id'],
['categorias', 'categorias.id', 'cuentas.categoria_id'] ['categorias', 'categorias.id', 'cuentas.categoria_id']
@ -37,7 +37,7 @@ class TipoCategoria extends Model {
protected $saldo; protected $saldo;
public function saldo(Service $service = null) { public function saldo(Service $service = null) {
if ($this->saldo === null) { if ($this->saldo === null) {
$this->saldo = array_reduce($this->categorias(), function($sum, $item) use ($service) { $this->saldo = array_reduce($this->categorias() ?? [], function($sum, $item) use ($service) {
return $sum + $item->saldo($service); return $sum + $item->saldo($service);
}); });
} }

View File

@ -50,6 +50,7 @@ services:
image: php-ui image: php-ui
env_file: env_file:
- .api.env - .api.env
- .env
build: build:
context: ui context: ui
volumes: volumes:

View File

@ -1,3 +1,5 @@
FROM php:8-fpm FROM php:8-fpm
COPY --from=composer /usr/bin/composer /usr/bin/composer
WORKDIR /app WORKDIR /app

View File

@ -0,0 +1,12 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Views\Blade as View;
class Importar {
public function __invoke(Request $request, Response $response, View $view): Response {
return $view->render($response, 'importar');
}
}

View File

@ -0,0 +1,4 @@
<?php
use Contabilidad\Common\Controller\Importar;
$app->get('/importar[/]', Importar::class);

View File

@ -0,0 +1,92 @@
@extends('layout.base')
@section('page_title')
Importar
@endsection
@section('page_content')
<h1>Importar</h1>
<form class="ui form" action="#" method="post" id="importar_form" enctype="multipart/form-data">
<div class="two wide field">
<label>Fecha</label>
<div class="ui date calendar">
<div class="ui icon input">
<input type="text" name="fecha" />
<i class="calendar outline icon"></i>
</div>
</div>
</div>
<div class="six wide field">
<label>Cuenta</label>
<select name="cuenta"></select>
</div>
<div class="inline field">
<input type="file" name="archivo" style="display: none;" />
<div class="ui labeled icon input" id="archivo_btn">
<div class="ui label">Archivo</div>
<input type="text" readonly="" />
<i class="search icon"></i>
</div>
</div>
<button class="ui button">Importar</button>
</form>
@endsection
@push('scripts')
<script type="text/javascript">
function getCuentas() {
sendGet(_urls.api + '/cuentas').then((data) => {
if (data.cuentas === null || data.cuentas.length === 0) {
return
}
const select = $("select[name='cuenta']")
let values = []
$.each(data.cuentas, (i, el) => {
const nombre = [el.nombre, el.categoria.nombre].join(' - ')
values.push({
name: nombre,
value: el.id,
text: nombre
})
})
select.dropdown({values})
})
}
$(document).ready(() => {
getCuentas()
const today = new Date()
const start = new Date(today.getFullYear(), today.getMonth() - 1)
$('.ui.calendar').calendar({
type: 'month',
initialDate: start,
maxDate: start,
months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
formatter: {
date: function(date, settings) {
if (!date) return ''
const year = date.getFullYear()
const month = date.getMonth() + 1
return [year, month].join('-')
}
}
})
$('#archivo_btn').css('cursor', 'pointer').click(() => {
$("[name='archivo']").trigger('click')
})
$("[name='archivo']").change((e) => {
const arch = $(e.currentTarget)
const filename = arch[0].files[0].name
$('#archivo_btn').find('input').val(filename)
})
$('#importar_form').submit((e) => {
e.preventDefault()
const data = new FormData(e.currentTarget)
sendPost(_urls.api + '/import', data, true).then((resp) => {
console.debug(resp)
})
return false
})
})
</script>
@endpush

View File

@ -2,6 +2,7 @@
<a class="item" href="{{$urls->base}}">Inicio</a> <a class="item" href="{{$urls->base}}">Inicio</a>
@include('layout.body.menu.cuentas') @include('layout.body.menu.cuentas')
@include('layout.body.menu.categorias') @include('layout.body.menu.categorias')
<a class="item" href="{{$urls->base}}importar">Importar</a>
<div class="right menu"> <div class="right menu">
<a class="item" href="{{$urls->base}}config">Config</a> <a class="item" href="{{$urls->base}}config">Config</a>
</div> </div>

View File

@ -7,7 +7,18 @@
base: '{{$urls->base}}', base: '{{$urls->base}}',
api: '{{$urls->api}}' api: '{{$urls->api}}'
} }
function buildAjax(url, method) { function buildAjax(url, method, files=false) {
if (files) {
return {
url: url,
headers: {
'Authorization': 'Bearer ' + API_KEY
},
method: method,
processData: false,
contentType: false
}
}
return { return {
url: url, url: url,
headers: { headers: {
@ -21,8 +32,8 @@
let ajax_obj = buildAjax(url, 'GET') let ajax_obj = buildAjax(url, 'GET')
return $.ajax(ajax_obj) return $.ajax(ajax_obj)
} }
function sendPost(url, data) { function sendPost(url, data, files=false) {
let ajax_obj = buildAjax(url, 'POST') let ajax_obj = buildAjax(url, 'POST', files)
ajax_obj['data'] = data ajax_obj['data'] = data
return $.ajax(ajax_obj) return $.ajax(ajax_obj)
} }