This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Consts;
|
||||||
|
|
||||||
|
class Consts
|
||||||
|
{
|
||||||
|
public const FORMAT_DATE = 'Y-m-d';
|
||||||
|
}
|
||||||
@@ -22,10 +22,10 @@ class FareTableCrudController extends AbstractCrudController
|
|||||||
|
|
||||||
yield TextField::new('comment')->setLabel('Name');
|
yield TextField::new('comment')->setLabel('Name');
|
||||||
|
|
||||||
yield AssociationField::new('roomCategory');
|
yield AssociationField::new('roomCategory')
|
||||||
|
->setCrudController(RoomCategoryCrudController::class);
|
||||||
|
|
||||||
yield DateField::new('startDate');
|
yield DateField::new('startDate');
|
||||||
yield DateField::new('endDate');
|
yield DateField::new('endDate');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Admin;
|
||||||
|
|
||||||
|
use App\Consts\Consts;
|
||||||
|
use App\DTO\SearchFilterByDate;
|
||||||
|
use App\Entity\RoomCategory;
|
||||||
|
use App\Entity\RoomPrice;
|
||||||
|
use App\Enum\AdminErrorEnum;
|
||||||
|
use App\Form\RoomPriceFormType;
|
||||||
|
use App\Form\SearchFilterByDateFormType;
|
||||||
|
use App\Repository\RoomCategoryRepository;
|
||||||
|
use App\Repository\RoomPriceRepository;
|
||||||
|
use App\Service\AdminUtils;
|
||||||
|
use DateInterval;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
||||||
|
#[IsGranted("ROLE_ADMIN")]
|
||||||
|
class RoomPriceAdminController extends AbstractController
|
||||||
|
{
|
||||||
|
public const string ROUTE_INDEX = 'roomPrice_prices';
|
||||||
|
public const string ROUTE_CREATE = 'roomPrice_prices_create';
|
||||||
|
public const string ROUTE_REMOVE = 'roomPrice_prices_remove';
|
||||||
|
|
||||||
|
private const string SESSION_SEARCHFILTER = 'RoomPrice_SearchFilter';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $entityManager,
|
||||||
|
private AdminUtils $adminUtils,
|
||||||
|
private RoomCategoryRepository $catRepo,
|
||||||
|
private RoomPriceRepository $priceRepo
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// List Room Prices from category.
|
||||||
|
#[AdminRoute('/roomPrices/{categoryId}', name: RoomPriceAdminController::ROUTE_INDEX)]
|
||||||
|
public function roomPrices(Request $request, string $categoryId): Response
|
||||||
|
{
|
||||||
|
$session = $request->getSession();
|
||||||
|
|
||||||
|
$roomCategoryItem = $this->catRepo->find($categoryId);
|
||||||
|
if ($roomCategoryItem === null) {
|
||||||
|
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var SearchFilterByDate */
|
||||||
|
$searchFilter = $session->get(RoomPriceAdminController::SESSION_SEARCHFILTER, null);
|
||||||
|
|
||||||
|
//Set default values to filter.
|
||||||
|
if ($searchFilter == null) {
|
||||||
|
$searchFilter = new SearchFilterByDate();
|
||||||
|
|
||||||
|
$dtFrom = (new DateTimeImmutable())->sub(new DateInterval("P1M"));
|
||||||
|
$searchFilter->setDtFrom($dtFrom);
|
||||||
|
$dtTo = (new DateTimeImmutable())->add(new DateInterval("P365D"));
|
||||||
|
$searchFilter->setDtTo($dtTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
$searchForm = $this->createForm(SearchFilterByDateFormType::class, $searchFilter);
|
||||||
|
$searchForm->handleRequest($request);
|
||||||
|
|
||||||
|
$roomPriceList = $this->priceRepo->getListForPeriod($searchFilter->getDtFrom(), $searchFilter->getDtTo(), $categoryId);
|
||||||
|
|
||||||
|
//Store value in session for following requests.
|
||||||
|
$session->set(RoomPriceAdminController::SESSION_SEARCHFILTER, $searchFilter);
|
||||||
|
|
||||||
|
return $this->render('admin/room_price/roomPriceList.html.twig', [
|
||||||
|
'searchForm' => $searchForm,
|
||||||
|
'categoryData' => $roomCategoryItem,
|
||||||
|
'roomPriceList' => $roomPriceList,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[AdminRoute('/roomPrices/{categoryId}/new', name: RoomPriceAdminController::ROUTE_CREATE)]
|
||||||
|
public function create(Request $request, string $categoryId): Response
|
||||||
|
{
|
||||||
|
$roomCategoryItem = $this->catRepo->find($categoryId);
|
||||||
|
if ($roomCategoryItem === null) {
|
||||||
|
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$roomPriceItem = new RoomPrice();
|
||||||
|
$roomPriceItem->setRoomCategory($roomCategoryItem);
|
||||||
|
|
||||||
|
$form = $this->createForm(RoomPriceFormType::class, $roomPriceItem, [
|
||||||
|
'selectRoomCategory' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
// $form->getData() holds the submitted values
|
||||||
|
// but, the original `$availabilityItem` variable has also been updated
|
||||||
|
|
||||||
|
if ($this->isRoomPriceCreationValid($roomPriceItem, $roomCategoryItem)) {
|
||||||
|
$this->entityManager->persist($roomPriceItem);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Element created!');
|
||||||
|
return $this->goToIndex($categoryId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('admin/adminEdit.html.twig', [
|
||||||
|
'form' => $form,
|
||||||
|
'ElementType' => "Room Categoy Bed Type",
|
||||||
|
'ElementName' => "Room Categoy - " . $roomCategoryItem
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[AdminRoute('/roomPrices/{categoryId}/remove/{date}/{guestNumber}', name: RoomPriceAdminController::ROUTE_REMOVE)]
|
||||||
|
public function remove(string $categoryId, string $date, string $guestNumber): Response
|
||||||
|
{
|
||||||
|
$item = $this->priceRepo->find(['roomCategory' => $categoryId, 'date' => $date, 'guestNumber' => $guestNumber]);
|
||||||
|
if ($item === null) {
|
||||||
|
return $this->adminUtils->goToAdminErrorType(AdminErrorEnum::ENTITY_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->entityManager->remove($item);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Element deleted!');
|
||||||
|
return $this->goToIndex($categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function goToIndex(int $roomCategory): Response
|
||||||
|
{
|
||||||
|
return $this->redirectToRoute(AdminUtils::GetAdminRoute(RoomPriceAdminController::ROUTE_INDEX), [
|
||||||
|
'categoryId' => $roomCategory,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isRoomPriceCreationValid(RoomPrice $priceItem, RoomCategory $roomCategoryItem): bool
|
||||||
|
{
|
||||||
|
$roomMaxGuestNum = $roomCategoryItem->getMaxGuestNum();
|
||||||
|
if ($priceItem->getGuestNumber() > $roomMaxGuestNum) {
|
||||||
|
$msg = "Guest number is greater than Room Category max Guest number ('$roomMaxGuestNum')";
|
||||||
|
$this->addFlash('error', $msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$alreadyExistsItem = $this->priceRepo->find([
|
||||||
|
'roomCategory' => $priceItem->getRoomCategory()->getId(),
|
||||||
|
'date' => $priceItem->getDate()->format(Consts::FORMAT_DATE),
|
||||||
|
'guestNumber' => $priceItem->getGuestNumber()
|
||||||
|
]);
|
||||||
|
if ($alreadyExistsItem != null) {
|
||||||
|
$this->addFlash('error', 'Price already exist!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller\Admin;
|
||||||
|
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\{TextField};
|
||||||
|
|
||||||
|
use App\Entity\RoomCategory;
|
||||||
|
use App\Service\AdminUtils;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||||
|
|
||||||
|
class RoomPriceCrudController extends AbstractCrudController
|
||||||
|
{
|
||||||
|
public static function getEntityFqcn(): string
|
||||||
|
{
|
||||||
|
//Room Prices are configured by RoomCategory.
|
||||||
|
return RoomCategory::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureCrud(Crud $crud): Crud
|
||||||
|
{
|
||||||
|
return $crud
|
||||||
|
->setPageTitle(Crud::PAGE_INDEX, "Room Prices")
|
||||||
|
->setHelp(Crud::PAGE_INDEX, "Edit prices for a RoomCategory.")
|
||||||
|
->showEntityActionsInlined();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureFields(string $pageName): iterable
|
||||||
|
{
|
||||||
|
yield TextField::new('name');
|
||||||
|
yield TextField::new('description');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureActions(Actions $actions): Actions
|
||||||
|
{
|
||||||
|
$actions->disable(Action::NEW, Action::DETAIL, Action::EDIT, Action::DELETE);
|
||||||
|
|
||||||
|
$viewAvailabilityAction = Action::new('priceList', 'Prices')
|
||||||
|
->linkToRoute(
|
||||||
|
AdminUtils::GetAdminRoute(RoomPriceAdminController::ROUTE_INDEX),
|
||||||
|
function (RoomCategory $categoryItem): array {
|
||||||
|
return ['categoryId' => $categoryItem->getId()];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $actions
|
||||||
|
->add(Crud::PAGE_INDEX, $viewAvailabilityAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,7 @@ class SomniaDashboardController extends AbstractDashboardController
|
|||||||
yield MenuItem::linkToCrud('Availability', 'fas fa-calendar-check', RoomCategory::class)->setController(RoomAvailabilityCrudController::class);
|
yield MenuItem::linkToCrud('Availability', 'fas fa-calendar-check', RoomCategory::class)->setController(RoomAvailabilityCrudController::class);
|
||||||
yield MenuItem::linkToCrud('Fare Tables', 'fas fa-table', FareTable::class);
|
yield MenuItem::linkToCrud('Fare Tables', 'fas fa-table', FareTable::class);
|
||||||
yield MenuItem::linkToCrud('Room Fares', 'fas fa-tag', RoomFare::class);
|
yield MenuItem::linkToCrud('Room Fares', 'fas fa-tag', RoomFare::class);
|
||||||
|
yield MenuItem::linkToCrud('Room Prices', 'fas fa-coins', RoomCategory::class)->setController(RoomPriceCrudController::class);
|
||||||
|
|
||||||
yield MenuItem::Section('Category Configuration');
|
yield MenuItem::Section('Category Configuration');
|
||||||
yield MenuItem::linkToCrud('Category', 'fas fa-layer-group', RoomCategory::class)->setController(RoomCategoryCrudController::class);
|
yield MenuItem::linkToCrud('Category', 'fas fa-layer-group', RoomCategory::class)->setController(RoomCategoryCrudController::class);
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\DTO;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
class SearchFilterByDate
|
||||||
|
{
|
||||||
|
protected DateTimeImmutable $dtFrom;
|
||||||
|
protected DateTimeImmutable $dtTo;
|
||||||
|
|
||||||
|
public function getDtFrom(): DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->dtFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDtFrom(DateTimeImmutable $dt)
|
||||||
|
{
|
||||||
|
$this->dtFrom = $dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDtTo(): DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->dtTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDtTo(DateTimeImmutable $dt)
|
||||||
|
{
|
||||||
|
$this->dtTo = $dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\RoomCategory;
|
||||||
|
use App\Entity\RoomPrice;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class RoomPriceFormType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$selectRoomCategory = $options['selectRoomCategory'];
|
||||||
|
|
||||||
|
if ($selectRoomCategory) {
|
||||||
|
$builder->add('roomCategory', EntityType::class, [
|
||||||
|
'class' => RoomCategory::class,
|
||||||
|
'choice_label' => 'name'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder->add('date', DateType::class);
|
||||||
|
$builder->add('guestNumber', IntegerType::class);
|
||||||
|
$builder->add('price', MoneyType::class, [
|
||||||
|
'divisor' => 100, //Prices are stored as integers in db.
|
||||||
|
]);
|
||||||
|
$builder->add('save', SubmitType::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => RoomPrice::class,
|
||||||
|
'selectRoomCategory' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('selectRoomCategory', 'bool');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\DTO\SearchFilterByDate;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class SearchFilterByDateFormType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
//$selectRoomCategory = $options['selectRoomCategory'];
|
||||||
|
|
||||||
|
$builder->add('dtFrom', DateType::class);
|
||||||
|
$builder->add('dtTo', DateType::class);
|
||||||
|
|
||||||
|
$builder->add('Search', SubmitType::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => SearchFilterByDate::class
|
||||||
|
]);
|
||||||
|
|
||||||
|
//$resolver->setAllowedTypes('selectRoomCategory', 'bool');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{# templates/admin/room_category_bed_type_admin/category_view.html.twig #}
|
||||||
|
{% extends '@EasyAdmin/page/content.html.twig' %}
|
||||||
|
|
||||||
|
{% block content_title 'Room Prices' %}
|
||||||
|
{% block main %}
|
||||||
|
<div class="row justify-content-between mb-4">
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{categoryData.name}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<a class="btn btn-primary" href="{{ path('admin_roomPrice_prices_create', { categoryId: categoryData.id }) }}">
|
||||||
|
Add Price
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ form(searchForm) }}
|
||||||
|
</div>
|
||||||
|
<table class="table datagrid {{ roomPriceList is empty ? 'datagrid-empty' }}">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Date</td>
|
||||||
|
<td>Guest Num</td>
|
||||||
|
<td>Amount</td>
|
||||||
|
<td>Actions</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for price in roomPriceList %}
|
||||||
|
<tr>
|
||||||
|
<td>{{price.date |date('d-m-Y')}}</td>
|
||||||
|
<td>{{price.guestNumber}}</td>
|
||||||
|
<td>{{price.price / 100}}</td>
|
||||||
|
<td>
|
||||||
|
<a class="btn btn-secondary" href="{{ path('admin_roomPrice_prices_remove', {
|
||||||
|
categoryId: price.roomCategory.id,
|
||||||
|
date: price.date |date('Y-m-d'),
|
||||||
|
guestNumber: price.guestNumber})
|
||||||
|
}}">
|
||||||
|
Remove
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user