Version 3.0

New technologies
This commit is contained in:
2022-08-05 21:28:59 -04:00
parent 06071884c7
commit a9968dec58
69 changed files with 600 additions and 2696 deletions

View File

@ -0,0 +1,151 @@
<?php
namespace Common\Alias;
use Common\Concept\Database as DatabaseInterface;
abstract class Database implements DatabaseInterface
{
protected string $host;
protected int $port;
public function setHost(string $host, ?int $port = null): DatabaseInterface
{
$this->host = $host;
if ($port !== null) {
$this->port = $port;
}
return $this;
}
public function getHost(): string
{
return $this->host;
}
public function getPort(): int
{
return $this->port;
}
protected string $name;
public function setName(string $database_name): Database
{
$this->name = $database_name;
return $this;
}
public function getName(): string
{
return $this->name;
}
protected string $username;
protected string $password;
public function setUser(string $username, string $password): Database
{
$this->username = $username;
$this->password = $password;
return $this;
}
public function getUser(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
protected \PDO $connection;
public function connect(): Database
{
if ($this->needsUser()) {
$this->connection = new \PDO($this->getDsn(), $this->getUser(), $this->getPassword());
return $this;
}
$this->connection = new \PDO($this->getDsn());
return $this;
}
public function getConnection(): \PDO
{
if (!isset($this->connection)) {
return $this->connect()->connection;
}
return $this->connection;
}
public function query(string $query): array
{
$st = $this->getConnection()->query($query);
if (!$st) {
throw new \PDOException("Could not retrieve anything with '{$query}'.");
}
$results = $st->fetchAll(\PDO::FETCH_ASSOC);
if (!$results) {
throw new \PDOException('Could not retrieve any results.');
}
return $results;
}
public function beginTransaction(): void
{
if (!$this->getConnection()->beginTransaction()) {
throw new \PDOException('Could not begin transaction.');
}
}
public function commit(): void
{
if (!$this->getConnection()->commit()) {
throw new \PDOException('Could not commit');
}
}
public function rollBack(): void
{
if (!$this->getConnection()->rollBack()) {
throw new \PDOException('Could not rollback.');
}
}
protected \PDOStatement $prepared_statement;
public function prepare(string $query): Database
{
$st = $this->getConnection()->prepare($query);
if (!$st) {
throw new \PDOException("Could not prepare query '{$query}'.");
}
$this->prepared_statement = $st;
return $this;
}
public function execute(array $data): array
{
if (!isset($this->prepared_statement)) {
throw new \Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($data)) {
throw new \PDOException('Could not execute prepared statement.');
}
$results = $this->prepared_statement->fetchAll(\PDO::FETCH_ASSOC);
if (!$results) {
throw new \PDOException('Could not retrieve any results.');
}
return $results;
}
public function insert(array $values): void
{
if (!isset($this->prepared_statement)) {
throw new \Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($values)) {
throw new \PDOException('Could not insert.');
}
}
public function update(array $data): void
{
if (!isset($this->prepared_statement)) {
throw new \Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($data)) {
throw new \PDOException('Could not update.');
}
}
public function delete(array $data): void
{
if (!isset($this->prepared_statement)) {
throw new \Exception('No prepared statement.');
}
if (!$this->prepared_statement->execute($data)) {
throw new \PDOException('Could not delete.');
}
}
}

View File

@ -1,6 +0,0 @@
<?php
namespace Contabilidad\Common\Alias;
interface DocumentHandler {
public function load(): ?array;
}

View File

@ -0,0 +1,26 @@
<?php
namespace Common\Alias\Factory;
use Common\Concept\Factory\Model as FactoryInterface;
use Common\Concept\Model as ModelInterface;
use Common\Concept\Repository;
use Psr\Container\ContainerInterface;
abstract class Model implements FactoryInterface
{
protected ContainerInterface $container;
public function setContainer(ContainerInterface $container): Model
{
$this->container = $container;
return $this;
}
public function getContainer(): ContainerInterface
{
return $this->container;
}
public function find(ModelInterface $model_name): Repository
{
$class = str_replace('Model', 'Repository', get_class($model_name));
return $this->getContainer()->get($class);
}
}

46
api/common/Alias/File.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace Common\Alias;
use function Safe\{fopen,fclose,fwrite};
use Common\Concept\File as FileInterface;
abstract class File implements FileInterface
{
protected string $filename;
public function setFilename(string $filename): FileInterface
{
$this->filename = $filename;
return $this;
}
public function getFilename(): string
{
return $this->filename;
}
public function isDir(): bool
{
return is_dir($this->getFilename());
}
public function isReadable(): bool
{
return is_readable($this->getFilename());
}
public function isWriteable(): bool
{
return is_writeable($this->getFilename());
}
public function read(?int $length = null): string
{
$fh = fopen($this->getFilename(), 'r');
$str = fgets($fh, $length);
fclose($fh);
return $str;
}
public function write(string $data, ?int $length = null): void
{
$fh = fopen($this->getFilename(), 'r');
fwrite($fh, $data, $length);
fclose($fh);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Common\Alias;
use function Safe\{touch,mkdir,unlink};
use Common\Concept\Filesystem as FilesystemInterface;
abstract class Filesystem implements FilesystemInterface
{
protected string $base_path;
public function setBasePath(string $path): FilesystemInterface
{
$this->base_path = $path;
return $this;
}
public function getBasePath(): string
{
return $this->base_path;
}
public function buildPath(string $relative_path): string
{
return implode(DIRECTORY_SEPARATOR, [
$this->getBasePath(),
$relative_path
]);
}
public function has(string $relative_path): bool
{
return file_exists($this->buildPath($relative_path));
}
public function mkdir(string $relative_path): void
{
mkdir($this->buildPath($relative_path));
}
public function create(string $relative_path): void
{
touch($this->buildPath($relative_path));
}
public function delete(string $relative_path): void
{
unlink($this->buildPath($relative_path));
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Common\Alias;
use Common\Concept\Model as ModelInterface;
abstract class Model implements ModelInterface
{
}

View File

@ -0,0 +1,62 @@
<?php
namespace Common\Alias;
use Common\Concept\Database;
use Common\Concept\Model;
use Common\Concept\Repository as RepositoryInterface;
abstract class Repository implements RepositoryInterface
{
protected string $table;
public function setTable(string $table): RepositoryInterface
{
$this->table = $table;
return $this;
}
public function getTable(): string
{
return $this->table;
}
protected array $properties;
public function addProperty(string $name): RepositoryInterface
{
$this->properties []= $name;
return $this;
}
public function setProperties(array $properties): RepositoryInterface
{
foreach ($properties as $property) {
$this->addProperty($property);
}
return $this;
}
public function getProperties(): array
{
return $this->properties;
}
protected Database $database;
public function setDatabase(Database $database): RepositoryInterface
{
$this->database = $database;
return $this;
}
public function getDatabase(): Database
{
return $this->database;
}
public function fetchAll(): array
{
$query = "SELECT * FROM {$this->getTable()}";
return array_map([$this, 'load'], $this->getDatabase()->query($query));
}
public function save(Model $model): void
{
$columns = implode(', ', array_map(function($item) {return "'{$item}'";}, $this->getProperties()));
$values = implode(', ', array_map(function($item) {return '?';}, $this->getProperties()));
$query = "INSERT INTO {$this->getTable()} ({$columns}) VALUES ({$values})";
$values = array_map(function($item) use ($model) {$method = str_replace(' ', '', ucwords(str_replace('_', '', $item)));return $model->{"get{$method}"}();}, $this->getProperties());
$this->getDatabase()->prepare($query)->insert($values);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Common\Concept;
interface Database
{
public function setHost(string $host, ?int $port = null): Database;
public function getHost(): string;
public function getPort(): int;
public function setName(string $database_name): Database;
public function getName(): string;
public function setUser(string $username, string $password): Database;
public function getUser(): string;
public function getPassword(): string;
public function getDsn(): string;
public function needsUser(): bool;
public function connect(): Database;
public function getConnection(): \PDO;
public function beginTransaction(): void;
public function commit(): void;
public function rollBack(): void;
public function query(string $query): array;
public function prepare(string $query): Database;
public function execute(array $data): array;
public function insert(array $values): void;
public function update(array $data): void;
public function delete(array $data): void;
}

View File

@ -1,11 +0,0 @@
<?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;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Common\Concept\Factory;
use Common\Concept\Model as ModelInterface;
use Common\Concept\Repository;
interface Model
{
public function find(ModelInterface $model_name): Repository;
}

View File

@ -0,0 +1,16 @@
<?php
namespace Common\Concept;
interface File
{
public function setFilename(string $filename): File;
public function getFilename(): string;
public function isDir(): bool;
public function isReadable(): bool;
public function isWriteable(): bool;
public function read(?int $length = null): string;
public function write(string $data, ?int $length = null);
public function append(string $data, ?int $length = null);
}

View File

@ -0,0 +1,17 @@
<?php
namespace Common\Concept;
interface Filesystem
{
public function setBasePath(string $path): Filesystem;
public function getBasePath(): string;
public function buildPath(string $relative_path): string;
public function has(string $relative_path): bool;
public function mkdir(string $relative_path): void;
public function create(string $relative_path): void;
public function get(string $relative_path): File;
public function delete(string $relative_path): void;
}

View File

@ -0,0 +1,6 @@
<?php
namespace Common\Concept;
interface Model
{
}

View File

@ -0,0 +1,11 @@
<?php
namespace Common\Concept;
interface Repository
{
public function fetchAll(): array;
public function fetchByKey($key): Model;
public function load(array $data_row): Model;
public function save(Model $model): void;
public function delete(Model $model): void;
}

View File

@ -1,34 +0,0 @@
<?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;
class Base {
use Json;
public function __invoke(Request $request, Response $response): 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;
}
}

View File

@ -1,112 +0,0 @@
<?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\Common\Service\TiposCambios as Service;
use Contabilidad\Categoria;
class Categorias {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
$categorias = $factory->find(Categoria::class)->many();
if ($categorias !== null) {
array_walk($categorias, function(&$item) use ($service) {
$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 = [
'categorias' => $categorias
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'categoria' => $categoria?->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) {
$categoria = Categoria::add($factory, $in);
$results []= ['categoria' => $categoria?->toArray(), 'agregado' => $categoria?->save()];
}
} else {
$categoria = Categoria::add($factory, $input);
$results []= ['categoria' => $categoria?->toArray(), 'agregado' => $categoria?->save()];
}
$output = [
'input' => $input,
'categorias' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'old' => $categoria->toArray()
];
$input = json_decode($request->getBody());
$categoria->edit($input);
$output['categoria'] = $categoria->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'categoria' => $categoria->toArray(),
'eliminado' => $categoria->delete()
];
return $this->withJson($response, $output);
}
public function cuentas(Request $request, Response $response, Factory $factory, Service $service, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$cuentas = null;
if ($categoria !== null) {
$cuentas = $categoria->cuentas();
if ($cuentas !== null) {
array_walk($cuentas, function(&$item) use ($service) {
$item = $item->toArray($service);
});
}
}
$output = [
'input' => $categoria_id,
'categoria' => $categoria?->toArray(),
'cuentas' => $cuentas
];
return $this->withJson($response, $output);
}
}

View File

@ -1,66 +0,0 @@
<?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\Factory\Model as Factory;
use ProVM\Common\Define\Controller\Json;
use Contabilidad\Common\Service\Consolidar as Service;
use Contabilidad\Cuenta;
use Contabilidad\Consolidado;
class Consolidados {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$consolidados = $factory->find(Consolidados::class)->where([['cuenta_id' => $cuenta_id]])->many();
$output = [
'consolidados' => array_map(function($item) {
return $item->toArray();
}, $consolidados)
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody()->getContents());
$output = [
'input' => $input,
'consolidados' => []
];
if (!is_array($input)) {
$input = [$input];
}
foreach ($input as $data) {
$consolidado = Consolidado::add($factory, $data);
$status = $consolidado->save();
$output['consolidados'] []= [
'consolidado' => $consolidado->toArray(),
'added' => $status
];
}
return $this->withJson($response, $output);
}
public function cli(Request $request, Response $response, Service $service): Response {
try {
if (!$service->isConsolidado()) {
$service->consolidar();
}
} catch (\Error | \Exception $e) {
error_log($e);
throw $e;
}
return $this->withJson($response, []);
}
public function update(Request $request, Response $response, Factory $factory, Service $service, $mes, $cuenta_id): Response {
try {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$mes = Carbon::parse($mes);
$service->consolidarCuenta($cuenta, $mes);
} catch (\Error | \Exception $e) {
error_log($e);
throw $e;
}
return $this->withJson($response, []);
}
}

View File

@ -1,197 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Contabilidad\Transaccion;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Carbon\Carbon;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Common\Service\TiposCambios as Service;
use Contabilidad\Cuenta;
class Cuentas {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$cuentas = $factory->find(Cuenta::class)->many();
if ($cuentas) {
array_walk($cuentas, function (&$item) {
$arr = $item->toArray();
$arr['categoria'] = $item->categoria()->toArray();
$item = $arr;
});
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']);
if ($c != 0) {
return $c;
}
return strcmp($a['nombre'], $b['nombre']);
});
}
$output = [
'cuentas' => $cuentas
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->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) {
$cuenta = Cuenta::add($factory, $in);
$results []= ['cuenta' => $cuenta?->toArray(), 'agregado' => $cuenta?->save()];
}
} else {
$cuenta = Cuenta::add($factory, $input);
$results []= ['cuenta' => $cuenta?->toArray(), 'agregado' => $cuenta?->save()];
}
$output = [
'input' => $input,
'cuentas' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'old' => $cuenta->toArray()
];
$input = json_decode($request->getBody());
$cuenta->edit($input);
$output['cuenta'] = $cuenta->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta->toArray(),
'eliminado' => $cuenta->delete()
];
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 {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$entradas = null;
if ($cuenta !== null) {
$entradas = $cuenta->entradas();
if ($entradas !== null) {
array_walk($entradas, function(&$item) {
$item = $item->toArray();
});
}
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'entradas' => $entradas
];
return $this->withJson($response, $output);
}
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'] = $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);
$transacciones = null;
if ($cuenta !== null) {
$transacciones = $cuenta->transacciones($limit, $start);
if (count($transacciones) > 0) {
foreach ($transacciones as &$transaccion) {
$transaccion = $this->transaccionToArray($service, $cuenta, $transaccion);
}
}
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
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) {
$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 {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$transacciones = 0;
if ($cuenta !== null) {
$transacciones = count($cuenta->transacciones());
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
}

View File

@ -1,76 +0,0 @@
<?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);
}
}

View File

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

View File

@ -1,71 +0,0 @@
<?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);
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Factory\Model as Factory;
use ProVM\Common\Define\Controller\Json;
use Contabilidad\Queue;
class Queues {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$queues = $factory->find(Queue::class)->many();
$output = [
'queues' => array_map(function($item) {return $item->toArray();}, $queues)
];
return $this->withJson($response, $output);
}
public function pending(Request $request, Response $response, Factory $factory): Response {
$pending = $factory->find(Queue::class)->where([['processed', 0]])->many();
$output = [
'pending' => array_map(function($item) {return $item->toArray();}, $pending)
];
return $this->withJson($response, $output);
}
public function processed(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody()->getContents());
$output = [
'input' => $input,
'queues' => []
];
if (!is_array($input->processed)) {
$input->processed = [$input->processed];
}
foreach ($input->processed as $id) {
$queue = $factory->find(Queue::class)->one($id);
$queue->setProcessed(true);
$status = $queue->save();
$output['queues'] []= [
'queue' => $queue->toArray(),
'processed' => $status
];
}
return $this->withJson($response, $output);
}
}

View File

@ -1,95 +0,0 @@
<?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);
}
}

View File

@ -1,132 +0,0 @@
<?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);
}
}

View File

@ -1,71 +0,0 @@
<?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);
}
}

View File

@ -1,95 +0,0 @@
<?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\Common\Service\Queuer as Service;
use Contabilidad\Transaccion;
class Transacciones {
use Json;
protected function parseTransacciones(?array $transacciones): ?array {
if ($transacciones !== null) {
usort($transacciones, function($a, $b) {
$d = $a['fecha'] - $b['fecha'];
if ($d === 0) {
return strcmp($a['cuenta']['nombre'], $b['cuenta']['nombre']);
}
return $d;
});
}
return $transacciones;
}
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$transacciones = $factory->find(Transaccion::class)->array();
$output = [
'transacciones' => $this->parseTransacciones($transacciones)
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'transaccion' => $transaccion?->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) {
$transaccion = Transaccion::add($factory, $in);
$results []= ['transaccion' => $transaccion?->toArray(), 'agregado' => $transaccion?->save()];
}
} else {
$transaccion = Transaccion::add($factory, $input);
$results []= ['transaccion' => $transaccion?->toArray(), 'agregado' => $transaccion?->save()];
}
$output = [
'input' => $input,
'transacciones' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, Service $queuer, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'old' => $transaccion->toArray()
];
$old_cuentas = ['credito' => $transaccion->credito_id, 'debito_id' => $transaccion->debito_id];
$input = json_decode($request->getBody());
$transaccion->edit($input);
$new_cuentas = ['credito' => $transaccion->credito_id, 'debito_id' => $transaccion->debito_id];
$cuentas = [];
foreach ($new_cuentas as $tipo => $id) {
if ($old_cuentas[$tipo] != $id) {
$cuentas []= $old_cuentas[$tipo];
$cuentas []= $id;
}
}
$this->updateConsolidar($queuer, $transaccion->fecha(), $cuentas);
$output['transaccion'] = $transaccion->toArray();
return $this->withJson($response, $output);
}
protected function updateConsolidar(Service $queuer, \DateTimeInterface $mes, $cuentas) {
foreach ($cuentas as $cuenta_id) {
$queuer->queue('update_consolidar', ['mes' => $mes->format('Y-m-1'), 'cuenta' => $cuenta_id]);
}
}
public function delete(Request $request, Response $response, Factory $factory, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'transaccion' => $transaccion->toArray(),
'eliminado' => $transaccion->delete()
];
return $this->withJson($response, $output);
}
}

View File

@ -1,21 +0,0 @@
<?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\WorkerHandler as Handler;
class Workers {
use Json;
public function register(Request $request, Response $response, Handler $handler): Response {
$post = $request->getBody();
$result = $handler->register($post);
$output = [
'input' => $post,
'result' => $result
];
return $this->withJson($response, $output);
}
}

View File

@ -1,20 +0,0 @@
<?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 Contabilidad\Common\Service\Consolidar as ConsolidarService;
class Consolidar {
protected $service;
public function __construct(ConsolidarService $service) {
$this->service = $service;
}
public function __invoke(Request $request, Handler $handler): Response {
if (!$this->service->isConsolidado()) {
$this->service->queue();
}
return $handler->handle($request);
}
}

View File

@ -1,174 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Carbon\Carbon;
use \Model;
use ProVM\Common\Factory\Model as ModelFactory;
use Contabilidad\Consolidado;
use Contabilidad\Cuenta;
use Contabilidad\Transaccion;
class Consolidar {
public function __construct(ModelFactory $factory, Queuer $queuer) {
$this->setFactory($factory);
$this->setQueuer($queuer);
}
protected ModelFactory $factory;
public function setFactory(ModelFactory $factory): Consolidar {
$this->factory = $factory;
return $this;
}
protected Queuer $queuer;
public function setQueuer(Queuer $queuer): Consolidar {
$this->queuer = $queuer;
return $this;
}
protected $cuentas;
public function getCuentas() {
if ($this->cuentas === null) {
$this->cuentas = $this->factory->find(Cuenta::class)->many();
}
return $this->cuentas;
}
public function isConsolidado(): bool {
$cuentas = $this->getCuentas();
if ($cuentas === null) {
return true;
}
foreach ($cuentas as $cuenta) {
$transacciones = $cuenta->hasTransacciones();
if (!$transacciones) {
continue;
}
$pendientes = $cuenta->hasConsolidadosPending();
if ($pendientes) {
return false;
}
}
return true;
}
public function queue() {
$this->queuer->queue('consolidar');
}
public function consolidar() {
ini_set('max_execution_time', 60*5);
foreach ($this->getCuentas() as $cuenta) {
if (!$cuenta->hasTransacciones()) {
continue;
}
$first = $this->getFirst($cuenta);
$last = $this->getLast($cuenta);
for ($current = $first->copy()->startOfMonth(); $current < $last->copy()->addMonthWithoutOverflow()->endOfMonth(); $current = $current->copy()->addMonthWithoutOverflow()) {
$this->consolidarCuenta($cuenta, $current);
}
}
}
public function consolidarCuenta(Cuenta $cuenta, Carbon $mes) {
if (!$cuenta->hasTransacciones($mes)) {
return;
}
$transacciones = $this->getTransacciones($cuenta, $mes);
if (count($transacciones) == 0) {
return;
}
array_walk($transacciones, function(&$item) use ($cuenta) {
$item->valor = $item->transformar($cuenta->moneda());
});
$saldo = array_reduce($transacciones, function($sum, $item) {
return $sum + $item->valor;
});
if ($cuenta->tipo()->cargo()) {
$saldo += -1;
}
$consolidado = $this->factory->find(Consolidado::class)->where([['cuenta_id', $cuenta->id], ['fecha', $mes->format('Y-m-1')]])->one();
if ($consolidado === null) {
$data = [
'cuenta_id' => $cuenta->id,
'fecha' => $mes->format('Y-m-1'),
'periodo' => 'P1M',
'saldo' => $saldo
];
$consolidado = $this->factory->create(Consolidado::class, $data);
}
$consolidado->saldo = $saldo;
$consolidado->save();
}
public function getFirst(Cuenta $cuenta): ?Carbon {
$first = [
Model::factory(Transaccion::class)
->select('fecha')
->whereEqual('debito_id', $cuenta->id)
->orderByAsc('fecha')
->findOne(),
Model::factory(Transaccion::class)
->select('fecha')
->whereEqual('credito_id', $cuenta->id)
->orderByAsc('fecha')
->findOne()
];
if (!$first[0]) {
if (!$first[1]) {
return null;
}
return $first[1]->fecha();
}
if (!$first[1]) {
return $first[0]->fecha();
}
if ($first[0]->fecha() < $first[1]->fecha()) {
return $first[0]->fecha();
}
return $first[1]->fecha();
}
public function getLast(Cuenta $cuenta): ?Carbon {
$fechas = [
Model::factory(Transaccion::class)
->select('fecha')
->whereEqual('debito_id', $cuenta->id)
->orderByDesc('fecha')
->findOne(),
Model::factory(Transaccion::class)
->select('fecha')
->whereEqual('credito_id', $cuenta->id)
->orderByDesc('fecha')
->findOne()
];
if (!$fechas[0]) {
if (!$fechas[1]) {
return null;
}
return $fechas[1]->fecha();
}
if (!$fechas[1]) {
return $fechas[0]->fecha();
}
if ($fechas[0]->fecha() > $fechas[1]->fecha()) {
return $fechas[0]->fecha();
}
return $fechas[1]->fecha();
}
public function getTransacciones(Cuenta $cuenta, Carbon $fecha) {
$start = $fecha->copy()->startOfMonth();
$end = $fecha->copy()->endOfMonth();
$debitos = Model::factory(Transaccion::class)
->whereEqual('debito_id', $cuenta->id)
->whereRaw("fecha BETWEEN '{$start->format('Y-m-d')}' AND '{$end->format('Y-m-d')}'")
->findMany();
if ($debitos) {
array_walk($debitos, function(&$item) {
$item->valor *= -1;
});
}
$creditos = Model::factory(Transaccion::class)
->whereEqual('credito_id', $cuenta->id)
->whereRaw("fecha BETWEEN '{$start->format('Y-m-d')}' AND '{$end->format('Y-m-d')}'")
->findMany();
$transacciones = array_merge($debitos, $creditos);
foreach ($transacciones as &$transaccion) {
$transaccion->setFactory($this->factory);
}
return $transacciones;
}
}

View File

@ -1,37 +0,0 @@
<?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;
}
}

View File

@ -1,16 +0,0 @@
<?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;
}
}

View File

@ -1,112 +0,0 @@
<?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);
}
}

View File

@ -1,75 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Contabilidad\Common\Concept\DocumentHandler;
use GuzzleHttp\Client;
class PdfHandler extends DocumentHandler {
protected Client $client;
protected string $url;
public function __construct(Client $client, string $pdf_folder, string $url) {
parent::__construct($pdf_folder);
$this->client = $client;
$this->url = $url;
}
public function load(): ?array {
$folder = $this->folder;
$files = new \DirectoryIterator($folder);
$output = [];
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'pdf') {
continue;
}
$output []= ['filename' => $file->getBasename()];
}
$response = $this->client->post($this->url, ['json' => ['files' => $output]]);
$output = json_decode($response->getBody());
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;
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Queue;
use Contabilidad\QueueArgument;
class Queuer {
protected Factory $factory;
public function __construct(Factory $factory) {
$this->setFactory($factory);
}
public function setFactory(Factory $factory): Queuer {
$this->factory = $factory;
return $this;
}
public function queue(string $command, array $arguments = []) {
if ($this->isProcessed($command, $arguments)) {
return;
}
$queue = $this->factory->create(Queue::class, ['command' => $command, 'created' => (new \DateTime('now'))->format('Y-m-d H:i:s')]);
$queue->save();
if (count($arguments) > 0) {
foreach ($arguments as $argument => $value) {
$arg = $this->factory->create(QueueArgument::class, ['queue_id' => $queue->id, 'argument' => $argument, 'value' => $value]);
$arg->save();
}
}
}
public function isProcessed(string $command, array $arguments = []): bool {
$queues = $this->find($command, $arguments);
if ($queues == null or count($queues) === 0) {
return false;
}
return true;
}
public function find(string $command, array $arguments = []): ?array {
$queues = $this->factory->find(Queue::class)->where([['command', $command], ['processed', 0]])->many();
if ($queues === null) {
return null;
}
if (count($arguments) > 0) {
$queues = array_filter($queues, function($item) use ($arguments) {return count($arguments) === count($item->matchArguments($arguments));});
}
return $queues;
}
}

View File

@ -1,93 +0,0 @@
<?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;
}
}

View File

@ -1,60 +0,0 @@
<?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;
}
}