From 5a60e79e47e9d223393cde595209f49b4b4c3bea Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Thu, 11 Sep 2025 15:05:07 -0300 Subject: [PATCH] FIX: Fetch by state --- .../routes/api/ventas/reservations.php | 3 + .../views/ventas/reservations.blade.php | 89 ++++++++++++++++--- .../Controller/API/Ventas/Reservations.php | 29 ++++++ .../Model/Venta/Reservation/State/Type.php | 1 + app/src/Repository/Venta/Reservation.php | 14 +-- app/src/Service/Venta/Reservation.php | 86 +++++++++++++----- 6 files changed, 184 insertions(+), 38 deletions(-) diff --git a/app/resources/routes/api/ventas/reservations.php b/app/resources/routes/api/ventas/reservations.php index d0dedcd..31ce261 100644 --- a/app/resources/routes/api/ventas/reservations.php +++ b/app/resources/routes/api/ventas/reservations.php @@ -12,7 +12,10 @@ $app->group('/reservations', function($app) { }); $app->post('/reservation/add[/]', [Reservations::class, 'addOne']); $app->group('/reservation/{reservation_id}', function($app) { + $app->get('/approve[/]', [Reservations::class, 'approve']); + $app->get('/reject[/]', [Reservations::class, 'reject']); $app->post('/edit[/]', [Reservations::class, 'edit']); + $app->delete('/remove[/]', [Reservations::class, 'remove']); $app->delete('[/]', [Reservations::class, 'delete']); $app->get('[/]', [Reservations::class, 'get']); }); diff --git a/app/resources/views/ventas/reservations.blade.php b/app/resources/views/ventas/reservations.blade.php index a5e52c6..a7f2cc4 100644 --- a/app/resources/views/ventas/reservations.blade.php +++ b/app/resources/views/ventas/reservations.blade.php @@ -257,7 +257,6 @@ date: null, ufs: null } - columnNames = [] reservations = [] @@ -294,10 +293,9 @@ return this } } - columnsData() { return this.reservations.map(reservation => { - const date = new Date(Date.parse(reservation.fecha) + 24 * 60 * 60 * 1000) + const date = new Date(Date.parse(reservation.date) + 24 * 60 * 60 * 1000) return { id: reservation.id, unidades: reservation.summary, @@ -309,7 +307,6 @@ } }) } - draw() { if (this.reservations.length === 0) { this.empty() @@ -332,8 +329,9 @@ tbody.appendChild(tr) }) this.show() - } + this.watch() + } drawActions(id) { return ` @@ -345,7 +343,23 @@ ` } - + watch() { + if (Object.keys(this.actions).length === 0) { + return + } + const actionNames = Object.keys(this.actions) + const tbody = this.component.querySelector('tbody') + const trs = tbody.querySelectorAll('tr') + trs.forEach(tr => { + actionNames.forEach(actionName => { + const button = tr.querySelector(`button.${actionName}`) + if (!button) { + return + } + button.addEventListener('click', this.actions[actionName].bind(this)) + }) + }) + } empty() { const tbody = this.component.querySelector('tbody') tbody.innerHTML = '' @@ -356,14 +370,38 @@ this.show() } - show() { this.component.style.display = this.display.reservations } - hide() { this.component.style.display = 'none' } + send = { + get(url) { + return APIClient.fetch(url).then(response => response.json()).then(json => { + if (json.success) { + window.location.reload() + } + }) + }, + post(url, body) { + const method = 'post' + return APIClient.fetch(url, {method, body}).then(response => response.json()).then(json => { + if (json.success) { + window.location.reload() + } + }) + }, + delete(url) { + const method = 'delete' + return APIClient.fetch(url, {method}).then(response => response.json()).then(json => { + if (json.success) { + window.location.reload() + } + }) + } + } + actions = {} } class ActiveReservations extends Reservations { @@ -378,7 +416,6 @@ return row }) } - drawActions(id) { return ` @@ -390,12 +427,44 @@ ` } + actions = { + edit: event => { + event.preventDefault() + const id = event.currentTarget.dataset.id + reservations.components.modals.edit.load(id) + return false + }, + remove: event => { + event.preventDefault() + const id = event.currentTarget.dataset.id + const url = `{{ $urls->api }}/ventas/reservation/${id}/remove` + this.send.delete(url) + return false + } + } } class PendingReservations extends Reservations { constructor({component_id, formatters = {date, ufs}}) { super({component_id, formatters}) } + + actions = { + approve: event => { + event.preventDefault() + const id = event.currentTarget.dataset.id + const url = `{{ $urls->api }}/ventas/reservation/${id}/approve` + this.send.get(url) + return false + }, + reject: event => { + event.preventDefault() + const id = event.currentTarget.dataset.id + const url = `{{ $urls->api }}/ventas/reservation/${id}/reject` + this.send.get(url) + return false + } + } } class RejectedReservations extends Reservations { @@ -411,10 +480,10 @@ return data[idx] }) } - drawActions(id) { return '' } + watch() {} } const reservations = { diff --git a/app/src/Controller/API/Ventas/Reservations.php b/app/src/Controller/API/Ventas/Reservations.php index 87ef6b4..607be0f 100644 --- a/app/src/Controller/API/Ventas/Reservations.php +++ b/app/src/Controller/API/Ventas/Reservations.php @@ -181,4 +181,33 @@ class Reservations } catch (ServiceAction\Read) {} return $this->withJson($response, $output); } + + public function approve(ServerRequestInterface $request, ResponseInterface $response, + Service\Venta\Reservation $reservationService, int $reservation_id): ResponseInterface + { + $output = [ + 'reservation_id' => $reservation_id, + 'success' => false, + ]; + try { + $reservation = $reservationService->get($reservation_id); + $reservationService->approve($reservation); + $output['success'] = true; + } catch (ServiceAction\Read | ServiceAction\Update) {} + return $this->withJson($response, $output); + } + public function reject(ServerRequestInterface $request, ResponseInterface $response, + Service\Venta\Reservation $reservationService, int $reservation_id): ResponseInterface + { + $output = [ + 'reservation_id' => $reservation_id, + 'success' => false, + ]; + try { + $reservation = $reservationService->get($reservation_id); + $reservationService->reject($reservation); + $output['success'] = true; + } catch (ServiceAction\Read | ServiceAction\Update) {} + return $this->withJson($response, $output); + } } diff --git a/app/src/Model/Venta/Reservation/State/Type.php b/app/src/Model/Venta/Reservation/State/Type.php index 352b72d..96d60e5 100644 --- a/app/src/Model/Venta/Reservation/State/Type.php +++ b/app/src/Model/Venta/Reservation/State/Type.php @@ -7,6 +7,7 @@ enum Type: int case ACTIVE = 1; case PROMISED = 2; case REJECTED = -1; + case CANCELLED = -2; public function jsonSerialize(): array { diff --git a/app/src/Repository/Venta/Reservation.php b/app/src/Repository/Venta/Reservation.php index bb6cdd9..45f005f 100644 --- a/app/src/Repository/Venta/Reservation.php +++ b/app/src/Repository/Venta/Reservation.php @@ -134,11 +134,10 @@ class Reservation extends Common\Ideal\Repository ->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'); - + ->select('a.*') + ->from("{$this->getTable()} a") + ->joined("INNER JOIN ({$sub2}) er ON er.reservation_id = a.id") + ->where('a.project_id = :project_id AND er.type = :state'); return $this->fetchMany($query, ['project_id' => $project_id, 'state' => $state]); } @@ -178,7 +177,10 @@ class Reservation extends Common\Ideal\Repository public function fetchRejected(int $project_id): array { try { - return $this->fetchState($project_id, Model\Venta\Reservation\State\Type::REJECTED->value); + return [ + ...$this->fetchState($project_id, Model\Venta\Reservation\State\Type::REJECTED->value), + ...$this->fetchState($project_id, Model\Venta\Reservation\State\Type::CANCELLED->value) + ]; } catch (InvalidState $exception) { throw new Common\Implement\Exception\EmptyResult('Select rejected reservations', $exception); } diff --git a/app/src/Service/Venta/Reservation.php b/app/src/Service/Venta/Reservation.php index 968f76d..a12f442 100644 --- a/app/src/Service/Venta/Reservation.php +++ b/app/src/Service/Venta/Reservation.php @@ -132,6 +132,9 @@ class Reservation extends Ideal\Service\API } catch (ServiceAction\Read) {} } } catch (Implement\Exception\EmptyResult) { + if (!$this->reservationRepository->getConnection()->getPDO()->inTransaction()) { + $this->reservationRepository->getConnection()->getPDO()->beginTransaction(); + } $buyerData = []; foreach ($data as $key => $value) { if (!str_starts_with($key, 'buyer_')) { @@ -147,34 +150,38 @@ class Reservation extends Ideal\Service\API $reservation = $this->reservationRepository->create($reservationData); $reservation = $this->reservationRepository->save($reservation); - try { - $stateType = Model\Venta\Reservation\State\Type::ACTIVE; - $stateData = [ - 'reservation_id' => $reservation->id, - 'date' => $data['date'], - 'type' => $stateType->value, - ]; - $state = $this->stateRepository->create($stateData); - $this->stateRepository->save($state); - } catch (PDOException $exception) { - $this->logger->warning($exception->getMessage(), ['reservation_id' => $reservation->id, 'exception' => $exception->getTraceAsString()]); + $stateType = Model\Venta\Reservation\State\Type::INACTIVE; + $stateData = [ + 'reservation_id' => $reservation->id, + 'date' => $data['date'], + 'type' => $stateType->value, + ]; + $state = $this->stateRepository->create($stateData); + $this->stateRepository->save($state); + + $units = array_combine($data['units'], $data['units_value']); + $this->addUnits($reservation, $units); + + if (array_key_exists('broker_rut', $data) and !empty($data['broker_rut'])) { + $this->addBroker($reservation, $data['broker_rut']); + } + + if (array_key_exists('promotions', $data)) { + $this->addPromotions($reservation, $data['promotions']); + } + + if ($this->reservationRepository->getConnection()->getPDO()->inTransaction()) { + $this->reservationRepository->getConnection()->getPDO()->commit(); } } catch (PDOException $exception) { + $this->logger->warning($exception->getMessage(), ['exception' => $exception->getTraceAsString()]); + if ($this->reservationRepository->getConnection()->getPDO()->inTransaction()) { + $this->reservationRepository->getConnection()->getPDO()->rollBack(); + } throw new ServiceAction\Create(__CLASS__, $exception); } } - $units = array_combine($data['units'], $data['units_value']); - $this->addUnits($reservation, $units); - - if (array_key_exists('broker_rut', $data) and !empty($data['broker_rut'])) { - $this->addBroker($reservation, $data['broker_rut']); - } - - if (array_key_exists('promotions', $data)) { - $this->addPromotions($reservation, $data['promotions']); - } - return $this->process($reservation); } public function edit(Define\Model $model, array $new_data): Model\Venta\Reservation @@ -195,6 +202,41 @@ class Reservation extends Ideal\Service\API throw new ServiceAction\Delete(__CLASS__, $exception); } } + + /** + * @param Model\Venta\Reservation $reservation + * @return void + * @throws ServiceAction\Update + */ + public function approve(Model\Venta\Reservation $reservation): void + { + try { + $stateData = [ + 'reservation_id' => $reservation->id, + 'date' => new DateTimeImmutable(), + 'type' => Model\Venta\Reservation\State\Type::ACTIVE->value, + ]; + $state = $this->stateRepository->create($stateData); + $this->stateRepository->save($state); + } catch (PDOException $exception) { + throw new ServiceAction\Update(__CLASS__, $exception); + } + } + public function reject(Model\Venta\Reservation $reservation): void + { + try { + $stateData = [ + 'reservation_id' => $reservation->id, + 'date' => new DateTimeImmutable(), + 'type' => Model\Venta\Reservation\State\Type::REJECTED->value, + ]; + $state = $this->stateRepository->create($stateData); + $this->stateRepository->save($state); + } catch (PDOException $exception) { + throw new ServiceAction\Update(__CLASS__, $exception); + } + } + protected function process(Define\Model $model): Model\Venta\Reservation { $model->addFactory('states', new Implement\Repository\Factory()