forked from Somnia/SomniaRooms
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4abe778657 | ||
|
|
d6ad52c044 | ||
|
|
72a9f69799 | ||
|
|
faea267b1f | ||
|
|
e8bfcd9c6b | ||
|
|
ed4ba9d82f | ||
|
|
528d1a39ab | ||
|
|
4f451c2f75 | ||
|
|
86abb7222e | ||
|
|
7b6d1408f8 | ||
|
|
a4ec188cff | ||
|
|
15a884fafa | ||
|
|
9c8759e24d | ||
|
|
7ec989a76a | ||
|
|
8e7bad7771 | ||
|
|
7a56158563 | ||
|
|
84d9c81afd | ||
|
|
30773d7af4 |
@@ -28,3 +28,7 @@ APP_SECRET=d130654a5d6a8ab5945b20b22dfeee74
|
||||
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
||||
DATABASE_URL="postgresql://app:[email protected]:54195/app?serverVersion=16&charset=utf8"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
###> nelmio/cors-bundle ###
|
||||
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
||||
###< nelmio/cors-bundle ###
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request: ~
|
||||
workflow_dispatch: ~
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
lint:
|
||||
name: Docker Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Lint Dockerfile
|
||||
uses: hadolint/[email protected]
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Publish_on_Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- devbuild_*
|
||||
workflow_dispatch: ~
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Push
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to local registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{vars.REGISTRY_INSTANCE}}
|
||||
username: ${{gitea.repository_owner}}
|
||||
password: ${{secrets.REGISTRY_TOKEN}} # It should be a user/org token with package perms.
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{vars.REGISTRY_INSTANCE}}/somnia/somniarooms:devbuild
|
||||
target: frankenphp_prod
|
||||
platforms: linux/amd64,linux/arm64
|
||||
secrets: |
|
||||
GIT_AUTH_TOKEN=${{secrets.REGISTRY_TOKEN}}
|
||||
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9003,
|
||||
"pathMappings": {
|
||||
"/app" : "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch currently open script",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"cwd": "${fileDirname}",
|
||||
"port": 0,
|
||||
"runtimeArgs": [
|
||||
"-dxdebug.start_with_request=yes"
|
||||
],
|
||||
"env": {
|
||||
"XDEBUG_MODE": "debug,develop",
|
||||
"XDEBUG_CONFIG": "client_port=${port}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Launch Built-in web server",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"runtimeArgs": [
|
||||
"-dxdebug.mode=debug",
|
||||
"-dxdebug.start_with_request=yes",
|
||||
"-S",
|
||||
"localhost:0"
|
||||
],
|
||||
"program": "",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"port": 9003,
|
||||
"serverReadyAction": {
|
||||
"pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"action": "openExternally"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
# InstantRooms
|
||||
# SomniaRooms
|
||||
|
||||
A Hotel Room booking system.
|
||||
|
||||
Powered by:
|
||||
- PHP Synfony.
|
||||
- Easyadmin
|
||||
- PHP Symfony.
|
||||
- Easyadmin.
|
||||
|
||||
## Commands:
|
||||
|
||||
@@ -16,9 +16,24 @@ docker compose build --no-cache
|
||||
Start Containers:
|
||||
```
|
||||
docker compose up --pull always -d --wait
|
||||
docker compose --env-file .\.env.local up -d --wait
|
||||
```
|
||||
|
||||
Stop containers
|
||||
```
|
||||
docker compose down --remove-orphans
|
||||
```
|
||||
|
||||
### Prod:
|
||||
|
||||
You can use docker compose for prod or build and deploy a prod image.
|
||||
|
||||
**Build Prod image**
|
||||
```
|
||||
docker build -t somniarooms:prod --target frankenphp_prod .
|
||||
```
|
||||
|
||||
**Deploy prod image**
|
||||
```
|
||||
docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./env.prod.local somniarooms:prod
|
||||
```
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
"doctrine/doctrine-migrations-bundle": "^3.3",
|
||||
"doctrine/orm": "^3.1",
|
||||
"easycorp/easyadmin-bundle": "^4.10",
|
||||
"nelmio/cors-bundle": "^2.4",
|
||||
"phpdocumentor/reflection-docblock": "^5.4",
|
||||
"phpstan/phpdoc-parser": "^1.28",
|
||||
"runtime/frankenphp-symfony": "^0.2.0",
|
||||
"symfony/console": "7.0.*",
|
||||
"symfony/dotenv": "7.0.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/form": "7.0.*",
|
||||
"symfony/framework-bundle": "7.0.*",
|
||||
"symfony/property-access": "7.0.*",
|
||||
"symfony/property-info": "7.0.*",
|
||||
|
||||
Generated
+69
-7
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e0a5e615a56df791d0b9a2dbbe533659",
|
||||
"content-hash": "f177a4872c4ced09856f94d423de0b21",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/cache",
|
||||
@@ -1320,6 +1320,68 @@
|
||||
],
|
||||
"time": "2024-04-19T18:13:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nelmio/cors-bundle",
|
||||
"version": "2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nelmio/NelmioCorsBundle.git",
|
||||
"reference": "78fcdb91f76b080a1008133def9c7f613833933d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/78fcdb91f76b080a1008133def9c7f613833933d",
|
||||
"reference": "78fcdb91f76b080a1008133def9c7f613833933d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.6",
|
||||
"symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0"
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nelmio\\CorsBundle\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nelmio",
|
||||
"homepage": "http://nelm.io"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application",
|
||||
"keywords": [
|
||||
"api",
|
||||
"cors",
|
||||
"crossdomain"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nelmio/NelmioCorsBundle/issues",
|
||||
"source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.4.0"
|
||||
},
|
||||
"time": "2023-11-30T16:41:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
"version": "2.2.0",
|
||||
@@ -3079,16 +3141,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/form",
|
||||
"version": "v7.0.6",
|
||||
"version": "v7.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/form.git",
|
||||
"reference": "d5db6599775a563792391d0045decc240e7ebd1e"
|
||||
"reference": "b4df6a399a2b03782a0163807239db342659f54f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/form/zipball/d5db6599775a563792391d0045decc240e7ebd1e",
|
||||
"reference": "d5db6599775a563792391d0045decc240e7ebd1e",
|
||||
"url": "https://api.github.com/repos/symfony/form/zipball/b4df6a399a2b03782a0163807239db342659f54f",
|
||||
"reference": "b4df6a399a2b03782a0163807239db342659f54f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3155,7 +3217,7 @@
|
||||
"description": "Allows to easily create, process and reuse HTML forms",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/form/tree/v7.0.6"
|
||||
"source": "https://github.com/symfony/form/tree/v7.0.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3171,7 +3233,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-03-28T09:20:36+00:00"
|
||||
"time": "2024-04-28T11:44:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/framework-bundle",
|
||||
|
||||
@@ -9,4 +9,5 @@ return [
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
|
||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
nelmio_cors:
|
||||
defaults:
|
||||
origin_regex: true
|
||||
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||
allow_headers: ['Content-Type', 'Authorization']
|
||||
expose_headers: ['Link']
|
||||
max_age: 3600
|
||||
paths:
|
||||
'^/api/':
|
||||
allow_origin: ['*']
|
||||
allow_headers: ['*']
|
||||
allow_methods: ['POST', 'GET']
|
||||
max_age: 3600
|
||||
'^/': null
|
||||
@@ -16,6 +16,14 @@ security:
|
||||
main:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
form_login:
|
||||
login_path: app_login
|
||||
check_path: app_login
|
||||
enable_csrf: true
|
||||
logout:
|
||||
path: app_logout
|
||||
# where to redirect after logout
|
||||
# target: app_any_route
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#the-firewall
|
||||
@@ -26,7 +34,7 @@ security:
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
- { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
|
||||
when@test:
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# SomniaRooms - API Endpoints and Objects
|
||||
|
||||
En este documento se indican los endpoints disponibles en la API de SomniaRooms, así como los mensajes de petición y respuesta en cada caso. (Vamos, un Swagger pero escrito a mano.)
|
||||
|
||||
## Config (`api/config`)
|
||||
|
||||
Permite al cliente obtener las variables de configuración del sistema. Ver más sobre las [variables de configuración](config-vars.md) en la documentación.
|
||||
|
||||
**TODO**
|
||||
|
||||
|
||||
## Room Category (`api/room/category`)
|
||||
|
||||
Estos endpoints permiten obtener la información de las categorias del sistema.
|
||||
|
||||
**TODO**
|
||||
|
||||
|
||||
## Search (`api/search`)
|
||||
|
||||
Conjunto de Endpoints para consultar el estado de las habitaciones.
|
||||
|
||||
|
||||
## Reservas (`api/booking`)
|
||||
|
||||
Endpoint para la gestión y consulta de las reservas.
|
||||
|
||||
### `api/booking/details`
|
||||
Permite obtener el detalle de una reserva.
|
||||
|
||||
Es necesario enviar la información de la reserva a consultar.
|
||||
|
||||
**Request (JSON):**
|
||||
```json
|
||||
{
|
||||
"refNumber": "2024001",
|
||||
"email": "[email protected]"
|
||||
}
|
||||
```
|
||||
|
||||
Se debe incluir el número de referencia de la reserva y el correo electrónico asociado.
|
||||
|
||||
**Response (JSON):**
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
### `api/booking/create`
|
||||
|
||||
Endpoint para crear nuevas reservas.
|
||||
|
||||
Se enviarán los datos de la reserva con el siguiente formato:
|
||||
|
||||
**Request (JSON)_**
|
||||
```json
|
||||
{
|
||||
"arrivalDate": "20240510",
|
||||
"departureDate": "20240514",
|
||||
"customerDetails": {
|
||||
"name": "Juan",
|
||||
"surname": "Perez",
|
||||
"email": "[email protected]",
|
||||
"phoneNumber": "12345678"
|
||||
},
|
||||
"rooms": [
|
||||
{
|
||||
"roomCategory": 1,
|
||||
"guestNumber": 2,
|
||||
"amount": 1
|
||||
},
|
||||
{
|
||||
"roomCategory": 1,
|
||||
"guestNumber": 3,
|
||||
"amount": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Si la reserva es correcta se devolverá el número de referencia de la reserva:
|
||||
```json
|
||||
{
|
||||
"refNumber": "2024001",
|
||||
"email": "[email protected]",
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,54 @@
|
||||
# SomniaRooms - App Configuration Variables.
|
||||
|
||||
This document contains a list of all config variables of SomniaRooms. Variables are stored in DB (table _`config_variable`_) and can be configured from admin panel.
|
||||
|
||||
Variables are divided in sections. Some sections are ony accesible from SomniaRoom, but others can be retrieved by anyone using config endpoint (_`/config`_).
|
||||
|
||||
## List of sections:
|
||||
|
||||
WIP
|
||||
|
||||
TODO -> Add default table with basic information to all sections.
|
||||
|
||||
### **Company**
|
||||
Variables in this section contains information about the Hotel.
|
||||
|
||||
- SectionName -> company
|
||||
- Availability -> public
|
||||
- Nº variables -> X
|
||||
|
||||
**Variable list:**
|
||||
|
||||
- Name - Name of hotel.
|
||||
|
||||
|
||||
|
||||
### **Price Management**
|
||||
Configure how backend services calculates prices for bookings.
|
||||
|
||||
- SectionName -> price_mng
|
||||
- Availability -> internal
|
||||
- Nº variables -> X
|
||||
|
||||
**Variable list:**
|
||||
|
||||
WIP
|
||||
|
||||
#### FinalPriceCalculation.
|
||||
Establece cómo se deben tratar los precios de cada día para generar el precio final de la estancia.
|
||||
|
||||
Algunos algoritmos para estos serán:
|
||||
- Precio más alto.
|
||||
- Precio más bajo.
|
||||
- Suma de precios. --> Por defecto.
|
||||
- Media de precios.
|
||||
|
||||
#### MultipleRoomFareValid
|
||||
Estrategia de selección de tarifa en caso de que haya varias válidas (mientras no exista un sistema de prioridad de tarifas...)
|
||||
|
||||
Posibles algoritmos:
|
||||
- Precio más alto.
|
||||
- Precio más bajo.
|
||||
- Media.
|
||||
|
||||
|
||||
@@ -2,4 +2,9 @@
|
||||
; See https://github.com/docker/for-linux/issues/264
|
||||
; The `client_host` below may optionally be replaced with `discover_client_host=yes`
|
||||
; Add `start_with_request=yes` to start debug session on each request
|
||||
xdebug.client_host = host.docker.internal
|
||||
xdebug.client_host=host.docker.internal
|
||||
xdebug.start_with_request=yes
|
||||
;xdebug.mode=debug
|
||||
xdebug.discover_client_host=1
|
||||
xdebug.idekey=docker
|
||||
;xdebug.log=/dev/stdout
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240427153125 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SEQUENCE config_variable_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE config_variable (id INT NOT NULL, key VARCHAR(32) NOT NULL, value VARCHAR(255) NOT NULL, section VARCHAR(64) DEFAULT NULL, PRIMARY KEY(id))');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP SEQUENCE config_variable_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE config_variable');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240427171934 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE room_availability (day DATE NOT NULL, room_category_id INT NOT NULL, num_available INT NOT NULL, PRIMARY KEY(day, room_category_id))');
|
||||
$this->addSql('CREATE INDEX IDX_89C5BA2C67333DD ON room_availability (room_category_id)');
|
||||
$this->addSql('ALTER TABLE room_availability ADD CONSTRAINT FK_89C5BA2C67333DD FOREIGN KEY (room_category_id) REFERENCES room_category (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_718569538A90ABA9 ON config_variable (key)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE room_availability DROP CONSTRAINT FK_89C5BA2C67333DD');
|
||||
$this->addSql('DROP TABLE room_availability');
|
||||
$this->addSql('DROP INDEX UNIQ_718569538A90ABA9');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240429200025 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SEQUENCE fare_table_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE SEQUENCE room_fare_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE fare_table (id INT NOT NULL, room_category_id INT NOT NULL, start_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, end_date TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, comment VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_8E43BE7067333DD ON fare_table (room_category_id)');
|
||||
$this->addSql('CREATE TABLE room_fare (id INT NOT NULL, fare_table_id INT NOT NULL, guest_number INT NOT NULL, fare_amount INT NOT NULL, day_type VARCHAR(2) DEFAULT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_35C9B213A7233EF2 ON room_fare (fare_table_id)');
|
||||
$this->addSql('ALTER TABLE fare_table ADD CONSTRAINT FK_8E43BE7067333DD FOREIGN KEY (room_category_id) REFERENCES room_category (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE room_fare ADD CONSTRAINT FK_35C9B213A7233EF2 FOREIGN KEY (fare_table_id) REFERENCES fare_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE room_availability ALTER day TYPE VARCHAR(255)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP SEQUENCE fare_table_id_seq CASCADE');
|
||||
$this->addSql('DROP SEQUENCE room_fare_id_seq CASCADE');
|
||||
$this->addSql('ALTER TABLE fare_table DROP CONSTRAINT FK_8E43BE7067333DD');
|
||||
$this->addSql('ALTER TABLE room_fare DROP CONSTRAINT FK_35C9B213A7233EF2');
|
||||
$this->addSql('DROP TABLE fare_table');
|
||||
$this->addSql('DROP TABLE room_fare');
|
||||
$this->addSql('ALTER TABLE room_availability ALTER day TYPE DATE');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240508180113 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SEQUENCE booking_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE SEQUENCE booking_customer_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE SEQUENCE booking_room_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE booking (id INT NOT NULL, arrival_date DATE NOT NULL, departure_date DATE NOT NULL, booking_time TIMESTAMP(0) WITH TIME ZONE NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE TABLE booking_customer (id INT NOT NULL, booking_id INT NOT NULL, name VARCHAR(64) NOT NULL, surname VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_BA0D10843301C60 ON booking_customer (booking_id)');
|
||||
$this->addSql('CREATE TABLE booking_room (id INT NOT NULL, booking_id INT NOT NULL, room_category_id INT NOT NULL, guest_number INT NOT NULL, room_price INT NOT NULL, amount INT NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_6A0E73D53301C60 ON booking_room (booking_id)');
|
||||
$this->addSql('CREATE INDEX IDX_6A0E73D567333DD ON booking_room (room_category_id)');
|
||||
$this->addSql('ALTER TABLE booking_customer ADD CONSTRAINT FK_BA0D10843301C60 FOREIGN KEY (booking_id) REFERENCES booking (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE booking_room ADD CONSTRAINT FK_6A0E73D53301C60 FOREIGN KEY (booking_id) REFERENCES booking (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE booking_room ADD CONSTRAINT FK_6A0E73D567333DD FOREIGN KEY (room_category_id) REFERENCES room_category (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP SEQUENCE booking_id_seq CASCADE');
|
||||
$this->addSql('DROP SEQUENCE booking_customer_id_seq CASCADE');
|
||||
$this->addSql('DROP SEQUENCE booking_room_id_seq CASCADE');
|
||||
$this->addSql('ALTER TABLE booking_customer DROP CONSTRAINT FK_BA0D10843301C60');
|
||||
$this->addSql('ALTER TABLE booking_room DROP CONSTRAINT FK_6A0E73D53301C60');
|
||||
$this->addSql('ALTER TABLE booking_room DROP CONSTRAINT FK_6A0E73D567333DD');
|
||||
$this->addSql('DROP TABLE booking');
|
||||
$this->addSql('DROP TABLE booking_customer');
|
||||
$this->addSql('DROP TABLE booking_room');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20240508180617 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE booking ADD ref_number VARCHAR(20) NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE booking DROP ref_number');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Controller\Admin\BookingCustomerCrudController;
|
||||
use App\Entity\Booking;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\CollectionField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class BookingCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return Booking::class;
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
$fieldRefNumber = TextField::new ('refNumber');
|
||||
$fieldBookingTime = DateTimeField::new ('BookingTime');
|
||||
if ($pageName == Crud::PAGE_EDIT) {
|
||||
$fieldRefNumber->setDisabled(true);
|
||||
$fieldBookingTime->setDisabled(true);
|
||||
}
|
||||
|
||||
yield FormField::addFieldset('Booking details');
|
||||
yield $fieldRefNumber;
|
||||
yield $fieldBookingTime;
|
||||
|
||||
yield DateField::new ('ArrivalDate');
|
||||
yield DateField::new ('DepartureDate');
|
||||
|
||||
yield AssociationField::new ('bookingCustomer')->renderAsEmbeddedForm(BookingCustomerCrudController::class);
|
||||
yield CollectionField::new ('bookingRooms')->useEntryCrudForm();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\BookingCustomer;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class BookingCustomerCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return BookingCustomer::class;
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
yield TextField::new('Name');
|
||||
yield TextField::new('Surname');
|
||||
yield TextField::new('Email');
|
||||
yield TextField::new('PhoneNumber');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Controller\Admin\RoomCategoryCrudController;
|
||||
use App\Entity\BookingRoom;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\MoneyField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\NumberField;
|
||||
|
||||
class BookingRoomCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return BookingRoom::class;
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
yield AssociationField::new ('RoomCategory')->setCrudController(RoomCategoryCrudController::class);
|
||||
yield NumberField::new ('GuestNumber');
|
||||
yield MoneyField::new ('RoomPrice')->setCurrency("EUR");
|
||||
yield NumberField::new ('Amount');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\ConfigVariable;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
|
||||
class ConfigVariableCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return ConfigVariable::class;
|
||||
}
|
||||
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
yield TextField::new('section');
|
||||
yield TextField::new('key');
|
||||
yield TextField::new('value');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\{AssociationField, TextField, DateField};
|
||||
|
||||
use App\Entity\FareTable;
|
||||
use App\Entity\RoomCategory;
|
||||
use App\Controller\Admin\RoomCategoryCrudController;
|
||||
|
||||
class FareTableCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return FareTable::class;
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
//$catRepo = $this->entityManager->getRepository(SomeEntity::class);
|
||||
|
||||
yield TextField::new('comment')->setLabel('Name');
|
||||
|
||||
yield AssociationField::new('roomCategory');
|
||||
|
||||
yield DateField::new('startDate');
|
||||
yield DateField::new('endDate');
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\RoomAvailability;
|
||||
use App\Entity\RoomCategory;
|
||||
use App\Repository\RoomAvailabilityRepository;
|
||||
use App\Repository\RoomCategoryRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[IsGranted("ROLE_ADMIN")]
|
||||
class RoomAvailabilityAdminController extends AbstractController
|
||||
{
|
||||
|
||||
private $adminUrlGenerator;
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
public function __construct(AdminUrlGenerator $adminUrlGenerator, EntityManagerInterface $em)
|
||||
{
|
||||
$this->adminUrlGenerator = $adminUrlGenerator;
|
||||
$this->entityManager = $em;
|
||||
}
|
||||
|
||||
#[Route('/admin/roomAvailability', name: 'admin_roomAvailability')]
|
||||
public function index(RoomAvailabilityRepository $repo, RoomCategoryRepository $catRepo): Response
|
||||
{
|
||||
$categoryList = $catRepo->findAll();
|
||||
|
||||
return $this->render('admin/room_availability/index.html.twig', [
|
||||
'roomCategoryList' => $categoryList,
|
||||
]);
|
||||
}
|
||||
|
||||
// List RoomAvailability items from category.
|
||||
#[Route('/admin/roomAvailability/{categoryId}', name: 'admin_roomAvailability_category')]
|
||||
public function categoryAvailability(RoomAvailabilityRepository $repo, RoomCategoryRepository $catRepo, string $categoryId): Response
|
||||
{
|
||||
$category = $catRepo->find($categoryId);
|
||||
$availabilityList = $repo->findByRoomCategory($categoryId);
|
||||
|
||||
return $this->render('admin/room_availability/category_view.html.twig', [
|
||||
'categoryData' => $category,
|
||||
'availabilityList' => $availabilityList,
|
||||
]);
|
||||
}
|
||||
|
||||
// Edit a RoomAvailability numAvailable.
|
||||
#[Route('/admin/roomAvailability/edit/{categoryId}/{day}', name: 'admin_roomAvailability_category_update')]
|
||||
public function updateCategoryAvailability(Request $request, RoomAvailabilityRepository $repo, string $categoryId, string $day): Response
|
||||
{
|
||||
$availabilityItem = $repo->find(["day" => $day, "roomCategory" => $categoryId]);
|
||||
|
||||
if ($availabilityItem == null) {
|
||||
//TODOME: Aquí deberíamos llevar a una página de error o algo.
|
||||
|
||||
$url = $this->adminUrlGenerator->setRoute('admin_roomAvailability_category', [
|
||||
'categoryId' => $item->getId(),
|
||||
])->generateUrl();
|
||||
return $this->redirect($url);
|
||||
}
|
||||
|
||||
$form = $this->createFormBuilder($availabilityItem)
|
||||
->add('numAvailable', NumberType::class, ['label' => 'Num Available'])
|
||||
->add('save', SubmitType::class, ['label' => 'Update value'])
|
||||
->getForm();
|
||||
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
// $form->getData() holds the submitted values
|
||||
// but, the original `$availabilityItem` variable has also been updated
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
$url = $this->adminUrlGenerator->setRoute('admin_roomAvailability_category', [
|
||||
'categoryId' => $categoryId,
|
||||
])->generateUrl();
|
||||
|
||||
return $this->redirect($url);
|
||||
}
|
||||
|
||||
return $this->render('admin/room_availability/availability_day_edit.html.twig', [
|
||||
'form' => $form,
|
||||
'availabilityData' => $availabilityItem,
|
||||
]);
|
||||
}
|
||||
|
||||
// Bulk edit all roomAvailability of a category on period.
|
||||
#[Route('/admin/roomAvailability/{categoryId}/bulkedit', name: 'admin_roomAvailability_category_bulkupdate')]
|
||||
public function bulkUpdateCategoryAvailability(Request $request, RoomAvailabilityRepository $repo, string $categoryId): Response
|
||||
{
|
||||
|
||||
$form = $this->createFormBuilder()
|
||||
->add('startDate', DateType::class, [
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('endDate', DateType::class, [
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('numAvailable', NumberType::class, [
|
||||
'label' => 'Num Available',
|
||||
])
|
||||
->add('save', SubmitType::class, ['label' => 'Update value'])
|
||||
->getForm();
|
||||
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
// $form->getData() holds the submitted values
|
||||
$data = $form->getData();
|
||||
|
||||
$categoryItem = $this->entityManager->find(RoomCategory::class, $categoryId);
|
||||
$repo->bulkEditRoomAvailabilityForCategoryOnPeriod($categoryItem, $data['startDate'], $data['endDate'], $data['numAvailable']);
|
||||
|
||||
$url = $this->adminUrlGenerator->setRoute('admin_roomAvailability')->generateUrl();
|
||||
|
||||
return $this->redirect($url);
|
||||
}
|
||||
|
||||
return $this->render('admin/room_availability/availability_bulk_edit.html.twig', [
|
||||
'form' => $form,
|
||||
'categoryId' => $categoryId,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/roomAvailabilityTEST/', name: 'admin_roomAvailability_test')]
|
||||
public function availabilityTest(EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
$avl = new RoomAvailability();
|
||||
|
||||
$avl->setDay(new \DateTime("2024-04-24"));
|
||||
$avl->setRoomCategory($entityManager->find(RoomCategory::class, 4));
|
||||
$avl->setNumAvailable(2);
|
||||
|
||||
$entityManager->persist($avl);
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->json($avl);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace App\Controller\Admin;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
|
||||
@@ -27,8 +28,9 @@ class RoomCategoryCrudController extends AbstractCrudController
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
return [
|
||||
//IdField::new('id'),
|
||||
TextField::new('name'),
|
||||
TextEditorField::new('description'),
|
||||
TextField::new('description'),
|
||||
TextField::new('bedType'),
|
||||
IntegerField::new('maxGuestNum'),
|
||||
CollectionField::new('roomCategoryDetails')->useEntryCrudForm(),
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\RoomCategoryDetails;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
|
||||
class RoomCategoryDetailsCrudController extends AbstractCrudController
|
||||
@@ -20,7 +18,7 @@ class RoomCategoryDetailsCrudController extends AbstractCrudController
|
||||
{
|
||||
return [
|
||||
TextField::new('detailsSection'),
|
||||
TextEditorField::new('detailValue'),
|
||||
TextField::new('detailValue'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ namespace App\Controller\Admin;
|
||||
use App\Entity\RoomCategoryPhoto;
|
||||
use App\Entity\RoomCategoryPhotoKinds;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\{TextField, ChoiceField};
|
||||
|
||||
class RoomCategoryPhotoCrudController extends AbstractCrudController
|
||||
{
|
||||
@@ -20,7 +17,7 @@ class RoomCategoryPhotoCrudController extends AbstractCrudController
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
yield TextField::new('path');
|
||||
yield TextEditorField::new('altText');
|
||||
yield TextField::new('altText');
|
||||
yield ChoiceField::new('kind')->setChoices(['Main' => 'main', 'Bathroom' => 'bathroom', 'Other' => 'other']);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\RoomFare;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\MoneyField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\NumberField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||
|
||||
class RoomFareCrudController extends AbstractCrudController
|
||||
{
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return RoomFare::class;
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
$fieldFareTable = AssociationField::new ('fareTable')->setRequired(true);
|
||||
if ($pageName == Crud::PAGE_EDIT) {
|
||||
$fieldFareTable->setDisabled(true);
|
||||
}
|
||||
yield $fieldFareTable;
|
||||
|
||||
yield NumberField::new ('guestNumber');
|
||||
yield MoneyField::new ('fareAmount')->setCurrency('EUR');
|
||||
|
||||
$dayTypeList = ["MONDAY" => 1, "TUESDAY" => 2, "WEDNESDAY" => 3, "THURSDAY" => 4, "FRIDAY" => 5, "SATURDAY" => 6, "SUNDAY" => 0];
|
||||
yield ChoiceField::new('dayType')->setChoices($dayTypeList);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,18 @@
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Booking;
|
||||
use App\Entity\ConfigVariable;
|
||||
use App\Entity\FareTable;
|
||||
use App\Entity\RoomCategory;
|
||||
use App\Entity\RoomFare;
|
||||
use App\Entity\User;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
use App\Entity\RoomCategory;
|
||||
|
||||
class SomniaDashboardController extends AbstractDashboardController
|
||||
{
|
||||
#[Route('/admin', name: 'admin')]
|
||||
@@ -20,7 +24,7 @@ class SomniaDashboardController extends AbstractDashboardController
|
||||
|
||||
public function configureDashboard(): Dashboard
|
||||
{
|
||||
return Dashboard::new()
|
||||
return Dashboard::new ()
|
||||
->setTitle('SomniaRooms');
|
||||
}
|
||||
|
||||
@@ -28,7 +32,18 @@ class SomniaDashboardController extends AbstractDashboardController
|
||||
{
|
||||
yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
|
||||
|
||||
yield MenuItem::Section('Booking Management');
|
||||
yield MenuItem::linkToCrud('Bookings', 'fas fa-book', Booking::class);
|
||||
|
||||
yield MenuItem::Section('Room Management');
|
||||
yield MenuItem::linkToCrud('Category', 'fas fa-list', RoomCategory::class);
|
||||
yield MenuItem::linkToCrud('Category', 'fas fa-layer-group', RoomCategory::class);
|
||||
yield MenuItem::linkToRoute('Room Availability', 'fas fa-calendar-check', 'admin_roomAvailability');
|
||||
yield MenuItem::linkToCrud('Fare Tables', 'fas fa-table', FareTable::class);
|
||||
yield MenuItem::linkToCrud('Room Fares', 'fas fa-tag', RoomFare::class);
|
||||
|
||||
yield MenuItem::Section('App Management');
|
||||
yield MenuItem::linkToCrud('Config Vars', 'fas fa-gear', ConfigVariable::class);
|
||||
yield MenuItem::linkToCrud('Users', 'fas fa-user', User::class)
|
||||
->setPermission("ROLE_SUPERADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\User;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
class UserCrudController extends AbstractCrudController
|
||||
{
|
||||
public function __construct(
|
||||
public UserPasswordHasherInterface $userPasswordHasher
|
||||
) {}
|
||||
|
||||
public static function getEntityFqcn(): string
|
||||
{
|
||||
return User::class;
|
||||
}
|
||||
|
||||
public function configureFields(string $pageName): iterable
|
||||
{
|
||||
yield TextField::new ('username');
|
||||
yield TextField::new ('username')->onlyWhenUpdating()->setDisabled(true);
|
||||
|
||||
$availableRoles = ['User' => 'ROLE_USER', 'Admin' => 'ROLE_ADMIN', 'Test' => 'ROLE_TEST', 'SuperAdmin' => 'ROLE_SUPERADMIN'];
|
||||
yield ChoiceField::new ('roles')
|
||||
->setChoices($availableRoles)
|
||||
->allowMultipleChoices(true);
|
||||
|
||||
|
||||
yield FormField::addPanel('Set password')->setIcon('fa fa-key');
|
||||
|
||||
$fieldPassword = Field::new ('plainPassword', 'New password')
|
||||
->hideOnIndex()
|
||||
->setRequired(false)
|
||||
->setFormType(RepeatedType::class)
|
||||
->setFormTypeOptions([
|
||||
'type' => PasswordType::class,
|
||||
'first_options' => ['label' => 'New password'],
|
||||
'second_options' => ['label' => 'Repeat password'],
|
||||
'error_bubbling' => true,
|
||||
'invalid_message' => 'The password fields do not match.',
|
||||
]);
|
||||
if($pageName == Crud::PAGE_NEW) {
|
||||
$fieldPassword->setRequired(true);
|
||||
}
|
||||
|
||||
yield $fieldPassword;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param User $entityInstance
|
||||
*/
|
||||
public function persistEntity(EntityManagerInterface $entityManager, $entityInstance): void
|
||||
{
|
||||
//Hash password on creation.
|
||||
$plaintextPassword = $entityInstance->getPlainPassword();
|
||||
|
||||
$hashedPassword = $this->userPasswordHasher->hashPassword($entityInstance, $plaintextPassword);
|
||||
$entityInstance->setPassword($hashedPassword);
|
||||
|
||||
parent::updateEntity($entityManager, $entityInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $entityInstance
|
||||
*/
|
||||
public function updateEntity(EntityManagerInterface $entityManager, $entityInstance): void
|
||||
{
|
||||
$plaintextPassword = $entityInstance->getPlainPassword();
|
||||
|
||||
if($plaintextPassword && $plaintextPassword != "") {
|
||||
//New password set. Hash password.
|
||||
$hashedPassword = $this->userPasswordHasher->hashPassword($entityInstance, $plaintextPassword);
|
||||
$entityInstance->setPassword($hashedPassword);
|
||||
}
|
||||
|
||||
parent::updateEntity($entityManager, $entityInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Booking;
|
||||
use App\Entity\BookingCustomer;
|
||||
use App\Entity\BookingRoom;
|
||||
use App\Repository\BookingRepository;
|
||||
use App\Repository\RoomCategoryRepository;
|
||||
use App\Repository\RoomAvailabilityRepository;
|
||||
use App\Service\PricesService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class BookingController extends AbstractController
|
||||
{
|
||||
#[Route('/api/booking/details', name: 'app_booking_details')]
|
||||
public function getBookingDetails(Request $request, BookingRepository $repoBooking): JsonResponse
|
||||
{
|
||||
$payload = $request->toArray();
|
||||
if (\is_null($payload)) {
|
||||
throw new \Exception('Invalid Payload', 400);
|
||||
}
|
||||
|
||||
$refNumber = $payload["refNumber"];
|
||||
$customerEmail = $payload["email"];
|
||||
|
||||
$bookingList = $repoBooking->findBy(["refNumber" => $refNumber]);
|
||||
if (\is_null($bookingList) || \count($bookingList) == 0) {
|
||||
throw new HttpException(404, 'Invalid Booking');
|
||||
}
|
||||
|
||||
$customerBooking = $bookingList[0];
|
||||
if ($customerBooking->getBookingCustomer()->getEmail() != $customerEmail) {
|
||||
throw new HttpException(404, 'Invalid Booking');
|
||||
}
|
||||
|
||||
return $this->json($customerBooking);
|
||||
}
|
||||
|
||||
#[Route('/api/booking/create', name: 'app_booking_create')]
|
||||
public function newBooking(Request $request, BookingRepository $repoBooking, EntityManagerInterface $entityManager,
|
||||
RoomCategoryRepository $repoCat, RoomAvailabilityRepository $repoAvailability,
|
||||
PricesService $priceSrv): JsonResponse
|
||||
{
|
||||
$payload = $request->toArray();
|
||||
if (\is_null($payload)) {
|
||||
throw new \Exception('Invalid Payload', 400);
|
||||
}
|
||||
|
||||
|
||||
$booking = new Booking();
|
||||
try {
|
||||
$reqArrivalDate = $payload["arrivalDate"];
|
||||
$reqDepartureDate = $payload["departureDate"];
|
||||
|
||||
$arrivalDate = new \DateTime($reqArrivalDate);
|
||||
$departureDate = new \DateTime($reqDepartureDate);
|
||||
|
||||
if ($arrivalDate >= $departureDate) {
|
||||
throw new HttpException(400, "Arrival cant be before departure");
|
||||
}
|
||||
|
||||
$entityManager->beginTransaction();
|
||||
|
||||
$currentTime = new \DateTime();
|
||||
|
||||
// Create booking from request data.
|
||||
$booking->setBookingTime($currentTime);
|
||||
$booking->setArrivalDate($arrivalDate);
|
||||
$booking->setDepartureDate($departureDate);
|
||||
|
||||
$highest_id = $entityManager->createQueryBuilder()
|
||||
->select('MAX(e.id)')
|
||||
->from('App\Entity\Booking', 'e')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
|
||||
$booking->setRefNumber($currentTime->format("Y") . $highest_id + 1);
|
||||
|
||||
$entityManager->persist($booking);
|
||||
|
||||
// Create booking customer.
|
||||
$reqCustomer = $payload["customerDetails"];
|
||||
$bookingCustomer = new BookingCustomer();
|
||||
$bookingCustomer->setName($reqCustomer["name"]);
|
||||
$bookingCustomer->setSurname($reqCustomer["surname"]);
|
||||
$bookingCustomer->setEmail($reqCustomer["email"]);
|
||||
$bookingCustomer->setPhoneNumber($reqCustomer["phoneNumber"]);
|
||||
$bookingCustomer->setBooking($booking);
|
||||
|
||||
$entityManager->persist($bookingCustomer);
|
||||
|
||||
$reqRoomList = $payload["rooms"];
|
||||
if (\count($reqRoomList) == 0) {
|
||||
throw new HttpException(400, "Se debe indicar al menos una habitación");
|
||||
}
|
||||
|
||||
// Create each room.
|
||||
foreach ($reqRoomList as $roomItem) {
|
||||
$room = new BookingRoom();
|
||||
$room->setBooking($booking);
|
||||
|
||||
//Get Room category.
|
||||
$roomCategory = $repoCat->find($roomItem["roomCategory"]);
|
||||
if ($roomCategory == null) {
|
||||
throw new HttpException(400, "La RoomCategory no es válida.");
|
||||
}
|
||||
|
||||
//Check room availability.
|
||||
$amountAvailable = $repoAvailability->getAvailabilityForCategoryInPeriod($roomCategory, $arrivalDate, $departureDate);
|
||||
$selectedAmount = $roomItem['amount'];
|
||||
$selectedGuestAmount = $roomItem["guestNumber"];
|
||||
|
||||
if($amountAvailable == null || $selectedAmount > $amountAvailable) {
|
||||
throw new HttpException(400, "No se pueden reservar más habitaciones de las disponibles.");
|
||||
}
|
||||
|
||||
$room->setRoomCategory($roomCategory);
|
||||
$room->setGuestNumber($selectedGuestAmount);
|
||||
$room->setAmount($selectedAmount);
|
||||
|
||||
//Get Price for period.
|
||||
$priceList = $priceSrv->getFinalPricesForSearch($roomCategory, $arrivalDate, $departureDate);
|
||||
$priceFound = false;
|
||||
foreach ($priceList as $key => $value) {
|
||||
if($key == $selectedGuestAmount) {
|
||||
$room->setRoomPrice($value);
|
||||
$priceFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$priceFound) {
|
||||
throw new HttpException(500,
|
||||
"No se puedo obtener el precio para la categoría: " . $roommCategory->getId() . " , guests: " . $selecctedGuestAmount);
|
||||
}
|
||||
|
||||
$entityManager->persist($room);
|
||||
|
||||
//If booking is nice, let's update current availability.
|
||||
$repoAvailability->updateAvailabilityForRoomCategoryOnPeriod($roomCategory, $arrivalDate, $departureDate, $selectedAmount, true); //Update using "diff" mode.
|
||||
}
|
||||
|
||||
$entityManager->commit();
|
||||
$entityManager->flush();
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$entityManager->rollback();
|
||||
throw $th;
|
||||
}
|
||||
|
||||
$results = ["refNumber" => $booking->getRefNumber()];
|
||||
|
||||
return $this->json($results);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use App\Repository\ConfigVariableRepository;
|
||||
|
||||
class ConfigVariableController extends AbstractController
|
||||
{
|
||||
#[Route('/api/config', name: 'app_config_variable')]
|
||||
public function getAllVariables(ConfigVariableRepository $repo): Response
|
||||
{
|
||||
$varList = $repo->findAllVariables();
|
||||
|
||||
return $this->json(['results' => $varList]);
|
||||
}
|
||||
|
||||
#[Route('/api/config/section', name: 'app_config_sections')]
|
||||
public function getSections(ConfigVariableRepository $repo): Response
|
||||
{
|
||||
$sectionList = $repo->getAllSections();
|
||||
$returnList = [];
|
||||
foreach ($sectionList as $section) {
|
||||
$sectionValue = $section["section"];
|
||||
$returnList[] = $sectionValue;
|
||||
}
|
||||
|
||||
return $this->json(['results' => $returnList]);
|
||||
}
|
||||
|
||||
#[Route('/api/config/section/{section}', name: 'app_config_variableBySection')]
|
||||
public function getVariablesBySection(ConfigVariableRepository $repo, string $section): Response
|
||||
{
|
||||
if(\is_null($section) || $section == '') {
|
||||
return new Response("Invalid section value", 404);
|
||||
}
|
||||
|
||||
$varList = $repo->findAllBySection($section);
|
||||
|
||||
return $this->json(['results' => $varList]);
|
||||
}
|
||||
|
||||
#[Route('/api/config/var/{key}', name: 'app_config_variable')]
|
||||
public function getByKey(ConfigVariableRepository $repo, string $key): Response
|
||||
{
|
||||
$var = $repo->findVariableByKey($key);
|
||||
|
||||
return $this->json($var);
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,16 @@ use Symfony\Component\Serializer\Context\Normalizer\ObjectNormalizerContextBuild
|
||||
class RoomCategoryController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route('/room/category', name: 'app_room_category')]
|
||||
public function getAll(RoomCategoryRepository $catRepo, SerializerInterface $serializer): Response
|
||||
#[Route('/api/room/category', name: 'app_room_AllCategories')]
|
||||
public function getAll(RoomCategoryRepository $catRepo): Response
|
||||
{
|
||||
$catList = $catRepo->findAll();
|
||||
|
||||
return $this->json(["results" => $catList]);
|
||||
}
|
||||
|
||||
#[Route('/room/category/{id}', name: 'app_room_category')]
|
||||
public function getById(RoomCategory $category, SerializerInterface $serializer): Response
|
||||
#[Route('/api/room/category/{id}', name: 'app_room_category')]
|
||||
public function getById(RoomCategory $category): Response
|
||||
{
|
||||
return $this->json($category);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\FareTableRepository;
|
||||
use App\Repository\RoomAvailabilityRepository;
|
||||
use App\Repository\RoomCategoryRepository;
|
||||
use App\Service\PricesService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class SearchRoomsController extends AbstractController
|
||||
{
|
||||
#[Route('/api/search/availability', name: 'app_search_availability')]
|
||||
public function getAvailabilityForPeriod(Request $request, RoomAvailabilityRepository $repoAvailability): Response
|
||||
{
|
||||
$payload = $request->toArray();
|
||||
if (\is_null($payload)) {
|
||||
throw new \Exception('Invalid Payload', 400);
|
||||
}
|
||||
$filter = $payload["filter"];
|
||||
|
||||
$startDate = new \DateTime($filter["startDate"]);
|
||||
$endDate = new \DateTime($filter["endDate"]);
|
||||
|
||||
$rs = $repoAvailability->getAvailabilityForPeriod($startDate, $endDate);
|
||||
|
||||
return $this->json(["results" => $rs]);
|
||||
}
|
||||
|
||||
#[Route('/api/search/availabilityDetails', name: 'app_search_availabilityDetails')]
|
||||
public function getAvailabilityForPeriodDetails(Request $request, RoomAvailabilityRepository $repoAvailability): Response
|
||||
{
|
||||
$payload = $request->toArray();
|
||||
if (\is_null($payload)) {
|
||||
throw new \Exception('Invalid Payload', 400);
|
||||
}
|
||||
$filter = $payload["filter"];
|
||||
|
||||
$startDate = new \DateTime($filter["startDate"]);
|
||||
$endDate = new \DateTime($filter["endDate"]);
|
||||
|
||||
$rs = $repoAvailability->getAvailabilityForPeriodDetails($startDate, $endDate);
|
||||
|
||||
return $this->json($rs);
|
||||
}
|
||||
|
||||
#[Route('/api/search/fares', name: 'app_search_faresForCategory')]
|
||||
public function getFaresForCategoryOnPeriod(Request $request, RoomCategoryRepository $repoCat, PricesService $priceService): Response
|
||||
{
|
||||
$payload = $request->toArray();
|
||||
if (\is_null($payload)) {
|
||||
throw new \Exception('Invalid Payload', 400);
|
||||
}
|
||||
$filter = $payload["filter"];
|
||||
|
||||
$catId = $filter["roomCategory"];
|
||||
$roomCategory = $repoCat->find($catId);
|
||||
if ($roomCategory === null) {
|
||||
throw new \Exception('Invalid Category', 400);
|
||||
}
|
||||
|
||||
$startDate = new \DateTime($filter["startDate"]);
|
||||
$endDate = new \DateTime($filter["endDate"]);
|
||||
|
||||
$daysDiff = $startDate->diff($endDate)->days;
|
||||
if (!$daysDiff || $startDate > $endDate) {
|
||||
throw new \Exception('Invalid time interval', 400);
|
||||
}
|
||||
|
||||
$fareList = $priceService->getFinalPricesForSearch($roomCategory, $startDate, $endDate);
|
||||
$rs = [];
|
||||
foreach ($fareList as $key => $value) {
|
||||
$rs[] = [ "guestNumber" => $key, "price" => $value];
|
||||
}
|
||||
|
||||
return $this->json(["results" => $rs]);
|
||||
}
|
||||
|
||||
#[Route('/search/debug', name: 'app_search_faresDebug')]
|
||||
public function faresDebug(Request $request, PricesService $priceService, FareTableRepository $repoFareTable, RoomCategoryRepository $repoCat): Response
|
||||
{
|
||||
$fareTable[] = $repoFareTable->find(1);
|
||||
$fareTable[] = $repoFareTable->find(2);
|
||||
$cat = $repoCat->find(4);
|
||||
|
||||
$startDate = new \DateTime("2024-04-27");
|
||||
$endDate = new \DateTime("2024-05-02");
|
||||
|
||||
$rs = $priceService->getFinalPricesForSearch($cat, $startDate, $endDate);
|
||||
|
||||
return $this->json($rs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\UserRepository;
|
||||
|
||||
class SecurityController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/login', name: 'app_login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
// get the login error if there is one
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
|
||||
// last username entered by the user
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('security/login.html.twig', [
|
||||
'last_username' => $lastUsername,
|
||||
'error' => $error,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(path: '/logout', name: 'app_logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
|
||||
#[Route(path: '/addAdmin', name: 'app_security_addAdmin')]
|
||||
public function addAdmin(UserPasswordHasherInterface $passwordHasher, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
$user = new User();
|
||||
$user->setUsername("admin");
|
||||
$user->setRoles(["ROLE_USER", "ROLE_ADMIN"]);
|
||||
$plaintextPassword = "admin";
|
||||
|
||||
// hash the password (based on the security.yaml config for the $user class)
|
||||
$hashedPassword = $passwordHasher->hashPassword(
|
||||
$user,
|
||||
$plaintextPassword
|
||||
);
|
||||
$user->setPassword($hashedPassword);
|
||||
|
||||
$entityManager->persist($user);
|
||||
$entityManager->flush();
|
||||
|
||||
return new Response("Created user - Admin");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\BookingRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BookingRepository::class)]
|
||||
class Booking
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_MUTABLE)]
|
||||
private ?\DateTimeInterface $ArrivalDate = null;
|
||||
|
||||
#[ORM\Column(type : Types::DATE_MUTABLE)]
|
||||
private ?\DateTimeInterface $DepartureDate = null;
|
||||
|
||||
#[ORM\Column(type : Types::DATETIMETZ_MUTABLE)]
|
||||
private ?\DateTimeInterface $BookingTime = null;
|
||||
|
||||
#[ORM\OneToOne(mappedBy : 'Booking', cascade: ['persist', 'remove'])]
|
||||
private ?BookingCustomer $bookingCustomer = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, BookingRoom>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: BookingRoom::class, mappedBy: 'Booking', orphanRemoval: true, cascade: ['persist'])]
|
||||
private Collection $bookingRooms;
|
||||
|
||||
#[ORM\Column(length: 20)]
|
||||
private ?string $refNumber = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->bookingRooms = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getArrivalDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->ArrivalDate;
|
||||
}
|
||||
|
||||
public function setArrivalDate(\DateTimeInterface $ArrivalDate): static
|
||||
{
|
||||
$this->ArrivalDate = $ArrivalDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDepartureDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->DepartureDate;
|
||||
}
|
||||
|
||||
public function setDepartureDate(\DateTimeInterface $DepartureDate): static
|
||||
{
|
||||
$this->DepartureDate = $DepartureDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBookingTime(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->BookingTime;
|
||||
}
|
||||
|
||||
public function setBookingTime(\DateTimeInterface $BookingTime): static
|
||||
{
|
||||
$this->BookingTime = $BookingTime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBookingCustomer(): ?BookingCustomer
|
||||
{
|
||||
return $this->bookingCustomer;
|
||||
}
|
||||
|
||||
public function setBookingCustomer(BookingCustomer $bookingCustomer): static
|
||||
{
|
||||
// set the owning side of the relation if necessary
|
||||
if ($bookingCustomer->getBooking() !== $this) {
|
||||
$bookingCustomer->setBooking($this);
|
||||
}
|
||||
|
||||
$this->bookingCustomer = $bookingCustomer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, BookingRoom>
|
||||
*/
|
||||
public function getBookingRooms(): Collection
|
||||
{
|
||||
return $this->bookingRooms;
|
||||
}
|
||||
|
||||
public function addBookingRoom(BookingRoom $bookingRoom): static
|
||||
{
|
||||
if (!$this->bookingRooms->contains($bookingRoom)) {
|
||||
$this->bookingRooms->add($bookingRoom);
|
||||
$bookingRoom->setBooking($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeBookingRoom(BookingRoom $bookingRoom): static
|
||||
{
|
||||
if ($this->bookingRooms->removeElement($bookingRoom)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($bookingRoom->getBooking() === $this) {
|
||||
$bookingRoom->setBooking(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRefNumber(): ?string
|
||||
{
|
||||
return $this->refNumber;
|
||||
}
|
||||
|
||||
public function setRefNumber(string $refNumber): static
|
||||
{
|
||||
$this->refNumber = $refNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
function __toString(): string
|
||||
{
|
||||
return $this->getRefNumber();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\BookingCustomerRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Ignore;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BookingCustomerRepository::class)]
|
||||
class BookingCustomer
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\OneToOne(inversedBy: 'bookingCustomer', cascade: ['persist', 'remove'])]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: "CASCADE")]
|
||||
#[Ignore]
|
||||
private ?Booking $Booking = null;
|
||||
|
||||
#[ORM\Column(length: 64)]
|
||||
private ?string $Name = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $Surname = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $Email = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $PhoneNumber = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function getBooking(): ?Booking
|
||||
{
|
||||
return $this->Booking;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function setBooking(Booking $Booking): static
|
||||
{
|
||||
$this->Booking = $Booking;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->Name;
|
||||
}
|
||||
|
||||
public function setName(string $Name): static
|
||||
{
|
||||
$this->Name = $Name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSurname(): ?string
|
||||
{
|
||||
return $this->Surname;
|
||||
}
|
||||
|
||||
public function setSurname(string $Surname): static
|
||||
{
|
||||
$this->Surname = $Surname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmail(): ?string
|
||||
{
|
||||
return $this->Email;
|
||||
}
|
||||
|
||||
public function setEmail(string $Email): static
|
||||
{
|
||||
$this->Email = $Email;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPhoneNumber(): ?string
|
||||
{
|
||||
return $this->PhoneNumber;
|
||||
}
|
||||
|
||||
public function setPhoneNumber(string $PhoneNumber): static
|
||||
{
|
||||
$this->PhoneNumber = $PhoneNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
function __toString(): string
|
||||
{
|
||||
return $this->getName() . " " . $this->getSurname();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\BookingRoomRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Ignore;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BookingRoomRepository::class)]
|
||||
class BookingRoom
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'bookingRooms')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: "CASCADE")]
|
||||
#[Ignore]
|
||||
private ?Booking $Booking = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
#[Ignore]
|
||||
private ?RoomCategory $RoomCategory = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $GuestNumber = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $RoomPrice = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $Amount = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function getBooking(): ?Booking
|
||||
{
|
||||
return $this->Booking;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function setBooking(?Booking $Booking): static
|
||||
{
|
||||
$this->Booking = $Booking;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function getRoomCategory(): ?RoomCategory
|
||||
{
|
||||
return $this->RoomCategory;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function setRoomCategory(?RoomCategory $RoomCategory): static
|
||||
{
|
||||
$this->RoomCategory = $RoomCategory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRoomCategoryId(): int
|
||||
{
|
||||
return $this->getRoomCategory()->getId();
|
||||
}
|
||||
|
||||
public function getGuestNumber(): ?int
|
||||
{
|
||||
return $this->GuestNumber;
|
||||
}
|
||||
|
||||
public function setGuestNumber(int $GuestNumber): static
|
||||
{
|
||||
$this->GuestNumber = $GuestNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRoomPrice(): ?int
|
||||
{
|
||||
return $this->RoomPrice;
|
||||
}
|
||||
|
||||
public function setRoomPrice(int $RoomPrice): static
|
||||
{
|
||||
$this->RoomPrice = $RoomPrice;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAmount(): ?int
|
||||
{
|
||||
return $this->Amount;
|
||||
}
|
||||
|
||||
public function setAmount(int $Amount): static
|
||||
{
|
||||
$this->Amount = $Amount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
function __toString(): string
|
||||
{
|
||||
return $this->getAmount() . " " . $this->getRoomCategory()->getName() . " - " . $this->getGuestNumber() . " Guests";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ConfigVariableRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ConfigVariableRepository::class)]
|
||||
class ConfigVariable
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 32, unique: true)]
|
||||
private ?string $key = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $value = null;
|
||||
|
||||
#[ORM\Column(length: 64, nullable: true)]
|
||||
private ?string $section = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getKey(): ?string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setKey(string $key): static
|
||||
{
|
||||
$this->key = $key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue(): ?string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(string $value): static
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSection(): ?string
|
||||
{
|
||||
return $this->section;
|
||||
}
|
||||
|
||||
public function setSection(?string $section): static
|
||||
{
|
||||
$this->section = $section;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\FareTableRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: FareTableRepository::class)]
|
||||
class FareTable
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: "CASCADE")]
|
||||
private ?RoomCategory $roomCategory = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
|
||||
private ?\DateTimeInterface $startDate = null;
|
||||
|
||||
#[ORM\Column(type : Types::DATETIME_MUTABLE, nullable: true)]
|
||||
private ?\DateTimeInterface $endDate = null;
|
||||
|
||||
#[ORM\Column(length : 255, nullable: true)]
|
||||
private ?string $comment = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, RoomFare>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: RoomFare::class, mappedBy: 'fareTable', orphanRemoval: true)]
|
||||
private Collection $roomFares;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->roomFares = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getRoomCategory(): ?RoomCategory
|
||||
{
|
||||
return $this->roomCategory;
|
||||
}
|
||||
|
||||
public function setRoomCategory(?RoomCategory $roomCategory): static
|
||||
{
|
||||
$this->roomCategory = $roomCategory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStartDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function setStartDate(\DateTimeInterface $startDate): static
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function setEndDate(?\DateTimeInterface $endDate): static
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getComment(): ?string
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
public function setComment(?string $comment): static
|
||||
{
|
||||
$this->comment = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, RoomFare>
|
||||
*/
|
||||
public function getRoomFares(): Collection
|
||||
{
|
||||
return $this->roomFares;
|
||||
}
|
||||
|
||||
public function addRoomFare(RoomFare $roomFare): static
|
||||
{
|
||||
if (!$this->roomFares->contains($roomFare)) {
|
||||
$this->roomFares->add($roomFare);
|
||||
$roomFare->setFareTable($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeRoomFare(RoomFare $roomFare): static
|
||||
{
|
||||
if ($this->roomFares->removeElement($roomFare)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($roomFare->getFareTable() === $this) {
|
||||
$roomFare->setFareTable(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
if (!\is_null($this->comment)) {
|
||||
return $this->comment;
|
||||
}
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get max guest number for current roomFares.
|
||||
*/
|
||||
public function getMaxGuestNumber(): int
|
||||
{
|
||||
$maxGuests = $this->getRoomFares()->reduce(function (int $acc, RoomFare $value): int {
|
||||
return \max($acc, $value->getGuestNumber());
|
||||
}, -1);
|
||||
|
||||
return $maxGuests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get min guest number for current roomFares.
|
||||
*/
|
||||
public function getMinGuestNumber(): int
|
||||
{
|
||||
$firstGuestNum = $this->getRoomFares()->first()->getGuestNumber() ?? 999;
|
||||
|
||||
$maxGuests = $this->getRoomFares()->reduce(function (int $acc, RoomFare $value): int {
|
||||
return \min($acc, $value->getGuestNumber());
|
||||
}, $firstGuestNum);
|
||||
|
||||
return $maxGuests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene las tarifas que aplican al GuestNumber indicado.
|
||||
* @param int $guestNumber GuestNumber para búsqueda.
|
||||
* @return RoomFare[] Lista de tarifas.
|
||||
*/
|
||||
public function getRoomFaresByGuestNumber(int $guestNumber)
|
||||
{
|
||||
$roomFareList = $this->getRoomFares()->filter(function (RoomFare $element) use ($guestNumber) {
|
||||
return $element->getGuestNumber() == $guestNumber;
|
||||
});
|
||||
|
||||
return $roomFareList;
|
||||
}
|
||||
|
||||
public function isActiveOnDate(\DateTimeInterface $date): bool
|
||||
{
|
||||
$active = ($this->startDate <= $date && $this->endDate > $date);
|
||||
return $active;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\RoomAvailabilityRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: RoomAvailabilityRepository::class)]
|
||||
class RoomAvailability
|
||||
{
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column]
|
||||
private ?string $day = null;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\ManyToOne(targetEntity: RoomCategory::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: "CASCADE")]
|
||||
private ?RoomCategory $roomCategory = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $numAvailable = null;
|
||||
|
||||
public function getDay(): ?\DateTimeInterface
|
||||
{
|
||||
return new \DateTime($this->day);
|
||||
}
|
||||
|
||||
public function setDay(\DateTimeInterface $day): static
|
||||
{
|
||||
$this->day = $day->format("Y-m-d");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRoomCategory(): ?RoomCategory
|
||||
{
|
||||
return $this->roomCategory;
|
||||
}
|
||||
|
||||
public function setRoomCategory(?RoomCategory $roomCategory): static
|
||||
{
|
||||
$this->roomCategory = $roomCategory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumAvailable(): ?int
|
||||
{
|
||||
return $this->numAvailable;
|
||||
}
|
||||
|
||||
public function setNumAvailable(int $numAvailable): static
|
||||
{
|
||||
$this->numAvailable = $numAvailable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function modNumAvailable(int $diffAmount):void {
|
||||
$this->numAvailable -= $diffAmount;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace App\Entity;
|
||||
use App\Repository\RoomCategoryRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Symfony\Component\Serializer\Annotation\Ignore;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
@@ -159,4 +158,9 @@ class RoomCategory
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\RoomFareRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: RoomFareRepository::class)]
|
||||
class RoomFare
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'roomFares')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?FareTable $fareTable = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $guestNumber = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $fareAmount = null;
|
||||
|
||||
#[ORM\Column(length: 2, nullable: true)]
|
||||
private ?string $dayType = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getFareTable(): ?FareTable
|
||||
{
|
||||
return $this->fareTable;
|
||||
}
|
||||
|
||||
public function setFareTable(?FareTable $fareTable): static
|
||||
{
|
||||
$this->fareTable = $fareTable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGuestNumber(): ?int
|
||||
{
|
||||
return $this->guestNumber;
|
||||
}
|
||||
|
||||
public function setGuestNumber(int $guestNumber): static
|
||||
{
|
||||
$this->guestNumber = $guestNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFareAmount(): ?int
|
||||
{
|
||||
return $this->fareAmount;
|
||||
}
|
||||
|
||||
public function setFareAmount(int $fareAmount): static
|
||||
{
|
||||
$this->fareAmount = $fareAmount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDayType(): ?string
|
||||
{
|
||||
return $this->dayType;
|
||||
}
|
||||
|
||||
public function setDayType(?string $dayType): static
|
||||
{
|
||||
$this->dayType = $dayType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
#[ORM\Column]
|
||||
private ?string $password = null;
|
||||
|
||||
/**
|
||||
* @var string Store unhashed user password on user creation/edit.
|
||||
*/
|
||||
private ?string $plainPassword = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -98,6 +103,19 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function getPlainPassword(): ?string
|
||||
{
|
||||
return $this->plainPassword;
|
||||
}
|
||||
|
||||
public function setPlainPassword(string $plainPassword): static
|
||||
{
|
||||
$this->plainPassword = $plainPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\BookingCustomer;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<BookingCustomer>
|
||||
*
|
||||
* @method BookingCustomer|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method BookingCustomer|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method BookingCustomer[] findAll()
|
||||
* @method BookingCustomer[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class BookingCustomerRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, BookingCustomer::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return BookingCustomer[] Returns an array of BookingCustomer objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('b.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?BookingCustomer
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Booking;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Booking>
|
||||
*
|
||||
* @method Booking|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Booking|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Booking[] findAll()
|
||||
* @method Booking[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class BookingRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Booking::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Booking[] Returns an array of Booking objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('b.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Booking
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\BookingRoom;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<BookingRoom>
|
||||
*
|
||||
* @method BookingRoom|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method BookingRoom|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method BookingRoom[] findAll()
|
||||
* @method BookingRoom[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class BookingRoomRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, BookingRoom::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return BookingRoom[] Returns an array of BookingRoom objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('b.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?BookingRoom
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\ConfigVariable;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<ConfigVariable>
|
||||
*
|
||||
* @method ConfigVariable|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method ConfigVariable|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method ConfigVariable[] findAll()
|
||||
* @method ConfigVariable[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ConfigVariableRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, ConfigVariable::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all config variables for API.
|
||||
*/
|
||||
public function findAllVariables(): array
|
||||
{
|
||||
return $this->createQueryBuilder('c')
|
||||
->select('c.key, c.value, c.section')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string Config section.
|
||||
* @return ConfigVariable[] Returns an array of ConfigVariable objects
|
||||
*/
|
||||
public function findAllBySection(string $section): array
|
||||
{
|
||||
return $this->createQueryBuilder('c')
|
||||
->select('c.key, c.value')
|
||||
->andWhere('c.section = :val')
|
||||
->setParameter('val', $section)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Config Variable by it's key.
|
||||
* @param string Variable Key
|
||||
* @return ConfigVariable|null Config Variable.
|
||||
*/
|
||||
public function findVariableByKey($varKey)
|
||||
{
|
||||
return $this->createQueryBuilder('c')
|
||||
->select('c.key, c.value, c.section')
|
||||
->andWhere('c.key = :val')
|
||||
->setParameter('val', $varKey)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all section names that contains config variables.
|
||||
* @return string[] Returns an array of strings.
|
||||
*/
|
||||
public function getAllSections(): array
|
||||
{
|
||||
return $this->createQueryBuilder('c')
|
||||
->select('c.section')
|
||||
->distinct()
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\FareTable;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<FareTable>
|
||||
*
|
||||
* @method FareTable|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method FareTable|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method FareTable[] findAll()
|
||||
* @method FareTable[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class FareTableRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, FareTable::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all fareTables for time period.
|
||||
* @param integer|string ID Categoria.
|
||||
* @param DateTimeInterface Period start date
|
||||
* @param DateTimeInterface Period end date
|
||||
* @return array Array with all faretables active in given period.
|
||||
*/
|
||||
public function findFareTable(int $roomCategory, \DateTimeInterface $startDate, \DateTimeInterface $endDate): array
|
||||
{
|
||||
$strStartDate = $startDate->format('Y-m-d');
|
||||
$strEndDate = $endDate->format('Y-m-d');
|
||||
|
||||
return $this->createQueryBuilder('f')
|
||||
->andWhere('f.roomCategory = :category')
|
||||
->andWhere('f.startDate < :endDate')
|
||||
->andWhere('f.endDate >= :startDate')
|
||||
->setParameter('category', $roomCategory)
|
||||
->setParameter('startDate', $strStartDate)
|
||||
->setParameter('endDate', $strEndDate)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene una lista con los números de huéspedes permitidos por todos los tarifarios pasados.
|
||||
* @param FareTable[] $fareTableList Lista con los FareTable a consultar.
|
||||
* @return int[] Lista con los números de huéspedes permitidos.
|
||||
*/
|
||||
public function getAllowedGuestNumberOnFareTableList(array $fareTableList): array
|
||||
{
|
||||
$fareTableCount = count($fareTableList);
|
||||
if($fareTableCount == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fareTableIdList = \array_map(function (FareTable $element) {
|
||||
return $element->getId();
|
||||
}, $fareTableList);
|
||||
$strIdList = \implode(",", $fareTableIdList);
|
||||
|
||||
//Subquery -> Obtiene los números de huéspedes de las tarifas de un tarifario.
|
||||
//Query -> Cuenta en cuantos tarifarios aparece cada número de huéspedes y queda con aquellos que aparecen en todos (having).
|
||||
$sql = "SELECT guest_number
|
||||
FROM (SELECT DISTINCT fare_table_id, guest_number FROM room_fare WHERE fare_table_id IN ($strIdList))
|
||||
GROUP BY guest_number
|
||||
HAVING COUNT(fare_table_id) = $fareTableCount";
|
||||
|
||||
$conn = $this->getEntityManager()->getConnection();
|
||||
$rs = $conn->executeQuery($sql);
|
||||
|
||||
$allowedGuestNumbers = [];
|
||||
while (true) {
|
||||
$value = $rs->fetchOne();
|
||||
if (!$value) {
|
||||
break;
|
||||
}
|
||||
$allowedGuestNumbers[] = $value;
|
||||
}
|
||||
|
||||
return $allowedGuestNumbers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\RoomAvailability;
|
||||
use App\Entity\RoomCategory;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<RoomAvailability>
|
||||
*
|
||||
* @method RoomAvailability|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method RoomAvailability|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method RoomAvailability[] findAll()
|
||||
* @method RoomAvailability[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class RoomAvailabilityRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, RoomAvailability::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RoomAvailability[] Returns an array of RoomAvailability objects
|
||||
*/
|
||||
public function findByRoomCategory($categoryId): array
|
||||
{
|
||||
return $this->createQueryBuilder('a')
|
||||
->andWhere('a.roomCategory = :val')
|
||||
->setParameter('val', $categoryId)
|
||||
->orderBy('a.day', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeInterface Period start (included in range).
|
||||
* @param \DateTimeInterface Period end (not included in range).
|
||||
* @return RoomAvailability[] Returns an array of RoomAvailability objects
|
||||
*/
|
||||
public function getAvailabilityForPeriod(\DateTimeInterface $startDate, \DateTimeInterface $endDate): array
|
||||
{
|
||||
$strStartDate = $startDate->format('Y-m-d');
|
||||
$strEndDate = $endDate->format('Y-m-d');
|
||||
|
||||
return $this->createQueryBuilder('ra')
|
||||
->select('IDENTITY(ra.roomCategory) as roomCategory, MIN(ra.numAvailable) as availability')
|
||||
->andWhere('ra.day >= :startDate')
|
||||
->andWhere('ra.day < :endDate')
|
||||
->groupBy('ra.roomCategory')
|
||||
->setParameter('startDate', $strStartDate)
|
||||
->setParameter('endDate', $strEndDate)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeInterface Period start (included in range).
|
||||
* @param \DateTimeInterface Period end (not included in range).
|
||||
* @return int Returns number of available room of that category on selected period.
|
||||
*/
|
||||
public function getAvailabilityForCategoryInPeriod(RoomCategory $roomCategory, \DateTimeInterface $startDate, \DateTimeInterface $endDate): int | null
|
||||
{
|
||||
$strStartDate = $startDate->format('Y-m-d');
|
||||
$strEndDate = $endDate->format('Y-m-d');
|
||||
$roomCatId = $roomCategory->getId();
|
||||
|
||||
return $this->createQueryBuilder('ra')
|
||||
->select('MIN(ra.numAvailable) as availability')
|
||||
->andWhere('ra.day >= :startDate')
|
||||
->andWhere('ra.day < :endDate')
|
||||
->andWhere('ra.roomCategory = :roomCategory')
|
||||
->setParameter('roomCategory', $roomCatId)
|
||||
->setParameter('startDate', $strStartDate)
|
||||
->setParameter('endDate', $strEndDate)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function getAvailabilityForPeriodDetails(\DateTimeInterface $startDate, \DateTimeInterface $endDate): array
|
||||
{
|
||||
$strStartDate = $startDate->format('Y-m-d');
|
||||
$strEndDate = $endDate->format('Y-m-d');
|
||||
|
||||
return $this->createQueryBuilder('ra')
|
||||
->andWhere('ra.day >= :startDate')
|
||||
->andWhere('ra.day < :endDate')
|
||||
->setParameter('startDate', $strStartDate)
|
||||
->setParameter('endDate', $strEndDate)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates All Availability of selected roomCategory during period. It can operate in 2 modes, normal and diff mode.
|
||||
* - Normal mode -> Sets value.
|
||||
* - Diff mode -> apply value as sum ( currentAvailability += diff )
|
||||
* NOTE: A Flush is needed after this operation.
|
||||
*/
|
||||
public function updateAvailabilityForRoomCategoryOnPeriod(RoomCategory $roomCategory, \DateTimeInterface $startDate, \DateTimeInterface $endDate, int $amount, bool $isDiff = false): void
|
||||
{
|
||||
$strStartDate = $startDate->format('Y-m-d');
|
||||
$strEndDate = $endDate->format('Y-m-d');
|
||||
$roomCatId = $roomCategory->getId();
|
||||
|
||||
$availabilityItemList = $this->createQueryBuilder('ra')
|
||||
->andWhere('ra.day >= :startDate')
|
||||
->andWhere('ra.day < :endDate')
|
||||
->andWhere('ra.roomCategory = :roomCategory')
|
||||
->setParameter('roomCategory', $roomCatId)
|
||||
->setParameter('startDate', $strStartDate)
|
||||
->setParameter('endDate', $strEndDate)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
foreach ($availabilityItemList as $item) {
|
||||
if ($isDiff) {
|
||||
$item->modNumAvailable($amount);
|
||||
} else {
|
||||
$item->setNumAvailable($amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given RoomCategory, sets its availability for all days on a period with the given amount.
|
||||
*/
|
||||
public function bulkEditRoomAvailabilityForCategoryOnPeriod(RoomCategory $roomCategory, \DateTimeInterface $startDate, \DateTimeInterface $endDate, int $amount): void
|
||||
{
|
||||
$strStartDate = $startDate->format('Y-m-d');
|
||||
$strEndDate = $endDate->format('Y-m-d');
|
||||
$roomCatId = $roomCategory->getId();
|
||||
|
||||
$entityManager = $this->getEntityManager();
|
||||
$entityManager->beginTransaction();
|
||||
|
||||
try {
|
||||
|
||||
//First, delete old data.
|
||||
$this->createQueryBuilder('ra')
|
||||
->delete()
|
||||
->andWhere('ra.day >= :startDate')
|
||||
->andWhere('ra.day < :endDate')
|
||||
->andWhere('ra.roomCategory = :roomCategory')
|
||||
->setParameter('roomCategory', $roomCatId)
|
||||
->setParameter('startDate', $strStartDate)
|
||||
->setParameter('endDate', $strEndDate)
|
||||
->getQuery()
|
||||
->execute();
|
||||
|
||||
$entityManager->flush();
|
||||
|
||||
//For each day, create item and persist it.
|
||||
$daterange = new \DatePeriod($startDate, new \DateInterval('P1D'), $endDate);
|
||||
foreach ($daterange as $date) {
|
||||
$item = new RoomAvailability();
|
||||
$item->setRoomCategory($roomCategory);
|
||||
$item->setDay($date);
|
||||
$item->setNumAvailable($amount);
|
||||
|
||||
$entityManager->persist($item);
|
||||
}
|
||||
|
||||
$entityManager->flush();
|
||||
$entityManager->commit();
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$entityManager->rollback();
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\RoomFare;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<RoomFare>
|
||||
*
|
||||
* @method RoomFare|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method RoomFare|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method RoomFare[] findAll()
|
||||
* @method RoomFare[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class RoomFareRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, RoomFare::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return RoomFare[] Returns an array of RoomFare objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('r')
|
||||
// ->andWhere('r.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('r.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?RoomFare
|
||||
// {
|
||||
// return $this->createQueryBuilder('r')
|
||||
// ->andWhere('r.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\FareTable;
|
||||
use App\Entity\RoomCategory;
|
||||
use App\Repository\FareTableRepository;
|
||||
use App\Repository\RoomFareRepository;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* Servicio para calcular los precios de las estancias.
|
||||
* En un futuro este servicio deberá admitir múltiples estrategias de cálculo de precios finales.
|
||||
*/
|
||||
class PricesService
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
private FareTableRepository $repoFareTable,
|
||||
private RoomFareRepository $repoRoomFares
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula los precios de la estancia para la categoría y periodo indicados.
|
||||
* Devolverá para cada numero de personas, el precio final de la estancia.
|
||||
*
|
||||
* e. g. --> [ 2 => 120€, 3 => 180€ ]
|
||||
*
|
||||
* @return array Contiene los precios en función del número de personas.
|
||||
*/
|
||||
public function getFinalPricesForSearch(RoomCategory $category, \DateTimeInterface $startDate, \DateTimeInterface $endDate): array
|
||||
{
|
||||
$daysDiff = $startDate->diff($endDate)->days;
|
||||
if (!$daysDiff) {
|
||||
return "Invalid date period :( ";
|
||||
}
|
||||
|
||||
$fareTablesForPeriod = $this->repoFareTable->findFareTable($category->getId(), $startDate, $endDate);
|
||||
|
||||
//Obtenemos el número de personas permitido por todos los tarifarios activos.
|
||||
$allowedGuestNumbers = $this->repoFareTable->getAllowedGuestNumberOnFareTableList($fareTablesForPeriod);
|
||||
|
||||
$rs = [];
|
||||
|
||||
//Calculamos los precios para cada GuestNumber permitido.
|
||||
foreach ($allowedGuestNumbers as $currentGuestNumber) {
|
||||
|
||||
//Calculamos el precio de cada día.
|
||||
// e.g. --> [ "2024-04-30" => 20, "2024-05-01" => 28, "2024-05-02" => 20, ...].
|
||||
$dailyPrices = $this->getDailyPricesForSearch($startDate, $endDate, $currentGuestNumber, $fareTablesForPeriod);
|
||||
|
||||
$rs[$currentGuestNumber] = $dailyPrices;
|
||||
}
|
||||
|
||||
//Aplicamos la estrategia de precios configurada para calcular el precio final de la estancia.
|
||||
// TODOME: Crear y configurar diversas estrategias de precios -> Por el momento utilizamos "Suma del precio de cada día".
|
||||
|
||||
$finalResult = [];
|
||||
foreach ($rs as $guestNum => $priceArray) {
|
||||
$finalAmount = \array_reduce($priceArray, function ($acc, $element) {
|
||||
return $acc + $element;
|
||||
});
|
||||
|
||||
$finalResult[$guestNum] = $finalAmount;
|
||||
}
|
||||
|
||||
return $finalResult;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dado un número de huéspedes, calcula el precio de cada día del periodo indicado.
|
||||
* @param \DateTimeInterface $startDate Fecha de inicio del periodo.
|
||||
* @param \DateTimeInterface $endDate Fecha fin del periodo.
|
||||
* @param int $guestNumber Número de huéspedes para los que calcular los precios.
|
||||
* @param array $fareTableList Array con los tarifarios que aplican en el periodo indicado.
|
||||
* @return array Devuelve un array con los precios: e.g. --> [ "2024-04-30" => 2500, "2024-05-01" => 3500, "2024-05-02" => 3000, ...].
|
||||
*/
|
||||
public function getDailyPricesForSearch(\DateTimeInterface $startDate, \DateTimeInterface $endDate, int $guestNumber, array $fareTableList): array
|
||||
{
|
||||
$daysDiff = $startDate->diff($endDate)->days;
|
||||
|
||||
$dailyPrices = [];
|
||||
$currentDate = new \DateTime($startDate->format("Y-m-d")); //TODOME: Comprobar si es tipo referencia y está cambiando.
|
||||
for ($i = 0; $i < $daysDiff; $i++) {
|
||||
$strCurrentDate = $currentDate->format("Y-m-d");
|
||||
|
||||
//Obtenemos el tarifario que aplica --> TODOME: Buscar si se puede cachear esto, para no recarlcularlo para cada GuestNumber.
|
||||
$currentFareTableList = \array_filter($fareTableList, function (FareTable $element) use ($currentDate) {
|
||||
return $element->isActiveOnDate($currentDate);
|
||||
});
|
||||
|
||||
if (count($currentFareTableList) != 1) {
|
||||
$dailyPrices[$strCurrentDate] = "ERROR - Number of FareTable found: " . count($currentFareTableList);
|
||||
continue;
|
||||
}
|
||||
|
||||
$currentFareTable = array_values($currentFareTableList)[0];
|
||||
|
||||
//TODOME: -> Obtener precio específico (RoomPrices).
|
||||
|
||||
//Si no hay precio, calcular mediante tarifarios.
|
||||
$roomFares = $currentFareTable->getRoomFaresByGuestNumber($guestNumber);
|
||||
if (count($roomFares) < 1) {
|
||||
$dailyPrices[$strCurrentDate] = "ERROR - Invalid RoomFares found";
|
||||
continue;
|
||||
}
|
||||
|
||||
//Si sólo hay 1 tarifa, aplico esa, sino calculo la apropiada según condiciones.
|
||||
$fareAmount = null;
|
||||
if (count($roomFares) == 1) {
|
||||
$fareAmount = $roomFares->first()->getFareAmount();
|
||||
} else {
|
||||
$fareAmount = $this->calculateFareApplyingConditions($roomFares, $currentDate);
|
||||
}
|
||||
|
||||
$dailyPrices[$strCurrentDate] = $fareAmount;
|
||||
|
||||
//Add 1 day to current date.
|
||||
$currentDate->add(new \DateInterval("P1D"));
|
||||
}
|
||||
|
||||
return $dailyPrices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula el precio a aplicar según las condiciones de las tarifas.
|
||||
* @param RoomFare[] $roomFareList Lista con las posibles tarifas.
|
||||
* @param DateTimeInterface $fareCondDate Fecha utilizada para las condiciones.
|
||||
*
|
||||
*/
|
||||
public function calculateFareApplyingConditions(ArrayCollection $roomFareList, \DateTimeInterface $fareCondDate): int
|
||||
{
|
||||
$currentDayOfWeek = $fareCondDate->format("w");
|
||||
|
||||
//Comprobamos si a alguna de las tarifas se la aplica la condición de "DayType" y coincide con el actual.
|
||||
// Si es así, devolvemos su valor.
|
||||
foreach ($roomFareList as $roomFare) {
|
||||
$dayType = $roomFare->getDayType();
|
||||
if ($dayType == null || $dayType == "") {
|
||||
continue;
|
||||
}
|
||||
if($dayType == $currentDayOfWeek) {
|
||||
return $roomFare->getFareAmount();
|
||||
}
|
||||
}
|
||||
|
||||
//TODOME: Si existen múltiples tarifas posibles, debería haber una estrategia para elegir una u otra. Variable de configuración para esto.
|
||||
$faresWithoutConditions = $roomFareList->filter(function ($element) {
|
||||
return $element->getDayType() == null;
|
||||
});
|
||||
|
||||
return $faresWithoutConditions->first()->getFareAmount();
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,18 @@
|
||||
"ref": "b131e6cbfe1b898a508987851963fff485986285"
|
||||
}
|
||||
},
|
||||
"nelmio/cors-bundle": {
|
||||
"version": "2.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.5",
|
||||
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
|
||||
},
|
||||
"files": [
|
||||
"./config/packages/nelmio_cors.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "7.0",
|
||||
"recipe": {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{# templates/admin/room_availability/availability_day_edit.html.twig #}
|
||||
{% extends '@EasyAdmin/page/content.html.twig' %}
|
||||
|
||||
{% block content_title 'Category - Room Availability - Edit' %}
|
||||
{% block main %}
|
||||
<div>
|
||||
<h3>{{categoryId}}</h3>
|
||||
</div>
|
||||
{{ form(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{# templates/admin/room_availability/availability_day_edit.html.twig #}
|
||||
{% extends '@EasyAdmin/page/content.html.twig' %}
|
||||
|
||||
{% block content_title 'Room Availability - Edit' %}
|
||||
{% block main %}
|
||||
<div>
|
||||
<h3>{{availabilityData.day |date('d-m-Y')}}</h3>
|
||||
</div>
|
||||
{{ form(form) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,30 @@
|
||||
{# templates/admin/room_availability/category_view.html.twig #}
|
||||
{% extends '@EasyAdmin/page/content.html.twig' %}
|
||||
|
||||
{% block content_title 'Room Availability' %}
|
||||
{% block main %}
|
||||
<div>
|
||||
<h3>{{categoryData.name}}</h3>
|
||||
</div>
|
||||
<table class="datagrid">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Day</td>
|
||||
<td>Amount</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for availability in availabilityList %}
|
||||
<tr>
|
||||
<td>{{availability.day |date('d-m-Y')}}</td>
|
||||
<td>{{availability.numAvailable}}</td>
|
||||
<td>
|
||||
<a href="{{ ea_url().setRoute('admin_roomAvailability_category_update', { day: availability.day |date('Y-m-d'), categoryId: categoryData.id }) }}">
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{# templates/admin/room_availability/index.html.twig #}
|
||||
{% extends '@EasyAdmin/page/content.html.twig' %}
|
||||
|
||||
{% block content_title 'Room Availability' %}
|
||||
{% block main %}
|
||||
<table class="datagrid">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Category</td>
|
||||
<td>Details</td>
|
||||
<td>Period update</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in roomCategoryList %}
|
||||
<tr>
|
||||
<td>{{category.id}}</td>
|
||||
<td>{{category.name}}</td>
|
||||
<td>
|
||||
<a href="{{ ea_url().setRoute('admin_roomAvailability_category', { categoryId: category.id }) }}">
|
||||
View Details
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ ea_url().setRoute('admin_roomAvailability_category_bulkupdate', { categoryId: category.id }) }}">
|
||||
Update Period
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Log in!{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form method="post">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.user %}
|
||||
<div class="mb-3">
|
||||
You are logged in as {{ app.user.userIdentifier }}, <a href="{{ path('app_logout') }}">Logout</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
|
||||
<label for="username">Username</label>
|
||||
<input type="text" value="{{ last_username }}" name="_username" id="username" class="form-control" autocomplete="username" required autofocus>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="_password" id="password" class="form-control" autocomplete="current-password" required>
|
||||
|
||||
<input type="hidden" name="_csrf_token"
|
||||
value="{{ csrf_token('authenticate') }}"
|
||||
>
|
||||
|
||||
{#
|
||||
Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
|
||||
See https://symfony.com/doc/current/security/remember_me.html
|
||||
|
||||
<div class="checkbox mb-3">
|
||||
<label>
|
||||
<input type="checkbox" name="_remember_me"> Remember me
|
||||
</label>
|
||||
</div>
|
||||
#}
|
||||
|
||||
<button class="btn btn-lg btn-primary" type="submit">
|
||||
Sign in
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user