Compare commits
10 Commits
8be085222a
...
186cd0f5b8
Author | SHA1 | Date | |
---|---|---|---|
186cd0f5b8 | |||
167d8e1ab7 | |||
46802507a7 | |||
aaf2ed7612 | |||
3ced9e40b1 | |||
594cb68b09 | |||
af68c4b8ec | |||
ea8f483dd5 | |||
5f53c77a1f | |||
b9adb9108b |
@ -1,11 +1,12 @@
|
||||
FROM php:8.2-fpm
|
||||
FROM php:8.4-fpm
|
||||
|
||||
ENV TZ=America/Santiago
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends libzip-dev libicu-dev git libpng-dev unzip tzdata \
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends libzip-dev libicu-dev git \
|
||||
libpng-dev unzip tzdata libxml2-dev \
|
||||
&& rm -r /var/lib/apt/lists/* \
|
||||
&& docker-php-ext-install pdo pdo_mysql zip intl gd bcmath \
|
||||
&& pecl install xdebug-3.3.2 \
|
||||
&& docker-php-ext-install pdo pdo_mysql zip intl gd bcmath dom \
|
||||
&& pecl install xdebug-3.4.2 \
|
||||
&& docker-php-ext-enable xdebug \
|
||||
&& echo $TZ > /etc/timezone
|
||||
|
||||
|
@ -8,9 +8,9 @@ interface Provider
|
||||
{
|
||||
/**
|
||||
* @param string $money_symbol
|
||||
* @param DateTimeInterface $dateTime
|
||||
* @param ?DateTimeInterface $dateTime = null
|
||||
* @return float
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
public function get(string $money_symbol, DateTimeInterface $dateTime): float;
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float;
|
||||
}
|
||||
|
@ -24,6 +24,11 @@ abstract class Repository implements Define\Repository
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConnection(): Define\Connection
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function load(array $data_row): Define\Model
|
||||
{
|
||||
$model = $this->create($data_row);
|
||||
|
@ -4,6 +4,7 @@
|
||||
"type": "project",
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-pdo": "*",
|
||||
|
@ -22,8 +22,12 @@ return [
|
||||
$ine = new Incoviba\Service\Money\Ine(new GuzzleHttp\Client([
|
||||
'base_uri' => 'https://api-calculadora.ine.cl/ServiciosCalculadoraVariacion'
|
||||
]));
|
||||
$sii = new Incoviba\Service\Money\SII(new GuzzleHttp\Client([
|
||||
'base_uri' => 'https://www.sii.cl/valores_y_fechas/'
|
||||
]), $container->get(Incoviba\Service\Valor::class), $container->get(Incoviba\Repository\UF::class));
|
||||
return (new Incoviba\Service\Money($container->get(Psr\Log\LoggerInterface::class)))
|
||||
->register('uf', $mindicador)
|
||||
->register('uf', $sii)
|
||||
->register('usd', $mindicador)
|
||||
->register('ipc', $ine);
|
||||
},
|
||||
|
@ -2,8 +2,10 @@
|
||||
namespace Incoviba\Repository;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
use Incoviba\Common\Define;
|
||||
use Incoviba\Common\Ideal;
|
||||
use Incoviba\Common\Ideal\Repository;
|
||||
use Incoviba\Common\Implement;
|
||||
use Incoviba\Model;
|
||||
|
||||
@ -13,6 +15,19 @@ class UF extends Ideal\Repository
|
||||
{
|
||||
return 'uf';
|
||||
}
|
||||
protected function getIndex(Define\Model $model): string
|
||||
{
|
||||
return 'fecha';
|
||||
}
|
||||
protected function setIndex(Define\Model &$model, mixed $value): Repository
|
||||
{
|
||||
$model->fecha = new DateTimeImmutable($value);
|
||||
return $this;
|
||||
}
|
||||
protected function getKey(): string
|
||||
{
|
||||
return 'fecha';
|
||||
}
|
||||
|
||||
public function create(?array $data = null): Model\UF
|
||||
{
|
||||
|
@ -5,61 +5,89 @@ use DateTimeInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Incoviba\Common\Define\Money\Provider;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Service\Money\MiIndicador;
|
||||
|
||||
class Money
|
||||
{
|
||||
const UF = 'uf';
|
||||
const USD = 'usd';
|
||||
const IPC = 'ipc';
|
||||
|
||||
public function __construct(protected LoggerInterface $logger) {}
|
||||
|
||||
protected array $providers;
|
||||
public function register(string $name, Provider $provider): Money
|
||||
{
|
||||
if (isset($this->providers) and isset($this->providers[$name]) and $this->providers[$name] === $provider) {
|
||||
if (isset($this->providers) and isset($this->providers[$name]) and in_array($provider, $this->providers[$name])) {
|
||||
return $this;
|
||||
}
|
||||
$this->providers[$name] = $provider;
|
||||
if (!isset($this->providers[$name])) {
|
||||
$this->providers[$name] = [];
|
||||
}
|
||||
$this->providers[$name] []= $provider;
|
||||
return $this;
|
||||
}
|
||||
public function getProvider(string $name): Provider
|
||||
public function getProviders(string $name): array
|
||||
{
|
||||
return $this->providers[$name];
|
||||
}
|
||||
|
||||
public function get(string $provider, DateTimeInterface $dateTime): float
|
||||
{
|
||||
try {
|
||||
return $this->getProvider($provider)->get(MiIndicador::getSymbol($provider), $dateTime);
|
||||
} catch (EmptyResponse) {
|
||||
return 0;
|
||||
$providers = $this->getProviders($provider);
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->get(self::getSymbol($provider), $dateTime);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->notice($exception);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public function getUF(?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
try {
|
||||
return $this->getProvider('uf')->get(MiIndicador::UF, $dateTime);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->debug($exception);
|
||||
return 0;
|
||||
$providers = $this->getProviders('uf');
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->get(self::UF, $dateTime);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->notice($exception);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public function getIPC(DateTimeInterface $start, DateTimeInterface $end): float
|
||||
{
|
||||
if ($start >= $end) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return $this->getProvider('ipc')->getVar($start, $end);
|
||||
} catch (EmptyResponse) {
|
||||
return 0;
|
||||
$providers = $this->getProviders('ipc');
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->getVar($start, $end);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->notice($exception);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public function getUSD(DateTimeInterface $dateTime): float
|
||||
{
|
||||
try {
|
||||
return $this->getProvider('usd')->get(MiIndicador::USD, $dateTime);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->critical($exception);
|
||||
return 0;
|
||||
$providers = $this->getProviders('usd');
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
return $provider->get(self::USD, $dateTime);
|
||||
} catch (EmptyResponse $exception) {
|
||||
$this->logger->notice($exception);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function getSymbol(string $identifier): string
|
||||
{
|
||||
$upper = strtoupper($identifier);
|
||||
$output = '';
|
||||
eval("\$output = self::{$upper};");
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ use Exception;
|
||||
use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
use DateInterval;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Common\Define\Money\Provider;
|
||||
|
||||
@ -19,8 +19,11 @@ class Ine implements Provider
|
||||
* @throws EmptyResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get(string $money_symbol, DateTimeInterface $dateTime): float
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
if ($dateTime === null) {
|
||||
$dateTime = new DateTimeImmutable();
|
||||
}
|
||||
$end = new DateTimeImmutable($dateTime->format('Y-m-1'));
|
||||
$start = $end->sub(new DateInterval('P1M'));
|
||||
return $this->getVar($start, $end);
|
||||
@ -42,9 +45,6 @@ class Ine implements Provider
|
||||
$request_uri = implode('?', [
|
||||
$this->uri,
|
||||
http_build_query($request_query),
|
||||
/*implode('&', array_map(function($val, $key) {
|
||||
return "{$key}={$val}";
|
||||
}, $request_query, array_keys($request_query)))*/
|
||||
]);
|
||||
try {
|
||||
$response = $this->client->get($request_uri);
|
||||
|
@ -9,10 +9,6 @@ use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
|
||||
class MiIndicador implements Provider
|
||||
{
|
||||
const UF = 'uf';
|
||||
const IPC = 'ipc';
|
||||
const USD = 'dolar';
|
||||
|
||||
public function __construct(protected ClientInterface $client) {}
|
||||
|
||||
/**
|
||||
@ -42,11 +38,4 @@ class MiIndicador implements Provider
|
||||
}
|
||||
return $json->serie[0]->valor;
|
||||
}
|
||||
public static function getSymbol(string $identifier): string
|
||||
{
|
||||
$upper = strtoupper($identifier);
|
||||
$output = '';
|
||||
eval("\$output = self::{$upper};");
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
133
app/src/Service/Money/SII.php
Normal file
133
app/src/Service/Money/SII.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
namespace Incoviba\Service\Money;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateTimeImmutable;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Dom\HTMLDocument;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Incoviba\Common\Implement\Exception\EmptyResponse;
|
||||
use Incoviba\Common\Define;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Service;
|
||||
|
||||
class SII implements Define\Money\Provider
|
||||
{
|
||||
public function __construct(protected ClientInterface $client, protected Service\Valor $valorService,
|
||||
protected Repository\UF $ufRepository) {}
|
||||
|
||||
public function get(string $money_symbol, ?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
if ($money_symbol === Service\Money::UF) {
|
||||
return $this->getUF($dateTime);
|
||||
}
|
||||
$class = __CLASS__;
|
||||
throw new EmptyResponse("{$money_symbol} not found in {$class}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTimeInterface|null $dateTime
|
||||
* @return float
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
protected function getUF(?DateTimeInterface $dateTime = null): float
|
||||
{
|
||||
if ($dateTime === null) {
|
||||
$dateTime = new DateTimeImmutable();
|
||||
}
|
||||
$year = $this->getUFYear($dateTime);
|
||||
return $year[$dateTime->format('Y-m-d')];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTimeImmutable|null $dateTime
|
||||
* @return array
|
||||
* @throws EmptyResponse
|
||||
*/
|
||||
protected function getUFYear(?DateTimeImmutable $dateTime = null): array
|
||||
{
|
||||
if ($dateTime === null) {
|
||||
$dateTime = new DateTimeImmutable();
|
||||
}
|
||||
$request_uri = "uf/uf{$dateTime->format('Y')}.htm";
|
||||
try {
|
||||
$response = $this->client->get($request_uri);
|
||||
} catch (GuzzleException) {
|
||||
throw new EmptyResponse($request_uri);
|
||||
}
|
||||
if ((int) floor($response->getStatusCode() / 100) !== 2) {
|
||||
throw new EmptyResponse($request_uri);
|
||||
}
|
||||
$body = $response->getBody();
|
||||
$contents = $body->getContents();
|
||||
if (trim($contents) === '') {
|
||||
throw new EmptyResponse($request_uri);
|
||||
}
|
||||
$domHandler = HTMLDocument::createFromString($contents);
|
||||
$table = $domHandler->querySelector('div#mes_all');
|
||||
$tbody = $table->querySelector('tbody');
|
||||
$trs = $tbody->querySelectorAll('tr');
|
||||
/**
|
||||
* [th Dia, th Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic]
|
||||
* [th 1, td #, #, #, #, #, #, #, #, #, #, #, #]
|
||||
*/
|
||||
$year = [];
|
||||
$trim = mb_chr(160);
|
||||
foreach ($trs as $i => $tr) {
|
||||
$tds = $tr->querySelectorAll('td');
|
||||
foreach ($tds as $j => $td) {
|
||||
$value = $td->textContent;
|
||||
if (trim($value, "{$trim} ") === '') {
|
||||
continue;
|
||||
}
|
||||
$m = str_pad($j + 1, 2, '0', STR_PAD_LEFT);
|
||||
$d = str_pad($i + 1, 2, '0', STR_PAD_LEFT);
|
||||
$date = "{$dateTime->format('Y')}-{$m}-{$d}";
|
||||
$value = $this->valorService->clean($value);
|
||||
$year[$date] = $value;
|
||||
}
|
||||
}
|
||||
$this->saveUFs($year);
|
||||
return $year;
|
||||
}
|
||||
protected function saveUFs(array $ufs): void
|
||||
{
|
||||
$dates = array_keys($ufs);
|
||||
$dateString = "'" . implode("', '", $dates) . "'";
|
||||
|
||||
$query1 = $this->ufRepository->getConnection()->getQueryBuilder()
|
||||
->select('DISTINCT fecha')
|
||||
->from($this->ufRepository->getTable())
|
||||
->where("fecha IN ({$dateString})");
|
||||
try {
|
||||
$statement = $this->ufRepository->getConnection()->query($query1);
|
||||
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($results as $row) {
|
||||
if (isset($ufs[$row['fecha']])) {
|
||||
unset($ufs[$row['fecha']]);
|
||||
}
|
||||
}
|
||||
} catch (PDOException) {
|
||||
return;
|
||||
}
|
||||
$values = [];
|
||||
foreach ($ufs as $fecha => $value) {
|
||||
$values []= [$fecha, $value];
|
||||
}
|
||||
$valueString = implode(', ', array_fill(0, count($values), '(?, ?)'));
|
||||
$query2 = "INSERT INTO {$this->ufRepository->getTable()} (fecha, valor) VALUES {$valueString}";
|
||||
$this->ufRepository->getConnection()->getPDO()->beginTransaction();
|
||||
try {
|
||||
$this->ufRepository->getConnection()->execute($query2, $values);
|
||||
if ($this->ufRepository->getConnection()->getPDO()->inTransaction()) {
|
||||
$this->ufRepository->getConnection()->getPDO()->commit();
|
||||
}
|
||||
} catch (PDOException) {
|
||||
if ($this->ufRepository->getConnection()->getPDO()->inTransaction()) {
|
||||
$this->ufRepository->getConnection()->getPDO()->rollBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ class Venta extends Service
|
||||
protected Venta\BonoPie $bonoPieService,
|
||||
protected Venta\Pago $pagoService,
|
||||
protected Proyecto\Terreno $terrenoService,
|
||||
protected Money $moneyService,
|
||||
protected UF $ufService,
|
||||
protected Valor $valorService
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
@ -177,6 +177,15 @@ class Venta extends Service
|
||||
|
||||
protected function process(Model\Venta $venta): Model\Venta
|
||||
{
|
||||
if ($venta->uf === 0.0) {
|
||||
$uf = $this->ufService->get($venta->fecha);
|
||||
if ($uf > 0.0) {
|
||||
try {
|
||||
$venta = $this->ventaRepository->edit($venta, ['uf' => $uf]);
|
||||
} catch (Implement\Exception\EmptyResult) {}
|
||||
}
|
||||
}
|
||||
|
||||
$venta->addFactory('formaPago', (new Implement\Repository\Factory())
|
||||
->setCallable([$this->formaPagoService, 'getByVenta'])
|
||||
->setArgs(['venta_id' => $venta->id]));
|
||||
@ -186,6 +195,7 @@ class Venta extends Service
|
||||
$venta->addFactory('currentEstado', (new Implement\Repository\Factory())
|
||||
->setCallable([$this->estadoVentaRepository, 'fetchCurrentByVenta'])
|
||||
->setArgs([$venta->id]));
|
||||
|
||||
return $venta;
|
||||
}
|
||||
|
||||
@ -207,7 +217,7 @@ class Venta extends Service
|
||||
public function add(array $data): Model\Venta
|
||||
{
|
||||
$fecha = new DateTimeImmutable($data['fecha_venta']);
|
||||
$data['uf'] = $this->moneyService->getUF($fecha);
|
||||
$data['uf'] = $this->ufService->get($fecha);
|
||||
$propietario = $this->addPropietario($data);
|
||||
$propiedad = $this->addPropiedad($data);
|
||||
$formaPago = $this->addFormaPago($data);
|
||||
@ -445,7 +455,7 @@ class Venta extends Service
|
||||
} catch (DateMalformedStringException) {
|
||||
$fecha = new DateTimeImmutable();
|
||||
}
|
||||
$uf = $this->moneyService->getUF($fecha);
|
||||
$uf = $this->ufService->get($fecha);
|
||||
$valor = $data['valor_pago_ufs'] !== '' ? $this->valorService->clean($data['valor_pago_ufs']) * $uf : $this->valorService->clean($data['valor_pago_pesos']);
|
||||
$pagoData = [
|
||||
'valor' => $valor,
|
||||
@ -471,7 +481,7 @@ class Venta extends Service
|
||||
} catch (DateMalformedStringException) {
|
||||
$fecha = new DateTimeImmutable();
|
||||
}
|
||||
$uf = $this->moneyService->getUF($fecha);
|
||||
$uf = $this->ufService->get($fecha);
|
||||
$subsidioData = [
|
||||
'fecha_venta' => $fecha->format('Y-m-d'),
|
||||
'ahorro' => $this->valorService->clean($data['valor_ahorro']),
|
||||
@ -489,7 +499,7 @@ class Venta extends Service
|
||||
} catch (DateMalformedStringException) {
|
||||
$fecha = new DateTimeImmutable();
|
||||
}
|
||||
$uf = $this->moneyService->getUF($fecha);
|
||||
$uf = $this->ufService->get($fecha);
|
||||
$valor = $this->valorService->clean($data['valor_credito']) * $uf;
|
||||
if ($venta->formaPago()->credito === null) {
|
||||
if ($data['valor_credito'] === 0) {
|
||||
|
62
app/tests/unit/src/Service/Money/SIITest.php
Normal file
62
app/tests/unit/src/Service/Money/SIITest.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace ProVM\Unit\Service\Money;
|
||||
|
||||
use PDO;
|
||||
use PDOStatement;
|
||||
use GuzzleHttp\Client;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Incoviba\Service;
|
||||
use Incoviba\Repository;
|
||||
use Incoviba\Common\Define;
|
||||
|
||||
class SIITest extends TestCase
|
||||
{
|
||||
protected Client $client;
|
||||
protected Service\UF $ufService;
|
||||
protected Service\Valor $valorService;
|
||||
protected \PDO $pdo;
|
||||
protected Define\Connection $connection;
|
||||
protected \PDOStatement $statement;
|
||||
protected Repository\UF $ufRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->client = new Client(['base_uri' => 'https://www.sii.cl/valores_y_fechas/']);
|
||||
|
||||
$this->ufService = $this->getMockBuilder(Service\UF::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->ufService->method('get')->willReturn(1.0);
|
||||
|
||||
$this->valorService = new Service\Valor($this->ufService);
|
||||
|
||||
$this->pdo = $this->getMockBuilder(PDO::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->pdo->method('beginTransaction')->willReturn(true);
|
||||
$this->pdo->method('commit')->willReturn(true);
|
||||
#$this->pdo->method('rollBack')->willReturn(null);
|
||||
|
||||
$this->statement = $this->getMockBuilder(PDOStatement::class)->getMock();
|
||||
$this->statement->method('fetchAll')->willReturn([]);
|
||||
|
||||
$this->connection = $this->getMockBuilder(Define\Connection::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->connection->method('getPDO')->willReturn($this->pdo);
|
||||
$this->connection->method('query')->willReturn($this->statement);
|
||||
$this->connection->method('execute')->willReturn($this->statement);
|
||||
|
||||
$this->ufRepository = $this->getMockBuilder(Repository\UF::class)
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->ufRepository->method('getConnection')->willReturn($this->connection);
|
||||
$this->ufRepository->method('getTable')->willReturn('uf');
|
||||
}
|
||||
|
||||
public function testGet(): void
|
||||
{
|
||||
$provider = new Service\Money\SII($this->client, $this->valorService, $this->ufRepository);
|
||||
|
||||
$date = new \DateTimeImmutable('2025-05-05');
|
||||
$expected = 39107.9;
|
||||
|
||||
$this->assertEquals($expected, $provider->get(Service\Money::UF, $date));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user