Version 3.0

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

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +0,0 @@
<?php
namespace Contabilidad\Common\Concept;
use Contabilidad\Common\Alias\DocumentHandler as HandlerInterface;
abstract class DocumentHandler implements HandlerInterface {
protected string $folder;
public function __construct(string $source_folder) {
$this->folder = $source_folder;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,34 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
class Base {
use Json;
public function __invoke(Request $request, Response $response): Response {
return $this->withJson($response, []);
}
public function generate_key(Request $request, Response $response): Response {
$server_addr = explode('.', $request->getServerParams()['SERVER_ADDR']);
$remote_addr = explode('.', $request->getServerParams()['REMOTE_ADDR']);
for ($i = 0; $i < 3; $i ++) {
if ($server_addr[$i] != $remote_addr[$i]) {
throw new \InvalidArgumentException('Invalid connection address.');
}
}
$salt = mt_rand();
$signature = hash_hmac('sha256', $salt, 'contabilidad', true);
$key = urlencode(base64_encode($signature));
return $this->withJson($response, ['key' => $key]);
}
public function info(Request $request, Response $response): Response {
ob_start();
phpinfo();
$data = ob_get_clean();
$response->getBody()->write($data);
return $response;
}
}

View File

@ -1,112 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Common\Service\TiposCambios as Service;
use Contabilidad\Categoria;
class Categorias {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
$categorias = $factory->find(Categoria::class)->many();
if ($categorias !== null) {
array_walk($categorias, function(&$item) use ($service) {
$arr = $item->toArray();
if ($item->cuentas()) {
$arr['cuentas'] = array_map(function($item) {
return $item->toArray();
}, $item->cuentas());
}
$maps = ['activo', 'pasivo', 'ganancia', 'perdida'];
foreach ($maps as $m) {
$p = $m . 's';
$t = ucfirst($m);
$cuentas = $item->getCuentasOf($t);
if ($cuentas === false or $cuentas === null) {
$arr[$p] = 0;
continue;
}
$arr[$p] = array_reduce($cuentas, function($sum, $item) use($service) {
return $sum + $item->saldo($service, true);
});
}
$item = $arr;
});
usort($categorias, function($a, $b) {
return strcmp($a['nombre'], $b['nombre']);
});
}
$output = [
'categorias' => $categorias
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'categoria' => $categoria?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$categoria = Categoria::add($factory, $in);
$results []= ['categoria' => $categoria?->toArray(), 'agregado' => $categoria?->save()];
}
} else {
$categoria = Categoria::add($factory, $input);
$results []= ['categoria' => $categoria?->toArray(), 'agregado' => $categoria?->save()];
}
$output = [
'input' => $input,
'categorias' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'old' => $categoria->toArray()
];
$input = json_decode($request->getBody());
$categoria->edit($input);
$output['categoria'] = $categoria->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$output = [
'input' => $categoria_id,
'categoria' => $categoria->toArray(),
'eliminado' => $categoria->delete()
];
return $this->withJson($response, $output);
}
public function cuentas(Request $request, Response $response, Factory $factory, Service $service, $categoria_id): Response {
$categoria = $factory->find(Categoria::class)->one($categoria_id);
$cuentas = null;
if ($categoria !== null) {
$cuentas = $categoria->cuentas();
if ($cuentas !== null) {
array_walk($cuentas, function(&$item) use ($service) {
$item = $item->toArray($service);
});
}
}
$output = [
'input' => $categoria_id,
'categoria' => $categoria?->toArray(),
'cuentas' => $cuentas
];
return $this->withJson($response, $output);
}
}

View File

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

View File

@ -1,197 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Contabilidad\Transaccion;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Carbon\Carbon;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Common\Service\TiposCambios as Service;
use Contabilidad\Cuenta;
class Cuentas {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$cuentas = $factory->find(Cuenta::class)->many();
if ($cuentas) {
array_walk($cuentas, function (&$item) {
$arr = $item->toArray();
$arr['categoria'] = $item->categoria()->toArray();
$item = $arr;
});
usort($cuentas, function($a, $b) {
$t = strcmp($a['tipo']['descripcion'], $b['tipo']['descripcion']);
if ($t != 0) {
return $t;
}
$c = strcmp($a['categoria']['nombre'], $b['categoria']['nombre']);
if ($c != 0) {
return $c;
}
return strcmp($a['nombre'], $b['nombre']);
});
}
$output = [
'cuentas' => $cuentas
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$cuenta = Cuenta::add($factory, $in);
$results []= ['cuenta' => $cuenta?->toArray(), 'agregado' => $cuenta?->save()];
}
} else {
$cuenta = Cuenta::add($factory, $input);
$results []= ['cuenta' => $cuenta?->toArray(), 'agregado' => $cuenta?->save()];
}
$output = [
'input' => $input,
'cuentas' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'old' => $cuenta->toArray()
];
$input = json_decode($request->getBody());
$cuenta->edit($input);
$output['cuenta'] = $cuenta->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta->toArray(),
'eliminado' => $cuenta->delete()
];
return $this->withJson($response, $output);
}
public function categoria(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'categoria' => $cuenta?->categoria()->toArray()
];
return $this->withJson($response, $output);
}
public function entradas(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$entradas = null;
if ($cuenta !== null) {
$entradas = $cuenta->entradas();
if ($entradas !== null) {
array_walk($entradas, function(&$item) {
$item = $item->toArray();
});
}
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'entradas' => $entradas
];
return $this->withJson($response, $output);
}
protected function transaccionToArray(Service $service, Cuenta $cuenta, Transaccion $transaccion): array {
$arr = $transaccion->toArray();
if ($cuenta->moneda()->codigo === 'CLP') {
if ($transaccion->debito()->moneda()->codigo !== 'CLP' or $transaccion->credito()->moneda()->codigo !== 'CLP') {
if ($transaccion->debito()->moneda()->codigo !== 'CLP') {
$c = $transaccion->debito();
} else {
$c = $transaccion->credito();
}
$service->get($transaccion->fecha(), $c->moneda()->id);
$arr['valor'] = $transaccion->valor;
$arr['valorFormateado'] = $cuenta->moneda()->format($arr['valor']);
}
}
$arr['debito']['categoria'] = $transaccion->debito()->categoria()->toArray();
$arr['credito']['categoria'] = $transaccion->credito()->categoria()->toArray();
return $arr;
}
public function transacciones(Request $request, Response $response, Factory $factory, Service $service, $cuenta_id, $limit = null, $start = 0): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$transacciones = null;
if ($cuenta !== null) {
$transacciones = $cuenta->transacciones($limit, $start);
if (count($transacciones) > 0) {
foreach ($transacciones as &$transaccion) {
$transaccion = $this->transaccionToArray($service, $cuenta, $transaccion);
}
}
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
public function transaccionesMonth(Request $request, Response $response, Factory $factory, Service $service, $cuenta_id, $month): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$month = Carbon::parse($month);
$transacciones = null;
if ($cuenta !== null) {
$transacciones = $cuenta->transaccionesMonth($month);
if (count($transacciones) > 0) {
foreach ($transacciones as &$transaccion) {
$transaccion = $this->transaccionToArray($service, $cuenta, $transaccion);
}
}
}
$output = [
'input' => compact('cuenta_id', 'month'),
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
public function transaccionesAcumulation(Request $request, Response $response, Factory $factory, $cuenta_id, $date): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$f = Carbon::parse($date);
$acum = 0;
if ($cuenta !== null) {
$acum = $cuenta->acumulacion($f);
}
$output = [
'input' => compact('cuenta_id', 'date'),
'cuenta' => $cuenta?->toArray(),
'format' => $cuenta->moneda()->toArray(),
'acumulation' => $acum
];
return $this->withJson($response, $output);
}
public function transaccionesAmount(Request $request, Response $response, Factory $factory, $cuenta_id): Response {
$cuenta = $factory->find(Cuenta::class)->one($cuenta_id);
$transacciones = 0;
if ($cuenta !== null) {
$transacciones = count($cuenta->transacciones());
}
$output = [
'input' => $cuenta_id,
'cuenta' => $cuenta?->toArray(),
'transacciones' => $transacciones
];
return $this->withJson($response, $output);
}
}

View File

@ -1,76 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use Contabilidad\Common\Service\FileHandler as Handler;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Cuenta;
class Files {
use Json;
public function __invoke(Request $request, Response $response, Handler $handler): Response {
$files = $handler->listFiles();
usort($files, function($a, $b) {
$f = strcmp($a->folder, $b->folder);
if ($f == 0) {
return strcmp($a->filename, $b->filename);
}
return $f;
});
return $this->withJson($response, compact('files'));
}
public function upload(Request $request, Response $response, Handler $handler, Factory $factory): Response {
$post = $request->getParsedBody();
$cuenta = $factory->find(Cuenta::class)->one($post['cuenta']);
$file = $request->getUploadedFiles()['archivo'];
$new_name = implode(' - ', [$cuenta->nombre, $cuenta->categoria()->nombre, $post['fecha']]);
$output = [
'input' => [
'name' => $file->getClientFilename(),
'type' => $file->getClientMediaType(),
'size' => $file->getSize(),
'error' => $file->getError()
],
'new_name' => $new_name,
'uploaded' => $handler->uploadFile($file, $new_name)
];
return $this->withJson($response, $output);
}
public function get(Request $request, Response $response, Handler $handler, $folder, $filename): Response {
$file = $handler->getFile($folder, $filename);
return $response
->withHeader('Content-Type', $handler->getType($folder))
->withHeader('Content-Disposition', 'attachment; filename=' . $filename)
->withAddedHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->withHeader('Cache-Control', 'post-check=0, pre-check=0')
->withHeader('Pragma', 'no-cache')
->withBody($file);
}
public function edit(Request $request, Response $response, Handler $handler, Factory $factory, $folder, $filename): Response {
$post = json_decode($request->getBody());
$cuenta = $factory->find(Cuenta::class)->one($post->cuenta);
$new_name = implode(' - ', [$cuenta->nombre, $cuenta->categoria()->nombre, $post->fecha]);
$output = [
'input' => [
'folder' => $folder,
'filename' => $filename,
'post' => $post
],
'edited' => $handler->editFilename($folder, $filename, $new_name)
];
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Handler $handler, $folder, $filename): Response {
$output = [
'input' => [
'folder' => $folder,
'filename' => $filename
],
'deleted' => $handler->deleteFile($folder, $filename)
];
return $this->withJson($response, $output);
}
}

View File

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

View File

@ -1,71 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Moneda;
class Monedas {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$monedas = $factory->find(Moneda::class)->array();
if ($monedas) {
usort($monedas, function($a, $b) {
return strcmp($a['denominacion'], $b['denominacion']);
});
}
$output = [
'monedas' => $monedas
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $moneda_id): Response {
$moneda = $factory->find(Moneda::class)->one($moneda_id);
$output = [
'input' => $moneda_id,
'moneda' => $moneda?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$moneda = Moneda::add($factory, $in);
$results []= ['moneda' => $moneda?->toArray(), 'agregado' => $moneda?->save()];
}
} else {
$moneda = Moneda::add($factory, $input);
$results []= ['moneda' => $moneda?->toArray(), 'agregado' => $moneda?->save()];
}
$output = [
'input' => $input,
'monedas' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $moneda_id): Response {
$moneda = $factory->find(Moneda::class)->one($moneda_id);
$output = [
'input' => $moneda_id,
'old' => $moneda->toArray()
];
$input = json_decode($request->getBody());
$moneda->edit($input);
$output['moneda'] = $moneda->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $moneda_id): Response {
$moneda = $factory->find(Moneda::class)->one($moneda_id);
$output = [
'input' => $moneda_id,
'moneda' => $moneda->toArray(),
'eliminado' => $moneda->delete()
];
return $this->withJson($response, $output);
}
}

View File

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

View File

@ -1,95 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Carbon\Carbon;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Common\Service\TiposCambios as Service;
use Contabilidad\TipoCambio;
class TiposCambios {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$tipos = $factory->find(TipoCambio::class)->array();
if ($tipos) {
usort($tipos, function($a, $b) {
return strcmp($a['fecha'], $b['fecha']);
});
}
$output = [
'tipos' => $tipos
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCambio::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'tipo' => $tipo?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$tipo = TipoCambio::add($factory, $in);
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
}
} else {
$tipo = TipoCambio::add($factory, $input);
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
}
$output = [
'input' => $input,
'tipos' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCambio::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'old' => $tipo->toArray()
];
$input = json_decode($request->getBody());
$tipo->edit($input);
$output['tipo'] = $tipo->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCambio::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'tipo' => $tipo->toArray(),
'eliminado' => $tipo->delete()
];
return $this->withJson($response, $output);
}
public function obtain(Request $request, Response $response, Factory $factory, Service $service): Response {
$post = $request->getParsedBody();
$valor = $service->get($post['fecha'], $post['moneda_id']);
if ($valor === null) {
return $this->withJson($response, ['input' => $post, 'tipo' => null, 'error' => 'No se encontró valor']);
}
$data = [
'fecha' => $post['fecha'],
'desde_id' => $post['moneda_id'],
'hasta_id' => 1,
'valor' => $valor
];
$tipo = TipoCambio::add($factory, $data);
if ($tipo !== false and $tipo->is_new()) {
$tipo->save();
}
$output = [
'input' => $post,
'tipo' => $tipo?->toArray()
];
return $this->withJson($response, $output);
}
}

View File

@ -1,132 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Contabilidad\TipoCuenta;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Common\Service\TiposCambios as Service;
use Contabilidad\TipoCategoria;
class TiposCategorias {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory, Service $service): Response {
$tipos = $factory->find(TipoCategoria::class)->many();
if ($tipos !== null) {
array_walk($tipos, function(&$item) use ($service) {
$arr = $item->toArray();
$arr['categorias'] = $item->categorias();
if ($arr['categorias'] !== null) {
$arr['categorias'] = array_map(function($item) {
return $item->toArray();
}, $item->categorias());
}
$arr['saldo'] = abs($item->saldo($service));
$arr['totales'] = $item->getTotales($service);
$item = $arr;
});
usort($tipos, function($a, $b) {
return strcmp($a['descripcion'], $b['descripcion']);
});
}
$output = [
'tipos' => $tipos
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$tipo = TipoCategoria::add($factory, $in);
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
}
} else {
$tipo = TipoCategoria::add($factory, $input);
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
}
$output = [
'input' => $input,
'tipos' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCategoria::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'old' => $tipo->toArray()
];
$input = json_decode($request->getBody());
$tipo->edit($input);
$output['tipo'] = $tipo->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCategoria::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'tipo' => $tipo->toArray(),
'eliminado' => $tipo->delete()
];
return $this->withJson($response, $output);
}
public function categorias(Request $request, Response $response, Factory $factory, Service $service, $tipo_id): Response {
$tipo = $factory->find(TipoCategoria::class)->one($tipo_id);
$categorias = null;
if ($tipo != null) {
$categorias = $tipo->categorias();
if ($categorias !== null) {
array_walk($categorias, function(&$item) use ($service) {
$arr = $item->toArray($service);
$arr['totales'] = $item->getTotales($service);
$item = $arr;
});
}
}
$output = [
'input' => $tipo_id,
'tipo' => $tipo?->toArray(),
'categorias' => $categorias
];
return $this->withJson($response, $output);
}
public function balance(Request $request, Response $response, Factory $factory, Service $service): Response {
$tipos = $factory->find(TipoCategoria::class)->many();
$balance = array_reduce($tipos, function($sum, $item) use ($service) {
$totales = $item->getTotales($service);
if (!is_array($sum)) {
$sum = [];
}
foreach ($totales as $p => $total) {
if (!isset($sum[$p])) {
$sum[$p] = 0;
}
$sum[$p] += $total;
}
return $sum;
});
/*$balance = array_reduce($tipos, function($sum, $item) use ($service) {
$maps = ['activo', 'pasivo', 'ganancia', 'perdida'];
foreach ($maps as $m) {
$p = $m . 's';
$t = ucfirst($m);
if (!isset($sum[$p])) {
$sum[$p] = 0;
}
$cuentas = $item->getCuentasOf($t);
if ($cuentas === false or $cuentas === null) {
continue;
}
$sum[$p] += array_reduce($cuentas, function($sum, $item) use($service) {
return $sum + $item->saldo($service, true);
});
}
return $sum;
});*/
return $this->withJson($response, $balance);
}
}

View File

@ -1,71 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\TipoCuenta;
class TiposCuentas {
use Json;
public function __invoke(Request $request, Response $response, Factory $factory): Response {
$tipos = $factory->find(TipoCuenta::class)->array();
if ($tipos) {
usort($tipos, function($a, $b) {
return strcmp($a['descripcion'], $b['descripcion']);
});
}
$output = [
'tipos' => $tipos
];
return $this->withJson($response, $output);
}
public function show(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCuenta::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'tipo' => $tipo?->toArray()
];
return $this->withJson($response, $output);
}
public function add(Request $request, Response $response, Factory $factory): Response {
$input = json_decode($request->getBody());
$results = [];
if (is_array($input)) {
foreach ($input as $in) {
$tipo = TipoCuenta::add($factory, $in);
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
}
} else {
$tipo = TipoCuenta::add($factory, $input);
$results []= ['tipo' => $tipo?->toArray(), 'agregado' => $tipo?->save()];
}
$output = [
'input' => $input,
'tipos' => $results
];
return $this->withJson($response, $output);
}
public function edit(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCuenta::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'old' => $tipo->toArray()
];
$input = json_decode($request->getBody());
$tipo->edit($input);
$output['tipo'] = $tipo->toArray();
return $this->withJson($response, $output);
}
public function delete(Request $request, Response $response, Factory $factory, $tipo_id): Response {
$tipo = $factory->find(TipoCuenta::class)->one($tipo_id);
$output = [
'input' => $tipo_id,
'tipo' => $tipo->toArray(),
'eliminado' => $tipo->delete()
];
return $this->withJson($response, $output);
}
}

View File

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

View File

@ -1,21 +0,0 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use ProVM\Common\Define\Controller\Json;
use Contabilidad\Common\Service\WorkerHandler as Handler;
class Workers {
use Json;
public function register(Request $request, Response $response, Handler $handler): Response {
$post = $request->getBody();
$result = $handler->register($post);
$output = [
'input' => $post,
'result' => $result
];
return $this->withJson($response, $output);
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace Contabilidad\Common\Middleware;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Psr\Http\Message\ResponseInterface as Response;
use Contabilidad\Common\Service\Consolidar as ConsolidarService;
class Consolidar {
protected $service;
public function __construct(ConsolidarService $service) {
$this->service = $service;
}
public function __invoke(Request $request, Handler $handler): Response {
if (!$this->service->isConsolidado()) {
$this->service->queue();
}
return $handler->handle($request);
}
}

View File

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

View File

@ -1,37 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Contabilidad\Common\Concept\DocumentHandler;
class CsvHandler extends DocumentHandler {
public function load(): ?array {
$folder = $this->folder;
$files = new \DirectoryIterator($folder);
$output = [];
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'csv') {
continue;
}
$bank = 'unknown';
$text = trim(file_get_contents($file->getRealPath()));
if (str_contains($text, 'SCOTIABANK')) {
$bank = 'Scotiabank';
}
if (str_contains($text, 'BICE')) {
$bank = 'BICE';
}
$data = explode(PHP_EOL, $text);
array_walk($data, function(&$item) {
$item = trim($item, '; ');
if (str_contains($item, ';') !== false) {
$item = explode(';', $item);
}
});
$output []= ['bank' => $bank, 'filename' => $file->getBasename(), 'data' => $data];
}
return $this->build($output);
}
protected function build(array $data): ?array {
return $data;
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
class DocumentHandler {
protected array $handlers;
public function __construct(array $handlers) {
$this->handlers = $handlers;
}
public function handle(): array {
$output = [];
foreach ($this->handlers as $handler) {
$output = array_merge($output, $handler->load());
}
return $output;
}
}

View File

@ -1,112 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Psr\Http\Message\UploadedFileInterface;
use Psr\Http\Message\StreamInterface;
use Nyholm\Psr7\Stream;
class FileHandler {
protected $base_folder;
protected $valid_types;
protected $folders;
public function __construct(object $params) {
$this->base_folder = $params->folder;
$this->addValidTypes(array_keys($params->types));
$this->addFolders($params->types);
}
public function addFolders(array $folders): FileHandler {
foreach ($folders as $type => $folder) {
$this->addFolder($type, $folder);
}
return $this;
}
public function addFolder(string $type, string $folder): FileHandler {
$this->folders[$type] = $folder;
return $this;
}
public function addValidTypes(array $valid_types): FileHandler {
foreach ($valid_types as $type) {
$this->addValidType($type);
}
return $this;
}
public function addValidType(string $type): FileHandler {
$this->valid_types []= $type;
return $this;
}
public function getType(string $folder): string {
return array_search($folder, $this->folders);
}
public function uploadFile(UploadedFileInterface $file, string $new_name = null): bool {
if ($file->getError() !== UPLOAD_ERR_OK) {
return false;
}
if (!in_array($file->getClientMediaType(), $this->valid_types)) {
return false;
}
if ($new_name === null) {
$new_name = $file->getClientFilename();
}
$filenfo = new \SplFileInfo($file->getClientFilename());
if (!str_contains($new_name, $filenfo->getExtension())) {
$new_name .= '.' . $filenfo->getExtension();
}
$to = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $this->folders[$file->getClientMediaType()], $new_name]);
$file->moveTo($to);
return file_exists($to);
}
public function listFiles(): array {
$output = [];
foreach ($this->folders as $f) {
$folder = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $f]);
if (!file_exists($folder)) {
continue;
}
$files = new \DirectoryIterator($folder);
foreach ($files as $file) {
if ($file->isDir()) {
continue;
}
$output []= (object) ['folder' => $f, 'filename' => $file->getBasename()];
}
}
return $output;
}
protected function validateFilename(string $folder, string $filename): bool|string {
if (!in_array($folder, $this->folders)) {
return false;
}
$f = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $folder, $filename]);
if (!file_exists($f)) {
return false;
}
return $f;
}
public function getInfo(string $folder, string $filename): \SplFileInfo|bool {
if (!$f = $this->validateFilename($folder, $filename)) {
return false;
}
return new \SplFileInfo($f);
}
public function getFile(string $folder, string $filename): StreamInterface|bool {
if (!$f = $this->validateFilename($folder, $filename)) {
return false;
}
return Stream::create(file_get_contents($f));
}
public function editFilename(string $folder, string $filename, string $new_name): bool {
if (!$f = $this->validateFilename($folder, $filename)) {
return false;
}
$info = new \SplFileInfo($f);
$new = implode(DIRECTORY_SEPARATOR, [$this->base_folder, $folder, $new_name . '.' . $info->getExtension()]);
return rename($f, $new);
}
public function deleteFile(string $folder, string $filename): bool {
if (!$f = $this->validateFilename($folder, $filename)) {
return false;
}
return unlink($f);
}
}

View File

@ -1,75 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Contabilidad\Common\Concept\DocumentHandler;
use GuzzleHttp\Client;
class PdfHandler extends DocumentHandler {
protected Client $client;
protected string $url;
public function __construct(Client $client, string $pdf_folder, string $url) {
parent::__construct($pdf_folder);
$this->client = $client;
$this->url = $url;
}
public function load(): ?array {
$folder = $this->folder;
$files = new \DirectoryIterator($folder);
$output = [];
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'pdf') {
continue;
}
$output []= ['filename' => $file->getBasename()];
}
$response = $this->client->post($this->url, ['json' => ['files' => $output]]);
$output = json_decode($response->getBody());
return $this->build($output);
}
protected function build(array $data): ?array {
foreach ($data as &$file) {
$i = $this->findStartRow($file->text);
if ($i === -1) {
continue;
}
$e = $this->findEndRow($file->text, $i);
if ($e == $i) {
continue;
}
$file->data = array_filter($file->text, function($key) use ($i, $e) {
return ($key >= $i) and ($key <= $e);
}, ARRAY_FILTER_USE_KEY);
}
return $data;
}
protected function findStartRow(array $data): int {
foreach ($data as $i => $row) {
if (!is_array($row)) {
continue;
}
$maybe = false;
foreach ($row as $cell) {
if (str_contains($cell, '/')) {
$maybe = true;
}
if ($maybe and str_contains($cell, '$')) {
return $i - 1;
}
}
}
return -1;
}
protected function findEndRow(array $data, int $start): int {
$l = count($data[$start]);
for ($i = $start; $i < count($data); $i ++) {
if (!is_array($data[$i])) {
return $i - 1;
}
if (count($data[$i]) != $l) {
return $i - 1;
}
}
return $start;
}
}

View File

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

View File

@ -1,93 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use Carbon\Carbon;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use ProVM\Common\Factory\Model as Factory;
use Contabilidad\Moneda;
use Contabilidad\TipoCambio;
class TiposCambios {
protected $client;
protected $factory;
protected $base_url;
protected $key;
public function __construct(Client $client, Factory $factory, $api_url, $api_key) {
$this->client = $client;
$this->factory = $factory;
$this->base_url = $api_url;
$this->key = $api_key;
}
protected function getWeekday(\DateTimeInterface $fecha) {
if ($fecha->weekday() == 0) {
return $fecha->subWeek()->weekday(5);
}
if ($fecha->weekday() == 6) {
return $fecha->weekday(5);
}
return $fecha;
}
protected function getValor(\DateTimeInterface $fecha, string $moneda_codigo) {
$data = [
'fecha' => $fecha->format('Y-m-d'),
'desde' => $moneda_codigo
];
$headers = [
'Authorization' => "Bearer {$this->key}"
];
$url = implode('/', [
$this->base_url,
'cambio',
'get'
]);
try {
$response = $this->client->request('POST', $url, ['json' => $data, 'headers' => $headers]);
} catch (ConnectException | RequestException | ServerException $e) {
error_log($e);
return null;
}
if ($response->getStatusCode() !== 200) {
error_log('Could not connect to python API.');
return null;
}
$result = json_decode($response->getBody());
if (isset($result->message) and $result->message === 'Not Authorized') {
error_log('Not authorized for connecting to python API.');
return null;
}
return $result->serie[0]->valor;
}
public function get(string $fecha, int $moneda_id) {
$fecha = Carbon::parse($fecha);
$moneda = $this->factory->find(Moneda::class)->one($moneda_id);
if ($moneda->codigo == 'USD') {
$fecha = $this->getWeekday($fecha);
}
// If a value exists in the database
$cambio = $moneda->cambio($fecha);
if ($cambio !== null) {
if ($cambio->desde()->id != $moneda->id) {
return 1 / $cambio->valor;
}
return $cambio->valor;
}
$valor = $this->getValor($fecha, $moneda->codigo);
if ($valor === null) {
return 1;
}
$data = [
'fecha' => $fecha->format('Y-m-d H:i:s'),
'desde_id' => $moneda->id,
'hasta_id' => 1,
'valor' => $valor
];
$tipo = TipoCambio::add($this->factory, $data);
if ($tipo !== false and $tipo->is_new()) {
$tipo->save();
}
return $valor;
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace Contabilidad\Common\Service;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use thiagoalessio\TesseractOCR\TesseractOCR;
use Contabilidad\Common\Concept\DocumentHandler;
class XlsHandler extends DocumentHandler {
public function load(): ?array {
$folder = $this->folder;
$files = new \DirectoryIterator($folder);
$output = [];
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'xls') {
continue;
}
$reader = IOFactory::createReader(ucfirst($file->getExtension()));
$xls = $reader->load($file->getRealPath());
$data = [];
$bank = 'unknown';
for ($s = 0; $s < $xls->getSheetCount(); $s ++) {
$sheet = $xls->getSheet($s);
foreach ($sheet->getRowIterator() as $row) {
$r = [];
foreach ($row->getCellIterator() as $cell) {
$r []= $cell->getValue();
}
$data []= $r;
}
foreach ($sheet->getDrawingCollection() as $drawing) {
if ($drawing instanceof MemoryDrawing) {
ob_start();
call_user_func(
$drawing->getRenderingFunction(),
$drawing->getImageResource()
);
$imageContents = ob_get_contents();
$size = ob_get_length();
ob_end_clean();
$ocr = new TesseractOCR();
$ocr->imageData($imageContents, $size);
$image = $ocr->run();
if (str_contains($image, 'BICE')) {
$bank = 'BICE';
}
if (str_contains($image, 'Scotiabank')) {
$bank = 'Scotiabank';
}
}
}
}
$output []= ['bank' => $bank, 'filename' => $file->getBasename(), 'data' => $data];
}
return $this->build($output);
}
protected function build(array $data): ?array {
return $data;
}
}

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,11 @@
],
"autoload": {
"psr-4": {
"Contabilidad\\Common\\": "common",
"Contabilidad\\": "src"
"Common\\": "common/",
"Contabilidad\\": "src/",
"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

@ -1,7 +1,6 @@
<?php
use DI\ContainerBuilder as Builder;
use DI\Bridge\Slim\Bridge as Bridge;
use Zeuxisoo\Whoops\Slim\WhoopsMiddleware;
include_once 'composer.php';
@ -24,12 +23,7 @@ foreach ($folders as $f) {
}
}
$container = $builder->build();
$app = Bridge::create($container);
$app->addRoutingMiddleware();
$app->add(new WhoopsMiddleware());
$app = Bridge::create($builder->build());
$folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'middlewares']);
if (file_exists($folder)) {
@ -42,7 +36,4 @@ if (file_exists($folder)) {
}
}
include_once 'databases.php';
include_once 'router.php';
return $app;

View File

@ -1,37 +0,0 @@
<?php
$databases = $app->getContainer()->get('databases');
foreach ($databases->databases as $name => $settings) {
if (!is_object($settings)) {
continue;
}
$auth = false;
$dsn = '';
switch (strtolower($settings->type)) {
case 'mysql':
$data = [
['host', $settings->host->name],
['dbname', $settings->name]
];
if (isset($settings->host->port)) {
$data []= ['port', $settings->host->port];
}
array_walk($data, function(&$item) {
$item = implode('=', $item);
});
$dsn = implode(':', [
'mysql',
implode(';', $data)
]);
$auth = true;
break;
}
ORM::configure($dsn, null, $name);
if ($auth) {
ORM::configure('username', $settings->user->name, $name);
ORM::configure('password', $settings->user->password, $name);
}
}
if (isset($databases->short_names) and $databases->short_names) {
Model::$short_table_names = true;
}

View File

@ -1,4 +1,2 @@
<?php
use Contabilidad\Common\Middleware\Auth;
$app->add($app->getContainer()->get(Auth::class));
$app->add($app->getContainer()->get(Contabilidad\Common\Middleware\Auth::class));

View File

@ -0,0 +1,11 @@
<?php
$app->addRoutingMiddleware();
$folder = $app->getContainer()->get('folders')->routes;
$files = new DirectoryIterator($folder);
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'php') {
continue;
}
include_once $file->getRealPath();
}

View File

@ -1,4 +0,0 @@
<?php
use Contabilidad\Common\Middleware\Consolidar;
$app->add(new Consolidar($app->getContainer()->get(\Contabilidad\Common\Service\Consolidar::class)));

View File

@ -0,0 +1,2 @@
<?php
$app->add($app->getContainer()->get(Zeuxisoo\Whoops\Slim\WhoopsMiddleware::class));

View File

@ -1,9 +0,0 @@
<?php
$folder = $app->getContainer()->get('folders')->routes;
$files = new DirectoryIterator($folder);
foreach ($files as $file) {
if ($file->isDir() or $file->getExtension() != 'php') {
continue;
}
include_once $file->getRealPath();
}

View File

@ -18,22 +18,6 @@ return [
$arr['base'],
'public'
]);
$arr['uploads'] = implode(DIRECTORY_SEPARATOR, [
$arr['base'],
'uploads'
]);
$arr['pdfs'] = implode(DIRECTORY_SEPARATOR, [
$arr['uploads'],
'pdfs'
]);
$arr['csvs'] = implode(DIRECTORY_SEPARATOR, [
$arr['uploads'],
'csvs'
]);
$arr['xlss'] = implode(DIRECTORY_SEPARATOR, [
$arr['uploads'],
'xlss'
]);
return (object) $arr;
},
'urls' => function(Container $c) {

View File

@ -2,7 +2,7 @@
return [
'databases' => function() {
$arr = [
ORM::DEFAULT_CONNECTION => [
'default' => [
'type' => 'mysql',
'host' => [
'name' => $_ENV['MYSQL_HOST'] ?? 'db'

View File

@ -1,58 +0,0 @@
<?php
use Psr\Container\ContainerInterface as Container;
return [
GuzzleHttp\Client::class => function(Container $c) {
return new GuzzleHttp\Client();
},
Contabilidad\Common\Service\Auth::class => function(Container $c) {
return new Contabilidad\Common\Service\Auth($c->get('api_key'));
},
Contabilidad\Common\Middleware\Auth::class => function(Container $c) {
return new Contabilidad\Common\Middleware\Auth(
$c->get(Nyholm\Psr7\Factory\Psr17Factory::class),
$c->get(Contabilidad\Common\Service\Auth::class)
);
},
Contabilidad\Common\Service\PdfHandler::class => function(Container $c) {
return new Contabilidad\Common\Service\PdfHandler($c->get(GuzzleHttp\Client::class), $c->get('folders')->pdfs, implode('/', [
$c->get('urls')->python,
'pdf',
'parse'
]));
},
Contabilidad\Common\Service\CsvHandler::class => function(Container $c) {
return new Contabilidad\Common\Service\CsvHandler($c->get('folders')->csvs);
},
Contabilidad\Common\Service\XlsHandler::class => function(Container $c) {
return new Contabilidad\Common\Service\XlsHandler($c->get('folders')->xlss);
},
Contabilidad\Common\Service\DocumentHandler::class => function(Container $c) {
$handlers = [
$c->get(Contabilidad\Common\Service\XlsHandler::class),
$c->get(Contabilidad\Common\Service\CsvHandler::class),
$c->get(Contabilidad\Common\Service\PdfHandler::class)
];
return new Contabilidad\Common\Service\DocumentHandler($handlers);
},
Contabilidad\Common\Service\TiposCambios::class => function(Container $c) {
return new Contabilidad\Common\Service\TiposCambios(
$c->get(GuzzleHttp\Client::class),
$c->get(ProVM\Common\Factory\Model::class),
$c->get('python_api'),
$c->get('python_key')
);
},
Contabilidad\Common\Service\FileHandler::class => function(Container $c) {
return new Contabilidad\Common\Service\FileHandler((object) [
'folder' => $c->get('folders')->uploads,
'types' => [
'text/csv' => 'csvs',
'application/pdf' => 'pdfs',
'application/vnd.ms-excel' => 'xlss',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlss',
'application/json' => 'jsons'
]
]);
}
];

View File

@ -0,0 +1,14 @@
<?php
use Psr\Container\ContainerInterface;
return [
\Common\Concept\Database::class => function(ContainerInterface $container) {
$settings = $container->get('databases')->default;
return new \Contabilidad\Implement\Database\MySQL(
$settings->host->name,
$settings->user->name,
$settings->user->password,
$settings->name
);
}
];

View File

@ -1,11 +0,0 @@
<?php
use Psr\Container\ContainerInterface as Container;
return [
\Contabilidad\Common\Service\Queuer::class => function(Container $container) {
return new \Contabilidad\Common\Service\Queuer($container->get(\ProVM\Common\Factory\Model::class));
},
\Contabilidad\Common\Service\Consolidar::class => function(Container $container) {
return new \Contabilidad\Common\Service\Consolidar($container->get(\ProVM\Common\Factory\Model::class), $container->get(\Contabilidad\Common\Service\Queuer::class));
}
];

View File

@ -1,113 +0,0 @@
<?php
namespace Contabilidad;
use Carbon\Carbon;
use ProVM\Common\Alias\Model;
use Contabilidad\Common\Service\TiposCambios as Service;
/**
* @property int $id
* @property string $nombre
* @property TipoCategoria $tipo_id
*/
class Categoria extends Model {
public static $_table = 'categorias';
protected static $fields = ['nombre', 'tipo_id'];
protected $cuentas;
public function cuentas() {
if ($this->cuentas === null) {
$this->cuentas = $this->parentOf(Cuenta::class, [Model::CHILD_KEY => 'categoria_id']);
if ($this->cuentas !== null) {
usort($this->cuentas, function($a, $b) {
return strcmp($a->nombre, $b->nombre);
});
}
}
return $this->cuentas;
}
protected $tipo;
public function tipo() {
if ($this->tipo === null) {
$this->tipo = $this->childOf(TipoCategoria::class, [Model::SELF_KEY => 'tipo_id']);
}
return $this->tipo;
}
public function getCuentasOf($tipo) {
return $this->factory->find(Cuenta::class)
->select([['cuentas', '*']])
->join([
['tipos_cuenta', 'tipos_cuenta.id', 'cuentas.tipo_id']
])
->where([
['tipos_cuenta.descripcion', $tipo],
['cuentas.categoria_id', $this->id]
])
->many();
}
protected $cuentas_of;
public function getCuentas() {
if ($this->cuentas_of === null) {
$tipos = $this->factory->find(TipoCuenta::class)->many();
$cos = [];
foreach ($tipos as $tipo) {
$p = strtolower($tipo->descripcion) . 's';
$cos[$p] = [];
$cuentas = $this->getCuentasOf($tipos->descripcion);
if ($cuentas === null) {
continue;
}
$cos[$p] = $cuentas;
}
$this->cuentas_of = $cos;
}
return $this->cuentas_of;
}
protected $totales;
public function getTotales(Service $service) {
if ($this->totales === null) {
$tipos = $this->factory->find(TipoCuenta::class)->many();
$totals = [];
foreach ($tipos as $tipo) {
$p = strtolower($tipo->descripcion) . 's';
$totals[$p] = 0;
$cuentas = $this->getCuentasOf($tipo->descripcion);
if ($cuentas === null) {
continue;
}
$totals[$p] = array_reduce($cuentas, function($sum, $item) use ($service) {
return $sum + $item->saldo($service, true);
});
}
$this->totales = $totals;
}
return $this->totales;
}
protected $saldo;
public function saldo(Service $service = null) {
if ($this->saldo === null) {
$this->saldo = 0;
if ($this->cuentas() !== null) {
$sum = 0;
$debitos = ['Activo', 'Perdida'];
foreach ($this->cuentas() as $cuenta) {
if (array_search($cuenta->tipo()->descripcion, $debitos) !== false) {
$sum -= $cuenta->saldo($service, true);
continue;
}
$sum += $cuenta->saldo($service, true);
}
$this->saldo = $sum;
}
}
return $this->saldo;
}
public function toArray(): array {
$arr = parent::toArray();
$arr['tipo'] = $this->tipo()->toArray();
return $arr;
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property string $key
*/
class Coneccion extends Model {
public static $_table = 'conecciones';
protected static $fields = ['key'];
protected $estados;
public function estados() {
if ($this->estados === null) {
$this->estados = $this->parentOf(TipoEstadoConeccion::class, [Model::CHILD_KEY => 'coneccion_id']);
}
return $this->estados;
}
}

View File

@ -1,48 +0,0 @@
<?php
namespace Contabilidad;
use DateTimeInterface;
use DateInterval;
use Carbon\Carbon;
use Carbon\CarbonInterval;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property Cuenta $cuenta_id
* @property DateTimeInterface $fecha
* @property DateInterval $periodo
* @property float $saldo
*/
class Consolidado extends Model {
public static $_table = 'consolidados';
protected $cuenta;
public function cuenta() {
if ($this->cuenta === null) {
$this->cuenta = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'cuenta_id']);
}
return $this->cuenta;
}
public function fecha(DateTimeInterface $fecha = null) {
if ($fecha === null) {
return Carbon::parse($this->fecha);
}
if ($this->periodo()->days > 31) {
$this->fecha = $fecha->format('Y-1-1');
} else {
$this->fecha = $fecha->format('Y-m-1');
}
return $this;
}
public function periodo(DateInterval $periodo = null) {
if ($periodo === null) {
return new CarbonInterval($this->periodo);
}
$this->periodo = CarbonInterval::getDateIntervalSpec($periodo);
return $this;
}
public function saldo() {
return $this->saldo;
}
}

View File

@ -1,195 +0,0 @@
<?php
namespace Contabilidad;
use Carbon\Carbon;
use ProVM\Common\Alias\Model;
use Contabilidad\Common\Service\TiposCambios as Service;
/**
* @property int $id
* @property string $nombre
* @property Categoria $categoria_id
* @property TipoCuenta $tipo_id
* @property Moneda $moneda_id
*/
class Cuenta extends Model {
public static $_table = 'cuentas';
protected static $fields = ['nombre', 'categoria_id', 'tipo_id', 'moneda_id'];
protected $categoria;
public function categoria() {
if ($this->categoria === null) {
$this->categoria = $this->childOf(Categoria::class, [Model::SELF_KEY => 'categoria_id']);
}
return $this->categoria;
}
protected $tipo;
public function tipo() {
if ($this->tipo === null) {
$this->tipo = $this->childOf(TipoCuenta::class, [Model::SELF_KEY => 'tipo_id']);
}
return $this->tipo;
}
protected $moneda;
public function moneda() {
if ($this->moneda === null) {
$this->moneda = $this->childOf(Moneda::class, [Model::SELF_KEY => 'moneda_id']);
}
return $this->moneda;
}
protected $cargos;
public function cargos() {
if ($this->cargos === null) {
$this->cargos = $this->parentOf(Transaccion::class, [Model::CHILD_KEY => 'credito_id']);
}
return $this->cargos;
}
protected $abonos;
public function abonos() {
if ($this->abonos === null) {
$this->abonos = $this->parentOf(Transaccion::class, [Model::CHILD_KEY => 'debito_id']);
}
return $this->abonos;
}
protected $consolidados;
public function consolidados() {
if ($this->consolidados === null) {
$this->consolidados = $this->parentOf(Consolidado::class, [Model::CHILD_KEY => 'cuenta_id']);
}
return $this->consolidados;
}
public function hasConsolidados(\DateTimeInterface $mes = null): bool {
$t = Carbon::now();
$q = Model::factory(Consolidado::class)
->whereEqual('cuenta_id', $this->id);
if ($mes !== null) {
//$q = $q->whereEqual('fecha', $mes->format('Y-m-1'));
$q = $q->whereRaw("fecha BETWEEN '{$mes->format('Y-m-1')}' AND '{$mes->format('Y-m-t')}'");
}
$q = $q->count('id');
return (bool) $q;
}
public function hasConsolidadosPending(\DateTimeInterface $mes = null): bool {
$t = Carbon::now();
$q = Model::factory(Consolidado::class)
->whereEqual('cuenta_id', $this->id)
->whereGte('fecha', $t->copy()->subMonthNoOverflow()->startOfMonth()->format('Y-m-d'));
if ($mes !== null) {
$q = $q->whereRaw("fecha BETWEEN '{$mes->format('Y-m-1')}' AND '{$mes->format('Y-m-t')}'");
}
return !(bool) $q
->orderByDesc('fecha')
->count('id');
}
public function hasTransacciones(\DateTimeInterface $mes = null): bool {
$q = Model::factory(Transaccion::class)
->select('transacciones.*')
->join('cuentas', 'cuentas.id = transacciones.debito_id OR cuentas.id = transacciones.credito_id')
->whereEqual('cuentas.id', $this->id);
if ($mes !== null) {
$q = $q->whereRaw("fecha BETWEEN '{$mes->format('Y-m-1')}' AND '{$mes->format('Y-m-t')}'");
}
return (bool) $q->count('transacciones.id');
}
protected $transacciones;
protected function parseTransaccion(Transaccion $transaccion) {
$transaccion->setFactory($this->factory);
if ($this->tipo()->cargo()) {
if ($transaccion->credito_id == $this->id) {
$transaccion->valor = -$transaccion->valor;
}
} else {
if ($transaccion->debito_id == $this->id) {
$transaccion->valor = -$transaccion->valor;
}
}
$transaccion->valor = $transaccion->transformar($this->moneda());
return $transaccion;
}
public function transacciones($limit = null, $start = 0) {
$transacciones = Model::factory(Transaccion::class)
->select('transacciones.*')
->join('cuentas', 'cuentas.id = transacciones.debito_id OR cuentas.id = transacciones.credito_id')
->whereEqual('cuentas.id', $this->id)
->orderByAsc('transacciones.fecha');
if ($limit !== null) {
$transacciones = $transacciones->limit($limit)
->offset($start);
}
$transacciones = $transacciones->findMany();
foreach ($transacciones as &$transaccion) {
$transaccion = $this->parseTransaccion($transaccion);
}
return $transacciones;
}
public function transaccionesMonth(Carbon $month) {
$start = $month->copy()->startOfMonth();
$end = $month->copy()->endOfMonth();
$transacciones = Model::factory(Transaccion::class)
->select('transacciones.*')
->join('cuentas', 'cuentas.id = transacciones.debito_id OR cuentas.id = transacciones.credito_id')
->whereEqual('cuentas.id', $this->id)
->whereRaw("transacciones.fecha BETWEEN '{$start->format('Y-m-d')}' AND '{$end->format('Y-m-d')}'")
->orderByAsc('transacciones.fecha');
$transacciones = $transacciones->findMany();
foreach ($transacciones as &$transaccion) {
$transaccion = $this->parseTransaccion($transaccion);
}
return $transacciones;
}
public function acumulacion(Carbon $date) {
$consolidados = $this->consolidados();
if ($consolidados === null) {
return 0;
}
$saldo = 0;
foreach ($consolidados as $consolidado) {
if ($consolidado->fecha() >= $date) {
continue;
}
$saldo += $consolidado->saldo();
}
return $saldo;
}
protected $saldo;
public function saldo(Service $service = null, $in_clp = false) {
if ($this->saldo === null) {
$this->saldo = 0;
if (count($this->transacciones()) > 0) {
$this->saldo = array_reduce($this->transacciones(), function($sum, $item) {
return $sum + $item->valor;
});
}
}
if ($in_clp and $this->moneda()->codigo !== 'CLP') {
$fecha = Carbon::today();
if ($this->moneda()->codigo == 'USD') {
$fecha = match ($fecha->weekday()) {
0 => $fecha->subWeek()->weekday(5),
6 => $fecha->weekday(5),
default => $fecha
};
}
$service->get($fecha->format('Y-m-d'), $this->moneda()->id);
return $this->moneda()->cambiar($fecha, $this->saldo);
}
return $this->saldo;
}
public function format($valor) {
return $this->moneda()->format($valor);
}
public function toArray(Service $service = null, $in_clp = false): array {
$arr = parent::toArray();
$arr['tipo'] = $this->tipo()->toArray();
$arr['moneda'] = $this->moneda()->toArray();
$arr['saldo'] = $this->saldo($service, $in_clp);
$arr['saldoFormateado'] = $this->format($this->saldo($service, $in_clp));
return $arr;
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property Coneccion $coneccion_id
* @property DateTime $fecha
* @property TipoEstadoConeccion $tipo_id
*/
class EstadoConeccion extends Model {
public static $_table = 'estados_coneccion';
protected static $fields = ['coneccion_id', 'fecha', 'tipo_id'];
protected $coneccion;
public function coneccion() {
if ($this->coneccion === null) {
$this->coneccion = $this->childOf(Coneccion::class, [Model::SELF_KEY => 'coneccion_id']);
}
return $this->coneccion;
}
protected $tipo;
public function tipo() {
if ($this->tipo === null) {
$this->tipo = $this->childOf(TipoEstadoConeccion::class, [Model::SELF_KEY => 'tipo_id']);
}
return $this->tipo;
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Contabilidad\Implement\Database;
use Common\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,17 @@
<?php
namespace Contabilidad\Implement;
use Common\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 Contabilidad\Implement;
use Common\Concept\File as FileInterface;
use Common\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));
}
}

18
api/src/Model/Cuenta.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace Contabilidad\Model;
use Common\Alias\Model;
class Cuenta extends Model
{
protected int $id;
public function setId(int $id): Cuenta
{
$this->id = $id;
return $this;
}
public function getId(): int
{
return $this->id;
}
}

View File

@ -1,59 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property string $denominacion
* @property string $codigo
* @property string $sufijo
* @property string $prefijo
* @property int $decimales
*/
class Moneda extends Model {
public static $_table = 'monedas';
protected static $fields = ['denominacion', 'codigo'];
public function format($valor) {
return trim(implode('', [
$this->prefijo,
number_format($valor, $this->decimales, ',', '.'),
$this->sufijo
]));
}
public function cambio(\DateTime $fecha, Moneda $moneda = null) {
if ($moneda === null) {
$moneda = $this->factory->find(Moneda::class)->one(1);
}
$cambio = $this->factory->find(TipoCambio::class)
->where([['desde_id', $this->id], ['hasta_id', $moneda->id], ['fecha', $fecha->format('Y-m-d H:i:s')]])
->one();
if ($cambio === null) {
$cambio = $this->factory->find(TipoCambio::class)
->where([['hasta_id', $this->id], ['desde_id', $moneda->id], ['fecha', $fecha->format('Y-m-d H:i:s')]])
->one();
}
return $cambio;
}
public function cambiar(\DateTime $fecha, float $valor, Moneda $moneda = null) {
$cambio = $this->cambio($fecha, $moneda);
if (!$cambio) {
return $valor;
}
if ($cambio->desde()->id != $this->id) {
return $cambio->transform($valor, TipoCambio::DESDE);
}
return $cambio->transform($valor);
}
public function toArray(): array {
$arr = parent::toArray();
$arr['format'] = [
'prefijo' => $this->prefijo,
'sufijo' => $this->sufijo,
'decimales' => $this->decimales
];
return $arr;
}
}

View File

@ -1,98 +0,0 @@
<?php
namespace Contabilidad;
use DateTimeInterface;
use Carbon\Carbon;
use ProVM\Common\Alias\Model;
use ProVM\Common\Factory\Model as Factory;
/**
* @property int $id
* @property string $command
* @property DateTimeInterface $created
* @property bool $processed
*/
class Queue extends Model {
public static $_table = 'queue';
protected static $fields = ['command', 'created', 'processed'];
public function created(DateTimeInterface $fecha = null) {
if ($fecha === null) {
return Carbon::parse($this->created);
}
$this->created = $fecha->format('Y-m-d H:i:s');
return $this;
}
public function hasArguments(): bool {
return Model::factory(QueueArgument::class)
->whereEqual('queue_id', $this->id)
->groupBy('queue_id')
->count('id') > 0;
}
protected $arguments;
public function arguments() {
if ($this->arguments === null) {
$this->arguments = $this->parentOf(QueueArgument::class, [Model::CHILD_KEY => 'queue_id']);
}
return $this->arguments;
}
public function matchArguments(array $arguments): array {
$args = $this->arguments();
if ($args === null) {
return [];
}
$matched = [];
foreach ($arguments as $argument => $value) {
foreach ($args as $arg) {
if ($arg->argument == $argument and $arg->value == $value) {
$matched []= $arg;
}
}
}
return $matched;
}
public function isProcessed(): bool {
return $this->processed > 0;
}
public function setProcessed(bool $processed): Queue {
$this->processed = $processed ? 1 : 0;
return $this;
}
public static function find(Factory $factory, $data) {
$where = [
'command' => $data['command'],
'processed' => $data['processed'] ?? 0
];
array_walk($where, function(&$item, $key) {
$item = [$key, $item];
});
$where = array_values($where);
return $factory->find(Queue::class)->where($where)->one();
}
public function toArray(): array
{
$arr = parent::toArray();
$cmd = [(string) $this];
$arr['arguments'] = [];
if ($this->hasArguments()) {
$arr['arguments'] = array_map(function($item) use (&$cmd) {
$cmd []= (string) $item;
return $item->toArray();
}, $this->arguments());
}
$arr['cmd'] = implode(' ', $cmd);
return $arr;
}
public function __toString(): string {
$str = "{$this->command}";
$arguments = $this->arguments();
if ($arguments !== null and count($arguments) > 0) {
$arguments = implode(' ', array_map(function($item) {return (string) $item;}, $arguments));
$str .= " {$arguments}";
}
return $str;
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property Queue $queue_id
* @property string $argument
* @property string $value
*/
class QueueArgument extends Model {
public static $_table = 'queue_arguments';
protected $queue;
public function queue() {
if ($this->queue === null) {
$this->queue = $this->childOf(Queue::class, [Model::SELF_KEY => 'queue_id']);
}
return $this->queue;
}
public function __toString(): string {
return "--{$this->argument} '{$this->value}'";
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Contabilidad\Repository;
use Common\Alias\Repository;
use Common\Concept\Model;
use Contabilidad\Model\Cuenta as BaseModel;
class Cuenta extends Repository
{
public function __construct()
{
$this->setTable('cuentas');
$this->setProperties(['id', 'nombre', 'tipo']);
}
public function load(array $data_row): Model
{
return (new BaseModel())
->setId($data_row['id']);
}
public function fetchByKey($key): Model
{
$query = "SELECT * FROM {$this->getTable()} WHERE `id` = ?";
return $this->load($this->getDatabase()->prepare($query)->execute($key)[0]);
}
public function delete(Model $model): void
{
$query = "DELETE FROM {$this->getTable()} WHERE `id` = ?";
$this->getDatabase()->prepare($query)->delete([$model->getId()]);
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace Contabilidad;
use Carbon\Carbon;
use DateTime;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property DateTime $fecha
* @property Moneda $desde_id
* @property Moneda $hasta_id
* @property float $valor
*/
class TipoCambio extends Model {
const DESDE = -1;
const HASTA = 1;
public static $_table = 'tipos_cambio';
protected static $fields = ['fecha', 'valor', 'desde_id', 'hasta_id'];
protected $desde;
public function desde() {
if ($this->desde === null) {
$this->desde = $this->childOf(Moneda::class, [Model::SELF_KEY => 'desde_id']);
}
return $this->desde;
}
protected $hasta;
public function hasta() {
if ($this->hasta === null) {
$this->hasta = $this->childOf(Moneda::class, [Model::SELF_KEY => 'hasta_id']);
}
return $this->hasta;
}
public function fecha(DateTime $fecha = null) {
if ($fecha === null) {
return Carbon::parse($this->fecha);
}
$this->fecha = $fecha->format('Y-m-d H:i:s');
return $this;
}
public function transform(float $valor, int $direction = TipoCambio::HASTA): float {
if ($direction == TipoCambio::HASTA) {
return $valor * $this->valor;
}
return $valor / $this->valor;
}
}

View File

@ -1,66 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
use Contabilidad\Common\Service\TiposCambios as Service;
/**
* @property int $id
* @property string $descripcion
* @property int $activo
*/
class TipoCategoria extends Model {
public static $_table = 'tipos_categoria';
protected static $fields = ['descripcion', 'activo'];
protected $categorias;
public function categorias() {
if ($this->categorias === null) {
$this->categorias = $this->parentOf(Categoria::class, [Model::CHILD_KEY => 'tipo_id']);
}
return $this->categorias;
}
public function getCuentasOf($tipo) {
return $this->factory->find(Cuenta::class)
->select('cuentas.*')
->join([
['tipos_cuenta', 'tipos_cuenta.id', 'cuentas.tipo_id'],
['categorias', 'categorias.id', 'cuentas.categoria_id']
])
->where([
['tipos_cuenta.descripcion', $tipo],
['categorias.tipo_id', $this->id]
])->many();
}
protected $totales;
public function getTotales(Service $service) {
if ($this->totales === null) {
$tipos = $this->factory->find(TipoCuenta::class)->many();
$totals = [];
foreach ($tipos as $tipo) {
$p = strtolower($tipo->descripcion) . 's';
$totals[$p] = 0;
$cuentas = $this->getCuentasOf($tipo->descripcion);
if ($cuentas === null) {
continue;
}
$totals[$p] = array_reduce($cuentas, function($sum, $item) use ($service) {
return $sum + $item->saldo($service, true);
});
}
$this->totales = $totals;
}
return $this->totales;
}
protected $saldo;
public function saldo(Service $service = null) {
if ($this->saldo === null) {
$this->saldo = array_reduce($this->categorias() ?? [], function($sum, $item) use ($service) {
return $sum + $item->saldo($service);
});
}
return $this->saldo;
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property string $descripcion
* @property string $color
*/
class TipoCuenta extends Model {
public static $_table = 'tipos_cuenta';
protected static $fields = ['descripcion', 'color'];
public function cargo()
{
$tipos = [
'activo',
'perdida',
'banco'
];
if (in_array(strtolower($this->descripcion), $tipos)) {
return false;
}
return true;
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace Contabilidad;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property string $descripcion
*/
class TipoEstadoConeccion extends Model {
public static $_table = 'tipos_estado_coneccion';
protected static $fields = ['descripcion'];
}

View File

@ -1,66 +0,0 @@
<?php
namespace Contabilidad;
use DateTime;
use Carbon\Carbon;
use ProVM\Common\Alias\Model;
/**
* @property int $id
* @property Cuenta $debito_id
* @property Cuenta $credito_id
* @property DateTime $fecha
* @property string $glosa
* @property string $detalle
* @property double $valor
* @property Moneda $moneda_id
*/
class Transaccion extends Model {
public static $_table = 'transacciones';
protected static $fields = ['debito_id', 'credito_id', 'fecha', 'glosa', 'detalle', 'valor', 'moneda_id'];
protected $debito;
public function debito() {
if ($this->debito === null) {
$this->debito = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'debito_id']);
}
return $this->debito;
}
protected $credito;
public function credito() {
if ($this->credito === null) {
$this->credito = $this->childOf(Cuenta::class, [Model::SELF_KEY => 'credito_id']);
}
return $this->credito;
}
public function fecha(DateTime $fecha = null) {
if ($fecha === null) {
return Carbon::parse($this->fecha);
}
$this->fecha = $fecha->format('Y-m-d');
return $this;
}
protected $moneda;
public function moneda() {
if ($this->moneda === null) {
$this->moneda = $this->childOf(Moneda::class, [Model::SELF_KEY => 'moneda_id']);
}
return $this->moneda;
}
public function transformar(Moneda $moneda = null) {
if (($moneda !== null and $this->moneda()->id === $moneda->id) or ($moneda === null and $this->moneda()->id === 1)) {
return $this->valor;
}
return $this->moneda()->cambiar($this->fecha(), $this->valor, $moneda);
}
public function toArray(): array {
$arr = parent::toArray();
$arr['debito'] = $this->debito()->toArray();
$arr['credito'] = $this->credito()->toArray();
$arr['fechaFormateada'] = $this->fecha()->format('d-m-Y');
$arr['valorFormateado'] = $this->debito()->moneda()->format($this->valor);
return $arr;
}
}