diff --git a/app/resources/database/migrations/20250922162206_add_comments_to_reservations.php b/app/resources/database/migrations/20250922162206_add_comments_to_reservations.php
new file mode 100644
index 0000000..5770e5b
--- /dev/null
+++ b/app/resources/database/migrations/20250922162206_add_comments_to_reservations.php
@@ -0,0 +1,26 @@
+table('reservations')
+ ->addColumn('comments', 'text')
+ ->update();
+ }
+}
diff --git a/app/resources/views/ventas/reservations.blade.php b/app/resources/views/ventas/reservations.blade.php
index a7f2cc4..92d46c9 100644
--- a/app/resources/views/ventas/reservations.blade.php
+++ b/app/resources/views/ventas/reservations.blade.php
@@ -47,9 +47,9 @@
@@ -102,12 +102,13 @@
@include('ventas.reservations.modal.add')
+ @include('ventas.reservations.modal.comment')
@endsection
@push('page_styles')
@@ -293,10 +294,10 @@
return this
}
}
- columnsData() {
- return this.reservations.map(reservation => {
+ get columnsData() {
+ return this.reservations.map(reservation => {
const date = new Date(Date.parse(reservation.date) + 24 * 60 * 60 * 1000)
- return {
+ return {
id: reservation.id,
unidades: reservation.summary,
cliente: reservation.buyer.nombreCompleto,
@@ -314,7 +315,7 @@
}
const tbody = this.component.querySelector('tbody')
tbody.innerHTML = ''
- this.columnsData().forEach(column => {
+ this.columnsData.forEach(column => {
const tr = document.createElement('tr')
const contents = []
const id = column.id
@@ -376,6 +377,9 @@
hide() {
this.component.style.display = 'none'
}
+ find(id) {
+ return this.reservations.find(reservation => reservation.id === parseInt(id))
+ }
send = {
get(url) {
return APIClient.fetch(url).then(response => response.json()).then(json => {
@@ -392,14 +396,6 @@
}
})
},
- delete(url) {
- const method = 'delete'
- return APIClient.fetch(url, {method}).then(response => response.json()).then(json => {
- if (json.success) {
- window.location.reload()
- }
- })
- }
}
actions = {}
}
@@ -409,10 +405,10 @@
super({component_id, formatters})
}
- columnsData() {
- const data = super.columnsData();
+ get columnsData() {
+ const data = super.columnsData;
return data.map(row => {
- delete (row['valida'])
+ delete(row['valida'])
return row
})
}
@@ -436,9 +432,12 @@
},
remove: event => {
event.preventDefault()
- const id = event.currentTarget.dataset.id
- const url = `{{ $urls->api }}/ventas/reservation/${id}/remove`
- this.send.delete(url)
+ const reservation_id = event.currentTarget.dataset.id
+ const url = `{{ $urls->api }}/ventas/reservation/${reservation_id}/remove`
+ const method = 'delete'
+ const action = 'Cancelar'
+ const reservation_data = this.find(reservation_id)
+ reservations.components.modals.comment.show({reservation_id, action, url, method, reservation_data})
return false
}
}
@@ -459,9 +458,12 @@
},
reject: event => {
event.preventDefault()
- const id = event.currentTarget.dataset.id
- const url = `{{ $urls->api }}/ventas/reservation/${id}/reject`
- this.send.get(url)
+ const reservation_id = event.currentTarget.dataset.id
+ const url = `{{ $urls->api }}/ventas/reservation/${reservation_id}/reject`
+ const method = 'delete'
+ const action = 'Rechazar'
+ const reservation_data = this.find(reservation_id)
+ reservations.components.modals.comment.show({reservation_id, action, url, method, reservation_data})
return false
}
}
@@ -472,11 +474,11 @@
super({component_id, formatters})
}
- columnsData() {
- const data = super.columnsData()
+ get columnsData() {
+ const data = super.columnsData
return this.reservations.map((reservation, idx) => {
- data[idx]['estado'] = reservation.state.charAt(0).toUpperCase() + reservation.state.slice(1)
- data[idx]['comentarios'] = reservation.comments?.join('
\n') ?? ''
+ data[idx]['estado'] = this.mapState(reservation.current_state)
+ data[idx]['comentarios'] = reservation.comments ?? ''
return data[idx]
})
}
@@ -484,6 +486,13 @@
return ''
}
watch() {}
+
+ mapState(state) {
+ return {
+ 'canceled': 'Cancelado',
+ 'rejected': 'Rechazado'
+ }[state.toLowerCase()]
+ }
}
const reservations = {
@@ -498,7 +507,8 @@
rejected: null
},
modals: {
- add: null
+ add: null,
+ comment: null,
}
},
display: {
@@ -638,6 +648,7 @@
this.show.projects()
this.components.modals.add = new AddReservationModal(configuration.ids.projects)
+ this.components.modals.comment = new CommentModal()
const project_id = {{ $project_id ?? 'null' }};
if (project_id !== null) {
@@ -645,6 +656,7 @@
}
}
}
+
$(document).ready(() => {
reservations.setup({
ids: {
diff --git a/app/resources/views/ventas/reservations/modal/add.blade.php b/app/resources/views/ventas/reservations/modal/add.blade.php
index 4c7fcf2..1150441 100644
--- a/app/resources/views/ventas/reservations/modal/add.blade.php
+++ b/app/resources/views/ventas/reservations/modal/add.blade.php
@@ -238,6 +238,7 @@
}
}
class AddModalUnits {
+ parent = null
ids = {
buttons_holder: '',
units: ''
@@ -252,7 +253,8 @@
units: null,
}
- constructor() {
+ constructor(parent) {
+ this.parent = parent
this.ids = {
buttons_holder: 'add_unit_buttons',
units: 'add_units'
@@ -265,6 +267,9 @@
}
this.setup()
}
+ get promotions() {
+ return this.parent.data.promotions[this.parent.data.current_project]
+ }
draw = {
button: type => {
return [
@@ -293,36 +298,25 @@
return
}
this.components.units.innerHTML = this.data.units.map(unit => {
- return [
- '',
- '
',
- `
`,
- `
`,
- '
',
- '
',
- `
${unit.type.charAt(0).toUpperCase() + unit.type.slice(1)}
`,
- '',
- '
',
- '
',
- '
',
- '
',
- '',
- ``,
- '
',
- '
',
- ].join('')
+ return this.draw.unit(unit)
}).join('')
- this.components.units.querySelectorAll('.dropdown').forEach(dropdown => {
+ this.components.units.querySelectorAll('.dropdown.add_units').forEach(dropdown => {
+ $(dropdown).dropdown({
+ onChange: (value, text, $selectedItem) => {
+ const unitPromotions = this.promotions.filter(promotion => promotion.units.length > 0)
+ const promotions = unitPromotions.filter(promotion => promotion.units.filter(unit => unit.id === parseInt(value)).length > 0)
+ $selectedItem.parent().parent().parent().parent().find('.add_promotions_unit')
+ .dropdown('change values', promotions.map(promotion => {
+ return {
+ value: promotion.id,
+ name: promotion.description,
+ text: promotion.description,
+ }
+ }))
+ }
+ })
+ })
+ this.components.units.querySelectorAll('.dropdown.add_promotions_unit').forEach(dropdown => {
$(dropdown).dropdown()
})
this.components.units.querySelectorAll('.remove_unit').forEach(button => {
@@ -331,6 +325,54 @@
this.remove(idx)
})
})
+ },
+ unit: unit => {
+ let promotions = ''
+ if (unit.type === 'departamento') {
+ if (this.promotions.filter(promotion => promotion.units.length > 0).length > 0) {
+ promotions = [
+ '',
+ '
',
+ '
',
+ '
'
+ ].join('')
+ }
+ }
+ return [
+ '',
+ '
',
+ `
`,
+ `
`,
+ '
',
+ '
',
+ `
${unit.type.charAt(0).toUpperCase() + unit.type.slice(1)}
`,
+ '',
+ '
',
+ '
',
+ '
',
+ promotions,
+ '
',
+ '',
+ ``,
+ '
',
+ '
',
+ ].join('')
}
}
reset() {
@@ -436,11 +478,30 @@
this.get.brokers(project_id)
this.get.promotions(project_id).then(promotions => {
- this.components.promotions.data.promotions = promotions
+ this.components.promotions.data.promotions = promotions.map(promotion => {
+ return {
+ text: promotion.name,
+ name: promotion.name,
+ value: promotion.id
+ }
+ })
this.components.promotions.draw.promotions()
})
- this.get.units(project_id).then(units => {
- this.components.units.data.types = units
+ this.get.units(project_id).then(unitTypes => {
+ Object.entries(unitTypes).map(([type, units]) => {
+ units = units.map(unit => {
+ return {
+ text: unit.descripcion,
+ name: unit.descripcion,
+ value: unit.id
+ }
+ })
+ units.sort((a, b) => {
+ return parseInt(a.text) - parseInt(b.text)
+ })
+ unitTypes[type] = units
+ })
+ this.components.units.data.types = unitTypes
this.components.units.draw.buttons()
})
}
@@ -564,13 +625,7 @@
if (json.promotions.length === 0) {
return this.data.promotions[project_id] = []
}
- return this.data.promotions[project_id] = json.promotions.map(promotion => {
- return {
- text: promotion.name,
- name: promotion.name,
- value: promotion.id
- }
- })
+ return this.data.promotions[project_id] = json.promotions
})
},
units: project_id => {
@@ -593,18 +648,9 @@
if (!(type in this.data.units[project_id])) {
this.data.units[project_id][type] = []
}
- this.data.units[project_id][type].push({
- text: unit.descripcion,
- name: unit.descripcion,
- value: unit.id
- })
- })
- Object.entries(this.data.units[project_id]).forEach(([type, units]) => {
- units.sort((a, b) => {
- return parseInt(a.text) - parseInt(b.text)
- })
- this.data.units[project_id][type] = units
+ this.data.units[project_id][type].push(unit)
})
+
return this.data.units[project_id]
})
},
@@ -706,7 +752,7 @@
this.components.promotions = new AddModalPromotions()
this.components.projects = document.getElementById(this.ids.projects)
this.components.project_name = document.getElementById(this.ids.project_name)
- this.components.units = new AddModalUnits()
+ this.components.units = new AddModalUnits(this)
this.components.$modal.modal({
onApprove: () => {
diff --git a/app/resources/views/ventas/reservations/modal/comment.blade.php b/app/resources/views/ventas/reservations/modal/comment.blade.php
new file mode 100644
index 0000000..55eb4fb
--- /dev/null
+++ b/app/resources/views/ventas/reservations/modal/comment.blade.php
@@ -0,0 +1,157 @@
+
+
+@push('page_scripts')
+
+@endpush
diff --git a/app/src/Controller/API/Ventas/Reservations.php b/app/src/Controller/API/Ventas/Reservations.php
index 607be0f..6d92b97 100644
--- a/app/src/Controller/API/Ventas/Reservations.php
+++ b/app/src/Controller/API/Ventas/Reservations.php
@@ -71,9 +71,9 @@ class Reservations
$output['errors'] []= $this->parseError($exception);
}*/
- if (count($input['reservations']) === count($output['reservations'])) {
+ /*if (count($input['reservations']) === count($output['reservations'])) {
$output['success'] = true;
- }
+ }*/
return $this->withJson($response, $output);
}
diff --git a/app/src/Model/Venta/Reservation.php b/app/src/Model/Venta/Reservation.php
index cc777eb..8f84ce4 100644
--- a/app/src/Model/Venta/Reservation.php
+++ b/app/src/Model/Venta/Reservation.php
@@ -117,6 +117,8 @@ class Reservation extends Common\Ideal\Model
return $base >= $price;
}
+ public string $comments;
+
protected function jsonComplement(): array
{
return [
@@ -131,7 +133,10 @@ class Reservation extends Common\Ideal\Model
'base' => $this->base(),
'price' => $this->price(),
'valid' => $this->valid(),
- 'summary' => $this->summary()
+ 'summary' => $this->summary(),
+ 'states' => $this->states() ?? [],
+ 'current_state' => $this->currentState()?->type?->name ?? null,
+ 'comments' => $this->comments ?? '',
];
}
}
diff --git a/app/src/Model/Venta/Reservation/State.php b/app/src/Model/Venta/Reservation/State.php
index 64eeed5..c7468d5 100644
--- a/app/src/Model/Venta/Reservation/State.php
+++ b/app/src/Model/Venta/Reservation/State.php
@@ -16,7 +16,10 @@ class State extends Common\Ideal\Model
return [
'reservation_id' => $this->reservation->id,
'date' => $this->date->format('Y-m-d'),
- 'type' => $this->type
+ 'type' => [
+ 'id' => $this->type->value,
+ 'name' => $this->type->name,
+ ]
];
}
}
diff --git a/app/src/Model/Venta/Reservation/State/Type.php b/app/src/Model/Venta/Reservation/State/Type.php
index 96d60e5..2ea7319 100644
--- a/app/src/Model/Venta/Reservation/State/Type.php
+++ b/app/src/Model/Venta/Reservation/State/Type.php
@@ -18,6 +18,6 @@ enum Type: int
}
public static function getTypes(): array
{
- return [self::ACTIVE->value, self::INACTIVE->value, self::REJECTED->value];
+ return [self::ACTIVE->value, self::INACTIVE->value, self::REJECTED->value, self::CANCELLED->value];
}
}
diff --git a/app/src/Repository/Venta/Promotion.php b/app/src/Repository/Venta/Promotion.php
index 0b20bff..9839032 100644
--- a/app/src/Repository/Venta/Promotion.php
+++ b/app/src/Repository/Venta/Promotion.php
@@ -187,7 +187,7 @@ class Promotion extends Common\Ideal\Repository
->joined('LEFT OUTER JOIN proyecto_tipo_unidad ptu ON ptu.id = pul.unit_line_id')
->joined('LEFT OUTER JOIN promotion_units pu ON pu.promotion_id = a.id')
->joined('LEFT OUTER JOIN unidad ON unidad.id = pu.unit_id')
- ->joined('LEFT OUTER JOIN proyecto_tipo_unidad ptu1 ON ptu.id = unidad.pt')
+ ->joined('LEFT OUTER JOIN proyecto_tipo_unidad ptu1 ON ptu1.id = unidad.pt')
->where('pp.project_id = :project_id OR put.project_id = :project_id OR ptu.proyecto = :project_id OR ptu1.proyecto = :project_id')
->group('a.id');
return $this->fetchMany($query, ['project_id' => $project_id]);
diff --git a/app/src/Repository/Venta/Reservation.php b/app/src/Repository/Venta/Reservation.php
index e181eba..a59feaa 100644
--- a/app/src/Repository/Venta/Reservation.php
+++ b/app/src/Repository/Venta/Reservation.php
@@ -4,6 +4,7 @@ namespace Incoviba\Repository\Venta;
use DateTimeInterface;
use DateInterval;
use Incoviba\Common\Define;
+use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Exception\Model\InvalidState;
use PDO;
use Incoviba\Common;
@@ -29,7 +30,7 @@ class Reservation extends Common\Ideal\Repository
public function create(?array $data = null): Model\Venta\Reservation
{
- $map = (new Common\Implement\Repository\MapperParser())
+ $map = (new Common\Implement\Repository\MapperParser(['comments']))
->register('project_id', (new Common\Implement\Repository\Mapper())
->setProperty('project')
->setFunction(function($data) {
@@ -91,18 +92,57 @@ class Reservation extends Common\Ideal\Repository
/**
* @param int $buyer_rut
+ * @param int $unit_id
* @param DateTimeInterface $date
* @return Model\Venta\Reservation
- * @throws Common\Implement\Exception\EmptyResult
+ * @throws EmptyResult
*/
- public function fetchByBuyerAndDate(int $buyer_rut, DateTimeInterface $date): Model\Venta\Reservation
+ public function fetchByBuyerAndUnitAndDate(int $buyer_rut, int $unit_id, DateTimeInterface $date): Model\Venta\Reservation
+ {
+ $query = $this->connection->getQueryBuilder()
+ ->select('a.*')
+ ->from('reservations a')
+ ->joined('INNER JOIN reservation_details rd ON a.id = rd.reservation_id')
+ ->where('a.buyer_rut = :buyer_rut AND rd.unit_id = :unit_id AND a.date >= :date');
+ return $this->fetchOne($query, ['buyer_rut' => $buyer_rut, 'unit_id' => $unit_id, 'date' => $date->sub(new DateInterval('P10D'))->format('Y-m-d')]);
+ }
+
+ /**
+ * @param int $buyer_rut
+ * @param int $project_id
+ * @param DateTimeInterface $date
+ * @return array
+ * @throws EmptyResult
+ */
+ public function fetchByBuyerAndProjectAndDate(int $buyer_rut, int $project_id, DateTimeInterface $date): array
+ {
+ $query = $this->connection->getQueryBuilder()
+ ->select()
+ ->from('reservations')
+ ->where('buyer_rut = :buyer_rut AND project_id = :project_id AND date >= :date');
+ return $this->fetchMany($query, ['buyer_rut' => $buyer_rut, 'project_id' => $project_id, 'date' => $date->sub(new DateInterval('P10D'))->format('Y-m-d')]);
+ }
+
+ /**
+ * @param int $buyer_rut
+ * @param DateTimeInterface $date
+ * @return array
+ * @throws EmptyResult
+ */
+ public function fetchByBuyerAndDate(int $buyer_rut, DateTimeInterface $date): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from('reservations')
->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')]);
+ return $this->fetchMany($query, ['buyer_rut' => $buyer_rut, 'date' => $date->sub(new DateInterval('P10D'))->format('Y-m-d')]);
}
+
+ /**
+ * @param int $project_id
+ * @return array
+ * @throws EmptyResult
+ */
public function fetchByProject(int $project_id): array
{
$query = $this->connection->getQueryBuilder()
@@ -114,13 +154,16 @@ class Reservation extends Common\Ideal\Repository
/**
* @param int $project_id
- * @param int $state
+ * @param int|string $state
* @return array
- * @throws Common\Implement\Exception\EmptyResult
+ * @throws EmptyResult
* @throws InvalidState
*/
- public function fetchState(int $project_id, int $state): array
+ public function fetchState(int $project_id, int|string $state): array
{
+ if (is_string($state)) {
+ $state = Model\Venta\Reservation\State\Type::from($state)->value;
+ }
if (!in_array($state, Model\Venta\Reservation\State\Type::getTypes())) {
throw new InvalidState();
}
@@ -141,6 +184,7 @@ class Reservation extends Common\Ideal\Repository
return $this->fetchMany($query, ['project_id' => $project_id,
'state' => $state]);
}
+
/**
* @param int $project_id
* @return array
diff --git a/app/src/Service/Venta/MediosPago/Toku/Subscription.php b/app/src/Service/Venta/MediosPago/Toku/Subscription.php
index 01f7d98..4e1505c 100644
--- a/app/src/Service/Venta/MediosPago/Toku/Subscription.php
+++ b/app/src/Service/Venta/MediosPago/Toku/Subscription.php
@@ -99,13 +99,13 @@ class Subscription extends AbstractEndPoint
public function queue(int $venta_id): bool
{
try {
- $venta = $this->ventaService->getById($venta_id);
+ $this->ventaService->getById($venta_id);
} catch (Read $exception) {
$this->logger->warning($exception);
return false;
}
try {
- $subscription = $this->subscriptionRepsitory->fetchByVenta($venta_id);
+ $this->subscriptionRepsitory->fetchByVenta($venta_id);
return false;
} catch (EmptyResult) {
return true;
diff --git a/app/src/Service/Venta/Reservation.php b/app/src/Service/Venta/Reservation.php
index 6326cca..f6d3141 100644
--- a/app/src/Service/Venta/Reservation.php
+++ b/app/src/Service/Venta/Reservation.php
@@ -123,7 +123,7 @@ class Reservation extends Ideal\Service\API
$date = new DateTimeImmutable($data['date']);
} catch (DateMalformedStringException) {}
try {
- $reservation = $this->reservationRepository->fetchByBuyerAndDate($data['buyer_rut'], $date);
+ $reservation = $this->reservationRepository->fetchByBuyerAndUnitAndDate($data['buyer_rut'], (int) $data['units'][0], $date);
if (array_key_exists('broker_rut', $data) and $data['broker_rut'] !== '') {
try {