Merge remote-tracking branch 'origin/feature/cierres' into feature/cierres

This commit is contained in:
Juan Pablo Vial
2025-07-12 09:43:19 -04:00
11 changed files with 262 additions and 13 deletions

View File

@ -0,0 +1,10 @@
<?php
namespace Incoviba\Common\Ideal\Service;
use Incoviba\Common\Define;
use Incoviba\Common\Ideal;
abstract class Repository extends Ideal\Service
{
abstract public function getRepository(): Define\Repository;
}

View File

@ -64,10 +64,10 @@ class Select extends Ideal\Query implements Define\Query\Select
public function having(array|string $conditions): Select public function having(array|string $conditions): Select
{ {
if (is_string($conditions)) { if (is_string($conditions)) {
return $this->addCondition($conditions); return $this->addHaving($conditions);
} }
foreach ($conditions as $condition) { foreach ($conditions as $condition) {
$this->addCondition($condition); $this->addHaving($condition);
} }
return $this; return $this;
} }

View File

@ -20,9 +20,9 @@ final class CreateTokuAccounts extends AbstractMigration
public function change(): void public function change(): void
{ {
$this->table('toku_accounts') $this->table('toku_accounts')
->addColumn('toku_id', 'integer')
->addColumn('sociedad_rut', 'integer', ['limit' => 8, 'signed' => false, 'null' => false]) ->addColumn('sociedad_rut', 'integer', ['limit' => 8, 'signed' => false, 'null' => false])
->addColumn('account_key', 'string', ['length' => 255]) ->addColumn('toku_id', 'string', ['length' => 255, 'null' => false])
->addColumn('account_key', 'string', ['length' => 255, 'null' => false])
->addColumn('enabled', 'boolean', ['default' => true]) ->addColumn('enabled', 'boolean', ['default' => true])
->addIndex(['toku_id'], ['unique' => true]) ->addIndex(['toku_id'], ['unique' => true])
->addForeignKey('sociedad_rut', 'inmobiliaria', 'rut', ['delete' => 'CASCADE', 'update' => 'CASCADE']) ->addForeignKey('sociedad_rut', 'inmobiliaria', 'rut', ['delete' => 'CASCADE', 'update' => 'CASCADE'])

View File

@ -7,4 +7,5 @@ $app->group('/toku', function($app) {
$app->get('/test[/]', [Toku::class, 'test']); $app->get('/test[/]', [Toku::class, 'test']);
$app->delete('/reset[/]', [Toku::class, 'reset']); $app->delete('/reset[/]', [Toku::class, 'reset']);
$app->post('/enqueue[/]', [Toku::class, 'enqueue']); $app->post('/enqueue[/]', [Toku::class, 'enqueue']);
$app->post('/update[/{type}[/]]', [Toku::class, 'update']);
}); });

View File

@ -146,4 +146,25 @@ class Toku extends Controller
} }
return $this->withJson($response, $output); return $this->withJson($response, $output);
} }
public function update(ServerRequestInterface $request, ResponseInterface $response,
Service\Venta\MediosPago\Toku $tokuService, ?string $type = null): ResponseInterface
{
$body = $request->getBody()->getContents();
$input = json_decode($body, true);
$output = [
'type' => $type,
'input' => $input,
'output' => [],
'success' => false
];
try {
$output['output'] = $tokuService->update($input, $type);
$output['success'] = true;
} catch (Exception $exception) {
$this->logger->error($exception);
}
return $this->withJson($response, $output);
}
} }

View File

@ -1,10 +1,10 @@
<?php <?php
namespace Incoviba\Service; namespace Incoviba\Service;
use Exception;
use DateTimeImmutable; use DateTimeImmutable;
use DateMalformedStringException; use DateMalformedStringException;
use Incoviba\Exception\ServiceAction\{Create, Read, Update}; use Incoviba\Exception\ServiceAction\{Create, Read, Update};
use Incoviba\Common\Define;
use PDOException; use PDOException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal\Service; use Incoviba\Common\Ideal\Service;
@ -12,7 +12,7 @@ use Incoviba\Common\Implement;
use Incoviba\Repository; use Incoviba\Repository;
use Incoviba\Model; use Incoviba\Model;
class Venta extends Service class Venta extends Service\Repository
{ {
public function __construct( public function __construct(
LoggerInterface $logger, LoggerInterface $logger,
@ -189,6 +189,11 @@ class Venta extends Service
} }
} }
public function getRepository(): Define\Repository
{
return $this->ventaRepository;
}
protected function process(Model\Venta $venta): Model\Venta protected function process(Model\Venta $venta): Model\Venta
{ {
if ($venta->uf === 0.0) { if ($venta->uf === 0.0) {

View File

@ -302,6 +302,40 @@ class Toku extends Ideal\Service
return $queues; return $queues;
} }
public function update(array $ids, ?string $type = null): array
{
if ($type === null) {
$types = [
'customers',
'subscriptions',
'invoices'
];
$results = [];
foreach ($types as $type) {
$results[$type] = $this->update($ids[$type], $type);
}
return $results;
}
$results = [];
switch ($type) {
case 'subscriptions':
try {
$results['subscription'] = $this->subscription->update($ids);
} catch (EmptyResult | EmptyResponse $exception) {
$this->logger->error($exception);
}
break;
case 'invoices':
try {
$results['invoice'] = $this->invoice->updateAll($ids);
} catch (EmptyResult $exception) {
$this->logger->error($exception);
}
break;
}
return $results;
}
/** /**
* @param ServerRequestInterface $request * @param ServerRequestInterface $request
* @param array $tokenConfig * @param array $tokenConfig

View File

@ -4,6 +4,7 @@ namespace Incoviba\Service\Venta\MediosPago\Toku;
use DateMalformedStringException; use DateMalformedStringException;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use PDO;
use PDOException; use PDOException;
use Psr\Http\Client\ClientInterface; use Psr\Http\Client\ClientInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -199,6 +200,41 @@ class Invoice extends AbstractEndPoint
return $this->pagoService->depositar($invoice->cuota->pago, $date); return $this->pagoService->depositar($invoice->cuota->pago, $date);
} }
/**
* @param array $idsData
* @return array
* @throws EmptyResult
*/
public function updateAll(array $idsData): array
{
$tokuIds = array_column($idsData, 'toku_id');
$oldIds = array_column($idsData, 'product_id');
$placeholders = array_map(fn($id) => "id{$id}", array_keys($oldIds));
$placeholdersString = implode(', ', array_map(fn($id) => ":{$id}", $placeholders));
$query = $this->pagoService->getRepository()->getConnection()->getQueryBuilder()
->select('pago.id, CONCAT_WS("-", unidad.descripcion, CONCAT_WS("-", propietario.rut, propietario.dv)) AS old_pid')
->from('pago')
->joined('JOIN cuota ON cuota.pago = pago.id')
->joined('JOIN venta ON venta.pie = cuota.pie')
->joined('JOIN propietario ON propietario.rut = venta.propietario')
->joined('JOIN propiedad_unidad pu ON pu.propiedad = venta.propiedad')
->joined('JOIN unidad ON pu.unidad = unidad.id')
->having("old_pid IN ({$placeholdersString})");
$values = array_combine($placeholders, $oldIds);
try {
$statement = $this->pagoService->getRepository()->getConnection()->execute($query, $values);
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $exception) {
$this->logger->error($exception);
throw new EmptyResult($query, $exception);
}
$ids = array_column($results, 'pago.id');
$newIds = array_combine($ids, $tokuIds);
return array_map(fn($id) => ['product_id' => $id, 'toku_id' => $newIds[$id]], $ids);
}
public function save(array $data): bool public function save(array $data): bool
{ {
return $this->doSave($this->invoiceRepository, $data); return $this->doSave($this->invoiceRepository, $data);
@ -215,7 +251,7 @@ class Invoice extends AbstractEndPoint
{ {
$paramsMap = [ $paramsMap = [
'customer' => 'customer', 'customer' => 'customer',
'product_id' => 'product_id', 'product_id' => 'cuota_id',
'due_date' => 'fecha', 'due_date' => 'fecha',
'subscription' => 'subscription', 'subscription' => 'subscription',
'amount' => 'valor', 'amount' => 'valor',

View File

@ -1,6 +1,7 @@
<?php <?php
namespace Incoviba\Service\Venta\MediosPago\Toku; namespace Incoviba\Service\Venta\MediosPago\Toku;
use PDO;
use PDOException; use PDOException;
use Psr\Http\Client\ClientInterface; use Psr\Http\Client\ClientInterface;
use Incoviba\Common\Implement\Exception\EmptyResponse; use Incoviba\Common\Implement\Exception\EmptyResponse;
@ -111,6 +112,101 @@ class Subscription extends AbstractEndPoint
} }
} }
/**
* @param array $idsData
* @return array
* @throws EmptyResult
* @throws EmptyResponse
*/
public function update(array $idsData): array
{
$tokuIds = array_column($idsData, 'toku_id');
$oldPids = array_column($idsData, 'product_id');
$placeholders = array_map(fn($id) => "id{$id}", array_keys($oldPids));
$placeholdersString = implode(', ', array_map(fn($id) => ":{$id}", $placeholders));
$query = $this->ventaService->getRepository()->getConnection()->getQueryBuilder()
->select('venta.id, CONCAT_WS("-", unidad.descripcion, CONCAT_WS("-", propietario.rut, propietario.dv)) AS old_pid')
->from('venta')
->joined('JOIN propietario ON propietario.rut = venta.propietario')
->joined('JOIN propiedad_unidad pu ON pu.propiedad = venta.propiedad')
->joined('JOIN unidad ON pu.unidad = unidad.id')
->having("old_pid IN ({$placeholdersString})");
$values = array_combine($placeholders, $oldPids);
try {
$statement = $this->ventaService->getRepository()->getConnection()->execute($query, $values);
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $exception) {
$this->logger->error($exception->getMessage(), [
'query' => $query,
'values' => $values,
'ids' => $idsData,
'exception' => $exception]);
throw new EmptyResult($query, $exception);
}
$accountKeys = $this->getAccountKey(array_column($results, 'id'));
$newPids = [];
$keys = [];
foreach ($results as $result) {
$idx = array_search($result['old_pid'], $oldPids);
$newPids[$idx] = $result['id'];
if (array_key_exists($result['id'], $accountKeys)) {
$keys[$idx] = $accountKeys[$result['id']];
}
}
$output = [];
foreach ($tokuIds as $idx => $tokuId) {
if (!isset($newPids[$idx])) {
continue;
}
$data = [
'product_id' => $newPids[$idx],
];
try {
if (!$this->edit($tokuId, $data, array_key_exists($idx, $keys) ? $keys[$idx] : null)) {
$this->logger->error('Error while updating Toku', [
'toku_id' => $tokuId,
'old_pid' => $oldPids[$idx],
'product_id' => $newPids[$idx],
'account_key' => array_key_exists($idx, $keys) ? $keys[$idx] : null]);
$output[] = [
'toku_id' => $tokuId,
'old_pid' => $oldPids[$idx],
'product_id' => $newPids[$idx],
'account_key' => array_key_exists($idx, $keys) ? $keys[$idx] : null,
'error' => 'Error while updating Toku'
];
continue;
}
} catch (EmptyResponse $exception) {
$this->logger->error($exception->getMessage(), [
'toku_id' => $tokuId,
'old_pid' => $oldPids[$idx],
'product_id' => $newPids[$idx],
'account_key' => array_key_exists($idx, $keys) ? $keys[$idx] : null,
'exception' => $exception]);
$output[] = [
'toku_id' => $tokuId,
'old_pid' => $oldPids[$idx],
'product_id' => $newPids[$idx],
'account_key' => array_key_exists($idx, $keys) ? $keys[$idx] : null,
'error' => $exception->getMessage()
];
continue;
}
$output[] = [
'toku_id' => $tokuId,
'old_pid' => $oldPids[$idx],
'product_id' => $newPids[$idx],
'account_key' => array_key_exists($idx, $keys) ? $keys[$idx] : null
];
}
return $output;
}
public function save(array $data): bool public function save(array $data): bool
{ {
return $this->doSave($this->subscriptionRepsitory, $data); return $this->doSave($this->subscriptionRepsitory, $data);
@ -133,11 +229,11 @@ class Subscription extends AbstractEndPoint
if ($ref === null) { if ($ref === null) {
continue; continue;
} }
if ($ref === 'pieValor') { if ($ref === 'pieValor' and array_key_exists('venta', $data)) {
$params[$key] = $data['venta']->formaPago()?->pie?->valor ?? 0; $params[$key] = $data['venta']?->formaPago()?->pie?->valor ?? 0;
continue; continue;
} }
if ($ref === 'datosVenta') { if ($ref === 'datosVenta' and array_key_exists('venta', $data)) {
$params[$key] = $this->datosVenta($data['venta']); $params[$key] = $this->datosVenta($data['venta']);
continue; continue;
} }
@ -169,4 +265,38 @@ class Subscription extends AbstractEndPoint
'Unidades' => $venta->propiedad()->summary() 'Unidades' => $venta->propiedad()->summary()
]; ];
} }
/**
* @param array $ventaIds
* @return array
* @throws EmptyResult
*/
protected function getAccountKey(array $ventaIds): array
{
$placeholders = array_map(fn($id) => "id{$id}", array_keys($ventaIds));
$placeholdersString = implode(', ', array_map(fn($id) => ":{$id}", $placeholders));
$query = $this->ventaService->getRepository()->getConnection()->getQueryBuilder()
->select('account_key, venta.id AS venta_id')
->from('toku_accounts')
->joined('JOIN proyecto ON proyecto.inmobiliaria = toku_accounts.sociedad_rut')
->joined('JOIN proyecto_tipo_unidad ptu ON ptu.proyecto = proyecto.id')
->joined('JOIN unidad ON unidad.pt = ptu.id')
->joined('JOIN propiedad_unidad pu ON pu.unidad = unidad.id')
->joined('JOIN venta ON venta.propiedad = pu.propiedad')
->where("venta.id IN ({$placeholdersString}) AND toku_accounts.enabled = 1");
$values = array_combine($placeholders, $ventaIds);
try {
$statement = $this->ventaService->getRepository()->getConnection()->execute($query, $values);
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $exception) {
$this->logger->error($exception->getMessage(), [
'query' => $query,
'values' => $values,
'exception' => $exception]);
throw new EmptyResult($query, $exception);
}
$keys = array_column($results, 'account_key');
$ids = array_column($results, 'venta_id');
return array_combine($ids, $keys);
}
} }

View File

@ -4,25 +4,37 @@ namespace Incoviba\Service\Venta;
use DateTimeInterface; use DateTimeInterface;
use DateTimeImmutable; use DateTimeImmutable;
use DateMalformedStringException; use DateMalformedStringException;
use Incoviba\Common\Define;
use Incoviba\Exception\ServiceAction\Create; use Incoviba\Exception\ServiceAction\Create;
use Incoviba\Exception\ServiceAction\Read; use Incoviba\Exception\ServiceAction\Read;
use Incoviba\Exception\ServiceAction\Update; use Incoviba\Exception\ServiceAction\Update;
use PDOException; use PDOException;
use Incoviba\Common\Ideal;
use Incoviba\Common\Implement\Exception\EmptyResult; use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Repository; use Incoviba\Repository;
use Incoviba\Model; use Incoviba\Model;
use Incoviba\Service; use Incoviba\Service;
use Psr\Log\LoggerInterface;
class Pago class Pago extends Ideal\Service\Repository
{ {
public function __construct( public function __construct(
LoggerInterface $logger,
protected Repository\Venta\Pago $pagoRepository, protected Repository\Venta\Pago $pagoRepository,
protected Repository\Venta\EstadoPago $estadoPagoRepository, protected Repository\Venta\EstadoPago $estadoPagoRepository,
protected Repository\Venta\TipoEstadoPago $tipoEstadoPagoRepository, protected Repository\Venta\TipoEstadoPago $tipoEstadoPagoRepository,
protected Service\UF $ufService, protected Service\UF $ufService,
protected Service\Valor $valorService, protected Service\Valor $valorService,
protected Service\Queue $queueService protected Service\Queue $queueService
) {} )
{
parent::__construct($logger);
}
public function getRepository(): Define\Repository
{
return $this->pagoRepository;
}
public function depositar(Model\Venta\Pago $pago, DateTimeInterface $fecha): bool public function depositar(Model\Venta\Pago $pago, DateTimeInterface $fecha): bool
{ {

View File

@ -8,4 +8,4 @@
0 2 * * * /code/bin/incoviba money:uf:update >> /logs/commands 2>&1 0 2 * * * /code/bin/incoviba money:uf:update >> /logs/commands 2>&1
0 2 1 * * /code/bin/incoviba money:ipc >> /logs/commands 2>&1 0 2 1 * * /code/bin/incoviba money:ipc >> /logs/commands 2>&1
*/2 * * * * /code/bin/incoviba queue >> /logs/commands 2>&1 */2 * * * * /code/bin/incoviba queue >> /logs/commands 2>&1
#0 3 * * * /code/bin/incoviba external:services >> /logs/commands 2>&1 0 3 * * * /code/bin/incoviba external:services >> /logs/commands 2>&1