Compare commits
60 Commits
61448a2521
...
master
Author | SHA1 | Date | |
---|---|---|---|
4954947829 | |||
d0d123e12e | |||
2b3b475d91 | |||
e5cf3cfa07 | |||
71975ec6d9 | |||
84a3f8e2e3 | |||
f36399c1f4 | |||
5a9dc6602c | |||
d7dfc2d221 | |||
1d62a0c04e | |||
c1eeba04a2 | |||
3cb2a877de | |||
a2a90497ae | |||
b17225549c | |||
925388df92 | |||
af78106700 | |||
665f426011 | |||
56b371d20c | |||
2126d30ee7 | |||
71b4211fc3 | |||
0378a2cf09 | |||
73df98dca5 | |||
4abe3448c0 | |||
f9bbbfd920 | |||
9e29dd09b7 | |||
52443c2226 | |||
572a9dc87c | |||
66882d9f85 | |||
aacda4d1e4 | |||
1a0c83fb2b | |||
f99b984f6c | |||
a5428b252e | |||
605c905f5d | |||
4bc1e4ae4d | |||
93f77bfbb8 | |||
fddba2fb87 | |||
430e29eaec | |||
79c7d5ad63 | |||
e6ebb2c279 | |||
45952bb3ac | |||
6b03d62ce0 | |||
894cc26b21 | |||
64ffb53f0c | |||
42310ef0e4 | |||
a6362a6770 | |||
e9c63abc3a | |||
9f47c8a85f | |||
34eedb93d7 | |||
960c418848 | |||
0e5714edc8 | |||
f33bddfbea | |||
25f873c453 | |||
34b429530f | |||
9d2504f016 | |||
8ef4ab1c7d | |||
10b2485cfd | |||
0382f8c286 | |||
a3311f805e | |||
378de3ed86 | |||
69c2cffa6c |
1
.api.env.sample
Normal file
1
.api.env.sample
Normal file
@ -0,0 +1 @@
|
|||||||
|
API_KEY=
|
@ -1,3 +1,4 @@
|
|||||||
|
COMPOSE_PROFILES=
|
||||||
MYSQL_HOST=
|
MYSQL_HOST=
|
||||||
MYSQL_ROOT_PASSWORD=
|
MYSQL_ROOT_PASSWORD=
|
||||||
MYSQL_DATABASE=
|
MYSQL_DATABASE=
|
||||||
|
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
81
.idea/contabilidad.iml
generated
81
.idea/contabilidad.iml
generated
@ -1,81 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="WEB_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/kint-php/kint" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/zeuxisoo/slim-whoops" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phar-io/version" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phar-io/manifest" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/theseer/tokenizer" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/webmozart/assert" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/philo/laravel-blade" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/nesbot/carbon" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/type" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/diff" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/illuminate/support" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/lines-of-code" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/illuminate/filesystem" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/object-enumerator" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/illuminate/events" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/comparator" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/illuminate/contracts" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/simple-cache" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/code-unit" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/illuminate/view" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/http-server-handler" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/doctrine/instantiator" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/exporter" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/illuminate/container" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/http-factory" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/doctrine/inflector" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/cli-parser" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/container" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/version" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/http-message" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/complexity" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/http-server-middleware" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/recursion-context" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/psr/log" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/resource-operations" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/global-state" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/composer" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/environment" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpdocumentor/reflection-docblock" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/code-unit-reverse-lookup" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpdocumentor/type-resolver" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/polyfill-mbstring" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/sebastian/object-reflector" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpdocumentor/reflection-common" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/finder" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/deprecation-contracts" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/translation-contracts" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/filp/whoops" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/polyfill-ctype" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/debug" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/nyholm/psr7-server" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/translation" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/nyholm/psr7" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/symfony/polyfill-php80" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/nikic/php-parser" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/nikic/fast-route" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/php-di/php-di" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/php-di/phpdoc-reader" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/php-di/slim-bridge" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/php-di/invoker" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpunit/php-code-coverage" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpunit/phpunit" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpunit/php-text-template" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpunit/php-invoker" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpunit/php-file-iterator" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpunit/php-timer" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/myclabs/deep-copy" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/phpspec/prophecy" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/opis/closure" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/php-http/message-factory" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/rubellum/slim-blade-view" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/ui/vendor/slim/slim" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/contabilidad.iml" filepath="$PROJECT_DIR$/.idea/contabilidad.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
87
.idea/php.xml
generated
87
.idea/php.xml
generated
@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="PhpIncludePathManager">
|
|
||||||
<include_path>
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/kint-php/kint" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/zeuxisoo/slim-whoops" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phar-io/version" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phar-io/manifest" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/theseer/tokenizer" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/webmozart/assert" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/philo/laravel-blade" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/nesbot/carbon" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/type" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/diff" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/illuminate/support" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/lines-of-code" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/illuminate/filesystem" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/object-enumerator" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/illuminate/events" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/comparator" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/illuminate/contracts" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/simple-cache" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/code-unit" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/illuminate/view" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/http-server-handler" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/doctrine/instantiator" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/exporter" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/illuminate/container" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/http-factory" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/doctrine/inflector" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/cli-parser" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/container" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/version" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/http-message" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/complexity" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/http-server-middleware" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/recursion-context" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/psr/log" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/resource-operations" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/global-state" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/composer" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/environment" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpdocumentor/reflection-docblock" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/code-unit-reverse-lookup" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpdocumentor/type-resolver" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/polyfill-mbstring" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/sebastian/object-reflector" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpdocumentor/reflection-common" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/finder" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/deprecation-contracts" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/translation-contracts" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/filp/whoops" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/polyfill-ctype" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/debug" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/nyholm/psr7-server" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/translation" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/nyholm/psr7" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/symfony/polyfill-php80" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/nikic/php-parser" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/nikic/fast-route" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/php-di/php-di" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/php-di/phpdoc-reader" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/php-di/slim-bridge" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/php-di/invoker" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpunit/php-code-coverage" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpunit/phpunit" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpunit/php-text-template" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpunit/php-invoker" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpunit/php-file-iterator" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpunit/php-timer" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/myclabs/deep-copy" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/phpspec/prophecy" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/opis/closure" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/php-http/message-factory" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/rubellum/slim-blade-view" />
|
|
||||||
<path value="$PROJECT_DIR$/ui/vendor/slim/slim" />
|
|
||||||
</include_path>
|
|
||||||
</component>
|
|
||||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.0">
|
|
||||||
<option name="suggestChangeDefaultLanguageLevel" value="false" />
|
|
||||||
</component>
|
|
||||||
<component name="PhpUnit">
|
|
||||||
<phpunit_settings>
|
|
||||||
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/ui/vendor/autoload.php" />
|
|
||||||
</phpunit_settings>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
1
.python.env.sample
Normal file
1
.python.env.sample
Normal file
@ -0,0 +1 @@
|
|||||||
|
PYTHON_KEY=
|
1
api/.gitignore
vendored
Normal file
1
api/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
**/uploads/
|
@ -1,8 +1,8 @@
|
|||||||
FROM php:8-fpm
|
FROM php:8-fpm
|
||||||
|
|
||||||
RUN apt-get update -y && apt-get install -y git libzip-dev zip
|
RUN apt-get update -y && apt-get install -y git libzip-dev zip libpng-dev libfreetype6-dev libjpeg62-turbo-dev tesseract-ocr
|
||||||
|
|
||||||
RUN docker-php-ext-install pdo pdo_mysql zip
|
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install pdo pdo_mysql zip gd
|
||||||
|
|
||||||
COPY --from=composer /usr/bin/composer /usr/bin/composer
|
COPY --from=composer /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
6
api/common/Alias/DocumentHandler.php
Normal file
6
api/common/Alias/DocumentHandler.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Alias;
|
||||||
|
|
||||||
|
interface DocumentHandler {
|
||||||
|
public function load(): ?array;
|
||||||
|
}
|
11
api/common/Concept/DocumentHandler.php
Normal file
11
api/common/Concept/DocumentHandler.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Concept;
|
||||||
|
|
||||||
|
use Contabilidad\Common\Alias\DocumentHandler as HandlerInterface;
|
||||||
|
|
||||||
|
abstract class DocumentHandler implements HandlerInterface {
|
||||||
|
protected string $folder;
|
||||||
|
public function __construct(string $source_folder) {
|
||||||
|
$this->folder = $source_folder;
|
||||||
|
}
|
||||||
|
}
|
@ -11,4 +11,24 @@ class Base {
|
|||||||
public function __invoke(Request $request, Response $response): Response {
|
public function __invoke(Request $request, Response $response): Response {
|
||||||
return $this->withJson($response, []);
|
return $this->withJson($response, []);
|
||||||
}
|
}
|
||||||
|
public function generate_key(Request $request, Response $response): Response {
|
||||||
|
$server_addr = explode('.', $request->getServerParams()['SERVER_ADDR']);
|
||||||
|
$remote_addr = explode('.', $request->getServerParams()['REMOTE_ADDR']);
|
||||||
|
for ($i = 0; $i < 3; $i ++) {
|
||||||
|
if ($server_addr[$i] != $remote_addr[$i]) {
|
||||||
|
throw new \InvalidArgumentException('Invalid connection address.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$salt = mt_rand();
|
||||||
|
$signature = hash_hmac('sha256', $salt, 'contabilidad', true);
|
||||||
|
$key = urlencode(base64_encode($signature));
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,43 @@ use Psr\Http\Message\ServerRequestInterface as Request;
|
|||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
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\TiposCambios as Service;
|
||||||
use Contabilidad\Categoria;
|
use Contabilidad\Categoria;
|
||||||
|
|
||||||
class Categorias {
|
class Categorias {
|
||||||
use Json;
|
use Json;
|
||||||
|
|
||||||
public function __invoke(Request $request, Response $response, Factory $factory): Response {
|
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
|
||||||
$categorias = $factory->find(Categoria::class)->array();
|
$categorias = $factory->find(Categoria::class)->many();
|
||||||
if ($categorias) {
|
if ($categorias !== null) {
|
||||||
usort($categorias, function($a, $b) {
|
array_walk($categorias, function(&$item) use ($service) {
|
||||||
return strcmp($a['nombre'], $b['nombre']);
|
$arr = $item->toArray();
|
||||||
});
|
if ($item->cuentas()) {
|
||||||
|
$arr['cuentas'] = array_map(function($item) {
|
||||||
|
return $item->toArray();
|
||||||
|
}, $item->cuentas());
|
||||||
|
}
|
||||||
|
$maps = ['activo', 'pasivo', 'ganancia', 'perdida'];
|
||||||
|
foreach ($maps as $m) {
|
||||||
|
$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) {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@ -68,14 +91,14 @@ class Categorias {
|
|||||||
];
|
];
|
||||||
return $this->withJson($response, $output);
|
return $this->withJson($response, $output);
|
||||||
}
|
}
|
||||||
public function cuentas(Request $request, Response $response, Factory $factory, $categoria_id): Response {
|
public function cuentas(Request $request, Response $response, Factory $factory, Service $service, $categoria_id): Response {
|
||||||
$categoria = $factory->find(Categoria::class)->one($categoria_id);
|
$categoria = $factory->find(Categoria::class)->one($categoria_id);
|
||||||
$cuentas = null;
|
$cuentas = null;
|
||||||
if ($categoria !== null) {
|
if ($categoria !== null) {
|
||||||
$cuentas = $categoria->cuentas();
|
$cuentas = $categoria->cuentas();
|
||||||
if ($cuentas !== null) {
|
if ($cuentas !== null) {
|
||||||
array_walk($cuentas, function(&$item) {
|
array_walk($cuentas, function(&$item) use ($service) {
|
||||||
$item = $item->toArray();
|
$item = $item->toArray($service);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Contabilidad\Common\Controller;
|
namespace Contabilidad\Common\Controller;
|
||||||
|
|
||||||
|
use Contabilidad\Transaccion;
|
||||||
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 Carbon\Carbon;
|
||||||
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\TiposCambios as Service;
|
||||||
use Contabilidad\Cuenta;
|
use Contabilidad\Cuenta;
|
||||||
|
|
||||||
class Cuentas {
|
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']);
|
||||||
|
if ($t != 0) {
|
||||||
|
return $t;
|
||||||
|
}
|
||||||
$c = strcmp($a['categoria']['nombre'], $b['categoria']['nombre']);
|
$c = strcmp($a['categoria']['nombre'], $b['categoria']['nombre']);
|
||||||
if ($c == 0) {
|
if ($c != 0) {
|
||||||
return strcmp($a['nombre'], $b['nombre']);
|
return $c;
|
||||||
}
|
}
|
||||||
return $c;
|
return strcmp($a['nombre'], $b['nombre']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$output = [
|
$output = [
|
||||||
@ -72,6 +84,15 @@ class Cuentas {
|
|||||||
];
|
];
|
||||||
return $this->withJson($response, $output);
|
return $this->withJson($response, $output);
|
||||||
}
|
}
|
||||||
|
public function categoria(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
|
||||||
|
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $cuenta_id,
|
||||||
|
'cuenta' => $cuenta?->toArray(),
|
||||||
|
'categoria' => $cuenta?->categoria()->toArray()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
public function entradas(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
|
public function entradas(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
|
||||||
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
||||||
$entradas = null;
|
$entradas = null;
|
||||||
@ -90,15 +111,48 @@ class Cuentas {
|
|||||||
];
|
];
|
||||||
return $this->withJson($response, $output);
|
return $this->withJson($response, $output);
|
||||||
}
|
}
|
||||||
public function transacciones(Request $request, Response $response, Factory $factory, $cuenta_id, $limit = null, $start = 0): Response {
|
protected function transaccionToArray(Service $service, Cuenta $cuenta, Transaccion $transaccion): array {
|
||||||
|
$arr = $transaccion->toArray();
|
||||||
|
if ($cuenta->moneda()->codigo === 'CLP') {
|
||||||
|
if ($transaccion->debito()->moneda()->codigo !== 'CLP' or $transaccion->credito()->moneda()->codigo !== 'CLP') {
|
||||||
|
if ($transaccion->debito()->moneda()->codigo !== 'CLP') {
|
||||||
|
$c = $transaccion->debito();
|
||||||
|
} else {
|
||||||
|
$c = $transaccion->credito();
|
||||||
|
}
|
||||||
|
$service->get($transaccion->fecha(), $c->moneda()->id);
|
||||||
|
$arr['valor'] = $c->moneda()->cambiar($transaccion->fecha(), $transaccion->valor);
|
||||||
|
$arr['valorFormateado'] = $cuenta->moneda()->format($arr['valor']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$arr['debito']['categoria'] = $transaccion->debito()->categoria()->toArray();
|
||||||
|
$arr['credito']['categoria'] = $transaccion->credito()->categoria()->toArray();
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
public function transacciones(Request $request, Response $response, Factory $factory, Service $service, $cuenta_id, $limit = null, $start = 0): Response {
|
||||||
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
||||||
$transacciones = null;
|
$transacciones = null;
|
||||||
if ($cuenta !== null) {
|
if ($cuenta !== null) {
|
||||||
$transacciones = $cuenta->transacciones($limit, $start);
|
$transacciones = $cuenta->transacciones($limit, $start);
|
||||||
if (count($transacciones)) {
|
if (count($transacciones) > 0) {
|
||||||
array_walk($transacciones, function(&$item) {
|
foreach ($transacciones as &$transaccion) {
|
||||||
$item = $item->toArray();
|
/*$arr = $transaccion->toArray();
|
||||||
});
|
if ($cuenta->moneda()->codigo === 'CLP') {
|
||||||
|
if ($transaccion->debito()->moneda()->codigo !== 'CLP' or $transaccion->credito()->moneda()->codigo !== 'CLP') {
|
||||||
|
if ($transaccion->debito()->moneda()->codigo !== 'CLP') {
|
||||||
|
$c = $transaccion->debito();
|
||||||
|
} else {
|
||||||
|
$c = $transaccion->credito();
|
||||||
|
}
|
||||||
|
$service->get($transaccion->fecha(), $c->moneda()->id);
|
||||||
|
$arr['valor'] = $c->moneda()->cambiar($transaccion->fecha(), $transaccion->valor);
|
||||||
|
$arr['valorFormateado'] = $cuenta->moneda()->format($arr['valor']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$arr['debito']['categoria'] = $transaccion->debito()->categoria()->toArray();
|
||||||
|
$arr['credito']['categoria'] = $transaccion->credito()->categoria()->toArray();*/
|
||||||
|
$transaccion = $this->transaccionToArray($service, $cuenta, $transaccion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$output = [
|
$output = [
|
||||||
@ -108,6 +162,55 @@ class Cuentas {
|
|||||||
];
|
];
|
||||||
return $this->withJson($response, $output);
|
return $this->withJson($response, $output);
|
||||||
}
|
}
|
||||||
|
public function transaccionesMonth(Request $request, Response $response, Factory $factory, Service $service, $cuenta_id, $month): Response {
|
||||||
|
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
||||||
|
$month = Carbon::parse($month);
|
||||||
|
$transacciones = null;
|
||||||
|
if ($cuenta !== null) {
|
||||||
|
$transacciones = $cuenta->transaccionesMonth($month);
|
||||||
|
if (count($transacciones) > 0) {
|
||||||
|
foreach ($transacciones as &$transaccion) {
|
||||||
|
/*$arr = $transaccion->toArray();
|
||||||
|
if ($cuenta->moneda()->codigo === 'CLP') {
|
||||||
|
if ($transaccion->debito()->moneda()->codigo !== 'CLP' or $transaccion->credito()->moneda()->codigo !== 'CLP') {
|
||||||
|
if ($transaccion->debito()->moneda()->codigo !== 'CLP') {
|
||||||
|
$c = $transaccion->debito();
|
||||||
|
} else {
|
||||||
|
$c = $transaccion->credito();
|
||||||
|
}
|
||||||
|
$service->get($transaccion->fecha(), $c->moneda()->id);
|
||||||
|
$arr['valor'] = $c->moneda()->cambiar($transaccion->fecha(), $transaccion->valor);
|
||||||
|
$arr['valorFormateado'] = $cuenta->moneda()->format($arr['valor']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$arr['debito']['categoria'] = $transaccion->debito()->categoria()->toArray();
|
||||||
|
$arr['credito']['categoria'] = $transaccion->credito()->categoria()->toArray();*/
|
||||||
|
$transaccion = $this->transaccionToArray($service, $cuenta, $transaccion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => compact('cuenta_id', 'month'),
|
||||||
|
'cuenta' => $cuenta?->toArray(),
|
||||||
|
'transacciones' => $transacciones
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function transaccionesAcumulation(Request $request, Response $response, Factory $factory, $cuenta_id, $date): Response {
|
||||||
|
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
||||||
|
$f = Carbon::parse($date);
|
||||||
|
$acum = 0;
|
||||||
|
if ($cuenta !== null) {
|
||||||
|
$acum = $cuenta->acumulacion($f);
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => compact('cuenta_id', 'date'),
|
||||||
|
'cuenta' => $cuenta?->toArray(),
|
||||||
|
'format' => $cuenta->moneda()->toArray(),
|
||||||
|
'acumulation' => $acum
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
public function transaccionesAmount(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
|
public function transaccionesAmount(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
|
||||||
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
|
||||||
$transacciones = 0;
|
$transacciones = 0;
|
||||||
|
76
api/common/Controller/Files.php
Normal file
76
api/common/Controller/Files.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Controller;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use ProVM\Common\Define\Controller\Json;
|
||||||
|
use Contabilidad\Common\Service\FileHandler as Handler;
|
||||||
|
use ProVM\Common\Factory\Model as Factory;
|
||||||
|
use Contabilidad\Cuenta;
|
||||||
|
|
||||||
|
class Files {
|
||||||
|
use Json;
|
||||||
|
|
||||||
|
public function __invoke(Request $request, Response $response, Handler $handler): Response {
|
||||||
|
$files = $handler->listFiles();
|
||||||
|
usort($files, function($a, $b) {
|
||||||
|
$f = strcmp($a->folder, $b->folder);
|
||||||
|
if ($f == 0) {
|
||||||
|
return strcmp($a->filename, $b->filename);
|
||||||
|
}
|
||||||
|
return $f;
|
||||||
|
});
|
||||||
|
return $this->withJson($response, compact('files'));
|
||||||
|
}
|
||||||
|
public function upload(Request $request, Response $response, Handler $handler, Factory $factory): Response {
|
||||||
|
$post = $request->getParsedBody();
|
||||||
|
$cuenta = $factory->find(Cuenta::class)->one($post['cuenta']);
|
||||||
|
$file = $request->getUploadedFiles()['archivo'];
|
||||||
|
$new_name = implode(' - ', [$cuenta->nombre, $cuenta->categoria()->nombre, $post['fecha']]);
|
||||||
|
$output = [
|
||||||
|
'input' => [
|
||||||
|
'name' => $file->getClientFilename(),
|
||||||
|
'type' => $file->getClientMediaType(),
|
||||||
|
'size' => $file->getSize(),
|
||||||
|
'error' => $file->getError()
|
||||||
|
],
|
||||||
|
'new_name' => $new_name,
|
||||||
|
'uploaded' => $handler->uploadFile($file, $new_name)
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function get(Request $request, Response $response, Handler $handler, $folder, $filename): Response {
|
||||||
|
$file = $handler->getFile($folder, $filename);
|
||||||
|
return $response
|
||||||
|
->withHeader('Content-Type', $handler->getType($folder))
|
||||||
|
->withHeader('Content-Disposition', 'attachment; filename=' . $filename)
|
||||||
|
->withAddedHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||||
|
->withHeader('Cache-Control', 'post-check=0, pre-check=0')
|
||||||
|
->withHeader('Pragma', 'no-cache')
|
||||||
|
->withBody($file);
|
||||||
|
}
|
||||||
|
public function edit(Request $request, Response $response, Handler $handler, Factory $factory, $folder, $filename): Response {
|
||||||
|
$post = json_decode($request->getBody());
|
||||||
|
$cuenta = $factory->find(Cuenta::class)->one($post->cuenta);
|
||||||
|
$new_name = implode(' - ', [$cuenta->nombre, $cuenta->categoria()->nombre, $post->fecha]);
|
||||||
|
$output = [
|
||||||
|
'input' => [
|
||||||
|
'folder' => $folder,
|
||||||
|
'filename' => $filename,
|
||||||
|
'post' => $post
|
||||||
|
],
|
||||||
|
'edited' => $handler->editFilename($folder, $filename, $new_name)
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function delete(Request $request, Response $response, Handler $handler, $folder, $filename): Response {
|
||||||
|
$output = [
|
||||||
|
'input' => [
|
||||||
|
'folder' => $folder,
|
||||||
|
'filename' => $filename
|
||||||
|
],
|
||||||
|
'deleted' => $handler->deleteFile($folder, $filename)
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
}
|
@ -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\PdfHandler;
|
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, PdfHandler $handler): Response {
|
public function uploads(Request $request, Response $response, Handler $handler): Response {
|
||||||
$output = $handler->load();
|
$output = $handler->handle();
|
||||||
return $this->withJson($response, $output);
|
return $this->withJson($response, $output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
api/common/Controller/Monedas.php
Normal file
71
api/common/Controller/Monedas.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Controller;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use ProVM\Common\Define\Controller\Json;
|
||||||
|
use ProVM\Common\Factory\Model as Factory;
|
||||||
|
use Contabilidad\Moneda;
|
||||||
|
|
||||||
|
class Monedas {
|
||||||
|
use Json;
|
||||||
|
|
||||||
|
public function __invoke(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$monedas = $factory->find(Moneda::class)->array();
|
||||||
|
if ($monedas) {
|
||||||
|
usort($monedas, function($a, $b) {
|
||||||
|
return strcmp($a['denominacion'], $b['denominacion']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'monedas' => $monedas
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function show(Request $request, Response $response, Factory $factory, $moneda_id): Response {
|
||||||
|
$moneda = $factory->find(Moneda::class)->one($moneda_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $moneda_id,
|
||||||
|
'moneda' => $moneda?->toArray()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function add(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$results = [];
|
||||||
|
if (is_array($input)) {
|
||||||
|
foreach ($input as $in) {
|
||||||
|
$moneda = Moneda::add($factory, $in);
|
||||||
|
$results []= ['moneda' => $moneda?->toArray(), 'agregado' => $moneda?->save()];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$moneda = Moneda::add($factory, $input);
|
||||||
|
$results []= ['moneda' => $moneda?->toArray(), 'agregado' => $moneda?->save()];
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => $input,
|
||||||
|
'monedas' => $results
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function edit(Request $request, Response $response, Factory $factory, $moneda_id): Response {
|
||||||
|
$moneda = $factory->find(Moneda::class)->one($moneda_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $moneda_id,
|
||||||
|
'old' => $moneda->toArray()
|
||||||
|
];
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$moneda->edit($input);
|
||||||
|
$output['moneda'] = $moneda->toArray();
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function delete(Request $request, Response $response, Factory $factory, $moneda_id): Response {
|
||||||
|
$moneda = $factory->find(Moneda::class)->one($moneda_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $moneda_id,
|
||||||
|
'moneda' => $moneda->toArray(),
|
||||||
|
'eliminado' => $moneda->delete()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
}
|
95
api/common/Controller/TiposCambios.php
Normal file
95
api/common/Controller/TiposCambios.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Controller;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use ProVM\Common\Define\Controller\Json;
|
||||||
|
use ProVM\Common\Factory\Model as Factory;
|
||||||
|
use Contabilidad\Common\Service\TiposCambios as Service;
|
||||||
|
use Contabilidad\TipoCambio;
|
||||||
|
|
||||||
|
class TiposCambios {
|
||||||
|
use Json;
|
||||||
|
|
||||||
|
public function __invoke(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$tipos = $factory->find(TipoCambio::class)->array();
|
||||||
|
if ($tipos) {
|
||||||
|
usort($tipos, function($a, $b) {
|
||||||
|
return strcmp($a['fecha'], $b['fecha']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'tipos' => $tipos
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function show(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCambio::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'tipo' => $tipo?->toArray()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function add(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$results = [];
|
||||||
|
if (is_array($input)) {
|
||||||
|
foreach ($input as $in) {
|
||||||
|
$tipo = TipoCambio::add($factory, $in);
|
||||||
|
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$tipo = TipoCambio::add($factory, $input);
|
||||||
|
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => $input,
|
||||||
|
'tipos' => $results
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function edit(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCambio::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'old' => $tipo->toArray()
|
||||||
|
];
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$tipo->edit($input);
|
||||||
|
$output['tipo'] = $tipo->toArray();
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function delete(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCambio::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'tipo' => $tipo->toArray(),
|
||||||
|
'eliminado' => $tipo->delete()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function obtain(Request $request, Response $response, Factory $factory, Service $service): Response {
|
||||||
|
$post = $request->getParsedBody();
|
||||||
|
$valor = $service->get($post['fecha'], $post['moneda_id']);
|
||||||
|
if ($valor === null) {
|
||||||
|
return $this->withJson($response, ['input' => $post, 'tipo' => null, 'error' => 'No se encontró valor']);
|
||||||
|
}
|
||||||
|
$data = [
|
||||||
|
'fecha' => $post['fecha'],
|
||||||
|
'desde_id' => $post['moneda_id'],
|
||||||
|
'hasta_id' => 1,
|
||||||
|
'valor' => $valor
|
||||||
|
];
|
||||||
|
$tipo = TipoCambio::add($factory, $data);
|
||||||
|
if ($tipo !== false and $tipo->is_new()) {
|
||||||
|
$tipo->save();
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => $post,
|
||||||
|
'tipo' => $tipo?->toArray()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
}
|
132
api/common/Controller/TiposCategorias.php
Normal file
132
api/common/Controller/TiposCategorias.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Controller;
|
||||||
|
|
||||||
|
use Contabilidad\TipoCuenta;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use ProVM\Common\Define\Controller\Json;
|
||||||
|
use ProVM\Common\Factory\Model as Factory;
|
||||||
|
use Contabilidad\Common\Service\TiposCambios as Service;
|
||||||
|
use Contabilidad\TipoCategoria;
|
||||||
|
|
||||||
|
class TiposCategorias {
|
||||||
|
use Json;
|
||||||
|
|
||||||
|
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
|
||||||
|
$tipos = $factory->find(TipoCategoria::class)->many();
|
||||||
|
if ($tipos !== null) {
|
||||||
|
array_walk($tipos, function(&$item) use ($service) {
|
||||||
|
$arr = $item->toArray();
|
||||||
|
$arr['categorias'] = $item->categorias();
|
||||||
|
if ($arr['categorias'] !== null) {
|
||||||
|
$arr['categorias'] = array_map(function($item) {
|
||||||
|
return $item->toArray();
|
||||||
|
}, $item->categorias());
|
||||||
|
}
|
||||||
|
$arr['saldo'] = abs($item->saldo($service));
|
||||||
|
$arr['totales'] = $item->getTotales($service);
|
||||||
|
$item = $arr;
|
||||||
|
});
|
||||||
|
usort($tipos, function($a, $b) {
|
||||||
|
return strcmp($a['descripcion'], $b['descripcion']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'tipos' => $tipos
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function add(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$results = [];
|
||||||
|
if (is_array($input)) {
|
||||||
|
foreach ($input as $in) {
|
||||||
|
$tipo = TipoCategoria::add($factory, $in);
|
||||||
|
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$tipo = TipoCategoria::add($factory, $input);
|
||||||
|
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => $input,
|
||||||
|
'tipos' => $results
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function edit(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCategoria::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'old' => $tipo->toArray()
|
||||||
|
];
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$tipo->edit($input);
|
||||||
|
$output['tipo'] = $tipo->toArray();
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function delete(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCategoria::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'tipo' => $tipo->toArray(),
|
||||||
|
'eliminado' => $tipo->delete()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function categorias(Request $request, Response $response, Factory $factory, Service $service, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCategoria::class)->one($tipo_id);
|
||||||
|
$categorias = null;
|
||||||
|
if ($tipo != null) {
|
||||||
|
$categorias = $tipo->categorias();
|
||||||
|
if ($categorias !== null) {
|
||||||
|
array_walk($categorias, function(&$item) use ($service) {
|
||||||
|
$arr = $item->toArray($service);
|
||||||
|
$arr['totales'] = $item->getTotales($service);
|
||||||
|
$item = $arr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'tipo' => $tipo?->toArray(),
|
||||||
|
'categorias' => $categorias
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function balance(Request $request, Response $response, Factory $factory, Service $service): Response {
|
||||||
|
$tipos = $factory->find(TipoCategoria::class)->many();
|
||||||
|
$balance = array_reduce($tipos, function($sum, $item) use ($service) {
|
||||||
|
$totales = $item->getTotales($service);
|
||||||
|
if (!is_array($sum)) {
|
||||||
|
$sum = [];
|
||||||
|
}
|
||||||
|
foreach ($totales as $p => $total) {
|
||||||
|
if (!isset($sum[$p])) {
|
||||||
|
$sum[$p] = 0;
|
||||||
|
}
|
||||||
|
$sum[$p] += $total;
|
||||||
|
}
|
||||||
|
return $sum;
|
||||||
|
});
|
||||||
|
/*$balance = array_reduce($tipos, function($sum, $item) use ($service) {
|
||||||
|
$maps = ['activo', 'pasivo', 'ganancia', 'perdida'];
|
||||||
|
foreach ($maps as $m) {
|
||||||
|
$p = $m . 's';
|
||||||
|
$t = ucfirst($m);
|
||||||
|
if (!isset($sum[$p])) {
|
||||||
|
$sum[$p] = 0;
|
||||||
|
}
|
||||||
|
$cuentas = $item->getCuentasOf($t);
|
||||||
|
if ($cuentas === false or $cuentas === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$sum[$p] += array_reduce($cuentas, function($sum, $item) use($service) {
|
||||||
|
return $sum + $item->saldo($service, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return $sum;
|
||||||
|
});*/
|
||||||
|
return $this->withJson($response, $balance);
|
||||||
|
}
|
||||||
|
}
|
71
api/common/Controller/TiposCuentas.php
Normal file
71
api/common/Controller/TiposCuentas.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Controller;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use ProVM\Common\Define\Controller\Json;
|
||||||
|
use ProVM\Common\Factory\Model as Factory;
|
||||||
|
use Contabilidad\TipoCuenta;
|
||||||
|
|
||||||
|
class TiposCuentas {
|
||||||
|
use Json;
|
||||||
|
|
||||||
|
public function __invoke(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$tipos = $factory->find(TipoCuenta::class)->array();
|
||||||
|
if ($tipos) {
|
||||||
|
usort($tipos, function($a, $b) {
|
||||||
|
return strcmp($a['descripcion'], $b['descripcion']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'tipos' => $tipos
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function show(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCuenta::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'tipo' => $tipo?->toArray()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function add(Request $request, Response $response, Factory $factory): Response {
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$results = [];
|
||||||
|
if (is_array($input)) {
|
||||||
|
foreach ($input as $in) {
|
||||||
|
$tipo = TipoCuenta::add($factory, $in);
|
||||||
|
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$tipo = TipoCuenta::add($factory, $input);
|
||||||
|
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
|
||||||
|
}
|
||||||
|
$output = [
|
||||||
|
'input' => $input,
|
||||||
|
'tipos' => $results
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function edit(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCuenta::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'old' => $tipo->toArray()
|
||||||
|
];
|
||||||
|
$input = json_decode($request->getBody());
|
||||||
|
$tipo->edit($input);
|
||||||
|
$output['tipo'] = $tipo->toArray();
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
public function delete(Request $request, Response $response, Factory $factory, $tipo_id): Response {
|
||||||
|
$tipo = $factory->find(TipoCuenta::class)->one($tipo_id);
|
||||||
|
$output = [
|
||||||
|
'input' => $tipo_id,
|
||||||
|
'tipo' => $tipo->toArray(),
|
||||||
|
'eliminado' => $tipo->delete()
|
||||||
|
];
|
||||||
|
return $this->withJson($response, $output);
|
||||||
|
}
|
||||||
|
}
|
29
api/common/Middleware/Auth.php
Normal file
29
api/common/Middleware/Auth.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Middleware;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ResponseFactoryInterface as Factory;
|
||||||
|
use Contabilidad\Common\Service\Auth as Service;
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
protected Factory $factory;
|
||||||
|
protected Service $service;
|
||||||
|
public function __construct(Factory $factory, Service $service) {
|
||||||
|
$this->factory = $factory;
|
||||||
|
$this->service = $service;
|
||||||
|
}
|
||||||
|
public function __invoke(Request $request, Handler $handler): Response {
|
||||||
|
if ($request->getMethod() == 'OPTIONS') {
|
||||||
|
return $handler->handle($request);
|
||||||
|
}
|
||||||
|
if (!$this->service->isValid($request)) {
|
||||||
|
$response = $this->factory->createResponse(401);
|
||||||
|
$response->getBody()->write(json_encode(['message' => 'Invalid API KEY.']));
|
||||||
|
return $response
|
||||||
|
->withHeader('Content-Type', 'application/json');
|
||||||
|
}
|
||||||
|
return $handler->handle($request);
|
||||||
|
}
|
||||||
|
}
|
49
api/common/Service/Auth.php
Normal file
49
api/common/Service/Auth.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
protected string $key;
|
||||||
|
public function __construct(string $api_key) {
|
||||||
|
$this->key = $api_key;
|
||||||
|
}
|
||||||
|
public function isValid(Request $request): bool {
|
||||||
|
if ($request->hasHeader('Authorization')) {
|
||||||
|
$sent_key = $this->getAuthKey($request->getHeader('Authorization'));
|
||||||
|
return $this->key == $sent_key;
|
||||||
|
}
|
||||||
|
if (isset($request->getParsedBody()['api_key'])) {
|
||||||
|
$sent_key = $request->getParsedBody()['api_key'];
|
||||||
|
return $this->key == $sent_key;
|
||||||
|
}
|
||||||
|
$post = $request->getParsedBody() ?? json_decode($request->getBody());
|
||||||
|
$sent_key = $this->getArrayKey($post);
|
||||||
|
if ($sent_key !== null) {
|
||||||
|
return $this->key == $sent_key;
|
||||||
|
}
|
||||||
|
$sent_key = $this->getArrayKey($request->getQueryParams());
|
||||||
|
return $this->key == $sent_key;
|
||||||
|
}
|
||||||
|
protected function getAuthKey($auth) {
|
||||||
|
if (is_array($auth)) {
|
||||||
|
$auth = $auth[0];
|
||||||
|
}
|
||||||
|
if (str_contains($auth, 'Bearer')) {
|
||||||
|
$auth = explode(' ', $auth)[1];
|
||||||
|
}
|
||||||
|
return $auth;
|
||||||
|
}
|
||||||
|
protected function getArrayKey($array) {
|
||||||
|
$posible_keys = [
|
||||||
|
'API_KEY',
|
||||||
|
'api_key',
|
||||||
|
];
|
||||||
|
foreach ($posible_keys as $key) {
|
||||||
|
if (isset($array[$key])) {
|
||||||
|
return $array[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
37
api/common/Service/CsvHandler.php
Normal file
37
api/common/Service/CsvHandler.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
use Contabilidad\Common\Concept\DocumentHandler;
|
||||||
|
|
||||||
|
class CsvHandler extends DocumentHandler {
|
||||||
|
public function load(): ?array {
|
||||||
|
$folder = $this->folder;
|
||||||
|
$files = new \DirectoryIterator($folder);
|
||||||
|
$output = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir() or $file->getExtension() != 'csv') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$bank = 'unknown';
|
||||||
|
$text = trim(file_get_contents($file->getRealPath()));
|
||||||
|
if (str_contains($text, 'SCOTIABANK')) {
|
||||||
|
$bank = 'Scotiabank';
|
||||||
|
}
|
||||||
|
if (str_contains($text, 'BICE')) {
|
||||||
|
$bank = 'BICE';
|
||||||
|
}
|
||||||
|
$data = explode(PHP_EOL, $text);
|
||||||
|
array_walk($data, function(&$item) {
|
||||||
|
$item = trim($item, '; ');
|
||||||
|
if (str_contains($item, ';') !== false) {
|
||||||
|
$item = explode(';', $item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$output []= ['bank' => $bank, 'filename' => $file->getBasename(), 'data' => $data];
|
||||||
|
}
|
||||||
|
return $this->build($output);
|
||||||
|
}
|
||||||
|
protected function build(array $data): ?array {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
16
api/common/Service/DocumentHandler.php
Normal file
16
api/common/Service/DocumentHandler.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
class DocumentHandler {
|
||||||
|
protected array $handlers;
|
||||||
|
public function __construct(array $handlers) {
|
||||||
|
$this->handlers = $handlers;
|
||||||
|
}
|
||||||
|
public function handle(): array {
|
||||||
|
$output = [];
|
||||||
|
foreach ($this->handlers as $handler) {
|
||||||
|
$output = array_merge($output, $handler->load());
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
112
api/common/Service/FileHandler.php
Normal file
112
api/common/Service/FileHandler.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
use Psr\Http\Message\UploadedFileInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Nyholm\Psr7\Stream;
|
||||||
|
|
||||||
|
class FileHandler {
|
||||||
|
protected $base_folder;
|
||||||
|
protected $valid_types;
|
||||||
|
protected $folders;
|
||||||
|
public function __construct(object $params) {
|
||||||
|
$this->base_folder = $params->folder;
|
||||||
|
$this->addValidTypes(array_keys($params->types));
|
||||||
|
$this->addFolders($params->types);
|
||||||
|
}
|
||||||
|
public function addFolders(array $folders): FileHandler {
|
||||||
|
foreach ($folders as $type => $folder) {
|
||||||
|
$this->addFolder($type, $folder);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function addFolder(string $type, string $folder): FileHandler {
|
||||||
|
$this->folders[$type] = $folder;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function addValidTypes(array $valid_types): FileHandler {
|
||||||
|
foreach ($valid_types as $type) {
|
||||||
|
$this->addValidType($type);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function addValidType(string $type): FileHandler {
|
||||||
|
$this->valid_types []= $type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
public function getType(string $folder): string {
|
||||||
|
return array_search($folder, $this->folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadFile(UploadedFileInterface $file, string $new_name = null): bool {
|
||||||
|
if ($file->getError() !== UPLOAD_ERR_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!in_array($file->getClientMediaType(), $this->valid_types)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($new_name === null) {
|
||||||
|
$new_name = $file->getClientFilename();
|
||||||
|
}
|
||||||
|
$filenfo = new \SplFileInfo($file->getClientFilename());
|
||||||
|
if (!str_contains($new_name, $filenfo->getExtension())) {
|
||||||
|
$new_name .= '.' . $filenfo->getExtension();
|
||||||
|
}
|
||||||
|
$to = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $this->folders[$file->getClientMediaType()], $new_name]);
|
||||||
|
$file->moveTo($to);
|
||||||
|
return file_exists($to);
|
||||||
|
}
|
||||||
|
public function listFiles(): array {
|
||||||
|
$output = [];
|
||||||
|
foreach ($this->folders as $f) {
|
||||||
|
$folder = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $f]);
|
||||||
|
if (!file_exists($folder)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$files = new \DirectoryIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$output []= (object) ['folder' => $f, 'filename' => $file->getBasename()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
protected function validateFilename(string $folder, string $filename): bool|string {
|
||||||
|
if (!in_array($folder, $this->folders)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$f = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $folder, $filename]);
|
||||||
|
if (!file_exists($f)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $f;
|
||||||
|
}
|
||||||
|
public function getInfo(string $folder, string $filename): \SplFileInfo|bool {
|
||||||
|
if (!$f = $this->validateFilename($folder, $filename)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return new \SplFileInfo($f);
|
||||||
|
}
|
||||||
|
public function getFile(string $folder, string $filename): StreamInterface|bool {
|
||||||
|
if (!$f = $this->validateFilename($folder, $filename)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Stream::create(file_get_contents($f));
|
||||||
|
}
|
||||||
|
public function editFilename(string $folder, string $filename, string $new_name): bool {
|
||||||
|
if (!$f = $this->validateFilename($folder, $filename)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$info = new \SplFileInfo($f);
|
||||||
|
$new = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $folder, $new_name . '.' . $info->getExtension()]);
|
||||||
|
return rename($f, $new);
|
||||||
|
}
|
||||||
|
public function deleteFile(string $folder, string $filename): bool {
|
||||||
|
if (!$f = $this->validateFilename($folder, $filename)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return unlink($f);
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Contabilidad\Common\Service;
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
use Contabilidad\Common\Concept\DocumentHandler;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
class PdfHandler {
|
class PdfHandler extends DocumentHandler {
|
||||||
protected Client $client;
|
protected Client $client;
|
||||||
protected string $folder;
|
|
||||||
protected string $url;
|
protected string $url;
|
||||||
public function __construct(Client $client, string $pdf_folder, string $url) {
|
public function __construct(Client $client, string $pdf_folder, string $url) {
|
||||||
|
parent::__construct($pdf_folder);
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->folder = $pdf_folder;
|
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +25,51 @@ class PdfHandler {
|
|||||||
}
|
}
|
||||||
$response = $this->client->post($this->url, ['json' => ['files' => $output]]);
|
$response = $this->client->post($this->url, ['json' => ['files' => $output]]);
|
||||||
$output = json_decode($response->getBody());
|
$output = json_decode($response->getBody());
|
||||||
return $output;
|
return $this->build($output);
|
||||||
|
}
|
||||||
|
protected function build(array $data): ?array {
|
||||||
|
foreach ($data as &$file) {
|
||||||
|
$i = $this->findStartRow($file->text);
|
||||||
|
if ($i === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$e = $this->findEndRow($file->text, $i);
|
||||||
|
if ($e == $i) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$file->data = array_filter($file->text, function($key) use ($i, $e) {
|
||||||
|
return ($key >= $i) and ($key <= $e);
|
||||||
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
protected function findStartRow(array $data): int {
|
||||||
|
foreach ($data as $i => $row) {
|
||||||
|
if (!is_array($row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$maybe = false;
|
||||||
|
foreach ($row as $cell) {
|
||||||
|
if (str_contains($cell, '/')) {
|
||||||
|
$maybe = true;
|
||||||
|
}
|
||||||
|
if ($maybe and str_contains($cell, '$')) {
|
||||||
|
return $i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
protected function findEndRow(array $data, int $start): int {
|
||||||
|
$l = count($data[$start]);
|
||||||
|
for ($i = $start; $i < count($data); $i ++) {
|
||||||
|
if (!is_array($data[$i])) {
|
||||||
|
return $i - 1;
|
||||||
|
}
|
||||||
|
if (count($data[$i]) != $l) {
|
||||||
|
return $i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $start;
|
||||||
}
|
}
|
||||||
}
|
}
|
93
api/common/Service/TiposCambios.php
Normal file
93
api/common/Service/TiposCambios.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use GuzzleHttp\Exception\ServerException;
|
||||||
|
use ProVM\Common\Factory\Model as Factory;
|
||||||
|
use Contabilidad\Moneda;
|
||||||
|
use Contabilidad\TipoCambio;
|
||||||
|
|
||||||
|
class TiposCambios {
|
||||||
|
protected $client;
|
||||||
|
protected $factory;
|
||||||
|
protected $base_url;
|
||||||
|
protected $key;
|
||||||
|
public function __construct(Client $client, Factory $factory, $api_url, $api_key) {
|
||||||
|
$this->client = $client;
|
||||||
|
$this->factory = $factory;
|
||||||
|
$this->base_url = $api_url;
|
||||||
|
$this->key = $api_key;
|
||||||
|
}
|
||||||
|
protected function getWeekday(\DateTimeInterface $fecha) {
|
||||||
|
if ($fecha->weekday() == 0) {
|
||||||
|
return $fecha->subWeek()->weekday(5);
|
||||||
|
}
|
||||||
|
if ($fecha->weekday() == 6) {
|
||||||
|
return $fecha->weekday(5);
|
||||||
|
}
|
||||||
|
return $fecha;
|
||||||
|
}
|
||||||
|
protected function getValor(\DateTimeInterface $fecha, string $moneda_codigo) {
|
||||||
|
$data = [
|
||||||
|
'fecha' => $fecha->format('Y-m-d'),
|
||||||
|
'desde' => $moneda_codigo
|
||||||
|
];
|
||||||
|
$headers = [
|
||||||
|
'Authorization' => "Bearer {$this->key}"
|
||||||
|
];
|
||||||
|
$url = implode('/', [
|
||||||
|
$this->base_url,
|
||||||
|
'cambio',
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
try {
|
||||||
|
$response = $this->client->request('POST', $url, ['json' => $data, 'headers' => $headers]);
|
||||||
|
} catch (ConnectException | RequestException | ServerException $e) {
|
||||||
|
error_log($e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ($response->getStatusCode() !== 200) {
|
||||||
|
error_log('Could not connect to python API.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$result = json_decode($response->getBody());
|
||||||
|
if (isset($result->message) and $result->message === 'Not Authorized') {
|
||||||
|
error_log('Not authorized for connecting to python API.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $result->serie[0]->valor;
|
||||||
|
}
|
||||||
|
public function get(string $fecha, int $moneda_id) {
|
||||||
|
$fecha = Carbon::parse($fecha);
|
||||||
|
$moneda = $this->factory->find(Moneda::class)->one($moneda_id);
|
||||||
|
if ($moneda->codigo == 'USD') {
|
||||||
|
$fecha = $this->getWeekday($fecha);
|
||||||
|
}
|
||||||
|
// If a value exists in the database
|
||||||
|
$cambio = $moneda->cambio($fecha);
|
||||||
|
if ($cambio !== null) {
|
||||||
|
if ($cambio->desde()->id != $moneda->id) {
|
||||||
|
return 1 / $cambio->valor;
|
||||||
|
}
|
||||||
|
return $cambio->valor;
|
||||||
|
}
|
||||||
|
$valor = $this->getValor($fecha, $moneda->codigo);
|
||||||
|
if ($valor === null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
$data = [
|
||||||
|
'fecha' => $fecha->format('Y-m-d H:i:s'),
|
||||||
|
'desde_id' => $moneda->id,
|
||||||
|
'hasta_id' => 1,
|
||||||
|
'valor' => $valor
|
||||||
|
];
|
||||||
|
$tipo = TipoCambio::add($this->factory, $data);
|
||||||
|
if ($tipo !== false and $tipo->is_new()) {
|
||||||
|
$tipo->save();
|
||||||
|
}
|
||||||
|
return $valor;
|
||||||
|
}
|
||||||
|
}
|
60
api/common/Service/XlsHandler.php
Normal file
60
api/common/Service/XlsHandler.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad\Common\Service;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
|
||||||
|
use thiagoalessio\TesseractOCR\TesseractOCR;
|
||||||
|
use Contabilidad\Common\Concept\DocumentHandler;
|
||||||
|
|
||||||
|
class XlsHandler extends DocumentHandler {
|
||||||
|
public function load(): ?array {
|
||||||
|
$folder = $this->folder;
|
||||||
|
$files = new \DirectoryIterator($folder);
|
||||||
|
$output = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir() or $file->getExtension() != 'xls') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$reader = IOFactory::createReader(ucfirst($file->getExtension()));
|
||||||
|
$xls = $reader->load($file->getRealPath());
|
||||||
|
$data = [];
|
||||||
|
$bank = 'unknown';
|
||||||
|
for ($s = 0; $s < $xls->getSheetCount(); $s ++) {
|
||||||
|
$sheet = $xls->getSheet($s);
|
||||||
|
foreach ($sheet->getRowIterator() as $row) {
|
||||||
|
$r = [];
|
||||||
|
foreach ($row->getCellIterator() as $cell) {
|
||||||
|
$r []= $cell->getValue();
|
||||||
|
}
|
||||||
|
$data []= $r;
|
||||||
|
}
|
||||||
|
foreach ($sheet->getDrawingCollection() as $drawing) {
|
||||||
|
if ($drawing instanceof MemoryDrawing) {
|
||||||
|
ob_start();
|
||||||
|
call_user_func(
|
||||||
|
$drawing->getRenderingFunction(),
|
||||||
|
$drawing->getImageResource()
|
||||||
|
);
|
||||||
|
$imageContents = ob_get_contents();
|
||||||
|
$size = ob_get_length();
|
||||||
|
ob_end_clean();
|
||||||
|
$ocr = new TesseractOCR();
|
||||||
|
$ocr->imageData($imageContents, $size);
|
||||||
|
$image = $ocr->run();
|
||||||
|
if (str_contains($image, 'BICE')) {
|
||||||
|
$bank = 'BICE';
|
||||||
|
}
|
||||||
|
if (str_contains($image, 'Scotiabank')) {
|
||||||
|
$bank = 'Scotiabank';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$output []= ['bank' => $bank, 'filename' => $file->getBasename(), 'data' => $data];
|
||||||
|
}
|
||||||
|
return $this->build($output);
|
||||||
|
}
|
||||||
|
protected function build(array $data): ?array {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,9 @@
|
|||||||
"robmorgan/phinx": "^0.12.9",
|
"robmorgan/phinx": "^0.12.9",
|
||||||
"odan/phinx-migrations-generator": "^5.4",
|
"odan/phinx-migrations-generator": "^5.4",
|
||||||
"martin-mikac/csv-to-phinx-seeder": "^1.6",
|
"martin-mikac/csv-to-phinx-seeder": "^1.6",
|
||||||
"guzzlehttp/guzzle": "^7.4"
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
|
"phpoffice/phpspreadsheet": "^1.19",
|
||||||
|
"thiagoalessio/tesseract_ocr": "^2.12"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
|
@ -20,6 +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, 'default' => 'ffffff'])
|
||||||
->create();
|
->create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
api/db/migrations/20211204205950_moneda.php
Normal file
29
api/db/migrations/20211204205950_moneda.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class Moneda extends AbstractMigration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Change Method.
|
||||||
|
*
|
||||||
|
* Write your reversible migrations using this method.
|
||||||
|
*
|
||||||
|
* More information on writing migrations is available here:
|
||||||
|
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||||
|
*
|
||||||
|
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||||
|
* with the Table class.
|
||||||
|
*/
|
||||||
|
public function change(): void
|
||||||
|
{
|
||||||
|
$this->table('monedas')
|
||||||
|
->addColumn('denominacion', 'string')
|
||||||
|
->addColumn('codigo', 'string', ['length' => 3])
|
||||||
|
->addColumn('prefijo', 'string', ['default' => ''])
|
||||||
|
->addColumn('sufijo', 'string', ['default' => ''])
|
||||||
|
->addColumn('decimales', 'integer', ['default' => 0])
|
||||||
|
->create();
|
||||||
|
}
|
||||||
|
}
|
26
api/db/migrations/20211204210207_cuenta_moneda.php
Normal file
26
api/db/migrations/20211204210207_cuenta_moneda.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class CuentaMoneda extends AbstractMigration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Change Method.
|
||||||
|
*
|
||||||
|
* Write your reversible migrations using this method.
|
||||||
|
*
|
||||||
|
* More information on writing migrations is available here:
|
||||||
|
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||||
|
*
|
||||||
|
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||||
|
* with the Table class.
|
||||||
|
*/
|
||||||
|
public function change(): void
|
||||||
|
{
|
||||||
|
$this->table('cuentas')
|
||||||
|
->addColumn('moneda_id', 'integer')
|
||||||
|
->addForeignKey('moneda_id', 'monedas')
|
||||||
|
->update();
|
||||||
|
}
|
||||||
|
}
|
30
api/db/migrations/20211205002439_tipo_cambio.php
Normal file
30
api/db/migrations/20211205002439_tipo_cambio.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class TipoCambio extends AbstractMigration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Change Method.
|
||||||
|
*
|
||||||
|
* Write your reversible migrations using this method.
|
||||||
|
*
|
||||||
|
* More information on writing migrations is available here:
|
||||||
|
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||||
|
*
|
||||||
|
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||||
|
* with the Table class.
|
||||||
|
*/
|
||||||
|
public function change(): void
|
||||||
|
{
|
||||||
|
$this->table('tipos_cambio')
|
||||||
|
->addColumn('fecha', 'datetime')
|
||||||
|
->addColumn('desde_id', 'integer')
|
||||||
|
->addForeignKey('desde_id', 'monedas')
|
||||||
|
->addColumn('hasta_id', 'integer')
|
||||||
|
->addForeignKey('hasta_id', 'monedas')
|
||||||
|
->addColumn('valor', 'double')
|
||||||
|
->create();
|
||||||
|
}
|
||||||
|
}
|
41
api/db/seeds/Moneda.php
Normal file
41
api/db/seeds/Moneda.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
use Phinx\Seed\AbstractSeed;
|
||||||
|
|
||||||
|
class Moneda extends AbstractSeed
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run Method.
|
||||||
|
*
|
||||||
|
* Write your database seeder using this method.
|
||||||
|
*
|
||||||
|
* More information on writing seeders is available here:
|
||||||
|
* https://book.cakephp.org/phinx/0/en/seeding.html
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
[
|
||||||
|
'denominacion' => 'Pesos Chilenos',
|
||||||
|
'codigo' => 'CLP',
|
||||||
|
'prefijo' => '$ '
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'denominacion' => 'Dólar',
|
||||||
|
'codigo' => 'USD',
|
||||||
|
'prefijo' => 'US$ ',
|
||||||
|
'decimales' => 2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'denominacion' => 'Unidad de Fomento',
|
||||||
|
'codigo' => 'CLF',
|
||||||
|
'sufijo' => ' UF',
|
||||||
|
'decimales' => 2
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$this->table('monedas')
|
||||||
|
->insert($data)
|
||||||
|
->saveData();
|
||||||
|
}
|
||||||
|
}
|
@ -5,25 +5,26 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
|
|
||||||
if ($request_method = 'OPTIONS') {
|
if ($request_method = 'OPTIONS') {
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
|
||||||
add_header 'Access-Control-Allow-Origin' '*';
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,
|
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||||
X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||||
|
add_header 'Access-Control-Max-Age' 1728000;
|
||||||
add_header 'Content-Type' 'application/json';
|
add_header 'Content-Type' 'application/json';
|
||||||
add_header 'Content-Length' 0;
|
add_header 'Content-Length' 0;
|
||||||
return 204;
|
return 204;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_header 'Access-Control-Allow-Origin' '*';
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,
|
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||||
X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||||
|
|
||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
|
@ -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
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
use Contabilidad\Common\Controller\Base;
|
use Contabilidad\Common\Controller\Base;
|
||||||
|
|
||||||
|
$app->get('/key/generate[/]', [Base::class, 'generate_key']);
|
||||||
|
$app->get('/balance[/]', [Contabilidad\Common\Controller\TiposCategorias::class, 'balance']);
|
||||||
|
$app->get('/info', [Base::class, 'info']);
|
||||||
$app->get('/', Base::class);
|
$app->get('/', Base::class);
|
||||||
|
@ -9,8 +9,11 @@ $app->group('/cuenta/{cuenta_id}', function($app) {
|
|||||||
$app->get('/entradas', [Cuentas::class, 'entradas']);
|
$app->get('/entradas', [Cuentas::class, 'entradas']);
|
||||||
$app->group('/transacciones', function($app) {
|
$app->group('/transacciones', function($app) {
|
||||||
$app->get('/amount', [Cuentas::class, 'transaccionesAmount']);
|
$app->get('/amount', [Cuentas::class, 'transaccionesAmount']);
|
||||||
|
$app->get('/month/{month}', [Cuentas::class, 'transaccionesMonth']);
|
||||||
|
$app->get('/acum/{date}', [Cuentas::class, 'transaccionesAcumulation']);
|
||||||
$app->get('[/{limit:[0-9]+}[/{start:[0-9]+}]]', [Cuentas::class, 'transacciones']);
|
$app->get('[/{limit:[0-9]+}[/{start:[0-9]+}]]', [Cuentas::class, 'transacciones']);
|
||||||
});
|
});
|
||||||
|
$app->get('/categoria', [Cuentas::class, 'categoria']);
|
||||||
$app->put('/edit', [Cuentas::class, 'edit']);
|
$app->put('/edit', [Cuentas::class, 'edit']);
|
||||||
$app->delete('/delete', [Cuentas::class, 'delete']);
|
$app->delete('/delete', [Cuentas::class, 'delete']);
|
||||||
$app->get('[/]', [Cuentas::class, 'show']);
|
$app->get('[/]', [Cuentas::class, 'show']);
|
||||||
|
12
api/resources/routes/monedas.php
Normal file
12
api/resources/routes/monedas.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\Monedas;
|
||||||
|
|
||||||
|
$app->group('/monedas', function($app) {
|
||||||
|
$app->post('/add[/]', [Monedas::class, 'add']);
|
||||||
|
$app->get('[/]', Monedas::class);
|
||||||
|
});
|
||||||
|
$app->group('/moneda/{moneda_id}', function($app) {
|
||||||
|
$app->put('/edit', [Monedas::class, 'edit']);
|
||||||
|
$app->delete('/delete', [Monedas::class, 'delete']);
|
||||||
|
$app->get('[/]', [Monedas::class, 'show']);
|
||||||
|
});
|
16
api/resources/routes/tipos.php
Normal file
16
api/resources/routes/tipos.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
$folder = implode(DIRECTORY_SEPARATOR, [
|
||||||
|
__DIR__,
|
||||||
|
'tipos'
|
||||||
|
]);
|
||||||
|
if (file_exists($folder)) {
|
||||||
|
$app->group('/tipos', function($app) use ($folder) {
|
||||||
|
$files = new DirectoryIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir() or $file->getExtension() != 'php') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
include_once $file->getRealPath();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
13
api/resources/routes/tipos/cambios.php
Normal file
13
api/resources/routes/tipos/cambios.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\TiposCambios;
|
||||||
|
|
||||||
|
$app->group('/cambios', function($app) {
|
||||||
|
$app->post('/obtener[/]', [TiposCambios::class, 'obtain']);
|
||||||
|
$app->post('/add[/]', [TiposCambios::class, 'add']);
|
||||||
|
$app->get('[/]', TiposCambios::class);
|
||||||
|
});
|
||||||
|
$app->group('/cambio/{tipo_id}', function($app) {
|
||||||
|
$app->put('/edit[/]', [TiposCambios::class, 'edit']);
|
||||||
|
$app->delete('/delete[/]', [TiposCambios::class, 'delete']);
|
||||||
|
$app->get('[/]', [TiposCambios::class, 'show']);
|
||||||
|
});
|
13
api/resources/routes/tipos/categorias.php
Normal file
13
api/resources/routes/tipos/categorias.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\TiposCategorias;
|
||||||
|
|
||||||
|
$app->group('/categorias', function($app) {
|
||||||
|
$app->post('/add[/]', [TiposCategorias::class, 'add']);
|
||||||
|
$app->get('[/]', TiposCategorias::class);
|
||||||
|
});
|
||||||
|
$app->group('/categoria/{tipo_id}', function($app) {
|
||||||
|
$app->get('/categorias', [TiposCategorias::class, 'categorias']);
|
||||||
|
$app->put('/edit', [TiposCategorias::class, 'edit']);
|
||||||
|
$app->delete('/delete', [TiposCategorias::class, 'delete']);
|
||||||
|
$app->get('[/]', [TiposCategorias::class, 'show']);
|
||||||
|
});
|
12
api/resources/routes/tipos/cuentas.php
Normal file
12
api/resources/routes/tipos/cuentas.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\TiposCuentas;
|
||||||
|
|
||||||
|
$app->group('/cuentas', function($app) {
|
||||||
|
$app->post('/add[/]', [TiposCuentas::class, 'add']);
|
||||||
|
$app->get('[/]', TiposCuentas::class);
|
||||||
|
});
|
||||||
|
$app->group('/cuenta/{tipo_id}', function($app) {
|
||||||
|
$app->put('/edit', [TiposCuentas::class, 'edit']);
|
||||||
|
$app->delete('/delete', [TiposCuentas::class, 'delete']);
|
||||||
|
$app->get('[/]', [TiposCuentas::class, 'show']);
|
||||||
|
});
|
12
api/resources/routes/uploads.php
Normal file
12
api/resources/routes/uploads.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\Files;
|
||||||
|
|
||||||
|
$app->group('/uploads', function($app) {
|
||||||
|
$app->post('/add[/]', [Files::class, 'upload']);
|
||||||
|
$app->get('[/]', Files::class);
|
||||||
|
});
|
||||||
|
$app->group('/upload/{folder}/{filename}', function($app) {
|
||||||
|
$app->put('[/]', [Files::class, 'edit']);
|
||||||
|
$app->delete('[/]', [Files::class, 'delete']);
|
||||||
|
$app->get('[/]', [Files::class, 'get']);
|
||||||
|
});
|
@ -31,7 +31,7 @@ $app->addRoutingMiddleware();
|
|||||||
$app->add(new WhoopsMiddleware());
|
$app->add(new WhoopsMiddleware());
|
||||||
|
|
||||||
|
|
||||||
$folder = 'middlewares';
|
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'middlewares']);
|
||||||
if (file_exists($folder)) {
|
if (file_exists($folder)) {
|
||||||
$files = new DirectoryIterator($folder);
|
$files = new DirectoryIterator($folder);
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
|
4
api/setup/middlewares/01_auth.php
Normal file
4
api/setup/middlewares/01_auth.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Middleware\Auth;
|
||||||
|
|
||||||
|
$app->add($app->getContainer()->get(Auth::class));
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
'debug' => $_ENV['DEBUG'] ?? false
|
'debug' => $_ENV['DEBUG'] ?? false,
|
||||||
|
'api_key' => $_ENV['API_KEY'],
|
||||||
|
'python_api' => $_ENV['PYTHON_API'] ?? 'http://python:5000',
|
||||||
|
'python_key' => $_ENV['PYTHON_KEY']
|
||||||
];
|
];
|
||||||
|
@ -19,13 +19,21 @@ return [
|
|||||||
'public'
|
'public'
|
||||||
]);
|
]);
|
||||||
$arr['uploads'] = implode(DIRECTORY_SEPARATOR, [
|
$arr['uploads'] = implode(DIRECTORY_SEPARATOR, [
|
||||||
$arr['public'],
|
$arr['base'],
|
||||||
'uploads'
|
'uploads'
|
||||||
]);
|
]);
|
||||||
$arr['pdfs'] = implode(DIRECTORY_SEPARATOR, [
|
$arr['pdfs'] = implode(DIRECTORY_SEPARATOR, [
|
||||||
$arr['uploads'],
|
$arr['uploads'],
|
||||||
'pdfs'
|
'pdfs'
|
||||||
]);
|
]);
|
||||||
|
$arr['csvs'] = implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$arr['uploads'],
|
||||||
|
'csvs'
|
||||||
|
]);
|
||||||
|
$arr['xlss'] = implode(DIRECTORY_SEPARATOR, [
|
||||||
|
$arr['uploads'],
|
||||||
|
'xlss'
|
||||||
|
]);
|
||||||
return (object) $arr;
|
return (object) $arr;
|
||||||
},
|
},
|
||||||
'urls' => function(Container $c) {
|
'urls' => function(Container $c) {
|
||||||
|
@ -2,14 +2,57 @@
|
|||||||
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\PdfHandler::class => function(Container $c) {
|
Contabilidad\Common\Service\Auth::class => function(Container $c) {
|
||||||
return new Contabilidad\Common\Service\PdfHandler($c->get(GuzzleHttp\Client::class), $c->get('folders')->pdfs, implode('/', [
|
return new Contabilidad\Common\Service\Auth($c->get('api_key'));
|
||||||
$c->get('urls')->python,
|
},
|
||||||
'pdf',
|
Contabilidad\Common\Middleware\Auth::class => function(Container $c) {
|
||||||
'parse'
|
return new Contabilidad\Common\Middleware\Auth(
|
||||||
]));
|
$c->get(Nyholm\Psr7\Factory\Psr17Factory::class),
|
||||||
}
|
$c->get(Contabilidad\Common\Service\Auth::class)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Contabilidad\Common\Service\PdfHandler::class => function(Container $c) {
|
||||||
|
return new Contabilidad\Common\Service\PdfHandler($c->get(GuzzleHttp\Client::class), $c->get('folders')->pdfs, implode('/', [
|
||||||
|
$c->get('urls')->python,
|
||||||
|
'pdf',
|
||||||
|
'parse'
|
||||||
|
]));
|
||||||
|
},
|
||||||
|
Contabilidad\Common\Service\CsvHandler::class => function(Container $c) {
|
||||||
|
return new Contabilidad\Common\Service\CsvHandler($c->get('folders')->csvs);
|
||||||
|
},
|
||||||
|
Contabilidad\Common\Service\XlsHandler::class => function(Container $c) {
|
||||||
|
return new Contabilidad\Common\Service\XlsHandler($c->get('folders')->xlss);
|
||||||
|
},
|
||||||
|
Contabilidad\Common\Service\DocumentHandler::class => function(Container $c) {
|
||||||
|
$handlers = [
|
||||||
|
$c->get(Contabilidad\Common\Service\XlsHandler::class),
|
||||||
|
$c->get(Contabilidad\Common\Service\CsvHandler::class),
|
||||||
|
$c->get(Contabilidad\Common\Service\PdfHandler::class)
|
||||||
|
];
|
||||||
|
return new Contabilidad\Common\Service\DocumentHandler($handlers);
|
||||||
|
},
|
||||||
|
Contabilidad\Common\Service\TiposCambios::class => function(Container $c) {
|
||||||
|
return new Contabilidad\Common\Service\TiposCambios(
|
||||||
|
$c->get(GuzzleHttp\Client::class),
|
||||||
|
$c->get(ProVM\Common\Factory\Model::class),
|
||||||
|
$c->get('python_api'),
|
||||||
|
$c->get('python_key')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Contabilidad\Common\Service\FileHandler::class => function(Container $c) {
|
||||||
|
return new Contabilidad\Common\Service\FileHandler((object) [
|
||||||
|
'folder' => $c->get('folders')->uploads,
|
||||||
|
'types' => [
|
||||||
|
'text/csv' => 'csvs',
|
||||||
|
'application/pdf' => 'pdfs',
|
||||||
|
'application/vnd.ms-excel' => 'xlss',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlss',
|
||||||
|
'application/json' => 'jsons'
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Contabilidad;
|
namespace Contabilidad;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use ProVM\Common\Alias\Model;
|
use ProVM\Common\Alias\Model;
|
||||||
|
use Contabilidad\Common\Service\TiposCambios as Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@ -10,12 +12,17 @@ use ProVM\Common\Alias\Model;
|
|||||||
*/
|
*/
|
||||||
class Categoria extends Model {
|
class Categoria extends Model {
|
||||||
public static $_table = 'categorias';
|
public static $_table = 'categorias';
|
||||||
protected static $fields = ['nombre'];
|
protected static $fields = ['nombre', 'tipo_id'];
|
||||||
|
|
||||||
protected $cuentas;
|
protected $cuentas;
|
||||||
public function cuentas() {
|
public function cuentas() {
|
||||||
if ($this->cuentas === null) {
|
if ($this->cuentas === null) {
|
||||||
$this->cuentas = $this->parentOf(Cuenta::class, [Model::CHILD_KEY => 'categoria_id']);
|
$this->cuentas = $this->parentOf(Cuenta::class, [Model::CHILD_KEY => 'categoria_id']);
|
||||||
|
if ($this->cuentas !== null) {
|
||||||
|
usort($this->cuentas, function($a, $b) {
|
||||||
|
return strcmp($a->nombre, $b->nombre);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $this->cuentas;
|
return $this->cuentas;
|
||||||
}
|
}
|
||||||
@ -27,24 +34,80 @@ class Categoria extends Model {
|
|||||||
return $this->tipo;
|
return $this->tipo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCuentasOf($tipo) {
|
||||||
|
return $this->factory->find(Cuenta::class)
|
||||||
|
->select([['cuentas', '*']])
|
||||||
|
->join([
|
||||||
|
['tipos_cuenta', 'tipos_cuenta.id', 'cuentas.tipo_id']
|
||||||
|
])
|
||||||
|
->where([
|
||||||
|
['tipos_cuenta.descripcion', $tipo],
|
||||||
|
['cuentas.categoria_id', $this->id]
|
||||||
|
])
|
||||||
|
->many();
|
||||||
|
}
|
||||||
|
protected $cuentas_of;
|
||||||
|
public function getCuentas() {
|
||||||
|
if ($this->cuentas_of === null) {
|
||||||
|
$tipos = $this->factory->find(TipoCuenta::class)->many();
|
||||||
|
$cos = [];
|
||||||
|
foreach ($tipos as $tipo) {
|
||||||
|
$p = strtolower($tipo->descripcion) . 's';
|
||||||
|
$cos[$p] = [];
|
||||||
|
$cuentas = $this->getCuentasOf($tipos->descripcion);
|
||||||
|
if ($cuentas === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$cos[$p] = $cuentas;
|
||||||
|
}
|
||||||
|
$this->cuentas_of = $cos;
|
||||||
|
}
|
||||||
|
return $this->cuentas_of;
|
||||||
|
}
|
||||||
|
protected $totales;
|
||||||
|
public function getTotales(Service $service) {
|
||||||
|
if ($this->totales === null) {
|
||||||
|
$tipos = $this->factory->find(TipoCuenta::class)->many();
|
||||||
|
$totals = [];
|
||||||
|
foreach ($tipos as $tipo) {
|
||||||
|
$p = strtolower($tipo->descripcion) . 's';
|
||||||
|
$totals[$p] = 0;
|
||||||
|
$cuentas = $this->getCuentasOf($tipo->descripcion);
|
||||||
|
if ($cuentas === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$totals[$p] = array_reduce($cuentas, function($sum, $item) use ($service) {
|
||||||
|
return $sum + $item->saldo($service, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$this->totales = $totals;
|
||||||
|
}
|
||||||
|
return $this->totales;
|
||||||
|
}
|
||||||
|
|
||||||
protected $saldo;
|
protected $saldo;
|
||||||
public function saldo() {
|
public function saldo(Service $service = null) {
|
||||||
if ($this->saldo === null) {
|
if ($this->saldo === null) {
|
||||||
$this->saldo = 0;
|
$this->saldo = 0;
|
||||||
if ($this->cuentas() !== null) {
|
if ($this->cuentas() !== null) {
|
||||||
$this->saldo = array_reduce($this->cuentas(), function($sum, $item) {
|
$sum = 0;
|
||||||
return $sum + $item->saldo();
|
$debitos = ['Activo', 'Perdida'];
|
||||||
});
|
foreach ($this->cuentas() as $cuenta) {
|
||||||
|
if (array_search($cuenta->tipo()->descripcion, $debitos) !== false) {
|
||||||
|
$sum -= $cuenta->saldo($service, true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$sum += $cuenta->saldo($service, true);
|
||||||
|
}
|
||||||
|
$this->saldo = $sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->saldo;
|
return $this->saldo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray(): array {
|
public function toArray(): array {
|
||||||
$arr = parent::toArray();
|
$arr = parent::toArray();
|
||||||
$arr['tipo'] = $this->tipo()->toArray();
|
$arr['tipo'] = $this->tipo()->toArray();
|
||||||
$arr['saldo'] = $this->saldo();
|
return $arr;
|
||||||
$arr['saldoFormateado'] = '$' . number_format($this->saldo(), 0, ',', '.');
|
|
||||||
return $arr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Contabilidad;
|
namespace Contabilidad;
|
||||||
|
|
||||||
|
use DateTimeInterface;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use PhpParser\Node\Expr\AssignOp\Mod;
|
||||||
use ProVM\Common\Alias\Model;
|
use ProVM\Common\Alias\Model;
|
||||||
|
use Contabilidad\Common\Service\TiposCambios as Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property string $nombre
|
* @property string $nombre
|
||||||
* @property Categoria $categoria_id
|
* @property Categoria $categoria_id
|
||||||
* @property TipoCuenta $tipo_id
|
* @property TipoCuenta $tipo_id
|
||||||
|
* @property Moneda $moneda_id
|
||||||
*/
|
*/
|
||||||
class Cuenta extends Model {
|
class Cuenta extends Model {
|
||||||
public static $_table = 'cuentas';
|
public static $_table = 'cuentas';
|
||||||
protected static $fields = ['nombre', 'categoria_id', 'tipo_id'];
|
protected static $fields = ['nombre', 'categoria_id', 'tipo_id', 'moneda_id'];
|
||||||
|
|
||||||
protected $categoria;
|
protected $categoria;
|
||||||
public function categoria() {
|
public function categoria() {
|
||||||
@ -20,67 +25,128 @@ class Cuenta extends Model {
|
|||||||
}
|
}
|
||||||
return $this->categoria;
|
return $this->categoria;
|
||||||
}
|
}
|
||||||
protected $cuenta;
|
protected $tipo;
|
||||||
public function cuenta() {
|
public function tipo() {
|
||||||
if ($this->cuenta === null) {
|
if ($this->tipo === null) {
|
||||||
$this->cuenta = $this->childOf(TipoCuenta::class, [Model::SELF_KEY => 'tipo_id']);
|
$this->tipo = $this->childOf(TipoCuenta::class, [Model::SELF_KEY => 'tipo_id']);
|
||||||
}
|
}
|
||||||
return $this->cuenta;
|
return $this->tipo;
|
||||||
|
}
|
||||||
|
protected $moneda;
|
||||||
|
public function moneda() {
|
||||||
|
if ($this->moneda === null) {
|
||||||
|
$this->moneda = $this->childOf(Moneda::class, [Model::SELF_KEY => 'moneda_id']);
|
||||||
|
}
|
||||||
|
return $this->moneda;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $cargos;
|
protected $cargos;
|
||||||
public function cargos() {
|
public function cargos() {
|
||||||
if ($this->cargos === null) {
|
if ($this->cargos === null) {
|
||||||
$this->cargos = $this->parentOf(Transaccion::class, [Model::CHILD_KEY => 'hasta_id']);
|
$this->cargos = $this->parentOf(Transaccion::class, [Model::CHILD_KEY => 'credito_id']);
|
||||||
}
|
}
|
||||||
return $this->cargos;
|
return $this->cargos;
|
||||||
}
|
}
|
||||||
protected $abonos;
|
protected $abonos;
|
||||||
public function abonos() {
|
public function abonos() {
|
||||||
if ($this->abonos === null) {
|
if ($this->abonos === null) {
|
||||||
$this->abonos = $this->parentOf(Transaccion::class, [Model::CHILD_KEY => 'desde_id']);
|
$this->abonos = $this->parentOf(Transaccion::class, [Model::CHILD_KEY => 'debito_id']);
|
||||||
}
|
}
|
||||||
return $this->abonos;
|
return $this->abonos;
|
||||||
}
|
}
|
||||||
protected $transacciones;
|
protected $transacciones;
|
||||||
public function transacciones($limit = null, $start = 0) {
|
public function transacciones($limit = null, $start = 0) {
|
||||||
if ($this->transacciones === null) {
|
|
||||||
$transacciones = Model::factory(Transaccion::class)
|
$transacciones = Model::factory(Transaccion::class)
|
||||||
->join('cuentas', 'cuentas.id = transacciones.desde_id OR cuentas.id = transacciones.hasta_id')
|
->select('transacciones.*')
|
||||||
|
->join('cuentas', 'cuentas.id = transacciones.debito_id OR cuentas.id = transacciones.credito_id')
|
||||||
->whereEqual('cuentas.id', $this->id)
|
->whereEqual('cuentas.id', $this->id)
|
||||||
->orderByAsc('transacciones.fecha');
|
->orderByAsc('transacciones.fecha');
|
||||||
if ($limit !== null) {
|
if ($limit !== null) {
|
||||||
$transacciones = $transacciones->limit($limit)
|
$transacciones = $transacciones->limit($limit)
|
||||||
->offset($start);
|
->offset($start);
|
||||||
}
|
}
|
||||||
$this->transacciones = $transacciones->findMany();
|
$transacciones = $transacciones->findMany();
|
||||||
foreach ($this->transacciones as &$transaccion) {
|
foreach ($transacciones as &$transaccion) {
|
||||||
$transaccion->setFactory($this->factory);
|
$transaccion->setFactory($this->factory);
|
||||||
if ($transaccion->desde_id === $this->id) {
|
if ($transaccion->debito_id === $this->id) {
|
||||||
$transaccion->valor = - $transaccion->valor;
|
$transaccion->valor = - $transaccion->valor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return $transacciones;
|
||||||
return $this->transacciones;
|
}
|
||||||
|
public function transaccionesMonth(Carbon $month) {
|
||||||
|
$start = $month->copy()->startOfMonth();
|
||||||
|
$end = $month->copy()->endOfMonth();
|
||||||
|
|
||||||
|
$transacciones = Model::factory(Transaccion::class)
|
||||||
|
->select('transacciones.*')
|
||||||
|
->join('cuentas', 'cuentas.id = transacciones.debito_id OR cuentas.id = transacciones.credito_id')
|
||||||
|
->whereEqual('cuentas.id', $this->id)
|
||||||
|
->whereRaw("transacciones.fecha BETWEEN '{$start->format('Y-m-d')}' AND '{$end->format('Y-m-d')}'")
|
||||||
|
->orderByAsc('transacciones.fecha');
|
||||||
|
$transacciones = $transacciones->findMany();
|
||||||
|
|
||||||
|
foreach ($transacciones as &$transaccion) {
|
||||||
|
$transaccion->setFactory($this->factory);
|
||||||
|
if ($transaccion->desde_id === $this->id) {
|
||||||
|
$transaccion->valor = - $transaccion->valor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transacciones;
|
||||||
|
}
|
||||||
|
public function acumulacion(Carbon $date) {
|
||||||
|
$abonos = Model::factory(Transaccion::class)
|
||||||
|
->whereEqual('credito_id', $this->id)
|
||||||
|
->whereLt('fecha', $date->format('Y-m-d'))
|
||||||
|
->groupBy('credito_id')
|
||||||
|
->sum('valor');
|
||||||
|
$cargos = Model::factory(Transaccion::class)
|
||||||
|
->whereEqual('debito_id', $this->id)
|
||||||
|
->whereLt('fecha', $date->format('Y-m-d'))
|
||||||
|
->groupBy('debito_id')
|
||||||
|
->sum('valor');
|
||||||
|
|
||||||
|
if (in_array($this->tipo()->descripcion, ['activo', 'banco', 'perdida'])) {
|
||||||
|
return $abonos - $cargos;
|
||||||
|
}
|
||||||
|
return $cargos - $abonos;
|
||||||
}
|
}
|
||||||
protected $saldo;
|
protected $saldo;
|
||||||
public function saldo() {
|
public function saldo(Service $service = null, $in_clp = false) {
|
||||||
if ($this->saldo === null) {
|
if ($this->saldo === null) {
|
||||||
$this->saldo = 0;
|
$this->saldo = 0;
|
||||||
if (count($this->transacciones()) > 0) {
|
if (count($this->transacciones()) > 0) {
|
||||||
$this->saldo = array_reduce($this->transacciones(), function($sum, $item) {
|
$this->saldo = array_reduce($this->transacciones(), function($sum, $item) {
|
||||||
return $sum + $item->valor;
|
return $sum + $item->valor;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($in_clp and $this->moneda()->codigo !== 'CLP') {
|
||||||
|
$fecha = Carbon::today();
|
||||||
|
if ($this->moneda()->codigo == 'USD') {
|
||||||
|
$fecha = match ($fecha->weekday()) {
|
||||||
|
0 => $fecha->subWeek()->weekday(5),
|
||||||
|
6 => $fecha->weekday(5),
|
||||||
|
default => $fecha
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$service->get($fecha->format('Y-m-d'), $this->moneda()->id);
|
||||||
|
return $this->moneda()->cambiar($fecha, $this->saldo);
|
||||||
|
}
|
||||||
return $this->saldo;
|
return $this->saldo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray(): array {
|
public function format($valor) {
|
||||||
|
return $this->moneda()->format($valor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(Service $service = null, $in_clp = false): array {
|
||||||
$arr = parent::toArray();
|
$arr = parent::toArray();
|
||||||
$arr['categoria'] = $this->categoria()->toArray();
|
$arr['tipo'] = $this->tipo()->toArray();
|
||||||
$arr['saldo'] = $this->saldo();
|
$arr['moneda'] = $this->moneda()->toArray();
|
||||||
$arr['saldoFormateado'] = '$' . number_format($this->saldo(), 0, ',', '.');
|
$arr['saldo'] = $this->saldo($service, $in_clp);
|
||||||
|
$arr['saldoFormateado'] = $this->format($this->saldo($service, $in_clp));
|
||||||
return $arr;
|
return $arr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
api/src/Moneda.php
Normal file
56
api/src/Moneda.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad;
|
||||||
|
|
||||||
|
use ProVM\Common\Alias\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $denominacion
|
||||||
|
* @property string $codigo
|
||||||
|
* @property string $sufijo
|
||||||
|
* @property string $prefijo
|
||||||
|
* @property int $decimales
|
||||||
|
*/
|
||||||
|
class Moneda extends Model {
|
||||||
|
public static $_table = 'monedas';
|
||||||
|
protected static $fields = ['denominacion', 'codigo'];
|
||||||
|
|
||||||
|
public function format($valor) {
|
||||||
|
return trim(implode('', [
|
||||||
|
$this->prefijo,
|
||||||
|
number_format($valor, $this->decimales, ',', '.'),
|
||||||
|
$this->sufijo
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
public function cambio(\DateTime $fecha) {
|
||||||
|
$cambio = $this->factory->find(TipoCambio::class)
|
||||||
|
->where([['desde_id', $this->id], ['hasta_id', 1], ['fecha', $fecha->format('Y-m-d H:i:s')]])
|
||||||
|
->one();
|
||||||
|
if ($cambio === null) {
|
||||||
|
$cambio = $this->factory->find(TipoCambio::class)
|
||||||
|
->where([['hasta_id', $this->id], ['desde_id', 1], ['fecha', $fecha->format('Y-m-d H:i:s')]])
|
||||||
|
->one();
|
||||||
|
}
|
||||||
|
return $cambio;
|
||||||
|
}
|
||||||
|
public function cambiar(\DateTime $fecha, float $valor) {
|
||||||
|
$cambio = $this->cambio($fecha);
|
||||||
|
if (!$cambio) {
|
||||||
|
return $valor;
|
||||||
|
}
|
||||||
|
if ($cambio->desde()->id != $this->id) {
|
||||||
|
return $cambio->transform($valor, TipoCambio::DESDE);
|
||||||
|
}
|
||||||
|
return $cambio->transform($valor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(): array {
|
||||||
|
$arr = parent::toArray();
|
||||||
|
$arr['format'] = [
|
||||||
|
'prefijo' => $this->prefijo,
|
||||||
|
'sufijo' => $this->sufijo,
|
||||||
|
'decimales' => $this->decimales
|
||||||
|
];
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
}
|
50
api/src/TipoCambio.php
Normal file
50
api/src/TipoCambio.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace Contabilidad;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use DateTime;
|
||||||
|
use ProVM\Common\Alias\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property DateTime $fecha
|
||||||
|
* @property Moneda $desde_id
|
||||||
|
* @property Moneda $hasta_id
|
||||||
|
* @property float $valor
|
||||||
|
*/
|
||||||
|
class TipoCambio extends Model {
|
||||||
|
const DESDE = -1;
|
||||||
|
const HASTA = 1;
|
||||||
|
|
||||||
|
public static $_table = 'tipos_cambio';
|
||||||
|
protected static $fields = ['fecha', 'valor', 'desde_id', 'hasta_id'];
|
||||||
|
|
||||||
|
protected $desde;
|
||||||
|
public function desde() {
|
||||||
|
if ($this->desde === null) {
|
||||||
|
$this->desde = $this->childOf(Moneda::class, [Model::SELF_KEY => 'desde_id']);
|
||||||
|
}
|
||||||
|
return $this->desde;
|
||||||
|
}
|
||||||
|
protected $hasta;
|
||||||
|
public function hasta() {
|
||||||
|
if ($this->hasta === null) {
|
||||||
|
$this->hasta = $this->childOf(Moneda::class, [Model::SELF_KEY => 'hasta_id']);
|
||||||
|
}
|
||||||
|
return $this->hasta;
|
||||||
|
}
|
||||||
|
public function fecha(DateTime $fecha = null) {
|
||||||
|
if ($fecha === null) {
|
||||||
|
return Carbon::parse($this->fecha);
|
||||||
|
}
|
||||||
|
$this->fecha = $fecha->format('Y-m-d H:i:s');
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transform(float $valor, int $direction = TipoCambio::HASTA): float {
|
||||||
|
if ($direction == TipoCambio::HASTA) {
|
||||||
|
return $valor * $this->valor;
|
||||||
|
}
|
||||||
|
return $valor / $this->valor;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
namespace Contabilidad;
|
namespace Contabilidad;
|
||||||
|
|
||||||
use ProVM\Common\Alias\Model;
|
use ProVM\Common\Alias\Model;
|
||||||
|
use Contabilidad\Common\Service\TiposCambios as Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@ -9,6 +10,57 @@ use ProVM\Common\Alias\Model;
|
|||||||
* @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;
|
||||||
|
public function categorias() {
|
||||||
|
if ($this->categorias === null) {
|
||||||
|
$this->categorias = $this->parentOf(Categoria::class, [Model::CHILD_KEY => 'tipo_id']);
|
||||||
|
}
|
||||||
|
return $this->categorias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCuentasOf($tipo) {
|
||||||
|
return $this->factory->find(Cuenta::class)
|
||||||
|
->select('cuentas.*')
|
||||||
|
->join([
|
||||||
|
['tipos_cuenta', 'tipos_cuenta.id', 'cuentas.tipo_id'],
|
||||||
|
['categorias', 'categorias.id', 'cuentas.categoria_id']
|
||||||
|
])
|
||||||
|
->where([
|
||||||
|
['tipos_cuenta.descripcion', $tipo],
|
||||||
|
['categorias.tipo_id', $this->id]
|
||||||
|
])->many();
|
||||||
|
}
|
||||||
|
protected $totales;
|
||||||
|
public function getTotales(Service $service) {
|
||||||
|
if ($this->totales === null) {
|
||||||
|
$tipos = $this->factory->find(TipoCuenta::class)->many();
|
||||||
|
$totals = [];
|
||||||
|
foreach ($tipos as $tipo) {
|
||||||
|
$p = strtolower($tipo->descripcion) . 's';
|
||||||
|
$totals[$p] = 0;
|
||||||
|
$cuentas = $this->getCuentasOf($tipo->descripcion);
|
||||||
|
if ($cuentas === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$totals[$p] = array_reduce($cuentas, function($sum, $item) use ($service) {
|
||||||
|
return $sum + $item->saldo($service, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$this->totales = $totals;
|
||||||
|
}
|
||||||
|
return $this->totales;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $saldo;
|
||||||
|
public function saldo(Service $service = null) {
|
||||||
|
if ($this->saldo === null) {
|
||||||
|
$this->saldo = array_reduce($this->categorias() ?? [], function($sum, $item) use ($service) {
|
||||||
|
return $sum + $item->saldo($service);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return $this->saldo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@ use ProVM\Common\Alias\Model;
|
|||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
* @property string $descripcion
|
* @property string $descripcion
|
||||||
|
* @property string $color
|
||||||
*/
|
*/
|
||||||
class TipoCuenta extends Model {
|
class TipoCuenta extends Model {
|
||||||
public static $_table = 'tipos_cuenta';
|
public static $_table = 'tipos_cuenta';
|
||||||
protected static $fields = ['descripcion'];
|
protected static $fields = ['descripcion', 'color'];
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Contabilidad;
|
namespace Contabilidad;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use ProVM\Common\Alias\Model;
|
use ProVM\Common\Alias\Model;
|
||||||
|
|
||||||
@ -8,7 +9,7 @@ use ProVM\Common\Alias\Model;
|
|||||||
* @property int $id
|
* @property int $id
|
||||||
* @property Cuenta $debito_id
|
* @property Cuenta $debito_id
|
||||||
* @property Cuenta $credito_id
|
* @property Cuenta $credito_id
|
||||||
* @property \DateTime $fecha
|
* @property DateTime $fecha
|
||||||
* @property string $glosa
|
* @property string $glosa
|
||||||
* @property string $detalle
|
* @property string $detalle
|
||||||
* @property double $valor
|
* @property double $valor
|
||||||
@ -31,7 +32,7 @@ class Transaccion extends Model {
|
|||||||
}
|
}
|
||||||
return $this->credito;
|
return $this->credito;
|
||||||
}
|
}
|
||||||
public function fecha(\DateTime $fecha = null) {
|
public function fecha(DateTime $fecha = null) {
|
||||||
if ($fecha === null) {
|
if ($fecha === null) {
|
||||||
return Carbon::parse($this->fecha);
|
return Carbon::parse($this->fecha);
|
||||||
}
|
}
|
||||||
@ -43,7 +44,7 @@ class Transaccion extends Model {
|
|||||||
$arr['debito'] = $this->debito()->toArray();
|
$arr['debito'] = $this->debito()->toArray();
|
||||||
$arr['credito'] = $this->credito()->toArray();
|
$arr['credito'] = $this->credito()->toArray();
|
||||||
$arr['fechaFormateada'] = $this->fecha()->format('d-m-Y');
|
$arr['fechaFormateada'] = $this->fecha()->format('d-m-Y');
|
||||||
$arr['valorFormateado'] = '$' . number_format($this->valor, 0, ',', '.');
|
$arr['valorFormateado'] = $this->debito()->moneda()->format($this->valor);
|
||||||
return $arr;
|
return $arr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,23 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
|
profiles:
|
||||||
|
- api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: php
|
image: php
|
||||||
build:
|
build:
|
||||||
context: api
|
context: api
|
||||||
env_file: .env
|
env_file:
|
||||||
|
- .env
|
||||||
|
- .api.env
|
||||||
|
- .python.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./api/:/app/
|
- ./api/:/app/
|
||||||
- ./api/php.ini:/usr/local/etc/php/conf.d/php.ini
|
- ./api/php.ini:/usr/local/etc/php/conf.d/php.ini
|
||||||
- ./logs/api/php/:/var/log/php/
|
- ./logs/api/php/:/var/log/php/
|
||||||
api-proxy:
|
api-proxy:
|
||||||
|
profiles:
|
||||||
|
- api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: nginx
|
image: nginx
|
||||||
ports:
|
ports:
|
||||||
@ -21,25 +28,38 @@ services:
|
|||||||
- ./logs/api/:/var/log/nginx/
|
- ./logs/api/:/var/log/nginx/
|
||||||
- ./api/:/app/
|
- ./api/:/app/
|
||||||
db:
|
db:
|
||||||
|
profiles:
|
||||||
|
- api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: mariadb
|
image: mariadb
|
||||||
env_file: .env
|
env_file: .env
|
||||||
volumes:
|
volumes:
|
||||||
- contabilidad_data:/var/lib/mysql
|
- contabilidad_data:/var/lib/mysql
|
||||||
adminer:
|
adminer:
|
||||||
|
profiles:
|
||||||
|
- api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: adminer
|
image: adminer
|
||||||
ports:
|
ports:
|
||||||
- "9002:8080"
|
- "9002:8080"
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
|
profiles:
|
||||||
|
- ui
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: php-ui
|
image: php-ui
|
||||||
|
env_file:
|
||||||
|
- .api.env
|
||||||
|
- .env
|
||||||
build:
|
build:
|
||||||
context: ui
|
context: ui
|
||||||
volumes:
|
volumes:
|
||||||
- ./ui/:/app/
|
- ./ui/:/app/
|
||||||
|
- ./ui/php.ini:/usr/local/etc/php/conf.d/php.ini
|
||||||
|
- ./logs/ui/php/:/var/log/php/
|
||||||
ui-proxy:
|
ui-proxy:
|
||||||
|
profiles:
|
||||||
|
- ui
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: nginx
|
image: nginx
|
||||||
ports:
|
ports:
|
||||||
@ -50,9 +70,15 @@ services:
|
|||||||
- ./ui/:/app/
|
- ./ui/:/app/
|
||||||
|
|
||||||
python:
|
python:
|
||||||
|
profiles:
|
||||||
|
- python
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
build:
|
build:
|
||||||
context: ./python
|
context: ./python
|
||||||
|
env_file:
|
||||||
|
- .python.env
|
||||||
|
ports:
|
||||||
|
- "9003:5000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./python/src/:/app/src/
|
- ./python/src/:/app/src/
|
||||||
- ./python/config/:/app/config/
|
- ./python/config/:/app/config/
|
||||||
|
@ -2,7 +2,7 @@ FROM python
|
|||||||
|
|
||||||
RUN apt-get update -y && apt-get install -y ghostscript python3-tk libgl-dev
|
RUN apt-get update -y && apt-get install -y ghostscript python3-tk libgl-dev
|
||||||
|
|
||||||
RUN pip install flask pyyaml pypdf4 gunicorn camelot-py[cv] pikepdf
|
RUN pip install flask pyyaml pypdf4 gunicorn camelot-py[cv] pikepdf httpx
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@ -12,4 +12,5 @@ EXPOSE 5000
|
|||||||
|
|
||||||
WORKDIR /app/src
|
WORKDIR /app/src
|
||||||
|
|
||||||
CMD ["gunicorn", "-b 0.0.0.0:5000", "app:app"]
|
CMD ["python", "app.py"]
|
||||||
|
#CMD ["gunicorn", "-b 0.0.0.0:5000", "app:app"]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
passwords:
|
passwords:
|
||||||
- 0839
|
- 0839
|
||||||
- 159608395
|
- 159608395
|
||||||
|
- 15960839
|
||||||
|
BIN
python/data/EECCvirtual-Visa.pdf
Normal file
BIN
python/data/EECCvirtual-Visa.pdf
Normal file
Binary file not shown.
285
python/src/ai/dictionary.py
Normal file
285
python/src/ai/dictionary.py
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import sklearn
|
||||||
|
import enlighten
|
||||||
|
from sklearn.preprocessing import LabelEncoder
|
||||||
|
|
||||||
|
import src.contabilidad.pdf as pdf
|
||||||
|
import src.contabilidad.text_handler as th
|
||||||
|
from src.ai.models import Phrase, phrase_factory, Word, word_factory
|
||||||
|
from src.contabilidad.log import LOG_LEVEL
|
||||||
|
|
||||||
|
|
||||||
|
class Dictionary:
|
||||||
|
def __init__(self, filename, logger):
|
||||||
|
self.filename = filename
|
||||||
|
self._logger = logger
|
||||||
|
self.__processed = []
|
||||||
|
self.__phrases = None
|
||||||
|
self.__words = None
|
||||||
|
self.load()
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
if not os.path.isfile(self.filename):
|
||||||
|
return
|
||||||
|
with open(self.filename, 'r') as file:
|
||||||
|
data = json.load(file)
|
||||||
|
if 'words' in data.keys():
|
||||||
|
self.__words = []
|
||||||
|
[self.__words.append(word_factory(w)) for w in data['words']]
|
||||||
|
if 'phrases' in data.keys():
|
||||||
|
self.__phrases = []
|
||||||
|
[self.__phrases.append(phrase_factory(ph)) for ph in data['phrases']]
|
||||||
|
if 'processed' in data.keys():
|
||||||
|
self.__processed = []
|
||||||
|
self.__processed = data['processed']
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.sort_words()
|
||||||
|
self.sort_phrases()
|
||||||
|
with open(self.filename, 'w') as file:
|
||||||
|
json.dump(self.to_json(), file, indent=2)
|
||||||
|
|
||||||
|
def to_data(self):
|
||||||
|
encoder = LabelEncoder()
|
||||||
|
data = encoder.fit_transform([w.get_word() for w in self.get_words()])
|
||||||
|
[self.__words[i].set_fit(f) for i, f in enumerate(data)]
|
||||||
|
print(data)
|
||||||
|
# return [ph.to_data() for ph in self.get_phrases()]
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
output = {
|
||||||
|
'processed': [],
|
||||||
|
'words': [],
|
||||||
|
'phrases': []
|
||||||
|
}
|
||||||
|
if self.__processed is not None and len(self.__processed) > 0:
|
||||||
|
output['processed'] = self.__processed
|
||||||
|
if self.__words is not None and len(self.__words) > 0:
|
||||||
|
output['words'] = [w.to_json() for w in self.__words]
|
||||||
|
if self.__phrases is not None and len(self.__phrases) > 0:
|
||||||
|
output['phrases'] = [p.to_json() for p in self.__phrases]
|
||||||
|
return output
|
||||||
|
|
||||||
|
def find_phrase(self, phrase: Phrase = None, phrase_dict: dict = None, phrase_list: list = None):
|
||||||
|
if not self.__phrases:
|
||||||
|
return -1
|
||||||
|
if phrase is not None:
|
||||||
|
phrase_list = [w.get_word() for w in phrase.get_words()]
|
||||||
|
elif phrase_dict is not None:
|
||||||
|
phrase_list = phrase_dict['words']
|
||||||
|
elif phrase_list is not None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
return find_phrase(self.__phrases, phrase_list)
|
||||||
|
|
||||||
|
def add_phrase(self, phrase: Phrase = None, phrase_dict: dict = None, phrase_list: list = None):
|
||||||
|
if self.__phrases is None:
|
||||||
|
self.__phrases = []
|
||||||
|
if phrase is not None:
|
||||||
|
pass
|
||||||
|
elif phrase_dict is not None:
|
||||||
|
phrase = phrase_factory(phrase_dict)
|
||||||
|
elif phrase_list is not None:
|
||||||
|
phrase = phrase_factory({'words': phrase_list})
|
||||||
|
else:
|
||||||
|
return self
|
||||||
|
i = self.find_phrase(phrase)
|
||||||
|
if i > -1:
|
||||||
|
self.__phrases[i].add_freq()
|
||||||
|
return self
|
||||||
|
self.__phrases.append(phrase)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_phrases(self, phrase_list: list):
|
||||||
|
if self.__phrases is None:
|
||||||
|
self.__phrases = []
|
||||||
|
phs = [sorted(w.get_word() for w in p) for p in self.__phrases]
|
||||||
|
with enlighten.get_manager() as manager:
|
||||||
|
with manager.counter(total=len(phrase_list), desc='Phrases', unit='phrases', color='green') as bar1:
|
||||||
|
for i, phrase in enumerate(phrase_list):
|
||||||
|
# print(f'Adding phrase {i}.')
|
||||||
|
p2 = sorted([w.get_word() for w in phrase])
|
||||||
|
if p2 in phs:
|
||||||
|
k = phs.index(p2)
|
||||||
|
self.__phrases[k].add_freq()
|
||||||
|
continue
|
||||||
|
ph = phrase_factory({'words': phrase})
|
||||||
|
self.__phrases.append(ph)
|
||||||
|
phs.append(p2)
|
||||||
|
bar1.update()
|
||||||
|
|
||||||
|
def get_phrases(self):
|
||||||
|
return self.__phrases
|
||||||
|
|
||||||
|
def sort_phrases(self):
|
||||||
|
if self.__phrases is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
def sort_phrase(p):
|
||||||
|
if p is None:
|
||||||
|
return 0
|
||||||
|
if isinstance(p, Phrase):
|
||||||
|
return p.get_freq(), p.get_type().get_desc(), len(p.get_words())
|
||||||
|
return p['frequency'], p['type']['description'], len(p['words'])
|
||||||
|
self.__phrases = sorted(self.__phrases,
|
||||||
|
key=sort_phrase)
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.log(repr(self.__phrases), LOG_LEVEL.ERROR)
|
||||||
|
self._logger.log(e)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def sort_words(self):
|
||||||
|
if self.__words is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
def sort_word(w):
|
||||||
|
if w is None:
|
||||||
|
return 0
|
||||||
|
if isinstance(w, Word):
|
||||||
|
return w.get_freq(), w.get_type().get_desc(), w.get_word()
|
||||||
|
return w['frequency'], w['type']['description'], w['word']
|
||||||
|
self.__words = sorted(self.__words, key=sort_word, reverse=True)
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.log(repr(self.__words))
|
||||||
|
self._logger.log(e)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def find_word(self, word: Word = None, word_dict: dict = None, word_str: str = None):
|
||||||
|
if not self.__words:
|
||||||
|
return -1
|
||||||
|
if word is not None:
|
||||||
|
word_str = word.get_word()
|
||||||
|
elif word_dict is not None:
|
||||||
|
word_str = word_dict['word']
|
||||||
|
elif word_str is not None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
return find_word(self.__words, word_str)
|
||||||
|
|
||||||
|
def add_word(self, word: Word = None, word_dict: dict = None, word_str: str = None):
|
||||||
|
if self.__words is None:
|
||||||
|
self.__words = []
|
||||||
|
if word is not None:
|
||||||
|
pass
|
||||||
|
elif word_dict is not None:
|
||||||
|
word = word_factory(word_dict)
|
||||||
|
elif word_str is not None:
|
||||||
|
word = word_factory({'word': word_str})
|
||||||
|
else:
|
||||||
|
return self
|
||||||
|
i = self.find_word(word)
|
||||||
|
if i > -1:
|
||||||
|
self.__words[i].add_freq()
|
||||||
|
return self
|
||||||
|
self.__words.append(word)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_words(self, words: list):
|
||||||
|
[self.add_word(word=w) for w in words if isinstance(w, Word)]
|
||||||
|
[self.add_word(word_dict=w) for w in words if isinstance(w, dict)]
|
||||||
|
[self.add_word(word_str=w) for w in words if isinstance(w, str)]
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_words(self):
|
||||||
|
return filter_unique_words(self.__words)
|
||||||
|
|
||||||
|
def match_words(self, word_list: list):
|
||||||
|
new_list = []
|
||||||
|
for w in word_list:
|
||||||
|
wi = self.find_word(word_str=w)
|
||||||
|
new_list.append(self.__words[wi])
|
||||||
|
return new_list
|
||||||
|
|
||||||
|
def append_to_phrase(self, seed: list = None, length: int = 1):
|
||||||
|
if seed is None:
|
||||||
|
return [self.__words[0]]
|
||||||
|
max_index = max(seed) + length
|
||||||
|
if max_index > len(self.__words):
|
||||||
|
if length == 1:
|
||||||
|
return False
|
||||||
|
return self.append_to_phrase(seed, length - 1)
|
||||||
|
return seed + self.__words[max_index]
|
||||||
|
|
||||||
|
def get_possible_phrases(self, word_list):
|
||||||
|
print('Adding words.')
|
||||||
|
self.add_words(word_list)
|
||||||
|
|
||||||
|
print('Creating phrases.')
|
||||||
|
with enlighten.get_manager() as manager:
|
||||||
|
with manager.counter(total=len(word_list)**2, desc='Phrases', unit='words', color='red') as bar1:
|
||||||
|
phrases = []
|
||||||
|
for length in range(1, len(word_list) + 1):
|
||||||
|
bar2 = bar1.add_subcounter(color='green')
|
||||||
|
for start in range(0, len(word_list)):
|
||||||
|
phrase = build_phrase(word_list, start, start + length)
|
||||||
|
phrase = self.match_words(phrase)
|
||||||
|
phrases.append(phrase)
|
||||||
|
start += length
|
||||||
|
bar2.update()
|
||||||
|
bar1.update()
|
||||||
|
|
||||||
|
print(f'Created {len(phrases)} phrases.')
|
||||||
|
phrases = sorted(phrases, key=lambda e: len(e))
|
||||||
|
|
||||||
|
print('Adding phrases.')
|
||||||
|
# Really slow (~115000 phrases in one pdf)
|
||||||
|
self.add_phrases(phrases)
|
||||||
|
return self.__phrases
|
||||||
|
|
||||||
|
def is_processed(self, filename: str):
|
||||||
|
return os.path.basename(filename) in self.__processed
|
||||||
|
|
||||||
|
def process(self, filename: str, password: str = None):
|
||||||
|
if self.is_processed(filename):
|
||||||
|
print('Already processed.')
|
||||||
|
return
|
||||||
|
t = filename.split('.')
|
||||||
|
temp = os.path.realpath(os.path.join(os.path.dirname(filename), t[0] + '-temp.pdf'))
|
||||||
|
print('Removing PDF encryption.')
|
||||||
|
pdf.remove_encryption(filename, password, temp)
|
||||||
|
print('Getting text')
|
||||||
|
obj = pdf.get_text(temp)
|
||||||
|
os.remove(temp)
|
||||||
|
print('Getting possible phrases.')
|
||||||
|
phrases = self.get_possible_phrases(th.split_words(obj))
|
||||||
|
self.__processed.append(os.path.basename(filename))
|
||||||
|
return phrases
|
||||||
|
|
||||||
|
|
||||||
|
def build_phrase(word_list, start: int, end: int = None):
|
||||||
|
if end is None:
|
||||||
|
return word_list[start:]
|
||||||
|
return word_list[start:end]
|
||||||
|
|
||||||
|
|
||||||
|
def filter_unique_words(words):
|
||||||
|
new_list = []
|
||||||
|
for w in words:
|
||||||
|
if w not in new_list:
|
||||||
|
new_list.append(w)
|
||||||
|
return new_list
|
||||||
|
|
||||||
|
|
||||||
|
def validate_phrase(phrase):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def find_phrase(phrases: list, phrase: list):
|
||||||
|
phrase_list = [sorted([w.get_word() for w in p.get_words()]) for p in phrases]
|
||||||
|
sphrase = sorted(phrase)
|
||||||
|
if sphrase in phrase_list:
|
||||||
|
return phrase_list.index(sphrase)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
def find_word(words: list, word: str):
|
||||||
|
word_list = [w.get_word() for w in words]
|
||||||
|
if word in word_list:
|
||||||
|
return word_list.index(word)
|
||||||
|
return -1
|
243
python/src/ai/models.py
Normal file
243
python/src/ai/models.py
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class Type:
|
||||||
|
def __init__(self, _id, _description):
|
||||||
|
self.__id = _id
|
||||||
|
self.__description = _description
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.__id
|
||||||
|
|
||||||
|
def get_desc(self):
|
||||||
|
return self.__description
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return self.get_id()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return json.dumps({
|
||||||
|
'id': self.get_id(),
|
||||||
|
'description': self.get_desc()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def type_factory(_type: str, _id: int):
|
||||||
|
if _type == 'Word' or _type == 'WordType':
|
||||||
|
t = WordType()
|
||||||
|
elif _type == 'Phrase' or _type == 'PhraseType':
|
||||||
|
t = PhraseType()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
t.load(_id)
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
class WordType(Type):
|
||||||
|
STRING = 0
|
||||||
|
NUMERIC = 1
|
||||||
|
CURRENCY = 2
|
||||||
|
DATE = 4
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(0, 'string')
|
||||||
|
|
||||||
|
def load(self, word_type: int):
|
||||||
|
if word_type == self.STRING:
|
||||||
|
self.__description = 'string'
|
||||||
|
elif word_type == self.NUMERIC:
|
||||||
|
self.__description = 'numeric'
|
||||||
|
elif word_type == self.CURRENCY:
|
||||||
|
self.__description = 'currency'
|
||||||
|
elif word_type == self.DATE:
|
||||||
|
self.__description = 'date'
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class PhraseType(Type):
|
||||||
|
TEXT = 0
|
||||||
|
TITLE = 1
|
||||||
|
HEADER = 2
|
||||||
|
MOVEMENT = 4
|
||||||
|
INVALID = 99
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(PhraseType, self).__init__(0, 'text')
|
||||||
|
|
||||||
|
def load(self, phrase_type: int):
|
||||||
|
if phrase_type == self.TEXT:
|
||||||
|
self.__description = 'text'
|
||||||
|
elif phrase_type == self.TITLE:
|
||||||
|
self.__description = 'title'
|
||||||
|
elif phrase_type == self.HEADER:
|
||||||
|
self.__description = 'header'
|
||||||
|
|
||||||
|
|
||||||
|
class Word:
|
||||||
|
def __init__(self):
|
||||||
|
self.__id = 0
|
||||||
|
self.__word = None
|
||||||
|
self.__type_id = 0
|
||||||
|
self.__type = None
|
||||||
|
self.__frequency = 1
|
||||||
|
|
||||||
|
def set_id(self, idx: int):
|
||||||
|
self.__id = idx
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_word(self, word: str):
|
||||||
|
self.__word = word
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_type(self, word_type):
|
||||||
|
if isinstance(word_type, WordType):
|
||||||
|
self.__type_id = word_type.get_id()
|
||||||
|
# self.__type = word_type
|
||||||
|
if isinstance(word_type, int):
|
||||||
|
self.__type_id = word_type
|
||||||
|
# self.__type = type_factory('Word', word_type)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_freq(self, amount: int = 1):
|
||||||
|
self.__frequency += amount
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_id(self) -> int:
|
||||||
|
return self.__id
|
||||||
|
|
||||||
|
def get_word(self) -> str:
|
||||||
|
return self.__word
|
||||||
|
|
||||||
|
def get_type_id(self) -> int:
|
||||||
|
return self.__type_id
|
||||||
|
|
||||||
|
def get_type(self) -> WordType:
|
||||||
|
if self.__type is None:
|
||||||
|
self.__type = type_factory('Word', self.__type_id)
|
||||||
|
return self.__type
|
||||||
|
|
||||||
|
def get_freq(self) -> int:
|
||||||
|
return self.__frequency
|
||||||
|
|
||||||
|
def to_json(self) -> dict:
|
||||||
|
output = {
|
||||||
|
'id': self.get_id(),
|
||||||
|
'word': self.get_word(),
|
||||||
|
'type': self.get_type_id(),
|
||||||
|
'freq': self.get_freq()
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return json.dumps(self.to_json())
|
||||||
|
|
||||||
|
|
||||||
|
def word_factory(word: dict) -> Word:
|
||||||
|
w = Word()
|
||||||
|
w.set_id(word['id'])
|
||||||
|
w.set_word(word['word'])
|
||||||
|
if 'type' in word:
|
||||||
|
w.set_type(word['type'])
|
||||||
|
if 'freq' in word:
|
||||||
|
w.add_freq(word['freq'] - 1)
|
||||||
|
return w
|
||||||
|
|
||||||
|
|
||||||
|
class Phrase:
|
||||||
|
def __init__(self):
|
||||||
|
self.__id = 0
|
||||||
|
self.__words = None
|
||||||
|
self.__type_id = 0
|
||||||
|
self.__type = None
|
||||||
|
self.__frequency = 1
|
||||||
|
|
||||||
|
def set_id(self, idx: int):
|
||||||
|
self.__id = idx
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_word(self, word):
|
||||||
|
if isinstance(word, Word):
|
||||||
|
self.__words.append(word.get_id())
|
||||||
|
if isinstance(word, dict):
|
||||||
|
if 'id' in word:
|
||||||
|
self.__words.append(word['id'])
|
||||||
|
if isinstance(word, int):
|
||||||
|
self.__words.append(word)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_words(self, words: list):
|
||||||
|
if self.__words is None:
|
||||||
|
self.__words = []
|
||||||
|
for w in words:
|
||||||
|
if isinstance(w, Word):
|
||||||
|
self.add_word(w)
|
||||||
|
if isinstance(w, dict):
|
||||||
|
self.add_word(w)
|
||||||
|
if isinstance(w, int):
|
||||||
|
self.add_word(w)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_type(self, phrase_type):
|
||||||
|
if isinstance(phrase_type, PhraseType):
|
||||||
|
self.__type_id = phrase_type.get_id()
|
||||||
|
# self.__type = phrase_type
|
||||||
|
if isinstance(phrase_type, int):
|
||||||
|
self.__type_id = phrase_type
|
||||||
|
# self.__type = type_factory('Phrase', phrase_type)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_freq(self, amount: int = 1):
|
||||||
|
self.__frequency += amount
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_id(self) -> int:
|
||||||
|
return self.__id
|
||||||
|
|
||||||
|
def get_words(self) -> list:
|
||||||
|
return self.__words
|
||||||
|
|
||||||
|
def get_type_id(self) -> int:
|
||||||
|
return self.__type_id
|
||||||
|
|
||||||
|
def get_type(self) -> PhraseType:
|
||||||
|
if self.__type is None:
|
||||||
|
self.__type = type_factory('Phrase', self.__type_id)
|
||||||
|
return self.__type
|
||||||
|
|
||||||
|
def get_freq(self) -> int:
|
||||||
|
return self.__frequency
|
||||||
|
|
||||||
|
def match(self, word_list: list):
|
||||||
|
if len(word_list) != len(self.__words):
|
||||||
|
return False
|
||||||
|
new_words = sorted(self.__words)
|
||||||
|
new_list = sorted(word_list)
|
||||||
|
if new_words == new_list:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
output = {
|
||||||
|
'id': self.get_id(),
|
||||||
|
'words': self.get_words(),
|
||||||
|
'type': self.get_type_id(),
|
||||||
|
'freq': self.get_freq()
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return json.dumps(self.to_json())
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.get_words())
|
||||||
|
|
||||||
|
|
||||||
|
def phrase_factory(phrase: dict) -> Phrase:
|
||||||
|
ph = Phrase()
|
||||||
|
ph.set_id(phrase['id'])
|
||||||
|
ph.set_words(phrase['words'])
|
||||||
|
if 'type' in phrase:
|
||||||
|
ph.set_type(phrase['type'])
|
||||||
|
if 'freq' in phrase:
|
||||||
|
ph.add_freq(phrase['freq'] - 1)
|
||||||
|
return ph
|
126
python/src/ai/network.py
Normal file
126
python/src/ai/network.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import timeit
|
||||||
|
|
||||||
|
import tensorflow as tf
|
||||||
|
import sklearn
|
||||||
|
import numpy as np
|
||||||
|
from sklearn.preprocessing import LabelEncoder
|
||||||
|
|
||||||
|
import src.contabilidad.pdf as pdf
|
||||||
|
import src.contabilidad.text_handler as th
|
||||||
|
|
||||||
|
|
||||||
|
class Layer:
|
||||||
|
def __init__(self):
|
||||||
|
self.__weights = None
|
||||||
|
self.__bias = None
|
||||||
|
|
||||||
|
def set_size(self, inputs: int, size: int):
|
||||||
|
self.__weights = [[0 for j in range(0, inputs)] for i in range(0, size)]
|
||||||
|
self.__bias = [0 for i in range(0, size)]
|
||||||
|
|
||||||
|
def add_weight(self, vector: list, idx: int = None):
|
||||||
|
if idx is None:
|
||||||
|
self.__weights.append(vector)
|
||||||
|
return self
|
||||||
|
self.__weights = self.__weights[:idx] + [vector] + self.__weights[idx:]
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_weight(self, value: float, weight_index: int, input_index: int):
|
||||||
|
self.__weights[weight_index][input_index] = value
|
||||||
|
|
||||||
|
def set_bias(self, value: list):
|
||||||
|
self.__bias = value
|
||||||
|
|
||||||
|
def train(self, input_values: list, output_values: list):
|
||||||
|
output = self.get_output(input_values)
|
||||||
|
errors = []
|
||||||
|
for i, v in enumerate(output):
|
||||||
|
error = (output_values[i] - v) / output_values[i]
|
||||||
|
new_value = v * error
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return {
|
||||||
|
'bias': self.__bias,
|
||||||
|
'weights': self.__weights
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_output(self, vector: list):
|
||||||
|
output = []
|
||||||
|
for i, weight in enumerate(self.__weights):
|
||||||
|
val = 0
|
||||||
|
for j, v in enumerate(weight):
|
||||||
|
val += v * vector[j]
|
||||||
|
output[i] = val + self.__bias[i]
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def layer_factory(layer_dict: dict):
|
||||||
|
layer = Layer()
|
||||||
|
layer.set_bias(layer_dict['bias'])
|
||||||
|
[layer.add_weight(w) for w in layer_dict['weights']]
|
||||||
|
return layer
|
||||||
|
|
||||||
|
|
||||||
|
class Network:
|
||||||
|
def __init__(self, filename: str):
|
||||||
|
self._filename = filename
|
||||||
|
self.__layers = None
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
with open(self._filename) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
if 'layers' in data.keys():
|
||||||
|
self.add_layers(data['layers'])
|
||||||
|
|
||||||
|
def add_layers(self, layers: list):
|
||||||
|
for lr in layers:
|
||||||
|
layer = layer_factory(lr)
|
||||||
|
self.__layers.append(layer)
|
||||||
|
|
||||||
|
|
||||||
|
class AI:
|
||||||
|
def __init__(self, dictionary_filename, logger):
|
||||||
|
self.__dict = None
|
||||||
|
self.__network = None
|
||||||
|
self.__sources = None
|
||||||
|
self._phrases = None
|
||||||
|
self.filename = ''
|
||||||
|
|
||||||
|
def add_source(self, text):
|
||||||
|
if self.__sources is None:
|
||||||
|
self.__sources = []
|
||||||
|
self.__sources.append(text)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_filename(self, filename: str):
|
||||||
|
self.filename = filename
|
||||||
|
return self
|
||||||
|
|
||||||
|
def process_sources(self):
|
||||||
|
for source in self.__sources:
|
||||||
|
self.process(**source)
|
||||||
|
|
||||||
|
def process(self, filename, password):
|
||||||
|
encoder = LabelEncoder()
|
||||||
|
t = filename.split('.')
|
||||||
|
temp = os.path.realpath(os.path.join(os.path.dirname(filename), t[0] + '-temp.pdf'))
|
||||||
|
pdf.remove_encryption(filename, password, temp)
|
||||||
|
obj = pdf.get_text(temp)
|
||||||
|
os.remove(temp)
|
||||||
|
word_list = th.split_words(obj)
|
||||||
|
fits = encoder.fit_transform(word_list)
|
||||||
|
phrases = []
|
||||||
|
for length in range(1, len(word_list) + 1):
|
||||||
|
for start in range(0, len(word_list)):
|
||||||
|
phrase = word_list[start:(start + length)]
|
||||||
|
phrase = np.append(np.array([fits[word_list.index(w)] for w in phrase]),
|
||||||
|
np.zeros([len(word_list) - len(phrase)]))
|
||||||
|
phrases.append(phrase)
|
||||||
|
phrases = np.array(phrases)
|
||||||
|
self._phrases = phrases
|
||||||
|
|
||||||
|
def active_train(self):
|
||||||
|
pass
|
@ -1,22 +1,43 @@
|
|||||||
import io
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from flask import Flask, request
|
import httpx
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
|
||||||
import contabilidad.pdf as pdf
|
import contabilidad.pdf as pdf
|
||||||
import contabilidad.passwords as passwords
|
import contabilidad.passwords as passwords
|
||||||
import contabilidad.log as log
|
|
||||||
import contabilidad.text_handler as th
|
import contabilidad.text_handler as th
|
||||||
|
from contabilidad.log import Log
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
log.logging['filename'] = '/var/log/python/contabilidad.log'
|
log = Log('/var/log/python/contabilidad.log')
|
||||||
|
api_key = os.environ.get('PYTHON_KEY')
|
||||||
|
|
||||||
|
|
||||||
|
def validate_key(request_obj):
|
||||||
|
if 'Authorization' in request_obj.headers:
|
||||||
|
auth = request_obj.headers.get('Authorization')
|
||||||
|
if isinstance(auth, list):
|
||||||
|
auth = auth[0]
|
||||||
|
if 'Bearer' in auth:
|
||||||
|
try:
|
||||||
|
auth = auth.split(' ')[1]
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return auth == api_key
|
||||||
|
if 'API_KEY' in request_obj.values:
|
||||||
|
return request_obj.values.get('API_KEY') == api_key
|
||||||
|
if 'api_key' in request_obj.values:
|
||||||
|
return request_obj.values.get('api_key') == api_key
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
@app.route('/pdf/parse', methods=['POST'])
|
@app.route('/pdf/parse', methods=['POST'])
|
||||||
def pdf_parse():
|
def pdf_parse():
|
||||||
|
if not validate_key(request):
|
||||||
|
return jsonify({'message': 'Not Authorized'})
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if not isinstance(data['files'], list):
|
if not isinstance(data['files'], list):
|
||||||
data['files'] = [data['files']]
|
data['files'] = [data['files']]
|
||||||
@ -32,6 +53,11 @@ def pdf_parse():
|
|||||||
continue
|
continue
|
||||||
pdf.remove_encryption(filename, p, temp)
|
pdf.remove_encryption(filename, p, temp)
|
||||||
obj = pdf.get_data(temp)
|
obj = pdf.get_data(temp)
|
||||||
|
try:
|
||||||
|
text = th.text_cleanup(pdf.get_text(temp))
|
||||||
|
except IndexError as ie:
|
||||||
|
print(ie, file=sys.stderr)
|
||||||
|
continue
|
||||||
outputs = []
|
outputs = []
|
||||||
for o in obj:
|
for o in obj:
|
||||||
out = json.loads(o.df.to_json(orient='records'))
|
out = json.loads(o.df.to_json(orient='records'))
|
||||||
@ -48,8 +74,35 @@ def pdf_parse():
|
|||||||
out[i] = line
|
out[i] = line
|
||||||
outputs.append(out)
|
outputs.append(out)
|
||||||
os.remove(temp)
|
os.remove(temp)
|
||||||
output.append({'filename': file['filename'], 'text': outputs})
|
output.append({'bank': text['bank'], 'filename': file['filename'], 'tables': outputs, 'text': text['text']})
|
||||||
return json.dumps(output)
|
return jsonify(output)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/cambio/get', methods=['POST'])
|
||||||
|
def cambios():
|
||||||
|
if not validate_key(request):
|
||||||
|
return jsonify({'message': 'Not Authorized'})
|
||||||
|
data = request.get_json()
|
||||||
|
valid = {
|
||||||
|
"CLF": "uf",
|
||||||
|
"IVP": "ivp",
|
||||||
|
"USD": "dolar",
|
||||||
|
"USDo": "dolar_intercambio",
|
||||||
|
"EUR": "euro",
|
||||||
|
"IPC": "ipc",
|
||||||
|
"UTM": "utm",
|
||||||
|
"IMACEC": "imacec",
|
||||||
|
"TPM": "tpm",
|
||||||
|
"CUP": "libra_cobre",
|
||||||
|
"TZD": "tasa_desempleo",
|
||||||
|
"BTC": "bitcoin"
|
||||||
|
}
|
||||||
|
base_url = 'https://mindicador.cl/api/'
|
||||||
|
url = f"{base_url}{valid[data['desde']]}/{'-'.join(list(reversed(data['fecha'].split('-'))))}"
|
||||||
|
res = httpx.get(url)
|
||||||
|
if res.status_code != httpx.codes.OK:
|
||||||
|
return jsonify({'error': 'Valor no encontrado.'})
|
||||||
|
return jsonify(res.json())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Binary file not shown.
@ -1,19 +1,65 @@
|
|||||||
|
import os.path
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
logging = {
|
|
||||||
'filename': '/var/log/python/error.log'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LOG_LEVEL:
|
class LOG_LEVEL:
|
||||||
INFO = 'INFO'
|
INFO = 0
|
||||||
WARNING = 'WARNING'
|
WARNING = 1
|
||||||
DEBUG = 'DEBUG'
|
DEBUG = 2
|
||||||
ERROR = 'ERROR'
|
ERROR = 4
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def desc(level):
|
||||||
|
mapping = {
|
||||||
|
LOG_LEVEL.INFO: 'INFO',
|
||||||
|
LOG_LEVEL.WARNING: 'WARNING',
|
||||||
|
LOG_LEVEL.DEBUG: 'DEBUG',
|
||||||
|
LOG_LEVEL.ERROR: 'ERROR'
|
||||||
|
}
|
||||||
|
return mapping[level]
|
||||||
|
|
||||||
|
|
||||||
def log(message, level=LOG_LEVEL.INFO):
|
class Logger:
|
||||||
filename = logging['filename']
|
def __init__(self):
|
||||||
with open(filename, 'a') as f:
|
self._logs = []
|
||||||
f.write(time.strftime('[%Y-%m-%d %H:%M:%S] ') + ' - ' + level + ': ' + message)
|
|
||||||
|
def add_log(self, filename: str, min_level: int = LOG_LEVEL.INFO):
|
||||||
|
self._logs.append({'log': Log(filename), 'level': min_level})
|
||||||
|
self._logs.sort(key=lambda e: e['level'])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def log(self, message, level: int = LOG_LEVEL.INFO):
|
||||||
|
for log in self._logs:
|
||||||
|
if log['level'] >= level:
|
||||||
|
log['log'].log(message, level)
|
||||||
|
|
||||||
|
|
||||||
|
class Log:
|
||||||
|
MAX_SIZE = 10 * 1024 * 1024
|
||||||
|
|
||||||
|
def __init__(self, filename: str = '/var/log/python/error.log'):
|
||||||
|
self._filename = filename
|
||||||
|
|
||||||
|
def log(self, message, level: int = LOG_LEVEL.INFO):
|
||||||
|
if isinstance(message, Exception):
|
||||||
|
message = traceback.format_exc()
|
||||||
|
if level < LOG_LEVEL.ERROR:
|
||||||
|
level = LOG_LEVEL.ERROR
|
||||||
|
self.rotate_file()
|
||||||
|
with open(self._filename, 'a') as f:
|
||||||
|
f.write(time.strftime('[%Y-%m-%d %H:%M:%S] ') + ' - ' + LOG_LEVEL.desc(level=level) + ': ' + message + "\n")
|
||||||
|
|
||||||
|
def rotate_file(self):
|
||||||
|
if not os.path.isfile(self._filename):
|
||||||
|
return
|
||||||
|
file_size = os.path.getsize(self._filename)
|
||||||
|
if file_size > self.MAX_SIZE:
|
||||||
|
self.next_file()
|
||||||
|
|
||||||
|
def next_file(self):
|
||||||
|
name = self._filename.split('.')
|
||||||
|
n = 1
|
||||||
|
if name[-2].isnumeric():
|
||||||
|
n = int(name[-2]) + 1
|
||||||
|
self._filename = '.'.join([name[0], str(n), name[-1]])
|
||||||
|
@ -1,48 +1,112 @@
|
|||||||
def text_cleanup(text, filename: str = None):
|
def text_cleanup(text: str):
|
||||||
if isinstance(text, list):
|
if isinstance(text, list):
|
||||||
output = []
|
text = "\n\n\n".join(text)
|
||||||
for t in text:
|
if 'bice' in text.lower():
|
||||||
output.append(text_cleanup(t, filename=filename))
|
return {'bank': 'BICE', 'text': bice(text)}
|
||||||
return output
|
if 'scotiabank' in text.lower():
|
||||||
if filename is None:
|
return {'bank': 'Scotiabank', 'text': scotiabank(text)}
|
||||||
return text
|
if 'TARJETA' in text:
|
||||||
if 'bice' in filename.lower():
|
return {'bank': 'Scotiabank', 'text': tarjeta(text)}
|
||||||
return bice(text)
|
return {'bank': 'unknown', 'text': basic(text)}
|
||||||
if 'scotiabank' in filename.lower():
|
|
||||||
return scotiabank(text)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def bice(text):
|
def bice(text):
|
||||||
lines = text.split("\n\n\n")
|
lines = [t2.strip() for t in text.split("\n\n\n")
|
||||||
print(lines)
|
for t1 in t.split("\n\n") for t2 in t1.split("\n") if t2.strip() != '']
|
||||||
return text
|
output = []
|
||||||
|
output += extract_from_to(lines, 'NOMBRE DEL CLIENTE', end='LAS CONDES', line_length=3)
|
||||||
|
ti = [t for t in lines if 'MOVIMIENTOS DE LA CUENTA CORRIENTE' in t][0]
|
||||||
|
output += extract_from_to(lines, 'LAS CONDES', end=ti, line_length=3)
|
||||||
|
output += [ti]
|
||||||
|
ti = [i for i, t in enumerate(lines) if 'FECHA' in t]
|
||||||
|
output += extract_from_to(lines, ti[0], end=ti[1], line_length=4)
|
||||||
|
output += extract_from_to(lines, 'RESUMEN DEL PERIODO', end='SALDO INICIAL', line_length=1)
|
||||||
|
output += extract_from_to(lines, 'SALDO INICIAL', end='LINEA SOBREGIRO AUTORIZADA', line_length=4)
|
||||||
|
output += extract_from_to(lines, 'LINEA SOBREGIRO AUTORIZADA', end='OBSERVACIONES', line_length=3)
|
||||||
|
output += extract_from_to(lines, 'OBSERVACIONES', line_length=1)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
def scotiabank(text):
|
def scotiabank(text):
|
||||||
words = text.split("\n")
|
words = split_words(text)
|
||||||
output = [words[0]]
|
output = [words[0]]
|
||||||
output = output + extract_from_to(words, 'No. CTA.', end='VENCIMIENTO LINEA DE CREDITO', line_length=3)
|
output += extract_from_to(words, 'No. CTA.', end='VENCIMIENTO LINEA DE CREDITO', line_length=3)
|
||||||
output = output + extract_from_to(words, 'VENCIMIENTO LINEA DE CREDITO',
|
output += extract_from_to(words, 'VENCIMIENTO LINEA DE CREDITO',
|
||||||
end='NOMBRE EJECUTIVO: LILIAN AVILA MANRIQUEZ', line_length=2)
|
end='NOMBRE EJECUTIVO: LILIAN AVILA MANRIQUEZ', line_length=2)
|
||||||
output = output + extract_from_to(words, 'NOMBRE EJECUTIVO: LILIAN AVILA MANRIQUEZ', end='SALDO ANTERIOR',
|
output += extract_from_to(words, 'NOMBRE EJECUTIVO: LILIAN AVILA MANRIQUEZ', end='SALDO ANTERIOR',
|
||||||
line_length=1)
|
line_length=1)
|
||||||
output = output + extract_from_to(words, 'SALDO ANTERIOR', end='FECHA', line_length=4)
|
output += extract_from_to(words, 'SALDO ANTERIOR', end='FECHA', line_length=4)
|
||||||
output = output + extract_data(words, 'FECHA', end='ACTUALICE SIEMPRE ANTECEDENTES LEGALES, ', line_length=6,
|
output += extract_data(words, 'FECHA', end='ACTUALICE SIEMPRE ANTECEDENTES LEGALES, ', line_length=6,
|
||||||
merge_list=[['DOCTO', 'No.'], ['SALDO', 'DIARIO']])
|
merge_list=[['DOCTO', 'No.'], ['SALDO', 'DIARIO']])
|
||||||
[print(li) for li in output]
|
output += extract_from_to(words, 'ACTUALICE SIEMPRE ANTECEDENTES LEGALES, ', 1)
|
||||||
return text
|
return output
|
||||||
|
|
||||||
|
|
||||||
def extract_from_to(word_list, start, line_length, end: str = None, merge_list=None):
|
def tarjeta(text):
|
||||||
|
words = split_words(text)
|
||||||
|
output = ['ESTADO DE CUENTA NACIONAL DE TARJETA DE CRÉDITO']
|
||||||
|
i = [i for i, w in enumerate(words) if 'FECHA ESTADO DE CUENTA' in w][0] + 2
|
||||||
|
output += extract_from_to(words, 'NOMBRE DEL TITULAR', end=i, line_length=2)
|
||||||
|
output += ['I. INFORMACIóN GENERAL']
|
||||||
|
i = [i for i, w in enumerate(words) if 'CUPO TOTAL' in w][1]
|
||||||
|
output += extract_from_to(words, 'CUPO TOTAL', end=i, line_length=3)
|
||||||
|
output += extract_from_to(words, i, end='ROTATIVO', line_length=4)
|
||||||
|
output += extract_from_to(words, 'ROTATIVO', end='TASA INTERÉS VIGENTE', line_length=3)
|
||||||
|
output += extract_from_to(words, 'TASA INTERÉS VIGENTE',
|
||||||
|
end='CAE se calcula sobre un supuesto de gasto mensual de UF 20 y pagadero en 12 cuotas.',
|
||||||
|
line_length=4)
|
||||||
|
output += extract_from_to(words, 'DESDE', end='PERÍODO FACTURADO', line_length=2)
|
||||||
|
output += extract_from_to(words, 'PERÍODO FACTURADO', end='II.', line_length=3)
|
||||||
|
output += ['II. DETALLE']
|
||||||
|
output += extract_from_to(words, '1. PERÍODO ANTERIOR', end='SALDO ADEUDADO INICIO PERÍODO ANTERIOR', line_length=3)
|
||||||
|
i = words.index('2. PERÍODO ACTUAL')
|
||||||
|
output += extract_from_to(words, 'SALDO ADEUDADO INICIO PERÍODO ANTERIOR', end=i - 1, line_length=2,
|
||||||
|
merge_list=[['MONTO FACTURADO A PAGAR (PERÍODO ANTERIOR)', '(A)']], merge_character=" ")
|
||||||
|
output += ['2. PERÍODO ACTUAL']
|
||||||
|
output += extract_from_to(words, 'LUGAR DE', end='1.TOTAL OPERACIONES', line_length=7,
|
||||||
|
merge_list=[['OPERACIÓN', 'O COBRO'], ['TOTAL A', 'PAGAR'], ['VALOR CUOTA', 'MENSUAL']])
|
||||||
|
i = words.index('1.TOTAL OPERACIONES') + 3
|
||||||
|
output += extract_from_to(words, '1.TOTAL OPERACIONES', end=i, line_length=3)
|
||||||
|
output += extract_from_to(words, i, end='TOTAL PAGOS A LA CUENTA', line_length=7)
|
||||||
|
i = words.index('TOTAL PAGOS A LA CUENTA') + 2
|
||||||
|
output += extract_from_to(words, 'TOTAL PAGOS A LA CUENTA', end=i, line_length=2)
|
||||||
|
output += extract_from_to(words, i, end='TOTAL PAT A LA CUENTA', line_length=8)
|
||||||
|
i = words.index('TOTAL PAT A LA CUENTA') + 2
|
||||||
|
output += extract_from_to(words, 'TOTAL PAT A LA CUENTA', end=i, line_length=2)
|
||||||
|
output += extract_from_to(words, i, end=i + 3, line_length=2,
|
||||||
|
merge_list=[
|
||||||
|
['2.PRODUCTOS O SERVICIOS VOLUNTARIAMENTE CONTRATADOS SIN MOVIMIENTOS', '(C)']],
|
||||||
|
merge_character=" ")
|
||||||
|
if '3.CARGOS, COMISIONES, IMPUESTOS Y ABONOS' in words:
|
||||||
|
i = words.index('3.CARGOS, COMISIONES, IMPUESTOS Y ABONOS') + 3
|
||||||
|
output += extract_from_to(words, '3.CARGOS, COMISIONES, IMPUESTOS Y ABONOS', end=i, line_length=3)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def basic(text):
|
||||||
|
return split_words(text)
|
||||||
|
|
||||||
|
|
||||||
|
def split_words(text):
|
||||||
|
if isinstance(text, list):
|
||||||
|
text = "\n\n\n".join(text)
|
||||||
|
words = [t.strip() for t in text.split("\n") if t.strip() != '']
|
||||||
|
return words
|
||||||
|
|
||||||
|
|
||||||
|
def extract_from_to(word_list, start, line_length, end=None, merge_list=None, merge_character="\n"):
|
||||||
|
if not isinstance(start, int):
|
||||||
|
start = word_list.index(start)
|
||||||
if end is not None:
|
if end is not None:
|
||||||
return extract_by_line(word_list[word_list.index(start):word_list.index(end)], line_length, merge_list)
|
if not isinstance(end, int):
|
||||||
return extract_by_line(word_list[word_list.index(start):], line_length, merge_list)
|
end = word_list.index(end)
|
||||||
|
return extract_by_line(word_list[start:end], line_length, merge_list, merge_character)
|
||||||
|
return extract_by_line(word_list[start:], line_length, merge_list, merge_character)
|
||||||
|
|
||||||
|
|
||||||
def extract_by_line(word_list, line_length, merge_list=None):
|
def extract_by_line(word_list, line_length, merge_list=None, merge_character="\n"):
|
||||||
if merge_list is not None:
|
if merge_list is not None:
|
||||||
word_list = merge_words(word_list, merge_list)
|
word_list = merge_words(word_list, merge_list, merge_character)
|
||||||
output = []
|
output = []
|
||||||
line = []
|
line = []
|
||||||
for k, w in enumerate(word_list):
|
for k, w in enumerate(word_list):
|
||||||
@ -54,22 +118,39 @@ def extract_by_line(word_list, line_length, merge_list=None):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def merge_words(word_list, merge_list):
|
def merge_words(word_list, merge_list, merge_character):
|
||||||
for m in merge_list:
|
for m in merge_list:
|
||||||
i = word_list.index(m[0])
|
ixs = find_words(word_list, m)
|
||||||
word_list = word_list[:i] + ["\n".join(m)] + word_list[i+len(m):]
|
if ixs is None:
|
||||||
|
continue
|
||||||
|
for i in ixs:
|
||||||
|
word_list = word_list[:i] + [merge_character.join(m)] + word_list[i + len(m):]
|
||||||
return word_list
|
return word_list
|
||||||
|
|
||||||
|
|
||||||
def extract_data(word_list, start, line_length, end=None, merge_list=None, date_sep='/'):
|
def find_words(word_list, find_list):
|
||||||
|
ixs = [i for i, w in enumerate(word_list) if find_list[0] == w]
|
||||||
|
output = []
|
||||||
|
for i in ixs:
|
||||||
|
mistake = False
|
||||||
|
for k, m in enumerate(find_list):
|
||||||
|
if m != word_list[i + k]:
|
||||||
|
mistake = True
|
||||||
|
break
|
||||||
|
if mistake:
|
||||||
|
continue
|
||||||
|
output.append(i)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def extract_data(word_list, start, line_length, end=None, merge_list=None, merge_character="\n", date_sep='/'):
|
||||||
word_list = word_list[word_list.index(start):]
|
word_list = word_list[word_list.index(start):]
|
||||||
if end is not None:
|
if end is not None:
|
||||||
word_list = word_list[:word_list.index(end)]
|
word_list = word_list[:word_list.index(end)]
|
||||||
if merge_list is not None:
|
if merge_list is not None:
|
||||||
word_list = merge_words(word_list, merge_list)
|
word_list = merge_words(word_list, merge_list, merge_character)
|
||||||
output = []
|
output = []
|
||||||
line = []
|
line = []
|
||||||
line_num = 0
|
|
||||||
col = 0
|
col = 0
|
||||||
for k, w in enumerate(word_list):
|
for k, w in enumerate(word_list):
|
||||||
if col > 0 and col % line_length == 0:
|
if col > 0 and col % line_length == 0:
|
||||||
@ -87,4 +168,5 @@ def extract_data(word_list, start, line_length, end=None, merge_list=None, date_
|
|||||||
continue
|
continue
|
||||||
line.append(w)
|
line.append(w)
|
||||||
col += 1
|
col += 1
|
||||||
|
output.append(line)
|
||||||
return output
|
return output
|
||||||
|
@ -3,22 +3,51 @@ import os
|
|||||||
|
|
||||||
import contabilidad.pdf as pdf
|
import contabilidad.pdf as pdf
|
||||||
import contabilidad.text_handler as th
|
import contabilidad.text_handler as th
|
||||||
|
from contabilidad.log import Logger, LOG_LEVEL
|
||||||
|
import ai.dictionary as dictionary
|
||||||
|
from ai.network import AI
|
||||||
|
|
||||||
|
|
||||||
|
def parse_settings(args):
|
||||||
|
output = {'filename': args.filename}
|
||||||
|
if not os.path.isfile(output['filename']):
|
||||||
|
output['filename'] = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data', args.filename))
|
||||||
|
t = args.filename.split('.')
|
||||||
|
output['temp'] = os.path.realpath(os.path.join(os.path.dirname(output['filename']), t[0] + '-temp.pdf'))
|
||||||
|
output['dictionary'] = os.path.join(os.path.dirname(output['filename']), 'dictionary.json')
|
||||||
|
output['network'] = os.path.join(os.path.dirname(output['filename']), 'network.json')
|
||||||
|
output['log_file'] = args.log_file
|
||||||
|
if not os.path.isfile(output['log_file']):
|
||||||
|
output['log_file'] = os.path.join(os.path.dirname(os.path.dirname(output['filename'])), output['log_file'])
|
||||||
|
output['error_log_file'] = os.path.join(os.path.dirname(output['log_file']), 'error.log')
|
||||||
|
output['logger'] = Logger()
|
||||||
|
output['logger'].add_log(output['log_file']).add_log(output['error_log_file'], LOG_LEVEL.ERROR)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
filename = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data', args.filename))
|
settings = parse_settings(args)
|
||||||
temp = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data', args.temp_filename))
|
|
||||||
pdf.remove_encryption(filename, args.password, temp)
|
print('Loading AI')
|
||||||
obj = pdf.get_data(temp)
|
network = AI(settings['dictionary'], settings['logger'])
|
||||||
obj = pdf.get_text(filename, args.password)
|
network.set_filename(settings['network'])
|
||||||
text = th.text_cleanup(obj, filename=str(args.filename))
|
network.add_source({'filename': settings['filename'], 'password': args.password})
|
||||||
os.remove(temp)
|
network.process_sources()
|
||||||
|
exit()
|
||||||
|
|
||||||
|
print('Loading dictionary.')
|
||||||
|
dictio = dictionary.Dictionary(settings['dictionary'], settings['logger'])
|
||||||
|
print('Getting possible phrases.')
|
||||||
|
dictio.process(settings['filename'], args.password)
|
||||||
|
dictio.to_data()
|
||||||
|
# print('Saving dictionary.')
|
||||||
|
# dictio.save()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-f', '--filename', type=str)
|
parser.add_argument('-f', '--filename', type=str)
|
||||||
parser.add_argument('-p', '--password', type=str, default='')
|
parser.add_argument('-p', '--password', type=str, default='')
|
||||||
parser.add_argument('-t', '--temp_filename', type=str)
|
parser.add_argument('-l', '--log_file', type=str, default=None)
|
||||||
_args = parser.parse_args()
|
_args = parser.parse_args()
|
||||||
main(_args)
|
main(_args)
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
FROM php:8-fpm
|
FROM php:8-fpm
|
||||||
|
|
||||||
|
RUN apt-get update -y && apt-get install -y git libzip-dev zip
|
||||||
|
|
||||||
|
RUN docker-php-ext-install zip
|
||||||
|
|
||||||
|
COPY --from=composer /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
12
ui/common/Controller/Config.php
Normal file
12
ui/common/Controller/Config.php
Normal 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 Config {
|
||||||
|
public function __invoke(Request $request, Response $response, View $view): Response {
|
||||||
|
return $view->render($response, 'config.list');
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,8 @@ class Cuentas {
|
|||||||
return $view->render($response, 'cuentas.list');
|
return $view->render($response, 'cuentas.list');
|
||||||
}
|
}
|
||||||
public function show(Request $request, Response $response, View $view, $cuenta_id): Response {
|
public function show(Request $request, Response $response, View $view, $cuenta_id): Response {
|
||||||
return $view->render($response, 'cuentas.show', compact('cuenta_id'));
|
$max_transacciones = 100;
|
||||||
|
return $view->render($response, 'cuentas.show', compact('cuenta_id', 'max_transacciones'));
|
||||||
}
|
}
|
||||||
public function add(Request $request, Response $response, View $view): Response {
|
public function add(Request $request, Response $response, View $view): Response {
|
||||||
return $view->render($response, 'cuentas.add');
|
return $view->render($response, 'cuentas.add');
|
||||||
|
@ -9,4 +9,7 @@ class Home {
|
|||||||
public function __invoke(Request $request, Response $response, View $view): Response {
|
public function __invoke(Request $request, Response $response, View $view): Response {
|
||||||
return $view->render($response, 'home');
|
return $view->render($response, 'home');
|
||||||
}
|
}
|
||||||
|
public function info(Request $request, Response $response, View $view): Response {
|
||||||
|
return $view->render($response, 'info');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
12
ui/common/Controller/Importar.php
Normal file
12
ui/common/Controller/Importar.php
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
12
ui/common/Controller/TiposCategorias.php
Normal file
12
ui/common/Controller/TiposCategorias.php
Normal 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 TiposCategorias {
|
||||||
|
public function __invoke(Request $request, Response $response, View $view): Response {
|
||||||
|
return $view->render($response, 'categorias.tipos.list');
|
||||||
|
}
|
||||||
|
}
|
12
ui/common/Controller/TiposCuentas.php
Normal file
12
ui/common/Controller/TiposCuentas.php
Normal 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 TiposCuentas {
|
||||||
|
public function __invoke(Request $request, Response $response, View $view): Response {
|
||||||
|
return $view->render($response, 'cuentas.tipos.list');
|
||||||
|
}
|
||||||
|
}
|
24
ui/common/Controller/Uploads.php
Normal file
24
ui/common/Controller/Uploads.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
class Uploads {
|
||||||
|
public function __invoke(Request $request, Response $response, View $view): Response {
|
||||||
|
return $view->render($response, 'uploads.list');
|
||||||
|
}
|
||||||
|
public function get(Request $request, Response $response, Client $client, $folder, $filename): Response {
|
||||||
|
$resp = $client->get(implode('/', ['upload', $folder, $filename]));
|
||||||
|
$file = $resp->getBody();
|
||||||
|
return $response
|
||||||
|
->withHeader('Content-Type', $resp->getHeader('Content-Type'))
|
||||||
|
->withHeader('Content-Disposition', 'attachment; filename=' . $filename)
|
||||||
|
->withAddedHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||||
|
->withHeader('Cache-Control', 'post-check=0, pre-check=0')
|
||||||
|
->withHeader('Pragma', 'no-cache')
|
||||||
|
->withBody($file);
|
||||||
|
}
|
||||||
|
}
|
@ -5,10 +5,11 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php-di/php-di": "^6.3",
|
"php-di/php-di": "^6.3",
|
||||||
"php-di/slim-bridge": "^3.1",
|
"php-di/slim-bridge": "^3.1",
|
||||||
"rubellum/slim-blade-view": "^0.1.1",
|
|
||||||
"nyholm/psr7-server": "^1.0",
|
"nyholm/psr7-server": "^1.0",
|
||||||
"zeuxisoo/slim-whoops": "^0.7.3",
|
"zeuxisoo/slim-whoops": "^0.7.3",
|
||||||
"nyholm/psr7": "^1.4"
|
"nyholm/psr7": "^1.4",
|
||||||
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
|
"berrnd/slim-blade-view": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
@ -24,11 +25,5 @@
|
|||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Contabilidad\\Common\\": "common"
|
"Contabilidad\\Common\\": "common"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"repositories": [
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "http://git.provm.cl/ProVM/controller.git"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
4
ui/php.ini
Normal file
4
ui/php.ini
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[PHP]
|
||||||
|
display_errors = E_ALL
|
||||||
|
log_errors = true
|
||||||
|
error_log = /var/log/php/error.log
|
@ -1,28 +1,92 @@
|
|||||||
|
class Categoria {
|
||||||
|
constructor({id, nombre, tipo_id, tipo, saldo, saldoFormateado}) {
|
||||||
|
this.id = id
|
||||||
|
this.nombre = nombre
|
||||||
|
this.tipo_id = tipo_id
|
||||||
|
this.tipo = tipo
|
||||||
|
this.modal = null
|
||||||
|
}
|
||||||
|
setModal(modal) {
|
||||||
|
this.modal = modal
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
return $('<tr></tr>').append(
|
||||||
|
$('<td></td>').html(this.nombre)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').html(this.tipo.descripcion)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'edit icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.edit()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
).append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular red icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'remove icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.remove()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
edit() {
|
||||||
|
this.modal.find('form').trigger('reset')
|
||||||
|
this.modal.find('form').find("[name='id']").val(this.id)
|
||||||
|
this.modal.find('form').find("[name='nombre']").val(this.nombre)
|
||||||
|
this.modal.find('form').find("#tipos").dropdown('set selected', this.tipo.id)
|
||||||
|
modalToEdit(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
sendDelete(_urls.api + '/categoria/' + this.id + '/delete').then(() => {
|
||||||
|
categorias.getCategorias()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const categorias = {
|
const categorias = {
|
||||||
id: '#categorias',
|
id: '#categorias',
|
||||||
categorias: [],
|
categorias: [],
|
||||||
modal: null,
|
modal: null,
|
||||||
getCategorias: function() {
|
getCategorias: function() {
|
||||||
return $.ajax({
|
this.categorias = []
|
||||||
url: _urls.api + '/categorias',
|
return sendGet(_urls.api + '/categorias').then((data) => {
|
||||||
method: 'GET',
|
if (data.categorias === null || data.categorias.length === 0) {
|
||||||
dataType: 'json'
|
|
||||||
}).then((data) => {
|
|
||||||
if (data.categorias === null || data.categorias.length == 0) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.categorias = data.categorias
|
$.each(data.categorias, (i, el) => {
|
||||||
|
const cat = new Categoria(el)
|
||||||
|
cat.setModal(this.modal)
|
||||||
|
this.categorias.push(cat)
|
||||||
|
})
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.draw()
|
this.draw()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
getTipos: function() {
|
||||||
|
sendGet(_urls.api + '/tipos/categorias').then((data) => {
|
||||||
|
this.modal.find('#tipos').dropdown()
|
||||||
|
let values = []
|
||||||
|
$.each(data.tipos, (i, el) => {
|
||||||
|
values.push({value: el.id, text: el.descripcion, name: el.descripcion})
|
||||||
|
})
|
||||||
|
this.modal.find('#tipos').dropdown('change values', values)
|
||||||
|
})
|
||||||
|
},
|
||||||
getParent: function() {
|
getParent: function() {
|
||||||
let parent = $(this.id).find('tbody')
|
let parent = $(this.id).find('tbody')
|
||||||
if (parent.length == 0) {
|
if (parent.length === 0) {
|
||||||
const table = $('<table></table>').attr('class', 'ui table').append(
|
const table = $('<table></table>').attr('class', 'ui table').append(
|
||||||
$('<thead></thead>').append(
|
$('<thead></thead>').append(
|
||||||
$('<tr></tr>').append(
|
$('<tr></tr>').append(
|
||||||
$('<th></th>').html('Categoría')
|
$('<th></th>').html('Categoría')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').html('Tipo')
|
||||||
).append(
|
).append(
|
||||||
$('<th></th>').attr('class', 'right aligned').append(
|
$('<th></th>').attr('class', 'right aligned').append(
|
||||||
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
||||||
@ -47,27 +111,31 @@ const categorias = {
|
|||||||
const parent = this.getParent()
|
const parent = this.getParent()
|
||||||
parent.html('')
|
parent.html('')
|
||||||
$.each(this.categorias, (i, el) => {
|
$.each(this.categorias, (i, el) => {
|
||||||
parent.append(
|
parent.append(el.draw())
|
||||||
$('<tr></tr>').append(
|
|
||||||
$('<td></td>').html(el.nombre)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
add: function() {
|
add: function() {
|
||||||
this.modal.find('form').trigger('reset')
|
this.modal.find('form').trigger('reset')
|
||||||
|
modalToAdd(this.modal)
|
||||||
this.modal.modal('show')
|
this.modal.modal('show')
|
||||||
},
|
},
|
||||||
doAdd: function() {
|
doAdd: function() {
|
||||||
const data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
nombre: $("[name='nombre']").val()
|
nombre: $("[name='nombre']").val(),
|
||||||
|
tipo_id: $("[name='tipo']").val()
|
||||||
})
|
})
|
||||||
return $.ajax({
|
return sendPost(_urls.api + '/categorias/add', data).then((data) => {
|
||||||
url: _urls.api + '/categorias/add',
|
this.modal.modal('hide')
|
||||||
method: 'POST',
|
this.getCategorias()
|
||||||
data: data,
|
})
|
||||||
dataType: 'json'
|
},
|
||||||
}).then((data) => {
|
doEdit: function() {
|
||||||
|
const id = $("[name='id']").val()
|
||||||
|
const data = JSON.stringify({
|
||||||
|
nombre: $("[name='nombre']").val(),
|
||||||
|
tipo_id: $("[name='tipo']").val()
|
||||||
|
})
|
||||||
|
return sendPut(_urls.api + '/categoria/' + id +'/edit', data).then((data) => {
|
||||||
this.modal.modal('hide')
|
this.modal.modal('hide')
|
||||||
this.getCategorias()
|
this.getCategorias()
|
||||||
})
|
})
|
||||||
@ -80,9 +148,16 @@ const categorias = {
|
|||||||
})
|
})
|
||||||
this.modal.find('form').submit((e) => {
|
this.modal.find('form').submit((e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.doAdd()
|
const add = $(e.currentTarget).find('.plus.icon')
|
||||||
|
if (add.length > 0) {
|
||||||
|
this.doAdd()
|
||||||
|
} else {
|
||||||
|
this.doEdit()
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.getTipos()
|
||||||
},
|
},
|
||||||
setup: function() {
|
setup: function() {
|
||||||
this.setupModal()
|
this.setupModal()
|
||||||
|
80
ui/public/assets/scripts/colorpicker.js
Normal file
80
ui/public/assets/scripts/colorpicker.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
class Color {
|
||||||
|
constructor({hex, red, green, blue}) {
|
||||||
|
this.hex = hex ?? 'ffffff'
|
||||||
|
this.red = red ?? 255
|
||||||
|
this.green = green ?? 255
|
||||||
|
this.blue = blue ?? 255
|
||||||
|
}
|
||||||
|
toHex() {
|
||||||
|
this.hex = this.red.toString(16).padStart(2, '0') + this.green.toString(16).padStart(2, '0') + this.blue.toString(16).padStart(2, '0')
|
||||||
|
return this.hex
|
||||||
|
}
|
||||||
|
toRGB() {
|
||||||
|
this.red = parseInt(this.hex.substring(0,2), 16)
|
||||||
|
this.green = parseInt(this.hex.substring(2,4), 16)
|
||||||
|
this.blue = parseInt(this.hex.substring(4), 16)
|
||||||
|
return [this.red, this.green, this.blue]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ColorPicker {
|
||||||
|
constructor(elem) {
|
||||||
|
this.setup(elem)
|
||||||
|
}
|
||||||
|
setup(elem) {
|
||||||
|
this.elem = elem
|
||||||
|
this.color = new Color({})
|
||||||
|
this.color.hex = elem.val()
|
||||||
|
this.color.toRGB()
|
||||||
|
this.holder = $('<div></div>').attr('class', 'color_picker_holder ui horizontal segments')
|
||||||
|
const red = $('<div></div>').attr('class', 'ui basic segment').append($('<div></div>').attr('class', 'ui red slider').slider({
|
||||||
|
max: 255,
|
||||||
|
start: this.color.red,
|
||||||
|
onMove: (content) => {
|
||||||
|
this.color.red = content
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
const green = $('<div></div>').attr('class', 'ui basic segment').append($('<div></div>').attr('class', 'ui green slider').slider({
|
||||||
|
max: 255,
|
||||||
|
start: this.color.green,
|
||||||
|
onMove: (content) => {
|
||||||
|
this.color.green = content
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
const blue = $('<div></div>').attr('class', 'ui basic segment').append($('<div></div>').attr('class', 'ui blue slider').slider({
|
||||||
|
max: 255,
|
||||||
|
start: this.color.blue,
|
||||||
|
onMove: (content) => {
|
||||||
|
this.color.blue = content
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
const color_cell = $('<div></div>').attr('class', 'ui center aligned compact basic segment').append(
|
||||||
|
$('<i></i>').attr('class', 'massive icons').append(
|
||||||
|
$('<i></i>').attr('class', 'square icon color_cell').css('color', '#' + this.color.toHex())
|
||||||
|
).append(
|
||||||
|
$('<i></i>').attr('class', 'square outline icon')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
this.holder.append(color_cell)
|
||||||
|
const vseg = $('<div></div>').attr('class', 'ui basic segments')
|
||||||
|
vseg.append(red)
|
||||||
|
vseg.append(green)
|
||||||
|
vseg.append(blue)
|
||||||
|
this.holder.append($('<div></div>').attr('class', 'ui basic segment').append(vseg))
|
||||||
|
this.elem.after(this.holder)
|
||||||
|
}
|
||||||
|
setColor(color) {
|
||||||
|
this.color.hex = color
|
||||||
|
this.color.toRGB()
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
this.elem.val(this.color.toHex())
|
||||||
|
this.holder.find('.red').slider('set value', this.color.red, false)
|
||||||
|
this.holder.find('.green').slider('set value', this.color.green, false)
|
||||||
|
this.holder.find('.blue').slider('set value', this.color.blue, false)
|
||||||
|
this.holder.find('.color_cell').css('color', '#' + this.color.toHex())
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +1,126 @@
|
|||||||
|
class Cuenta {
|
||||||
|
constructor({id, nombre, categoria_id, tipo_id, moneda_id, categoria, tipo, moneda, saldo, saldoFormateado}) {
|
||||||
|
this.id = id
|
||||||
|
this.nombre = nombre
|
||||||
|
this.categoria_id = categoria_id
|
||||||
|
this.tipo_id = tipo_id
|
||||||
|
this.moneda_id = moneda_id
|
||||||
|
this.categoria = categoria
|
||||||
|
this.tipo = tipo
|
||||||
|
this.moneda = moneda
|
||||||
|
this.modal = null
|
||||||
|
}
|
||||||
|
setModal(modal) {
|
||||||
|
this.modal = modal
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
return $('<tr></tr>').append(
|
||||||
|
$('<td></td>').html(this.nombre)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').html(this.categoria.nombre + ' - ' + this.categoria.tipo.descripcion)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').css('color', '#' + this.tipo.color).html(this.tipo.descripcion)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').html(this.moneda.codigo)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'edit icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.edit()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
).append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular red icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'remove icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.remove()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
edit() {
|
||||||
|
const form = this.modal.find('form')
|
||||||
|
form.trigger('reset')
|
||||||
|
form.find("[name='id']").val(this.id)
|
||||||
|
form.find("[name='nombre']").val(this.nombre)
|
||||||
|
form.find("[name='categoria']").dropdown('set selected', this.categoria.id)
|
||||||
|
form.find("[name='tipo']").dropdown('set selected', this.tipo.id)
|
||||||
|
form.find("[name='moneda']").dropdown('set selected', this.moneda.id)
|
||||||
|
modalToEdit(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
sendDelete(_urls.api + '/cuenta/' + this.id + '/delete').then(() => {
|
||||||
|
cuentas.get().cuentas()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const cuentas = {
|
const cuentas = {
|
||||||
id: '#cuentas',
|
id: '#cuentas',
|
||||||
cuentas: [],
|
cuentas: [],
|
||||||
categorias: [],
|
categorias: [],
|
||||||
getCuentas: function() {
|
tipos: [],
|
||||||
return $.ajax({
|
get: function() {
|
||||||
url: _urls.api + '/cuentas',
|
return {
|
||||||
method: 'GET',
|
cuentas: () => {
|
||||||
dataType: 'json'
|
this.cuentas = []
|
||||||
}).then((data) => {
|
return sendGet(_urls.api + '/cuentas').then((data) => {
|
||||||
if (data.cuentas === null || data.cuentas.length == 0) {
|
if (data.cuentas === null || data.cuentas.length === 0) {
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
$.each(data.cuentas, (i, el) => {
|
||||||
|
const cuenta = new Cuenta(el)
|
||||||
|
cuenta.setModal(this.modal)
|
||||||
|
this.cuentas.push(cuenta)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().cuentas()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
categorias: () => {
|
||||||
|
return sendGet(_urls.api + '/categorias').then((data) => {
|
||||||
|
if (data.categorias === null || data.categorias.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.categorias = data.categorias
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().categorias()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tipos: () => {
|
||||||
|
return sendGet(_urls.api + '/tipos/cuentas').then((data) => {
|
||||||
|
if (data.tipos === null || data.tipos.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.tipos = data.tipos
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().tipos()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
monedas: () => {
|
||||||
|
return sendGet(_urls.api + '/monedas').then((data) => {
|
||||||
|
if (data.monedas === null || data.monedas.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.monedas = data.monedas
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().monedas()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
parent: () => {
|
||||||
|
const segment = $(this.id)
|
||||||
|
let parent = segment.find('tbody')
|
||||||
|
if (parent.length === 0) {
|
||||||
|
parent = this.buildParent(segment)
|
||||||
|
}
|
||||||
|
return parent
|
||||||
}
|
}
|
||||||
this.cuentas = data.cuentas
|
}
|
||||||
}).then(() => {
|
|
||||||
this.draw()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getCategorias: function() {
|
|
||||||
return $.ajax({
|
|
||||||
url: _urls.api + '/categorias',
|
|
||||||
method: 'GET',
|
|
||||||
dataType: 'json'
|
|
||||||
}).then((data) => {
|
|
||||||
if (data.categorias === null || data.categorias.length == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.categorias = data.categorias
|
|
||||||
}).then(() => {
|
|
||||||
this.drawCategorias()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
drawCategorias: function() {
|
|
||||||
const select = $("[name='categoria']")
|
|
||||||
$.each(this.categorias, (i, el) => {
|
|
||||||
select.append(
|
|
||||||
$('<option></option>').attr('value', el.id).html(el.nombre)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
buildParent: function(segment) {
|
buildParent: function(segment) {
|
||||||
const table = $('<table></table>').attr('class', 'ui table').append(
|
const table = $('<table></table>').attr('class', 'ui table').append(
|
||||||
@ -45,6 +129,10 @@ const cuentas = {
|
|||||||
$('<th></th>').html('Cuenta')
|
$('<th></th>').html('Cuenta')
|
||||||
).append(
|
).append(
|
||||||
$('<th></th>').html('Categoría')
|
$('<th></th>').html('Categoría')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').html('Tipo')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').html('Moneda')
|
||||||
).append(
|
).append(
|
||||||
$('<th></th>').attr('class', 'right aligned').append(
|
$('<th></th>').attr('class', 'right aligned').append(
|
||||||
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
||||||
@ -64,44 +152,73 @@ const cuentas = {
|
|||||||
segment.append(table)
|
segment.append(table)
|
||||||
return parent
|
return parent
|
||||||
},
|
},
|
||||||
getParent: function() {
|
|
||||||
const segment = $(this.id)
|
|
||||||
let parent = segment.find('tbody')
|
|
||||||
if (parent.length == 0) {
|
|
||||||
parent = this.buildParent(segment)
|
|
||||||
}
|
|
||||||
return parent
|
|
||||||
},
|
|
||||||
draw: function() {
|
draw: function() {
|
||||||
const parent = this.getParent()
|
return {
|
||||||
parent.html('')
|
cuentas: () => {
|
||||||
$.each(this.cuentas, (i, el) => {
|
const parent = this.get().parent()
|
||||||
parent.append(
|
parent.html('')
|
||||||
$('<tr></tr>').append(
|
$.each(this.cuentas, (i, el) => {
|
||||||
$('<td></td>').html(el.nombre)
|
parent.append(el.draw())
|
||||||
).append(
|
})
|
||||||
$('<td></td>').html(el.categoria.nombre)
|
},
|
||||||
)
|
categorias: () => {
|
||||||
)
|
const select = $("[name='categoria']")
|
||||||
})
|
$.each(this.categorias, (i, el) => {
|
||||||
|
select.append(
|
||||||
|
$('<option></option>').attr('value', el.id).html(el.nombre + ' - ' + el.tipo.descripcion)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tipos: () => {
|
||||||
|
const select = $("[name='tipo']")
|
||||||
|
$.each(this.tipos, (i, el) => {
|
||||||
|
select.append($('<option></option>').attr('value', el.id).html(el.descripcion))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
monedas: () => {
|
||||||
|
const select = $("[name='moneda']")
|
||||||
|
$.each(this.monedas, (i, el) => {
|
||||||
|
const opt = $('<option></option>').attr('value', el.id).html(el.denominacion)
|
||||||
|
if (el.codigo === 'CLP') {
|
||||||
|
opt.attr('selected', 'selected')
|
||||||
|
}
|
||||||
|
select.append(opt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
add: function() {
|
add: function() {
|
||||||
this.modal.find('form').trigger('reset')
|
this.modal.find('form').trigger('reset')
|
||||||
|
modalToAdd(this.modal)
|
||||||
this.modal.modal('show')
|
this.modal.modal('show')
|
||||||
},
|
},
|
||||||
doAdd: function() {
|
doAdd: function() {
|
||||||
const data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
categoria_id: $("[name='categoria']").val(),
|
categoria_id: $("[name='categoria']").val(),
|
||||||
nombre: $("[name='nombre']").val()
|
nombre: $("[name='nombre']").val(),
|
||||||
|
tipo_id: $("[name='tipo']").val(),
|
||||||
|
moneda_id: $("[name='moneda']").val()
|
||||||
})
|
})
|
||||||
return $.ajax({
|
return sendPost(
|
||||||
url: _urls.api + '/cuentas/add',
|
_urls.api + '/cuentas/add',
|
||||||
method: 'POST',
|
data
|
||||||
data: data,
|
).then((data) => {
|
||||||
dataType: 'json'
|
|
||||||
}).then((data) => {
|
|
||||||
this.modal.modal('hide')
|
this.modal.modal('hide')
|
||||||
this.getCuentas()
|
this.get().cuentas()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
doEdit: function() {
|
||||||
|
form = this.modal.find('form')
|
||||||
|
const id = form.find("[name='id']").val()
|
||||||
|
const data = JSON.stringify({
|
||||||
|
nombre: form.find("[name='nombre']").val(),
|
||||||
|
categoria_id: form.find("[name='categoria']").val(),
|
||||||
|
tipo_id: form.find("[name='tipo']").val(),
|
||||||
|
moneda_id: form.find("[name='moneda']").val()
|
||||||
|
})
|
||||||
|
sendPut(_urls.api + '/cuenta/' + id + '/edit', data).then(() => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
this.get().cuentas()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setupModal: function() {
|
setupModal: function() {
|
||||||
@ -112,14 +229,23 @@ const cuentas = {
|
|||||||
})
|
})
|
||||||
this.modal.find('form').submit((e) => {
|
this.modal.find('form').submit((e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.doAdd()
|
const add = $(e.currentTarget).find('.ui.button').find('.plus.icon')
|
||||||
|
if (add.length > 0) {
|
||||||
|
this.doAdd()
|
||||||
|
} else {
|
||||||
|
this.doEdit()
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setup: function() {
|
setup: function() {
|
||||||
this.setupModal()
|
this.setupModal()
|
||||||
this.getCuentas().then(() => {
|
this.get().cuentas().then(() => {
|
||||||
this.getCategorias()
|
this.get().categorias().then(() => {
|
||||||
|
this.get().tipos().then(() => {
|
||||||
|
this.get().monedas()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +1,272 @@
|
|||||||
|
class Transaccion {
|
||||||
|
constructor({id, debito_id, credito_id, fecha, glosa, detalle, valor, debito, credito, fechaFormateada, valorFormateado, format}) {
|
||||||
|
this.id = id
|
||||||
|
this.debito_id = debito_id
|
||||||
|
this.credito_id = credito_id
|
||||||
|
this.fecha = {
|
||||||
|
fecha,
|
||||||
|
formateada: fechaFormateada
|
||||||
|
}
|
||||||
|
this.glosa = glosa
|
||||||
|
this.detalle = detalle
|
||||||
|
this.valor = {
|
||||||
|
valor,
|
||||||
|
formateado: valorFormateado
|
||||||
|
}
|
||||||
|
this.format_array = format
|
||||||
|
this.debito = debito
|
||||||
|
this.credito = credito
|
||||||
|
this.modal = null
|
||||||
|
}
|
||||||
|
setCuenta(cuenta) {
|
||||||
|
this.cuenta = cuenta
|
||||||
|
}
|
||||||
|
setModal(modal) {
|
||||||
|
this.modal = modal
|
||||||
|
}
|
||||||
|
isDebito() {
|
||||||
|
return this.debito.id === this.cuenta.id;
|
||||||
|
}
|
||||||
|
isIncrement() {
|
||||||
|
const debits = ['Activo', 'Perdida']
|
||||||
|
if (debits.indexOf(this.cuenta.tipo.descripcion)) {
|
||||||
|
return this.isDebito()
|
||||||
|
}
|
||||||
|
return !this.isDebito()
|
||||||
|
}
|
||||||
|
draw({saldo, format, format_array, format_call}) {
|
||||||
|
const fuente = (this.isDebito()) ? this.credito : this.debito
|
||||||
|
return $('<tr></tr>').append(
|
||||||
|
$('<td></td>').html(this.fecha.formateada)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<a></a>').attr('href', _urls.base + 'cuenta/' + fuente.id).html(fuente.nombre + ' (' + fuente.categoria.nombre + ')')
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').html(this.glosa + '<br />' + this.detalle)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html((this.isIncrement()) ? '' : format_call({value: this.valor.valor, format, format_array}))
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html((this.isIncrement()) ? format_call({value: this.valor.valor, format, format_array}) : '')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html(format_call({value: saldo, format_array, format}))
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'edit icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.edit()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
).append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular red icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'remove icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.remove()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
edit() {
|
||||||
|
const form = this.modal.find('form')
|
||||||
|
form.trigger('reset')
|
||||||
|
form.find("[name='id']").val(this.id)
|
||||||
|
form.find(".ui.calendar").calendar('set date', new Date(this.fecha.fecha))
|
||||||
|
form.find("[name='cuenta']").dropdown('set selected', (this.isDebito()) ? this.credito_id : this.credito_id)
|
||||||
|
form.find("[name='glosa']").val(this.glosa)
|
||||||
|
form.find("[name='detalle']").val(this.detalle)
|
||||||
|
form.find("[name='valor']").val(((this.isDebito()) ? -1 : 1) * this.valor.valor)
|
||||||
|
modalToEdit(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
sendDelete(_urls.api + '/transaccion/' + this.id + '/delete').then(() => {
|
||||||
|
transacciones.get().transacciones()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const transacciones = {
|
const transacciones = {
|
||||||
id: '#transacciones',
|
id: '#transacciones',
|
||||||
|
mes: '#mes',
|
||||||
|
buttons: {
|
||||||
|
prev: '#prev_button',
|
||||||
|
left: '#left_button',
|
||||||
|
right: '#right_button',
|
||||||
|
next: '#next_button'
|
||||||
|
},
|
||||||
cuenta_id: 0,
|
cuenta_id: 0,
|
||||||
cuenta: null,
|
cuenta: null,
|
||||||
transacciones: [],
|
transacciones: [],
|
||||||
cuentas: [],
|
cuentas: [],
|
||||||
|
date: new Date(),
|
||||||
saldo: 0,
|
saldo: 0,
|
||||||
|
acumulation: 0,
|
||||||
|
intl_format: null,
|
||||||
|
max: null,
|
||||||
|
format: ({format_array, format, value}) => {
|
||||||
|
let output = []
|
||||||
|
if (format_array.prefijo !== '') {
|
||||||
|
output.push(format_array.prefijo)
|
||||||
|
}
|
||||||
|
output.push(format.format(Math.round(value * Math.pow(10, format_array.decimales)) / Math.pow(10, format_array.decimales)))
|
||||||
|
if (format_array.sufijo !== '') {
|
||||||
|
output.push(format_array.sufijo)
|
||||||
|
}
|
||||||
|
return output.join('')
|
||||||
|
},
|
||||||
get: function() {
|
get: function() {
|
||||||
return {
|
return {
|
||||||
transacciones: () => {
|
transacciones: () => {
|
||||||
|
this.draw().loading()
|
||||||
let promises = []
|
let promises = []
|
||||||
$.ajax({
|
sendGet(_urls.api + '/cuenta/' + this.cuenta_id + '/transacciones/amount').then((data) => {
|
||||||
url: _urls.api + '/cuenta/' + this.cuenta_id + '/transacciones/amount',
|
if (data.cuenta === null) {
|
||||||
method: 'GET',
|
return
|
||||||
dataType: 'json'
|
|
||||||
}).then((data) => {
|
|
||||||
const amount = data.transacciones
|
|
||||||
const step = 100
|
|
||||||
for (let i = 0; i < amount; i += step) {
|
|
||||||
promises.push($.ajax({
|
|
||||||
url: _urls.api + '/cuenta/' + this.cuenta_id + '/transacciones/' + step + '/' + i,
|
|
||||||
method: 'GET',
|
|
||||||
dataType: 'json'
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
Promise.all(promises).then((data_arr) => {
|
this.cuenta = data.cuenta
|
||||||
if (data_arr[0].cuenta === null) {
|
this.intl_format = Intl.NumberFormat('es-CL')
|
||||||
return
|
sendGet(_urls.api + '/cuenta/' + this.cuenta_id + '/categoria').then((resp) => {
|
||||||
}
|
this.cuenta.categoria = resp.categoria
|
||||||
this.cuenta = data_arr[0].cuenta
|
|
||||||
this.saldo = this.cuenta.saldo
|
|
||||||
$('#cuenta').html(this.cuenta.nombre + ' (' + this.cuenta.categoria.nombre + ')')
|
|
||||||
this.transacciones = []
|
|
||||||
data_arr.forEach(data => {
|
|
||||||
if (data.transacciones === null || data.transacciones.length == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.transacciones.push(...data.transacciones)
|
|
||||||
})
|
|
||||||
this.transacciones.sort((a, b) => {
|
|
||||||
return (new Date(b.fecha)) - (new Date(a.fecha))
|
|
||||||
})
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.draw()
|
//this.saldo = this.cuenta.saldo
|
||||||
|
$('#cuenta').html(this.cuenta.nombre + ' (' + this.cuenta.categoria.nombre + ')').append(
|
||||||
|
$('<i></i>').attr('class', 'square full icon').css('color', '#' + this.cuenta.tipo.color)
|
||||||
|
)
|
||||||
|
promises = this.get().transaccionesMes(this.date)
|
||||||
|
if (promises.length > 0) {
|
||||||
|
Promise.all(promises).then((data_arr) => {
|
||||||
|
this.transacciones = []
|
||||||
|
data_arr.forEach(data => {
|
||||||
|
if (data.transacciones === null || data.transacciones.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(data.transacciones, (i, el) => {
|
||||||
|
const tr = new Transaccion(el)
|
||||||
|
tr.setCuenta(this.cuenta)
|
||||||
|
tr.setModal(this.modal)
|
||||||
|
this.transacciones.push(tr)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.transacciones.sort((a, b) => {
|
||||||
|
return (new Date(b.fecha)) - (new Date(a.fecha))
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.get().transaccionesAcumulacion(this.date).then((response) => {
|
||||||
|
this.acumulation = response.acumulation
|
||||||
|
this.saldo = response.acumulation
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().table()
|
||||||
|
this.draw().acumulation()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.draw().table()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
transaccionesLimit: () => {
|
||||||
|
let promises = []
|
||||||
|
const amount = data.transacciones
|
||||||
|
let step = 50
|
||||||
|
let start = 0
|
||||||
|
if (this.max !== null) {
|
||||||
|
step = Math.min(50, this.max)
|
||||||
|
start = Math.max(0, amount - this.max)
|
||||||
|
}
|
||||||
|
for (let i = start; i <= amount; i += step) {
|
||||||
|
promises.push(
|
||||||
|
sendGet(_urls.api + '/cuenta/' + this.cuenta_id + '/transacciones/' + step + '/' + i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return promises
|
||||||
|
},
|
||||||
|
transaccionesMes: (mes) => {
|
||||||
|
let promises = []
|
||||||
|
promises.push(
|
||||||
|
sendGet(_urls.api + '/cuenta/' + this.cuenta_id + '/transacciones/month/' + [mes.getFullYear(), mes.getMonth() + 1, '1'].join('-'))
|
||||||
|
)
|
||||||
|
return promises
|
||||||
|
},
|
||||||
|
transaccionesAcumulacion: (mes) => {
|
||||||
|
return sendGet(_urls.api + '/cuenta/' + this.cuenta_id + '/transacciones/acum/' + [mes.getFullYear(), mes.getMonth() + 1, '1'].join('-'))
|
||||||
|
},
|
||||||
cuentas: () => {
|
cuentas: () => {
|
||||||
return $.ajax({
|
return sendGet(_urls.api + '/cuentas').then((data) => {
|
||||||
url: _urls.api + '/cuentas',
|
if (data.cuentas === null || data.cuentas.length === 0) {
|
||||||
method: 'GET',
|
|
||||||
dataType: 'json'
|
|
||||||
}).then((data) => {
|
|
||||||
if (data.cuentas === null || data.cuentas.length == 0) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.cuentas = data.cuentas
|
this.cuentas = data.cuentas
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
const select = this.modal.find("[name='cuenta']")
|
const select = this.modal.find("[name='cuenta']")
|
||||||
$.each(this.cuentas, (i, el) => {
|
$.each(this.cuentas, (i, el) => {
|
||||||
select.append(
|
this.get().categoria(i).then(() => {
|
||||||
$('<option></option>').attr('value', el.id).html(el.nombre + ' (' + el.categoria.nombre + ')')
|
select.append(
|
||||||
)
|
$('<option></option>').attr('value', el.id).html(el.nombre + ' (' + el.categoria.nombre + ')')
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
categoria: (idx) => {
|
||||||
|
return sendGet(_urls.api + '/cuenta/' + this.cuentas[idx].id + '/categoria').then((data) => {
|
||||||
|
this.cuentas[idx].categoria = data.categoria
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
draw: function() {
|
draw: function() {
|
||||||
const format = Intl.NumberFormat('es-CL', {style: 'currency', currency: 'CLP'})
|
return {
|
||||||
const parent = $(this.id)
|
loading: () => {
|
||||||
parent.html('')
|
const parent = $(this.id)
|
||||||
$.each(this.transacciones, (i, el) => {
|
parent.html('')
|
||||||
const fuente = (el.valor < 0) ? el.hasta : el.desde
|
parent.append(
|
||||||
parent.append(
|
$('<tr></tr>').append(
|
||||||
$('<tr></tr>').append(
|
$('<td></td>').attr('colspan', 7).append(
|
||||||
$('<td></td>').html(el.fechaFormateada)
|
$('<div></div>').attr('class', 'ui active dimmer').append(
|
||||||
).append(
|
$('<div></div>').attr('class', 'ui indeterminate elastic text loader').html('Buscando los datos')
|
||||||
$('<td></td>').append(
|
)
|
||||||
$('<a></a>').attr('href', _urls.base + 'cuenta/' + fuente.id).html(fuente.nombre + ' (' + fuente.categoria.nombre + ')')
|
)
|
||||||
)
|
)
|
||||||
).append(
|
|
||||||
$('<td></td>').html(el.glosa + '<br />' + el.detalle)
|
|
||||||
).append(
|
|
||||||
$('<td></td>').attr('class', 'right aligned').html((el.valor < 0) ? '' : el.valorFormateado.replace('-', ''))
|
|
||||||
).append(
|
|
||||||
$('<td></td>').attr('class', 'right aligned').html((el.valor < 0) ? el.valorFormateado.replace('-', '') : '')
|
|
||||||
).append(
|
|
||||||
$('<td></td>').attr('class', 'right aligned').html(format.format(this.saldo))
|
|
||||||
)
|
)
|
||||||
)
|
},
|
||||||
this.saldo -= parseInt(el.valor)
|
table: () => {
|
||||||
})
|
const parent = $(this.id)
|
||||||
|
parent.html('')
|
||||||
|
$.each(this.transacciones, (i, el) => {
|
||||||
|
this.saldo = this.saldo + parseInt(el.valor.valor) * ((el.isIncrement()) ? 1 : -1)
|
||||||
|
parent.append(el.draw({saldo: this.saldo, format: this.intl_format, format_array: this.cuenta.moneda.format, format_call: this.format}))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
acumulation: () => {
|
||||||
|
const parent = $(this.id)
|
||||||
|
parent.prepend(
|
||||||
|
$('<tr></tr>').append(
|
||||||
|
$('<td></td>').html('')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').html('')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').html('Acumulacion Anterior')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html('')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html('')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html(this.format({
|
||||||
|
format_array: this.cuenta.moneda.format,
|
||||||
|
format: this.intl_format,
|
||||||
|
value: this.acumulation
|
||||||
|
}))
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html('')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
add: function() {
|
add: function() {
|
||||||
return {
|
return {
|
||||||
@ -99,28 +275,82 @@ const transacciones = {
|
|||||||
this.modal.modal('show')
|
this.modal.modal('show')
|
||||||
},
|
},
|
||||||
exec: () => {
|
exec: () => {
|
||||||
|
const fecha = $("[name='fecha']").val()
|
||||||
|
const data1 = JSON.stringify({
|
||||||
|
desde_id: $("[name='moneda']").val(),
|
||||||
|
hasta_id: 1,
|
||||||
|
fecha: fecha,
|
||||||
|
valor: $("[name='cambio']").val()
|
||||||
|
})
|
||||||
|
sendPost(_urls.api + '/tipos/cambios/add', data1)
|
||||||
|
|
||||||
const valor = $("[name='valor']").val()
|
const valor = $("[name='valor']").val()
|
||||||
const cuenta = $("[name='cuenta']").val()
|
const cuenta = $("[name='cuenta']").val()
|
||||||
const data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
desde_id: (valor < 0) ? this.cuenta_id : cuenta,
|
debito_id: (valor < 0) ? this.cuenta_id : cuenta,
|
||||||
hasta_id: (valor < 0) ? cuenta : this.cuenta_id,
|
credito_id: (valor < 0) ? cuenta : this.cuenta_id,
|
||||||
fecha: $("[name='fecha']").val(),
|
fecha: fecha,
|
||||||
glosa: $("[name='glosa']").val(),
|
glosa: $("[name='glosa']").val(),
|
||||||
detalle: $("[name='detalle']").val(),
|
detalle: $("[name='detalle']").val(),
|
||||||
valor: (valor < 0) ? -valor : valor
|
valor: (valor < 0) ? -valor : valor
|
||||||
})
|
})
|
||||||
return $.ajax({
|
return sendPost(_urls.api + '/transacciones/add', data).then(() => {
|
||||||
url: _urls.api + '/transacciones/add',
|
|
||||||
method: 'POST',
|
|
||||||
data: data,
|
|
||||||
dataType: 'json'
|
|
||||||
}).then(() => {
|
|
||||||
this.modal.modal('hide')
|
this.modal.modal('hide')
|
||||||
this.get().transacciones()
|
this.get().transacciones()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
edit: function() {
|
||||||
|
const id = $("[name='id']").val()
|
||||||
|
const fecha = $("[name='fecha']").val()
|
||||||
|
const cuenta = $("[name='cuenta']").val()
|
||||||
|
const glosa = $("[name='glosa']").val()
|
||||||
|
const detalle = $("[name='detalle']").val()
|
||||||
|
const valor = $("[name='valor']").val()
|
||||||
|
const data = JSON.stringify({
|
||||||
|
debito_id: (valor < 0) ? this.cuenta_id : cuenta,
|
||||||
|
credito_id: (valor < 0) ? cuenta : this.cuenta_id,
|
||||||
|
fecha,
|
||||||
|
glosa,
|
||||||
|
detalle,
|
||||||
|
valor: (valor < 0) ? -valor : valor
|
||||||
|
})
|
||||||
|
return sendPut(_urls.api + '/transaccion/' + id + '/edit', data).then(() => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
this.get().transacciones()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changeMonth: function(dif) {
|
||||||
|
let d = this.date
|
||||||
|
d.setMonth(this.date.getMonth() + dif)
|
||||||
|
this.date = d
|
||||||
|
this.checkButtons()
|
||||||
|
$(this.mes).calendar('set date', this.date)
|
||||||
|
},
|
||||||
|
changeYear: function(dif) {
|
||||||
|
let d = this.date
|
||||||
|
d.setFullYear(this.date.getFullYear() + dif)
|
||||||
|
this.date = d
|
||||||
|
this.checkButtons()
|
||||||
|
$(this.mes).calendar('set date', this.date)
|
||||||
|
},
|
||||||
|
checkButtons: function() {
|
||||||
|
let f = new Date()
|
||||||
|
if (this.date.getMonth() === f.getMonth() && this.date.getFullYear() === f.getFullYear()) {
|
||||||
|
$(this.buttons.right).addClass('disabled')
|
||||||
|
} else {
|
||||||
|
$(this.buttons.right).removeClass('disabled')
|
||||||
|
}
|
||||||
|
if (this.date.getFullYear() === f.getFullYear()) {
|
||||||
|
$(this.buttons.next).addClass('disabled')
|
||||||
|
} else {
|
||||||
|
$(this.buttons.next).removeClass('disabled')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refresh: function () {
|
||||||
|
this.get().transacciones()
|
||||||
|
},
|
||||||
build: function() {
|
build: function() {
|
||||||
return {
|
return {
|
||||||
modal: () => {
|
modal: () => {
|
||||||
@ -131,7 +361,12 @@ const transacciones = {
|
|||||||
})
|
})
|
||||||
this.modal.find('form').submit((e) => {
|
this.modal.find('form').submit((e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.add().exec()
|
const add = $(e.currentTarget).find('.plus.icon')
|
||||||
|
if (add.length > 0) {
|
||||||
|
this.add().exec()
|
||||||
|
} else {
|
||||||
|
this.edit()
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
this.modal.find('.ui.calendar').calendar({
|
this.modal.find('.ui.calendar').calendar({
|
||||||
@ -148,12 +383,53 @@ const transacciones = {
|
|||||||
maxDate: new Date()
|
maxDate: new Date()
|
||||||
})
|
})
|
||||||
this.get().cuentas()
|
this.get().cuentas()
|
||||||
|
},
|
||||||
|
mes: () => {
|
||||||
|
const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Dicembre']
|
||||||
|
$(this.mes).calendar({
|
||||||
|
type: 'month',
|
||||||
|
initialDate: this.date,
|
||||||
|
maxDate: new Date(),
|
||||||
|
text: {
|
||||||
|
months: meses,
|
||||||
|
monthsShort: meses.map((item) => {item.slice(0, 3)})
|
||||||
|
},
|
||||||
|
formatter: {
|
||||||
|
date: function (date, settings) {
|
||||||
|
if (!date) return '';
|
||||||
|
return meses[date.getMonth()] + ', ' + date.getFullYear()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChange: (date) => {
|
||||||
|
this.date = date
|
||||||
|
this.refresh()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
buttons: () => {
|
||||||
|
$(this.buttons.right).click((e) => {
|
||||||
|
this.changeMonth(1)
|
||||||
|
})
|
||||||
|
$(this.buttons.left).click((e) => {
|
||||||
|
this.changeMonth(-1)
|
||||||
|
})
|
||||||
|
$(this.buttons.next).click(() => {
|
||||||
|
this.changeYear(1)
|
||||||
|
})
|
||||||
|
$(this.buttons.prev).click(() => {
|
||||||
|
this.changeYear(-1)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup: function() {
|
setup: function() {
|
||||||
this.build().modal()
|
this.build().modal()
|
||||||
$(this.id).parent().find('.ui.button').click(() => {
|
this.build().mes()
|
||||||
|
this.build().buttons()
|
||||||
|
$(this.id).parent().find('#refresh').click(() => {
|
||||||
|
this.refresh()
|
||||||
|
})
|
||||||
|
$(this.id).parent().find('#add').click(() => {
|
||||||
this.add().show()
|
this.add().show()
|
||||||
})
|
})
|
||||||
this.get().transacciones()
|
this.get().transacciones()
|
||||||
|
@ -1,127 +1,361 @@
|
|||||||
|
const down_icon = 'right chevron'
|
||||||
|
const up_icon = 'down chevron'
|
||||||
|
|
||||||
|
class Cuenta {
|
||||||
|
constructor({id, nombre, tipo_id, categoria_id, tipo, saldo, saldoFormateado}) {
|
||||||
|
this.id = id
|
||||||
|
this.nombre = nombre
|
||||||
|
this.tipo_id = tipo_id
|
||||||
|
this.categoria_id = categoria_id
|
||||||
|
this.tipo = tipo
|
||||||
|
this.saldo = {
|
||||||
|
saldo,
|
||||||
|
formateado: saldoFormateado
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTipos(tipos) {
|
||||||
|
this.tipos = tipos
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
const tr = $('<tr></tr>').attr('data-id', this.id).attr('data-class', 'cuenta').append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<i></i>').attr('class', 'angle right icon')
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<i></i>').attr('class', 'angle right icon')
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<a></a>').attr('href', _urls.base + 'cuenta/' + this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'square full icon').css('color', '#' + this.tipo.color)
|
||||||
|
).append(this.nombre)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
$.each(this.tipos, (i, m) => {
|
||||||
|
const td = $('<td></td>').attr('class', 'right aligned')
|
||||||
|
if (m.descripcion === this.tipo.descripcion) {
|
||||||
|
td.html(this.saldo.formateado)
|
||||||
|
}
|
||||||
|
tr.append(td)
|
||||||
|
})
|
||||||
|
const prev = this.prev()
|
||||||
|
prev.after(tr)
|
||||||
|
}
|
||||||
|
prev() {
|
||||||
|
let prev = $("[data-id='" + this.categoria_id + "'][data-class='categoria']")
|
||||||
|
let n = 0
|
||||||
|
while (prev.next().attr('data-class') === 'cuenta') {
|
||||||
|
prev = prev.next()
|
||||||
|
n ++;
|
||||||
|
if (n >= 100) {
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
$("[data-id='" + this.id + "'][data-class='cuenta']").remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Categoria {
|
||||||
|
constructor({id, nombre, tipo_id, tipo, totales}) {
|
||||||
|
this.id = id
|
||||||
|
this.nombre = nombre
|
||||||
|
this.tipo_id = tipo_id
|
||||||
|
this.tipo = tipo
|
||||||
|
this.totales = totales
|
||||||
|
this.is_open = false
|
||||||
|
this.cuentas = []
|
||||||
|
}
|
||||||
|
setTipos(tipos) {
|
||||||
|
this.tipos = tipos
|
||||||
|
}
|
||||||
|
draw({format}) {
|
||||||
|
const button = $('<button></button>').attr('class', 'ui mini compact circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', down_icon + ' icon')
|
||||||
|
).click((e) => {
|
||||||
|
const plus = button.find('.' + down_icon.replace(' ', '.') + '.icon')
|
||||||
|
if (plus.length > 0) {
|
||||||
|
this.loadCuentas()
|
||||||
|
button.find('i.icon').removeClass(down_icon).addClass(up_icon)
|
||||||
|
this.is_open = true
|
||||||
|
} else {
|
||||||
|
this.removeCuentas()
|
||||||
|
button.find('i.icon').removeClass(up_icon).addClass(down_icon)
|
||||||
|
this.is_open = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const tr = $('<tr></tr>').attr('data-id', this.id).attr('data-class', 'categoria').append(
|
||||||
|
$('<td></td>').append($('<span></span>').html('<i class="angle right icon"></i>'))
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('colspan', 2).append(
|
||||||
|
$('<div></div>').append(button).append(this.nombre)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
$.each(this.tipos, (i, el) => {
|
||||||
|
tr.append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').html(format.format(this.totales[el.descripcion.toLowerCase() + 's']))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
$("[data-id='" + this.tipo_id + "'][data-class='tipo_categoria']").after(tr)
|
||||||
|
if (this.is_open) {
|
||||||
|
button.trigger('click')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
this.removeCuentas()
|
||||||
|
$("[data-id='" + this.id + "'][data-class='categoria']").remove()
|
||||||
|
}
|
||||||
|
loadCuentas() {
|
||||||
|
if (this.cuentas.length === 0) {
|
||||||
|
sendGet(_urls.api + '/categoria/' + this.id + '/cuentas').then((data) => {
|
||||||
|
if (data.cuentas === null || data.cuentas.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(data.cuentas, (i, el) => {
|
||||||
|
const cuenta = new Cuenta(el)
|
||||||
|
cuenta.setTipos(this.tipos)
|
||||||
|
this.cuentas.push(cuenta)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.drawCuentas()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.drawCuentas()
|
||||||
|
}
|
||||||
|
drawCuentas() {
|
||||||
|
if (this.cuentas.length === 0) {
|
||||||
|
$("[data-id='"+this.id+"'][data-class='categoria']").after(
|
||||||
|
$('<tr></tr>').attr('data-class', 'cuenta').attr('data-id', 'vacio').append(
|
||||||
|
$('<td></td>')
|
||||||
|
).append(
|
||||||
|
$('<td></td>')
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('colspan', 5).html('No hay cuentas.')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(this.cuentas, (i, el) => {
|
||||||
|
el.draw()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
removeCuentas() {
|
||||||
|
if (this.cuentas.length === 0) {
|
||||||
|
$("tr[data-class='cuenta'][data-id='vacio']").remove()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(this.cuentas, (i, el) => {
|
||||||
|
el.remove()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TipoCategoria {
|
||||||
|
constructor({id, descripcion, activo, totales}) {
|
||||||
|
this.id = id
|
||||||
|
this.descripcion = descripcion
|
||||||
|
this.activo = activo
|
||||||
|
this.totales = totales
|
||||||
|
this.categorias = []
|
||||||
|
}
|
||||||
|
setTipos(tipos) {
|
||||||
|
this.tipos = tipos
|
||||||
|
}
|
||||||
|
draw({format}) {
|
||||||
|
const button = $('<button></button>').attr('class', 'ui mini compact circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', down_icon + ' icon')
|
||||||
|
).click((e) => {
|
||||||
|
const plus = button.find('.' + down_icon.replace(' ', '.') + '.icon')
|
||||||
|
if (plus.length > 0) {
|
||||||
|
this.loadCategorias()
|
||||||
|
button.find('i.icon').removeClass(down_icon).addClass(up_icon)
|
||||||
|
} else {
|
||||||
|
this.removeCategorias()
|
||||||
|
button.find('i.icon').removeClass(up_icon).addClass(down_icon)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const tr = $('<tr></tr>').attr('data-id', this.id).attr('data-class', 'tipo_categoria').append(
|
||||||
|
$('<td></td>').attr('colspan', 3).append(
|
||||||
|
$('<div></div>').append(button).append(this.descripcion)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
$.each(this.tipos, (i, el) => {
|
||||||
|
tr.append($('<td></td>').attr('class', 'right aligned').html(format.format(this.totales[el.descripcion.toLowerCase() + 's'])))
|
||||||
|
})
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
loadCategorias() {
|
||||||
|
if (this.categorias.length === 0) {
|
||||||
|
sendGet(_urls.api + '/tipos/categoria/' + this.id + '/categorias').then((data) => {
|
||||||
|
if (data.categorias === null || data.categorias.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(data.categorias, (i, el) => {
|
||||||
|
const cat = new Categoria(el)
|
||||||
|
cat.setTipos(this.tipos)
|
||||||
|
this.categorias.push(cat)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.drawCategorias()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.drawCategorias()
|
||||||
|
}
|
||||||
|
drawCategorias() {
|
||||||
|
const format = Intl.NumberFormat('es-CL', {style: 'currency', currency: 'CLP'})
|
||||||
|
$.each(this.categorias, (i, el) => {
|
||||||
|
el.draw({format})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
removeCategorias() {
|
||||||
|
$.each(this.categorias, (i, el) => {
|
||||||
|
el.remove()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
const cuentas = {
|
const cuentas = {
|
||||||
id: '#cuentas',
|
id: '#cuentas',
|
||||||
categorias: [],
|
balance: 0,
|
||||||
get: function() {
|
tipos: [],
|
||||||
|
tipos_categorias: [],
|
||||||
|
build: function() {
|
||||||
return {
|
return {
|
||||||
parent: () => {
|
parent: (segment) => {
|
||||||
let parent = $(this.id)
|
const tr = $('<tr></tr>').append(
|
||||||
if (parent.length === 0) {
|
$('<th></th>').attr('colspan', 3).html('Cuenta')
|
||||||
const table = $('<table></table>').attr('class', 'ui striped table').append(
|
)
|
||||||
$('<thead></thead>').append(
|
$.each(this.tipos, (i, el) => {
|
||||||
$('<tr></tr>').append(
|
tr.append(
|
||||||
$('<th></th>').html('Cuenta')
|
$('<th></th>').attr('class', 'right aligned').css('color', '#' + el.color).html(el.descripcion)
|
||||||
).append(
|
|
||||||
$('<th></th>').attr('class', 'right aligned').html('Saldo')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
parent = $('<tbody></tbody>').attr('id', this.id)
|
})
|
||||||
table.append(parent)
|
const table = $('<table></table>').attr('class', 'ui striped table').append(
|
||||||
$('h1.header').after(table)
|
$('<thead></thead>').append(tr)
|
||||||
}
|
)
|
||||||
|
const parent = $('<tbody></tbody>')
|
||||||
|
table.append(parent)
|
||||||
|
segment.append(table)
|
||||||
return parent
|
return parent
|
||||||
},
|
},
|
||||||
categorias: () => {
|
resultado: (segment) => {
|
||||||
return $.ajax({
|
segment.append(
|
||||||
url: _urls.api + '/categorias',
|
$('<table></table>').attr('class', 'ui collapsing table').append(
|
||||||
method: 'GET',
|
$('<tr></tr>').append(
|
||||||
dataType: 'json'
|
$('<td></td>').html('Ganancias')
|
||||||
}).then((data) => {
|
).append(
|
||||||
if (data.categorias === null || data.categorias.length == 0) {
|
$('<td></td>').attr('data-tipo', 'ganancias')
|
||||||
return
|
)
|
||||||
}
|
).append(
|
||||||
this.categorias = data.categorias
|
$('<tr></tr>').append(
|
||||||
}).then(() => {
|
$('<td></td>').html('Perdidas')
|
||||||
this.draw().categorias()
|
).append(
|
||||||
})
|
$('<td></td>').attr('data-tipo', 'perdidas')
|
||||||
},
|
)
|
||||||
cuentas: (categoria_id) => {
|
).append(
|
||||||
return $.ajax({
|
$('<tr></tr>').append(
|
||||||
url: _urls.api + '/categoria/' + categoria_id + '/cuentas',
|
$('<td></td>').html('<b>Resultado</b>')
|
||||||
method: 'GET',
|
).append(
|
||||||
dataType: 'json'
|
$('<td></td>').attr('data-tipo', 'resultado')
|
||||||
}).then((data) => {
|
)
|
||||||
if (data.cuentas === null || data.cuentas.length == 0) {
|
)
|
||||||
return
|
)
|
||||||
}
|
|
||||||
const idx = this.categorias.findIndex(el => {
|
|
||||||
if (el.id == categoria_id) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.categorias[idx].cuentas = data.cuentas
|
|
||||||
}).then(() => {
|
|
||||||
this.draw().cuentas(categoria_id)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remove: function() {
|
get: function() {
|
||||||
return {
|
return {
|
||||||
cuentas: (categoria_id) => {
|
parent: () => {
|
||||||
const idx = this.categorias.findIndex(el => {
|
const segment = $(this.id)
|
||||||
if (el.id == categoria_id) {
|
let parent = segment.find('tbody')
|
||||||
return true
|
if (parent.length === 0) {
|
||||||
}
|
parent = this.build().parent(segment)
|
||||||
})
|
|
||||||
const parent = $("[data-id='" + categoria_id + "']")
|
|
||||||
for (let i = 0; i < this.categorias[idx].cuentas.length; i ++) {
|
|
||||||
parent.next().remove()
|
|
||||||
}
|
}
|
||||||
|
return parent
|
||||||
|
},
|
||||||
|
tipos_cuentas: () => {
|
||||||
|
return sendGet(_urls.api + '/tipos/cuentas').then((data) => {
|
||||||
|
if (data.tipos === null || data.tipos.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.tipos = data.tipos
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tipos_categorias: () => {
|
||||||
|
return sendGet(_urls.api + '/tipos/categorias').then((data) => {
|
||||||
|
if (data.tipos === null || data.tipos.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(data.tipos, (i, el) => {
|
||||||
|
const tipo = new TipoCategoria(el)
|
||||||
|
tipo.setTipos(this.tipos)
|
||||||
|
this.tipos_categorias.push(tipo)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().tipos_categorias()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
balance: () => {
|
||||||
|
sendGet(_urls.api + '/balance').then((data) => {
|
||||||
|
this.balance = data
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().balance()
|
||||||
|
}).then(() => {
|
||||||
|
this.draw().resultado()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
draw: function() {
|
draw: function() {
|
||||||
return {
|
return {
|
||||||
categorias: () => {
|
tipos_categorias: () => {
|
||||||
|
const format = Intl.NumberFormat('es-CL', {style: 'currency', currency: 'CLP'})
|
||||||
const parent = this.get().parent()
|
const parent = this.get().parent()
|
||||||
$.each(this.categorias, (i, el) => {
|
$.each(this.tipos_categorias, (i, el) => {
|
||||||
const button = $('<button></button>').attr('class', 'ui mini compact icon button').append(
|
parent.append(el.draw({format, tipos: this.tipos}))
|
||||||
$('<i></i>').attr('class', 'plus icon')
|
|
||||||
).click((e) => {
|
|
||||||
const plus = button.find('.plus')
|
|
||||||
if (plus.length == 0) {
|
|
||||||
console.debug(e.target)
|
|
||||||
this.remove().cuentas(el.id)
|
|
||||||
button.find('i.icon').removeClass('minus').addClass('plus')
|
|
||||||
} else {
|
|
||||||
console.debug(e.target)
|
|
||||||
this.get().cuentas(el.id)
|
|
||||||
button.find('i.icon').removeClass('plus').addClass('minus')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const f = $('<tr></tr>').attr('data-id', el.id).append(
|
|
||||||
$('<td></td>').append(
|
|
||||||
$('<div></div>').append(button).append(el.nombre)
|
|
||||||
)
|
|
||||||
).append(
|
|
||||||
$('<td></td>').attr('class', 'right aligned').html(el.saldoFormateado)
|
|
||||||
)
|
|
||||||
parent.append(f)
|
|
||||||
})
|
})
|
||||||
|
this.get().balance()
|
||||||
},
|
},
|
||||||
cuentas: (categoria_id) => {
|
balance: () => {
|
||||||
const idx = this.categorias.findIndex(el => {
|
const parent = this.get().parent().parent()
|
||||||
if (el.id == categoria_id) {
|
if (parent.find('tfoot').length === 0) {
|
||||||
return true
|
parent.append(
|
||||||
}
|
$('<tfoot></tfoot>')
|
||||||
})
|
)
|
||||||
const parent = $("[data-id='" + categoria_id + "']")
|
}
|
||||||
$.each(this.categorias[idx].cuentas, (i, el) => {
|
const format = Intl.NumberFormat('es-CL', {style: 'currency', currency: 'CLP'})
|
||||||
parent.after(
|
const foot = parent.find('tfoot')
|
||||||
$('<tr></tr>').attr('class', 'item').append(
|
foot.html('')
|
||||||
$('<td></td>').append(
|
const tr = $('<tr></tr>').append(
|
||||||
$('<a></a>').attr('href', _urls.base + 'cuenta/' + el.id).html(el.nombre)
|
$('<th></th>').attr('colspan', 3).html('<b>Total</b>')
|
||||||
|
)
|
||||||
|
$.each(this.tipos, (i, el) => {
|
||||||
|
tr.append(
|
||||||
|
$('<th></th>').attr('class', 'right aligned').append(
|
||||||
|
$('<b></b>').html(format.format(this.balance[el.descripcion.toLowerCase() + 's']))
|
||||||
)
|
)
|
||||||
).append(
|
|
||||||
$('<td></td>').attr('class', 'right aligned').html(el.saldoFormateado)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
foot.append(tr)
|
||||||
|
},
|
||||||
|
resultado: () => {
|
||||||
|
const div = $('#resultado')
|
||||||
|
if (div.find("[data-tipo='resultado']").length === 0) {
|
||||||
|
div.html('')
|
||||||
|
this.build().resultado(div)
|
||||||
|
}
|
||||||
|
const format = Intl.NumberFormat('es-CL', {style: 'currency', currency: 'CLP'})
|
||||||
|
div.find("[data-tipo='ganancias']").html(format.format(this.balance['ganancias']))
|
||||||
|
div.find("[data-tipo='perdidas']").html(format.format(this.balance['perdidas']))
|
||||||
|
div.find("[data-tipo='resultado']").html('<b>' + format.format(this.balance['ganancias'] - this.balance['perdidas']) + '</b>')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup: async function() {
|
setup: async function() {
|
||||||
this.get().categorias()
|
this.get().tipos_cuentas().then(() => {
|
||||||
|
this.get().tipos_categorias()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
182
ui/public/assets/scripts/tipos_categorias.list.js
Normal file
182
ui/public/assets/scripts/tipos_categorias.list.js
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
class TipoCategoria {
|
||||||
|
constructor({id, descripcion, activo}) {
|
||||||
|
this.id = id
|
||||||
|
this.descripcion = descripcion
|
||||||
|
this.activo = activo
|
||||||
|
this.modal = null
|
||||||
|
}
|
||||||
|
setModal(modal) {
|
||||||
|
this.modal = modal
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
const chk = $('<input/>').attr('type', 'checkbox').attr('name', 'activo_' + this.id).attr('value', '1')
|
||||||
|
if (this.activo === '1') {
|
||||||
|
chk.parent().addClass('active')
|
||||||
|
chk.attr('checked', 'checked')
|
||||||
|
chk.checkbox('check')
|
||||||
|
}
|
||||||
|
chk.checkbox({
|
||||||
|
onChange: () => {
|
||||||
|
this.changeActivo()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return $('<tr></tr>').append(
|
||||||
|
$('<td></td>').html(this.descripcion)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<div></div>').attr('class', 'ui checkbox').append(chk).append(
|
||||||
|
$('<label></label>').html(' ')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'edit icon')
|
||||||
|
).click((e) => {
|
||||||
|
this.edit()
|
||||||
|
})
|
||||||
|
).append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular red icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'remove icon')
|
||||||
|
).click((e) => {
|
||||||
|
this.remove()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
changeActivo() {
|
||||||
|
const data = JSON.stringify({
|
||||||
|
descripcion: this.descripcion,
|
||||||
|
activo: this.activo === '1' ? 0 : 1
|
||||||
|
})
|
||||||
|
return sendPut(_urls.api + '/tipos/categoria/' + this.id + '/edit', data).then((data) => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
tipos_categorias.getTipos()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
edit() {
|
||||||
|
const form = this.modal.find('form')
|
||||||
|
form.find("[name='id']").val(this.id)
|
||||||
|
form.find("[name='descripcion']").val(this.descripcion)
|
||||||
|
if (this.activo === '1') {
|
||||||
|
form.find("[name='activo']").attr('checked', 'checked')
|
||||||
|
}
|
||||||
|
modalToEdit(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
sendDelete(_urls.api + '/tipos/categoria/' + this.id + '/delete').then(() => {
|
||||||
|
tipos_categorias.getTipos()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tipos_categorias = {
|
||||||
|
id: '#tipos_categorias',
|
||||||
|
tipos: [],
|
||||||
|
modal: null,
|
||||||
|
getTipos: function() {
|
||||||
|
this.tipos = []
|
||||||
|
return sendGet(_urls.api + '/tipos/categorias').then((data) => {
|
||||||
|
if (data.tipos === null || data.tipos.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(data.tipos, (i, el) => {
|
||||||
|
const tipo = new TipoCategoria(el)
|
||||||
|
tipo.setModal(this.modal)
|
||||||
|
this.tipos.push(tipo)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.draw()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
buildParent: function(segment) {
|
||||||
|
const table = $('<table></table>').attr('class', 'ui table').append(
|
||||||
|
$('<thead></thead>').append(
|
||||||
|
$('<tr></tr>').append(
|
||||||
|
$('<th></th>').attr('class', 'twelve wide').html('Tipo Categoría')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').attr('class', 'two wide').html('Activo')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').attr('class', 'two wide right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'plus icon')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
table.find('.ui.button').click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.add()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
parent = $('<tbody></tbody>')
|
||||||
|
table.append(parent)
|
||||||
|
segment.append(table)
|
||||||
|
return parent
|
||||||
|
},
|
||||||
|
getParent: function() {
|
||||||
|
const segment = $(this.id)
|
||||||
|
let parent = segment.find('tbody')
|
||||||
|
if (parent.length === 0) {
|
||||||
|
parent = this.buildParent(segment)
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
},
|
||||||
|
draw: function() {
|
||||||
|
const parent = this.getParent()
|
||||||
|
parent.html('')
|
||||||
|
$.each(this.tipos, (i, el) => {
|
||||||
|
parent.append(el.draw())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
add: function() {
|
||||||
|
this.modal.find('form').trigger('reset')
|
||||||
|
modalToAdd(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
},
|
||||||
|
doAdd: function() {
|
||||||
|
const data = JSON.stringify({
|
||||||
|
descripcion: $("[name='descripcion']").val(),
|
||||||
|
activo: $("[name='activo']").val()
|
||||||
|
})
|
||||||
|
return sendPost(_urls.api + '/tipos/categorias/add', data).then((data) => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
this.getTipos()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
doEdit: function() {
|
||||||
|
const form = this.modal.find('form')
|
||||||
|
const id = form.find("[name='id']").val()
|
||||||
|
const data = JSON.stringify({
|
||||||
|
descripcion: form.find("[name='descripcion']").val(),
|
||||||
|
activo: form.find("[name='activo']").is(':checked') ? 1 : 0
|
||||||
|
})
|
||||||
|
return sendPut(_urls.api + '/tipos/categoria/' + id + '/edit', data).then((data) => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
this.getTipos()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setupModal: function() {
|
||||||
|
this.modal = $('.ui.modal')
|
||||||
|
this.modal.modal()
|
||||||
|
this.modal.find('.close.icon').click(() => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
})
|
||||||
|
this.modal.find('form').submit((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const add = $(e.currentTarget).find('.plus.icon')
|
||||||
|
if (add.length > 0) {
|
||||||
|
this.doAdd()
|
||||||
|
} else {
|
||||||
|
this.doEdit()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
this.setupModal()
|
||||||
|
this.getTipos()
|
||||||
|
}
|
||||||
|
}
|
166
ui/public/assets/scripts/tipos_cuentas.list.js
Normal file
166
ui/public/assets/scripts/tipos_cuentas.list.js
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
class TipoCuenta {
|
||||||
|
constructor({id, descripcion, color}) {
|
||||||
|
this.id = id
|
||||||
|
this.descripcion = descripcion
|
||||||
|
this.color = color
|
||||||
|
this.modal = null
|
||||||
|
}
|
||||||
|
setModal(modal) {
|
||||||
|
this.modal = modal
|
||||||
|
}
|
||||||
|
setPicker(picker) {
|
||||||
|
this.picker = picker
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
return $('<tr></tr>').append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<i></i>').attr('class', 'square full icon').css('color', '#' + this.color)
|
||||||
|
).append(this.descripcion)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'edit icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.edit()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
).append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny circular red icon button').attr('data-id', this.id).append(
|
||||||
|
$('<i></i>').attr('class', 'remove icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.remove()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
edit() {
|
||||||
|
const form = this.modal.find('form')
|
||||||
|
form.find("[name='id']").val(this.id)
|
||||||
|
form.find("[name='descripcion']").val(this.descripcion)
|
||||||
|
form.find("[name='color']").val(this.color)
|
||||||
|
this.picker.setColor(this.color)
|
||||||
|
modalToEdit(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
sendDelete(_urls.api + '/tipos/cuenta/' + this.id + '/delete').then(() => {
|
||||||
|
tipos_cuentas.getTipos()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tipos_cuentas = {
|
||||||
|
id: '#tipos_cuentas',
|
||||||
|
tipos: [],
|
||||||
|
modal: null,
|
||||||
|
getTipos: function() {
|
||||||
|
this.tipos = []
|
||||||
|
return sendGet(_urls.api + '/tipos/cuentas').then((data) => {
|
||||||
|
if (data.tipos === null || data.tipos.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.each(data.tipos, (i, el) => {
|
||||||
|
const tipo = new TipoCuenta(el)
|
||||||
|
tipo.setModal(this.modal)
|
||||||
|
tipo.setPicker(this.picker)
|
||||||
|
this.tipos.push(tipo)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.draw()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
buildParent: function(segment) {
|
||||||
|
const table = $('<table></table>').attr('class', 'ui table').append(
|
||||||
|
$('<thead></thead>').append(
|
||||||
|
$('<tr></tr>').append(
|
||||||
|
$('<th></th>').html('Tipo')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'plus icon')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
table.find('.ui.button').click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.add()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
parent = $('<tbody></tbody>')
|
||||||
|
table.append(parent)
|
||||||
|
segment.append(table)
|
||||||
|
return parent
|
||||||
|
},
|
||||||
|
getParent: function() {
|
||||||
|
const segment = $(this.id)
|
||||||
|
let parent = segment.find('tbody')
|
||||||
|
if (parent.length === 0) {
|
||||||
|
parent = this.buildParent(segment)
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
},
|
||||||
|
draw: function() {
|
||||||
|
const parent = this.getParent()
|
||||||
|
parent.html('')
|
||||||
|
$.each(this.tipos, (i, el) => {
|
||||||
|
parent.append(el.draw())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
add: function() {
|
||||||
|
this.modal.find('form').trigger('reset')
|
||||||
|
this.picker.setColor('ffffff')
|
||||||
|
modalToAdd(this.modal)
|
||||||
|
this.modal.modal('show')
|
||||||
|
},
|
||||||
|
doAdd: function() {
|
||||||
|
const data = JSON.stringify({
|
||||||
|
descripcion: this.modal.find('form').find("[name='descripcion']").val(),
|
||||||
|
color: this.modal.find('form').find("[name='color']").val()
|
||||||
|
})
|
||||||
|
return sendPost(
|
||||||
|
_urls.api + '/tipos/cuentas/add',
|
||||||
|
data
|
||||||
|
).then((data) => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
this.getCuentas()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
doEdit: function() {
|
||||||
|
id = this.modal.find('form').find("[name='id']").val()
|
||||||
|
const data = JSON.stringify({
|
||||||
|
descripcion: this.modal.find('form').find("[name='descripcion']").val(),
|
||||||
|
color: this.modal.find('form').find("[name='color']").val()
|
||||||
|
})
|
||||||
|
sendPut(_urls.api + '/tipos/cuenta/' + id + '/edit', data).then((data) => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
this.getTipos()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setupModal: function() {
|
||||||
|
this.modal = $('.ui.modal')
|
||||||
|
this.modal.modal()
|
||||||
|
this.modal.find('.close.icon').css('cursor', 'pointer').click(() => {
|
||||||
|
this.modal.modal('hide')
|
||||||
|
})
|
||||||
|
this.picker = new ColorPicker(this.modal.find("[name='color']"))
|
||||||
|
this.modal.find('form').submit((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const add = $(e.currentTarget).find('.add.icon')
|
||||||
|
if (add.length > 0) {
|
||||||
|
this.doAdd()
|
||||||
|
} else {
|
||||||
|
this.doEdit()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
this.setupModal()
|
||||||
|
this.getTipos()
|
||||||
|
}
|
||||||
|
}
|
221
ui/public/assets/scripts/uploads.list.js
Normal file
221
ui/public/assets/scripts/uploads.list.js
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
class Archivo {
|
||||||
|
constructor({folder, filename}) {
|
||||||
|
this.folder = folder
|
||||||
|
this.filename = filename
|
||||||
|
this.modal = null
|
||||||
|
}
|
||||||
|
setModal(modal) {
|
||||||
|
this.modal = modal
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
return $('<tr></tr>').append(
|
||||||
|
$('<td></td>').append(
|
||||||
|
$('<a></a>').attr('class', 'item').attr('href', _urls.base + ['upload', this.folder, this.filename].join('/')).html(this.filename)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<td></td>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui mini circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'edit icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const t = e.currentTarget
|
||||||
|
this.edit()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
).append(
|
||||||
|
$('<button></button>').attr('class', 'ui mini red circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'remove icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const t = e.currentTarget
|
||||||
|
this.remove()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
edit() {
|
||||||
|
this.modal.find('form').trigger('reset')
|
||||||
|
this.modal.find('form').find("[name='folder']").val(this.folder)
|
||||||
|
this.modal.find('form').find("[name='old_filename']").val(this.filename)
|
||||||
|
this.modal.find('form').find("[name='filename']").val(this.filename)
|
||||||
|
this.modal.modal('show')
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
return sendDelete([_urls.api, 'upload', this.folder, this.filename].join('/')).then((data) => {
|
||||||
|
if (data.deleted) {
|
||||||
|
archivos.get()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const archivos = {
|
||||||
|
id: '#archivos',
|
||||||
|
archivos: [],
|
||||||
|
modals: {
|
||||||
|
add: null,
|
||||||
|
edit: null
|
||||||
|
},
|
||||||
|
build: function() {
|
||||||
|
return {
|
||||||
|
parent: (segment) => {
|
||||||
|
const table = $('<table></table>').attr('class', 'ui striped table').append(
|
||||||
|
$('<thead></thead>').append(
|
||||||
|
$('<tr></tr>').append(
|
||||||
|
$('<th></th>').html('Archivo')
|
||||||
|
).append(
|
||||||
|
$('<th></th>').attr('class', 'right aligned').append(
|
||||||
|
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
|
||||||
|
$('<i></i>').attr('class', 'plus icon')
|
||||||
|
).click((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.add()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const parent = $('<tbody></tbody>')
|
||||||
|
table.append(parent)
|
||||||
|
segment.append(table)
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: function() {
|
||||||
|
return {
|
||||||
|
parent: () => {
|
||||||
|
const segment = $(this.id)
|
||||||
|
let parent = segment.find('tbody')
|
||||||
|
if (parent.length === 0) {
|
||||||
|
parent = this.build().parent(segment)
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
},
|
||||||
|
archivos: () => {
|
||||||
|
return sendGet(_urls.api + '/uploads').then((data) => {
|
||||||
|
if (data.files === null || data.files.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.archivos = []
|
||||||
|
$.each(data.files, (i, el) => {
|
||||||
|
const arch = new Archivo(el)
|
||||||
|
arch.setModal(this.modals.edit)
|
||||||
|
this.archivos.push(arch)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.draw()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cuentas: () => {
|
||||||
|
return sendGet(_urls.api + '/cuentas')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
draw: function() {
|
||||||
|
const tbody = this.get().parent()
|
||||||
|
tbody.empty()
|
||||||
|
$.each(this.archivos, (i, el) => {
|
||||||
|
tbody.append(el.draw())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
add: function() {
|
||||||
|
this.modals.add.find('form').trigger('reset')
|
||||||
|
this.modals.add.find("[name='cuenta']").dropdown('clear')
|
||||||
|
this.modals.add.modal('show')
|
||||||
|
},
|
||||||
|
doAdd: function() {
|
||||||
|
const data = new FormData(this.modals.add.find('form')[0])
|
||||||
|
return sendPost(_urls.api + '/uploads/add', data, true).then((resp) => {
|
||||||
|
this.modals.add.modal('hide')
|
||||||
|
this.get().archivos()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
doEdit: function() {
|
||||||
|
const folder = this.modals.edit.find("[name='folder']").val()
|
||||||
|
const filename = this.modals.edit.find("[name='old_filename']").val()
|
||||||
|
const data = JSON.stringify({
|
||||||
|
cuenta: this.modals.edit.find("[name='cuenta']").val(),
|
||||||
|
fecha: this.modals.edit.find("[name='fecha']").val()
|
||||||
|
})
|
||||||
|
sendPut([_urls.api, 'upload', folder, filename].join('/'), data).then((resp) => {
|
||||||
|
this.modals.edit.modal('hide')
|
||||||
|
if (resp.edited) {
|
||||||
|
this.get().archivos()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateCalendar: function(modal) {
|
||||||
|
const today = new Date()
|
||||||
|
const start = new Date(today.getFullYear(), today.getMonth() - 1)
|
||||||
|
modal.find('.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).toString().padStart(2, '0')
|
||||||
|
return [year, month].join('-')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateCuentas: function(modal, data) {
|
||||||
|
if (data.cuentas === null || data.cuentas.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const select = modal.find("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})
|
||||||
|
},
|
||||||
|
setupModal: function() {
|
||||||
|
this.modals.add = $('#add_modal')
|
||||||
|
this.modals.edit = $('#edit_modal')
|
||||||
|
$.each(this.modals, (i, el) => {
|
||||||
|
el.modal().find('.close.icon').click(() => {
|
||||||
|
el.modal('hide')
|
||||||
|
})
|
||||||
|
this.updateCalendar(el)
|
||||||
|
})
|
||||||
|
this.modals.add.find('form').submit((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.doAdd()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
this.modals.add.find('#archivo_btn').css('cursor', 'pointer').click(() => {
|
||||||
|
this.modals.add.find("[name='archivo']").trigger('click')
|
||||||
|
})
|
||||||
|
this.modals.add.find("[name='archivo']").change((e) => {
|
||||||
|
const arch = $(e.currentTarget)
|
||||||
|
const filename = arch[0].files[0].name
|
||||||
|
this.modals.add.find('#archivo_btn').find('input').val(filename)
|
||||||
|
})
|
||||||
|
this.modals.edit.find('form').submit((e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.doEdit()
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
this.get().cuentas().then((data) => {
|
||||||
|
this.updateCuentas(this.modals.add, data)
|
||||||
|
this.updateCuentas(this.modals.edit, data)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
this.setupModal()
|
||||||
|
this.get().archivos()
|
||||||
|
}
|
||||||
|
}
|
4
ui/resources/routes/config.php
Normal file
4
ui/resources/routes/config.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\Config;
|
||||||
|
|
||||||
|
$app->get('/config', Config::class);
|
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
use Contabilidad\Common\Controller\Home;
|
use Contabilidad\Common\Controller\Home;
|
||||||
|
|
||||||
|
$app->get('/info[/]', [Home::class, 'info']);
|
||||||
$app->get('[/]', Home::class);
|
$app->get('[/]', Home::class);
|
||||||
|
4
ui/resources/routes/importar.php
Normal file
4
ui/resources/routes/importar.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\Importar;
|
||||||
|
|
||||||
|
$app->get('/importar[/]', Importar::class);
|
13
ui/resources/routes/tipos.php
Normal file
13
ui/resources/routes/tipos.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'tipos']);
|
||||||
|
if (file_exists($folder)) {
|
||||||
|
$app->group('/tipos', function($app) use ($folder) {
|
||||||
|
$files = new DirectoryIterator($folder);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if ($file->isDir() or $file->getExtension() != 'php') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
include_once $file->getRealPath();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
4
ui/resources/routes/tipos/tipos_categorias.php
Normal file
4
ui/resources/routes/tipos/tipos_categorias.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\TiposCategorias;
|
||||||
|
|
||||||
|
$app->get('/categorias', TiposCategorias::class);
|
4
ui/resources/routes/tipos/tipos_cuentas.php
Normal file
4
ui/resources/routes/tipos/tipos_cuentas.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\TiposCuentas;
|
||||||
|
|
||||||
|
$app->get('/cuentas', TiposCuentas::class);
|
10
ui/resources/routes/uploads.php
Normal file
10
ui/resources/routes/uploads.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
use Contabilidad\Common\Controller\Uploads;
|
||||||
|
|
||||||
|
$app->group('/uploads', function($app) {
|
||||||
|
$app->get('/add', [Uploads::class, 'upload']);
|
||||||
|
$app->get('[/]', Uploads::class);
|
||||||
|
});
|
||||||
|
$app->group('/upload/{folder}/{filename}', function($app) {
|
||||||
|
$app->get('[/]', [Uploads::class, 'get']);
|
||||||
|
});
|
@ -8,7 +8,7 @@
|
|||||||
Categorías
|
Categorías
|
||||||
@endif
|
@endif
|
||||||
</h1>
|
</h1>
|
||||||
<div class="ui segment">
|
<div class="ui basic fitted segment">
|
||||||
@yield('categorias_content')
|
@yield('categorias_content')
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
@ -6,10 +6,19 @@
|
|||||||
<i class="close icon"></i>
|
<i class="close icon"></i>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<form class="ui form">
|
<form class="ui form">
|
||||||
|
<input type="hidden" name="id" />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Nombre</label>
|
<label>Nombre</label>
|
||||||
<input type="text" name="nombre" />
|
<input type="text" name="nombre" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Tipo</label>
|
||||||
|
<div class="ui selection dropdown" id="tipos">
|
||||||
|
<input type="hidden" name="tipo" />
|
||||||
|
<div class="default text">Tipo</div>
|
||||||
|
<div class="menu"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button class="ui icon button">
|
<button class="ui icon button">
|
||||||
<i class="plus icon"></i>
|
<i class="plus icon"></i>
|
||||||
</button>
|
</button>
|
||||||
|
14
ui/resources/views/categorias/tipos/base.blade.php
Normal file
14
ui/resources/views/categorias/tipos/base.blade.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
@extends('config.base')
|
||||||
|
|
||||||
|
@section('config_content')
|
||||||
|
<h1 class="ui header">
|
||||||
|
@hasSection('tipos_categorias_title')
|
||||||
|
Tipo Categoría @yield('tipos_categorias_title')
|
||||||
|
@else
|
||||||
|
Tipos Categoría
|
||||||
|
@endif
|
||||||
|
</h1>
|
||||||
|
<div class="ui basic fitted segment">
|
||||||
|
@yield('tipos_categorias_content')
|
||||||
|
</div>
|
||||||
|
@endsection
|
33
ui/resources/views/categorias/tipos/list.blade.php
Normal file
33
ui/resources/views/categorias/tipos/list.blade.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
@extends('categorias.tipos.base')
|
||||||
|
|
||||||
|
@section('tipos_categorias_content')
|
||||||
|
<div id="tipos_categorias"></div>
|
||||||
|
<div class="ui modal">
|
||||||
|
<i class="close icon"></i>
|
||||||
|
<div class="content">
|
||||||
|
<form class="ui form">
|
||||||
|
<input type="hidden" name="id" />
|
||||||
|
<div class="field">
|
||||||
|
<label>Descripción</label>
|
||||||
|
<input type="text" name="descripcion" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Activo</label>
|
||||||
|
<input type="checkbox" name="activo" value="1" />
|
||||||
|
</div>
|
||||||
|
<button class="ui icon button">
|
||||||
|
<i class="plus icon"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script type="text/javascript" src="{{$urls->scripts}}/tipos_categorias.list.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(() => {
|
||||||
|
tipos_categorias.setup()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
@endpush
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user