7 Commits

Author SHA1 Message Date
0f3febc00d 2022-12-20 2022-12-20 14:13:05 -03:00
85fef16b27 Path 2022-08-08 23:03:28 -04:00
cdb4b382b7 Limpieza de objetos externos 2022-08-08 22:36:04 -04:00
a9968dec58 Version 3.0
New technologies
2022-08-05 21:28:59 -04:00
06071884c7 FIX: Negative numbers in cuenta ui, and update_consolidar command route 2022-04-18 22:41:25 -04:00
6ff584013f Add update consolidado command and queuing 2022-04-18 22:13:17 -04:00
cf27465d75 Improve readability 2022-04-18 22:11:59 -04:00
168 changed files with 4137 additions and 2917 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
**/cache/
**/*.env
**/logs/
**/documents/
# Composer
**/vendor/

27
Features.md Normal file
View File

@ -0,0 +1,27 @@
# Features
+ [ ] Multiples Cuentas
+ [ ] Giro, eg: Banco, Caja
+ [ ] Inversiones, eg: Crypto
+ [ ] Multiples Categorías
+ [ ] Ingresos, eg: Boletas, Facturas, Sueldo
+ [ ] Únicos
+ [ ] Recurrentes
+ [ ] Gastos, eg: Comida, Hogar, Movilización
+ [ ] Únicos
+ [ ] Recurrentes
+ [ ] Inversiones, eg: Bitcoin
+ [ ] Deudas
+ [ ] Nueva transacción
+ [ ] Leer cartolas
+ [ ] Leer cartolas en pdf
+ [ ] Leer cartolas en excel (xls, xlsx)
+ [ ] Obtener cartolas de emails
+ [ ] Ingresar cartolas a cuenta
+ [ ] Cuadrar cuentas
+ [ ] Reportes
+ [ ] Resultado
+ [ ] Mes
+ [ ] Línea de tiempo
+ [ ] Presupuesto
+ [ ] Por cuenta

View File

@ -0,0 +1,186 @@
<?php
namespace ProVM\Alias\API;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use PDOException;
use function Safe\{error_log, json_decode};
use ProVM\Alias\Controller\Json;
use ProVM\Concept\API\Controller as ControllerInterface;
use ProVM\Concept\Repository;
abstract class Controller implements ControllerInterface
{
use Json;
public function __construct(ContainerInterface $container)
{
$this->setup($container);
}
abstract public function setup(ContainerInterface $container): void;
protected array $names;
public function setSingular(string $singular): ControllerInterface
{
$this->names['singular'] = $singular;
return $this;
}
public function setPlural(string $plural): ControllerInterface
{
$this->names['plural'] = $plural;
return $this;
}
public function getSingular(): string
{
return $this->names['singular'];
}
public function getPlural(): string
{
return $this->names['plural'];
}
protected Repository $repository;
public function setRepository(Repository $repository): ControllerInterface
{
$this->repository = $repository;
return $this;
}
public function getRepository(): Repository
{
return $this->repository;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$objs = [];
try {
$objs = $this->getRepository()->fetchAll();
} catch (PDOException $e) {
error_log($e);
}
return $this->withJson($response, [$this->getPlural() => $objs]);
}
public function get(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface
{
$obj = null;
try {
$obj = $this->getRepository()->fetchById($model_id);
} catch (PDOException $e) {
error_log($e);
}
return $this->withJson($response, [$this->getSingular() => $obj]);
}
public function add(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$body = $request->getBody();
$contents = $body->getContents();
$json = json_decode($contents)->{$this->getPlural()};
if (!is_array($json)) {
$json = [$json];
}
$output = [
'input' => $json,
$this->getPlural() => []
];
foreach ($json as $data) {
$obj = $this->getRepository()->create((array) $data);
$status = true;
$exists = true;
if ($obj->isNew()) {
$exists = false;
try {
$this->getRepository()->save($obj);
} catch (PDOException $e) {
error_log($e);
$status = false;
}
$output[$this->getPlural()] []= [
$this->getSingular() => $obj,
'exists' => $exists,
'added' => $status
];
}
}
return $this->withJson($response, $output);
}
public function edit(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$body = $request->getBody();
$contents = $body->getContents();
$json = json_decode($contents)->{$this->getPlural()};
if (!is_array($json)) {
$json = [$json];
}
$output = [
'input' => $json,
$this->getPlural() => []
];
foreach ($json as $data) {
$obj = $this->getRepository()->fetchById($data->id);
$old = clone $obj;
try {
$obj->edit((array) $data);
$status = $obj->isDirty();
if ($status) {
$this->getRepository()->save($obj);
}
} catch (PDOException $e) {
error_log($e);
$status = false;
}
$output[$this->getPlural()] []= [
'antes' => $old,
$this->getSingular() => $obj,
'edited' => $status
];
}
return $this->withJson($response, $output);
}
public function editOne(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface
{
$obj = $this->getRepository()->fetchById($model_id);
$body = $request->getBody();
$contents = $body->getContents();
$json = json_decode($contents, JSON_OBJECT_AS_ARRAY)[$this->getSingular()];
$output = [
'input' => $json,
'old' => clone $obj
];
try {
$obj->edit((array) $json);
$status = $obj->isDirty();
if ($status) {
$this->getRepository()->save($obj);
}
} catch (PDOException $e) {
error_log($e);
$status = false;
}
$output[$this->getSingular()] = $obj;
$output['edited'] = $status;
return $this->withJson($response, $output);
}
public function remove(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface
{
$output = [
$this->getSingular() => null,
'exists' => true,
'deleted' => false
];
try {
$obj = $this->getRepository()->fetchById($model_id);
$output[$this->getSingular()] = clone $obj;
try {
$this->repository->delete($obj);
} catch (PDOException $e) {
error_log($e);
$output['deleted'] = false;
}
} catch (PDOException $e) {
error_log($e);
$output['exists'] = false;
}
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Operation as OperationInterface;
use ProVM\Concept\API\Route\Parameter;
use ProVM\Concept\API\Route\Response;
abstract class Operation implements OperationInterface
{
protected array $tags;
public function setTags(array $tags): OperationInterface
{
foreach ($tags as $tag) {
$this->addTag($tag);
}
return $this;
}
public function addTag(string $tag): OperationInterface
{
$this->tags []= $tag;
return $this;
}
public function getTags(): array
{
return $this->tags;
}
protected string $summary;
public function setSummary(string $summary): OperationInterface
{
$this->summary = $summary;
return $this;
}
public function getSummary(): string
{
return $this->summary;
}
protected string $description;
public function setDescription(string $description): OperationInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected array $parameters;
public function setParameters(array $parameters): OperationInterface
{
foreach ($parameters as $parameter) {
$this->addParameter($parameter);
}
return $this;
}
public function addParameter(Parameter $parameter): OperationInterface
{
$this->parameters []= $parameter;
return $this;
}
public function getParameters(): array
{
return $this->parameters;
}
protected array $responses;
public function setResponses(array $responses): OperationInterface
{
foreach ($responses as $code => $response) {
$this->addResponse($code, $response);
}
return $this;
}
public function addResponse(int $code, Response $response): OperationInterface
{
$this->responses[$code] = $response;
return $this;
}
public function getResponses(): array
{
return $this->responses;
}
protected bool $deprecated;
public function setDeprecated(): OperationInterface
{
$this->deprecated = true;
return $this;
}
public function isDeprecated(): bool
{
return $this->deprecated ?? false;
}
public function jsonSerialize(): mixed
{
$arr = [];
$fields = ['tags', 'summary', 'description', 'parameters', 'responses'];
foreach ($fields as $field) {
if (isset($this->{$field})) {
$method = 'get' . ucwords($field);
$arr[$field] = $this->{$method}();
}
}
if ($this->isDeprecated()) {
$arr['deprecated'] = true;
}
return $arr;
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Parameter as ParameterInterface;
abstract class Parameter implements ParameterInterface
{
protected string $name;
public function setName(string $name): ParameterInterface
{
$this->name = $name;
return $this;
}
public function getName(): string
{
return $this->name;
}
protected string $description;
public function setDescription(string $description): ParameterInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected string $in;
public function setIn(string $in): ParameterInterface
{
$this->in = $in;
return $this;
}
public function getIn(): string
{
return $this->in;
}
protected bool $required;
public function setRequired(): ParameterInterface
{
$this->required = true;
return $this;
}
public function isRequired(): bool
{
return $this->required ?? false;
}
protected bool $deprecated;
public function setDeprecated(): ParameterInterface
{
$this->deprecated = true;
return $this;
}
public function isDeprecated(): bool
{
return $this->deprecated ?? false;
}
public function jsonSerialize(): mixed
{
$arr = [
'name' => $this->getName(),
'in' => $this->getIn(),
'description' => $this->getDescription()
];
if ($this->isRequired()) {
$arr['required'] = true;
}
if ($this->isDeprecated()) {
$arr['deprecated'] = true;
}
return $arr;
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Response as ResponseInterface;
abstract class Response implements ResponseInterface
{
protected string $description;
public function setDescription(string $description): ResponseInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected array $headers;
public function setHeaders(array $headers): ResponseInterface
{
foreach ($headers as $header) {
$this->addHeader($header);
}
return $this;
}
public function addHeader(string $header): ResponseInterface
{
$this->headers []= $header;
return $this;
}
public function getHeaders(): array
{
return $this->headers;
}
protected array $contents;
public function setContent(array $contents): ResponseInterface
{
foreach ($contents as $content) {
$this->addContent($content);
}
return $this;
}
public function addContent(string $content): ResponseInterface
{
$this->contents []= $content;
return $this;
}
public function getContents(): array
{
return $this->contents;
}
public function jsonSerialize(): mixed
{
$arr = [
'description' => $this->getDescription()
];
if (isset($this->headers) and count($this->headers) > 0) {
$arr['headers'] = $this->getHeaders();
}
if (isset($this->contents) and count($this->contents) > 0) {
$arr['content'] = $this->getContents();
}
return $arr;
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace ProVM\Alias\API\Route;
use ProVM\Concept\API\Route\Route as RouteInterface;
use ProVM\Concept\API\Route\Operation;
use ProVM\Concept\API\Route\Parameter;
abstract class Route implements RouteInterface
{
protected string $ref;
public function setRef(string $ref): RouteInterface
{
$this->ref = $ref;
return $this;
}
public function getRef(): string
{
return $this->ref;
}
protected string $summary;
public function setSummary(string $summary): RouteInterface
{
$this->summary = $summary;
return $this;
}
public function getSummary(): string
{
return $this->summary;
}
protected string $description;
public function setDescription(string $description): RouteInterface
{
$this->description = $description;
return $this;
}
public function getDescription(): string
{
return $this->description;
}
protected array $methods;
public function setMethods(array $methods): RouteInterface
{
foreach ($methods as $method => $operation) {
$this->addMethod($method, $operation);
}
return $this;
}
public function addMethod(string $method, Operation $operation): RouteInterface
{
$this->methods[$method] = $operation;
return $this;
}
public function getMethods(): array
{
return $this->methods;
}
protected array $parameters;
public function setParameters(array $parameters): RouteInterface
{
foreach ($parameters as $parameter) {
$this->addParameter($parameter);
}
return $this;
}
public function addParameter(Parameter $parameter): RouteInterface
{
$this->parameters []= $parameter;
return $this;
}
public function getParameters(): array
{
return $this->parameters;
}
public function jsonSerialize(): mixed
{
$arr = [];
if (isset($this->ref)) {
$arr['ref'] = $this->getRef();
}
if (isset($this->summary)) {
$arr['summary'] = $this->getSummary();
}
if (isset($this->description)) {
$arr['description'] = $this->getDescription();
}
foreach ($this->methods as $method => $operation) {
$arr[$method] = $operation;
}
if (isset($this->parameters)) {
$arr['parameters'] = $this->getParameters();
}
return $arr;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace ProVM\Alias\Controller;
use Psr\Http\Message\ResponseInterface;
use function Safe\json_encode;
trait Json
{
public function withJson(ResponseInterface $response, $json_data, int $status_code = 200): ResponseInterface
{
$response->getBody()->write(json_encode($json_data));
return $response
->withStatus($status_code)
->withHeader('Content-Type', 'application/json');
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace ProVM\Alias;
use Psr\Database\DatabaseInterface;
use Exception;
use PDO, PDOException, PDOStatement;
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): DatabaseInterface
{
$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): DatabaseInterface
{
$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(): DatabaseInterface
{
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): DatabaseInterface
{
$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

@ -0,0 +1,32 @@
<?php
namespace ProVM\Alias\Database;
use Error;
use function Safe\error_log;
use ProVM\Concept\Database\Query as QueryInterface;
abstract class Query implements QueryInterface
{
protected array $errors;
protected function error(Error $error): Query
{
$this->errors []= $error;
return $this;
}
protected function log(): void
{
if (!isset($this->errors) or count($this->errors) === 0) {
return;
}
$message = ['Query Error'];
foreach ($this->errors as $error) {
$message []= $error->getMessage();
}
error_log(implode(PHP_EOL, $message));
}
public function __toString(): string
{
return $this->build();
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace ProVM\Alias\Factory;
use Psr\Container\ContainerInterface;
use ProVM\Concept\Factory\Model as FactoryInterface;
use ProVM\Concept\Repository;
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(string $model_name): Repository
{
$class = str_replace('Model', 'Repository', $model_name);
return $this->getContainer()->get($class);
}
}

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

@ -0,0 +1,46 @@
<?php
namespace ProVM\Alias;
use function Safe\{fopen,fclose,fwrite};
use ProVM\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,46 @@
<?php
namespace ProVM\Alias;
use function Safe\{touch,mkdir,unlink};
use ProVM\Concept\Filesystem as FilesystemInterface;
use ProVM\Implement\Path;
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 Path::fromArray([
$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));
}
}

90
api/ProVM/Alias/Model.php Normal file
View File

@ -0,0 +1,90 @@
<?php
namespace ProVM\Alias;
use ReflectionObject;
use ProVM\Concept\Model as ModelInterface;
abstract class Model implements ModelInterface
{
protected int $id;
public function setId(int $id): ModelInterface
{
$this->id = $id;
return $this;
}
public function getId(): int
{
return $this->id;
}
protected bool $new = false;
public function setNew(): ModelInterface
{
$this->new = true;
return $this;
}
public function isNew(): bool
{
return $this->new;
}
protected array $edited;
public function setEdited(string $property): ModelInterface
{
$this->edited []= $property;
return $this;
}
public function getEdited(): array
{
return $this->edited;
}
public function isDirty(): bool
{
return isset($this->edited) and count($this->edited) > 0;
}
public function edit(array $data): void
{
$this->edited = [];
foreach ($data as $key => $val) {
if ($key === 'id') {
continue;
}
$getter = Model::parseGetter($key);
$setter = Model::parseSetter($key);
if (method_exists($this, $getter) and $this->{$getter}() !== $val) {
$this->{$setter}($val);
$this->setEdited($key);
}
}
}
public static function parseSetter(string $property): string
{
return 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $property)));
}
public static function parseGetter(string $property): string
{
return 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $property)));
}
public function jsonSerialize(): mixed
{
$ref = new ReflectionObject($this);
$properties = ($ref->getProperties());
$output = [
'id' => $this->getId()
];
foreach ($properties as $property) {
if (get_called_class() !== $property->getDeclaringClass()->getName()) {
continue;
}
$key = $property->getName();
$method = Model::parseGetter($key);
if (method_exists($this, $method)) {
$output[$key] = $this->{$method}();
}
}
return $output;
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace ProVM\Alias;
use Psr\Database\DatabaseInterface;
use Error;
use function Safe\error_log;
use ProVM\Concept\Model;
use ProVM\Concept\Repository as RepositoryInterface;
use ProVM\Concept\Factory\Model as FactoryInterface;
use ProVM\Implement\Database\QueryBuilder;
abstract class Repository implements RepositoryInterface
{
public function __construct(DatabaseInterface $database, QueryBuilder $builder, FactoryInterface $factory)
{
$this->setDatabase($database);
$this->setQueryBuilder($builder);
$this->setFactory($factory);
$this->setProperties(['id']);
$this->setup();
}
abstract public function setup(): void;
protected QueryBuilder $query;
public function setQueryBuilder(QueryBuilder $builder): RepositoryInterface
{
$this->query = $builder;
return $this;
}
public function getQueryBuilder(): QueryBuilder
{
return $this->query;
}
protected FactoryInterface $factory;
public function setFactory(FactoryInterface $factory): RepositoryInterface
{
$this->factory = $factory;
return $this;
}
public function getFactory(): FactoryInterface
{
return $this->factory;
}
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
{
$this->properties = [];
return $this->addProperties($properties);
}
public function addProperties(array $properties): RepositoryInterface
{
foreach ($properties as $property) {
$this->addProperty($property);
}
return $this;
}
public function getProperties(): array
{
return $this->properties;
}
protected DatabaseInterface $database;
public function setDatabase(DatabaseInterface $database): RepositoryInterface
{
$this->database = $database;
return $this;
}
public function getDatabase(): DatabaseInterface
{
return $this->database;
}
public function fetchAll(): array
{
$query = $this->getQueryBuilder()->select()->from($this->getTable())->build();
return array_map([$this, 'load'], $this->getDatabase()->query($query));
}
public function fetchById(int $id): Model
{
$query = $this->getQueryBuilder()->select()->from($this->getTable())->where('id = ?');
return $this->load($this->getDatabase()->prepare($query)->execute([$id])[0]);
}
protected function extractProperties(Model $model): array
{
$properties = [];
foreach ($this->getProperties() as $p) {
if ($model->isNew() and $p === 'id') {
continue;
}
$method = $model::parseGetter($p);
if (method_exists($model, $method)) {
try {
$properties[$p] = $model->{$method}();
} catch (Error $e) {
error_log($e);
}
}
}
return $properties;
}
public function save(Model $model): void
{
if ($model->isNew()) {
$this->saveNew($model);
return;
}
if ($model->isDirty()) {
$this->saveUpdate($model);
}
}
protected function saveNew(Model $model): void
{
$properties = $this->extractProperties($model);
$columns = array_keys($properties);
$ivalues = array_map(function($item) {return '?';}, array_keys($properties));
$values = array_values($properties);
$query = $this->getQueryBuilder()->insert()->into($this->getTable())->columns($columns)->values($ivalues)->build();
$this->getDatabase()->prepare($query)->insert($values);
}
protected function saveUpdate(Model $model): void
{
$edited = $model->getEdited();
$properties = array_intersect_key($this->extractProperties($model), array_combine($edited, $edited));
$sets = array_map(function($item) {
return "{$item} = ?";
}, array_keys($properties));
$values = array_values($properties);
$query = $this->getQueryBuilder()->update()->table($this->getTable())->set($sets)->where('id = ?')->build();
$this->getDatabase()->prepare($query)->update(array_merge($values, [$model->getId()]));
}
public function delete(Model $model): void
{
$query = $this->getQueryBuilder()->delete()->from($this->getTable())->where('id = ?')->build();
$this->getDatabase()->prepare($query)->delete([$model->getId()]);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace ProVM\Concept\API;
use ProVM\Concept\Repository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
interface Controller
{
public function setSingular(string $singular): Controller;
public function setPlural(string $plural): Controller;
public function getSingular(): string;
public function getPlural(): string;
public function setRepository(Repository $repository): Controller;
public function getRepository(): Repository;
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface;
public function get(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface;
public function add(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface;
public function edit(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface;
public function editOne(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface;
public function remove(ServerRequestInterface $request, ResponseInterface $response, int $model_id): ResponseInterface;
}

View File

@ -0,0 +1,7 @@
<?php
namespace ProVM\Concept\API;
interface OpenAPI
{
public static function add(array $data);
}

View File

@ -0,0 +1,24 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Operation extends JsonSerializable, OpenAPI
{
public function setTags(array $tags): Operation;
public function addTag(string $tag): Operation;
public function getTags(): array;
public function setSummary(string $summary): Operation;
public function getSummary(): string;
public function setDescription(string $description): Operation;
public function getDescription(): string;
public function setParameters(array $parameters): Operation;
public function addParameter(Parameter $parameter): Operation;
public function getParameters(): array;
public function setResponses(array $responses): Operation;
public function addResponse(int $code, Response $response): Operation;
public function getResponses(): array;
public function setDeprecated(): Operation;
public function isDeprecated(): bool;
}

View File

@ -0,0 +1,19 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Parameter extends JsonSerializable, OpenAPI
{
public function setName(string $name): Parameter;
public function getName(): string;
public function setIn(string $in): Parameter;
public function getIn(): string;
public function setDescription(string $description): Parameter;
public function getDescription(): string;
public function setRequired(): Parameter;
public function isRequired(): bool;
public function setDeprecated(): Parameter;
public function isDeprecated(): bool;
}

View File

@ -0,0 +1,17 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Response extends JsonSerializable, OpenAPI
{
public function setDescription(string $description): Response;
public function getDescription(): string;
public function setHeaders(array $headers): Response;
public function addHeader(string $header): Response;
public function getHeaders(): array;
public function setContent(array $contents): Response;
public function addContent(string $content): Response;
public function getContents(): array;
}

View File

@ -0,0 +1,21 @@
<?php
namespace ProVM\Concept\API\Route;
use JsonSerializable;
use ProVM\Concept\API\OpenAPI;
interface Route extends JsonSerializable, OpenAPI
{
public function setRef(string $ref): Route;
public function getRef(): string;
public function setSummary(string $summary): Route;
public function getSummary(): string;
public function setDescription(string $description): Route;
public function getDescription(): string;
public function setMethods(array $methods): Route;
public function addMethod(string $method, Operation $operation): Route;
public function getMethods(): array;
public function setParameters(array $parameters): Route;
public function addParameter(Parameter $parameter): Route;
public function getParameters(): array;
}

View File

@ -0,0 +1,7 @@
<?php
namespace ProVM\Concept\Database;
interface Query
{
public function build(): string;
}

View File

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

View File

@ -0,0 +1,16 @@
<?php
namespace ProVM\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 ProVM\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,16 @@
<?php
namespace ProVM\Concept;
use JsonSerializable;
interface Model extends JsonSerializable
{
public function setNew(): Model;
public function isNew(): bool;
public function setEdited(string $property): Model;
public function getEdited(): array;
public function isDirty(): bool;
public function edit(array $data): void;
public static function parseSetter(string $property): string;
public static function parseGetter(string $property): string;
}

View File

@ -0,0 +1,15 @@
<?php
namespace ProVM\Concept;
use ArrayAccess;
interface Path extends ArrayAccess
{
public function compose(string $base_path): Path;
public function add(string $path_segment): Path;
public function build(): string;
public static function fromArray(array $path): string;
public static function isAbsolute(string $path): bool;
public static function isRelative(string $path): bool;
}

View File

@ -0,0 +1,12 @@
<?php
namespace ProVM\Concept;
interface Repository
{
public function fetchAll(): array;
public function fetchById(int $id): Model;
public function load(array $data_row): Model;
public function create(array $data): Model;
public function save(Model $model): void;
public function delete(Model $model): void;
}

View File

@ -0,0 +1,37 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Operation as BaseOperation;
class Operation extends BaseOperation
{
public static function add(array $data): Operation
{
$operation = new Operation();
if (isset($data['tags'])) {
$operation->setTags($data['tags']);
}
if (isset($data['summary'])) {
$operation->setSummary($data['summary']);
}
if (isset($data['description'])) {
$operation->setDescription($data['description']);
}
if (isset($data['parameters'])) {
foreach ($data['parameters'] as $p) {
$parameter = Parameter::add($p);
$operation->addParameter($parameter);
}
}
if (isset($data['responses'])) {
foreach ($data['responses'] as $code => $r) {
$response = Response::add($r);
$operation->addResponse($code, $response);
}
}
if (isset($data['deprecated'])) {
$operation->setDeprecated();
}
return $operation;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Parameter as BaseParameter;
class Parameter extends BaseParameter
{
public static function add(array $data): Parameter
{
$parameter = new Parameter();
$parameter->setName($data['name']);
$parameter->setIn($data['in']);
if (isset($data['description'])) {
$parameter->setDescription($data['description']);
}
if (isset($data['required'])) {
$parameter->setRequired();
}
if (isset($data['deprecated'])) {
$parameter->setDeprecated();
}
return $parameter;
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Response as BaseResponse;
class Response extends BaseResponse
{
public static function add(array $data): Response
{
$response = new Response();
$response->setDescription($data['description']);
if (isset($data['headers'])) {
$response->setHeaders($data['headers']);
}
if (isset($data['content'])) {
$response->setContent($data['content']);
}
return $response;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace ProVM\Implement\API\Route;
use ProVM\Alias\API\Route\Route as BaseRoute;
class Route extends BaseRoute
{
public static function add(array $data): Route
{
$route = new Route();
if (isset($data['ref'])) {
$route->setRef($data['ref']);
}
if (isset($data['summary'])) {
$route->setSummary($data['summary']);
}
if (isset($data['description'])) {
$route->setDescription($data['description']);
}
$methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
foreach ($methods as $method) {
if (isset($data[$method])) {
$operation = Operation::add($data[$method]);
$route->addMethod($method, $operation);
}
}
if (isset($data['parameters'])) {
foreach ($data['parameters'] as $p) {
$parameter = Parameter::add($p);
$operation->addParameter($parameter);
}
}
return $route;
}
}

View File

@ -0,0 +1,118 @@
<?php
namespace ProVM\Implement;
use Psr\Collection\CollectionInterface;
use ReflectionFunction;
class Collection implements CollectionInterface
{
public function __construct()
{
$this->set(CollectionInterface::class, $this);
}
protected array $data;
public function set(mixed $name, $value): CollectionInterface
{
$this->data[$name] = $value;
return $this;
}
public function has(mixed $name): bool
{
return isset($this->data[$name]);
}
public function get(mixed $name)
{
return $this->process($this->data[$name]) ?? null;
}
public function remove(mixed $name): CollectionInterface
{
unset($this->data[$name]);
return $this;
}
protected function process($value)
{
if (is_callable($value)) {
return $this->processCallable($value);
}
if (is_array($value)) {
return $this->processArray($value);
}
return $value;
}
protected function processCallable(Callable $value)
{
$ref = new ReflectionFunction($value);
$param_array = [];
$params = $ref->getParameters();
foreach ($params as $p) {
$param_array[$p->getName()] = $this->get($p->getType()->getName());
}
return $ref->invokeArgs($param_array);
}
protected function processArray(array $value)
{
return $value;
}
public static function fromArray(array $source): CollectionInterface
{
$obj = new Collection();
foreach ($source as $key => $value) {
$obj->set($key, $value);
}
return $obj;
}
public static function fromObject(object $source): CollectionInterface
{
return Collection::fromArray((array) $source);
}
public function __get(mixed $name): mixed
{
return $this->get($name);
}
public function current(): mixed
{
return $this->data[$this->key()];
}
public function next(): void
{
next($this->data);
}
public function key(): mixed
{
return key($this->data);
}
public function valid(): bool
{
return isset($this->data);
}
public function rewind(): void
{
reset($this->data);
}
public function offsetExists(mixed $offset): bool
{
return $this->has($offset);
}
public function offsetGet(mixed $offset): mixed
{
return $this->get($offset);
}
public function offsetSet(mixed $offset, mixed $value): void
{
$this->set($offset, $value);
}
public function offsetUnset(mixed $offset): void
{
$this->remove($offset);
}
public function count(): int
{
return count($this->data);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace ProVM\Implement\Database;
use ProVM\Alias\Database;
class MySQL extends Database
{
public function __construct(string $host, string $username, string $password, string $database_name, int $port = 3306)
{
$this->setHost($host, $port);
$this->setUser($username, $password);
$this->setName($database_name);
}
public function getDsn(): string
{
$dsn = ["mysql:host={$this->getHost()}"];
if ($this->getPort() !== 3306) {
$dsn []= "port={$this->getPort()}";
}
$dsn []= "dbname={$this->getName()}";
return implode(';', $dsn);
}
public function needsUser(): bool
{
return true;
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use ProVM\Alias\Database\Query;
class Delete extends Query
{
protected string $table;
public function from($table): Delete
{
$this->table = $table;
return $this;
}
public function getFrom(): string
{
return $this->table;
}
protected array $conditions;
public function where(array|string $conditions): Delete
{
if (is_string($conditions)) {
return $this->addCondition($conditions);
}
foreach ($conditions as $condition) {
$this->addCondition($condition);
}
return $this;
}
public function addCondition(string $condition): Delete
{
$this->conditions []= $condition;
return $this;
}
public function getWhere(): string
{
return implode(' AND ', $this->conditions);
}
public function build(): string
{
$output = ['DELETE FROM'];
$output []= $this->getFrom();
$output []= "WHERE {$this->getWhere()}";
return implode(' ', $output);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use Error;
use ProVM\Alias\Database\Query;
class Insert extends Query
{
protected string $table;
public function into(string $table): Insert
{
$this->table = $table;
return $this;
}
public function getTable(): string
{
return $this->table;
}
protected Select $select;
public function select(Select $select): Insert
{
$this->select = $select;
return $this;
}
public function getSelect(): string
{
return $this->select->build();
}
protected array $columns;
public function columns(array $columns): Insert
{
foreach ($columns as $column) {
$this->addColumn($column);
}
return $this;
}
public function addColumn(string $column): Insert
{
$this->columns []= $column;
return $this;
}
public function getColumns(): string
{
return implode(', ', array_map(function($item) {return "`{$item}`";}, $this->columns));
}
protected array $values;
public function values(array $values): Insert
{
foreach ($values as $value) {
$this->addValue($value);
}
return $this;
}
public function addValue(string $value): Insert
{
$this->values []= $value;
return $this;
}
public function getValues(): string
{
return implode(', ', array_map(function($item) {
return $item;
}, $this->values));
}
protected array $updates;
public function update(array $updates): Insert
{
foreach ($updates as $update) {
$this->addUpdate($update);
}
return $this;
}
public function addUpdate(string $update_string): Insert
{
$this->updates []= $update_string;
return $this;
}
public function getUpdates(): string
{
return implode(', ', $this->updates);
}
public function build(): string
{
$output = ['INSERT INTO'];
$output []= $this->getTable();
try {
$output []= "({$this->getColumns()})";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= $this->getSelect();
} catch (\Error $e) {
$output []= "VALUES ({$this->getValues()})";
}
try {
$output []= "ON DUPLICATE KEY UPDATE {$this->getUpdates()}";
} catch (Error $e) {
$this->error($e);
}
$this->log();
return implode(' ', $output);
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use Error;
use function Safe\error_log;
use ProVM\Alias\Database\Query;
class Select extends Query
{
protected array $columns;
public function select(array|string $columns): Select
{
if (is_string($columns)) {
return $this->addColumn($columns);
}
foreach ($columns as $column) {
$this->addColumn($column);
}
return $this;
}
public function addColumn(string $column): Select
{
$this->columns []= $column;
return $this;
}
public function getSelect(): string
{
return implode(', ', array_map(function($item) {
return "`{$item}`";
}, $this->columns));
}
protected string $from;
public function from(string $table): Select
{
$this->from = $table;
return $this;
}
public function getFrom(): string
{
return $this->from;
}
protected array $joins;
public function join(array $joins): Select
{
if (is_string($joins[0])) {
return $this->addJoin($joins[0], $joins[1], $joins[2]);
}
foreach ($joins as $join) {
$this->addJoin($join[0], $join[1], $join[2]);
}
return $this;
}
public function addJoin(string $join_type, string $join_table, string $conditions): Select
{
$join_type = strtoupper($join_type);
$this->joins []= "{$join_type} {$join_table} {$conditions}";
return $this;
}
public function getJoins(): string
{
return implode('', $this->joins);
}
protected array $conditions;
public function where(array|string $conditions): Select
{
if (is_string($conditions)) {
return $this->addWhere($conditions);
}
foreach ($conditions as $condition) {
$this->addWhere($condition);
}
return $this;
}
public function addWhere($condition): Select
{
$this->conditions []= $condition;
return $this;
}
public function getWheres(): string
{
return implode(' AND ', $this->conditions);
}
protected array $orders;
public function order(array|string $orders): Select
{
if (is_string($orders)) {
return $this->addOrder($orders);
}
foreach ($orders as $order) {
if (is_array($order)) {
$this->addOrder($order[0], $order[1]);
continue;
}
$this->addOrder($order);
}
return $this;
}
public function addOrder(string $order, string $dir = 'desc'): Select
{
$dir = strtoupper($dir);
$this->orders []= "{$order} {$dir}";
return $this;
}
public function getOrders(): string
{
return implode(', ', $this->orders);
}
protected array $groups;
public function group(array|string $groups): Select
{
if (is_string($groups)) {
return $this->addGroup($groups);
}
foreach ($groups as $group) {
$this->addGroup($group);
}
return $this;
}
public function addGroup(string $group): Select
{
$this->groups []= $group;
return $this;
}
public function getGroups(): string
{
return implode(', ', $this->groups);
}
protected array $having;
public function having(array|string $havings): Select
{
if (is_string($havings)) {
return $this->addHaving($havings);
}
foreach ($havings as $having) {
$this->addHaving($having);
}
return $this;
}
public function addHaving(string $having): Select
{
$this->having []= $having;
return $this;
}
public function getHaving(): string
{
return implode(' AND ', $this->having);
}
protected int $limit;
public function limit(int $limit): Select
{
$this->limit = $limit;
return $this;
}
protected int $offset;
public function offset(int $offset): Select
{
$this->offset = $offset;
return $this;
}
public function getLimit(): string
{
$output = [$this->limit];
if (isset($this->offset)) {
$output []= 'OFFSET';
$output []= $this->offset;
}
return implode(' ', $output);
}
public function build(): string
{
$output = ['SELECT'];
try {
$output []= $this->getSelect();
} catch (Error $e) {
$output []= '*';
}
$output []= 'FROM';
$output []= $this->getFrom();
try {
$output [] = $this->getJoins();
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "WHERE {$this->getWheres()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "GROUP BY {$this->getGroups()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "HAVING {$this->getHaving()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "ORDER BY {$this->getOrders()}";
} catch (Error $e) {
$this->error($e);
}
try {
$output []= "LIMIT {$this->getLimit()}";
} catch (Error $e) {
$this->error($e);
}
$this->log();
return implode(' ', $output);
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace ProVM\Implement\Database\Query\MySQL;
use ProVM\Alias\Database\Query;
class Update extends Query
{
protected string $table;
public function table(string $table): Update
{
$this->table = $table;
return $this;
}
public function getTable(): string
{
return $this->table;
}
protected array $sets;
public function set(array|string $set_strings): Update
{
if (is_string($set_strings)) {
return $this->addSet($set_strings);
}
foreach ($set_strings as $set_string) {
$this->addSet($set_string);
}
return $this;
}
public function addSet(string $set_string): Update
{
$this->sets []= $set_string;
return $this;
}
public function getSets(): string
{
return implode(', ', $this->sets);
}
protected array $conditions;
public function where(array|string $conditions): Update
{
if (is_string($conditions)) {
return $this->addCondition($conditions);
}
foreach ($conditions as $condition) {
$this->addCondition($condition);
}
return $this;
}
public function addCondition(string $condition): Update
{
$this->conditions []= $condition;
return $this;
}
public function getWhere(): string
{
return implode(' AND ', $this->conditions);
}
public function build(): string
{
$output = ['UPDATE'];
$output []= $this->getTable();
$output []= "SET {$this->getSets()}";
$output []= "WHERE {$this->getWhere()}";
return implode(' ', $output);
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace ProVM\Implement\Database;
use Psr\Container\ContainerInterface;
use ProVM\Concept\Database\Query;
use ProVM\Implement\Database\Query\MySQL\Delete;
use ProVM\Implement\Database\Query\MySQL\Insert;
use ProVM\Implement\Database\Query\MySQL\Select;
use ProVM\Implement\Database\Query\MySQL\Update;
class QueryBuilder
{
public function __construct(ContainerInterface $container)
{
$arr = [
'select',
'insert',
'update',
'delete'
];
foreach ($arr as $b) {
$builder = implode("\\", [
'ProVM',
'Implement',
'Database',
'Query',
'MySQL',
ucwords($b)
]);
$this->addBuilder($container->get($builder));
}
}
protected array $builders;
public function addBuilder(Query $builder): QueryBuilder
{
$name = explode("\\", get_class($builder));
$name = array_pop($name);
$this->builders[strtolower($name)] = $builder;
return $this;
}
public function start(string $name): Query
{
return $this->builders[strtolower($name)];
}
public function select(): Select
{
return $this->start('select');
}
public function insert(): Insert
{
return $this->start('insert');
}
public function update(): Update
{
return $this->start('update');
}
public function delete(): Delete
{
return $this->start('delete');
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ProVM\Implement\Factory;
use Psr\Container\ContainerInterface;
use ProVM\Alias\Factory\Model as BaseModel;
class Model extends BaseModel
{
public function __construct(ContainerInterface $container)
{
$this->setContainer($container);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace ProVM\Implement\Factory;
use Psr\Container\ContainerInterface;
use ProVM\Concept\Repository as RepositoryInterface;
class Repository
{
protected ContainerInterface $container;
public function setContainer(ContainerInterface $container): Repository
{
$this->container = $container;
return $this;
}
public function getContainer(): ContainerInterface
{
return $this->container;
}
protected string $namespace;
public function setNamespace(string $namespace): Repository
{
$this->namespace = $namespace;
return $this;
}
public function getNamespace(): string
{
return $this->namespace;
}
public function find(string $repository_class): RepositoryInterface
{
$class = implode("\\", [
$this->getNamespace(),
$repository_class
]);
return $this->getContainer()->get($class);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace ProVM\Implement;
use ProVM\Alias\File as BaseFile;
class File extends BaseFile
{
public function __construct(string $filename)
{
$this->setFilename($filename);
}
public function append(string $data, ?int $length = null)
{
$this->write($data, $length);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace ProVM\Implement;
use ProVM\Concept\File as FileInterface;
use ProVM\Alias\Filesystem as BaseFilesystem;
class Filesystem extends BaseFilesystem
{
public function __construct(string $path)
{
$this->setBasePath($path);
}
public function get(string $relative_path): FileInterface
{
return new File($this->buildPath($relative_path));
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace ProVM\Implement;
use ProVM\Concept\Path as PathInterface;
class Path implements PathInterface
{
protected array $path;
public function compose(string $base_path): PathInterface
{
$this->path = [];
return $this->add($base_path);
}
public function add(string $path_segment): PathInterface
{
$this->path []= $path_segment;
return $this;
}
public function build(): string
{
return implode(DIRECTORY_SEPARATOR, $this->path);
}
public static function fromArray(array $path): string
{
$output = new Path();
$output->compose(array_shift($path));
foreach ($path as $p) {
$output->add($p);
}
return $output->build();
}
public static function isAbsolute(string $path): bool
{
return !file_exists(Path::fromArray([$path, '..']));
}
public static function isRelative(string $path): bool
{
return !Path::isAbsolute($path);
}
public function offsetExists(mixed $offset): bool
{
return isset($this->path[$offset]);
}
public function offsetGet(mixed $offset): mixed
{
return $this->path[$offset];
}
public function offsetSet(mixed $offset, mixed $value): void
{
$this->path[$offset] = $value;
}
public function offsetUnset(mixed $offset): void
{
unset($this->path[$offset]);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Psr\Collection;
use Iterator, ArrayAccess, Countable;
interface CollectionInterface extends Iterator, ArrayAccess, Countable
{
public function set(mixed $name, $value): CollectionInterface;
public function has(mixed $name): bool;
public function get(mixed $name);
public function remove(mixed $name): CollectionInterface;
public static function fromArray(array $source): CollectionInterface;
public static function fromObject(object $source): CollectionInterface;
}

View File

@ -0,0 +1,29 @@
<?php
namespace Psr\Database;
use PDO;
interface DatabaseInterface
{
public function setHost(string $host, ?int $port = null): DatabaseInterface;
public function getHost(): string;
public function getPort(): int;
public function setName(string $database_name): DatabaseInterface;
public function getName(): string;
public function setUser(string $username, string $password): DatabaseInterface;
public function getUser(): string;
public function getPassword(): string;
public function getDsn(): string;
public function needsUser(): bool;
public function connect(): DatabaseInterface;
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): DatabaseInterface;
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,6 +0,0 @@
<?php
namespace Contabilidad\Common\Alias;
interface DocumentHandler {
public function load(): ?array;
}

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

@ -1,34 +1,21 @@
<?php
namespace Contabilidad\Common\Controller;
namespace Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use function Safe\{json_decode, file_get_contents};
use ProVM\Alias\Controller\Json;
use ProVM\Implement\Path;
class Base {
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;
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, ContainerInterface $container): ResponseInterface
{
$filename = $container->get('documentation');
$documentation = json_decode(trim(file_get_contents($filename)));
return $this->withJson($response, $documentation);
}
}

View File

@ -1,112 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace 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;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\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);
class Categorias extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('categoria')
->setPlural('categorias')
->setRepository($container->get(Categoria::class));
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Common\Controller;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Coneccion;
class Conecciones extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('coneccion')
->setPlural('conecciones')
->setRepository($container->get(Coneccion::class));
}
}

View File

@ -1,53 +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\Common\Service\Consolidar as Service;
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, []);
}
}

View File

@ -1,197 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace 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;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\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);
class Cuentas extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('cuenta')
->setPlural('cuentas')
->setRepository($container->get(Cuenta::class));
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Common\Controller\Estado;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Estado\Coneccion;
class Conecciones extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('estado_coneccion')
->setPlural('estados_conecciones')
->setRepository($container->get(Coneccion::class));
}
}

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 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace 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;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\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);
class Monedas extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('moneda');
$this->setPlural('monedas');
$this->setRepository($container->get(Moneda::class));
}
}

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,75 +1,16 @@
<?php
namespace Contabilidad\Common\Controller;
namespace 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\Transaccion;
use Psr\Container\ContainerInterface;
use ProVM\Alias\API\Controller;
use Contabilidad\Repository\Transaccion;
class Transacciones {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$transacciones = $factory->find(Transaccion::class)->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;
});
}
$output = [
'transacciones' => $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, $transaccion_id): Response {
$transaccion = $factory->find(Transaccion::class)->one($transaccion_id);
$output = [
'input' => $transaccion_id,
'old' => $transaccion->toArray()
];
$input = json_decode($request->getBody());
$transaccion->edit($input);
$output['transaccion'] = $transaccion->toArray();
return $this->withJson($response, $output);
}
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);
class Transacciones extends Controller
{
public function setup(ContainerInterface $container): void
{
$this->setSingular('transaccion');
$this->setPlural('transacciones');
$this->setRepository($container->get(Transaccion::class));
}
}

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,11 +1,11 @@
<?php
namespace Contabilidad\Common\Middleware;
namespace 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;
use Common\Service\Auth as Service;
class Auth {
protected Factory $factory;

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

@ -0,0 +1,44 @@
<?php
namespace Common\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
use Slim\App;
use Slim\Interfaces\ErrorHandlerInterface;
use function Safe\error_log;
class Error implements ErrorHandlerInterface
{
public function __construct(App $app)
{
$this->setApp($app);
}
protected App $app;
public function setApp(App $app): Error
{
$this->app = $app;
return $this;
}
public function getApp(): App
{
return $this->app;
}
public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails): ResponseInterface
{
error_log($exception);
$response = $this->getApp()->getResponseFactory()->createResponse($exception->getCode());
$output = json_encode([
'uri' => $request->getUri()->getPath(),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTrace()
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$response->getBody()->write($output);
return $response->withHeader('Content-Type', 'application/json');
}
}

View File

@ -1,36 +1,74 @@
<?php
namespace Contabilidad\Common\Service;
namespace Common\Service;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use DI\NotFoundException;
class Auth {
protected string $key;
public function __construct(string $api_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;
public function isValid(Request $request): bool
{
return $this->key == $this->getKey($request);
}
public function getKey(Request $request): string
{
$errors = [];
try {
return $this->getHeaderKey($request);
} catch (NotFoundExceptionInterface $e) {
$errors []= $e;
}
try {
return $this->getBodyKey($request);
} catch (NotFoundExceptionInterface $e) {
$errors []= $e;
}
try {
return $this->getQueryKey($request);
} catch (NotFoundExceptionInterface $e) {
$errors []= $e;
}
throw new NotFoundException('API Key not found.');
}
protected function getHeaderKey(Request $request): string
{
if ($request->hasHeader('Authorization')) {
return $this->getAuthKey($request->getHeader('Authorization'));
}
throw new NotFoundException('API Key not found on header');
}
protected function getBodyKey(Request $request): string
{
if (isset($request->getParsedBody()['api_key'])) {
$sent_key = $request->getParsedBody()['api_key'];
return $this->key == $sent_key;
return $request->getParsedBody()['api_key'];
}
$post = $request->getParsedBody() ?? json_decode($request->getBody());
$sent_key = $this->getArrayKey($post);
if ($sent_key !== null) {
return $this->key == $sent_key;
try {
return $this->getArrayKey($post);
} catch (\Exception $e) {
throw new NotFoundException('API Key not found in body.');
}
$sent_key = $this->getArrayKey($request->getQueryParams());
return $this->key == $sent_key;
}
protected function getAuthKey($auth) {
protected function getQueryKey(Request $request): string
{
try {
return $this->getArrayKey($request->getQueryParams());
} catch (\Exception $e) {
throw new NotFoundException('API Key not found in query.');
}
}
protected function getAuthKey($auth)
{
if (is_array($auth)) {
$auth = $auth[0];
}
if (str_contains($auth, 'Bearer')) {
$auth = explode(' ', $auth)[1];
$auth = trim(str_replace('Bearer', '', $auth), ' ,');
}
return $auth;
}

View File

@ -1,155 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Carbon\Carbon;
use Contabilidad\Queue;
use \Model;
use ProVM\Common\Factory\Model as ModelFactory;
use Contabilidad\Consolidado;
use Contabilidad\Cuenta;
use Contabilidad\Transaccion;
class Consolidar {
protected $factory;
public function __construct(ModelFactory $factory) {
$this->factory = $factory;
}
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() {
$data = [
'command' => 'consolidar',
'created' => Carbon::now()->format('Y-m-d H:i:s')
];
$queue = Queue::add($this->factory, $data);
$queue->save();
}
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()) {
$transacciones = $this->getTransacciones($cuenta, $current);
if (count($transacciones) == 0) {
continue;
}
$f = $this->factory;
array_walk($transacciones, function(&$item) use ($cuenta, $f) {
$item->setFactory($f);
$item->valor = $item->transformar($cuenta->moneda());
});
$saldo = array_reduce($transacciones, function($sum, $item) {
return $sum + $item->valor;
});
$data = [
'cuenta_id' => $cuenta->id,
'fecha' => $current->format('Y-m-1'),
'periodo' => 'P1M',
'saldo' => $saldo
];
$consolidado = $this->factory->create(Consolidado::class, $data);
$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();
return array_merge($debitos, $creditos);
}
}

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

@ -0,0 +1,60 @@
<?php
namespace Common\Service;
use function Safe\{json_encode, json_decode, file_get_contents, file_put_contents};
use ProVM\Concept\API\Route\Route;
use ProVM\Implement\API\Route\Route as RouteObj;
class Documenter
{
public function __construct(string $filename)
{
$this->setFilename($filename);
}
protected string $filename;
public function setFilename(string $filename): Documenter
{
$this->filename = $filename;
return $this;
}
public function getFilename(): string
{
return $this->filename;
}
protected array $routes;
public function setRoutes(array $routes): Documenter
{
foreach ($routes as $path => $route) {
$this->addRoute($path, $route);
}
return $this;
}
public function addRoute(string $path, Route $route): Documenter
{
$this->routes[$path] = $route;
return $this;
}
public function getRoutes(): array
{
return $this->routes;
}
protected function load(): mixed
{
$json = json_decode(trim(file_get_contents($this->getFilename())), JSON_OBJECT_AS_ARRAY);
foreach ($json['paths'] as $path => $data) {
$route = RouteObj::add($data);
$this->addRoute($path, $route);
}
return $json;
}
public function save(): void
{
$json = $this->load();
$json['paths'] = $this->getRoutes();
ksort($json['paths']);
file_put_contents($this->getFilename(), json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
}
}

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

@ -0,0 +1,179 @@
<?php
namespace Common\Service;
use Slim\App;
use ProVM\Concept\API\Controller;
use ProVM\Implement\API\Route\Route;
class Router
{
public function __construct(App &$app, Documenter $documenter)
{
$this->setApp($app);
$this->setDocumenter($documenter);
}
protected App $app;
public function setApp(App &$app): Router
{
$this->app = $app;
return $this;
}
public function getApp(): App
{
return $this->app;
}
protected Documenter $documenter;
public function setDocumenter(Documenter $documenter): Router
{
$this->documenter = $documenter;
return $this;
}
public function getDocumenter(): Documenter
{
return $this->documenter;
}
protected Controller $controller;
public function setController(Controller $controller): Router
{
$this->controller = $controller;
return $this;
}
public function getController(): Controller
{
return $this->controller;
}
protected function document(): void
{
$docs = [
"/{$this->getController()->getPlural()}" => [
'get' => [
'description' => "Entrega una lista de {$this->getController()->getPlural()}",
'responses' => [
'200' => [
'description' => "Lista de {$this->getController()->getPlural()}"
]
]
]
],
"/{$this->getController()->getPlural()}/add" => [
'post' => [
'description' => "Agregar {$this->getController()->getPlural()} con una lista",
'parameters' => [
[
'name' => $this->getController()->getPlural(),
'in' => 'query',
'description' => 'Lista de datos para agregar',
'required' => true
]
],
'responses' => [
'200' => [
'description' => "Entrega un listado de {$this->getController()->getPlural()} y si fueron agregadas"
]
]
]
],
"/{$this->getController()->getPlural()}/edit" => [
'put' => [
'description' => "Editar multiples {$this->getController()->getPlural()} con una lista",
'parameters' => [
[
'name' => $this->getController()->getPlural(),
'in' => 'query',
'description' => 'Lista de datos para editar',
'required' => true
]
],
'responses' => [
'200' => [
'description' => "Entrega un listado de {$this->getController()->getPlural()} identificando si fueron editados"
]
]
]
],
"/{$this->getController()->getSingular()}/{model_id}" => [
'get' => [
'description' => "Entrega {$this->getController()->getSingular()}",
'parameters' => [
[
'name' => 'id',
'in' => 'path',
'description' => "{$this->getController()->getSingular()} id",
'required' => true
]
],
'responses' => [
'200' => [
'description' => "{$this->getController()->getSingular()} found or null"
]
]
]
],
"/{$this->getController()->getSingular()}/{model_id}/edit" => [
'put' => [
'description' => '',
'parameters' => [
[
'name' => 'model_id',
'in' => 'path',
'description' => "{$this->getController()->getSingular()} id",
'required' => true
],
[
'name' => "{$this->getController()->getSingular()}",
'in' => 'query',
'description' => '',
'required' => true
]
],
'responses' => [
'200' => [
'description' => ''
]
]
]
],
"/{$this->getController()->getSingular()}/{model_id}/remove" => [
'delete' => [
'description' => '',
'parameters' => [
[
'name' => 'model_id',
'in' => 'path',
'description' => "{$this->getController()->getSingular()} id",
'required' => true
]
],
'responses' => [
'200' => [
'description' => ''
]
]
]
]
];
foreach ($docs as $path => $r) {
$route = Route::add($r);
$this->getDocumenter()->addRoute($path, $route);
}
$this->getDocumenter()->save();
}
public function build(): void
{
$this->getApp()->group('/' . $this->getController()->getPlural(), function($app) {
$app->post('/add[/]', [get_class($this->getController()), 'add']);
$app->put('/edit[/]', [get_class($this->getController()), 'edit']);
$app->get('[/]', get_class($this->getController()));
});
$this->getApp()->group('/' . $this->getController()->getSingular() . '/{model_id}', function($app) {
$app->put('/edit[/]', [get_class($this->getController()), 'editOne']);
$app->delete('/remove[/]', [get_class($this->getController()), 'remove']);
$app->get('[/]', [get_class($this->getController()), 'get']);
});
$this->document();
}
}

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;
}
}

View File

@ -7,16 +7,12 @@
"php-di/slim-bridge": "^3.1",
"nyholm/psr7": "^1.4",
"nyholm/psr7-server": "^1.0",
"zeuxisoo/slim-whoops": "^0.7.3",
"provm/controller": "^1.0",
"provm/models": "^1.0.0-rc3",
"nesbot/carbon": "^2.50",
"robmorgan/phinx": "^0.12.9",
"odan/phinx-migrations-generator": "^5.4",
"martin-mikac/csv-to-phinx-seeder": "^1.6",
"guzzlehttp/guzzle": "^7.4",
"phpoffice/phpspreadsheet": "^1.19",
"thiagoalessio/tesseract_ocr": "^2.12"
"zeuxisoo/slim-whoops": "^0.7.3",
"thecodingmachine/safe": "^2.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
@ -30,20 +26,12 @@
],
"autoload": {
"psr-4": {
"Contabilidad\\Common\\": "common",
"Contabilidad\\": "src"
"Common\\": "common/",
"Contabilidad\\": "src/",
"ProVM\\": "ProVM/",
"Psr\\": "Psr/"
}
},
"repositories": [
{
"type": "git",
"url": "http://git.provm.cl/ProVM/controller.git"
},
{
"type": "git",
"url": "http://git.provm.cl/ProVM/models.git"
}
],
"config": {
"secure-http": false
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoCategoria extends AbstractMigration
{
public function change(): void
{
$this->table('tipos_categorias')
->addColumn('nombre', 'string')
->create();
}
}

View File

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoCategoria 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_categoria')
->addColumn('descripcion', 'string')
->addColumn('activo', 'boolean')
->create();
}
}

View File

@ -18,7 +18,7 @@ final class TipoEstadoConeccion extends AbstractMigration
*/
public function change(): void
{
$this->table('tipos_estado_coneccion')
$this->table('tipos_estados_conecciones')
->addColumn('descripcion', 'string')
->create();
}

View File

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoCuenta 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_cuenta')
->addColumn('descripcion', 'string')
->addColumn('color', 'string', ['length' => 6, 'default' => 'ffffff'])
->create();
}
}

View File

@ -21,7 +21,6 @@ final class Categoria extends AbstractMigration
$this->table('categorias')
->addColumn('nombre', 'string')
->addColumn('tipo_id', 'integer')
->addForeignKey('tipo_id', 'tipos_categoria', ['delete' => 'cascade', 'update' => 'cascade'])
->create();
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TipoTransaccion extends AbstractMigration
{
public function change(): void
{
$this->table('tipos_transacciones')
->addColumn('nombre', 'string')
->addColumn('color', 'string')
->create();
}
}

View File

@ -20,10 +20,6 @@ final class Cuenta extends AbstractMigration
{
$this->table('cuentas')
->addColumn('nombre', 'string')
->addColumn('categoria_id', 'integer')
->addForeignKey('categoria_id', 'categorias', ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('tipo_id', 'integer')
->addForeignKey('tipo_id', 'tipos_cuenta', ['delete' => 'cascade', 'update' => 'cascade'])
->create();
}
}

View File

@ -18,12 +18,10 @@ final class EstadoConeccion extends AbstractMigration
*/
public function change(): void
{
$this->table('estados_coneccion')
$this->table('estados_conecciones')
->addColumn('coneccion_id', 'integer')
->addForeignKey('coneccion_id', 'conecciones', ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('fecha', 'date')
->addColumn('tipo_id', 'integer')
->addForeignKey('tipo_id', 'tipos_estado_coneccion', ['delete' => 'cascade', 'update' => 'cascade'])
->create();
}
}

View File

@ -19,14 +19,12 @@ final class Transaccion extends AbstractMigration
public function change(): void
{
$this->table('transacciones')
->addColumn('debito_id', 'integer')
->addForeignKey('debito_id', 'cuentas', ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('credito_id', 'integer')
->addForeignKey('credito_id', 'cuentas', ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('fecha', 'datetime')
->addColumn('cuenta_id', 'integer')
->addColumn('glosa', 'string')
->addColumn('detalle', 'text')
->addColumn('tipo_id', 'integer')
->addColumn('valor', 'double')
->addColumn('categoria_id', 'integer')
->addColumn('fecha', 'datetime')
->create();
}
}

View File

@ -20,7 +20,6 @@ final class CuentaMoneda extends AbstractMigration
{
$this->table('cuentas')
->addColumn('moneda_id', 'integer')
->addForeignKey('moneda_id', 'monedas', ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -21,9 +21,9 @@ final class TipoCambio extends AbstractMigration
$this->table('tipos_cambio')
->addColumn('fecha', 'datetime')
->addColumn('desde_id', 'integer')
->addForeignKey('desde_id', 'monedas', ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('desde_id', 'monedas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('hasta_id', 'integer')
->addForeignKey('hasta_id', 'monedas', ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('hasta_id', 'monedas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('valor', 'double')
->create();
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CategoriaForeign extends AbstractMigration
{
public function change(): void
{
$this->table('categorias')
->addForeignKey('tipo_id', 'tipos_categorias', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class EstadoConeccionForeign extends AbstractMigration
{
public function change(): void
{
$this->table('estados_conecciones')
->addForeignKey('coneccion_id', 'conecciones', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('tipo_id', 'tipos_estados_conecciones', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class TransaccionForeign extends AbstractMigration
{
public function change(): void
{
$this->table('transacciones')
->addForeignKey('cuenta_id', 'cuentas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('tipo_id', 'tipos_transacciones', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('categoria_id', 'categorias', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class CuentaMonedaForeign extends AbstractMigration
{
public function change(): void
{
$this->table('cuentas')
->addForeignKey('moneda_id', 'monedas', ['id'], ['delete' => 'cascade', 'update' => 'cascade'])
->update();
}
}

View File

@ -1,27 +0,0 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Queue 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('queue')
->addColumn('command', 'string')
->addColumn('created', 'datetime')
->addColumn('processed', 'boolean', ['default' => 0])
->create();
}
}

View File

@ -1,28 +0,0 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class QueueArguments 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('queue_arguments')
->addColumn('queue_id', 'integer')
->addForeignKey('queue_id', 'queue', ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('argument', 'string', ['length' => 100])
->addColumn('value', 'string')
->create();
}
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class Consolidados 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('consolidados')
->addColumn('cuenta_id', 'integer')
->addForeignKey('cuenta_id', 'cuentas', ['delete' => 'cascade', 'update' => 'cascade'])
->addColumn('fecha', 'date')
->addColumn('periodo', 'string', ['length' => 50])
->addColumn('saldo', 'double')
->create();
}
}

View File

@ -1,36 +0,0 @@
<?php
use Phinx\Seed\AbstractSeed;
class TipoCuenta 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 = [
[
'descripcion' => 'Ganancia'
],
[
'descripcion' => 'Activo'
],
[
'descripcion' => 'Pasivo'
],
[
'descripcion' => 'Perdida'
]
];
$this->table('tipos_cuenta')
->insert($data)
->saveData();
}
}

View File

@ -1,12 +1,8 @@
<?php
try {
require_once implode(DIRECTORY_SEPARATOR, [
ini_set('error_reporting', E_ALL & ~E_NOTICE & ~E_DEPRECATED);
$app = require_once implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__),
'setup',
'app.php'
]);
$app->run();
} catch (Error | Exception $e) {
error_log($e);
throw $e;
}

View File

@ -0,0 +1,433 @@
{
"openapi": "3.0.0",
"info": {
"title": "Contabilidad",
"version": "1.0.0"
},
"paths": {
"/coneccion/{model_id}": {
"get": {
"description": "Entrega coneccion",
"parameters": [
{
"name": "id",
"in": "path",
"description": "coneccion id",
"required": true
}
],
"responses": {
"200": {
"description": "coneccion found or null"
}
}
}
},
"/coneccion/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "coneccion id",
"required": true
},
{
"name": "coneccion",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/coneccion/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "coneccion id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/conecciones": {
"get": {
"description": "Entrega una lista de conecciones",
"responses": {
"200": {
"description": "Lista de conecciones"
}
}
}
},
"/conecciones/add": {
"post": {
"description": "Agregar conecciones con una lista",
"parameters": [
{
"name": "conecciones",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de conecciones y si fueron agregadas"
}
}
}
},
"/conecciones/edit": {
"put": {
"description": "Editar multiples conecciones con una lista",
"parameters": [
{
"name": "conecciones",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de conecciones identificando si fueron editados"
}
}
}
},
"/cuenta/{model_id}": {
"get": {
"description": "Entrega cuenta",
"parameters": [
{
"name": "id",
"in": "path",
"description": "cuenta id",
"required": true
}
],
"responses": {
"200": {
"description": "cuenta found or null"
}
}
}
},
"/cuenta/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "cuenta id",
"required": true
},
{
"name": "cuenta",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/cuenta/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "cuenta id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/cuentas": {
"get": {
"description": "Entrega una lista de cuentas",
"responses": {
"200": {
"description": "Lista de cuentas"
}
}
}
},
"/cuentas/add": {
"post": {
"description": "Agregar cuentas con una lista",
"parameters": [
{
"name": "cuentas",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de cuentas y si fueron agregadas"
}
}
}
},
"/cuentas/edit": {
"put": {
"description": "Editar multiples cuentas con una lista",
"parameters": [
{
"name": "cuentas",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de cuentas identificando si fueron editados"
}
}
}
},
"/moneda/{model_id}": {
"get": {
"description": "Entrega moneda",
"parameters": [
{
"name": "id",
"in": "path",
"description": "moneda id",
"required": true
}
],
"responses": {
"200": {
"description": "moneda found or null"
}
}
}
},
"/moneda/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "moneda id",
"required": true
},
{
"name": "moneda",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/moneda/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "moneda id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/monedas": {
"get": {
"description": "Entrega una lista de monedas",
"responses": {
"200": {
"description": "Lista de monedas"
}
}
}
},
"/monedas/add": {
"post": {
"description": "Agregar monedas con una lista",
"parameters": [
{
"name": "monedas",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de monedas y si fueron agregadas"
}
}
}
},
"/monedas/edit": {
"put": {
"description": "Editar multiples monedas con una lista",
"parameters": [
{
"name": "monedas",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de monedas identificando si fueron editados"
}
}
}
},
"/transaccion/{model_id}": {
"get": {
"description": "Entrega transaccion",
"parameters": [
{
"name": "id",
"in": "path",
"description": "transaccion id",
"required": true
}
],
"responses": {
"200": {
"description": "transaccion found or null"
}
}
}
},
"/transaccion/{model_id}/edit": {
"put": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "transaccion id",
"required": true
},
{
"name": "transaccion",
"in": "query",
"description": "",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/transaccion/{model_id}/remove": {
"delete": {
"description": "",
"parameters": [
{
"name": "model_id",
"in": "path",
"description": "transaccion id",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/transacciones": {
"get": {
"description": "Entrega una lista de transacciones",
"responses": {
"200": {
"description": "Lista de transacciones"
}
}
}
},
"/transacciones/add": {
"post": {
"description": "Agregar transacciones con una lista",
"parameters": [
{
"name": "transacciones",
"in": "query",
"description": "Lista de datos para agregar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de transacciones y si fueron agregadas"
}
}
}
},
"/transacciones/edit": {
"put": {
"description": "Editar multiples transacciones con una lista",
"parameters": [
{
"name": "transacciones",
"in": "query",
"description": "Lista de datos para editar",
"required": true
}
],
"responses": {
"200": {
"description": "Entrega un listado de transacciones identificando si fueron editados"
}
}
}
}
}
}

View File

@ -1,7 +1,4 @@
<?php
use Contabilidad\Common\Controller\Base;
use 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);

View File

@ -1,13 +1,6 @@
<?php
use Contabilidad\Common\Controller\Categorias;
use Common\Controller\Categorias;
$app->group('/categorias', function($app) {
$app->post('/add[/]', [Categorias::class, 'add']);
$app->get('[/]', Categorias::class);
});
$app->group('/categoria/{categoria_id}', function($app) {
$app->get('/cuentas', [Categorias::class, 'cuentas']);
$app->put('/edit', [Categorias::class, 'edit']);
$app->delete('/delete', [Categorias::class, 'delete']);
$app->get('[/]', [Categorias::class, 'show']);
});
$router = $app->getContainer()->get(\Common\Service\Router::class);
$router->setController($app->getContainer()->get(Categorias::class));
$router->build();

Some files were not shown because too many files have changed in this diff Show More