EXCHANGE-149 - Confirmation modal for merge bookings (abit on backend for process merge)

This commit is contained in:
Fairuz
2021-05-10 17:02:33 +08:00
parent d3c670aeed
commit 94904ee373
12 changed files with 445 additions and 78 deletions
@@ -2,25 +2,21 @@
namespace App\Classes\Modules\Bookings\ControllersLogic;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingMergeObject;
use App\Classes\Modules\Bookings\Services\DeletesBooking;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Bookings\Services\GetBookings;
use App\Classes\Modules\Bookings\Standards\Rules\CanMergeBooking;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Banks\Services\FetchesBank;
use App\Classes\Modules\Currencies\Services\FetchesCurrency;
use App\Classes\Modules\Bookings\Standards\Rules\CanCreateBooking;
use App\Classes\Modules\Bookings\Services\CreatesBooking;
use App\Classes\Modules\Bookings\Services\GeneratesBookingMarking;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Http\Resources\BookingResource;
use App\Classes\Modules\Bookings\Services\UpdatesBookingTransaction;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -49,8 +45,14 @@ class MergeBookingLogic extends AbstractControllerLogic
/** @var FetchesCompany */
private $fetchesCompany;
/** @var FetchesCompany */
private $fetchesBooking;
/** @var GetBookings */
private $getBookings;
/** @var UpdatesBookingTransaction */
private $updatesBookingTransaction;
/** @var DeletesBooking */
private $deletesBooking;
/**
* CreateBookingLogic constructor.
@@ -59,13 +61,15 @@ class MergeBookingLogic extends AbstractControllerLogic
* @param GeneratesBookingMarking $generatesBookingMarking
* @param FetchesCompany $fetchesCompany
*/
public function __construct(CanMergeBooking $canMergeBooking, CreatesBooking $createsBooking, GeneratesBookingMarking $generatesBookingMarking, FetchesCompany $fetchesCompany, FetchesBooking $fetchesBooking)
public function __construct(CanMergeBooking $canMergeBooking, CreatesBooking $createsBooking, GeneratesBookingMarking $generatesBookingMarking, FetchesCompany $fetchesCompany, GetBookings $getBookings, UpdatesBookingTransaction $updatesBookingTransaction, DeletesBooking $deletesBooking)
{
$this->canMergeBooking = $canMergeBooking;
$this->createsBooking = $createsBooking;
$this->generatesBookingMarking = $generatesBookingMarking;
$this->fetchesCompany = $fetchesCompany;
$this->fetchesBooking = $fetchesBooking;
$this->getBookings = $getBookings;
$this->updatesBookingTransaction = $updatesBookingTransaction;
$this->deletesBooking = $deletesBooking;
}
@@ -74,23 +78,46 @@ class MergeBookingLogic extends AbstractControllerLogic
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\RequestValidationException
* @throws \App\Classes\Exceptions\ResourceNotFoundException
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$object = new BookingMergeObject($request->get('booking_ids'));
$booking_object = new BookingMergeObject($request->get('id'),$request->get('booking_ids'));
$this->canMergeBooking->passes($object);
$this->canMergeBooking->passes($booking_object);
/*
$bookings = $this->fetchesBooking->execute([
'id_in'=> $object->getBookingIds(),
'status' => ApprovalStatus::APPROVED,
]);
*/
#TODO: Fetch transaction (+details) record, reassign transaction id & recalculate amount
//dd($bookings);
$booking_main = $this->getBookings->handler(['id'=> $booking_object->getId()])[0];
$bookings_to_merge = $this->getBookings->handler(['id_in'=> $booking_object->getBookingIds()]);
$booking_amount = $booking_main->fix_amount;
$transaction_amount = 0;
$booking_main_transactions = $booking_main->transactions()->get();
foreach($booking_main_transactions as $transaction)
{
$transaction_amount += $transaction->amount;
}
foreach($bookings_to_merge as $booking)
{
$booking_amount += $booking->fix_amount;
$bookings_to_merge_transactions = $booking->transactions()->get();
foreach($bookings_to_merge_transactions as $transaction)
{
$transaction_amount += $transaction->amount;
}
//Soft-delete selected bookings
//$this->deletesBooking->execute($booking);
}
$booking_object->setBookingAmount($booking_amount);
$booking_object->setTransactionAmount($transaction_amount);
$this->updatesBookingTransaction->execute($booking_main,$bookings_to_merge, $booking_object);
dd('??');
return $this->response();
@@ -7,18 +7,37 @@ use App\Classes\General\Interfaces\DataTransferObject;
class BookingMergeObject implements DataTransferObject
{
/** @var int */
private $id;
/** @var array */
private $bookingIds;
/** @var float */
private $bookingAmount;
/** @var float */
private $transactionAmount;
/**
* BookingMergeObject constructor.
* @param int $id
* @param array $bookingIds
*/
public function __construct(array $bookingIds)
public function __construct(int $id, array $bookingIds)
{
$this->id = $id;
$this->bookingIds = $bookingIds;
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return array
*/
@@ -27,4 +46,37 @@ class BookingMergeObject implements DataTransferObject
return $this->bookingIds;
}
/**
* @return float
*/
public function getBookingAmount(): float
{
return $this->bookingAmount;
}
/**
* @param float $amount
* @return float
*/
public function setBookingAmount(float $amount)
{
$this->bookingAmount = $amount;
}
/**
* @return float
*/
public function getTransactionAmount(): float
{
return $this->transactionAmount;
}
/**
* @param float $amount
* @return float
*/
public function setTransactionAmount(float $amount)
{
$this->transactionAmount = $amount;
}
}
@@ -30,4 +30,5 @@ class FetchesBooking extends AbstractFetchRecord
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Classes\Modules\Bookings\Services;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\General\Eloquent\AbstractGetRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\Booking;
use Illuminate\Database\Eloquent\Collection;
class GetBookings extends AbstractGetRecord
{
/** @var Booking */
private $repository;
/**
* FetchesUser constructor.
* @param Booking $repository
*/
public function __construct(Booking $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
/**
* @param Builder $query
* @return Collection
* @throws ResourceNotFoundException
*/
public function getResults(Builder $query):Collection
{
if (!$query->exists()) {
throw new ResourceNotFoundException('Unable to find any record based on the criteria provided');
}
return $query->get();
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Classes\Modules\Bookings\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingMergeObject;
use App\Models\Booking;
class UpdatesBookingTransaction extends AbstractUpdateRecord
{
/**
* @param Booking $booking_main
* @param mixed $bookings_to_merge
* @param BookingMergeObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Booking $booking_main, $bookings_to_merge, BookingMergeObject $object) {
foreach($bookings_to_merge as $booking) {
$bookings_to_merge_transactions = $booking->transactions()->get();
foreach ($bookings_to_merge_transactions as $transaction) {
var_dump( $transaction->booking_id);
$transaction->booking_id = $object->getId();
$this->handler($transaction);
}
}
$booking_main->fix_amount=$object->getBookingAmount();
return $this->handler($booking_main);
}
}
@@ -0,0 +1,53 @@
<?php
namespace App\Classes\Modules\Bookings\Standards\Criterias;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use App\Classes\General\Eloquent\AbstractGetRecord;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingMergeObject;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\Booking;
class BookingsAreMergeable
{
/** @var Booking */
private $repository;
/**
* BookingsAreMergeable constructor.
* @param Booking $repository
*/
public function __construct(Booking $repository)
{
$this->repository = $repository;
}
/**
* @param BookingMergeObject $object
* @return bool
* @throws ResourceNotFoundException
*/
public function execute(BookingMergeObject $object){
$bookingOriginal=$this->repository->where(['id'=> $object->getId(),'status' => ApprovalStatus::APPROVED,])->first();
$bookingToMerge=$this->repository->whereIn('id', $object->getBookingIds())
->where([
'status' => ApprovalStatus::APPROVED,
'service_id' => $bookingOriginal->service_id,
'fix_currency_id' => $bookingOriginal->fix_currency_id,
'convertible_currency_id' => $bookingOriginal->convertible_currency_id,
])->get();
if($bookingOriginal===null || $bookingToMerge->count() !== count($object->getBookingIds())){
throw new ResourceNotFoundException('The booking doesn\'t exist');
}
return true;
}
}
@@ -2,7 +2,10 @@
namespace App\Classes\Modules\Bookings\Standards\Rules;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Bookings\Standards\Criterias\BookingsAreMergeable;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingMergeObject;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingObject;
use App\Classes\Modules\Bookings\Standards\Validators\BookingMergeValidation;
@@ -12,14 +15,19 @@ class CanMergeBooking extends AbstractRule
/** @var BookingMergeValidation */
private $bookingMergeValidation;
/** @var BookingsAreMergeable */
private $bookingsAreMergeable;
/**
* CanCreateBooking constructor.
* @param BookingMergeValidation $bookingMergeValidation
* @param BookingsAreMergeable $bookingsAreMergeable
*/
public function __construct(BookingMergeValidation $bookingMergeValidation)
public function __construct(BookingMergeValidation $bookingMergeValidation, BookingsAreMergeable $bookingsAreMergeable)
{
$this->bookingMergeValidation = $bookingMergeValidation;
$this->bookingsAreMergeable = $bookingsAreMergeable;
}
/**
@@ -41,11 +49,13 @@ class CanMergeBooking extends AbstractRule
}
/**
* @param BookingObject $object
* @param BookingMergeObject $object
* @return bool
* @throws ResourceNotFoundException
*/
protected function criteria($object): bool
{
$this->bookingsAreMergeable->execute($object);
return true;
}
}
@@ -0,0 +1,66 @@
<template>
<div class="row m-b-10 parentContainer">
<div class="col">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row" v-show="!isLoading">
<div class="col">
<div class="row align-items-center">
<div class="col p-t-5 p-b-5">
<div class="col text-center">
<div class="row m-b-20">
<div class="col">
<h5 class="all-caps">Merge Transfers</h5>
<div class="fs-11">Are you sure you want to merge? This action is undoable.</div>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div data-dismiss="modal" class="btn btn-sm btn-default bg-master-lighter btn-block b-rad-none">Cancel</div>
</div>
<div class="col p-l-5">
<div data-dismiss="modal" class="btn btn-sm btn-success btn-block b-rad-none" @click="mergeTransfer()">Merge</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
import staticFormHandler from '../../../general/mixins/staticFormHandler'
export default {
props: {
'id': {
type: Number,
required: true
},
'data': {
type: Array,
required: true
}
},
data(){
return {
parameters:null,
}
},
methods: {
mergeTransfer(){
let bookings=[];
this.data.forEach(function(booking) {
bookings.push(booking.id);
});
this.parameters = {};
this.$set(this.parameters, 'id', this.id);
this.$set(this.parameters, 'booking_ids', bookings);
this.submit(route('api.booking.merge'), 'post', this.section, true, true);
},
},
mixins: [componentHandler, staticFormHandler]
}
</script>
@@ -1,56 +1,68 @@
<template>
<div class="row align-items-center m-b-15 parentContainer">
<div class="col-auto">
<div class="font-heading fs-12">
<input type="checkbox" v-bind:data-id="this.item.id" @change="selectedBookings">
</div>
</div>
<div class="col-auto">
<div class="row">
<div class="col-auto p-r-10 all-caps">
<div class="font-heading all-caps fs-11 bold">{{this.item.marking}}</div>
<div class="row w-100 m-0 p-0">
<div class="col-auto" @click="showPurchaseDetails(item.id)">
<div class="hint-text">
<i class="fa fa-plus fs-8 muted hint-text"></i>
</div>
</div>
<div class="row">
<div class="col">
<div class="font-heading all-caps fs-10 muted">{{this.item.created_at}}</div>
<div class="col-auto">
<div class="font-heading fs-12" style="line-height: 0px;">
<input type="checkbox" v-bind:data-id="this.item.id" @change="selectedBookings">
</div>
</div>
<div class="col-2">
<div class="font-heading all-caps fs-10 muted">{{this.item.created_at}}</div>
</div>
<div class="col-2">
<div class="font-heading all-caps fs-11 bold">{{this.item.marking}}</div>
</div>
<div class="col-2">
<div class="font-heading fs-12 bold">
<span class="flag-icon fs-12" :class="'flag-icon-'+item.convertible_currency.country.short_code.toLowerCase()"></span> {{this.item.convertible_currency.short_code}}
</div>
</div>
<div class="col-2">
<div class="font-heading fs-11">{{this.item.service.name}}</div>
</div>
<div class="col-2">
<div class="font-heading fs-11 text-success bold">{{this.item.amount}} {{this.item.fixed_currency.short_code}}</div>
</div>
</div>
<div class="col" v-if="$store.getters.isAdmin">
<div class="font-heading fs-10 muted all-caps">Marking</div>
<div class="font-heading fs-12">
{{this.item.company.reference}}
<div class="row text-left no-margin bg-white hide" v-if="item.purchase_order!==null" v-for="po in this.item.purchase_order.details" v-bind:key="po.id">
<div class="col">
<div class="font-heading fs-11 text-success bold">{{po.description}}</div>
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Currency</div>
<div class="font-heading fs-12 bold">
<span class="flag-icon fs-12" :class="'flag-icon-'+item.convertible_currency.country.short_code.toLowerCase()"></span> {{this.item.convertible_currency.short_code}}
<div class="col">
<div class="font-heading fs-11 text-success bold">{{po.quantity}}</div>
</div>
<div class="col">
<div class="font-heading fs-11 text-success bold">{{po.unit_price}}</div>
</div>
<div class="col">
<div class="font-heading fs-11 text-success bold">{{po.total}}</div>
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Transfer Type</div>
<div class="font-heading fs-11">{{this.item.service.name}}</div>
</div>
<div class="col">
<div class="font-heading fs-10 muted all-caps">Payable Amount</div>
<div class="font-heading fs-11 text-success bold">{{this.item.amount}} {{this.item.fixed_currency.short_code}}</div>
</div>
<div class="col">
<div class="font-heading fs-10 muted all-caps">Status</div>
<div class="font-heading fs-11 text-success bold">{{this.item.status}}</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
data(){
return {
selected_id:0,
}
},
methods: {
selectedBookings(e) {
this.$root.$emit('selectedBookings', e.target);
this.$root.$emit('selectedBookings', e.target, this.item);
},
showPurchaseDetails(id){
console.log(this.selected_id);
this.selected_id = id;
},
},
mixins: [componentHandler]
@@ -5,7 +5,7 @@
<div class="row" v-show="!isLoading" v-if="booking">
<div class="col-9">
<div class="row">
<div class="col bg-white padding-25">
<div class="col bg-white p-t-25 p-b-0">
<div class="absolute" style="top: -5px;right: -5px;width: 0px;height: 0px;border-style: solid;border-width: 0px 110px 110px 0px;border-color: transparent rgb(249, 249, 249) transparent transparent;z-index: 2;">
<div @mouseover="bearShowing = true" style="width: 52px;height: 30px;margin-top: 75px;margin-left: 22px;"></div>
</div>
@@ -71,12 +71,34 @@
</div>
</div>
<div class="row">
<div class="col bg-white padding-25">
<div class="col bg-white padding-15">
<div class="row">
<p class="col-auto pull-left">Select Transfer Orders to merge with the order above</p>
</div>
<div class="row b-b b-dashed b-grey p-b-25">
<list-component key="2" section="transferSection" :endpoint="route('api.booking.list')" :options="{per_page: 10, id_not: booking.id, status: 2,service_id:booking.service.id,convertible_currency_id:booking.convertible_currency.id,fixed_currency_id:booking.fixed_currency.id}">
<div class="b-b b-dashed b-grey p-b-25">
<div class="row align-items-center m-b-15 parentContainer">
<div class="col-auto">
<div class="font-heading fs-12" style="line-height: 0px;">
<input type="checkbox" class="hide"/>
</div>
</div>
<div class="col-2">
<div class="font-heading fs-10 muted all-caps">Created At</div>
</div>
<div class="col-2">
<div class="font-heading fs-10 muted all-caps">Marking</div>
</div>
<div class="col-2">
<div class="font-heading fs-10 muted all-caps">Currency</div>
</div>
<div class="col-2">
<div class="font-heading fs-10 muted all-caps">Type</div>
</div>
<div class="col-2">
<div class="font-heading fs-10 muted all-caps">Amount</div>
</div>
</div>
<list-component key="2" section="transferSection" :endpoint="route('api.booking.list')" :options="{per_page: 10, id_not: booking.id, status: 2,service_id:booking.service.id,convertible_currency_id:booking.convertible_currency.id,fixed_currency_id:booking.fixed_currency.id,with_transactions:true}">
<template slot="list" slot-scope="{data}">
<booking-mergeable-component :data="data"></booking-mergeable-component>
</template>
@@ -86,25 +108,41 @@
</div>
</div>
<div class="col-3">
<div class="row m-l-0 m-r-0 m-b-15">
<div class="col bg-danger padding-15">
<div class="text-justify text-white fs-12">
Be cautious when merging multiple transfer orders, double check your reference numbers and the transfer amount before proceeding.
<br/><br/>
Please note that merged transfer orders can't be un-merged.
</div>
</div>
</div>
<div class="row no-margin">
<div class="col">
<div class="row">
<div class="col bg-white padding-15">
<div class="row" >
<div class="col">
<div class="row align-items-end m-b-10 text-complete">
<div class="row align-items-end text-complete">
<div class="col">
<div class="font-heading all-caps fs-10">Purchase Order Total Amount:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">0.00</div>
<div class="col-12 text-left m-t-10 m-b-10">
<div class="font-heading fs-12">
<span class="bold" :class="getAmountClass()">{{totalPurchaseAmount}}</span><span class="bold text-success">&nbsp;/&nbsp;{{totalAmount}}</span>
</div>
</div>
</div>
<div class="row m-t-20">
<div class="row">
<div class="col">
<button class="btn btn-xs all-caps b-rad-none btn-success btn-block" @click="mergeTransfer()">Merge Transfer Orders</button>
<button class="btn btn-xs all-caps b-rad-none btn-success btn-block requestModal" data-type="mergeTransferModal" :class="[{'disabled':!validForSubmit}]">Merge Transfer Orders</button>
</div>
</div>
<modal-component type="mergeTransferModal">
<booking-merge-modal-component section="bookingDetailSection" :id="booking.id" :data="this.bookings"></booking-merge-modal-component>
</modal-component>
</div>
</div>
</div>
@@ -117,7 +155,9 @@
</div>
</template>
<script>
import BookingMergeModalComponent from "../elements/BookingMergeModalComponent";
export default {
components: {BookingMergeModalComponent},
props: {
marking: {
type: Number,
@@ -139,6 +179,20 @@
computed: {
pendingQueue () {
return this.$store.getters.isInCompleteQueue(this.section);
},
totalAmount(){
return (Math.round((this.booking.amount + Number.EPSILON) * 100) / 100).toFixed(2);
},
totalPurchaseAmount(){
let totalPurchase = this.booking.purchase_order.amount;
this.bookings.forEach(function(booking) {
totalPurchase += booking.purchase_order.amount;
});
return (Math.round((totalPurchase + Number.EPSILON) * 100) / 100).toFixed(2);
},
validForSubmit(){
return (this.bookings.length>0);// && this.totalAmount == this.totalPurchaseAmount);
}
},
watch: {
@@ -152,11 +206,11 @@
this.$store.dispatch('updateListQueue', {'name': this.section});
},
mounted() {
this.$root.$on('selectedBookings', el => {
this.$root.$on('selectedBookings', (el, item) => {
if (el.checked) {
this.bookings.push(el.dataset.id);
this.bookings.push(item);
} else {
let index = this.bookings.indexOf(el.dataset.id);
let index = this.bookings.findIndex(b => b.id == el.dataset.id);
if (index > -1) {
this.bookings.splice(index, 1);
}
@@ -177,12 +231,19 @@
window.location.href = this.route('dashboard')
},
mergeTransfer(){
//TODO: Validate selected booking
console.log(this.bookings);
this.parameters={};
this.$set(this.parameters, 'booking_ids', this.bookings);
this.submit(route('api.booking.merge'), 'post', this.section, true, true);
/*
if(this.validForSubmit) {
//TODO: Validate selected booking
console.log(this.bookings);
this.parameters = {};
this.$set(this.parameters, 'booking_ids', this.bookings);
this.submit(route('api.booking.merge'), 'post', this.section, true, true);
}
*/
},
getAmountClass(){
return this.totalAmount == this.totalPurchaseAmount ? 'text-success' : 'text-danger';
},
}
}
@@ -1,5 +1,5 @@
<template>
<transition-component group enter-class="animate__animated animate__fadeInUp animate__delay-1 animate__faster p-r-30" leave-class="animate__animated animate__fadeOutDown animate__faster p-r-30" style="min-height: 300px;">
<transition-component group enter-class="animate__animated animate__fadeInUp animate__delay-1 animate__faster p-r-30" leave-class="animate__animated animate__fadeOutDown animate__faster p-r-30" style="min-height: 300px;width:100%">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
<div class="row" key="2" v-show="!$store.getters.isLoading(section)">
<div class="col">
+1 -1
View File
@@ -6,7 +6,6 @@ Route::group(['prefix' => 'booking', 'as' => 'booking.', 'namespace' => 'Booking
Route::get('/show/{marking}', 'FetchBookingController@fetch')->name('show');
Route::get('/list', 'ListBookingsController@list')->name('list');
Route::post('/create', 'CreateBookingController@create')->name('create');
Route::post('/merge', 'MergeBookingController@merge')->name('merge');
Route::put('/update/{id}', 'UpdateBookingController@update')->name('update');
Route::delete('/delete/{id}', 'DeleteBookingController@delete')->name('delete');
@@ -18,5 +17,6 @@ Route::group(['prefix' => 'booking', 'as' => 'booking.', 'namespace' => 'Booking
});
Route::post('{id}/purchase_order/verification', 'ApprovePurchaseOrderController@approve')->name('po.approval');
Route::post('/merge', 'MergeBookingController@merge')->name('merge');
});