diff --git a/app/common/Ideal/Service/Repository.php b/app/common/Ideal/Service/Repository.php new file mode 100644 index 0000000..825afd8 --- /dev/null +++ b/app/common/Ideal/Service/Repository.php @@ -0,0 +1,10 @@ +addCondition($conditions); + return $this->addHaving($conditions); } foreach ($conditions as $condition) { - $this->addCondition($condition); + $this->addHaving($condition); } return $this; } diff --git a/app/resources/database/migrations/20250701192525_create_toku_accounts.php b/app/resources/database/migrations/20250701192525_create_toku_accounts.php index 3226343..2d7270d 100644 --- a/app/resources/database/migrations/20250701192525_create_toku_accounts.php +++ b/app/resources/database/migrations/20250701192525_create_toku_accounts.php @@ -20,9 +20,9 @@ final class CreateTokuAccounts extends AbstractMigration public function change(): void { $this->table('toku_accounts') - ->addColumn('toku_id', 'integer') ->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]) ->addIndex(['toku_id'], ['unique' => true]) ->addForeignKey('sociedad_rut', 'inmobiliaria', 'rut', ['delete' => 'CASCADE', 'update' => 'CASCADE']) diff --git a/app/resources/routes/api/external/toku.php b/app/resources/routes/api/external/toku.php index 8d293e1..6a93cc8 100644 --- a/app/resources/routes/api/external/toku.php +++ b/app/resources/routes/api/external/toku.php @@ -7,4 +7,5 @@ $app->group('/toku', function($app) { $app->get('/test[/]', [Toku::class, 'test']); $app->delete('/reset[/]', [Toku::class, 'reset']); $app->post('/enqueue[/]', [Toku::class, 'enqueue']); + $app->post('/update[/{type}[/]]', [Toku::class, 'update']); }); diff --git a/app/src/Controller/API/Ventas/MediosPago/Toku.php b/app/src/Controller/API/Ventas/MediosPago/Toku.php index d100b6f..fd6bf25 100644 --- a/app/src/Controller/API/Ventas/MediosPago/Toku.php +++ b/app/src/Controller/API/Ventas/MediosPago/Toku.php @@ -146,4 +146,25 @@ class Toku extends Controller } 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); + } } diff --git a/app/src/Service/Venta.php b/app/src/Service/Venta.php index f5d3935..f982431 100644 --- a/app/src/Service/Venta.php +++ b/app/src/Service/Venta.php @@ -1,10 +1,10 @@ ventaRepository; + } + protected function process(Model\Venta $venta): Model\Venta { if ($venta->uf === 0.0) { diff --git a/app/src/Service/Venta/MediosPago/Toku.php b/app/src/Service/Venta/MediosPago/Toku.php index d484bb8..ebd7414 100644 --- a/app/src/Service/Venta/MediosPago/Toku.php +++ b/app/src/Service/Venta/MediosPago/Toku.php @@ -302,6 +302,40 @@ class Toku extends Ideal\Service 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 array $tokenConfig diff --git a/app/src/Service/Venta/MediosPago/Toku/Invoice.php b/app/src/Service/Venta/MediosPago/Toku/Invoice.php index c769f29..ef275c8 100644 --- a/app/src/Service/Venta/MediosPago/Toku/Invoice.php +++ b/app/src/Service/Venta/MediosPago/Toku/Invoice.php @@ -4,6 +4,7 @@ namespace Incoviba\Service\Venta\MediosPago\Toku; use DateMalformedStringException; use DateTimeImmutable; use DateTimeZone; +use PDO; use PDOException; use Psr\Http\Client\ClientInterface; use Psr\Log\LoggerInterface; @@ -199,6 +200,41 @@ class Invoice extends AbstractEndPoint 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 { return $this->doSave($this->invoiceRepository, $data); @@ -215,7 +251,7 @@ class Invoice extends AbstractEndPoint { $paramsMap = [ 'customer' => 'customer', - 'product_id' => 'product_id', + 'product_id' => 'cuota_id', 'due_date' => 'fecha', 'subscription' => 'subscription', 'amount' => 'valor', diff --git a/app/src/Service/Venta/MediosPago/Toku/Subscription.php b/app/src/Service/Venta/MediosPago/Toku/Subscription.php index 8c6ddce..01f7d98 100644 --- a/app/src/Service/Venta/MediosPago/Toku/Subscription.php +++ b/app/src/Service/Venta/MediosPago/Toku/Subscription.php @@ -1,6 +1,7 @@ "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 { return $this->doSave($this->subscriptionRepsitory, $data); @@ -133,11 +229,11 @@ class Subscription extends AbstractEndPoint if ($ref === null) { continue; } - if ($ref === 'pieValor') { - $params[$key] = $data['venta']->formaPago()?->pie?->valor ?? 0; + if ($ref === 'pieValor' and array_key_exists('venta', $data)) { + $params[$key] = $data['venta']?->formaPago()?->pie?->valor ?? 0; continue; } - if ($ref === 'datosVenta') { + if ($ref === 'datosVenta' and array_key_exists('venta', $data)) { $params[$key] = $this->datosVenta($data['venta']); continue; } @@ -169,4 +265,38 @@ class Subscription extends AbstractEndPoint '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); + } } diff --git a/app/src/Service/Venta/Pago.php b/app/src/Service/Venta/Pago.php index 8d54938..3ddc4bc 100644 --- a/app/src/Service/Venta/Pago.php +++ b/app/src/Service/Venta/Pago.php @@ -4,25 +4,37 @@ namespace Incoviba\Service\Venta; use DateTimeInterface; use DateTimeImmutable; use DateMalformedStringException; +use Incoviba\Common\Define; use Incoviba\Exception\ServiceAction\Create; use Incoviba\Exception\ServiceAction\Read; use Incoviba\Exception\ServiceAction\Update; use PDOException; +use Incoviba\Common\Ideal; use Incoviba\Common\Implement\Exception\EmptyResult; use Incoviba\Repository; use Incoviba\Model; use Incoviba\Service; +use Psr\Log\LoggerInterface; -class Pago +class Pago extends Ideal\Service\Repository { public function __construct( + LoggerInterface $logger, protected Repository\Venta\Pago $pagoRepository, protected Repository\Venta\EstadoPago $estadoPagoRepository, protected Repository\Venta\TipoEstadoPago $tipoEstadoPagoRepository, protected Service\UF $ufService, protected Service\Valor $valorService, 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 { diff --git a/cli/crontab b/cli/crontab index 03b80d7..a0f9640 100644 --- a/cli/crontab +++ b/cli/crontab @@ -8,4 +8,4 @@ 0 2 * * * /code/bin/incoviba money:uf:update >> /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 -#0 3 * * * /code/bin/incoviba external:services >> /logs/commands 2>&1 +0 3 * * * /code/bin/incoviba external:services >> /logs/commands 2>&1