diff --git a/app/resources/routes/api/money.php b/app/resources/routes/api/money.php index 2f3fb4c..6442dd4 100644 --- a/app/resources/routes/api/money.php +++ b/app/resources/routes/api/money.php @@ -3,5 +3,6 @@ use Incoviba\Controller\API\Money; $app->group('/money', function($app) { $app->post('/ipc[/]', [Money::class, 'ipc']); + $app->post('/uf[/]', [Money::class, 'uf']); $app->post('[/]', [Money::class, 'get']); }); diff --git a/app/resources/routes/api/ventas/propiedades.php b/app/resources/routes/api/ventas/propiedades.php new file mode 100644 index 0000000..da542fa --- /dev/null +++ b/app/resources/routes/api/ventas/propiedades.php @@ -0,0 +1,10 @@ +group('/propiedades', function($app) { + $files = new FilesystemIterator(implode(DIRECTORY_SEPARATOR, [__DIR__, 'propiedades'])); + foreach ($files as $file) { + if ($file->isDir()) { + continue; + } + include_once $file->getRealPath(); + } +}); diff --git a/app/resources/routes/api/ventas/propiedades/unidades.php b/app/resources/routes/api/ventas/propiedades/unidades.php new file mode 100644 index 0000000..d48884e --- /dev/null +++ b/app/resources/routes/api/ventas/propiedades/unidades.php @@ -0,0 +1,6 @@ +group('/unidad/{pu_id}', function($app) { + $app->post('/edit[/]', [PropiedadesUnidades::class, 'edit']); +}); diff --git a/app/resources/routes/ventas/facturacion.php b/app/resources/routes/ventas/facturacion.php index b49f175..c43e7de 100644 --- a/app/resources/routes/ventas/facturacion.php +++ b/app/resources/routes/ventas/facturacion.php @@ -4,3 +4,6 @@ use Incoviba\Controller\Ventas\Facturacion; $app->group('/facturacion', function($app) { $app->get('[/]', Facturacion::class); }); +$app->group('/factura/{venta_id}', function($app) { + $app->get('[/]', [Facturacion::class, 'show']); +}); diff --git a/app/resources/views/ventas/facturacion.blade.php b/app/resources/views/ventas/facturacion.blade.php index c29d765..d0a09d4 100644 --- a/app/resources/views/ventas/facturacion.blade.php +++ b/app/resources/views/ventas/facturacion.blade.php @@ -45,9 +45,8 @@ if (typeof this.sent.uf[date.toISOString()] !== 'undefined') { return this.sent.uf[date.toISOString()] } - const url = '{{$urls->api}}/money' + const url = '{{$urls->api}}/money/uf' const data = new FormData() - data.set('provider', 'uf') data.set('fecha', date.toISOString()) const options = { method: 'post', @@ -68,7 +67,6 @@ } const url = '{{$urls->api}}/money/ipc' const data = new FormData() - data.set('provider', 'ipc') data.set('start', start.toISOString()) data.set('end', end.toISOString()) const options = { diff --git a/app/resources/views/ventas/facturacion/show.blade.php b/app/resources/views/ventas/facturacion/show.blade.php index 07e844e..2bd334c 100644 --- a/app/resources/views/ventas/facturacion/show.blade.php +++ b/app/resources/views/ventas/facturacion/show.blade.php @@ -2,10 +2,313 @@ @section('page_content')
-
-
- +

+ Facturación - + + {{$venta->proyecto()->descripcion}} + + - + + {{$venta->propiedad()->summary()}} + +

+ +
+ +
+ +
+ +
+
+
+
+ @foreach ($venta->propiedad()->unidades as $unidad) +
+ +
+ +
UF
+
+
+ @endforeach
+
+
+
+
+
+ + {{mb_strtoupper($venta->proyecto()->inmobiliaria()->nombreCompleto())}} +
+ GIRO:
+ Dirección: +
+
+
+ + RUT: {{$venta->proyecto()->inmobiliaria()->rut()}}
+ FACTURA ELECTRÓNICA
+ N° # +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
Señor(es){{$venta->propietario()->nombreCompleto()}}RUT{{$venta->propietario()->rut()}}
GiroOtras Actividades ProfesionalesFecha Emisión{{(new IntlDateFormatter('es-CL', IntlDateFormatter::LONG, IntlDateFormatter::NONE))->format($venta->currentEstado()->fecha)}}
Dirección{{$venta->propietario()->datos->direccion->simple()}}Comuna{{mb_strtoupper($venta->propietario()->datos->direccion->comuna->descripcion)}}
+
+
+ + + + + + + + + + + + + + + + + + + + +
DETALLES
DescripciónCant/UnidadPrec. Unit.IndTotal
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
TOTALES
Monto Neto
Monto Exento
19% IVA
Monto Total
+
+
+
+
@endsection + +@push('page_scripts') + +@endpush diff --git a/app/setup/setups/services.php b/app/setup/setups/services.php index 376fa42..9ba91f9 100644 --- a/app/setup/setups/services.php +++ b/app/setup/setups/services.php @@ -16,8 +16,11 @@ return [ 'base_uri' => 'https://mindicador.cl/api/', 'headers' => ['Accept' => 'application/json'] ])); + $ine = new Incoviba\Service\Money\Ine(new GuzzleHttp\Client([ + 'base_uri' => 'https://api-calculadora.ine.cl/ServiciosCalculadoraVariacion' + ])); return (new Incoviba\Service\Money())->register('uf', $mindicador) - ->register('ipc', $mindicador); + ->register('ipc', $ine); }, Predis\Client::class => function(ContainerInterface $container) { return new Predis\Client([ diff --git a/app/setup/setups/views.php b/app/setup/setups/views.php index c632889..0046530 100644 --- a/app/setup/setups/views.php +++ b/app/setup/setups/views.php @@ -10,6 +10,8 @@ return [ 'login' => $container->get(Incoviba\Service\Login::class), 'format' => $container->get(Incoviba\Service\Format::class), 'API_KEY' => $container->get('API_KEY'), + 'UF' => $container->get(Incoviba\Service\UF::class), + 'IPC' => $container->get(Incoviba\Service\IPC::class) ]; if ($global_variables['login']->isIn()) { $global_variables['user'] = $global_variables['login']->getUser(); diff --git a/app/src/Controller/API/Money.php b/app/src/Controller/API/Money.php index 5dab0c6..28a0b14 100644 --- a/app/src/Controller/API/Money.php +++ b/app/src/Controller/API/Money.php @@ -62,34 +62,23 @@ class Money } return $this->data[$provider][$date->format('Y-m-d')]; } - /*public function uf(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService, Service\Money $moneyService): ResponseInterface + public function uf(ServerRequestInterface $request, ResponseInterface $response, Service\UF $ufService): ResponseInterface { $body = $request->getParsedBody(); $output = [ 'input' => $body, 'uf' => 0 ]; - $redisKey = 'uf'; $date = new DateTimeImmutable($body['fecha']); - try { - $ufs = $this->fetchRedis($redisService, $redisKey); - if (!isset($ufs[$date->format('Y-m-d')])) { - throw new EmptyRedis($redisKey); - } - } catch (EmptyRedis) { - error_log(var_export($ufs,true)); - if (!isset($ufs)) { - $ufs = []; - } - $uf = $moneyService->getUF($date); - $ufs[$date->format('Y-m-d')] = $uf; - $this->saveRedis($redisService, $redisKey, $ufs, 60 * 60 * 24 * 30); + $now = new DateTimeImmutable(); + if ($date > $now) { + return $this->withJson($response, $output); } - $output['uf'] = $ufs[$date->format('Y-m-d')]; + $output['uf'] = $ufService->get($date); return $this->withJson($response, $output); - }*/ - public function ipc(ServerRequestInterface $request, ResponseInterface $response, Service\Redis $redisService, - Service\Money $moneyService): ResponseInterface + } + public function ipc(ServerRequestInterface $request, ResponseInterface $response, + Service\IPC $ipcService): ResponseInterface { $data = $request->getParsedBody(); $output = [ @@ -97,7 +86,6 @@ class Money 'date_string' => '', 'ipc' => 0 ]; - $redisKey = 'ipc'; $start = new DateTimeImmutable($data['start']); $end = new DateTimeImmutable($data['end']); $now = new DateTimeImmutable(); @@ -105,23 +93,8 @@ class Money return $this->withJson($response, $output); } $dateKey = "{$start->format('Y-m')}-{$end->format('Y-m')}"; - $months = $start->diff($end)->m; - $current = new DateTimeImmutable((($start <= $end) ? $start : $end)->format('Y-m-15')); - $value = 0; - $ipcs = []; - try { - $ipcs = (array) $this->fetchRedis($redisService, $redisKey); - } catch (EmptyRedis) {} - for ($i = 1; $i < $months; $i ++) { - $current = $current->add(new DateInterval("P1M")); - if (!isset($ipcs[$current->format('Y-m')])) { - $ipcs[$current->format('Y-m')] = $moneyService->getIPC($current); - $this->saveRedis($redisService, $redisKey, $ipcs, $this->time); - } - $value += $ipcs[$current->format('Y-m')]; - } $output['date_string'] = $dateKey; - $output['ipc'] = $value; + $output['ipc'] = $ipcService->get($start, $end); return $this->withJson($response, $output); } } diff --git a/app/src/Controller/API/Ventas/Precios.php b/app/src/Controller/API/Ventas/Precios.php index 63b3cf9..8f8dbe9 100644 --- a/app/src/Controller/API/Ventas/Precios.php +++ b/app/src/Controller/API/Ventas/Precios.php @@ -1,16 +1,17 @@ getParsedBody(); + $output = [ + 'propiedad_unidad_id' => $pu_id, + 'input' => $body, + 'edited' => false + ]; + try { + $pu = $propiedadUnidadService->getById($pu_id); + $propiedadUnidadService->edit($pu, (array) $body); + $output['edited'] = true; + } catch (PDOException | EmptyResult) {} + return $this->withJson($response, $output); + } +} diff --git a/app/src/Model/Direccion.php b/app/src/Model/Direccion.php index 92b8e05..d27a4ff 100644 --- a/app/src/Model/Direccion.php +++ b/app/src/Model/Direccion.php @@ -10,6 +10,20 @@ class Direccion extends Model public string $extra; public Comuna $comuna; + public function simple(): string + { + $arr = [ + implode(' ', [ + $this->calle, + $this->numero + ]) + ]; + if ($this->extra !== '') { + $arr []= $this->extra; + } + return implode(', ', $arr); + } + public function full(): string { return implode(', ', [ diff --git a/app/src/Model/Venta/PropiedadUnidad.php b/app/src/Model/Venta/PropiedadUnidad.php new file mode 100644 index 0000000..a1bbb3a --- /dev/null +++ b/app/src/Model/Venta/PropiedadUnidad.php @@ -0,0 +1,16 @@ + $this->valor + ]); + } +} diff --git a/app/src/Repository/Proyecto.php b/app/src/Repository/Proyecto.php index be015ce..6aac760 100644 --- a/app/src/Repository/Proyecto.php +++ b/app/src/Repository/Proyecto.php @@ -70,13 +70,24 @@ class Proyecto extends Ideal\Repository 'valor_terreno', 'corredor', 'superficie_sobre_nivel', 'superficie_bajo_nivel', 'pisos', 'subterraneos'], $new_data); } + + public function fetchById(int $id): Define\Model + { + $query = $this->connection->getQueryBuilder() + ->select($this->columns()) + ->from("{$this->getTable()} a") + ->joined($this->joinTerreno()) + ->where("a.id = ?"); + return $this->fetchOne($query, [$id]); + } + public function fetchByName(string $name): Define\Model { $query = $this->connection->getQueryBuilder() ->select($this->columns()) ->from("{$this->getTable()} a") ->joined($this->joinTerreno()) - ->where("descripcion = ?"); + ->where("a.descripcion = ?"); return $this->fetchOne($query, [$name]); } public function fetchAllActive(): array diff --git a/app/src/Repository/Venta/Propiedad.php b/app/src/Repository/Venta/Propiedad.php index 173e810..1549295 100644 --- a/app/src/Repository/Venta/Propiedad.php +++ b/app/src/Repository/Venta/Propiedad.php @@ -9,7 +9,7 @@ use Incoviba\Service; class Propiedad extends Ideal\Repository { - public function __construct(Define\Connection $connection, protected Service\Venta\Unidad $unidadService) + public function __construct(Define\Connection $connection, protected Service\Venta\PropiedadUnidad $unidadService) { parent::__construct($connection); $this->setTable('propiedad'); diff --git a/app/src/Repository/Venta/PropiedadUnidad.php b/app/src/Repository/Venta/PropiedadUnidad.php new file mode 100644 index 0000000..9402e5f --- /dev/null +++ b/app/src/Repository/Venta/PropiedadUnidad.php @@ -0,0 +1,118 @@ +setTable('propiedad_unidad'); + } + + public function load(array $data_row): Define\Model + { + $unidad = $this->unidadRepository->fetchById($data_row['unidad']); + $data = [ + 'id' => $unidad->id, + 'subtipo' => $unidad->subtipo, + 'piso' => $unidad->piso, + 'descripcion' => $unidad->descripcion, + 'orientacion' => $unidad->orientacion, + 'prorrateo' => $unidad->prorrateo, + 'pt' => $unidad->proyectoTipoUnidad->id, + 'pu_id' => $data_row['id'], + 'propiedad' => $data_row['propiedad'], + 'valor' => $data_row['valor'] + ]; + return parent::load($data); + } + + public function create(?array $data = null): Model\Venta\PropiedadUnidad + { + $map = (new Implement\Repository\MapperParser(['subtipo', 'piso', 'descripcion', 'orientacion', 'prorrateo', 'valor', 'id'])) + ->register('propiedad', (new Implement\Repository\Mapper()) + ->setProperty('propiedad_id')) + ->register('pt', (new Implement\Repository\Mapper()) + ->setProperty('proyectoTipoUnidad') + ->setFunction(function($data) { + return $this->proyectoTipoUnidadService->getById($data['pt']); + })); + return $this->parseData(new Model\Venta\PropiedadUnidad(), $data, $map); + } + public function save(Define\Model $model): Model\Venta\PropiedadUnidad + { + $model->pu_id = $this->saveNew(['propiedad', 'unidad', 'valor'], [$model->propiedad_id, $model->id, $model->valor]); + return $model; + } + public function edit(Define\Model $model, array $new_data): Model\Venta\PropiedadUnidad + { + return $this->update($model, ['propiedad', 'unidad', 'valor'], $new_data); + } + + public function fetchById(int $id): Define\Model + { + $query = $this->connection->getQueryBuilder() + ->select() + ->from($this->getTable()) + ->where("id = ?"); + return $this->fetchOne($query, [$id]); + } + + public function fetchByVenta(int $venta_id): array + { + $query = $this->connection->getQueryBuilder() + ->select('a.*') + ->from("{$this->getTable()} a") + ->joined('JOIN unidad ON a.unidad = unidad.id + JOIN venta ON venta.propiedad = a.propiedad') + ->where('venta.id = ?'); + return $this->fetchMany($query, [$venta_id]); + } + public function fetchByPropiedad(int $propiedad_id): array + { + $query = $this->connection->getQueryBuilder() + ->select('a.*') + ->from("{$this->getTable()} a") + ->joined('JOIN `unidad` ON a.`unidad` = `unidad`.`id`') + ->where('a.`propiedad` = ?') + ->group('`unidad`.`id`'); + return $this->fetchMany($query, [$propiedad_id]); + } + + protected function update(Define\Model $model, array $columns, array $data): Define\Model + { + $changes = []; + $values = []; + foreach ($columns as $column) { + if (isset($data[$column])) { + $changes []= $column; + $values []= $data[$column]; + } + } + + if (count($changes) === 0) { + return $model; + } + $columns_string = implode(', ', array_map(function($property) {return "`{$property}` = ?";}, $changes)); + $query = $this->connection->getQueryBuilder() + ->update($this->getTable()) + ->set($columns_string) + ->where("id = ?"); + $values []= $model->{$this->getKey()}; + $this->connection->execute($query, $values); + return $this->fetchById($model->{$this->getKey()}); + } + + protected function getKey(): string + { + return 'pu_id'; + } +} diff --git a/app/src/Repository/Venta/Unidad.php b/app/src/Repository/Venta/Unidad.php index 8605d32..925425e 100644 --- a/app/src/Repository/Venta/Unidad.php +++ b/app/src/Repository/Venta/Unidad.php @@ -39,6 +39,16 @@ class Unidad extends Ideal\Repository return $this->update($model, ['subtipo', 'piso', 'descripcion', 'orientacion', 'pt'], $new_data); } + public function fetchById(int $id): Model\Venta\Unidad + { + $query = $this->connection->getQueryBuilder() + ->select('a.*, up.prorrateo') + ->from("{$this->getTable()} a") + ->joined($this->joinProrrateo()) + ->where('a.id = ?'); + return $this->fetchOne($query, [$id]); + } + public function fetchByVenta(int $venta_id): array { $query = $this->connection->getQueryBuilder() diff --git a/app/src/Service/IPC.php b/app/src/Service/IPC.php new file mode 100644 index 0000000..fcba1b4 --- /dev/null +++ b/app/src/Service/IPC.php @@ -0,0 +1,38 @@ + $now) { + return 0; + } + $dateKey = "{$from->format('Y-m')}-{$to->format('Y-m')}"; + $ipcs = []; + try { + $ipcs = json_decode($this->redisService->get($this->redisKey), JSON_OBJECT_AS_ARRAY); + if (!isset($ipcs[$dateKey])) { + throw new EmptyRedis($this->redisKey); + } + } catch (EmptyRedis) { + $ipc = $this->moneyService->getIPC($from, $to); + $ipcs[$dateKey] = $ipc; + $this->redisService->set($this->redisKey, json_encode($ipcs), 60 * 60 * 24 * 30); + } + return $ipcs[$dateKey]; + } + public function readjust(float $base, DateTimeInterface $from, DateTimeInterface $to = new DateTimeImmutable()):float + { + return $base * (1 + $this->get($from, $to->sub(new DateInterval('P1M')))); + } +} diff --git a/app/src/Service/Money.php b/app/src/Service/Money.php index b4d581c..c9a7b8c 100644 --- a/app/src/Service/Money.php +++ b/app/src/Service/Money.php @@ -41,10 +41,10 @@ class Money return 0; } } - public function getIPC(DateTimeInterface $dateTime): float + public function getIPC(DateTimeInterface $start, DateTimeInterface $end): float { try { - return $this->getProvider('ipc')->get(MiIndicador::IPC, $dateTime); + return $this->getProvider('ipc')->getVar($start, $end); } catch (EmptyResponse) { return 0; } diff --git a/app/src/Service/Money/Ine.php b/app/src/Service/Money/Ine.php new file mode 100644 index 0000000..79093fb --- /dev/null +++ b/app/src/Service/Money/Ine.php @@ -0,0 +1,49 @@ +format('Y-m-1')); + $start = $end->sub(new DateInterval('P1M')); + return $this->getVar($start, $end); + } + + public function getVar(DateTimeInterface $start, DateTimeInterface $end): float + { + $base = 1000000000; + $request_query = [ + 'mesInicio' => $start->format('m'), + 'AnioInicio' => $start->format('Y'), + 'mesTermino' => $end->format('m'), + 'AnioTermino' => $end->format('Y'), + 'valor_a_ajustar' => $base + ]; + $request_uri = implode('?', [ + $this->uri, + implode('&', array_map(function($val, $key) { + return "{$key}={$val}"; + }, $request_query, array_keys($request_query))) + ]); + try { + $response = $this->client->get($request_uri); + } catch (GuzzleException) { + throw new EmptyResponse($request_uri); + } + $body = $response->getBody(); + $json = json_decode($body->getContents()); + return ((int) str_replace('.', '', $json[0]->valorajustado) - $base) / $base; + } +} diff --git a/app/src/Service/UF.php b/app/src/Service/UF.php new file mode 100644 index 0000000..434ba42 --- /dev/null +++ b/app/src/Service/UF.php @@ -0,0 +1,35 @@ +redisService->get($this->redisKey), JSON_OBJECT_AS_ARRAY); + if (!isset($ufs[$date->format('Y-m-d')])) { + throw new EmptyRedis($this->redisKey); + } + $uf = $ufs[$date->format('Y-m-d')]; + } catch (EmptyRedis) { + $uf = $this->moneyService->getUF($date); + $ufs[$date->format('Y-m-d')] = $uf; + $this->redisService->set($this->redisKey, json_encode($ufs), 60 * 60 * 24 * 30); + } + return $uf; + } + public function transform(DateTimeInterface $date, float $input, string $from = 'uf'): float + { + + $uf = $this->get($date); + return $input * (($from === 'uf') ? $uf : 1/$uf); + } +} diff --git a/app/src/Service/Venta/PropiedadUnidad.php b/app/src/Service/Venta/PropiedadUnidad.php new file mode 100644 index 0000000..6787b11 --- /dev/null +++ b/app/src/Service/Venta/PropiedadUnidad.php @@ -0,0 +1,39 @@ +process($this->propiedadUnidadRepository->fetchById($unidad_id)); + } + public function getByVenta(int $venta_id): array + { + return array_map([$this, 'process'], $this->propiedadUnidadRepository->fetchByVenta($venta_id)); + } + public function getByPropiedad(int $propiedad_id): array + { + return array_map([$this, 'process'], $this->propiedadUnidadRepository->fetchByPropiedad($propiedad_id)); + } + public function edit(Model\Venta\PropiedadUnidad $propiedadUnidad, array $data): Model\Venta\PropiedadUnidad + { + return $this->process($this->propiedadUnidadRepository->edit($propiedadUnidad, $data)); + } + + protected function process($unidad): Model\Venta\PropiedadUnidad + { + try { + $unidad->precios = $this->precioService->getByUnidad($unidad->id); + $unidad->currentPrecio = $this->precioService->getVigenteByUnidad($unidad->id); + } catch (EmptyResult) { + } + return $unidad; + } +} diff --git a/app/src/Service/Venta/Unidad.php b/app/src/Service/Venta/Unidad.php index 899580e..1fc6f6e 100644 --- a/app/src/Service/Venta/Unidad.php +++ b/app/src/Service/Venta/Unidad.php @@ -10,7 +10,7 @@ class Unidad { public function __construct( protected Repository\Venta\Unidad $unidadRepository, - protected Service\Venta\Precio $precioService + protected Precio $precioService ) {} public function getById(int $unidad_id): Model\Venta\Unidad diff --git a/cli/src/Command/Money/UF.php b/cli/src/Command/Money/UF.php index b7c99e2..7e700ce 100644 --- a/cli/src/Command/Money/UF.php +++ b/cli/src/Command/Money/UF.php @@ -14,9 +14,8 @@ class UF extends Command { $this->logger->debug("Running {$this->getName()}"); $now = new DateTimeImmutable(); - $uri = '/api/money'; + $uri = '/api/money/uf'; $data = [ - 'provider' => 'uf', 'fecha' => $now->format('Y-m-d') ]; $output->writeln("POST {$uri}");