Merge pull request #2010 from sumitwebkul/gli-3353
Updated: Room bookings terminal status improvements
This commit is contained in:
@@ -147,7 +147,7 @@
|
||||
{if ($can_edit)}
|
||||
<td class="product_action center">
|
||||
<div class="btn-group pull-right">
|
||||
{if $data.id_status == $ROOM_STATUS_CANCELLED || $data.id_status == $ROOM_STATUS_NO_SHOW}
|
||||
{if isset($htl_booking_statuses[$data.id_status]) && $htl_booking_statuses[$data.id_status].is_terminal}
|
||||
<button href="#" class="btn btn-default delete_room_line">
|
||||
<i class="icon-trash"></i>
|
||||
{l s='Delete'}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" {if $current_room_status == $ROOM_STATUS_ASSIGNED}style="display:none;"{/if}>
|
||||
<div class="form-group" {if $current_room_status != $ROOM_STATUS_CHECKED_IN && $current_room_status != $ROOM_STATUS_CHECKED_OUT}style="display:none;"{/if}>
|
||||
<label class="control-label">{l s='Date'}</label>
|
||||
<div class="input-group">
|
||||
<input type="text" name="status_date" class="room_status_date wk-input-date" value="" />
|
||||
|
||||
@@ -3095,6 +3095,49 @@ class OrderCore extends ObjectModel
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the order status from the paid amount once the order is no longer
|
||||
* completely No-show/Cancelled, e.g. when the admin reverts a No-show room back
|
||||
* to Assigned or Checked-in.
|
||||
*
|
||||
* Paid amount 0 => Awaiting payment, less than order total => Partial payment
|
||||
* accepted, otherwise => Payment accepted.
|
||||
*
|
||||
* @param bool $useEmail whether to email the customer about the change
|
||||
* @return bool whether the order status was actually changed
|
||||
*/
|
||||
public function changeOrderStatusOnRoomBookingStatusChange($useEmail = true)
|
||||
{
|
||||
if (!in_array($this->current_state, array(
|
||||
Configuration::get('PS_OS_NO_SHOW'),
|
||||
Configuration::get('PS_OS_CANCELED'),
|
||||
))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// every room is still No-show/Cancelled, keep the order status as it is
|
||||
if ($this->getOrderCompleteRefundStatus()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$totalPaid = $this->getTotalPaid();
|
||||
if ($totalPaid <= 0) {
|
||||
$idOrderState = Configuration::get('PS_OS_AWAITING_PAYMENT');
|
||||
} elseif ($totalPaid < $this->getOrderTotal()) {
|
||||
$idOrderState = Configuration::get('PS_OS_PARTIAL_PAYMENT_ACCEPTED');
|
||||
} else {
|
||||
$idOrderState = Configuration::get('PS_OS_PAYMENT_ACCEPTED');
|
||||
}
|
||||
|
||||
$objOrderHistory = new OrderHistory();
|
||||
$objOrderHistory->id_order = (int) $this->id;
|
||||
$objOrderHistory->id_employee = isset(Context::getContext()->employee->id) ? (int) Context::getContext()->employee->id : 0;
|
||||
$objOrderHistory->changeIdOrderState($idOrderState, $this, !$this->hasInvoice());
|
||||
$useEmail ? $objOrderHistory->addWithemail() : $objOrderHistory->add();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getWsBookings()
|
||||
{
|
||||
return Db::getInstance()->executeS(
|
||||
|
||||
@@ -925,6 +925,11 @@ class AdminOrdersControllerCore extends AdminController
|
||||
$currentRoomStatus = (int) Tools::getValue('current_room_status');
|
||||
$htlOrderStatus = $objHotelBookingStatus->getAllStatuses($this->context->language->id);
|
||||
$allowedStatuses = HotelBookingStatus::getAllowedTransitions($currentRoomStatus);
|
||||
// no transition is allowed out of a terminal (final) status
|
||||
$objCurrentStatus = new HotelBookingStatus($currentRoomStatus);
|
||||
if (Validate::isLoadedObject($objCurrentStatus) && $objCurrentStatus->is_terminal) {
|
||||
$allowedStatuses = array();
|
||||
}
|
||||
foreach ($htlOrderStatus as &$status) {
|
||||
$status['disabled'] = !in_array((int) $status['id_status'], $allowedStatuses);
|
||||
}
|
||||
@@ -935,6 +940,8 @@ class AdminOrdersControllerCore extends AdminController
|
||||
'current_index' => self::$currentIndex,
|
||||
'hotel_order_status' => $htlOrderStatus,
|
||||
'ROOM_STATUS_ASSIGNED' => HotelBookingDetail::STATUS_ASSIGNED,
|
||||
'ROOM_STATUS_CHECKED_IN' => HotelBookingDetail::STATUS_CHECKED_IN,
|
||||
'ROOM_STATUS_CHECKED_OUT' => HotelBookingDetail::STATUS_CHECKED_OUT,
|
||||
'current_room_status' => $currentRoomStatus
|
||||
)
|
||||
);
|
||||
@@ -9060,6 +9067,11 @@ class AdminOrdersControllerCore extends AdminController
|
||||
$this->errors[] = Tools::displayError('Invalid booking status found.');
|
||||
} elseif ($newStatus == $objHotelBookingDetail->id_status) {
|
||||
$this->errors[] = Tools::displayError('This room is already set to the selected status.');
|
||||
} elseif (($objCurrentStatus = new HotelBookingStatus((int) $objHotelBookingDetail->id_status))
|
||||
&& Validate::isLoadedObject($objCurrentStatus)
|
||||
&& $objCurrentStatus->is_terminal
|
||||
) {
|
||||
$this->errors[] = Tools::displayError('Room status cannot be changed as the current status of this room is final.');
|
||||
} elseif (($newStatus == HotelBookingDetail::STATUS_NO_SHOW || $newStatus == HotelBookingDetail::STATUS_CANCELLED)
|
||||
&& ($objHotelBookingDetail->id_status == HotelBookingDetail::STATUS_CHECKED_IN
|
||||
|| $objHotelBookingDetail->id_status == HotelBookingDetail::STATUS_CHECKED_OUT)
|
||||
@@ -9083,6 +9095,24 @@ class AdminOrdersControllerCore extends AdminController
|
||||
$this->errors[] = Tools::displayError('Room cannot be marked as No-show before the booking\'s check-in date.');
|
||||
}
|
||||
|
||||
// reverting a No-show/Cancelled room makes it occupy the inventory again
|
||||
if (empty($this->errors)
|
||||
&& ($objHotelBookingDetail->id_status == HotelBookingDetail::STATUS_NO_SHOW
|
||||
|| $objHotelBookingDetail->id_status == HotelBookingDetail::STATUS_CANCELLED)
|
||||
&& $newStatus != HotelBookingDetail::STATUS_NO_SHOW
|
||||
&& $newStatus != HotelBookingDetail::STATUS_CANCELLED
|
||||
) {
|
||||
if ((new OrderReturn())->hasCompletelyRefundedBooking($objHotelBookingDetail->id)) {
|
||||
$this->errors[] = Tools::displayError('Room status cannot be changed as this room booking has already been refunded.');
|
||||
} elseif ($objHotelBookingDetail->chechRoomBooked(
|
||||
$objHotelBookingDetail->id_room,
|
||||
$objHotelBookingDetail->date_from,
|
||||
$objHotelBookingDetail->date_to
|
||||
)) {
|
||||
$this->errors[] = Tools::displayError('Room status cannot be changed as this room is already booked in another booking for the same dates. Please reallocate the room first.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($objHotelBookingDetail->id_status == HotelBookingDetail::STATUS_CHECKED_OUT
|
||||
&& (date('Y-m-d', strtotime($objHotelBookingDetail->check_out)) != date('Y-m-d', strtotime($objHotelBookingDetail->date_to)))
|
||||
&& ($booking = $objHotelBookingDetail->chechRoomBooked($objHotelBookingDetail->id_room, $objHotelBookingDetail->check_out, $objHotelBookingDetail->date_to))
|
||||
@@ -9169,8 +9199,29 @@ class AdminOrdersControllerCore extends AdminController
|
||||
? OrderReturn::EVENT_TYPE_CANCELLATION
|
||||
: OrderReturn::EVENT_TYPE_NO_SHOW;
|
||||
|
||||
// a No-show room moved to Cancelled usually already has a refund
|
||||
// request from the No-show: reuse it instead of creating a duplicate
|
||||
$existingRefundRequest = false;
|
||||
if ($objHotelBookingDetail->id_status == HotelBookingDetail::STATUS_NO_SHOW
|
||||
&& ($refundRequests = OrderReturn::getOrdersReturnDetail($order->id, 0, $objHotelBookingDetail->id))
|
||||
) {
|
||||
// latest request first
|
||||
$latestRequest = reset($refundRequests);
|
||||
if (!$latestRequest['denied']) {
|
||||
$existingRefundRequest = true;
|
||||
if (!$latestRequest['refunded']) {
|
||||
// still pending: it now stands for the cancellation
|
||||
$objOrderReturn = new OrderReturn((int) $latestRequest['id_order_return']);
|
||||
if (Validate::isLoadedObject($objOrderReturn)) {
|
||||
$objOrderReturn->event_type = $eventType;
|
||||
$objOrderReturn->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no refund request is created if refund is disabled for the hotel
|
||||
if ($order->isReturnable()) {
|
||||
if (!$existingRefundRequest && $order->isReturnable()) {
|
||||
$objOrderReturn = new OrderReturn();
|
||||
$objOrderReturn->id_customer = $order->id_customer;
|
||||
$objOrderReturn->id_order = $order->id;
|
||||
@@ -9211,6 +9262,13 @@ class AdminOrdersControllerCore extends AdminController
|
||||
$success = $statusChanged;
|
||||
} else {
|
||||
$statusChanged = $objHotelBookingDetail->changeStatus($newStatus, $changeParams);
|
||||
|
||||
if ($statusChanged) {
|
||||
// a No-show room reverted back: the order is no longer completely
|
||||
// No-show/Cancelled, so restore its status from the paid amount
|
||||
$order = new Order($order->id);
|
||||
$order->changeOrderStatusOnRoomBookingStatusChange();
|
||||
}
|
||||
$success = $statusChanged;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,15 @@ class HotelBookingStatus extends ObjectModel
|
||||
HotelBookingDetail::STATUS_ASSIGNED,
|
||||
HotelBookingDetail::STATUS_CHECKED_IN,
|
||||
),
|
||||
// only reachable when No-show is not marked as terminal (is_terminal = 0)
|
||||
HotelBookingDetail::STATUS_NO_SHOW => array(
|
||||
// revert: undo an accidental no-show
|
||||
HotelBookingDetail::STATUS_ASSIGNED,
|
||||
// late arrival: guest checks in after being marked no-show
|
||||
HotelBookingDetail::STATUS_CHECKED_IN,
|
||||
// booking cancelled after being marked no-show
|
||||
HotelBookingDetail::STATUS_CANCELLED,
|
||||
),
|
||||
);
|
||||
|
||||
return isset($transitions[$currentStatus]) ? $transitions[$currentStatus] : array();
|
||||
|
||||
@@ -1582,7 +1582,7 @@ class HotelHelper
|
||||
array(
|
||||
'id' => HotelBookingDetail::STATUS_NO_SHOW,
|
||||
'color' => '#FF8C00',
|
||||
'is_terminal' => 1,
|
||||
'is_terminal' => 0,
|
||||
'name' => array(
|
||||
'en' => 'No-show',
|
||||
'nl' => 'Niet verschenen',
|
||||
|
||||
@@ -61,9 +61,31 @@ class AdminRoomStatusesController extends ModuleAdminController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List column callback: display the terminal (final) flag as a Yes/No badge.
|
||||
*
|
||||
* @param mixed $value 1 or 0
|
||||
* @return string
|
||||
*/
|
||||
public function getTerminalStatusLabel($value)
|
||||
{
|
||||
return $value ? $this->l('Yes') : $this->l('No');
|
||||
if ($value) {
|
||||
return '<span class="badge badge-success">'.$this->l('Yes').'</span>';
|
||||
}
|
||||
|
||||
return '<span class="badge badge-danger">'.$this->l('No').'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the terminal (final) flag of the given status can be changed by the admin.
|
||||
* Only No-show is configurable for now; every other status keeps its fixed value.
|
||||
*
|
||||
* @param int $idBookingStatus
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTerminalEditable($idBookingStatus)
|
||||
{
|
||||
return (int) $idBookingStatus == HotelBookingDetail::STATUS_NO_SHOW;
|
||||
}
|
||||
|
||||
public function initToolbar()
|
||||
@@ -92,12 +114,38 @@ class AdminRoomStatusesController extends ModuleAdminController
|
||||
return;
|
||||
}
|
||||
|
||||
// decoupled from the real 'is_terminal' field name on purpose: copyFromPost()
|
||||
// writes any POST key matching a real object property, and 'disabled' on the
|
||||
// switch only blocks submission client-side (removable via devtools). Using
|
||||
// a name with no matching property makes server-side tampering impossible,
|
||||
// not just hidden.
|
||||
$this->fields_value['is_terminal_display'] = $this->object->is_terminal;
|
||||
// Only No-show gets an editable switch posting the real 'is_terminal' field.
|
||||
// Every other status shows a read-only Yes/No badge and posts nothing for it;
|
||||
// processSave() also rejects a tampered 'is_terminal' for those statuses.
|
||||
if ($this->isTerminalEditable($this->object->id)) {
|
||||
$this->fields_value['is_terminal'] = $this->object->is_terminal;
|
||||
$terminalInput = array(
|
||||
'type' => 'switch',
|
||||
'label' => $this->l('Terminal (final) status'),
|
||||
'name' => 'is_terminal',
|
||||
'hint' => $this->l('If set to Yes, a room with this status cannot be edited, only deleted. If No, the room status can still be changed, e.g. to undo an accidental no-show or for a late check-in.'),
|
||||
'values' => array(
|
||||
array(
|
||||
'id' => 'is_terminal_on',
|
||||
'value' => 1,
|
||||
'label' => $this->l('Yes'),
|
||||
),
|
||||
array(
|
||||
'id' => 'is_terminal_off',
|
||||
'value' => 0,
|
||||
'label' => $this->l('No'),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
$terminalInput = array(
|
||||
'type' => 'html',
|
||||
'label' => $this->l('Terminal (final) status'),
|
||||
'name' => 'is_terminal_display',
|
||||
'hint' => $this->l('If set to Yes, a room with this status cannot be edited, only deleted. If No, the room can still be edited. This setting is fixed for this status and cannot be changed.'),
|
||||
'html_content' => '<p class="form-control-static">'.$this->getTerminalStatusLabel($this->object->is_terminal).'</p>',
|
||||
);
|
||||
}
|
||||
|
||||
$this->fields_form = array(
|
||||
'legend' => array(
|
||||
@@ -119,25 +167,7 @@ class AdminRoomStatusesController extends ModuleAdminController
|
||||
'name' => 'color',
|
||||
'required' => true,
|
||||
),
|
||||
array(
|
||||
'type' => 'switch',
|
||||
'label' => $this->l('Terminal (final) status'),
|
||||
'name' => 'is_terminal_display',
|
||||
'hint' => $this->l('If set to Yes, a room with this status cannot be edited, only deleted. If No, the room can still be edited. This setting is fixed for each status, so it is disabled here.'),
|
||||
'disabled' => true,
|
||||
'values' => array(
|
||||
array(
|
||||
'id' => 'is_terminal_on',
|
||||
'value' => 1,
|
||||
'label' => $this->l('Yes'),
|
||||
),
|
||||
array(
|
||||
'id' => 'is_terminal_off',
|
||||
'value' => 0,
|
||||
'label' => $this->l('No'),
|
||||
),
|
||||
),
|
||||
),
|
||||
$terminalInput,
|
||||
),
|
||||
'submit' => array(
|
||||
'title' => $this->l('Save'),
|
||||
@@ -159,6 +189,22 @@ class AdminRoomStatusesController extends ModuleAdminController
|
||||
$this->errors[] = $this->l('Status name is required at least in ').$objDefaultLanguage['name'];
|
||||
}
|
||||
|
||||
if (Tools::getIsset('is_terminal')) {
|
||||
$isTerminal = Tools::getValue('is_terminal');
|
||||
if (!$this->isTerminalEditable($this->object->id)) {
|
||||
if ((int) $isTerminal != (int) $this->object->is_terminal) {
|
||||
$this->errors[] = $this->l('Terminal (final) status can not be changed for this room status.');
|
||||
}
|
||||
} elseif (!Validate::isBool($isTerminal)) {
|
||||
$this->errors[] = $this->l('Invalid value for terminal (final) status.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->errors)) {
|
||||
$this->display = 'edit';
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::processSave();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user