diff --git a/app/resources/database/migrations/20250318231051_create_promotion_projects.php b/app/resources/database/migrations/20250318231051_create_promotion_projects.php
new file mode 100644
index 0000000..0381c5f
--- /dev/null
+++ b/app/resources/database/migrations/20250318231051_create_promotion_projects.php
@@ -0,0 +1,29 @@
+table('promotion_tables')
+ ->addColumn('promotion_id', 'integer', ['signed' => false, 'null' => false])
+ ->addColumn('project_id', 'integer', ['signed' => false, 'null' => false])
+ ->addForeignKey('promotion_id', 'promotions', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->addForeignKey('project_id', 'proyecto', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->create();
+ }
+}
diff --git a/app/resources/database/migrations/20250318233223_create_promotion_contract_units.php b/app/resources/database/migrations/20250318233223_create_promotion_contract_units.php
new file mode 100644
index 0000000..a6c52c0
--- /dev/null
+++ b/app/resources/database/migrations/20250318233223_create_promotion_contract_units.php
@@ -0,0 +1,31 @@
+table('promotion_contract_units')
+ ->addColumn('promotion_id', 'integer', ['signed' => false, 'null' => false])
+ ->addColumn('contract_id', 'integer', ['signed' => false, 'null' => false])
+ ->addColumn('unit_id', 'integer', ['signed' => false, 'null' => false])
+ ->addForeignKey('promotion_id', 'promotions', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->addForeignKey('contract_id', 'broker_contracts', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->addForeignKey('unit_id', 'unidad', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
+ ->create();
+ }
+}
diff --git a/app/resources/routes/ventas/promotions.php b/app/resources/routes/ventas/promotions.php
index 43ddf79..bad3850 100644
--- a/app/resources/routes/ventas/promotions.php
+++ b/app/resources/routes/ventas/promotions.php
@@ -4,3 +4,6 @@ use Incoviba\Controller\Ventas\Promotions;
$app->group('/promotions', function($app) {
$app->get('[/]', Promotions::class);
});
+$app->group('/promotion/{promotion_id}', function($app) {
+ $app->get('[/]', [Promotions::class, 'show']);
+});
diff --git a/app/resources/views/ventas/promotions/base.blade.php b/app/resources/views/ventas/promotions/base.blade.php
index 0382e20..5901b96 100644
--- a/app/resources/views/ventas/promotions/base.blade.php
+++ b/app/resources/views/ventas/promotions/base.blade.php
@@ -1,9 +1,21 @@
@extends('layout.base')
+@section('page_title')
+ @hasSection('promotions_title')
+ Promoción - @yield('promotions_title')
+ @else
+ Promociones
+ @endif
+@endsection
+
@section('page_content')
@yield('promotions_content')
diff --git a/app/resources/views/ventas/promotions/show.blade.php b/app/resources/views/ventas/promotions/show.blade.php
new file mode 100644
index 0000000..0bb81e2
--- /dev/null
+++ b/app/resources/views/ventas/promotions/show.blade.php
@@ -0,0 +1,84 @@
+@extends('ventas.promotions.base')
+
+@section('promotions_title')
+ {{ $promotion->description }}
+@endsection
+
+@section('promotions_header')
+ {{ $promotion->description }}
+@endsection
+
+@section('promotions_content')
+
+
+
+
+ {{ ucwords($promotion->type->name()) }} {{ ($promotion->type === Incoviba\Model\Venta\Promotion\Type::FIXED) ? $format->ufs($promotion->amount) : $format->percent($promotion->amount, 2, true) }}
+
+
+ {{ $promotion->startDate->format('d-m-Y') }} -> {!! $promotion->endDate?->format('d-m-Y') ?? '∞' !!}
+
+
Válido hasta: {!! $promotion->validUntil?->format('d-m-Y') ?? '∞' !!}
+
+
+
+
+
+
+ Proyecto |
+ Operador |
+ Unidad |
+
+
+ |
+
+
+ |
+ Tipo |
+ |
+
+
+
+ @if (count($promotion->projects()) > 0)
+ @foreach ($promotion->projects() as $project)
+
+ {{ $project->descripcion }} |
+ Todos los Operadores |
+ Todas las Unidades |
+
+ @endforeach
+ @endif
+ @if (count($promotion->contracts()) > 0)
+ @foreach($promotion->contracts() as $contract)
+
+ {{ $contract->project->descripcion }} |
+ {{ $contract->broker->name }} |
+ Todas las Unidades |
+
+ @endforeach
+ @endif
+ @if (count($promotion->units()) > 0)
+ @foreach($promotion->units() as $unit)
+
+ {{ $unit->project->descripcion }} |
+ Todos los Operadores |
+ {{ $unit->proyectoTipoUnidad->tipoUnidad->descripcion }} |
+ {{ $unit->descripcion }} |
+
+ @endforeach
+ @endif
+ @if (count($promotion->contractUnits()) > 0)
+ @foreach($promotion->contractUnits() as $contractUnit)
+
+ {{ $contractUnit->contract->project->descripcion }} |
+ {{ $contractUnit->contract->broker->name }} |
+ {{ $contractUnit->unidad->proyectoTipoUnidad->tipoUnidad->descripcion }} |
+ {{ $contractUnit->unidad->descripcion }} |
+
+ @endforeach
+ @endif
+
+
+@endsection
diff --git a/app/resources/views/ventas/promotions/show/add_modal.blade.php b/app/resources/views/ventas/promotions/show/add_modal.blade.php
new file mode 100644
index 0000000..86d4bef
--- /dev/null
+++ b/app/resources/views/ventas/promotions/show/add_modal.blade.php
@@ -0,0 +1,2 @@
+
+
diff --git a/app/setup/setups/logs.php b/app/setup/setups/logs.php
index 217d91c..d0f7e64 100644
--- a/app/setup/setups/logs.php
+++ b/app/setup/setups/logs.php
@@ -9,19 +9,25 @@ return [
Psr\Log\LoggerInterface::class => function(ContainerInterface $container) {
return new Monolog\Logger('incoviba', [
new Monolog\Handler\FilterHandler(
- (new Monolog\Handler\RotatingFileHandler('/logs/error.log', 10))
+ ($container->has('ENVIRONMENT') and $container->get('ENVIRONMENT') === 'development')
+ ? (new Monolog\Handler\StreamHandler('/logs/error.log'))
+ ->setFormatter($container->get(Monolog\Formatter\LineFormatter::class))
+ : (new Monolog\Handler\RotatingFileHandler('/logs/error.log', 10))
->setFormatter($container->get(Monolog\Formatter\LineFormatter::class)),
Monolog\Level::Error,
Monolog\Level::Error
),
new Monolog\Handler\FilterHandler(
- (new Monolog\Handler\RotatingFileHandler('/logs/critical.log', 10))
+ ($container->has('ENVIRONMENT') and $container->get('ENVIRONMENT') === 'development')
+ ? (new Monolog\Handler\StreamHandler('/logs/critical.log'))
+ ->setFormatter($container->get(Monolog\Formatter\LineFormatter::class))
+ : (new Monolog\Handler\RotatingFileHandler('/logs/critical.log', 10))
->setFormatter($container->get(Monolog\Formatter\LineFormatter::class)),
Monolog\Level::Critical
),
new Monolog\Handler\FilterHandler(
($container->has('ENVIRONMENT') and $container->get('ENVIRONMENT') === 'development')
- ? (new Monolog\Handler\RotatingFileHandler('/logs/debug.log', 10))
+ ? (new Monolog\Handler\StreamHandler('/logs/debug.log'))
->setFormatter($container->get(Monolog\Formatter\LineFormatter::class))
: new Monolog\Handler\RedisHandler($container->get(Predis\ClientInterface::class), 'logs:notices'),
Monolog\Level::Debug,
@@ -29,7 +35,7 @@ return [
),
new Monolog\Handler\FilterHandler(
($container->has('ENVIRONMENT') and $container->get('ENVIRONMENT') === 'development')
- ? (new Monolog\Handler\RotatingFileHandler('/logs/notices.log', 10))
+ ? (new Monolog\Handler\StreamHandler('/logs/notices.log'))
->setFormatter($container->get(Monolog\Formatter\LineFormatter::class))
: (new Incoviba\Common\Implement\Log\MySQLHandler($container->get(Incoviba\Common\Define\Connection::class)))
->setFormatter(new Incoviba\Common\Implement\Log\PDOFormatter()),
diff --git a/app/src/Controller/Ventas/Promotions.php b/app/src/Controller/Ventas/Promotions.php
index f3651a1..b458650 100644
--- a/app/src/Controller/Ventas/Promotions.php
+++ b/app/src/Controller/Ventas/Promotions.php
@@ -10,10 +10,20 @@ use Incoviba\Service;
class Promotions extends Ideal\Controller
{
- public function __invoke(ServerRequestInterface $request, ResponseInterface $response, View $view, Service\Venta\Promotion $promotionService): ResponseInterface
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, View $view,
+ Service\Venta\Promotion $promotionService): ResponseInterface
{
$promotions = $promotionService->getAll('description');
return $view->render($response, 'ventas.promotions', ['promotions' => $promotions]);
}
+ public function show(ServerRequestInterface $request, ResponseInterface $response, View $view,
+ Service\Venta\Promotion $promotionService, int $promotion_id): ResponseInterface
+ {
+ $promotion = null;
+ try {
+ $promotion = $promotionService->getById($promotion_id);
+ } catch (ServiceAction\Read) {}
+ return $view->render($response, 'ventas.promotions.show', ['promotion' => $promotion]);
+ }
}
diff --git a/app/src/Model/Venta/Promotion.php b/app/src/Model/Venta/Promotion.php
index dc1660e..f987256 100644
--- a/app/src/Model/Venta/Promotion.php
+++ b/app/src/Model/Venta/Promotion.php
@@ -17,6 +17,15 @@ class Promotion extends Common\Ideal\Model
public Type $type;
public State $state = State::ACTIVE;
+ protected array $projects;
+ public function projects(): array
+ {
+ if (empty($this->projects)) {
+ $this->projects = $this->runFactory('projects');
+ }
+ return $this->projects;
+ }
+
protected array $contracts;
public function contracts(): array
{
@@ -35,6 +44,15 @@ class Promotion extends Common\Ideal\Model
return $this->units;
}
+ protected array $contractUnits;
+ public function contractUnits(): array
+ {
+ if (empty($this->contractUnits)) {
+ $this->contractUnits = $this->runFactory('contractUnits');
+ }
+ return $this->contractUnits;
+ }
+
public function value(float $price): float
{
if ($this->type === Type::FIXED) {
@@ -53,6 +71,7 @@ class Promotion extends Common\Ideal\Model
'valid_until' => $this->validUntil?->format('Y-m-d'),
'type' => $this->type,
'state' => $this->state,
+ 'projects' => $this->projects() ?? [],
'contracts' => $this->contracts() ?? [],
'units' => $this->units() ?? []
];
diff --git a/app/src/Repository/Proyecto.php b/app/src/Repository/Proyecto.php
index 561868c..6bf6364 100644
--- a/app/src/Repository/Proyecto.php
+++ b/app/src/Repository/Proyecto.php
@@ -164,6 +164,21 @@ class Proyecto extends Ideal\Repository
->where('inmobiliaria = ?');
return $this->fetchMany($query, [$inmobiliaria_rut]);
}
+
+ /**
+ * @param int $promotion_id
+ * @return array
+ * @throws Implement\Exception\EmptyResult
+ */
+ public function fetchByPromotion(int $promotion_id): array
+ {
+ $query = $this->connection->getQueryBuilder()
+ ->select('a.*')
+ ->from("{$this->getTable()} a")
+ ->joined('INNER JOIN promotion_projects pp ON pp.project_id = a.id')
+ ->where('pp.promotion_id = :promotion_id');
+ return $this->fetchMany($query, ['promotion_id' => $promotion_id]);
+ }
/*public function fetchSuperficieVendido(int $proyecto_id): float
{
diff --git a/app/src/Repository/Venta/Promotion.php b/app/src/Repository/Venta/Promotion.php
index 8517d08..fdedcbe 100644
--- a/app/src/Repository/Venta/Promotion.php
+++ b/app/src/Repository/Venta/Promotion.php
@@ -1,6 +1,8 @@
where('pc.contract_id = :contract_id AND pu.unit_id = :unit_id');
return $this->fetchOne($query, ['contract_id' => $contract_id, 'unit_id' => $unit_id]);
}
+
+ /**
+ * @param int $promotion_id
+ * @return array
+ * @throws Common\Implement\Exception\EmptyResult
+ */
+ public function fetchContractUnitsByPromotion(int $promotion_id): array
+ {
+ $query = $this->connection->getQueryBuilder()
+ ->select('contracts.id, unidad.id')
+ ->from("{$this->getTable()} a")
+ ->joined('INNER JOIN promotion_contract_units pcu ON pcu.promotion_id = a.id')
+ ->joined('INNER JOIN unidad ON unidad.id = pcu.unit_id')
+ ->joined('INNER JOIN contracts ON contracts.id = pcu.contract_id')
+ ->where('a.id = :promotion_id');
+ try {
+ $result = $this->connection->execute($query, ['promotion_id' => $promotion_id])->fetchAll(PDO::FETCH_ASSOC);
+ if (empty($result)) {
+ throw new Common\Implement\Exception\EmptyResult($query);
+ }
+ return $result;
+ } catch (PDOException $exception) {
+ throw new Common\Implement\Exception\EmptyResult($query, $exception);
+ }
+ }
}
diff --git a/app/src/Service/Venta/Promotion.php b/app/src/Service/Venta/Promotion.php
index 6e18bfa..efc726c 100644
--- a/app/src/Service/Venta/Promotion.php
+++ b/app/src/Service/Venta/Promotion.php
@@ -15,6 +15,7 @@ class Promotion extends Ideal\Service
{
public function __construct(LoggerInterface $logger,
protected Repository\Venta\Promotion $promotionRepository,
+ protected Repository\Proyecto $projectRepository,
protected Repository\Proyecto\Broker\Contract $contractRepository,
protected Repository\Venta\Unidad $unidadRepository)
{
@@ -135,11 +136,50 @@ class Promotion extends Ideal\Service
protected function process(Model\Venta\Promotion $model): Model\Venta\Promotion
{
+ $model->addFactory('projects', (new Implement\Repository\Factory())
+ ->setCallable(function($promotion_id) {
+ try {
+ return $this->projectRepository->fetchByPromotion($promotion_id);
+ } catch (Implement\Exception\EmptyResult) {
+ return [];
+ }
+ })
+ ->setArgs(['promotion_id' => $model->id]));
$model->addFactory('contracts', (new Implement\Repository\Factory())
- ->setCallable([$this->contractRepository, 'fetchByPromotion'])
+ ->setCallable(function($promotion_id) {
+ try {
+ return $this->contractRepository->fetchByPromotion($promotion_id);
+ } catch (Implement\Exception\EmptyResult) {
+ return [];
+ }
+ })
->setArgs(['promotion_id' => $model->id]));
$model->addFactory('units', (new Implement\Repository\Factory())
- ->setCallable([$this->unidadRepository, 'fetchByPromotion'])
+ ->setCallable(function($promotion_id) {
+ try {
+ return $this->unidadRepository->fetchByPromotion($promotion_id);
+ } catch (Implement\Exception\EmptyResult) {
+ return [];
+ }
+ })
+ ->setArgs(['promotion_id' => $model->id]));
+ $model->addFactory('contractUnits', (new Implement\Repository\Factory())
+ ->setCallable(function($promotion_id) {
+ try {
+ $ids = $this->promotionRepository->fetchContractUnitsByPromotion($promotion_id);
+ $contractUnits = [];
+ foreach ($ids as $id) {
+ try {
+ $contract = $this->contractRepository->fetchById($id['contract_id']);
+ $unidad = $this->unidadRepository->fetchById($id['unidad_id']);
+ $contractUnits[]= (object) ['contract' => $contract, 'unit' => $unidad];
+ } catch (Implement\Exception\EmptyResult) {}
+ }
+ return $contractUnits;
+ } catch (Implement\Exception\EmptyResult) {
+ return [];
+ }
+ })
->setArgs(['promotion_id' => $model->id]));
return $model;
}