diff --git a/app/common/Implement/Repository/Factory.php b/app/common/Implement/Repository/Factory.php
index 00fd799..10ec520 100644
--- a/app/common/Implement/Repository/Factory.php
+++ b/app/common/Implement/Repository/Factory.php
@@ -2,7 +2,9 @@
namespace Incoviba\Common\Implement\Repository;
use Closure;
+use Exception;
use Incoviba\Common\Define;
+use Incoviba\Common\Implement\Exception\EmptyResult;
class Factory implements Define\Repository\Factory
{
@@ -20,8 +22,16 @@ class Factory implements Define\Repository\Factory
return $this;
}
+ /**
+ * @return mixed
+ * @throws EmptyResult
+ */
public function run(): mixed
{
- return call_user_func_array($this->callable, $this->args);
+ try {
+ return call_user_func_array($this->callable, $this->args);
+ } catch (Exception $exception) {
+ throw new EmptyResult($exception->getMessage(), $exception);
+ }
}
}
diff --git a/app/resources/database/migrations/20250215133437_create_reservation.php b/app/resources/database/migrations/20250215133437_create_reservations.php
similarity index 78%
rename from app/resources/database/migrations/20250215133437_create_reservation.php
rename to app/resources/database/migrations/20250215133437_create_reservations.php
index db9c32a..1a8dec7 100644
--- a/app/resources/database/migrations/20250215133437_create_reservation.php
+++ b/app/resources/database/migrations/20250215133437_create_reservations.php
@@ -4,7 +4,7 @@ declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
-final class CreateReservation extends AbstractMigration
+final class CreateReservations extends AbstractMigration
{
/**
* Change Method.
@@ -23,9 +23,11 @@ final class CreateReservation extends AbstractMigration
$this->execute("ALTER DATABASE CHARACTER SET 'utf8mb4';");
$this->execute("ALTER DATABASE COLLATE='utf8mb4_general_ci';");
- $this->table('reservation')
+ $this->table('reservations')
+ ->addColumn('project_id', 'integer', ['signed' => false, 'null' => false])
->addColumn('buyer_rut', 'integer', ['signed' => false, 'null' => false])
->addColumn('date', 'date', ['null' => false])
+ ->addForeignKey('project_id', 'proyecto', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
->addForeignKey('buyer_rut', 'personas', 'rut', ['delete' => 'cascade', 'update' => 'cascade'])
->create();
diff --git a/app/resources/database/migrations/20250215135457_create_reservation_datas.php b/app/resources/database/migrations/20250215135457_create_reservation_details.php
similarity index 73%
rename from app/resources/database/migrations/20250215135457_create_reservation_datas.php
rename to app/resources/database/migrations/20250215135457_create_reservation_details.php
index ad85f98..3bdcac7 100644
--- a/app/resources/database/migrations/20250215135457_create_reservation_datas.php
+++ b/app/resources/database/migrations/20250215135457_create_reservation_details.php
@@ -4,7 +4,7 @@ declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
-final class CreateReservationDatas extends AbstractMigration
+final class CreateReservationDetails extends AbstractMigration
{
/**
* Change Method.
@@ -23,11 +23,13 @@ final class CreateReservationDatas extends AbstractMigration
$this->execute("ALTER DATABASE CHARACTER SET 'utf8mb4';");
$this->execute("ALTER DATABASE COLLATE='utf8mb4_general_ci';");
- $this->table('reservation_data')
+ $this->table('reservation_details')
->addColumn('reservation_id', 'integer', ['signed' => false, 'null' => false])
->addColumn('type', 'integer', ['length' => 1, 'signed' => false, 'null' => false])
->addColumn('reference_id', 'integer', ['signed' => false, 'null' => false])
- ->addColumn('value', 'decimal', ['precision' => 10, 'scale' => 2, 'signed' => false, 'default' => 0.00, 'null' => true])
+ ->addColumn('value', 'decimal', ['precision' => 10, 'scale' => 2, 'signed' => false, 'default' => null, 'null' => true])
+ ->addForeignKey('reservation_id', 'reservations', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->addIndex(['reservation_id', 'type', 'reference_id'], ['unique' => true, 'name' => 'idx_reservation_details'])
->create();
$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');
diff --git a/app/resources/database/migrations/20250215135822_create_reservation_states.php b/app/resources/database/migrations/20250215135822_create_reservation_states.php
index e617ef5..490d3dc 100644
--- a/app/resources/database/migrations/20250215135822_create_reservation_states.php
+++ b/app/resources/database/migrations/20250215135822_create_reservation_states.php
@@ -27,7 +27,7 @@ final class CreateReservationStates extends AbstractMigration
->addColumn('reservation_id', 'integer', ['signed' => false, 'null' => false])
->addColumn('date', 'date', ['null' => false])
->addColumn('type', 'integer', ['length' => 3, 'null' => false, 'default' => 0])
- ->addForeignKey('reservation_id', 'reservation', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->addForeignKey('reservation_id', 'reservations', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
->create();
$this->execute('SET unique_checks=1; SET foreign_key_checks=1;');
diff --git a/app/resources/database/migrations/20250805192903_change_telefono_size_in_propietario.php b/app/resources/database/migrations/20250805192903_change_telefono_size_in_propietario.php
new file mode 100644
index 0000000..54a7f08
--- /dev/null
+++ b/app/resources/database/migrations/20250805192903_change_telefono_size_in_propietario.php
@@ -0,0 +1,26 @@
+table('propietario')
+ ->changeColumn('telefono', 'biginteger', ['null' => true, 'signed' => false, 'default' => null])
+ ->update();
+ }
+}
diff --git a/app/resources/database/migrations/20250805212635_add_comments_to_estado_cierre.php b/app/resources/database/migrations/20250805212635_add_comments_to_estado_cierre.php
new file mode 100644
index 0000000..bbd9cae
--- /dev/null
+++ b/app/resources/database/migrations/20250805212635_add_comments_to_estado_cierre.php
@@ -0,0 +1,29 @@
+table('estado_cierre_comentarios')
+ ->addColumn('estado_cierre_id', 'integer', ['signed' => false])
+ ->addColumn('fecha', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
+ ->addColumn('comments', 'text')
+ ->addForeignKey('estado_cierre_id', 'estado_cierre', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->create();
+ }
+}
diff --git a/app/resources/routes/api/personas.php b/app/resources/routes/api/personas.php
new file mode 100644
index 0000000..b01069c
--- /dev/null
+++ b/app/resources/routes/api/personas.php
@@ -0,0 +1,7 @@
+group('/personas', function($app) {});
+$app->group('/persona/{rut}', function($app) {
+ $app->get('[/]', [Personas::class, 'get']);
+});
diff --git a/app/resources/routes/api/proyectos.php b/app/resources/routes/api/proyectos.php
index 15d0445..5ff5f90 100644
--- a/app/resources/routes/api/proyectos.php
+++ b/app/resources/routes/api/proyectos.php
@@ -30,4 +30,5 @@ $app->group('/proyecto/{proyecto_id}', function($app) {
$app->post('/edit[/]', [Proyectos::class, 'terreno']);
});
$app->get('/brokers', [Proyectos::class, 'brokers']);
+ $app->get('/promotions', [Proyectos::class, 'promotions']);
});
diff --git a/app/resources/routes/api/ventas/reservations.php b/app/resources/routes/api/ventas/reservations.php
index 7e2e194..7625931 100644
--- a/app/resources/routes/api/ventas/reservations.php
+++ b/app/resources/routes/api/ventas/reservations.php
@@ -3,6 +3,11 @@ use Incoviba\Controller\API\Ventas\Reservations;
$app->group('/reservations', function($app) {
$app->post('/add[/]', [Reservations::class, 'add']);
+ $app->group('/project/{project_id}', function($app) {
+ $app->get('/active[/]', [Reservations::class, 'active']);
+ $app->get('/pending[/]', [Reservations::class, 'pending']);
+ $app->get('/rejected[/]', [Reservations::class, 'rejected']);
+ });
$app->get('[/]', Reservations::class);
});
$app->group('/reservation/{reservation_id}', function($app) {
diff --git a/app/resources/routes/ventas/cierres.php b/app/resources/routes/ventas/cierres.php
index 62e3084..bf57def 100644
--- a/app/resources/routes/ventas/cierres.php
+++ b/app/resources/routes/ventas/cierres.php
@@ -1,5 +1,6 @@
group('/cierres', function($app) {
$app->get('[/]', Cierres::class);
diff --git a/app/resources/views/ventas/add.blade.php b/app/resources/views/ventas/add.blade.php
index e870736..c8aa737 100644
--- a/app/resources/views/ventas/add.blade.php
+++ b/app/resources/views/ventas/add.blade.php
@@ -156,7 +156,7 @@
+@endpush
diff --git a/app/resources/views/ventas/reservations/add_modal.blade.php b/app/resources/views/ventas/reservations/add_modal.blade.php
new file mode 100644
index 0000000..3f137b1
--- /dev/null
+++ b/app/resources/views/ventas/reservations/add_modal.blade.php
@@ -0,0 +1,733 @@
+
+
+
+
+
+ Cancelar
+
+
+ Agregar
+
+
+
+
+@include('layout.body.scripts.rut')
+
+@push('page_scripts')
+
+@endpush
diff --git a/app/setup/setups/services.php b/app/setup/setups/services.php
index 9cdecfc..5b396f1 100644
--- a/app/setup/setups/services.php
+++ b/app/setup/setups/services.php
@@ -116,7 +116,18 @@ return [
->registerSub($container->get(Incoviba\Service\Contabilidad\Cartola\BCI\Mes::class));
},
'TokuClient' => function(ContainerInterface $container) {
+ $logger = $container->get('externalLogger');
+ $stack = GuzzleHttp\HandlerStack::create();
+ $stack->push(GuzzleHttp\Middleware::mapRequest(function(Psr\Http\Message\RequestInterface $request) use ($logger) {
+ $logger->info('Toku Request', [
+ 'method' => $request->getMethod(),
+ 'uri' => (string) $request->getUri(),
+ 'headers' => $request->getHeaders(),
+ 'body' => $request->getBody()->getContents(),
+ ]);
+ }));
return new GuzzleHttp\Client([
+ 'handler' => $stack,
'base_uri' => $container->get('TOKU_URL'),
'headers' => [
'x-api-key' => $container->get('TOKU_TOKEN'),
diff --git a/app/src/Controller/API/Personas.php b/app/src/Controller/API/Personas.php
new file mode 100644
index 0000000..ab424a9
--- /dev/null
+++ b/app/src/Controller/API/Personas.php
@@ -0,0 +1,34 @@
+ $rut,
+ 'persona' => null,
+ 'success' => false
+ ];
+ try {
+ $persona = $personaService->getById($rut);
+ $output['persona'] = $persona;
+ $output['success'] = true;
+ } catch (ServiceAction\Read $exception) {
+ $this->logger->error($exception->getMessage(), ['exception' => $exception]);
+ }
+
+ return $this->withJson($response, $output);
+ }
+}
diff --git a/app/src/Controller/API/Proyectos.php b/app/src/Controller/API/Proyectos.php
index e6994ec..d30adb8 100644
--- a/app/src/Controller/API/Proyectos.php
+++ b/app/src/Controller/API/Proyectos.php
@@ -187,5 +187,18 @@ class Proyectos
}
return $this->withJson($response, $output);
}
-
+ public function promotions(ServerRequestInterface $request, ResponseInterface $response,
+ Service\Venta\Promotion $promotionService, int $proyecto_id): ResponseInterface
+ {
+ $output = [
+ 'project_id' => $proyecto_id,
+ 'promotions' => []
+ ];
+ try {
+ $output['promotions'] = $promotionService->getByProject($proyecto_id);
+ } catch (Read $exception) {
+ return $this->withError($response, $exception);
+ }
+ return $this->withJson($response, $output);
+ }
}
diff --git a/app/src/Controller/API/Ventas/Reservations.php b/app/src/Controller/API/Ventas/Reservations.php
index 01c65db..83fc3f7 100644
--- a/app/src/Controller/API/Ventas/Reservations.php
+++ b/app/src/Controller/API/Ventas/Reservations.php
@@ -11,7 +11,8 @@ class Reservations
{
use withJson;
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Service\Venta\Reservation $reservationService): ResponseInterface
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response,
+ Service\Venta\Reservation $reservationService): ResponseInterface
{
$reservations = [];
try {
@@ -22,6 +23,18 @@ class Reservations
return $this->withJson($response, compact('reservations'));
}
+ public function getByProject(ServerRequestInterface $request, ResponseInterface $response,
+ Service\Venta\Reservation $reservationService, int $project_id): ResponseInterface
+ {
+ $reservations = [];
+ try {
+ $reservations = $reservationService->getByProject($project_id);
+ } catch (ServiceAction\Read $exception) {
+ return $this->withError($response, $exception);
+ }
+
+ return $this->withJson($response, compact('reservations'));
+ }
public function get(ServerRequestInterface $request, ResponseInterface $response, Service\Venta\Reservation $reservationService, int $reservation_id): ResponseInterface
{
$output = [
@@ -100,4 +113,47 @@ class Reservations
return $this->withJson($response, $output);
}
+
+ public function active(ServerRequestInterface $request, ResponseInterface $response,
+ Service\Venta\Reservation $reservationService, int $project_id): ResponseInterface
+ {
+ $output = [
+ 'project_id' => $project_id,
+ 'reservations' => [],
+ 'success' => false,
+ ];
+ try {
+ $output['reservations'] = $reservationService->getActive($project_id);
+ $output['success'] = true;
+ } catch (ServiceAction\Read) {}
+ return $this->withJson($response, $output);
+ }
+ public function pending(ServerRequestInterface $request, ResponseInterface $response,
+ Service\Venta\Reservation $reservationService, int $project_id): ResponseInterface
+ {
+ $output = [
+ 'project_id' => $project_id,
+ 'reservations' => [],
+ 'success' => false,
+ ];
+ try {
+ $output['reservations'] = $reservationService->getPending($project_id);
+ $output['success'] = true;
+ } catch (ServiceAction\Read) {}
+ return $this->withJson($response, $output);
+ }
+ public function rejected(ServerRequestInterface $request, ResponseInterface $response,
+ Service\Venta\Reservation $reservationService, int $project_id): ResponseInterface
+ {
+ $output = [
+ 'project_id' => $project_id,
+ 'reservations' => [],
+ 'success' => false,
+ ];
+ try {
+ $output['reservations'] = $reservationService->getRejected($project_id);
+ $output['success'] = true;
+ } catch (ServiceAction\Read) {}
+ return $this->withJson($response, $output);
+ }
}
diff --git a/app/src/Controller/Ventas/Reservations.php b/app/src/Controller/Ventas/Reservations.php
new file mode 100644
index 0000000..9ee1edc
--- /dev/null
+++ b/app/src/Controller/Ventas/Reservations.php
@@ -0,0 +1,28 @@
+getVendibles('descripcion');
+ } catch (Read) {}
+ $regions = [];
+ try {
+ $regions = $regionRepository->fetchAll();
+ } catch (EmptyResult) {}
+ return $view->render($response, 'ventas.reservations', compact('projects', 'regions'));
+ }
+}
diff --git a/app/src/Exception/Model/InvalidState.php b/app/src/Exception/Model/InvalidState.php
new file mode 100644
index 0000000..3c82782
--- /dev/null
+++ b/app/src/Exception/Model/InvalidState.php
@@ -0,0 +1,13 @@
+datos)) {
- $this->datos = $this->runFactory('datos');
+ try {
+ $this->datos = $this->runFactory('datos');
+ } catch (EmptyResult) {
+ $this->datos = null;
+ }
}
return $this->datos;
}
diff --git a/app/src/Model/Proyecto/Broker.php b/app/src/Model/Proyecto/Broker.php
index e55df74..342e668 100644
--- a/app/src/Model/Proyecto/Broker.php
+++ b/app/src/Model/Proyecto/Broker.php
@@ -1,7 +1,10 @@
contracts;
}
+ public function getContract(Proyecto $proyecto, ?DateTimeInterface $date = null): ?Proyecto\Broker\Contract
+ {
+ if ($date === null) {
+ $date = new DateTimeImmutable();
+ }
+ $contracts = $this->contracts();
+ $valid = array_filter($contracts, fn(Proyecto\Broker\Contract $contract) => $contract->project->id === $proyecto->id);
+ $valid = array_filter($valid, fn(Proyecto\Broker\Contract $contract) => $contract->wasActive($date));
+ if (count($valid) === 0) {
+ return null;
+ }
+ return last($valid);
+ }
+
+ public function applyCommission(Proyecto $proyecto, float $price, ?DateTimeInterface $date = null): float
+ {
+ $contract = $this->getContract($proyecto, $date);
+ if ($contract === null) {
+ return $price;
+ }
+ return $contract->activate()->apply($proyecto, $price);
+ }
+ public function reverseCommission(Proyecto $proyecto, float $price, ?DateTimeInterface $date = null): float
+ {
+ $contract = $this->getContract($proyecto, $date);
+ if ($contract === null) {
+ return $price;
+ }
+ return $contract->activate()->reverse($proyecto, $price);
+ }
public function rutFull(): string
{
diff --git a/app/src/Model/Proyecto/Broker/Contract.php b/app/src/Model/Proyecto/Broker/Contract.php
index 89f6014..feb5625 100644
--- a/app/src/Model/Proyecto/Broker/Contract.php
+++ b/app/src/Model/Proyecto/Broker/Contract.php
@@ -1,6 +1,7 @@
promotions;
}
+ public function isActive(): bool
+ {
+ $state = $this->current();
+ return $state !== null and $state->isActive();
+ }
+ public function activate(): self
+ {
+ $this->current()->activate();
+ return $this;
+ }
+ public function wasActive(DateTimeInterface $date): bool
+ {
+ $states = $this->states();
+ return array_any($states, fn($state) => $state->date <= $date);
+ }
+
+ public function value(float $price): float
+ {
+ if (!$this->isActive()) {
+ return $price;
+ }
+ return $price * (1 + $this->commission);
+ }
+ public function inverse(float $price): float
+ {
+ if (!$this->isActive()) {
+ return $price;
+ }
+ return $price / (1 + $this->commission);
+ }
+ public function apply(Model\Proyecto $project, float $price): float
+ {
+ if (!$this->isActive() or $this->project->id !== $project->id) {
+ return $price;
+ }
+ return $this->value($price);
+ }
+ public function reverse(Model\Proyecto $project, float $price): float
+ {
+ if (!$this->isActive() or $this->project->id !== $project->id) {
+ return $price;
+ }
+ return $this->inverse($price);
+ }
+
protected function jsonComplement(): array
{
return [
diff --git a/app/src/Model/Proyecto/Broker/Contract/State.php b/app/src/Model/Proyecto/Broker/Contract/State.php
index c773d6d..c3a982c 100644
--- a/app/src/Model/Proyecto/Broker/Contract/State.php
+++ b/app/src/Model/Proyecto/Broker/Contract/State.php
@@ -11,6 +11,16 @@ class State extends Common\Ideal\Model
public DateTimeInterface $date;
public State\Type $type;
+ public function isActive(): bool
+ {
+ return $this->type === State\Type::ACTIVE;
+ }
+ public function activate(): self
+ {
+ $this->type = State\Type::ACTIVE;
+ return $this;
+ }
+
protected function jsonComplement(): array
{
return [
diff --git a/app/src/Model/Venta/Promotion.php b/app/src/Model/Venta/Promotion.php
index 8a1cb25..49b9676 100644
--- a/app/src/Model/Venta/Promotion.php
+++ b/app/src/Model/Venta/Promotion.php
@@ -3,6 +3,7 @@ namespace Incoviba\Model\Venta;
use DateTimeInterface;
use Incoviba\Common;
+use Incoviba\Model\Proyecto;
use Incoviba\Model\Proyecto\Broker;
use Incoviba\Model\Venta\Promotion\State;
use Incoviba\Model\Venta\Promotion\Type;
@@ -61,13 +62,95 @@ class Promotion extends Common\Ideal\Model
return $this->units;
}
+ public function isActive(): bool
+ {
+ return $this->state === State::ACTIVE;
+ }
+ public function activate(): self
+ {
+ $this->state = State::ACTIVE;
+ return $this;
+ }
+
public function value(float $price): float
{
+ if (!$this->isActive()) {
+ return $price;
+ }
if ($this->type === Type::FIXED) {
return $price + $this->amount;
}
return $price / (1 - $this->amount);
}
+ public function inverse(float $price): float
+ {
+ if (!$this->isActive()) {
+ return $price;
+ }
+ if ($this->type === Type::FIXED) {
+ return $price - $this->amount;
+ }
+ return $price * (1 - $this->amount);
+ }
+
+ public function apply(Unidad $unit, float $price, ?Broker $broker = null): float
+ {
+ if (!$this->isActive()) {
+ return $price;
+ }
+ $projectIds = array_map(fn(Proyecto $proyecto) => $proyecto->id, $this->projects());
+ if (in_array($unit->proyectoTipoUnidad->proyecto->id, $projectIds)) {
+ return $this->value($price);
+ }
+ if ($broker !== null) {
+ $brokerIds = array_map(fn(Broker $broker) => $broker->id, $this->brokers());
+ if (in_array($broker->id, $brokerIds)) {
+ return $this->value($price);
+ }
+ }
+ $typeIds = array_map(fn(Proyecto\TipoUnidad $type) => $type->id, $this->unitTypes());
+ if (in_array($unit->proyectoTipoUnidad->tipoUnidad->id, $typeIds)) {
+ return $this->value($price);
+ }
+ $lineIds = array_map(fn(Proyecto\ProyectoTipoUnidad $line) => $line->id, $this->unitLines());
+ if (in_array($unit->proyectoTipoUnidad->id, $lineIds)) {
+ return $this->value($price);
+ }
+ $unitIds = array_map(fn(Unidad $unit) => $unit->id, $this->units());
+ if (in_array($unit->id, $unitIds)) {
+ return $this->value($price);
+ }
+ return $price;
+ }
+ public function reverse(Unidad $unit, float $price, ?Broker $broker = null): float
+ {
+ if (!$this->isActive()) {
+ return $price;
+ }
+ $projectIds = array_map(fn(Proyecto $proyecto) => $proyecto->id, $this->projects());
+ if (in_array($unit->proyectoTipoUnidad->proyecto->id, $projectIds)) {
+ return $this->inverse($price);
+ }
+ if ($broker !== null) {
+ $brokerIds = array_map(fn(Broker $broker) => $broker->id, $this->brokers());
+ if (in_array($broker->id, $brokerIds)) {
+ return $this->inverse($price);
+ }
+ }
+ $typeIds = array_map(fn(Proyecto\TipoUnidad $type) => $type->id, $this->unitTypes());
+ if (in_array($unit->proyectoTipoUnidad->tipoUnidad->id, $typeIds)) {
+ return $this->inverse($price);
+ }
+ $lineIds = array_map(fn(Proyecto\ProyectoTipoUnidad $line) => $line->id, $this->unitLines());
+ if (in_array($unit->proyectoTipoUnidad->id, $lineIds)) {
+ return $this->inverse($price);
+ }
+ $unitIds = array_map(fn(Unidad $unit) => $unit->id, $this->units());
+ if (in_array($unit->id, $unitIds)) {
+ return $this->inverse($price);
+ }
+ return $price;
+ }
protected function jsonComplement(): array
{
diff --git a/app/src/Model/Venta/Reservation.php b/app/src/Model/Venta/Reservation.php
index 3a94ebf..bce17b4 100644
--- a/app/src/Model/Venta/Reservation.php
+++ b/app/src/Model/Venta/Reservation.php
@@ -7,11 +7,44 @@ use Incoviba\Model;
class Reservation extends Common\Ideal\Model
{
+ public Model\Proyecto $project;
public Model\Persona $buyer;
public DateTimeInterface $date;
public array $units = [];
public array $promotions = [];
public ?Model\Proyecto\Broker $broker = null;
+
+ public function offer(): float
+ {
+ return array_sum(array_column($this->units, 'value'));
+ }
+ public function withCommission(): float
+ {
+ $base = 0;
+ foreach ($this->units as $unit) {
+ foreach ($this->promotions as $promotion) {
+ $base += $promotion->activate()->reverse($unit['unit'], $unit['value'], $this->broker);
+ }
+ }
+ return $base;
+ }
+ public function base(): float
+ {
+ $base = $this->withCommission();
+ if ($this->broker !== null) {
+ $base = $this->broker->reverseCommission($this->project, $base, $this->date);
+ }
+ return $base;
+ }
+ public function price(): float
+ {
+ $price = 0;
+ foreach ($this->units as $unit) {
+ $price += $unit->unit->precio($this->date);
+ }
+ return $price;
+ }
+
protected array $states = [];
public function states(): array
@@ -38,13 +71,12 @@ class Reservation extends Common\Ideal\Model
$this->units[$i]['value'] = $value;
return $this;
}
- $this->units[] = [
+ $this->units[] = (object) [
'unit' => $unit,
'value' => $value,
];
return $this;
}
-
public function removeUnit(int $unit_id): self
{
if (($i = $this->findUnit($unit_id)) === null) {
@@ -54,7 +86,6 @@ class Reservation extends Common\Ideal\Model
$this->units = array_values($this->units);
return $this;
}
-
public function findUnit(int $unit_id): ?int
{
foreach ($this->units as $idx => $unit) {
@@ -64,20 +95,43 @@ class Reservation extends Common\Ideal\Model
}
return null;
}
-
public function hasUnit(int $unit_id): bool
{
return $this->findUnit($unit_id) !== null;
}
+ public function summary(): string
+ {
+ $unitSummary = array_map(function($unit) {
+ $type = $unit->unit->proyectoTipoUnidad->tipoUnidad->descripcion;
+ $cap = strtoupper(strstr($type, 0, 1));
+ return "{$cap}{$unit->unit->descripcion}";
+ }, $this->units);
+ return implode('', $unitSummary);
+ }
+
+ public function valid(): bool
+ {
+ $base = $this->base();
+ $price = $this->price();
+ return $base >= $price;
+ }
+
protected function jsonComplement(): array
{
return [
- 'buyer_rut' => $this->buyer->rut,
+ 'project_id' => $this->project->id,
+ 'buyer' => $this->buyer,
'date' => $this->date->format('Y-m-d'),
'units' => $this->units,
'promotions' => $this->promotions,
- 'broker_rut' => $this->broker?->rut,
+ 'broker' => $this->broker,
+ 'offer' => $this->offer(),
+ 'with_commission' => $this->withCommission(),
+ 'base' => $this->base(),
+ 'price' => $this->price(),
+ 'valid' => $this->valid(),
+ 'summary' => $this->summary()
];
}
}
diff --git a/app/src/Model/Venta/Reservation/Detail/Type.php b/app/src/Model/Venta/Reservation/Detail/Type.php
new file mode 100644
index 0000000..4129449
--- /dev/null
+++ b/app/src/Model/Venta/Reservation/Detail/Type.php
@@ -0,0 +1,17 @@
+ $this->value,
+ 'description' => $this->name
+ ];
+ }
+}
diff --git a/app/src/Model/Venta/Reservation/State.php b/app/src/Model/Venta/Reservation/State.php
index a24771b..64eeed5 100644
--- a/app/src/Model/Venta/Reservation/State.php
+++ b/app/src/Model/Venta/Reservation/State.php
@@ -9,17 +9,14 @@ class State extends Common\Ideal\Model
{
public Model\Venta\Reservation $reservation;
public DateTimeInterface $date;
- public int $type;
+ public Model\Venta\Reservation\State\Type $type;
protected function jsonComplement(): array
{
return [
'reservation_id' => $this->reservation->id,
'date' => $this->date->format('Y-m-d'),
- 'type' => [
- 'id' => $this->type,
- 'description' => State\Type::name($this->type)
- ]
+ 'type' => $this->type
];
}
-}
\ No newline at end of file
+}
diff --git a/app/src/Model/Venta/Reservation/State/Type.php b/app/src/Model/Venta/Reservation/State/Type.php
index 464ec24..35366ad 100644
--- a/app/src/Model/Venta/Reservation/State/Type.php
+++ b/app/src/Model/Venta/Reservation/State/Type.php
@@ -7,13 +7,15 @@ enum Type: int
case INACTIVE = 0;
case REJECTED = -1;
- public static function name(int $type): string
+ public function jsonSerialize(): array
{
- return match ($type) {
- self::ACTIVE => 'active',
- self::INACTIVE => 'inactive',
- self::REJECTED => 'rejected',
- default => throw new \InvalidArgumentException('Unexpected match value')
- };
+ return [
+ 'id' => $this->value,
+ 'description' => $this->name
+ ];
}
-}
\ No newline at end of file
+ public static function getTypes(): array
+ {
+ return [self::ACTIVE->value, self::INACTIVE->value, self::REJECTED->value];
+ }
+}
diff --git a/app/src/Repository/Persona/Datos.php b/app/src/Repository/Persona/Datos.php
index bfec12b..3d71052 100644
--- a/app/src/Repository/Persona/Datos.php
+++ b/app/src/Repository/Persona/Datos.php
@@ -29,6 +29,9 @@ class Datos extends Ideal\Repository
->register('direccion_id', (new Implement\Repository\Mapper())
->setProperty('direccion')
->setFunction(function($data) {
+ if ($data['direccion_id'] === null) {
+ return null;
+ }
return $this->direccionRepository->fetchById($data['direccion_id']);
})->setDefault(null))
->register('telefono', (new Implement\Repository\Mapper())->setFunction(function($data) {
diff --git a/app/src/Repository/Venta/Reservation.php b/app/src/Repository/Venta/Reservation.php
index 8ff13c7..c967189 100644
--- a/app/src/Repository/Venta/Reservation.php
+++ b/app/src/Repository/Venta/Reservation.php
@@ -3,14 +3,19 @@ namespace Incoviba\Repository\Venta;
use DateTimeInterface;
use DateInterval;
+use Incoviba\Common\Define;
+use Incoviba\Exception\Model\InvalidState;
use PDO;
use Incoviba\Common;
use Incoviba\Model;
use Incoviba\Repository;
+use PDOException;
class Reservation extends Common\Ideal\Repository
{
- public function __construct(Common\Define\Connection $connection, protected Repository\Persona $personaRepository,
+ public function __construct(Common\Define\Connection $connection,
+ protected Repository\Proyecto $proyectoRepository,
+ protected Repository\Persona $personaRepository,
protected Repository\Proyecto\Broker $brokerRepository,
protected Unidad $unitRepository, protected Promotion $promotionRepository)
{
@@ -25,37 +30,33 @@ class Reservation extends Common\Ideal\Repository
public function create(?array $data = null): Model\Venta\Reservation
{
$map = (new Common\Implement\Repository\MapperParser())
+ ->register('project_id', (new Common\Implement\Repository\Mapper())
+ ->setProperty('project')
+ ->setFunction(function($data) {
+ return $this->proyectoRepository->fetchById($data['project_id']);
+ }))
->register('buyer_rut', (new Common\Implement\Repository\Mapper())
->setProperty('buyer')
- ->setFunction(function($data) use ($data) {
+ ->setFunction(function($data) {
return $this->personaRepository->fetchById($data['buyer_rut']);
}))
- ->register('date', new Common\Implement\Repository\Mapper\DateTime('date'))
- ->register('broker_rut', (new Common\Implement\Repository\Mapper())
- ->setProperty('broker')
- ->setDefault(null)
- ->setFunction(function($data) use ($data) {
- try {
- return $this->brokerRepository->fetchById($data['broker_rut']);
- } catch (Common\Implement\Exception\EmptyResult) {
- return null;
- }
- }));
+ ->register('date', new Common\Implement\Repository\Mapper\DateTime('date'));
return $this->parseData(new Model\Venta\Reservation(), $data, $map);
}
public function save(Common\Define\Model $model): Model\Venta\Reservation
{
$model->id = $this->saveNew([
+ 'project_id',
'buyer_rut',
- 'date',
- 'broker_rut'
+ 'date'
], [
+ $model->project->id,
$model->buyer->rut,
- $model->date->format('Y-m-d'),
- $model->broker?->rut
+ $model->date->format('Y-m-d')
]);
$this->saveUnits($model);
$this->savePromotions($model);
+ $this->saveBroker($model);
return $model;
}
@@ -67,15 +68,20 @@ class Reservation extends Common\Ideal\Repository
*/
public function edit(Common\Define\Model $model, array $new_data): Model\Venta\Reservation
{
- return $this->update($model, ['buyer_rut', 'date', 'broker_rut'], $new_data);
+ $model = $this->update($model, ['project_id', 'buyer_rut', 'date'], $new_data);
+ $this->editUnits($model, $new_data);
+ $this->editPromotions($model, $new_data);
+ $this->editBroker($model, $new_data);
+ return $model;
}
public function load(array $data_row): Model\Venta\Reservation
{
$model = parent::load($data_row);
- $this->fetchUnits($model);
- $this->fetchPromotions($model);
+ $this->fetchUnits($model, $data_row);
+ $this->fetchBroker($model, $data_row);
+ $this->fetchPromotions($model, $data_row);
return $model;
}
@@ -93,6 +99,86 @@ class Reservation extends Common\Ideal\Repository
->where('buyer_rut = :buyer_rut AND date >= :date');
return $this->fetchOne($query, ['buyer_rut' => $buyer_rut, 'date' => $date->sub(new DateInterval('P10D'))->format('Y-m-d')]);
}
+ public function fetchByProject(int $project_id): array
+ {
+ $query = $this->connection->getQueryBuilder()
+ ->select()
+ ->from('reservations')
+ ->where('project_id = :project_id');
+ return $this->fetchMany($query, ['project_id' => $project_id]);
+ }
+
+ /**
+ * @param int $project_id
+ * @param int $state
+ * @return array
+ * @throws Common\Implement\Exception\EmptyResult
+ * @throws InvalidState
+ */
+ public function fetchState(int $project_id, int $state): array
+ {
+ if (!in_array($state, Model\Venta\Reservation\State\Type::getTypes())) {
+ throw new InvalidState();
+ }
+ $sub1 = $this->connection->getQueryBuilder()
+ ->select('MAX(id) AS id, reservation_id')
+ ->from('reservation_states')
+ ->group('reservation_id');
+ $sub2 = $this->connection->getQueryBuilder()
+ ->select('er1.*')
+ ->from('reservation_states er1')
+ ->joined("INNER JOIN ({$sub1}) er0 ON er0.id = er1.id");
+
+ $query = $this->connection->getQueryBuilder()
+ ->select()
+ ->from('reservations')
+ ->joined("INNER JOIN ({$sub2}) er ON er.reservation_id = reservations.id")
+ ->where('project_id = :project_id AND er.type = :state');
+
+ return $this->fetchMany($query, ['project_id' => $project_id,
+ 'state' => $state]);
+ }
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws Common\Implement\Exception\EmptyResult
+ */
+ public function fetchActive(int $project_id): array
+ {
+ try {
+ return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::ACTIVE->value);
+ } catch (InvalidState $exception) {
+ throw new Common\Implement\Exception\EmptyResult('Select active reservations', $exception);
+ }
+ }
+
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws Common\Implement\Exception\EmptyResult
+ */
+ public function fetchPending(int $project_id): array
+ {
+ try {
+ return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::INACTIVE->value);
+ } catch (InvalidState $exception) {
+ throw new Common\Implement\Exception\EmptyResult('Select pending reservations', $exception);
+ }
+ }
+
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws Common\Implement\Exception\EmptyResult
+ */
+ public function fetchRejected(int $project_id): array
+ {
+ try {
+ return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::REJECTED->value);
+ } catch (InvalidState $exception) {
+ throw new Common\Implement\Exception\EmptyResult('Select rejected reservations', $exception);
+ }
+ }
protected function saveUnits(Model\Venta\Reservation $reservation): void
{
@@ -101,22 +187,71 @@ class Reservation extends Common\Ideal\Repository
}
$queryCheck = $this->connection->getQueryBuilder()
->select('COUNT(id) AS cnt')
- ->from('reservation_data')
- ->where('reservation_id = :id AND type = "Unit" AND reference_id = :unit_id');
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id');
$statementCheck = $this->connection->prepare($queryCheck);
$queryInsert = $this->connection->getQueryBuilder()
->insert()
- ->into('reservation_data')
+ ->into('reservation_details')
->columns(['reservation_id', 'type', 'reference_id', 'value'])
->values([':reservation_id', ':type', ':reference_id', ':value']);
$statementInsert = $this->connection->prepare($queryInsert);
foreach ($reservation->units as $unit) {
- $statementCheck->execute(['id' => $reservation->id, 'unit_id' => $unit['unit']->id]);
+ $statementCheck->execute([
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'unit_id' => $unit->unit->id
+ ]);
$result = $statementCheck->fetch(PDO::FETCH_ASSOC);
if ($result['cnt'] > 0) {
continue;
}
- $statementInsert->execute(['reservation_id' => $reservation->id, 'type' => 'Unit', 'reference_id' => $unit['unit']->id, 'value' => $unit['value']]);
+ $statementInsert->execute([
+ 'reservation_id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'reference_id' => $unit->unit->id,
+ 'value' => $unit->value
+ ]);
+ }
+
+ $this->cleanUpUnits($reservation);
+ }
+ protected function cleanUpUnits(Model\Venta\Reservation $reservation): void
+ {
+ $this->cleanUpDetails($reservation,
+ Model\Venta\Reservation\Detail\Type::Unit->value,
+ array_map(fn($unit) => $unit->unit->id, $reservation->units));
+ }
+ protected function saveBroker(Model\Venta\Reservation &$reservation): void
+ {
+ if ($reservation->broker === null) {
+ $this->removeBroker($reservation);
+ return;
+ }
+ try {
+ $queryCheck = $this->connection->getQueryBuilder()
+ ->select('id')
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type');
+ $statementCheck = $this->connection->execute($queryCheck, [
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Broker->value
+ ]);
+ $result = $statementCheck->fetch(PDO::FETCH_ASSOC);
+ $new_id = $reservation->broker->id;
+ $reservation->broker = $this->brokerRepository->fetchById($result['id']);
+ $this->editBroker($reservation, ['broker_id' => $new_id]);
+ } catch (PDOException) {
+ $queryInsert = $this->connection->getQueryBuilder()
+ ->insert()
+ ->into('reservation_details')
+ ->columns(['reservation_id', 'type', 'reference_id'])
+ ->values([':reservation_id', ':type', ':reference_id']);
+ $this->connection->execute($queryInsert, [
+ 'reservation_id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Broker->value,
+ 'reference_id' => $reservation->broker->id
+ ]);
}
}
protected function savePromotions(Model\Venta\Reservation $reservation): void
@@ -126,60 +261,152 @@ class Reservation extends Common\Ideal\Repository
}
$queryCheck = $this->connection->getQueryBuilder()
->select('COUNT(id) AS cnt')
- ->from('reservation_data')
- ->where('reservation_id = :id AND type = "Promotion" AND reference_id = :promotion_id');
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type AND reference_id = :promotion_id');
$statementCheck = $this->connection->prepare($queryCheck);
$queryInsert = $this->connection->getQueryBuilder()
->insert()
- ->into('reservation_data')
+ ->into('reservation_details')
->columns(['reservation_id', 'type', 'reference_id'])
->values([':reservation_id', ':type', ':reference_id']);
$statementInsert = $this->connection->prepare($queryInsert);
foreach ($reservation->promotions as $promotion) {
- $statementCheck->execute(['id' => $reservation->id, 'promotion_id' => $promotion->id]);
+ $statementCheck->execute([
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Promotion->value,
+ 'promotion_id' => $promotion->id
+ ]);
$result = $statementCheck->fetch(PDO::FETCH_ASSOC);
if ($result['cnt'] > 0) {
continue;
}
- $statementInsert->execute(['reservation_id' => $reservation->id, 'type' => 'Promotion', 'reference_id' => $promotion->id]);
+ $statementInsert->execute([
+ 'reservation_id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Promotion->value,
+ 'reference_id' => $promotion->id
+ ]);
}
+
+ $this->cleanUpPromotions($reservation);
}
- protected function editUnits(Model\Venta\Reservation $reservation, array $new_data): void
+ protected function cleanUpPromotions(Model\Venta\Reservation $reservation): void
+ {
+ $this->cleanUpDetails($reservation,
+ Model\Venta\Reservation\Detail\Type::Promotion->value,
+ array_map(fn($promotion) => $promotion->id, $reservation->promotions));
+ }
+ protected function cleanUpDetails(Model\Venta\Reservation $reservation, int $type_id, array $currentIds): void
+ {
+ $queryCheck = $this->connection->getQueryBuilder()
+ ->select('COUNT(id) AS cnt')
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type');
+ $statementCheck = $this->connection->prepare($queryCheck);
+ $deleteParam = implode(', ', array_map(fn($id) => ":id$id", $currentIds));
+ $queryDelete = $this->connection->getQueryBuilder()
+ ->delete()
+ ->from('reservation_details')
+ ->where("reservation_id = :id AND type = :type AND reference_id NOT IN ({$deleteParam})");
+ $statementDelete = $this->connection->prepare($queryDelete);
+
+ try {
+ $statementCheck->execute([
+ 'id' => $reservation->id,
+ 'type' => $type_id
+ ]);
+ $result = $statementCheck->fetch(PDO::FETCH_ASSOC);
+ if ($result['cnt'] <= count($currentIds)) {
+ return;
+ }
+ $deleteIdValues = array_combine(array_map(fn($id) => "id$id", $currentIds), $currentIds);
+ $statementDelete->execute(array_merge(
+ ['id' => $reservation->id, 'type' => $type_id],
+ $deleteIdValues));
+ } catch (PDOException) {}
+ }
+
+ protected function editUnits(Model\Venta\Reservation &$reservation, array $new_data): void
{
$querySelect = $this->connection->getQueryBuilder()
->select()
- ->from('reservation_data')
- ->where('reservation_id = :id AND type = "Unit" AND reference_id = :unit_id');
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id');
$statementSelect = $this->connection->prepare($querySelect);
$queryUpdate = $this->connection->getQueryBuilder()
- ->update('reservation_data')
+ ->update('reservation_details')
->set('value = :value')
- ->where('reservation_id = :id AND type = "Unit" AND reference_id = :unit_id');
+ ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id');
$statementUpdate = $this->connection->prepare($queryUpdate);
- foreach ($new_data as $unit_id => $value) {
- $idx = $reservation->findUnit($unit_id);
+ $queryInsert = $this->connection->getQueryBuilder()
+ ->insert()
+ ->into('reservation_details')
+ ->columns(['reservation_id', 'type', 'reference_id', 'value'])
+ ->values([':reservation_id', ':type', ':reference_id', ':value']);
+ $statementInsert = $this->connection->prepare($queryInsert);
+ foreach ($new_data['units'] as $unit) {
+ $idx = $reservation->findUnit($unit['unit_id']);
if ($idx === null) {
- $reservation->addUnit($this->unitRepository->fetchById($unit_id), $value);
+ $reservation->addUnit($this->unitRepository->fetchById($unit['unit_id']), $unit['value']);
+ $statementInsert->execute([
+ 'reservation_id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'reference_id' => $unit['unit_id'],
+ 'value' => $unit['value']
+ ]);
continue;
}
- $statementSelect->execute(['id' => $reservation->id, 'unit_id' => $unit_id]);
+ $statementSelect->execute([
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'unit_id' => $unit['unit_id']
+ ]);
$result = $statementSelect->fetch(PDO::FETCH_ASSOC);
if (!$result) {
+ $reservation->addUnit($this->unitRepository->fetchById($unit['unit_id']), $unit['value']);
+ $statementInsert->execute([
+ 'reservation_id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'reference_id' => $unit['unit_id'],
+ 'value' => $unit['value']
+ ]);
continue;
}
- $statementUpdate->execute(['id' => $reservation->id, 'unit_id' => $unit_id, 'value' => $value]);
- $reservation->units[$idx]['value'] = $value;
+ $statementUpdate->execute([
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'unit_id' => $unit['unit_id'],
+ 'value' => $unit['value']
+ ]);
+ $reservation->units[$idx]['value'] = $unit['value'];
}
}
- protected function editPromotions(Model\Venta\Reservation $reservation, array $new_data): void
+ protected function editBroker(Model\Venta\Reservation &$reservation, array $new_data): void
+ {
+ if (!array_key_exists('broker_id', $new_data) or $new_data['broker_id'] === $reservation->broker->id) {
+ return;
+ }
+ try {
+ $query = $this->connection->getQueryBuilder()
+ ->update('reservation_details')
+ ->set('reference_id = :broker_id')
+ ->where('reservation_id = :id AND type = :type');
+ $this->connection->execute($query, [
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Broker->value,
+ 'broker_id' => $new_data['broker_id']
+ ]);
+ $reservation->broker = $this->brokerRepository->fetchById($new_data['broker_id']);
+ } catch (PDOException) {}
+ }
+ protected function editPromotions(Model\Venta\Reservation &$reservation, array $new_data): void
{
$querySelect = $this->connection->getQueryBuilder()
->select()
- ->from('reservation_data')
+ ->from('reservation_details')
->where('reservation_id = :id AND type = "Promotion" AND reference_id = :promotion_id');
$statementSelect = $this->connection->prepare($querySelect);
$queryUpdate = $this->connection->getQueryBuilder()
- ->update('reservation_data')
+ ->update('reservation_details')
->set('value = :value')
->where('reservation_id = :id AND type = "Promotion" AND reference_id = :promotion_id');
$statementUpdate = $this->connection->prepare($queryUpdate);
@@ -198,13 +425,26 @@ class Reservation extends Common\Ideal\Repository
$reservation->promotions[$idx] = $this->promotionRepository->fetchById($promotion_id);
}
}
- protected function fetchUnits(Model\Venta\Reservation &$reservation): Model\Venta\Reservation
+
+ protected function fetchUnits(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
+ {
+ $this->fetchSavedUnits($reservation);
+
+ if (array_key_exists('units', $new_data) and count($new_data['units']) > 0) {
+ $this->fetchUnsavedUnits($reservation, $new_data);
+ }
+ return $reservation;
+ }
+ protected function fetchSavedUnits(Model\Venta\Reservation &$reservation): Model\Venta\Reservation
{
$query = $this->connection->getQueryBuilder()
->select()
- ->from('reservation_data')
- ->where('reservation_id = :id AND type = "Unit"');
- $statement = $this->connection->execute($query, ['id' => $reservation->id]);
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = ?');
+ $statement = $this->connection->execute($query, [
+ 'id' => $reservation->id,
+ Model\Venta\Reservation\Detail\Type::Unit->value
+ ]);
while ($result = $statement->fetch(PDO::FETCH_ASSOC)) {
try {
@@ -213,13 +453,69 @@ class Reservation extends Common\Ideal\Repository
}
return $reservation;
}
- protected function fetchPromotions(Model\Venta\Reservation $reservation): Model\Venta\Reservation
+ protected function fetchUnsavedUnits(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
+ {
+ if (!array_key_exists('units', $new_data) or count($new_data['units']) > 0) {
+ return $reservation;
+ }
+ $queryCheck = $this->connection->getQueryBuilder()
+ ->select('COUNT(id) AS cnt')
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type AND reference_id = :unit_id');
+ $statementCheck = $this->connection->prepare($queryCheck);
+ foreach ($new_data['units'] as $unit) {
+ $statementCheck->execute([
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Unit->value,
+ 'unit_id' => $unit['unit_id']
+ ]);
+ $result = $statementCheck->fetch(PDO::FETCH_ASSOC);
+ if ($result['cnt'] > 0) {
+ continue;
+ }
+ try {
+ $reservation->addUnit($this->unitRepository->fetchById($unit['unit_id']), $unit['value']);
+ } catch (Common\Implement\Exception\EmptyResult) {}
+ }
+ return $reservation;
+ }
+ protected function fetchBroker(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
+ {
+ if (!array_key_exists('broker_id', $new_data)) {
+ $query = $this->connection->getQueryBuilder()
+ ->select('reference_id')
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type');
+ try {
+ $statement =$this->connection->execute($query, [
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Broker->value
+ ]);
+ $result = $statement->fetch(PDO::FETCH_ASSOC);
+ $reservation->broker = $this->brokerRepository->fetchById($result['reference_id']);
+ } catch (PDOException) {}
+
+ return $reservation;
+ }
+ try {
+ $reservation->broker = $this->brokerRepository->fetchById($new_data['broker_id']);
+ } catch (Common\Implement\Exception\EmptyResult) {}
+ return $reservation;
+ }
+ protected function fetchPromotions(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
+ {
+ $this->fetchSavedPromotions($reservation);
+ $this->fetchUnsavedPromotions($reservation, $new_data);
+ return $reservation;
+ }
+ protected function fetchSavedPromotions(Model\Venta\Reservation &$reservation): Model\Venta\Reservation
{
$query = $this->connection->getQueryBuilder()
->select()
- ->from('reservation_data')
- ->where('type = "Promotion" AND reservation_id = :id');
- $statement = $this->connection->execute($query, ['id' => $reservation->id]);
+ ->from('reservation_details')
+ ->where('type = :type AND reservation_id = :id');
+ $statement = $this->connection->execute($query,
+ ['id' => $reservation->id, 'type' => Model\Venta\Reservation\Detail\Type::Promotion->value]);
while ($result = $statement->fetch(PDO::FETCH_ASSOC)) {
try {
$reservation->promotions []= $this->promotionRepository->fetchById($result['reference_id']);
@@ -227,4 +523,43 @@ class Reservation extends Common\Ideal\Repository
}
return $reservation;
}
+ protected function fetchUnsavedPromotions(Model\Venta\Reservation &$reservation, array $new_data): Model\Venta\Reservation
+ {
+ if (!array_key_exists('promotions', $new_data) or count($new_data['promotions']) > 0) {
+ return $reservation;
+ }
+ $queryCheck = $this->connection->getQueryBuilder()
+ ->select('COUNT(id) AS cnt')
+ ->from('reservation_details')
+ ->where('type = :type AND reservation_id = :id AND reference_id = :promotion_id');
+ $statementCheck = $this->connection->prepare($queryCheck);
+ foreach ($new_data['promotions'] as $promotion) {
+ $statementCheck->execute([
+ 'id' => $reservation->id,
+ 'promotion_id' => $promotion
+ ]);
+ $result = $statementCheck->fetch(PDO::FETCH_ASSOC);
+ if ($result['cnt'] > 0) {
+ continue;
+ }
+ try {
+ $reservation->promotions []= $this->promotionRepository->fetchById($promotion);
+ } catch (Common\Implement\Exception\EmptyResult) {}
+ }
+ return $reservation;
+ }
+ protected function removeBroker(Model\Venta\Reservation &$reservation): void
+ {
+ try {
+ $query = $this->connection->getQueryBuilder()
+ ->delete()
+ ->from('reservation_details')
+ ->where('reservation_id = :id AND type = :type');
+ $this->connection->execute($query, [
+ 'id' => $reservation->id,
+ 'type' => Model\Venta\Reservation\Detail\Type::Broker->value
+ ]);
+ $reservation->broker = null;
+ } catch (PDOException) {}
+ }
}
diff --git a/app/src/Repository/Venta/Reservation/State.php b/app/src/Repository/Venta/Reservation/State.php
index 0a98a6d..9a34efa 100644
--- a/app/src/Repository/Venta/Reservation/State.php
+++ b/app/src/Repository/Venta/Reservation/State.php
@@ -22,7 +22,7 @@ class State extends Common\Ideal\Repository
$map = (new Common\Implement\Repository\MapperParser(['type']))
->register('reservation_id', (new Common\Implement\Repository\Mapper())
->setProperty('reservation')
- ->setFunction(function($data) use ($data) {
+ ->setFunction(function($data) {
return $this->reservationRepository->fetchById($data['reservation_id']);
}))
->register('date', new Common\Implement\Repository\Mapper\DateTime('date'));
diff --git a/app/src/Service/Persona.php b/app/src/Service/Persona.php
index bfabeca..625e6d1 100644
--- a/app/src/Service/Persona.php
+++ b/app/src/Service/Persona.php
@@ -35,7 +35,7 @@ class Persona extends Ideal\Service
/**
* @param int $rut
* @return Model\Persona
- * @throws Read|Create
+ * @throws Read
*/
public function getById(int $rut): Model\Persona
{
@@ -44,7 +44,11 @@ class Persona extends Ideal\Service
} catch (Implement\Exception\EmptyResult) {
try {
$this->propietarioRepository->fetchById($rut);
- return $this->add(compact('rut'));
+ try {
+ return $this->add(compact('rut'));
+ } catch (Create $exception) {
+ throw new Read(__CLASS__, $exception);
+ }
} catch (Implement\Exception\EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}
diff --git a/app/src/Service/Valor.php b/app/src/Service/Valor.php
index 7789e84..1454611 100644
--- a/app/src/Service/Valor.php
+++ b/app/src/Service/Valor.php
@@ -4,7 +4,7 @@ namespace Incoviba\Service;
use DateTimeInterface;
use DateTimeImmutable;
use DateMalformedStringException;
-use function PHPUnit\Framework\countOf;
+use Incoviba\Service\Valor\Phone;
class Valor
{
@@ -40,6 +40,14 @@ class Valor
}
return $value / $this->ufService->get($date);
}
+ public function phone(): Phone
+ {
+ return new Phone();
+ }
+ public function telefono(): Phone
+ {
+ return $this->phone();
+ }
protected function getDateTime(null|string|DateTimeInterface $date): DateTimeInterface
{
diff --git a/app/src/Service/Valor/Phone.php b/app/src/Service/Valor/Phone.php
new file mode 100644
index 0000000..3fc4eec
--- /dev/null
+++ b/app/src/Service/Valor/Phone.php
@@ -0,0 +1,28 @@
+\d{2})?(?=\d)(?=\d{4})(?=\d{4})/', $phone);
+ $output = [];
+ if (array_key_exists('country', $parts)) {
+ $output [] = "+{$parts[0]}";
+ }
+ $output [] = $parts[1] ?? '';
+ $output [] = $parts[2] ?? '';
+ $output [] = $parts[3] ?? '';
+ return implode(' ', $output);
+ }
+}
diff --git a/app/src/Service/Venta/Promotion.php b/app/src/Service/Venta/Promotion.php
index 6dc0194..052be9b 100644
--- a/app/src/Service/Venta/Promotion.php
+++ b/app/src/Service/Venta/Promotion.php
@@ -48,6 +48,20 @@ class Promotion extends Ideal\Service
}
}
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws Exception\ServiceAction\Read
+ */
+ public function getByProject(int $project_id): array
+ {
+ try {
+ return array_map([$this, 'process'], $this->promotionRepository->fetchByProject($project_id));
+ } catch (Implement\Exception\EmptyResult $exception) {
+ throw new Exception\ServiceAction\Read(__CLASS__, $exception);
+ }
+ }
+
/**
* @param int $contract_id
* @return array
diff --git a/app/src/Service/Venta/Propietario.php b/app/src/Service/Venta/Propietario.php
index 9ea89e5..cec026b 100644
--- a/app/src/Service/Venta/Propietario.php
+++ b/app/src/Service/Venta/Propietario.php
@@ -1,22 +1,24 @@
id;
}
$filteredData = $this->propietarioRepository->filterData($data);
+ if (array_key_exists('telefono', $filteredData)) {
+ $filteredData['telefono'] = $this->valorService->telefono()->toDatabase($filteredData['telefono']);
+ }
try {
return $this->propietarioRepository->edit($propietario, $filteredData);
} catch (PDOException | EmptyResult $exception) {
@@ -85,6 +90,10 @@ class Propietario extends Service
]);
$filtered_data = array_intersect_key($data, $fields);
+ if (array_key_exists('telefono', $filtered_data)) {
+ $filtered_data['telefono'] = $this->valorService->telefono()->toDatabase($filtered_data['telefono']);
+ }
+
try {
$propietario = $this->propietarioRepository->fetchById($data['rut']);
$edits = [];
@@ -95,6 +104,7 @@ class Propietario extends Service
} catch (EmptyResult) {
try {
$propietario = $this->propietarioRepository->create($filtered_data);
+ $this->logger->info('Propietario', ['propietario' => $propietario]);
$propietario = $this->propietarioRepository->save($propietario);
} catch (PDOException $exception) {
throw new Create(__CLASS__, $exception);
diff --git a/app/src/Service/Venta/Reservation.php b/app/src/Service/Venta/Reservation.php
index 9ceca60..166f0d8 100644
--- a/app/src/Service/Venta/Reservation.php
+++ b/app/src/Service/Venta/Reservation.php
@@ -14,7 +14,9 @@ use Incoviba\Repository;
class Reservation extends Ideal\Service\API
{
- public function __construct(LoggerInterface $logger, protected Repository\Venta\Reservation $reservationRepository)
+ public function __construct(LoggerInterface $logger,
+ protected Repository\Venta\Reservation $reservationRepository,
+ protected Repository\Venta\Reservation\State $stateRepository)
{
parent::__construct($logger);
}
@@ -22,11 +24,19 @@ class Reservation extends Ideal\Service\API
public function getAll(null|string|array $order = null): array
{
try {
- return $this->reservationRepository->fetchAll($order);
+ return array_map([$this, 'process'], $this->reservationRepository->fetchAll($order));
} catch (Implement\Exception\EmptyResult) {
return [];
}
}
+ public function getByProject(int $project_id): array
+ {
+ try {
+ return array_map([$this, 'process'], $this->reservationRepository->fetchByProject($project_id));
+ } catch (Implement\Exception\EmptyResult $exception) {
+ throw new ServiceAction\Read(__CLASS__, $exception);
+ }
+ }
public function get(int $id): Model\Venta\Reservation
{
@@ -37,6 +47,48 @@ class Reservation extends Ideal\Service\API
}
}
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws ServiceAction\Read
+ */
+ public function getActive(int $project_id): array
+ {
+ try {
+ return array_map([$this, 'process'], $this->reservationRepository->fetchActive($project_id));
+ } catch (Implement\Exception\EmptyResult $exception) {
+ throw new ServiceAction\Read(__CLASS__, $exception);
+ }
+ }
+
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws ServiceAction\Read
+ */
+ public function getPending(int $project_id): array
+ {
+ try {
+ return array_map([$this, 'process'], $this->reservationRepository->fetchPending($project_id));
+ } catch (Implement\Exception\EmptyResult $exception) {
+ throw new ServiceAction\Read(__CLASS__, $exception);
+ }
+ }
+
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws ServiceAction\Read
+ */
+ public function getRejected(int $project_id): array
+ {
+ try {
+ return array_map([$this, 'process'], $this->reservationRepository->fetchRejected($project_id));
+ } catch (Implement\Exception\EmptyResult $exception) {
+ throw new ServiceAction\Read(__CLASS__, $exception);
+ }
+ }
+
public function add(array $data): Model\Venta\Reservation
{
try {
@@ -76,6 +128,12 @@ class Reservation extends Ideal\Service\API
}
protected function process(Define\Model $model): Model\Venta\Reservation
{
+ $model->addFactory('states', new Implement\Repository\Factory()
+ ->setArgs(['reservation_id' => $model->id])
+ ->setCallable(function(int $reservation_id) {
+ return $this->stateRepository->fetchByReservation($reservation_id);
+ })
+ );
return $model;
}
}