Merge branch 'dillon/74-admin-workflow' into dillon/74-admin-workflow-2-20250228

This commit is contained in:
Dillon Ngo
2025-02-28 16:13:01 +08:00
112 changed files with 7252 additions and 368 deletions
@@ -0,0 +1,45 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class AnswerLikeWithUserId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
[$searchText, $userId] = $value;
if($userId && $searchText){
return $builder->where(function ($query) use ($searchText) {
$query->where('answer', 'like', '%' . $searchText . '%')
->orWhereHas('answer', function ($subquery) use ($searchText) {
$subquery->where('display_text', 'like', '%' . $searchText . '%');
});
})
->where('user_id', $userId);
}
else if($searchText){
return $builder->where(function ($query) use ($searchText) {
$query->where('answer', 'like', '%' . $searchText . '%')
->orWhereHas('answer', function ($subquery) use ($searchText) {
$subquery->where('display_text', 'like', '%' . $searchText . '%');
});
});
}
else{
return $builder->where('user_id', $userId);
}
// return $builder->where('answer', 'like', '%' . $value . '%')
// ->orWhereHas('answer', function ($subquery) use($value){
// $subquery->where('display_text', 'like', '%' . $value . '%');
// });
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
class EndDate implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDate('created_at', '<=', Carbon::parse($value)->format('Y-m-d'));
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class HasQuestionnaire implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('question', function ($subquery) use($value){
$subquery->whereHas('questionnaire', function ($subsubquery) use($value){
$subsubquery->where('id', $value);
});
});
}
}
@@ -0,0 +1,37 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use App\Models\QAUserAnswerSelected;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
class IdAfter implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
[$id, $reference, $questionGroups] = $value;
$nextId = QAUserAnswerSelected::where('id', '>', $id)
->where('reference', $reference)
->whereIn('answer', $questionGroups)
->orderBy('id', 'asc')
->limit(1)
->value('id');
if ($nextId) {
return $builder->where('id', '>', $id)
->where('id', '<', $nextId)->withTrashed();
}
return $builder->where('id', '>', $id)->withTrashed();
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class IsAdminFilter implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('is_admin_filter', $value);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class IsPrevious implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('is_previous', $value);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class QuestionGroupIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('question', function ($query) use ($value) {
$query->whereIn('group', $value);
});
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class QuestionId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('question_id', $value);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class QuestionIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('question', function ($query) use ($value) {
$query->whereIn('question_number', $value);
});
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class QuestionNumber implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('question_number', $value);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class QuestionnaireSetId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('questionnaire_set_id', $value);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
class StartDate implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDate('created_at', '>=', Carbon::parse($value)->format('Y-m-d'));
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class WithAnswers implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where(function ($query) use($value){
$query->whereIn('answer', $value);
});
}
}
@@ -30,13 +30,10 @@ class CanUpdateBankMetadata extends AbstractRule
*/
protected function authorized($object): bool
{
//cief todo: 66 - temporary workaround
// if (!Auth::user()->can('update bank_metadata')) {
// return false;
// }
// return true;
if (in_array(Auth::user()->type, RoleTypes::ADMIN_ROLES)) {
return true;
}
@@ -4,15 +4,8 @@ namespace App\Classes\Modules\Bookings\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Processors\ApprovePurchaseOrderProcessor;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Documents\Services\ApprovesDocument;
use App\Classes\Modules\Documents\Services\FetchesDocument;
use App\Classes\Modules\Documents\Services\RejectsDocument;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -32,23 +25,18 @@ class ApprovePurchaseOrderLogic extends AbstractControllerLogic
/** @var FetchesBooking */
private $fetchesBooking;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreateInvoiceTransactionProcessor */
private $createInvoiceTransactionProcessor;
/** @var ApprovePurchaseOrderProcessor */
private $approvePurchaseOrderProcessor;
/**
* ApprovePurchaseOrderLogic constructor.
* @param FetchesBooking $fetchesBooking
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
* @param ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor
*/
public function __construct(FetchesBooking $fetchesBooking, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor)
public function __construct(ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor, FetchesBooking $fetchesBooking)
{
$this->approvePurchaseOrderProcessor = $approvePurchaseOrderProcessor;
$this->fetchesBooking = $fetchesBooking;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
}
/**
@@ -58,18 +46,10 @@ class ApprovePurchaseOrderLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
$booking = $this->fetchesBooking->execute(['id' => $request->route('id')]);
$purchaseOrder = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();
$booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('id', '!=', $purchaseOrder->id)->delete();
$this->updatesTransactionStatus->execute($purchaseOrder, ApprovalStatus::APPROVED);
$this->createInvoiceTransactionProcessor->execute($booking);
$this->approvePurchaseOrderProcessor->execute($booking);
return $this->response([]);
}
}
}
@@ -5,31 +5,11 @@ namespace App\Classes\Modules\Bookings\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance;
use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor;
use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor;
use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\TransactionResource;
use App\Models\Booking;
use App\Models\Transaction;
use App\Models\Wallet;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Classes\Modules\Bookings\Processors\CreateBookingPaymentProcessor;
class CreateBookingPaymentLogic extends AbstractControllerLogic
{
@@ -44,71 +24,18 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
];
}
/** @var FetchesBookingQuotation */
private $fetchBookingQuotation;
/** @var CreateBookingPaymentProcessor */
private $createBookingPaymentProcessor;
/** @var FetchesCompanyPaymentAttemptLimit */
private $fetchesCompanyPaymentAttemptLimit;
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var CreatesTransaction */
private $createsTransaction;
/** @var CalculatesBookingOutstanding */
private $calculatesBookingOutstanding;
/** @var CreatesBillplzBill */
private $createsBillplzBill;
/** @var UpdatesWalletBalance */
private $updatesWalletBalance;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreateCashBackTransactionProcessor */
private $createCashBackTransactionProcessor;
/** @var RecalculatesWalletBalance */
private $recalculatesWalletBalance;
/** @var BookingToVoucherifyProcessor */
private $bookingToVoucherifyProcessor;
/** @var CalculatesBookingRefundAmount */
private $calculatesBookingRefundAmount;
/**
* CreateBookingPaymentLogic constructor.
* @param FetchesBookingQuotation $fetchBookingQuotation
* @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatesTransaction $createsTransaction
* @param CalculatesBookingOutstanding $calculatesBookingOutstanding
* @param CreatesBillplzBill $createsBillplzBill
* @param UpdatesWalletBalance $updatesWalletBalance
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor
* @param RecalculatesWalletBalance $recalculatesWalletBalance
* @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor
* @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
* @param CreateBookingPaymentProcessor $createBookingPaymentProcessor
*/
public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingOutstanding $calculatesBookingOutstanding, CreatesBillplzBill $createsBillplzBill, UpdatesWalletBalance $updatesWalletBalance, UpdatesTransactionStatus $updatesTransactionStatus, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor, CalculatesBookingRefundAmount $calculatesBookingRefundAmount)
public function __construct(CreateBookingPaymentProcessor $createBookingPaymentProcessor)
{
$this->fetchBookingQuotation = $fetchBookingQuotation;
$this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createsTransaction = $createsTransaction;
$this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
$this->createsBillplzBill = $createsBillplzBill;
$this->updatesWalletBalance = $updatesWalletBalance;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor;
$this->recalculatesWalletBalance = $recalculatesWalletBalance;
$this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor;
$this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
$this->createBookingPaymentProcessor = $createBookingPaymentProcessor;
}
/**
@@ -122,61 +49,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
$booking = Booking::find($request->route('id'));
$conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $request->input('amount'))), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')]);
$outstanding = $this->calculatesBookingOutstanding->execute($booking) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id);
if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.');
$configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode, null, $booking);
$paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company);
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
$paymentReference = null;
$amount = $configurations->getTotal();
if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY){
$billPlzBill = $this->createsBillplzBill->execute($booking->company->name, $request->user()->email, 'This payment is made for transfer ref. '.$booking->marking, $configurations->getTotal(), $billNumber, $request->input('bank_code'));
$paymentReference = $billPlzBill->id;
}
if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){
/** @var Wallet $wallet */
$wallet = $booking->company->wallets()->first();
if((float) number_format(($wallet->amount - $amount),2) < 0){
throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.');
}
$transaction_object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], '');
$transaction = $this->createsTransaction->execute($wallet, $transaction_object);
$paymentReference = $billNumber;
$walletBalance = $this->recalculatesWalletBalance->execute($wallet);
$this->updatesWalletBalance->execute($wallet, $walletBalance);
}
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
$object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id,
$configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(),
$configurations->getTotal(), $configurations->getForeignTotal(), 1,
$configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(),
$configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes($paymentAttemptLimit), ApprovalStatus::PENDING_SUBMISSION, [], $paymentReference);
/** @var Transaction $transaction */
$transaction = $this->createsTransaction->execute($booking, $object);
// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction);
$this->bookingToVoucherifyProcessor->execute($booking->company->employees()->first(), $transaction, $booking->company->id, $configurations->getSubTotal(), $configurations->getServiceCharge(), $configurations->getVoucherDiscountAmount(), $voucherCode);
if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
}
$transaction = $this->createBookingPaymentProcessor->execute($booking, $request->input('amount'), $request->input('payment_method'), $voucherCode, $request->input('bank_code'), $request->user()->email);
return $this->resourceResponse(new TransactionResource($transaction));
}
@@ -19,7 +19,7 @@ class FetchBookingLogic extends AbstractControllerLogic
protected function notification():array {
return [
'title' => 'Retrieved Booking',
'message' => 'You have successfully retrieved a Address'
'message' => 'You have successfully retrieved a Booking'
];
}
@@ -58,4 +58,4 @@ class FetchBookingLogic extends AbstractControllerLogic
}
}
}
@@ -1,31 +1,20 @@
<?php
namespace App\Classes\Modules\Bookings\ControllersLogic;
use App\Classes\Modules\Bookings\Services\UpdatesBookingFixedAmount;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\BookingResource;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Bookings\Standards\Rules\CanUpdateBooking;
use App\Classes\Modules\Bookings\DataTransferObjects\BookingObject;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use ErrorException;
use App\Classes\Modules\Bookings\Processors\UpdateBookingAmountProcessor;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Classes\Exceptions\MalformedRequestException;
class UpdateBookingAmountLogic extends AbstractControllerLogic
{
/**
* @return array
*/
@@ -39,33 +28,23 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic
/** @var CanUpdateBooking */
private $canUpdateBooking;
/** @var UpdatesBookingFixedAmount */
private $updatesBookingFixedAmount;
/** @var FetchesBooking */
private $fetchesBooking;
/** @var CalculatesBookingOutstanding */
private $calculatesBookingOutstanding;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var UpdateBookingAmountProcessor */
private $updateBookingAmountProcessor;
/**
* UpdateBookingAmountLogic constructor.
* @param CanUpdateBooking $canUpdateBooking
* @param UpdatesBookingFixedAmount $updatesBookingFixedAmount
* @param FetchesBooking $fetchesBooking
* @param CalculatesBookingOutstanding $calculatesBookingOutstanding
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param UpdateBookingAmountProcessor $updateBookingAmountProcessor
*/
public function __construct(CanUpdateBooking $canUpdateBooking, UpdatesBookingFixedAmount $updatesBookingFixedAmount, FetchesBooking $fetchesBooking, CalculatesBookingOutstanding $calculatesBookingOutstanding, UpdatesTransactionStatus $updatesTransactionStatus)
public function __construct(CanUpdateBooking $canUpdateBooking, FetchesBooking $fetchesBooking, UpdateBookingAmountProcessor $updateBookingAmountProcessor)
{
$this->canUpdateBooking = $canUpdateBooking;
$this->updatesBookingFixedAmount = $updatesBookingFixedAmount;
$this->fetchesBooking = $fetchesBooking;
$this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->updateBookingAmountProcessor = $updateBookingAmountProcessor;
}
/**
@@ -76,25 +55,11 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic
public function logic(Request $request) : JsonResponse
{
$booking = $this->fetchesBooking->execute(['id' => $request->route('id')]);
$fixAmount = floatval(str_replace(',', '', $request->input('fix_amount', $booking->fix_amount)));
$input_amount = number_format( floatval(str_replace(',', '', $request->input('fix_amount', $booking->fix_amount))), 5, '.', '');
$minimum_amount = $booking->fix_amount - $this->calculatesBookingOutstanding->execute($booking);
if (((float)$input_amount + 0.01) < (float)$minimum_amount) {
throw new MalformedRequestException('Booking Amount cannot be less than '. $minimum_amount .'.');
}
$poTransaction = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();
$booking->transactions()->where('type', TransactionType::PROFORMA)->delete();
if($poTransaction) {
$this->updatesTransactionStatus->execute($poTransaction, ApprovalStatus::PENDING_SUBMISSION);
}
$booking = $this->updatesBookingFixedAmount->execute($booking, $input_amount);
$this->updateBookingAmountProcessor->execute($booking, $fixAmount);
return $this->resourceResponse(new BookingResource($booking));
}
}
}
@@ -4,25 +4,8 @@ namespace App\Classes\Modules\Bookings\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Processors\CreatePurchaseOrderFor1688OrderProcessor;
use App\Classes\Modules\Bookings\Processors\UploadPurchaseOrderProcessor;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Documents\Services\ApprovesDocument;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\Services\FetchesDocument;
use App\Classes\Modules\Documents\Services\RejectsDocument;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Document;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -42,29 +25,19 @@ class UploadPurchaseOrderLogic extends AbstractControllerLogic
/** @var FetchesBooking */
private $fetchesBooking;
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFile;
/** @var CreatePurchaseOrderFor1688OrderProcessor */
private $createPurchaseOrderFor1688OrderProcessor;
/** @var UploadPurchaseOrderProcessor */
private $uploadPurchaseOrderProcessor;
/**
* UploadPurchaseOrderLogic constructor.
* @param FetchesBooking $fetchesBooking
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param CreatePurchaseOrderFor1688OrderProcessor $createPurchaseOrderFor1688OrderProcessor
* @param UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor
*/
public function __construct(FetchesBooking $fetchesBooking, CreatesDocument $createsDocument, CreatesFiles $createsFile, CreatePurchaseOrderFor1688OrderProcessor $createPurchaseOrderFor1688OrderProcessor)
public function __construct(FetchesBooking $fetchesBooking, UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor)
{
$this->fetchesBooking = $fetchesBooking;
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->createPurchaseOrderFor1688OrderProcessor = $createPurchaseOrderFor1688OrderProcessor;
$this->uploadPurchaseOrderProcessor = $uploadPurchaseOrderProcessor;
}
/**
@@ -74,19 +47,8 @@ class UploadPurchaseOrderLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
$booking = $this->fetchesBooking->execute(['id' => $request->route('id')]);
$object = new DocumentObject(DocumentType::ECOMMERCE_PURCHASE_ORDER, $request->input('files'), '', ApprovalStatus::APPROVED, '1688_purchase_orders');
/** @var Document $document */
$document = $this->createsDocument->execute($booking, $object);
$this->createsFile->execute($document, $object);
if(!in_array($booking->company->id, [199, 510])){
$this->createPurchaseOrderFor1688OrderProcessor->execute($booking);
}
$this->uploadPurchaseOrderProcessor->execute($booking, $request->input('files'));
return $this->response([]);
}
@@ -0,0 +1,53 @@
<?php
namespace App\Classes\Modules\Bookings\Processors;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
class ApprovePurchaseOrderProcessor
{
/** @var FetchesBooking */
private $fetchesBooking;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreateInvoiceTransactionProcessor */
private $createInvoiceTransactionProcessor;
/**
* ApprovePurchaseOrderProcessor constructor.
* @param FetchesBooking $fetchesBooking
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
*/
public function __construct(FetchesBooking $fetchesBooking, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor)
{
$this->fetchesBooking = $fetchesBooking;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
}
/**
* @param Booking $booking
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Booking $booking) {
$purchaseOrder = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();
$booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('id', '!=', $purchaseOrder->id)->delete();
$this->updatesTransactionStatus->execute($purchaseOrder, ApprovalStatus::APPROVED);
$this->createInvoiceTransactionProcessor->execute($booking);
}
}
@@ -0,0 +1,174 @@
<?php
namespace App\Classes\Modules\Bookings\Processors;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance;
use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor;
use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor;
use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use Illuminate\Support\Facades\Log;
use App\Models\Booking;
use App\Models\Transaction;
use App\Models\Wallet;
use Carbon\Carbon;
class CreateBookingPaymentProcessor
{
/** @var FetchesBookingQuotation */
private $fetchBookingQuotation;
/** @var FetchesCompanyPaymentAttemptLimit */
private $fetchesCompanyPaymentAttemptLimit;
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var CreatesTransaction */
private $createsTransaction;
/** @var CalculatesBookingOutstanding */
private $calculatesBookingOutstanding;
/** @var CreatesBillplzBill */
private $createsBillplzBill;
/** @var UpdatesWalletBalance */
private $updatesWalletBalance;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreateCashBackTransactionProcessor */
private $createCashBackTransactionProcessor;
/** @var RecalculatesWalletBalance */
private $recalculatesWalletBalance;
/** @var BookingToVoucherifyProcessor */
private $bookingToVoucherifyProcessor;
/** @var CalculatesBookingRefundAmount */
private $calculatesBookingRefundAmount;
/**
* CreateBookingPaymentProcessor constructor.
* @param FetchesBookingQuotation $fetchBookingQuotation
* @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatesTransaction $createsTransaction
* @param CalculatesBookingOutstanding $calculatesBookingOutstanding
* @param CreatesBillplzBill $createsBillplzBill
* @param UpdatesWalletBalance $updatesWalletBalance
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor
* @param RecalculatesWalletBalance $recalculatesWalletBalance
* @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor
* @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
*/
public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingOutstanding $calculatesBookingOutstanding, CreatesBillplzBill $createsBillplzBill, UpdatesWalletBalance $updatesWalletBalance, UpdatesTransactionStatus $updatesTransactionStatus, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor, CalculatesBookingRefundAmount $calculatesBookingRefundAmount)
{
$this->fetchBookingQuotation = $fetchBookingQuotation;
$this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createsTransaction = $createsTransaction;
$this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
$this->createsBillplzBill = $createsBillplzBill;
$this->updatesWalletBalance = $updatesWalletBalance;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor;
$this->recalculatesWalletBalance = $recalculatesWalletBalance;
$this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor;
$this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
}
/**
* Process booking payment.
*
* @param Booking $booking
* @param float $amount
* @param string $paymentMethod
* @param string|null $voucherCode
* @param string|null $bankCode
* @param string $email
* @return Transaction
* @throws MalformedRequestException
*/
public function execute(Booking $booking, string $amount, string $paymentMethod, ?string $voucherCode, ?string $bankCode, string $email, bool $checkSysRecordedOutstanding = true)
{
$conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $amount)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$paymentMethod]);
$outstanding = $this->calculatesBookingOutstanding->execute($booking) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id);
if($checkSysRecordedOutstanding){
if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.');
}
$configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode);
$paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company);
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
$paymentReference = null;
$amount = $configurations->getTotal();
if(PaymentMethodType::PAYMENT_METHODS[$paymentMethod] == PaymentMethodType::PAYMENT_GATEWAY){
$billPlzBill = $this->createsBillplzBill->execute($booking->company->name, $email, 'This payment is made for transfer ref. '.$booking->marking, $configurations->getTotal(), $billNumber, $bankCode);
$paymentReference = $billPlzBill->id;
}
if(PaymentMethodType::PAYMENT_METHODS[$paymentMethod] == PaymentMethodType::WALLET){
/** @var Wallet $wallet */
$wallet = $booking->company->wallets()->first();
if((float) number_format(($wallet->amount - $amount), 2) < 0){
throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.');
}
$transaction_object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], '');
$transaction = $this->createsTransaction->execute($wallet, $transaction_object);
$paymentReference = $billNumber;
$walletBalance = $this->recalculatesWalletBalance->execute($wallet);
$this->updatesWalletBalance->execute($wallet, $walletBalance);
}
$billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
$object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id,
$configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(),
$configurations->getTotal(), $configurations->getForeignTotal(), 1,
$configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(),
$configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes($paymentAttemptLimit), ApprovalStatus::PENDING_SUBMISSION, [], $paymentReference);
/** @var Transaction $transaction */
$transaction = $this->createsTransaction->execute($booking, $object);
// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction);
$this->bookingToVoucherifyProcessor->execute($booking->company->employees()->first(), $transaction, $booking->company->id, $configurations->getSubTotal(), $configurations->getVoucherDiscountAmount(), $voucherCode);
if(PaymentMethodType::PAYMENT_METHODS[$paymentMethod] == PaymentMethodType::WALLET){
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
}
return $transaction;
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Classes\Modules\Bookings\Processors;
use App\Classes\Modules\Bookings\Services\UpdatesBookingFixedAmount;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Exceptions\MalformedRequestException;
use App\Models\Booking;
class UpdateBookingAmountProcessor
{
/** @var UpdatesBookingFixedAmount */
private $updatesBookingFixedAmount;
/** @var CalculatesBookingOutstanding */
private $calculatesBookingOutstanding;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/**
* UpdateBookingAmountProcessor constructor.
* @param UpdatesBookingFixedAmount $updatesBookingFixedAmount
* @param CalculatesBookingOutstanding $calculatesBookingOutstanding
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
public function __construct(UpdatesBookingFixedAmount $updatesBookingFixedAmount, FetchesBooking $fetchesBooking, CalculatesBookingOutstanding $calculatesBookingOutstanding, UpdatesTransactionStatus $updatesTransactionStatus)
{
$this->updatesBookingFixedAmount = $updatesBookingFixedAmount;
$this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
$this->updatesTransactionStatus = $updatesTransactionStatus;
}
/**
* @param Booking $booking
* @return Booking $booking
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Booking $booking, float $fixAmount)
{
$input_amount = number_format($fixAmount, 5, '.', '');
$minimum_amount = $booking->fix_amount - $this->calculatesBookingOutstanding->execute($booking);
if (((float)$input_amount + 0.01) < (float)$minimum_amount) {
throw new MalformedRequestException('Booking Amount cannot be less than '. $minimum_amount .'.');
}
$poTransaction = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();
$booking->transactions()->where('type', TransactionType::PROFORMA)->delete();
if($poTransaction) {
$this->updatesTransactionStatus->execute($poTransaction, ApprovalStatus::PENDING_SUBMISSION);
}
$booking = $this->updatesBookingFixedAmount->execute($booking, $input_amount);
return $booking;
}
}
@@ -0,0 +1,60 @@
<?php
namespace App\Classes\Modules\Bookings\Processors;
use App\Classes\Modules\Bookings\Processors\CreatePurchaseOrderFor1688OrderProcessor;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Document;
use App\Models\Booking;
class UploadPurchaseOrderProcessor
{
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFile;
/** @var CreatePurchaseOrderFor1688OrderProcessor */
private $createPurchaseOrderFor1688OrderProcessor;
/**
* UploadPurchaseOrderProcessor constructor.
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param CreatePurchaseOrderFor1688OrderProcessor $createPurchaseOrderFor1688OrderProcessor
*/
public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFile, CreatePurchaseOrderFor1688OrderProcessor $createPurchaseOrderFor1688OrderProcessor)
{
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->createPurchaseOrderFor1688OrderProcessor = $createPurchaseOrderFor1688OrderProcessor;
}
/**
* @param Booking $booking
* @param array $files
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Booking $booking, array $files)
{
$object = new DocumentObject(DocumentType::ECOMMERCE_PURCHASE_ORDER, $files, '', ApprovalStatus::APPROVED, '1688_purchase_orders');
/** @var Document $document */
$document = $this->createsDocument->execute($booking, $object);
$this->createsFile->execute($document, $object);
if(!in_array($booking->company->id, [199, 510])){
$this->createPurchaseOrderFor1688OrderProcessor->execute($booking);
}
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Classes\Modules\Documents\Processors;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Document;
use App\Models\QAUserAnswerSelected;
class UploadDocumentProcessor
{
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFiles;
/**
* UploadDocumentProcessor constructor.
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFiles
*/
public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFiles)
{
$this->createsDocument = $createsDocument;
$this->createsFiles = $createsFiles;
}
/**
* @param QAUserAnswerSelected qaUserAnswerSelected
* @param array $filesUpload
* @param string $path
* @param string $documentType
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(QAUserAnswerSelected $qaUserAnswerSelected, $filesUpload, string $path, string $documentType = DocumentType::ADMIN_WORK_FLOW) {
$object = new DocumentObject($documentType, $filesUpload, '', ApprovalStatus::PENDING_VERIFICATION, $path);
/** @var Document $document */
$document = $this->createsDocument->execute($qaUserAnswerSelected, $object);
$result = $this->createsFiles->execute($document, $object);
return $result;
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Classes\Modules\Exports\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Exports\Services\ExportsQAWithGroups;
use App\Classes\Modules\Exports\Standards\Rules\CanExportQuestionsAnswers;
use Illuminate\Http\Request;
use App\Classes\General\AWSS3Helper;
use App\Classes\General\Helper;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\JsonResponse;
class ExportQAWithGroupsLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Export Questions & Answers',
'message' => 'You have successfully exported data'
];
}
/** @var ExportsQAWithGroups */
private $exportsQAWithGroups;
/** @var CanExportQuestionsAnswers */
private $canExport;
/**
* ExportQAWithGroupsLogic constructor.
* @param ExportsQAWithGroups $exportsQAWithGroups
* @param CanExportQuestionsAnswers $canExport
*/
public function __construct(ExportsQAWithGroups $exportsQAWithGroups, CanExportQuestionsAnswers $canExport)
{
$this->exportsQAWithGroups = $exportsQAWithGroups;
$this->canExport = $canExport;
}
/**
* @param Request $request
* @return Response
*/
public function logic(Request $request) : JsonResponse
{
$this->canExport->passes();
$this->exportsQAWithGroups->setFilters(Helper::deserializeFilters($request->input('filters')));
$exportFileName = 'qas.xls';
$filesystemDriver = Storage::getDefaultDriver();
if($filesystemDriver === 's3'){
return $this->response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $this->exportsQAWithGroups) ]);
}
return $this->response([ 'src' => null ]);
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Classes\Modules\Exports\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Exports\Services\ExportsQAWithoutGroups;
use App\Classes\Modules\Exports\Standards\Rules\CanExportQuestionsAnswers;
use Illuminate\Http\Request;
use App\Classes\General\AWSS3Helper;
use App\Classes\General\Helper;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\JsonResponse;
class ExportQAWithoutGroupsLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Export Questions & Answers',
'message' => 'You have successfully exported data'
];
}
/** @var ExportsQAWithoutGroups */
private $exportsQAWithoutGroups;
/** @var CanExportQuestionsAnswers */
private $canExport;
/**
* ExportQAWithoutGroupsLogic constructor.
* @param ExportsQAWithoutGroups $exportsQAWithoutGroups
* @param CanExportQuestionsAnswers $canExport
*/
public function __construct(ExportsQAWithoutGroups $exportsQAWithoutGroups, CanExportQuestionsAnswers $canExport)
{
$this->exportsQAWithoutGroups = $exportsQAWithoutGroups;
$this->canExport = $canExport;
}
/**
* @param Request $request
* @return Response
*/
public function logic(Request $request) : JsonResponse
{
$this->canExport->passes();
$this->exportsQAWithoutGroups->setFilters(Helper::deserializeFilters($request->input('filters')));
$exportFileName = 'qas.xls';
$filesystemDriver = Storage::getDefaultDriver();
if($filesystemDriver === 's3'){
return $this->response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $this->exportsQAWithoutGroups) ]);
}
return $this->response([ 'src' => null ]);
}
}
@@ -0,0 +1,137 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use App\Models\QAUserAnswerSelected;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use App\Classes\General\Eloquent\ApplyFiltersToQuery;
use App\Classes\ValueObjects\Constants\QuestionGroup;
use Carbon\Carbon;
class ExportsQAWithGroups implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
use Exportable;
private $filters;
public function headings(): array
{
return [
'Id',
'Version',
'Question Set',
'Question Text',
'Answer Text',
'Answer Value',
'Source Email',
'Time(Seconds)',
'Reference',
'Marking',
'Currency Rate',
'Total Amount',
'Created DateTime',
'MAIN?'
];
}
public function setFilters(array $filters = []): void
{
$this->filters = $filters;
}
/**
* @return \Illuminate\Support\Collection|mixed
*/
public function query()
{
$data = (new ApplyFiltersToQuery())->execute(QAUserAnswerSelected::query(), $this->filters, true);
return $data;
}
/**
* @param QAUserAnswerSelected $userAnswer
*
* @return array
*/
public function map($userAnswer): array
{
$source = $userAnswer->userSource;
$user = $userAnswer->source_id === 0 ? $userAnswer->user : null;
$answer = $userAnswer->answer()->first();
$answerValue = $answer ? $answer->value : null;
if($userAnswer->is_previous === 1){
$answerValue ='go_back';
}
$questionGroups = explode(',', $userAnswer->question->questionnaire->group);
$metadata = json_decode($userAnswer->question_metadata);
$rows = [[
$userAnswer->id,
$userAnswer->question->questionnaire->version,
$userAnswer->question->questionnaire->description,
$userAnswer->question->question_title,
$answer ? $answer->display_text : null,
$answerValue,
$user ? $user->email : $source->email,
$userAnswer->time_used_seconds,
$metadata->marking ?? null,
isset($metadata->company) ? $metadata->company->reference: null,
isset($metadata->payment_history[0]) ? $metadata->payment_history[0]->currency_rate : null,
isset($metadata->payment_history[0]) ? $metadata->payment_history[0]->currency->short_code . " " . round($metadata->payment_history[0]->amount, 2): null,
Carbon::parse($userAnswer->created_at)->format('d-m-Y h:i:s A'),
"YES"
]];
$nextId = QAUserAnswerSelected::where('id', '>', $userAnswer->id)
->where('reference', $userAnswer->reference)
->whereIn('answer', $questionGroups)
->orderBy('id', 'asc')
->limit(1)
->value('id');
if ($nextId) {
$temp = QAUserAnswerSelected::where('reference', '=', $userAnswer->reference)->where('id', '>', $userAnswer->id)
->where('id', '<', $nextId)->withTrashed()->get();
}
else{
$temp = QAUserAnswerSelected::where('reference', '=', $userAnswer->reference)->where('id', '>', $userAnswer->id)->withTrashed()->get();
}
foreach ($temp as $relatedAnswer) {
$source = $relatedAnswer->userSource;
$user = $relatedAnswer->source_id === 0 ? $relatedAnswer->user : null;
$answer = $relatedAnswer->answer()->first();
$answerValue = $answer ? $answer->value : null;
if($relatedAnswer->is_previous === 1){
$answerValue ='go_back';
}
$md = json_decode($relatedAnswer->question_metadata);
$rows[] = [
$relatedAnswer->id,
$relatedAnswer->question->questionnaire->version,
$relatedAnswer->question->questionnaire->description,
$relatedAnswer->question->question_title,
$answer ? $answer->display_text : null,
$answerValue,
$user ? $user->email : $source->email,
$relatedAnswer->time_used_seconds,
$md->marking ?? null,
isset($md->company) ? $md->company->reference: null,
isset($md->payment_history[0]) ? $md->payment_history[0]->currency_rate : null,
isset($md->payment_history[0]) ? $md->payment_history[0]->currency->short_code . " " . round($md->payment_history[0]->amount, 2): null,
Carbon::parse($userAnswer->created_at)->format('d-m-Y h:i:s A'),
];
}
return $rows;
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use App\Models\QAUserAnswerSelected;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use App\Classes\General\Eloquent\ApplyFiltersToQuery;
use Carbon\Carbon;
class ExportsQAWithoutGroups implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
use Exportable;
private $filters;
public function headings(): array
{
return [
'Id',
'Version',
'Question Set',
'Question Text',
'Answer Text',
'Answer Value',
'Source Email',
'Time(Seconds)',
'Reference',
'Marking',
'Currency Rate',
'Total Amount',
'Created DateTime',
'MAIN?'
];
}
public function setFilters(array $filters = []): void
{
$this->filters = $filters;
}
/**
* @return \Illuminate\Support\Collection|mixed
*/
public function query()
{
$data = (new ApplyFiltersToQuery())->execute(QAUserAnswerSelected::query()->withTrashed(), $this->filters, true);
return $data;
}
/**
* @param QAUserAnswerSelected $userAnswer
*
* @return array
*/
public function map($userAnswer): array
{
$source = $userAnswer->userSource;
$user = $userAnswer->source_id === 0 ? $userAnswer->user : null;
$answer = $userAnswer->answer()->first();
$answerValue = $answer ? $answer->value : null;
if($userAnswer->is_previous === 1){
$answerValue ='go_back';
}
$questionGroups = explode(',', $userAnswer->question->questionnaire->group);
$metadata = json_decode($userAnswer->question_metadata);
$rows = [[
$userAnswer->id,
$userAnswer->question->questionnaire->version,
$userAnswer->question->questionnaire->description,
$userAnswer->question->question_title,
$answer ? $answer->display_text : null,
$answerValue,
$user ? $user->email : $source->email,
$userAnswer->time_used_seconds,
$metadata->marking ?? null,
isset($metadata->company) ? $metadata->company->reference: null,
isset($metadata->payment_history[0]) ? $metadata->payment_history[0]->currency_rate : null,
isset($metadata->payment_history[0]) ? $metadata->payment_history[0]->currency->short_code . " " . round($metadata->payment_history[0]->amount, 2): null,
Carbon::parse($userAnswer->created_at)->format('d-m-Y h:i:s A'),
$answer ? in_array($answer->value, $questionGroups) : false,
]];
return $rows;
}
}
@@ -0,0 +1,48 @@
<?php
namespace App\Classes\Modules\Exports\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\ValueObjects\Constants\RoleTypes;
class CanExportQuestionsAnswers extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
if(Auth()->user()){
$roleToCheck = Auth()->user()->type;
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
return true;
}
else {
return false;
}
}
return false;
}
/**
* @param $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,111 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Http\Resources\BookingBaseResource;
use App\Models\Booking;
use App\Models\KeyValuePair;
use Carbon\Carbon;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchAdminWF1688BookingLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Booking for Admin Workflow ',
'message' => 'You have successfully retrieved a Booking for Admin Workflow'
];
}
/** @var CanFetchQuestion */
private $canFetchQuestion;
/** @var FetchesBooking */
private $fetchesBooking;
/**
* FetchAdminWFBookingLogic constructor.
* @param CanFetchQuestion $canFetchQuestion
* @param FetchesBooking $fetchesBooking
*/
public function __construct(CanFetchQuestion $canFetchQuestion, FetchesBooking $fetchesBooking)
{
$this->canFetchQuestion = $canFetchQuestion;
$this->fetchesBooking = $fetchesBooking;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canFetchQuestion->passes();
$excludedBookingIds = KeyValuePair::where('owner_type', 'App\Models\Booking')
->where(function ($query) {
$query->where('key', '1688_admin_workflow_processed')
->orWhere(function ($query) {
$query->where('key', '1688_admin_workflow_processing')
->where('updated_at', '>', Carbon::now()->subHour());
});
})
->pluck('owner_id')
->filter(function ($value) {
return is_numeric($value);
})
->toArray();
$timeAgo = Carbon::now()->subMonths(6);
$serviceId = 4;
$booking = Booking::where('service_id', $serviceId)
->where('status', ApprovalStatus::APPROVED)
->whereNotIn('id', $excludedBookingIds)
->whereHas('bills', function ($query) {
$query->whereHas('groupTransaction', function ($query) {
$query->whereHas('group', function ($query) {
$query->whereDoesntHave('billGroup')->whereIn('issuer', [2]);
});
});
})
->where('created_at', '>=', $timeAgo)
->latest()
->first();
if(!$booking){
return responseJson(null, 'No booking found', 404);
}
$booking = $this->fetchesBooking->execute(['id' => $booking->id, 'with_transactions' => true, 'order_by_id_desc' => true]);
markedProcessing($booking, '1688_admin_workflow_processing');
// return responseJson([
// 'passwords' => $booking->bank->holder_name ?? null,
// 'account_no' => $booking->bank->account_no ?? null,
// 'pin' => $booking->bank->bank_branch ?? null,
// 'holder_name' => $booking->bank->holder_name ?? null,
// 'booking' => $booking
// ]);
// return responseJson([
// 'booking' => new BookingBaseResource($booking)
// ]);
return $this->resourceResponse(new BookingBaseResource($booking));
}
}
@@ -0,0 +1,82 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
use App\Classes\ValueObjects\Constants\BookingAttributeNames;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Http\Resources\BookingBaseResource;
use App\Http\Resources\BookingResource;
use App\Models\Booking;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchAdminWFModelAttributesLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Model Attributes for Admin Workflow ',
'message' => 'You have successfully retrieved Model Attributes for Admin Workflow'
];
}
/** @var CanFetchQuestion */
private $canFetchQuestion;
/** @var FetchesBooking */
private $fetchesBooking;
/**
* FetchAdminWFModelAttributesLogic constructor.
* @param CanFetchQuestion $canFetchQuestion
* @param FetchesBooking $fetchesBooking
*/
public function __construct(CanFetchQuestion $canFetchQuestion, FetchesBooking $fetchesBooking)
{
$this->canFetchQuestion = $canFetchQuestion;
$this->fetchesBooking = $fetchesBooking;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canFetchQuestion->passes();
// $booking = Booking::find($request->route('booking_id'));
$booking = $this->fetchesBooking->execute(['id' => $request->route('booking_id'), 'with_transactions' => true, 'order_by_id_desc' => true]);
if (!$booking) {
return responseJson(null, 'No booking found', 404);
}
$attributes = $booking->modelAttributes()
->where('name', BookingAttributeNames::ORDER_REFERENCE_NO)
->get(['id', 'value'])
->map(fn ($attr) => $attr->only(['id', 'value']));
$transaction = $booking->bills()->first(); //cief todo: 74 - more than 1 record?
// return responseJson([
// 'booking' => $booking,
// 'booking_attributes' => $attributes,
// 'reference' =>$transaction->owner->owner->marking,
// 'marking' => $transaction->owner->owner->company->reference,
// 'currency_rate' => $transaction->currency_rate,
// 'total_amount' =>$transaction->currency->short_code . ' ' . number_format((float)$transaction->amount, 2, '.', '')
// ]);
return $this->resourceResponse(new BookingBaseResource($booking));
}
}
@@ -0,0 +1,99 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\BookingBaseResource;
use App\Http\Resources\BookingResource;
use App\Models\KeyValuePair;
use Carbon\Carbon;
use App\Models\Booking;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class FetchAdminWFPendingApprovalPOLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Pending Approval PO for Admin Workflow ',
'message' => 'You have successfully retrieved Pending Approval PO for Admin Workflow'
];
}
/** @var CanFetchQuestion */
private $canFetchQuestion;
/** @var FetchesBooking */
private $fetchesBooking;
/**
* FetchAdminWFPendingApprovalPOLogic constructor.
* @param CanFetchQuestion $canFetchQuestion
* @param FetchesBooking $fetchesBooking
*/
public function __construct(CanFetchQuestion $canFetchQuestion, FetchesBooking $fetchesBooking)
{
$this->canFetchQuestion = $canFetchQuestion;
$this->fetchesBooking = $fetchesBooking;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canFetchQuestion->passes();
$excludedBookingIds = KeyValuePair::where('owner_type', 'App\Models\Booking')
->where(function ($query) {
$query->where('key', 'approve_po_admin_workflow_processed')
->orWhere(function ($query) {
$query->where('key', 'approve_po_admin_workflow_processing')
->where('updated_at', '>', Carbon::now()->subMinutes(30));
});
})
->pluck('owner_id')
->filter(function ($value) {
return is_numeric($value);
})
->toArray();
// $booking = Booking::with('transactions')
// ->where('service_id', 4)
// ->where('status', ApprovalStatus::APPROVED)
// ->whereNotIn('id', $excludedBookingIds)
// ->whereHas('transactions', fn ($query) => $query->where('type', TransactionType::PURCHASE_ORDER)->where('status', '<', ApprovalStatus::APPROVED))
// ->first();
$booking = $this->fetchesBooking->execute(['purchase_order_approval' => true, 'status_in' => [2], 'id_not_in' => $excludedBookingIds, 'with_transactions' => true, 'order_by_id_desc' => true]);
if(!$booking){
return responseJson(null, 'No booking found', 404);
}
markedProcessing($booking, 'approve_po_admin_workflow_processing');
// $result = new BookingBaseResource($booking);
// return responseJson([
// 'booking' => $result,
// ]);
// return $booking ? responseJson(new BookingBaseResource($booking)) : responseJson(null, 'No booking found', 404);
return $this->resourceResponse(new BookingBaseResource($booking));
}
}
@@ -0,0 +1,96 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\BookingBaseResource;
use App\Models\KeyValuePair;
use Carbon\Carbon;
use App\Models\Booking;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchAdminWFPendingFillPOLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Pending Fill PO for Admin Workflow ',
'message' => 'You have successfully retrieved Pending Fill PO for Admin Workflow'
];
}
/** @var CanFetchQuestion */
private $canFetchQuestion;
/** @var FetchesBooking */
private $fetchesBooking;
/**
* FetchAdminWFPendingFillPOLogic constructor.
* @param CanFetchQuestion $canFetchQuestion
* @param FetchesBooking $fetchesBooking
*/
public function __construct(CanFetchQuestion $canFetchQuestion, FetchesBooking $fetchesBooking)
{
$this->canFetchQuestion = $canFetchQuestion;
$this->fetchesBooking = $fetchesBooking;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canFetchQuestion->passes();
$excludedBookingIds = KeyValuePair::where('owner_type', 'App\Models\Booking')
->where(function ($query) {
$query->where('key', 'fill_po_admin_workflow_processed')
->orWhere(function ($query) {
$query->where('key', 'fill_po_admin_workflow_processing')
->where('updated_at', '>', Carbon::now()->subHour());
});
})
->pluck('owner_id')
->filter(function ($value) {
return is_numeric($value);
})
->toArray();
// $booking = Booking::with('transactions')
// ->where('service_id', 4)
// ->where('status', ApprovalStatus::APPROVED)
// ->whereNotIn('id', $excludedBookingIds)
// ->whereDoesntHave('transactions', fn ($query) => $query->where('type', TransactionType::PURCHASE_ORDER))
// ->first();
$booking = $this->fetchesBooking->execute(['pending_purchase_order' => true, 'has_payment_status_in' => [2, 3], 'id_not_in' => $excludedBookingIds, 'with_transactions' => true, 'order_by_id_desc' => true]);
if(!$booking){
return responseJson(null, 'No booking found', 404);
}
markedProcessing($booking, 'fill_po_admin_workflow_processing');
// $result = new BookingBaseResource($booking);
// return responseJson([
// 'booking' => $result,
// ]);
// return $booking ? responseJson(new BookingResource($booking)) : responseJson(null, 'No booking found', 404);
return $this->resourceResponse(new BookingBaseResource($booking));
}
}
@@ -0,0 +1,62 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
use App\Classes\Modules\Questionnaires\Processors\FetchFirstQuestionV1AdminWFProcessor;
use App\Http\Resources\QuestionResource;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class FetchQuestionV1AdminWFLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Admin Workflow First Question',
'message' => 'You have successfully retrieved a Admin Workflow First Question'
];
}
/** @var CanFetchQuestion */
private $canFetchQuestion;
/** @var FetchFirstQuestionV1AdminWFProcessor */
private $fetchFirstQuestionQAProcessor;
/**
* FetchQuestionV1AdminWFLogic constructor.
* @param CanFetchQuestion $canFetchQuestion
* @param FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor
*/
public function __construct(CanFetchQuestion $canFetchQuestion, FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor)
{
$this->canFetchQuestion = $canFetchQuestion;
$this->fetchFirstQuestionQAProcessor = $fetchFirstQuestionQAProcessor;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canFetchQuestion->passes();
$userId = Auth::user()->id;
$query = $this->fetchFirstQuestionQAProcessor->execute($request);
return $this->resourceResponse(new QuestionResource($query, $userId));
}
}
@@ -0,0 +1,60 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Services\ListsQuestionnaireSet;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanListQuestions;
use App\Http\Resources\QuestionnaireSetsResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListQuestionnaireSetLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Questionnaire Sets',
'message' => 'You have successfully retrieved a list of questionnaire sets'
];
}
/** @var CanListQuestions */
private $canListQuestions;
/** @var ListsQuestionnaireSet */
private $listsQuestionnaireSet;
/**
* ListQuestionnaireSetLogic constructor.
* @param CanListQuestions $canListQuestions
* @param ListsQuestionnaireSet $listsQuestionnaires
*/
public function __construct(CanListQuestions $canListQuestions, ListsQuestionnaireSet $listsQuestionnaireSet)
{
$this->canListQuestions = $canListQuestions;
$this->listsQuestionnaireSet = $listsQuestionnaireSet;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
{
// $this->canListQuestions->passes(); //cief todo: 74
$query = $this->listsQuestionnaireSet->execute($this->listsQuestionnaireSet->deserializeFilters($request->input('filters')));
return $this->collectionResponse(QuestionnaireSetsResource::collection($query));
}
}
@@ -0,0 +1,60 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Services\ListsQuestionUserAnswerSelected;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanListQuestions;
use App\Http\Resources\QuestionsAnswersResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListQuestionsAnswersLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Questions Answers',
'message' => 'You have successfully retrieved a list of questions and answers'
];
}
/** @var CanListQuestions */
private $canListQuestions;
/** @var ListsQuestionUserAnswerSelected */
private $listsQuestionUserAnswerSelected;
/**
* ListQuestionsAnswersLogic constructor.
* @param CanListQuestions $canListQuestions
* @param ListsQuestionUserAnswerSelected $listsQuestionUserAnswerSelected
*/
public function __construct(CanListQuestions $canListQuestions, ListsQuestionUserAnswerSelected $listsQuestionUserAnswerSelected)
{
$this->canListQuestions = $canListQuestions;
$this->listsQuestionUserAnswerSelected = $listsQuestionUserAnswerSelected;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
{
$this->canListQuestions->passes();
$query = $this->listsQuestionUserAnswerSelected->execute($this->listsQuestionUserAnswerSelected->deserializeFilters($request->input('filters')));
return $this->collectionResponse(QuestionsAnswersResource::collection($query));
}
}
@@ -0,0 +1,70 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Services\ListsQuestions;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanListQuestions;
use App\Http\Resources\QuestionResource;
use App\Models\QAQuestionnaireSet;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListQuestionsLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Questions',
'message' => 'You have successfully retrieved a list of questions'
];
}
/** @var CanListQuestions */
private $canListQuestions;
/** @var ListsQuestions */
private $listsQuestions;
/**
* ListQuestionsLogic constructor.
* @param CanListQuestions $canListQuestions
* @param ListsQuestions $listsQuestions
*/
public function __construct(CanListQuestions $canListQuestions, ListsQuestions $listsQuestions)
{
$this->canListQuestions = $canListQuestions;
$this->listsQuestions = $listsQuestions;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
{
$this->canListQuestions->passes();
$setId = 0;
if ($request->route('set_id')) {
$setId = $request->route('set_id');
}
if($setId === 0){
$set = QAQuestionnaireSet::where('group', '1688,approve_po,fill_po')->latest('id')->first();
$setId = $set->id;
}
$query = $this->listsQuestions->execute(array_merge($this->listsQuestions->deserializeFilters($request->input('filters')), ['questionnaire_set_id' => $setId]));
return $this->collectionResponse(QuestionResource::collection($query));
}
}
@@ -0,0 +1,185 @@
<?php
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
use App\Classes\Modules\Questionnaires\Processors\SaveAnswerV1AdminWFProcessor;
use App\Classes\Modules\Questionnaires\Processors\FetchNextQuestionV1AdminWFProcessor;
use App\Classes\Modules\Questionnaires\Processors\FetchFirstQuestionV1AdminWFProcessor;
use App\Classes\Modules\Questionnaires\Processors\SaveAnswerActionV1AdminWFProcessor;
use App\Classes\Modules\Questionnaires\Processors\FetchNextQuestionMetadataV1AdminWFProcessor;
use App\Classes\Modules\Questionnaires\Services\ListsQuestionUserAnswerSelected;
use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair;
use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;
use App\Http\Resources\QuestionResource;
use App\Models\Booking;
use App\Models\QAQuestions;
class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Admin Workflow Next Question',
'message' => 'You have successfully retrieved a Admin Workflow Next Question'
];
}
/** @var CanFetchQuestion */
private $canFetchQuestion;
/** @var SaveAnswerV1AdminWFProcessor */
private $saveAnswerProcessor;
/** @var SaveAnswerActionV1AdminWFProcessor */
private $saveAnswerActionProcessor;
/** @var FetchNextQuestionV1AdminWFProcessor */
private $fetchNextQuestionV1AdminWFProcessor;
/** @var FetchNextQuestionMetadataV1AdminWFProcessor */
private $fetchNextQuestionMetadataProcessor;
/** @var FetchFirstQuestionV1AdminWFProcessor */
private $fetchFirstQuestionQAProcessor;
/** @var ListsQuestionUserAnswerSelected */
private $listsUserAnswerSelected;
/** @var CreatesKeyValuePair */
private $createsKeyValuePair;
/**
* UpdateNextQuestionV1AdminWFLogic constructor.
* @param CanFetchQuestion $canFetchQuestion
* @param SaveAnswerV1AdminWFProcessor $saveAnswerProcessor
* @param FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionV1AdminWFProcessor
* @param FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor
* @param ListsQuestionUserAnswerSelected $listsUserAnswerSelected
* @param CreatesKeyValuePair $createsKeyValuePair
* @param SaveAnswerActionV1AdminWFProcessor $saveAnswerActionProcessor
* @param FetchNextQuestionMetadataV1AdminWFProcessor $fetchNextQuestionMetadataProcessor
*/
public function __construct(CanFetchQuestion $canFetchQuestion, SaveAnswerV1AdminWFProcessor $saveAnswerProcessor, FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionV1AdminWFProcessor, FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor, ListsQuestionUserAnswerSelected $listsUserAnswerSelected, CreatesKeyValuePair $createsKeyValuePair, SaveAnswerActionV1AdminWFProcessor $saveAnswerActionProcessor, FetchNextQuestionMetadataV1AdminWFProcessor $fetchNextQuestionMetadataProcessor)
{
$this->canFetchQuestion = $canFetchQuestion;
$this->saveAnswerProcessor = $saveAnswerProcessor;
$this->fetchNextQuestionV1AdminWFProcessor = $fetchNextQuestionV1AdminWFProcessor;
$this->fetchFirstQuestionQAProcessor = $fetchFirstQuestionQAProcessor;
$this->listsUserAnswerSelected = $listsUserAnswerSelected;
$this->createsKeyValuePair = $createsKeyValuePair;
$this->saveAnswerActionProcessor = $saveAnswerActionProcessor;
$this->fetchNextQuestionMetadataProcessor = $fetchNextQuestionMetadataProcessor;
}
/**
* @param Request $request
* @return JsonResponse
* @throws MalformedRequestException
* @throws ResourceNotFoundException
*/
public function logic(Request $request) : JsonResponse
{
$userId = Auth::user()->id;
$timeUsedSeconds = 0;
$reference = null;
$questionMetadata = null;
$booking = null;
$previousAnswer = null;
$this->canFetchQuestion->passes();
$currentQuestion = $request->question;
$filesUpload = $request->only(['files', 'filesA', 'filesB', 'filesC']);
if ($request->has('questionContext')) {
$questionMetadata = $request->questionContext['questionMetadata'] ?? null;
$timeUsedSeconds = intval($request->questionContext['timeUsedSeconds']) ?? 0;
$reference = $request->questionContext['session_id'];
$booking = $questionMetadata ? Booking::where('id', $questionMetadata['id'])->first() : null;
}
//Save answer given by user (both next and previous)
$answerOptionId = 0;
if ($request->has('answerObj')) {
$answerOptionId = intval($request->answerObj['id']);
}
$this->saveAnswerProcessor->execute($userId, 0, $currentQuestion, $questionMetadata, $request->answer, $answerOptionId, $filesUpload, $timeUsedSeconds, $request->has('isPrevious'), $reference);
if(!$request->has('isPrevious')){
$this->saveAnswerActionProcessor->execute($booking, $questionMetadata, $answerOptionId, $currentQuestion, $filesUpload);
}
if ($request->has('questionContext')) {
$previousAnswer = $this->listsUserAnswerSelected->execute(['user_id' => $userId, 'reference' => $reference, 'is_previous' => 0, 'order_by' => (object)['column' => 'id','DESC' => true]])[0];
}
if($booking){
$this->markBookingAsProcessed($booking, $request->has('isPrevious'), $currentQuestion);
}
//Get returned question (previous or next) and additional metadata if applicable
$returnQuestion = $this->fetchNextQuestionV1AdminWFProcessor->execute($currentQuestion, $request->answerObj, $request->questionContext, $previousAnswer, $booking, $request->has('isPrevious'));
$questionMetadata = $this->fetchNextQuestionMetadataProcessor->execute($returnQuestion, $questionMetadata);
//When a questionnaire ended, return back the first question
if(is_null($returnQuestion)){
$returnQuestion = $this->fetchFirstQuestionQAProcessor->execute($request);
}
return $this->resourceResponse(new QuestionResource($returnQuestion, $userId, $request->has('isPrevious'), $this->isNoGoingBack($returnQuestion), $previousAnswer, $questionMetadata));
}
private function markBookingAsProcessed(Booking $booking, bool $isPrevious, array $currentQuestion){
//Marked data that has already been processed so that it does not appear again
if($currentQuestion && $currentQuestion['is_end'] === 1 && !$isPrevious){
$key1 = "admin_workflow_processed";
$key2 = "admin_workflow_processing";
if (strpos($currentQuestion['question_number'], '1688') === 0) {
$key1 = '1688_'.$key1;
$key2 = '1688_'.$key2;
}
if (strpos($currentQuestion['question_number'], 'fill_po') === 0) {
$key1 = 'xfill_po_'.$key1;
$key2 = 'xfill_po_'.$key2;
}
if (strpos($currentQuestion['question_number'], 'approve_po') === 0) {
$key1 = 'xapprove_po_'.$key1;
$key2 = 'xapprove_po_'.$key2;
}
$processingRecord = $booking->attributesKVP()->where('key', $key2)->first();
if ($processingRecord) {
$processingRecord->delete();
}
$kvp = $booking->attributesKVP()->where('key', $key1)->first();
if(!$kvp){
$keyValuePairObject = new KeyValuePairObject($key1, true);
$this->createsKeyValuePair->execute($booking, $keyValuePairObject);
}
}
}
private function isNoGoingBack(QAQuestions $returnQuestion){
if($returnQuestion && ($returnQuestion->is_start || $returnQuestion->is_end)){
return 1;
}
else if($returnQuestion && $returnQuestion->is_start === 0 && $returnQuestion && $returnQuestion->is_end === 0){
return 0;
}
return 0;
}
}
@@ -0,0 +1,56 @@
<?php
namespace App\Classes\Modules\Questionnaires\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class QAUserSourceObject implements DataTransferObject
{
/** @var string */
private $system;
/** @var string */
private $marking;
/** @var string */
private $email;
/**
* QAUserSourceObject constructor.
* @param string $system
* @param string $marking
* @param string $email
*/
public function __construct(string $system, string $marking, string $email)
{
$this->system = $system;
$this->marking = $marking;
$this->email = $email;
}
/**
* @return string
*/
public function getSystem(): string
{
return $this->system;
}
/**
* @return string
*/
public function getMarking(): string
{
return $this->marking;
}
/**
* @return string
*/
public function getEmail(): string
{
return $this->email;
}
}
@@ -0,0 +1,39 @@
<?php
namespace App\Classes\Modules\Questionnaires\Processors;
use App\Classes\Modules\Questionnaires\Services\FetchesQuestion;
use App\Models\QAQuestionnaireSet;
use ErrorException;
use Illuminate\Http\Request;
class FetchFirstQuestionV1AdminWFProcessor
{
/** @var FetchesQuestion */
private $fetchesQuestion;
/**
* FetchFirstQuestionV1AdminWFProcessor constructor.
* @param FetchesQuestion $fetchesQuestion
*/
public function __construct(FetchesQuestion $fetchesQuestion)
{
$this->fetchesQuestion = $fetchesQuestion;
}
public function execute(Request $request){
try {
$setId = $request->route('set_id');
if($setId === '0'){
$set = QAQuestionnaireSet::where('group', '1688,approve_po,fill_po')->latest('id')->first();
$setId = $set->id;
}
$query = $this->fetchesQuestion->execute(['questionnaire_set_id' => $setId]);
return $query;
} catch (\Exception $exception){
throw new ErrorException($exception->getMessage(), $exception->getCode());
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Classes\Modules\Questionnaires\Processors;
use App\Models\QAQuestions;
class FetchNextQuestionMetadataV1AdminWFProcessor
{
public function execute(QAQuestions $returnQuestion, ?array $questionMetadata) {
if($returnQuestion && $returnQuestion->question_number === '1688_underpaid_order_1'){
$amountProcessed = $questionMetadata['amount_processed'];
$amountPaid = $questionMetadata['paid_amount'];
$questionMetadata['amount_to_be_deducted'] = $amountProcessed - $amountPaid;
}
return $questionMetadata;
}
}
@@ -0,0 +1,148 @@
<?php
namespace App\Classes\Modules\Questionnaires\Processors;
use App\Classes\Modules\Questionnaires\Services\FetchesQuestion;
use App\Classes\Modules\Questionnaires\Services\ListsQuestions;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\QAType;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
use App\Models\Booking;
use App\Models\QAUserAnswerSelected;
class FetchNextQuestionV1AdminWFProcessor
{
/** @var FetchesQuestion */
private $fetchesQuestion;
/** @var ListsQuestions */
private $listsQuestions;
/** @var CalculatesBookingOutstanding */
private $calculatesBookingOutstanding;
/** @var CalculatesBookingRefundAmount */
private $calculatesBookingRefundAmount;
/** @var FetchesBookingQuotation */
private $fetchBookingQuotation;
/**
* FetchNextQuestionV1AdminWFProcessor constructor.
* @param FetchesQuestion $fetchesQuestion
* @param ListsQuestions $listsQuestions
* @param CalculatesBookingOutstanding $calculatesBookingOutstanding
* @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
* @param FetchesBookingQuotation $fetchBookingQuotation
*/
public function __construct(FetchesQuestion $fetchesQuestion, ListsQuestions $listsQuestions, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, FetchesBookingQuotation $fetchBookingQuotation)
{
$this->fetchesQuestion = $fetchesQuestion;
$this->listsQuestions = $listsQuestions;
$this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
$this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
$this->fetchBookingQuotation = $fetchBookingQuotation;
}
public function execute(array $currentQuestion, ?array $currentQuestionAnswer, array $questionContext, QAUserAnswerSelected $previousAnswer, ?Booking $booking, bool $isPrevious){
$isEnd = 0;
$questionId = 0;
$nextQuestionNumber = "";
$nextNestedQuestion = "";
$nextMainQuestion= "";
$questionnaireSetId = 0;
$questionType = QAType::DEFAULT;
$returnQuestion = null;
if ($currentQuestion && array_key_exists('id', $currentQuestion)) {
$questionId = $currentQuestion['id'];
$isEnd = $currentQuestion['is_end'];
$questionType = $currentQuestion['question_type'];
$nextNestedQuestion = $currentQuestion['next_nested_question'];
$nextMainQuestion = $currentQuestion['next_main_question'];
$questionnaireSetId = $currentQuestion['questionnaire_set_id'];
}
if ($currentQuestionAnswer && $currentQuestionAnswer['next_question_number']) {
$nextQuestionNumber = $currentQuestionAnswer['next_question_number'];
}
if ($isPrevious) {
if($previousAnswer)
{
$previousAnswer->delete();
}
else{
return null;
}
//Get previous question
$previousQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'id' => $previousAnswer['question_id']]);
$returnQuestion = $previousQuestion;
}
else{
//Get next question
$nextQuestion = null;
$question_number = "";
if($nextQuestionNumber !== ""){
$question_number = $nextQuestionNumber;
}
else if($nextNestedQuestion !== ""){
$question_number = $nextNestedQuestion;
}
else {
$question_number = $nextMainQuestion;
}
if($question_number !== ""){
//Check if question exists
$questions = $this->listsQuestions->execute(['questionnaire_set_id' => $questionnaireSetId, 'question_number' => $question_number]);
if($previousAnswer['answer'] === '1688_order_verified' && count($questions) == 0){
$questionMetadata = $questionContext['questionMetadata'] ?? null;
$amountProcessed = $questionMetadata['amount_processed'];
$amountPaid = $questionMetadata['paid_amount'];
$wallet = $booking->company->wallets()->first();
$walletAmount = $wallet->amount;
if ($amountProcessed === $amountPaid) {
$question_number = '1688_proceed_order';
}
else if ($amountProcessed > $amountPaid) {
$outstanding = $this->calculatesBookingOutstanding->execute($booking) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id);
$differenceUnderPayinCNY = $amountProcessed - $amountPaid;
$conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $differenceUnderPayinCNY)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS['wallet']);
$configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject);
$amountUnderpay = $configurations->getTotal();
// if($amountUnderpay > round($outstanding, 2)) {
// $question_number = '1688_underpaid_order_3';
// }
if($walletAmount > $amountUnderpay) {
$question_number = '1688_underpaid_order_1';
}
else {
$question_number = '1688_underpaid_order_2';
}
}
else {
$question_number = '1688_overpaid_order';
}
}
$nextQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'question_number' => $question_number]);
$returnQuestion = $nextQuestion;
}
}
return $returnQuestion;
}
}
@@ -0,0 +1,113 @@
<?php
namespace App\Classes\Modules\Questionnaires\Processors;
use App\Classes\Modules\Bookings\Processors\ApprovePurchaseOrderProcessor;
use App\Classes\Modules\Bookings\Processors\UploadPurchaseOrderProcessor;
use App\Classes\Modules\Bookings\Processors\CreateBookingPaymentProcessor;
use App\Classes\Modules\Bookings\Processors\UpdateBookingAmountProcessor;
use App\Classes\Modules\Transactions\Processors\CreatePaymentProofDocumentProcessor;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Models\QAAnswerOptions;
use Illuminate\Support\Facades\Log;
class SaveAnswerActionV1AdminWFProcessor
{
/** @var ApprovePurchaseOrderProcessor */
private $approvePurchaseOrderProcessor;
/** @var UploadPurchaseOrderProcessor */
private $uploadPurchaseOrderProcessor;
/** @var CreatePaymentProofDocumentProcessor */
private $createPaymentProofDocumentProcessor;
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var FetchesBookingQuotation */
private $fetchBookingQuotation;
/** @var CreateBookingPaymentProcessor */
private $createBookingPaymentProcessor;
/** @var UpdateBookingAmountProcessor */
private $updateBookingAmountProcessor;
/**
* SaveAnswerActionV1AdminWFProcessor constructor.
* @param ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor
* @param UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor
* @param CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor
* @param FetchesTransaction $fetchesTransaction
* @param FetchesBookingQuotation $fetchBookingQuotation
* @param CreateBookingPaymentProcessor $createBookingPaymentProcessor
* @param UpdateBookingAmountProcessor $updateBookingAmountProcessor
*/
public function __construct(ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor, UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor, CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor, FetchesTransaction $fetchesTransaction, FetchesBookingQuotation $fetchBookingQuotation, CreateBookingPaymentProcessor $createBookingPaymentProcessor, UpdateBookingAmountProcessor $updateBookingAmountProcessor)
{
$this->approvePurchaseOrderProcessor = $approvePurchaseOrderProcessor;
$this->uploadPurchaseOrderProcessor = $uploadPurchaseOrderProcessor;
$this->createPaymentProofDocumentProcessor = $createPaymentProofDocumentProcessor;
$this->fetchesTransaction = $fetchesTransaction;
$this->fetchBookingQuotation = $fetchBookingQuotation;
$this->createBookingPaymentProcessor = $createBookingPaymentProcessor;
$this->updateBookingAmountProcessor = $updateBookingAmountProcessor;
}
/**
* @return
*/
public function execute($booking, $questionMetadata, $answerOptionId, $currentQuestion, $filesUpload){
$answerOption = QAAnswerOptions::where('id', $answerOptionId)->first();
if($currentQuestion['question_number'] === 'approve_po' && $answerOption && $answerOption->value === "approve_po_approved"){
$this->approvePurchaseOrderProcessor->execute($booking);
}
else if($currentQuestion['question_number'] === '1688_submit' || $currentQuestion['question_number'] === '1688_underpaid_documents_submission' || $currentQuestion['question_number'] === '1688_overpaid_documents_submission')
{
if($filesUpload){
if ($booking && isset($filesUpload['filesA']) && isset($filesUpload['filesB']))
{
$mergedFilesUpload = array_merge(
$filesUpload['filesA'] ?? [],
$filesUpload['filesB'] ?? []
);
$this->uploadPurchaseOrderProcessor->execute($booking, $mergedFilesUpload);
}
if (isset($filesUpload['filesC']) && isset($questionMetadata['payment_history'][0]['transaction_bill']['id']))
{
$transaction = $this->fetchesTransaction->execute(['id' =>$questionMetadata['payment_history'][0]['transaction_bill']['id']]);
$this->createPaymentProofDocumentProcessor->execute($transaction, $filesUpload['filesC']);
}
}
}
else if($currentQuestion['question_number'] === '1688_underpaid_order_1')
{
$wallet = $booking->company->wallets()->first();
$amountProcessed = $questionMetadata['amount_processed'];
$amountPaid = $questionMetadata['paid_amount'];
if ($amountProcessed > $amountPaid) {
$differenceUnderInCNY = $amountProcessed - $amountPaid;
$conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $differenceUnderInCNY)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS['wallet']);
$configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject);
$amountUnderpay = $configurations->getTotal();
if($amountUnderpay != $differenceUnderInCNY){
$this->updateBookingAmountProcessor->execute($booking, $amountProcessed);
}
if($wallet->amount > $amountUnderpay){
$company = $booking->company()->first();
$employee = $company->employees()->first();
$transaction = $this->createBookingPaymentProcessor->execute($booking, (string) $differenceUnderInCNY, 'wallet', "", "", $employee->email, false);
}
}
}
}
}
@@ -0,0 +1,130 @@
<?php
namespace App\Classes\Modules\Questionnaires\Processors;
use App\Models\QAUserAnswerSelected;
use App\Classes\Modules\Documents\Processors\UploadDocumentProcessor;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\QAType;
use Illuminate\Support\Facades\Log;
class SaveAnswerV1AdminWFProcessor
{
/** @var UploadDocumentProcessor */
private $uploadDocumentForProcessor;
/**
* SaveAnswerV1AdminWFProcessor constructor.
* @param UploadDocumentProcessor $uploadDocumentForProcessor
*/
public function __construct(UploadDocumentProcessor $uploadDocumentForProcessor)
{
$this->uploadDocumentForProcessor = $uploadDocumentForProcessor;
}
/**
* @return QAUserAnswerSelected|\Illuminate\Database\Eloquent\Model
*/
public function execute($userId, $sourceId, $currentQuestion, $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $isPrevious, $reference = null){
$answer = null;
$attachments = [];
$questionId = $currentQuestion['id'];
$questionType = $currentQuestion['question_type'];
$questionNumber = $currentQuestion['question_number'];
if ($isPrevious){
$answerInText = "go_back";
}
if(is_null($answer))
{
$answer = new QAUserAnswerSelected;
}
$answer->user_id = $userId;
$answer->source_id = $sourceId;
$answer->question_id = $questionId;
$answer->question_metadata = json_encode($questionMetadata);
$answer->answer_option_id = $answerOptionId;
$answer->time_used_seconds = $timeUsedSeconds;
$answer->is_previous = $isPrevious;
if($reference){
$answer->reference = $reference;
}
$answer->save(); //cief todo: why 2 save() in this file, this is wrong
if($filesUpload){
if (isset($filesUpload['filesA']))
{
foreach ($filesUpload as $key => $files) {
if($key === 'filesA'){
$attachments[$key] = $this->saveFile($answer, $files, 'questionnaires', DocumentType::ADMIN_WORK_FLOW.'/'.DocumentType::ECOMMERCE_PURCHASE_ORDER_EN);
}
else if($key === "filesB"){
$attachments[$key] = $this->saveFile($answer, $files, 'questionnaires', DocumentType::ADMIN_WORK_FLOW.'/'.DocumentType::ECOMMERCE_PURCHASE_ORDER_CH);
}
else if($key === "filesC"){
$attachments[$key] = $this->saveFile($answer, $files, 'questionnaires', DocumentType::ADMIN_WORK_FLOW.'/'.DocumentType::CURRENCY_VENDOR_PAYMENT_PROOF);
}
}
}
else if (isset($filesUpload['files']) && $filesUpload['files'])
{
$attachments = $this->saveFile($answer, $filesUpload['files']);
}
}
if(!$isPrevious) {
if($questionType === QAType::SUBMIT_1688_3_TYPES_DOCUMENTS){
$structuredAnswer = [
'text' => "3_DOCUMENTS_UPLOADED",
'files' => $attachments,
];
$answer->answer = json_encode($structuredAnswer);
}
else if($questionType === QAType::REMARKS_WITH_DOCUMENT_UPLOAD){
$structuredAnswer = [
'text' => $answerInText,
'files' => $attachments,
];
$answer->answer = json_encode($structuredAnswer);
}
else if($questionType === QAType::DOCUMENT_UPLOAD){
$structuredAnswer = [
'files' => $attachments,
];
$answer->answer = json_encode($structuredAnswer);
}
else if($questionType === QAType::FLOAT_MONEY){
$answer->answer = floatval(str_replace(',', '', $answerInText));
}
else {
$answer->answer = $answerInText;
if($questionNumber === '1688_underpaid_order_1_proceed' || $questionNumber === '1688_underpaid_order_1'){
$answer->answer = $questionMetadata['amount_to_be_deducted'];
}
}
}
else {
$answer->answer = $answerInText;
}
$answer->save();
return $answer;
}
private function saveFile(QAUserAnswerSelected $answer, array $files, string $path = 'questionnaires', string $documentType = DocumentType::ADMIN_WORK_FLOW){
$result = $this->uploadDocumentForProcessor->execute($answer, $files, $path, $documentType);
$attachment = array_map(function ($item) use($documentType) {
return [
'name' => $item->document_id,
'file_id' => $item->id,
'document_type'=> $documentType,
];
}, $result);
return $attachment;
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Questionnaires\DataTransferObjects\QAUserSourceObject;
use App\Models\QAUserSource;
class CreatesQAUserSource extends AbstractUpdateRecord
{
/**
* @param QAUserSourceObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(QAUserSourceObject $object) {
$model = new QAUserSource();
$model->system = $object->getSystem();
$model->marking = $object->getMarking();
$model->email = $object->getEmail();
return $this->handler($model);
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\QAUserSource;
class FetchesQAUserSource extends AbstractFetchRecord
{
/** @var QAUserSource */
private $repository;
/**
* FetchesQAUserSource constructor.
* @param QAUserSource $repository
*/
public function __construct(QAUserSource $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\QAQuestions;
class FetchesQuestion extends AbstractFetchRecord
{
/** @var QAQuestions */
private $repository;
/**
* FetchesQuestion constructor.
* @param QAQuestions $repository
*/
public function __construct(QAQuestions $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\QAUserAnswerSelected;
class FetchesUserAnswerSelected extends AbstractFetchRecord
{
/** @var QAUserAnswerSelected */
private $repository;
/**
* FetchesUserAnswerSelected constructor.
* @param QAUserAnswerSelected $repository
*/
public function __construct(QAUserAnswerSelected $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\QAUserAnswerSelected;
class ListsQuestionUserAnswerSelected extends AbstractListRecord
{
/** @var QAUserAnswerSelected */
private $repository;
/**
* ListsQuestionUserAnswerSelected constructor.
* @param QAUserAnswerSelected $repository
*/
public function __construct(QAUserAnswerSelected $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\QAQuestionnaireSet;
class ListsQuestionnaireSet extends AbstractListRecord
{
/** @var QAQuestionnaireSet */
private $repository;
/**
* ListsQuestionnaireSet constructor.
* @param QAQuestionnaireSet $repository
*/
public function __construct(QAQuestionnaireSet $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Questionnaires\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\QAQuestions;
class ListsQuestions extends AbstractListRecord
{
/** @var QAQuestions */
private $repository;
/**
* ListsQuestions constructor.
* @param QAQuestions $repository
*/
public function __construct(QAQuestions $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,43 @@
<?php
namespace App\Classes\Modules\Questionnaires\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\ValueObjects\Constants\RoleTypes;
class CanFetchQuestion extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
$roleToCheck = Auth()->user()->type;
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
return true;
} else {
return false;
}
}
/**
* @param $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,45 @@
<?php
namespace App\Classes\Modules\Questionnaires\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\ValueObjects\Constants\RoleTypes;
class CanListQuestions extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
$roleToCheck = Auth()->user()->type;
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
return true;
} else {
return false;
}
}
/**
* @param $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -4,24 +4,10 @@ namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Companies\Services\UpdatesCompanyStatus;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\CompanyType;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\Jobs\SendUserPaymentProofUploadedEmail;
use App\Models\Company;
use App\Models\Document;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Processors\CreatePaymentProofDocumentProcessor;
class CreatePaymentProofDocumentLogic extends AbstractControllerLogic
{
@@ -39,37 +25,18 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFile;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreateInvoiceTransactionProcessor */
private $createInvoiceTransactionProcessor;
/** @var SendUserPaymentProofUploadedEmail */
private $sendUserPaymentProofUploadedEmail;
/** @var CreatePaymentProofDocumentProcessor */
private $createPaymentProofDocumentProcessor;
/**
* CreatePaymentProofDocumentLogic constructor.
* @param CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor
* @param FetchesTransaction $fetchesTransaction
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
*/
public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor, SendUserPaymentProofUploadedEmail $sendUserPaymentProofUploadedEmail)
public function __construct(CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor, FetchesTransaction $fetchesTransaction)
{
$this->createPaymentProofDocumentProcessor = $createPaymentProofDocumentProcessor;
$this->fetchesTransaction = $fetchesTransaction;
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
$this->sendUserPaymentProofUploadedEmail = $sendUserPaymentProofUploadedEmail;
}
/**
@@ -79,30 +46,10 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
$transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]);
$object = new DocumentObject(DocumentType::CURRENCY_VENDOR_PAYMENT_PROOF, $request->input('files'), '', ApprovalStatus::APPROVED, 'china_bank_slip');
/** @var Document $document */
$document = $this->createsDocument->execute($transaction, $object);
$file = $this->createsFile->execute($document, $object);
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
$this->createInvoiceTransactionProcessor->execute($transaction->owner->booking);
// send email to customer
// todo: a function to send a proof to the receipiant, they have to give us a email of the receipiant and also need to submiited purchase order
$companyEmployee = $transaction->owner->booking->company->employees;
foreach ($companyEmployee as $employee) {
if (app()->environment('production') || in_array($employee->email, ['cief.enquirycntr@gmail.com', 'tech.ciefmalaysia@gmail.com'])) {
$this->sendUserPaymentProofUploadedEmail::dispatch($employee, $transaction->owner->booking, $file[0]);
}
}
$this->createPaymentProofDocumentProcessor->execute($transaction, $request->input('files'));
return $this->response([]);
}
}
}
@@ -48,7 +48,7 @@ class DownloadMockUpWhiteFormPdfLogic
return ['id' => $value];
}, json_decode($request->input('payments')));
DB::beginTransaction();
DB::beginTransaction(); //cief todo: 74
$this->createSupplierTransactionProcessor->execute($supplier, $rate, $payments);
@@ -56,7 +56,7 @@ class DownloadMockUpWhiteFormPdfLogic
$pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $this->createSupplierTransactionProcessor->getBills(), 'transferFeeTransactions' => $this->createSupplierTransactionProcessor->getTransferTransactions(), 'supplier' => $supplier]);
DB::rollBack();
DB::rollBack(); //cief todo: 74
$exportFileName = 'MockUpWhiteForm.pdf';
$filesystemDriver = Storage::getDefaultDriver();
@@ -0,0 +1,81 @@
<?php
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\Jobs\SendUserPaymentProofUploadedEmail;
use App\Models\Transaction;
use App\Models\Document;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
class CreatePaymentProofDocumentProcessor
{
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFile;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreateInvoiceTransactionProcessor */
private $createInvoiceTransactionProcessor;
/** @var SendUserPaymentProofUploadedEmail */
private $sendUserPaymentProofUploadedEmail;
/**
* CreatePaymentProofDocumentProcessor constructor.
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
* @param SendUserPaymentProofUploadedEmail $sendUserPaymentProofUploadedEmail
*/
public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor, SendUserPaymentProofUploadedEmail $sendUserPaymentProofUploadedEmail)
{
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
$this->sendUserPaymentProofUploadedEmail = $sendUserPaymentProofUploadedEmail;
}
/**
* @param Transaction $transaction
* @param array $files
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Transaction $transaction, array $files)
{
$object = new DocumentObject(DocumentType::CURRENCY_VENDOR_PAYMENT_PROOF, $files, '', ApprovalStatus::APPROVED, 'china_bank_slip');
/** @var Document $document */
$document = $this->createsDocument->execute($transaction, $object);
$file = $this->createsFile->execute($document, $object);
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
$this->createInvoiceTransactionProcessor->execute($transaction->owner->booking);
// send email to customer
// todo: a function to send a proof to the receipiant, they have to give us a email of the receipiant and also need to submiited purchase order
$companyEmployee = $transaction->owner->booking->company->employees;
foreach ($companyEmployee as $employee) {
if (app()->environment('production') || in_array($employee->email, ['cief.enquirycntr@gmail.com', 'tech.ciefmalaysia@gmail.com'])) {
$this->sendUserPaymentProofUploadedEmail::dispatch($employee, $transaction->owner->booking, $file[0]);
}
}
}
}
@@ -2,6 +2,7 @@
namespace App\Classes\ValueObjects\Constants;
final class DocumentType {
public const PROFILE_PICTURE = 'PROFILE_PICTURE';
@@ -27,4 +28,8 @@ final class DocumentType {
public const BULK_PURCHASE_ORDER = 'BULK_PURCHASE_ORDER';
public const BILL_GROUP_PAYMENT_PROOF = 'BILL_GROUP_PAYMENT_PROOF';
public const ADMIN_WORK_FLOW = 'ADMIN_WORK_FLOW';
public const ECOMMERCE_PURCHASE_ORDER_EN = 'ECOMMERCE_PURCHASE_ORDER_EN';
public const ECOMMERCE_PURCHASE_ORDER_CH = 'ECOMMERCE_PURCHASE_ORDER_CH';
}
@@ -0,0 +1,27 @@
<?php
namespace App\Classes\ValueObjects\Constants;
final class QAType {
public const DEFAULT = 0;
public const MULTIPLE_CHOICES = 1;
public const FREE_TEXT = 2;
public const DOCUMENT_UPLOAD = 3;
// public const ANSWER = 4;
// public const TICKET_CRM = 5;
// public const RATING_5_STARS = 6;
public const REMARKS_WITH_DOCUMENT_UPLOAD = 7;
public const SUBMIT_1688_3_TYPES_DOCUMENTS = 8;
public const FLOAT_MONEY = 9;
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\ValueObjects\Constants;
final class QuestionGroup {
// public const _1688 = '1688';
// public const APPROVE_PO = 'approve_po';
// public const FILL_PO = 'fill_po';
// const OPTIONS_QUESTION_GROUP = [
// ['text' => '1688', 'id' => QuestionGroup::_1688],
// ['text' => 'approve_po', 'id' => QuestionGroup::APPROVE_PO],
// ['text' => 'fill_po', 'id' => QuestionGroup::FILL_PO],
// ];
// const QUESTION_GROUPS = [
// QuestionGroup::_1688,
// QuestionGroup::APPROVE_PO,
// QuestionGroup::FILL_PO,
// ];
}
@@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers\Exports;
use App\Classes\Modules\Exports\ControllersLogic\ExportQAWithGroupsLogic;
use App\Classes\Modules\Exports\ControllersLogic\ExportQAWithoutGroupsLogic;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Illuminate\Http\JsonResponse;
class ExportQuestionsAnswersController
{
/**
* @param Request $request
* @param ExportQAWithGroupsLogic $logic
* @return BinaryFileResponse
* @return JsonResponse
*/
public function exportWithGroups(Request $request, ExportQAWithGroupsLogic $logic) {
return $logic->execute($request);
}
/**
* @param Request $request
* @param ExportQAWithoutGroupsLogic $logic
* @return BinaryFileResponse
* @return JsonResponse
*/
public function exportWithoutGroups(Request $request, ExportQAWithoutGroupsLogic $logic) {
return $logic->execute($request);
}
}
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers\Questionnaires;
use App\Classes\Modules\Questionnaires\ControllersLogic\FetchAdminWF1688BookingLogic;
use App\Classes\Modules\Questionnaires\ControllersLogic\FetchAdminWFModelAttributesLogic;
use App\Classes\Modules\Questionnaires\ControllersLogic\FetchAdminWFPendingApprovalPOLogic;
use App\Classes\Modules\Questionnaires\ControllersLogic\FetchAdminWFPendingFillPOLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class AdminWorkflowBaseController
{
/**
* @param Request $request
* @param FetchAdminWF1688BookingLogic $logic
* @return JsonResponse
*/
public function fetchBooking(Request $request, FetchAdminWF1688BookingLogic $logic): JsonResponse {
return $logic->execute($request);
}
/**
* @param Request $request
* @param FetchAdminWFModelAttributesLogic $logic
* @return JsonResponse
*/
public function fetchModelAttributes(Request $request, FetchAdminWFModelAttributesLogic $logic): JsonResponse {
return $logic->execute($request);
}
/**
* @param Request $request
* @param FetchAdminWFPendingApprovalPOLogic $logic
* @return JsonResponse
*/
public function fetchPendingApprovalPO(Request $request, FetchAdminWFPendingApprovalPOLogic $logic): JsonResponse {
return $logic->execute($request);
}
/**
* @param Request $request
* @param FetchAdminWFPendingFillPOLogic $logic
* @return JsonResponse
*/
public function fetchPendingFillPO(Request $request, FetchAdminWFPendingFillPOLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Questionnaires;
use App\Classes\Modules\Questionnaires\ControllersLogic\FetchQuestionV1AdminWFLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchQuestionV1AdminWFController
{
/**
* @param Request $request
* @param FetchQuestionV1AdminWFLogic $logic
* @return JsonResponse
*/
public function fetch(Request $request, FetchQuestionV1AdminWFLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Questionnaires;
use App\Classes\Modules\Questionnaires\ControllersLogic\ListQuestionnaireSetLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListQuestionnaireSetController
{
/**
* @param Request $request
* @param ListQuestionnaireSetLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListQuestionnaireSetLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Questionnaires;
use App\Classes\Modules\Questionnaires\ControllersLogic\ListQuestionsAnswersLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListQuestionsAnswersController
{
/**
* @param Request $request
* @param ListQuestionsAnswersQALogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListQuestionsAnswersLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Questionnaires;
use App\Classes\Modules\Questionnaires\ControllersLogic\ListQuestionsLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListQuestionsController
{
/**
* @param Request $request
* @param ListQuestiosLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListQuestionsLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers\Questionnaires;
use App\Classes\Modules\Questionnaires\ControllersLogic\UpdateNextQuestionV1AdminWFLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Models\QAQuestionnaireSet;
class UpdateNextQuestionV1AdminWFController
{
/**
* @param Request $request
* @param FetchQAQuestionListLogic $logic
* @return JsonResponse
*/
public function fetch(Request $request, UpdateNextQuestionV1AdminWFLogic $logic): JsonResponse {
// To deploy different logic by versioning
// $currentQuestion = $request->question;
// $questionnaireSetId = $currentQuestion['questionnaire_set_id'];
// $questionnaireSet = QAQuestionnaireSet::where('id', $questionnaireSetId)->first();
// $group = '';
// $version = 0;
// if($questionnaireSet){
// $group = $questionnaireSet->group;
// $version = $questionnaireSet->version;
// }
return $logic->execute($request);
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AnswerOptionsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'display_text' => $this->display_text,
'value' => $this->value,
'order' => $this->order,
'question_number' => $this->question_number,
'next_question_number' => $this->next_question_number,
];
}
}
@@ -0,0 +1,61 @@
<?php
namespace App\Http\Resources;
use App\Classes\Modules\Bookings\Services\CalculatesBookingFloatingAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\BookingAttributeNames;
use Illuminate\Http\Resources\Json\JsonResource;
use Carbon\Carbon;
class BookingBaseResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function toArray($request)
{
return [
'id' => $this->id,
'company' => new CompanyResource($this->company),
'bank' => new BankResource($this->bank),
'service' => new ServiceTypeResource($this->service),
'marking' => $this->marking,
// 'amount' => $this->fix_amount,
// 'floating_amount' => floatval((App()->make(CalculatesBookingFloatingAmount::class))->execute($this->resource, $this->fix_currency_id)),
'paid_amount' => floatval((App()->make(CalculatesBookingPayableAmount::class))->execute($this->resource, $this->fix_currency_id)) - floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)),
// 'outstanding_amount' => floatval((App()->make(CalculatesBookingOutstanding::class))->execute($this->resource)),
'fixed_currency' => new CurrencyResource($this->fixedCurrency),
'order_reference_no' => $this->modelAttributes()->where('name', BookingAttributeNames::ORDER_REFERENCE_NO)->get()->map(function ($attr) {
return [
'id' => $attr->id,
'value' => $attr->value
];
}),
'status' => $this->status,
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
$this->mergeWhen($this->relationLoaded('transactions'), [
'payment_history' => TransactionResource::collection($this->transactions()->where(function($query){
$query->where(function($query){
$query->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED, ApprovalStatus::REFUNDED]);
})->orWhere(function($query){
$query->where(function($query){
$query->where('type', TransactionType::REFUND)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::REJECTED, ApprovalStatus::COMPLETED]);
})->orWhere(function($query){
$query->where('type', TransactionType::CREDIT_NOTE)->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
});
});
})->latest()->get())
])
];
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Models\QAQuestions;
use App\Models\QAAnswerOptions;
use App\Models\QAUserAnswerSelected;
use Illuminate\Support\Facades\Log;
class QuestionResource extends JsonResource
{
/** @var integer */
private $userId;
/** @var QAQuestions*/
private $question;
/** @var bool */
private $isPrevious;
/** @var integer */
private $isNoGoingBack;
/** @var integer */
private $previousAnswer;
/** @var object */
private $questionMetadata;
public function __construct($question, $userId, $isPrevious = false, $isNoGoingBack = null, $previousAnswer = null, $questionMetadata = null) {
$this->question = $question;
$this->userId = $userId;
$this->isPrevious = $isPrevious;
$this->isNoGoingBack = $isNoGoingBack;
$this->previousAnswer = $previousAnswer;
$this->questionMetadata = $questionMetadata;
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
if($this->question){
$q = (object)$this->question;
return [
'id' => $q->id,
'question_number' => $q->question_number,
'question_title' => $q->question_title,
'question_description' => $q->question_description,
'question_type' => $q->question_type,
'question_answers' => AnswerOptionsResource::collection(QAAnswerOptions::where('question_number', $q->question_number)->where('questionnaire_set_id', $q->questionnaire_set_id)->orderBy('order', 'ASC')->get()),
'questionnaire_set_id' => $q->questionnaire_set_id,
'question_metadata'=> $this->questionMetadata,
// 'questionnaire' => new QuestionnaireSetsResource($q->questionnaire),
// 'questionnaire_answers' => $q->is_end ? QuestionnaireAnswersResource::collection(QAUserAnswerSelected::where('user_id', $this->userId)->get()) : null,
'next_nested_question' => $q->next_nested_question,
'next_main_question' => $q->next_main_question,
'is_start' => $q->is_start,
'is_end' => $q->is_end,
'end_text' => $q->end_text,
'order' => $q->order,
'answer' => $this->previousAnswer,
'url' => $q->url,
'is_no_going_back' => $this->isNoGoingBack,
'is_previous' => $this->isPrevious,
];
}
else{
return [];
}
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Models\QAQuestions;
class QuestionnaireAnswersResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$question = QAQuestions::where('id', $this->question_id)->first();
return [
'question_title' => $question ? $question->question_title : null,
'answer' => $this->answer
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class QuestionnaireSetsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'group' => explode(',', $this->group),
'version' => $this->version,
];
}
}
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\DocumentType;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Models\QAAnswerOptions;
use App\Models\QAQuestions;
use Carbon\Carbon;
class QuestionsAnswersResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$question = QAQuestions::where('id', $this->question_id)->first();
$source = new UserSourceResource($this->userSource);
$user = $this->source_id === 0 ? new UserResource($this->user) : null;
$answerOption = QAAnswerOptions::where('id', $this->answer_option_id)->first();
$questionGroups = explode(',', $this->question->questionnaire->group);
$user_marking = '';
return [
'id' => $this->id,
'question_id' => $this->question_id,
'question_groups' => $questionGroups,
'questionnaire' => new QuestionnaireSetsResource($this->question->questionnaire),
'question_title' => $question ? $question->question_title : null,
'question_type' => $question ? $question->question_type : 0,
'question_metadata' => $this->question_metadata,
'answer' => $answerOption ? $answerOption->display_text : null,
'answer_value' => $this->answer,
'documentsList' => DocumentResource::collection($this->documents),
'documents' => new DocumentResource($this->documents->whereIn('document_type', DocumentType::ADMIN_WORK_FLOW)->first()),
'reference' => $this->reference,
'source_system' => null,
'source_marking' => $user ? $user_marking : $source->marking,
'source_email' => $user ? $user->email : $source->email,
'time' => $this->time_used_seconds,
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
'created_at_with_time' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'),
];
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserSourceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'system' => $this->system,
'email' => $this->email,
'marking' => $this->marking,
];
}
}
+10 -1
View File
@@ -2,11 +2,13 @@
namespace App\Models;
use App\Classes\General\Interfaces\Documentable;
use App\Classes\General\Interfaces\Transactionable;
use App\Classes\General\Interfaces\Voucherifiable;
use App\Classes\General\Traits\LogData;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Classes\General\Interfaces\KeyValueInterface;
use App\Scopes\CustomerBookingsScope;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
@@ -26,7 +28,7 @@ use Staudenmeir\EloquentHasManyDeep\HasRelationships;
* @property int convertible_currency_id
* @property int conversion_currency_id
*/
class Booking extends AbstractModel implements Documentable, Transactionable, Voucherifiable
class Booking extends AbstractModel implements Documentable, Transactionable, Voucherifiable, KeyValueInterface
{
use HasRelationships;
use SoftDeletes;
@@ -130,4 +132,11 @@ class Booking extends AbstractModel implements Documentable, Transactionable, Vo
return $this->morphMany(VoucherEntityMapping::class, 'owner');
}
/**
* @return MorphMany
*/
public function attributesKVP(): MorphMany
{
return $this->morphMany(KeyValuePair::class, 'owner');
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace App\Models;
class QAAnswerOptions extends AbstractModel
{
protected $table = 'qa_answer_options';
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace App\Models;
class QAQuestionnaireSet extends AbstractModel
{
protected $table = 'qa_questionnaire_sets';
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class QAQuestions extends AbstractModel
{
protected $table = 'qa_questions';
/**
* @return BelongsTo
*/
public function questionnaire(): BelongsTo
{
return $this->BelongsTo(QAQuestionnaireSet::class, 'questionnaire_set_id', 'id');
}
/**
* @return HasMany
*/
public function userAnswers()
{
return $this->hasMany(QAUserAnswerSelected::class, 'question_id', 'id');
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace App\Models;
use App\Classes\General\Interfaces\Documentable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Classes\General\Interfaces\KeyValueInterface;
/**
* Class Contact
* @package App\Models
*/
class QAUserAnswerSelected extends AbstractModel implements Documentable, KeyValueInterface
{
protected $table = 'qa_user_answer_selected';
use SoftDeletes;
/**
* @return MorphMany
*/
public function documents(): morphMany
{
return $this->morphMany(Document::class, 'owner');
}
/**
* @return BelongsTo
*/
public function userSource(): BelongsTo
{
return $this->BelongsTo(QAUserSource::class, 'source_id', 'id');
}
/**
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->BelongsTo(User::class, 'user_id', 'id');
}
/**
* @return BelongsTo
*/
public function question(): BelongsTo
{
return $this->BelongsTo(QAQuestions::class, 'question_id', 'id');
}
/**
* @return BelongsTo
*/
public function answer(): BelongsTo
{
return $this->BelongsTo(QAAnswerOptions::class, 'answer_option_id', 'id');
}
/**
* @return MorphMany
*/
public function attributesKVP(): MorphMany
{
return $this->morphMany(KeyValuePair::class, 'owner');
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace App\Models;
class QAUserSource extends AbstractModel
{
protected $table = 'qa_user_source';
}
@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateQAQuestionnaireSetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('qa_questionnaire_sets', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description')->nullable();
$table->string('group')->nullable();
$table->unsignedBigInteger('order')->default(0);
$table->unsignedBigInteger('next_set')->nullable();
$table->unsignedBigInteger('version')->default(1);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('qa_questionnaire_sets');
}
}
@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateQAQuestionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('qa_questions', function (Blueprint $table) {
$table->id();
$table->string('question_number');
$table->text('question_title');
$table->text('question_description')->nullable();
$table->unsignedBigInteger('questionnaire_set_id');
$table->string('next_nested_question')->nullable();
$table->string('next_main_question')->nullable();
$table->unsignedBigInteger('question_type');
$table->tinyInteger('is_start');
$table->tinyInteger('is_end');
$table->string('end_text')->nullable();
$table->unsignedBigInteger('order')->default(0);
$table->string('url')->nullable();
$table->timestamps();
$table->softDeletes();
$table->foreign('questionnaire_set_id')->references('id')->on('qa_questionnaire_sets');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('qa_questions');
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ChangeValueColumnToTextInKeyValuePairsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('key_value_pairs', function (Blueprint $table) {
$table->text('value')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('key_value_pairs', function (Blueprint $table) {
$table->string('value', 191)->change();
});
}
}
@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateQAAnswerOptionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('qa_answer_options', function (Blueprint $table) {
$table->id();
$table->text('display_text');
$table->string('value');
$table->unsignedBigInteger('order')->default(0);
$table->string('question_number');
$table->string('next_question_number')->nullable();
$table->unsignedBigInteger('questionnaire_set_id');
$table->timestamps();
$table->softDeletes();
$table->foreign('questionnaire_set_id')->references('id')->on('qa_questionnaire_sets');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('qa_answer_options');
}
}
@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateQAUserAnswerSelectedTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('qa_user_answer_selected', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id')->default(0);
$table->unsignedBigInteger('source_id')->default(0);
$table->unsignedBigInteger('question_id');
$table->longText('question_metadata')->nullable();
$table->unsignedBigInteger('answer_option_id');
$table->text('answer')->nullable();
$table->string('reference')->nullable();
$table->integer('time_used_seconds')->nullable();
$table->tinyInteger('is_previous')->default(0);
$table->timestamps();
$table->softDeletes();
$table->foreign('question_id')->references('id')->on('qa_questions');
// $table->foreign('answer_option_id')->references('id')->on('qa_answer_options');
// $table->foreign('user_id')->references('id')->on('users');
// $table->foreign('source_id')->references('id')->on('qa_user_source');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('qa_user_answer_selected');
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateQaUserSourceTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('qa_user_source', function (Blueprint $table) {
$table->id();
$table->string('system');
$table->string('email');
$table->string('marking');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('qa_user_source');
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddGroupToQaQuestionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('qa_questions', function (Blueprint $table) {
$table->string('group')->after('questionnaire_set_id')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('qa_questions', function (Blueprint $table) {
$table->dropColumn('group');
});
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsAdminFilterToQaQuestionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('qa_questions', function (Blueprint $table) {
$table->boolean('is_admin_filter')->after('url')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('qa_questions', function (Blueprint $table) {
$table->dropColumn('is_admin_filter');
});
}
}
+5 -1
View File
@@ -5,6 +5,7 @@ use Database\Seeders\CompaniesTableDevelopmentSeeder;
use Database\Seeders\CurrenciesTableDevelopmentSeeder;
use Database\Seeders\CurrencyRatesTableDevelopmentSeeder;
use Database\Seeders\DummyDataSeeder;
use Database\Seeders\QAWorkFlowSeeder;
use Database\Seeders\SegmentConstantsTableDevelopmentSeeder;
use Database\Seeders\SegmentsTableDevelopmentSeeder;
use Database\Seeders\ServiceTypesTableDevelopmentSeeder;
@@ -20,7 +21,7 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
DB::beginTransaction();
DB::beginTransaction();
//General
$this->call(CountriesTableSeeder::class);
@@ -48,6 +49,9 @@ class DatabaseSeeder extends Seeder
$this->call(DummyDataSeeder::class);
}
// Admin Work Flow
$this->call(QAWorkFlowSeeder::class);
// DB::commit();
}
}
+776
View File
@@ -0,0 +1,776 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\QAQuestionnaireSet;
use App\Models\QAQuestions;
use App\Models\QAAnswerOptions;
use App\Classes\ValueObjects\Constants\QAType;
class QAWorkFlowSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$questionnaireSets = [
[
'name' => 'Admin Work Flow',
'description' => 'Admin Work Flow',
'group' => '1688,approve_po,fill_po',
'version' => 1,
'questions' => [
[
'question_number' => 'node_0',
'question_title' => 'Are you ready to work today?',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => true,
'is_end' => false,
'answer_options' => [
['display_text' => 'Yes', 'value' => 'start_work', 'next_question_number' => 'start_work'],
['display_text' => 'No', 'value' => 'no_work', 'next_question_number' => 'no_work'],
],
'group' => '',
],
[
'question_number' => 'start_work',
'question_title' => 'What will you work on?',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => '1688', 'value' => '1688', 'next_question_number' => '1688'],
['display_text' => 'Approve PO', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
['display_text' => 'Fill PO', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
],
'group' => '',
],
[
'question_number' => 'no_work',
'question_title' => 'Come back when you are ready to work',
'question_type' => QAType::DEFAULT,
'is_start' => false,
'is_end' => true,
'group' => '',
],
[
'question_number' => '1688',
'question_title' => '1688',
'question_type' => QAType::MULTIPLE_CHOICES,
'url' => 'api.admin_work_flow.fetch_oldest_order', //'http://localhost:8082/api/v1/admin-work-flow/fetch-oldest-order', //this.route("api.admin_work_flow.fetch_oldest_order"),
'is_start' => true,
'is_end' => false,
'answer_options' => [
['display_text' => 'Login Issue', 'value' => '1688_login_issue', 'btn_color' => 'warning', 'next_question_number' => '1688_login_issue'],
['display_text' => 'Login Successful', 'value' => '1688_login_successful', 'next_question_number' => '1688_login_successful'],
],
'group' => '1688',
],
[
'question_number' => '1688_login_issue',
'question_title' => '1688_login_issue',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Need TAC', 'value' => '1688_login_issue_need_tac', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'Wrong login details', 'value' => '1688_login_issue_wrong_login_details', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'Others', 'value' => '1688_login_issue_others', 'next_question_number' => '1688_login_issue_others'],
['display_text' => 'Order Cancelled', 'value' => '1688_login_issue_refund_request', 'next_question_number' => '1688_login_issue_refund_request'],
],
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_login_successful',
'question_title' => '1688_login_successful',
'question_type' => QAType::MULTIPLE_CHOICES,
//'url' => 'api.admin_work_flow.fetch_model_attributes', //'http://localhost:8082/api/v1/admin-work-flow/{booking_id}/fetch-model-attributes',
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => "Can't verify order?", 'value' => '1688_login_successful_cannot_verify', 'next_question_number' => '1688_login_successful_cannot_verify'],
['display_text' => 'Order Verified', 'value' => '1688_order_verify', 'next_question_number' => '1688_order_verify'],
],
'group' => '1688',
],
[
'question_number' => '1688_login_successful_cannot_verify',
'question_title' => '1688_login_successful_cannot_verify',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Amount not found', 'value' => '1688_login_successful_cannot_verify_amount_not_found', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'Plus Member', 'value' => '1688_login_successful_cannot_verify_plus_member', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'Others', 'value' => '1688_login_successful_cannot_verify_others', 'next_question_number' => '1688_login_successful_cannot_verify_others'],
['display_text' => 'Customer did not verify 1688 account', 'value' => '1688_login_successful_cannot_verify_customer_did_not_verify_account', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'WorldFirst account linked another account', 'value' => '1688_login_successful_cannot_verify_worldfirst_account_linked_another_account', 'next_question_number' => '1688_issue_submit'],
],
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_login_successful_cannot_verify_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => '1688_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_order_verify',
'question_title' => 'Order Amount (CNY)',
'question_description' => 'Please key in the order amount',
'question_' => 'Order Amount',
'question_type' => QAType::FLOAT_MONEY,
'next_nested_question' => '1688_order_verification',
//'url' => 'api.admin_work_flow.fetch_model_attributes',
'is_start' => false,
'is_end' => false,
// 'answer_options' => [
// ['display_text' => 'Yes', 'value' => '1688_order_verification', 'next_question_number' => '1688_order_verification'],
// ],
'group' => '1688',
],
[
'question_number' => '1688_order_verification',
'question_title' => 'Are You Sure this is the correct amount?',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Yes', 'value' => '1688_order_verified', 'next_question_number' => '1688_order_verified'],
['display_text' => 'No', 'value' => '1688_order_verified', 'next_question_number' => '1688_order_verified'],
],
'group' => '1688',
],
[
'question_number' => '1688_proceed_order',
'question_title' => 'Proceed the order on 1688?',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Yes', 'value' => '1688_submit', 'next_question_number' => '1688_submit'],
['display_text' => 'Got Issue', 'value' => '1688_proceed_order_issue', 'next_question_number' => '1688_proceed_order_issue'],
],
'group' => '1688',
],
[
'question_number' => '1688_proceed_order_issue',
'question_title' => '1688_proceed_order_issue',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Wrong Pin Number', 'value' => '1688_proceed_order_issue_wrong_pin_number', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'Not Enough Stock', 'value' => '1688_proceed_order_issue_not_enough_stock', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'Others', 'value' => '1688_proceed_order_issue_others', 'next_question_number' => '1688_proceed_order_issue_others'],
['display_text' => 'No CrossBoarder', 'value' => '1688_proceed_order_issue_no_crossborder', 'next_question_number' => '1688_issue_submit'],
['display_text' => 'AngPau', 'value' => '1688_proceed_order_issue_angpau', 'next_question_number' => '1688_issue_submit'],
],
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_proceed_order_issue_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => '1688_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_login_issue_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => '1688_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_submit',
'question_title' => '1688_submit',
'question_type' => QAType::SUBMIT_1688_3_TYPES_DOCUMENTS,
'next_nested_question' => '1688_documents_submitted',
'is_start' => false,
'is_end' => false,
'group' => '1688',
],
[
'question_number' => '1688_documents_submitted',
'question_title' => 'Thank you for the hard work',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
[
'question_number' => '1688_issue_submit',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
[
'question_number' => '1688_login_issue_refund_request',
'question_title' => 'Refund Request sent',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
// [
// 'question_number' => '1688_insufficient_order',
// 'question_title' => 'Are You Sure this is the correct amount?',
// 'question_type' => QAType::MULTIPLE_CHOICES,
// 'is_start' => false,
// 'is_end' => false,
// 'answer_options' => [
// ['display_text' => 'Underpaid Order', 'value' => 'underpaid_order_1', 'next_question_number' => '1688_issue_submit'],
// ['display_text' => 'Underpaid Order', 'value' => 'underpaid_order_2', 'next_question_number' => '1688_issue_submit'],
// ['display_text' => 'Overpaid Order', 'value' => 'underpaid_order_2', 'next_question_number' => '1688_issue_submit'],
// ],
// 'group' => '1688',
// ],
// [
// 'question_number' => '1688_order_paid_precisely',
// 'question_title' => 'Amount paid is same as recorded in system',
// 'question_type' => QAType::MULTIPLE_CHOICES,
// 'is_start' => false,
// 'is_end' => false,
// 'answer_options' => [
// ['display_text' => 'Done', 'value' => '1688_issue_submit', 'next_question_number' => '1688_issue_submit'],
// ],
// 'group' => '1688',
// ],
[
'question_number' => '1688_underpaid_order_1',
'question_title' => "This order is underpaid. Clicking 'Next' will deduct the amount short from wallet automatically. ",
'question_type' => QAType::DEFAULT,
'is_start' => false,
'is_end' => false,
'next_nested_question' => '1688_underpaid_order_1_proceed',
// 'answer_options' => [
// ['display_text' => 'Proceed', 'value' => '1688_underpaid_order_1_proceed', 'next_question_number' => '1688_underpaid_order_1_proceed'],
// ],
'group' => '1688',
],
[
'question_number' => '1688_underpaid_order_1_proceed',
'question_title' => "The missing amount has successfully been deducted from the customers wallet",
'question_type' => QAType::DEFAULT,
'is_start' => false,
'is_end' => true,
'next_nested_question' => '1688_underpaid_proceed_order',
// 'answer_options' => [
// ['display_text' => 'Done', 'value' => '1688_issue_submit', 'next_question_number' => '1688_issue_submit'],
// ],
'group' => '1688',
],
[
'question_number' => '1688_underpaid_order_2',
'question_title' => 'Underpaid Order',
'question_description' => 'Insufficient wallet balance. Refund request to be sent',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Done', 'value' => '1688_underpaid_order_2_submitted', 'next_question_number' => '1688_underpaid_order_2_submitted'],
],
'group' => '1688',
],
// [
// 'question_number' => '1688_underpaid_order_3',
// 'question_title' => 'Underpaid Order?',
// 'question_description' => 'There is no outstanding on record, please go back and check if the amount processed is entered correctly.',
// 'question_type' => QAType::MULTIPLE_CHOICES,
// 'is_start' => false,
// 'is_end' => false,
// 'answer_options' => [
// ['display_text' => 'Done', 'value' => '1688_issue_submit', 'next_question_number' => '1688_issue_submit'],
// ],
// 'group' => '1688',
// ],
[
'question_number' => '1688_overpaid_order',
'question_title' => 'Overpaid Order',
'question_description' => 'Exceeded amount to be refunded to customers wallet',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Next', 'value' => '1688_overpaid_proceed_order', 'next_question_number' => '1688_overpaid_proceed_order'],
],
'group' => '1688',
],
// 1688 underpaid - starts
[
'question_number' => '1688_underpaid_proceed_order',
'question_title' => 'Please process the order on 1688',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Yes', 'value' => '1688_underpaid_documents_submission', 'next_question_number' => '1688_underpaid_documents_submission'],
['display_text' => 'Got Issue', 'value' => '1688_underpaid_proceed_order_issue', 'next_question_number' => '1688_underpaid_proceed_order_issue'],
],
'group' => '1688',
],
[
'question_number' => '1688_underpaid_proceed_order_issue',
'question_title' => '1688_underpaid_proceed_order_issue',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Wrong Pin Number', 'value' => '1688_underpaid_proceed_order_issue_wrong_pin_number', 'next_question_number' => '1688_underpaid_issue_submit'],
['display_text' => 'Not Enough Stock', 'value' => '1688_underpaid_proceed_order_issue_not_enough_stock', 'next_question_number' => '1688_underpaid_issue_submit'],
['display_text' => 'Others', 'value' => '1688_underpaid_proceed_order_issue_others', 'next_question_number' => '1688_underpaid_proceed_order_issue_others'],
['display_text' => 'No CrossBoarder', 'value' => '1688_underpaid_proceed_order_issue_no_crossborder', 'next_question_number' => '1688_underpaid_issue_submit'],
['display_text' => 'AngPau', 'value' => '1688_underpaid_proceed_order_issue_angpau', 'next_question_number' => '1688_underpaid_issue_submit'],
],
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_underpaid_proceed_order_issue_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => '1688_underpaid_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_underpaid_documents_submission',
'question_title' => '1688_underpaid_documents_submission',
'question_type' => QAType::SUBMIT_1688_3_TYPES_DOCUMENTS,
'next_nested_question' => '1688_underpaid_documents_submitted',
'is_start' => false,
'is_end' => false,
'group' => '1688',
],
[
'question_number' => '1688_underpaid_documents_submitted',
'question_title' => 'Thank you for your hard work',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
[
'question_number' => '1688_underpaid_issue_submit',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
[
'question_number' => '1688_underpaid_order_2_submitted',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
// 1688 underpaid - ends
// 1688 overpaid - starts
[
'question_number' => '1688_overpaid_proceed_order',
'question_title' => 'Please process the order on 1688',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Yes', 'value' => '1688_overpaid_documents_submission', 'next_question_number' => '1688_overpaid_documents_submission'],
['display_text' => 'Got Issue', 'value' => '1688_overpaid_proceed_order_issue', 'next_question_number' => '1688_overpaid_proceed_order_issue'],
],
'group' => '1688',
],
[
'question_number' => '1688_overpaid_proceed_order_issue',
'question_title' => '1688_overpaid_proceed_order_issue',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Wrong Pin Number', 'value' => '1688_overpaid_proceed_order_issue_wrong_pin_number', 'next_question_number' => '1688_overpaid_issue_submit'],
['display_text' => 'Not Enough Stock', 'value' => '1688_overpaid_proceed_order_issue_not_enough_stock', 'next_question_number' => '1688_overpaid_issue_submit'],
['display_text' => 'Others', 'value' => '1688_overpaid_proceed_order_issue_others', 'next_question_number' => '1688_overpaid_proceed_order_issue_others'],
['display_text' => 'No CrossBoarder', 'value' => '1688_overpaid_proceed_order_issue_no_crossborder', 'next_question_number' => '1688_overpaid_issue_submit'],
['display_text' => 'AngPau', 'value' => '1688_overpaid_proceed_order_issue_angpau', 'next_question_number' => '1688_overpaid_issue_submit'],
],
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_overpaid_proceed_order_issue_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => '1688_overpaid_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => '1688',
'is_admin_filter' => true
],
[
'question_number' => '1688_overpaid_documents_submission',
'question_title' => '1688_overpaid_documents_submission',
'question_type' => QAType::SUBMIT_1688_3_TYPES_DOCUMENTS,
'next_nested_question' => '1688_overpaid_documents_submitted',
'is_start' => false,
'is_end' => false,
'group' => '1688',
],
[
'question_number' => '1688_overpaid_documents_submitted',
'question_title' => 'Thank you for your hard work',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
[
'question_number' => '1688_overpaid_issue_submit',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
],
'group' => '1688',
],
// 1688 overpaid - ends
[
'question_number' => 'approve_po',
'question_title' => 'approve_po',
'question_type' => QAType::MULTIPLE_CHOICES,
'url' => "api.admin_work_flow.fetch_pending_approve_po",
'is_start' => true,
'is_end' => false,
'answer_options' => [
['display_text' => 'Approve', 'value' => 'approve_po_approved', 'next_question_number' => 'approve_po_approved'],
['display_text' => 'Edit PO', 'value' => 'approve_po_edit', 'next_question_number' => 'approve_po_edit'],
['display_text' => 'Reject', 'value' => 'approve_po_reject', 'next_question_number' => 'approve_po_reject'],
],
'group' => 'approve_po',
],
[
'question_number' => 'fill_po',
'question_title' => 'fill_po',
'question_type' => QAType::MULTIPLE_CHOICES,
'url' => "api.admin_work_flow.fetch_pending_fill_po",
'is_start' => true,
'is_end' => false,
'answer_options' => [
['display_text' => 'Edit PO', 'value' => 'fill_po_edit', 'next_question_number' => 'fill_po_edit'],
],
'group' => 'fill_po',
],
[
'question_number' => 'approve_po_filled',
'question_title' => 'PO Filled',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'approve_po',
],
[
'question_number' => 'approve_po_approved',
'question_title' => 'PO Approved',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'approve_po',
],
[
'question_number' => 'approve_po_edit',
'question_title' => 'approve_po_edit',
'question_type' => QAType::MULTIPLE_CHOICES,
'url' => "api.booking.show",
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Issue?', 'value' => 'approve_po_edit_po_issue', 'next_question_number' => 'approve_po_edit_po_issue'],
['display_text' => 'Done', 'value' => 'approve_po_filled', 'next_question_number' => 'approve_po_filled'],
],
'group' => 'approve_po',
],
[
'question_number' => 'approve_po_reject',
'question_title' => 'PO Rejected',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Sensitive Goods', 'value' => 'Sensitive Goods', 'next_question_number' => 'approve_po_reject_others_complete'],
['display_text' => 'Others', 'value' => 'approve_po_reject_others', 'next_question_number' => 'approve_po_reject_others'],
// ['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
// ['display_text' => 'Next Order', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'approve_po',
'is_admin_filter' => true
],
[
'question_number' => 'approve_po_reject_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => 'approve_po_reject_others_complete',
'is_start' => false,
'is_end' => false,
'group' => 'approve_po',
'is_admin_filter' => true
],
[
'question_number' => 'approve_po_reject_others_complete',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'approve_po',
],
[
'question_number' => 'approve_po_edit_po_issue',
'question_title' => 'approve_po_edit_po_issue',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Sensitive Goods', 'value' => 'Sensitive Goods', 'next_question_number' => 'approve_po_edit_po_issue_submit'],
['display_text' => 'Others', 'value' => 'approve_po_edit_po_issue_others', 'next_question_number' => 'approve_po_edit_po_issue_others'],
],
'group' => 'approve_po',
'is_admin_filter' => true
],
[
'question_number' => 'approve_po_edit_po_issue_submit',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'approve_po',
],
[
'question_number' => 'approve_po_edit_po_issue_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => 'approve_po_edit_po_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => 'approve_po',
'is_admin_filter' => true
],
[
'question_number' => 'approve_po_edit_po_issue_submit',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'approve_po',
],
[
'question_number' => 'fill_po_issue_others',
'question_title' => 'What other issues did you encounter? Upload documents if needed',
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
'next_nested_question' => 'fill_po_issue_submit',
'is_start' => false,
'is_end' => false,
'group' => 'fill_po',
'is_admin_filter' => true
],
[
'question_number' => 'fill_po_issue_submit',
'question_title' => 'Issue has been submitted',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'fill_po',
],
[
'question_number' => 'fill_po_edit',
'question_title' => 'fill_po_edit',
'question_type' => QAType::MULTIPLE_CHOICES,
'url' => "api.booking.show",
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Issue?', 'value' => 'fill_po_edit_issue', 'next_question_number' => 'fill_po_edit_issue'],
['display_text' => 'Done', 'value' => 'fill_po_edit_filled', 'next_question_number' => 'fill_po_edit_filled'],
],
'group' => 'fill_po',
],
[
'question_number' => 'fill_po_edit_issue',
'question_title' => 'fill_po_edit_issue',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => false,
'answer_options' => [
['display_text' => 'Sensitive Goods', 'value' => 'Sensitive Goods', 'next_question_number' => 'fill_po_issue_submit'],
['display_text' => 'Others', 'value' => 'fill_po_issue_others', 'next_question_number' => 'fill_po_issue_others'],
],
'group' => 'fill_po',
'is_admin_filter' => true
],
[
'question_number' => 'fill_po_edit_filled',
'question_title' => 'PO Filled',
'question_type' => QAType::MULTIPLE_CHOICES,
'is_start' => false,
'is_end' => true,
'answer_options' => [
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
],
'group' => 'fill_po',
],
[
'question_number' => 'stop_working',
'question_title' => 'Thank you. Reload page to restart.',
'question_type' => QAType::DEFAULT,
'is_start' => false,
'is_end' => true,
'group' => '',
],
]
],
];
foreach ($questionnaireSets as $set) {
$questionnaireSet = QAQuestionnaireSet::create([
'name' => $set['name'],
'description' => $set['description'],
'group' => $set['group'],
]);
foreach ($set['questions'] as $key => $questionData) {
$question = new QAQuestions;
$question->question_number = $questionData['question_number'];
$question->question_title = $questionData['question_title'];
if (isset($questionData['question_description'])) {
$question->question_description = $questionData['question_description'];
}
$question->question_type = $questionData['question_type'];
$question->questionnaire_set_id = $questionnaireSet->id;
if (isset($questionData['group']) && $questionData['group'] != '') {
$question->group = $questionData['group'];
}
if (isset($questionData['next_nested_question'])) {
$question->next_nested_question = $questionData['next_nested_question'];
}
if (isset($questionData['next_main_question'])) {
$question->next_main_question = $questionData['next_main_question'];
}
$question->is_start = $questionData['is_start'];
$question->is_end = $questionData['is_end'];
if (isset($questionData['end_text'])) {
$question->end_text = $questionData['end_text'];
}
if (isset($questionData['url'])) {
$question->url = $questionData['url'];
}
if (isset($questionData['is_admin_filter'])) {
$question->is_admin_filter = $questionData['is_admin_filter'];
}
$question->order = $key + 1;
$question->save();
if (isset($questionData['answer_options'])) {
foreach ($questionData['answer_options'] as $optionData) {
$answerOption = new QAAnswerOptions;
$answerOption->display_text = $optionData['display_text'];
$answerOption->value = $optionData['value'];
$answerOption->question_number = $questionData['question_number'];
$answerOption->next_question_number = $optionData['next_question_number'] ?? null;
$answerOption->questionnaire_set_id = $questionnaireSet->id;
$answerOption->save();
}
}
}
}
}
}
@@ -0,0 +1,202 @@
<template>
<div class="row parentContainer m-b-10">
<div class="col p-l-0">
<div class="row m-l-15 m-r-5 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="col-12">
<div class="row m-b-10">
<div class="col-2">{{ item.id }} <br> {{ item.created_at_with_time }} <br> {{ item.source_email }}</div>
<div class="col-1">{{ item.questionnaire.version }} <br> {{ item.questionnaire.description }}</div>
<div class="col-2">{{ item.question_title }} <br> Time(s): {{ item.time }} </div>
<div class="col-1">{{ parsedAnswer === 'go_back' ? '' : item.answer }}</div>
<div class="col-2">{{ parsedAnswer }}</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;" v-if="item.documentsList != null && item.documentsList.length > 1" >
<div
v-for="document in item.documentsList"
:key="document.id"
style="display: flex; flex-wrap: nowrap; gap: 5px;"
>
<div
v-for="file in document.files"
:key="file.id"
class="col-auto no-padding"
>
<document-file-viewer-component :file="file">
<template v-slot:button>
<button
class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border"
>
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;" v-else>
<div v-if="item.documents != null" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div
v-for="file in item.documents.files"
v-bind:key="file.id"
class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div>
<div class="col-2">
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).marking">Reference: <a :href="route('booking.details', JSON.parse(item.question_metadata).marking)" target="_blank">{{ JSON.parse(item.question_metadata).marking }}
</a></span>
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).company">Marking: {{ JSON.parse(item.question_metadata).company.reference }} </span>
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).payment_history">Currency Rate: {{ JSON.parse(item.question_metadata).payment_history[0].currency_rate }} </span>
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).payment_history">Total Amount: {{ JSON.parse(item.question_metadata).payment_history[0].currency.short_code}} {{((Math.round(( JSON.parse(item.question_metadata).payment_history[0].amount + Number.EPSILON) * 100) / 100)).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</span>
</div>
<div class="col parentContainer position-static">
<div class="row align-items-center justify-content-end">
<!-- <div class="col-auto position-static no-padding requestModal" data-type="deleteBillGroup">
<div class="btn bg-grey no-border">
<i class="fa fa-times text-danger"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="deleteBillGroup">
<general-confirmation-form-component
contentText="Are you sure you want to delete this Bill Group?"
modalType="delete"
class="text-center"
:apiRoute="route('api.transaction.group.bill.delete', item.id)"
apiMethod="delete"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div> -->
<div class="col-auto no-padding" v-show="showExpandedIcon">
<div class="btn btn-sm btn-default b-rad-none no-border" @click="expand()">
<i class="fa fa-download" :class="[{'fa-angle-up': expanded}, {'fa-angle-down': !expanded}]"></i>
</div>
</div>
<!-- <div class="col-auto no-padding" v-if="loading && item.question_groups.includes(item.answer_value)"> -->
<div class="col-auto no-padding" v-if="loading">
<div class="btn btn-sm btn-default b-rad-none no-border">
<span class="spinner"></span>
</div>
</div>
</div>
</div>
</div>
<!-- <div class="row m-b-10" v-show="expanded" v-if="item.question_groups.includes(item.answer_value)"> -->
<div class="row m-b-10" v-show="expanded">
<div class="col">
<div class="row">
<div class="col">
<div class="row align-items-center m-l-10 m-r-10 m-t-10 p-t-10 p-b-10 b-t b-grey muted all-caps fs-10">
<div class="col-1">Id</div>
<div class="col-2">Question Text</div>
<div class="col-1">Answer Text</div>
<div class="col-2">Answer Value</div>
<div class="col-1">Documents Uploaded</div>
<div class="col-1">Time(seconds)</div>
<div class="col-1">Created DateTime</div>
<div class="col-1">Order/Booking/Transfer</div>
</div>
</div>
</div>
<list-component style="min-height: auto;" :section="section" :endpoint="route('api.questionnaires.qa.list')" :options="options">
<template slot="list" slot-scope="{data}">
<question-answer-inner-component :data="data"></question-answer-inner-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
data(){
return {
section: 'qaListQuestions-' + this.data.reference + '-' + this.data.id,
expanded: false,
options: {
'order_by': {column: 'created_at', DESC: false},
'per_page': 20,
},
showExpandedIcon: false,
loading: true
}
},
watch: {
pendingQueue(inComplete){
if(!inComplete){
this.loading = false;
}
},
expandedList(newValue){
if (Array.isArray(newValue)) {
if(newValue.length > 0){
this.showExpandedIcon = true;
}
}
}
},
computed: {
expandedList () {
return this.$store.getters.getListData(this.section);
},
pendingQueue() {
return this.$store.getters.isInCompleteQueue(this.section);
},
parsedAnswer() {
try {
const parsed = this.item && this.item.answer_value ? JSON.parse(this.item.answer_value) : null;
return parsed && parsed.text ? parsed.text : this.item.answer_value;
} catch (error) {
return this.item.answer_value;
}
},
},
created(){
this.options['reference'] = this.item.reference;
this.options['id_after'] = [this.item.id, this.item.reference, this.item.question_groups];
},
methods: {
expand(){
this.expanded = !this.expanded;
},
},
mixins: [componentHandler]
}
</script>
<style scoped>
.spinner {
border: 2px solid rgba(0, 0, 0, 0.1);
border-left-color: #000;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
</style>
@@ -0,0 +1,146 @@
<template>
<div class="row parentContainer m-b-10">
<div class="col p-l-0">
<div class="row m-l-15 m-r-5 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="col-12">
<div class="row m-b-10">
<div class="col-1">{{ item.id }}</div>
<div class="col-1">{{ item.questionnaire.version }} - {{ item.questionnaire.description }}</div>
<div class="col-2">{{ item.question_title }}</div>
<div class="col-1">{{ item.answer }}</div>
<div class="col-1">{{ item.answer_value }}</div>
<div class="col-2">{{ item.source_email }}</div>
<div class="col-1">{{ item.time }}</div>
<div class="col-1">{{ item.created_at_with_time }}</div>
<div class="col parentContainer position-static">
<div class="row align-items-center justify-content-end">
<!-- <div class="col-auto position-static no-padding requestModal" data-type="deleteBillGroup">
<div class="btn bg-grey no-border">
<i class="fa fa-times text-danger"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="deleteBillGroup">
<general-confirmation-form-component
contentText="Are you sure you want to delete this Bill Group?"
modalType="delete"
class="text-center"
:apiRoute="route('api.transaction.group.bill.delete', item.id)"
apiMethod="delete"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div> -->
<div class="col-auto no-padding" v-show="showExpandedIcon">
<div class="btn btn-sm btn-default b-rad-none no-border" @click="expand()">
<i class="fa fa-download" :class="[{'fa-angle-up': expanded}, {'fa-angle-down': !expanded}]"></i>
</div>
</div>
<div class="col-auto no-padding" v-if="loading">
<div class="btn btn-sm btn-default b-rad-none no-border">
<span class="spinner"></span>
</div>
</div>
</div>
</div>
</div>
<div class="row m-b-10" v-show="expanded">
<div class="col">
<div class="row">
<div class="col">
<div class="row align-items-center m-l-10 m-r-10 m-t-10 p-t-10 p-b-10 b-t b-grey muted all-caps fs-10">
<div class="col-1">Id</div>
<div class="col-2">Question Text</div>
<div class="col-1">Answer Text</div>
<div class="col-2">Answer Value</div>
<div class="col-1">Documents Uploaded</div>
<div class="col-1">Time(seconds)</div>
<div class="col-1">Created DateTime</div>
<div class="col-2">Order/Booking/Transfer</div>
</div>
</div>
</div>
<list-component style="min-height: auto;" :section="section" :endpoint="route('api.questionnaires.qa.list')" :options="options">
<template slot="list" slot-scope="{data}">
<question-answer-inner-component :data="data"></question-answer-inner-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
data(){
return {
section: 'qaListQuestionGroups-' + this.data.reference + '-' + this.data.id,
expanded: false,
options: {
'order_by': {column: 'created_at', DESC: false},
'per_page': 20,
},
showExpandedIcon: false,
loading: true
}
},
watch: {
pendingQueue(inComplete){
if(!inComplete){
this.loading = false;
}
},
expandedList(newValue){
if (Array.isArray(newValue)) {
if(newValue.length > 0){
this.showExpandedIcon = true;
}
}
}
},
computed: {
expandedList () {
return this.$store.getters.getListData(this.section);
},
pendingQueue() {
return this.$store.getters.isInCompleteQueue(this.section);
}
},
created(){
this.options['reference'] = this.item.reference;
this.options['id_after'] = [this.item.id, this.item.reference, this.item.question_groups];
},
methods: {
expand(){
this.expanded = !this.expanded;
},
},
mixins: [componentHandler]
}
</script>
<style scoped>
.spinner {
border: 2px solid rgba(0, 0, 0, 0.1);
border-left-color: #000;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
</style>
@@ -0,0 +1,86 @@
<template>
<div class="row parentContainer m-b-10">
<div class="col">
<div class="row m-l-0 m-r-0 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="col-12">
<div class="row m-b-10">
<div class="col-1">{{ item.id }}</div>
<div class="col-2">{{ item.question_title }}</div>
<div class="col-1">{{ parsedAnswer === 'go_back' ? '' : item.answer }}</div>
<div class="col-2">{{ parsedAnswer }}</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;" v-if="item.documentsList != null && item.documentsList.length > 1" >
<div
v-for="document in item.documentsList"
:key="document.id"
style="display: flex; flex-wrap: nowrap; gap: 5px;"
>
<div
v-for="file in document.files"
:key="file.id"
class="col-auto no-padding"
>
<document-file-viewer-component :file="file">
<template v-slot:button>
<button
class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border"
>
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;" v-else>
<div v-if="item.documents != null" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div
v-for="file in item.documents.files"
v-bind:key="file.id"
class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div>
<div class="col-1">{{ item.time }}</div>
<div class="col-1">{{ item.created_at_with_time }}</div>
<div class="col-2">
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).marking">Reference: <a :href="route('booking.details', JSON.parse(item.question_metadata).marking)" target="_blank">
{{ JSON.parse(item.question_metadata).marking }}
</a></span>
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).company">Marking: {{ JSON.parse(item.question_metadata).company.reference }} </span>
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).payment_history">Currency Rate: {{ JSON.parse(item.question_metadata).payment_history[0].currency_rate }} </span>
<span class="bold d-inline-block" v-if="JSON.parse(item.question_metadata) && JSON.parse(item.question_metadata).payment_history">Total Amount: {{ JSON.parse(item.question_metadata).payment_history[0].currency.short_code}} {{((Math.round(( JSON.parse(item.question_metadata).payment_history[0].amount + Number.EPSILON) * 100) / 100)).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
computed: {
parsedAnswer() {
try {
const parsed = this.item && this.item.answer_value ? JSON.parse(this.item.answer_value) : null;
return parsed && parsed.text ? parsed.text : this.item.answer_value;
} catch (error) {
// console.error("Error parsing JSON:", error);
return this.item.answer_value;
}
},
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,194 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="row">
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterQuestionnaireSet">
<label>Questionnaire Set Version</label>
<selectable-component :endpoint="route('api.questionnaires.list')" :section="section + 'QuestionnaireSet'" valueColumn="id" :labelColumn="['name', 'version']" v-model="filterQuestionnaireSet"></selectable-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterQuestionGroup">
<label>Question Groups</label>
<select-component :options="questionGroups" v-model="filterQuestionGroup"></select-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterQuestion">
<label class="all-caps">Question</label>
<!-- <select-component :options="['1688_login_issue', '1688_login_issue_others', '1688_proceed_order_issue', '1688_proceed_order_issue_others', '1688_login_successful_cannot_verify', '1688_login_successful_cannot_verify_others', 'approve_po_reject', 'approve_po_reject_others', 'approve_po_edit_po_issue', 'approve_po_edit_po_issue_others', 'fill_po_edit_issue', 'fill_po_issue_others']" v-model="filterQuestion"></select-component> -->
<selectable-component :endpoint="route('api.questionnaires.q.list', 0) + '?filters=' + JSON.stringify({'is_admin_filter': true, order_by: {column: 'question_number', DESC: false}})" :section="section + 'QuestionsFiltering'" valueColumn="question_number" :labelColumn="['question_number']" v-model="filterQuestion"></selectable-component>
</validation-wrapper-component>
</div>
</div>
<div class="row m-t-10">
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterStartDate">
<label class="all-caps">Start Date</label>
<date-picker-component v-model.lazy="filterStartDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterEndDate">
<label class="all-caps">End Date</label>
<date-picker-component v-model.lazy="filterEndDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterAnswerText">
<label class="all-caps">Answer Text</label>
<input type="text" class="form-control" v-model.lazy="filterAnswerText">
</validation-wrapper-component>
</div>
</div>
<div class="row m-t-10">
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterUserId">
<label>Users</label>
<selectable-component :endpoint="route('api.account.user.list') + '?filters=' + JSON.stringify(userListOptions)" :section="section + 'UserList'" valueColumn="id" :labelColumn="['name', 'email']" v-model="filterUserId"></selectable-component>
</validation-wrapper-component>
</div>
</div>
<loading-component style="height: 200px; top: 0;" color="success" v-show="questionGroups.length === 0"></loading-component>
<div class="row justify-content-end m-t-10" v-show="!$store.getters.isLoading(section + 'ListQuestions')">
<div class="col-auto p-l-0">
<open-link-in-new-tab-component custom-class="btn btn-sm b-rad-none fs-14" :url="route('api.questionnaires.qa.export.without.groups') + '?filters=' + JSON.stringify(this.options)" :is-url-protected="true" :onClick="exportAction">
<i class="fa fa-download lh-40"></i> Download
</open-link-in-new-tab-component>
</div>
<div class="col-auto p-l-0">
<div class="btn b-rad-none" @click="resetSarch">
<i class="fa fa-refresh lh-40"></i> Clear
</div>
</div>
<div class="col-auto p-l-0">
<div class="btn btn-primary b-rad-none" @click="onSelected">
<i class="fa fa-search lh-40"></i> Search
</div>
</div>
</div>
<div class="row align-items-center m-t-10 p-t-10 p-b-10 b-t b-grey muted all-caps fs-10">
<div class="col-2">Id</div>
<div class="col-1">Version/Question Set</div>
<div class="col-2">Question Text / Time(seconds)</div>
<div class="col-1">Answer Text</div>
<div class="col-2">Answer Value</div>
<div class="col-1">Documents Uploaded</div>
<div class="col-2">Order/Booking/Transfer</div>
</div>
</div>
<list-component style="min-height: 600px;" :key="currentKey" :section="section + 'ListQuestions'" :endpoint="route('api.questionnaires.qa.list')" :options="options">
<template slot="list" slot-scope="{data}">
<question-answer-2-component :key="data.id" :data="data"></question-answer-2-component>
</template>
</list-component>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
props: {
section:{
type: String,
required: true
},
},
data(){
return {
filterUserId: "",
filterQuestion: "",
filterQuestionGroup: "",
filterQuestionnaireSet: "",
filterStartDate: "",
filterEndDate: "",
filterAnswerText: "",
userListOptions: {
'type_in': this.$store.getters.isSuperAdmin ? [1, 2] : [2],
},
options: {
'per_page': 10,
'order_by': {column: 'created_at', DESC: true},
},
currentKey: 1,
questionGroups: [],
}
},
created(){
this.fetchQuestionnaireSets();
},
validations: {
filterUserId: {},
filterQuestion: {},
filterQuestionGroup: {},
filterQuestionnaireSet: {},
filterStartDate: {},
filterEndDate: {},
filterAnswerText: {},
},
methods: {
fetchQuestionnaireSets(){
this.submit(route('api.questionnaires.list') +'?filters=' + JSON.stringify({ 'per_page': 10, order_by: {column: 'id', DESC: true} }), 'get', this.section, false, false)
},
successHandler(response){
this.questionGroups = response.payload.data[0].group;
// this.options['question_group_in'] = this.questionGroups;
},
onSelected(userId){
this.updateOptions();
this.currentKey+=1;
},
updateOptions(){
// if(this.filterUserId){
// this.options['user_id'] = this.filterUserId;
// }
if(this.filterQuestion){
this.options['question_in'] = [this.filterQuestion];
}
if(this.filterQuestionGroup){
this.options['question_group_in'] = [this.filterQuestionGroup];
}
if(this.filterQuestionnaireSet){
this.options['has_questionnaire'] = this.filterQuestionnaireSet;
}
if(this.filterStartDate){
this.options['start_date'] = this.filterStartDate;
}
if(this.filterEndDate){
this.options['end_date'] = this.filterEndDate;
}
if(this.filterAnswerText || this.filterUserId){
this.options['answer_like_with_user_id'] = [this.filterAnswerText, this.filterUserId];
}
},
exportAction(){
this.updateOptions();
return route('api.questionnaires.qa.export.without.groups') + '?filters=' + JSON.stringify(this.options);
},
resetSarch() {
this.filterUserId = "",
this.filterQuestion = "",
this.filterQuestionGroup = "",
this.filterQuestionnaireSet = "",
this.filterStartDate = "",
this.filterEndDate = "",
this.filterAnswerText = "",
// delete this.options['user_id'];
delete this.options['question_in'];
delete this.options['has_questionnaire'];
delete this.options['start_date'];
delete this.options['end_date'];
delete this.options['answer_like_with_user_id'];
delete this.options['question_group_in'];
// this.options['question_group_in'] = this.questionGroups;
this.currentKey+=1;
},
},
}
</script>
@@ -0,0 +1,167 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="row">
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterQuestionnaireSet">
<label>Questionnaire Set Version</label>
<selectable-component :endpoint="route('api.questionnaires.list')" :section="section + 'QuestionnaireSet'" valueColumn="id" :labelColumn="['name', 'version']" v-model="filterQuestionnaireSet"></selectable-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterQuestionGroup">
<label>Question Groups</label>
<select-component :options="questionGroups" v-model="filterQuestionGroup"></select-component>
<!-- <selectable-hard-coded-component section="questionGroupSelectable" :selectable-options="questionGroups" v-model="filterQuestionGroup"></selectable-hard-coded-component > -->
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterUserId">
<label>Users</label>
<selectable-component :endpoint="route('api.account.user.list') + '?filters=' + JSON.stringify(userListOptions)" :section="section + 'UserList'" valueColumn="id" :labelColumn="['name', 'email']" v-model="filterUserId"></selectable-component>
</validation-wrapper-component>
</div>
</div>
<div class="row m-t-10">
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterStartDate">
<label class="all-caps">Start Date</label>
<date-picker-component v-model.lazy="filterStartDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterEndDate">
<label class="all-caps">End Date</label>
<date-picker-component v-model.lazy="filterEndDate"></date-picker-component>
</validation-wrapper-component>
</div>
</div>
<loading-component style="height: 200px; top: 0;" color="success" v-show="questionGroups.length === 0"></loading-component>
<div class="row justify-content-end m-t-10" v-show="questionGroups.length > 0 && !$store.getters.isLoading(section + 'ListQuestionGroups')">
<div class="col-auto p-l-0">
<open-link-in-new-tab-component custom-class="btn btn-sm b-rad-none fs-14" :url="route('api.questionnaires.qa.export.with.groups') + '?filters=' + JSON.stringify(this.options)" :is-url-protected="true" :onClick="exportAction">
<i class="fa fa-download lh-40"></i> Download
</open-link-in-new-tab-component>
</div>
<div class="col-auto p-l-0">
<div class="btn b-rad-none" @click="resetSarch">
<i class="fa fa-refresh lh-40"></i> Clear
</div>
</div>
<div class="col-auto p-l-0">
<div class="btn btn-primary b-rad-none" @click="onSelected">
<i class="fa fa-search lh-40"></i> Search
</div>
</div>
</div>
<div class="row align-items-center m-t-10 p-t-10 p-b-10 b-t b-grey muted all-caps fs-10">
<div class="col-1">Id</div>
<div class="col-1">Version/Question Set</div>
<div class="col-2">Question Text</div>
<div class="col-1">Answer Text</div>
<div class="col-1">Answer Value</div>
<div class="col-2">Source Email</div>
<div class="col-1">Time(seconds)</div>
<div class="col-1">Created DateTime</div>
</div>
</div>
<list-component style="min-height: 600px;" :key="currentKey" :section="section + 'ListQuestionGroups'" :endpoint="route('api.questionnaires.qa.list')" :options="options" v-if="questionGroups.length > 0">
<template slot="list" slot-scope="{data}">
<question-answer-component :key="data.id" :data="data"></question-answer-component>
</template>
</list-component>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
props: {
section:{
type: String,
required: true
},
},
data(){
return {
filterUserId: "",
filterQuestionGroup: "",
filterQuestionnaireSet: "",
filterStartDate: "",
filterEndDate: "",
userListOptions: {
'type_in': this.$store.getters.isSuperAdmin ? [1, 2] : [2],
},
options: {
'per_page': 10,
'order_by': {column: 'created_at', DESC: true},
'with_answers': [],
},
currentKey: 1,
questionGroups: [],
}
},
created(){
this.fetchQuestionnaireSets();
},
validations: {
filterUserId: {},
filterQuestionGroup: {},
filterQuestionnaireSet: {},
filterStartDate: {},
filterEndDate: {},
},
methods: {
fetchQuestionnaireSets(){
this.submit(route('api.questionnaires.list') +'?filters=' + JSON.stringify({ 'per_page': 10, order_by: {column: 'id', DESC: true} }), 'get', this.section, false, false)
},
successHandler(response){
this.questionGroups = response.payload.data[0].group;
this.options.with_answers = this.questionGroups;
},
onSelected(userId){
this.updateOptions();
this.currentKey+=1;
},
updateOptions(){
if(this.filterUserId){
this.options['user_id'] = this.filterUserId;
}
if(this.filterQuestionGroup){
this.options['with_answers'] = [this.filterQuestionGroup];
}
if(this.filterQuestionnaireSet){
this.options['has_questionnaire'] = this.filterQuestionnaireSet;
}
if(this.filterStartDate){
this.options['start_date'] = this.filterStartDate;
}
if(this.filterEndDate){
this.options['end_date'] = this.filterEndDate;
}
},
exportAction(){
this.updateOptions();
return route('api.questionnaires.qa.export.with.groups') + '?filters=' + JSON.stringify(this.options);
},
resetSarch() {
this.filterUserId = "",
this.filterQuestionGroup = "",
this.filterQuestionnaireSet = "",
this.filterStartDate = "",
this.filterEndDate = "",
this.options['with_answers'] = this.questionGroups;
delete this.options['user_id'];
delete this.options['has_questionnaire'];
delete this.options['start_date'];
delete this.options['end_date'];
this.currentKey+=1;
},
},
}
</script>
@@ -283,7 +283,9 @@
this.parameters = {
products: this.products
};
this.$emit('update-parameters', {
parameters: this.parameters,
});
this.submit(route('api.transaction.po.create', this.data.id), 'post', this.section, true, true);
},
uploadProducts() {
@@ -291,13 +293,18 @@
this.parameters = {
files: this.files
};
this.$emit('update-parameters', {
parameters: this.parameters,
});
this.submit(route('api.transaction.po.import', this.data.id), 'post', this.section, true, true);
},
successHandler(){
if((Math.round((this.poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3) === (Math.round((this.data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)){
this.submitted = true;
}
this.$emit('amount-tally-poTotal', {
isDone: this.submitted,
});
this.updateList()
},
addProduct(){
@@ -0,0 +1,86 @@
<template>
<div class="row">
<div class="col">
<div class="row no-margin">
<div class="col p-b-15 p-l-0 p-r-0">
<div class="row">
<div class="col">
<div class="row" v-for="(product, index) in products" :key="product.id">
<div class="col p-b-10 p-t-10 " :class="[{'b-grey' : index !== Object.keys(products).length - 1}, {'b-b' : index !== Object.keys(products).length - 1}]">
<purchase-order-item-form-component :data="product" :index="index" :currency="data.fixed_currency.short_code" :editable="false" :section="section"></purchase-order-item-form-component>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12" v-if="data.company.address">
<div class="row justify-content-center align-items-center text-center">
<h6 class="font-heading all-caps bold">Total: </h6>
<h6><span v-if="!submitted" class="bold m-r-5" :class="[{'text-danger' : (Math.round((poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3) !== (Math.round((data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}, {'text-success' : (Math.round((this.poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3) === (Math.round((this.data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}]">{{(Math.round(( poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3)}}/</span><span class="text-primary bold m-l-5">{{(Math.round((data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{data.fixed_currency.short_code}}</span></h6>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import formHandler from '../../../general/mixins/formHandler';
import { required, requiredIf } from "vuelidate/lib/validators";
export default {
props:{
companySegmentIds: {
type: Array,
required: true
}
},
data(){
return {
interval:false,
submitted: false,
useUploadCsvPo: false,
product: {
stockCode: '',
description: '',
quantity: 1,
unit_price: 0,
},
products: [],
files: [],
uploadFiles: false,
}
},
validations: {
files: {
required: requiredIf(function () { return this.uploadFiles })
}
},
created() {
this.products = this.data.purchase_order ? this.data.purchase_order.details : [];
this.submitted = this.data.purchase_order ? this.data.purchase_order.status === 1 || this.data.purchase_order.status === 2: false;
},
computed: {
productTotal(){
return this.product.quantity * parseFloat((this.product.unit_price).toString().replaceAll(',', ''));
},
poTotal(){
return this.products.reduce(function(last, product) {
return last + product.total;
}, 0);
}
},
watch: {
'data': function () {
if (this.data && this.data.purchase_order && this.data.purchase_order.details) {
this.products = this.data.purchase_order.details;
this.submitted = this.data.purchase_order ? this.data.purchase_order.status === 1 || this.data.purchase_order.status === 2: false;
} else {
this.products = [];
}
}
},
mixins: [formHandler]
}
</script>
@@ -35,7 +35,7 @@
<div class="row align-items-center">
<div class="col-auto">
<p class="m-b-0 small muted">Unit Price</p>
<p class="m-b-0 bold">{{product.unit_price}}</p>
<p class="m-b-0 bold">{{product.unit_price.toFixed(3)}}</p>
</div>
<div class="col-auto text-center">
<p class="m-b-0 small muted">Quantity</p>
@@ -144,7 +144,7 @@
},
created() {
this.product = this.data;
this.product.unit_price = (Math.round((this.product.unit_price+ Number.EPSILON) * 1000) / 1000).toFixed(3)
this.product.unit_price = (Math.round((this.product.unit_price+ Number.EPSILON) * 1000) / 1000);
},
computed: {
productTotal(){
@@ -14,8 +14,7 @@
<div class="col">
<validation-wrapper-component selectable :validator="$v.voucherCode">
<label>Voucher Options</label>
<!-- <selectable-hard-coded-component section="rewardisActiveOptions" :selectable-options="options" v-model="voucherCode"></selectable-hard-coded-component > -->
<selectable-component :endpoint="route('api.voucher.campaigns.list') + '?filters=' + JSON.stringify({'is_display': 1})" :section="section" valueColumn="name" :labelColumn="['name', 'description']" v-model="voucherCode"></selectable-component>
<select-component :options="options" v-model="voucherCode"></select-component>
</validation-wrapper-component>
</div>
</div>
@@ -33,6 +33,10 @@
type: Boolean,
default: true
},
onClick: {
type: Function,
default: null
}
},
data() {
return {
@@ -75,6 +79,9 @@
});
},
handleClick(){
if(this.onClick){
this.url = this.onClick();
}
this.isDownloading = true;
if(window.LARAVEL_VAPOR_ENABLED){
this.submit(this.url, 'get', this.section, false, false);
@@ -0,0 +1,760 @@
<template>
<div class="row justify-content-center align-items-center text-center" style="min-height: 80vh;">
<div class="col">
<h1 class="m-b-50" :class="{ 'text-success': timer }">{{ formattedTime }}</h1>
<loading-component style="height: 50px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row" v-if="question" v-show="!isLoading && !noMoreWork">
<div class="col">
<div class="row m-b-5 animate__animated animate__fadeInUpBig animate__fast" v-if="error">
<div class="col">
<small class="bold fs-10 text-danger">{{error}}</small>
</div>
</div>
<!-- MAIN QUESTION AND INFO PART 1 -->
<div class="row m-b-20">
<div class="col">
<h3>{{questionTitle}}</h3>
<h4>{{question.question_description}}</h4>
<div v-if="question.question_number === '1688'">
<h1>Login Information</h1>
<div v-if="externalApiResponse"
class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
<div class="col-auto text-left">
<table>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">ORDER Marking: </span></h3>
</td>
<td>
<h3><a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">{{ externalApiResponse.data.marking
}}</a></h3>
</td>
</tr>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN ID/EMAIL/PHONE:
</span></h3>
</td>
<td>
<h3>{{ externalApiResponse.data.bank.account_no }}</h3>
</td>
</tr>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN PASSWORD:
</span></h3>
</td>
<td>
<h3>{{ externalApiResponse.data.bank.holder_name }}</h3>
</td>
</tr>
<tr>
<td>
<h3><span class="bold d-inline-block m-r-15">ALIPAY 6-DIGIT PAYMENT PIN:
</span></h3>
</td>
<td>
<h3>{{ externalApiResponse.data.bank.bank_branch }}</h3>
</td>
</tr>
</table>
</div>
</div>
</div>
<div v-if="question.question_number === '1688_login_successful'">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a v-if="externalApiResponse" :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">
{{ externalApiResponse.data.marking }}
</a>
</h3>
<div v-if="externalApiResponse" class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
<div class="col-auto text-left">
<h4>
<span class="bold d-inline-block m-r-15">Reference: {{ externalApiResponse.data.marking }}</span>
</h4>
<h4>
<span class="bold d-inline-block m-r-15">Marking: {{ externalApiResponse.data.company.reference }} </span>
</h4>
<h4>
<span class="bold d-inline-block m-r-15">Currency Rate: {{ externalApiResponse.data.payment_history[0].currency_rate }} </span>
</h4>
<h4>
<span class="bold d-inline-block m-r-15">Total Amount: {{externalApiResponse.data.payment_history[0].currency.short_code}} {{((Math.round(( externalApiResponse.data.payment_history[0].amount + Number.EPSILON) * 100) / 100)).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</span>
</h4>
</div>
</div>
<!-- <div v-if="externalApiResponse.data.booking_attributes && externalApiResponse.data.booking_attributes.length > 0" class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
<div class="col-auto text-left">
<h4>
<span class="bold d-inline-block m-r-15">Additional Info: </span>
</h4>
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
:key="index">
<h2>
{{ index + 1 + '. #' + attribute.value }}
</h2>
</div>
</div>
</div>
<div v-else class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
<div class="col-auto text-left">
<h4>
<span class="bold d-inline-block m-r-15">Additional Info: -</span>
</h4>
</div>
</div> -->
</div>
<div v-if="question.question_number === '1688_order_verify'">
<h3 v-if="externalApiResponse">
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">
{{ externalApiResponse.data.marking }}
</a>
</h3>
</div>
<div v-if="question.question_number === '1688_order_verification'">
<h6 class="font-heading all-caps bold">Total: </h6>
<h6>
<span v-if="question.answer && question.question_number === '1688_order_verification'" class="bold m-r-5"
:class="[{'text-danger' : (Math.round((parseFloat(externalApiResponse.data.amount_processed) + Number.EPSILON) * 1000) / 1000).toFixed(3) !== (Math.round((externalApiResponse.data.paid_amount + Number.EPSILON) * 1000) / 1000).toFixed(3)},
{'text-success' : (Math.round((parseFloat(externalApiResponse.data.amount_processed) + Number.EPSILON) * 1000) / 1000).toFixed(3) === (Math.round((externalApiResponse.data.paid_amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}]">{{(Math.round(( parseFloat(externalApiResponse.data.amount_processed) + Number.EPSILON) * 1000) / 1000).toFixed(3)}}/</span>
<span class="text-primary bold m-l-5">{{(Math.round((externalApiResponse.data.paid_amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{externalApiResponse.data.fixed_currency.short_code}}</span>
</h6>
</div>
<div v-if="question.question_number === '1688_underpaid_order_1'">
<h6 class="font-heading all-caps bold">Amount to be deducted from wallet: </h6>
<h6>
<span class="text-primary bold m-l-5">{{(Math.round((externalApiResponse.data.amount_to_be_deducted + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{externalApiResponse.data.fixed_currency.short_code}}</span>
</h6>
</div>
<div v-if="question.question_number === 'approve_po'">
<div v-if="externalApiResponse && externalApiResponse.data" class="row">
<div class="col">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">{{ externalApiResponse.data.marking }}</a>
</h3>
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<purchase-order-form-read-only-component :data="externalApiResponse.data" :companySegmentIds="companySegmentIds" :section="section"></purchase-order-form-read-only-component>
</div>
</div>
</div>
</div>
</div>
<div v-if="question.question_number === 'fill_po'">
<div v-if="externalApiResponse && externalApiResponse.data" class="row">
<div class="col">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">{{ externalApiResponse.data.marking }}</a>
</h3>
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<purchase-order-form-read-only-component :data="externalApiResponse.data" :companySegmentIds="companySegmentIds" :section="section"></purchase-order-form-read-only-component>
</div>
</div>
</div>
</div>
</div>
<div v-if="question.question_number === 'approve_po_edit' || question.question_number === 'fill_po_edit'">
<div v-if="externalApiResponse && externalApiResponse.data" class="row">
<div class="col">
<h3>
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
<a :href="route('booking.details', externalApiResponse.data.marking)"
target="_blank">{{ externalApiResponse.data.marking }}</a>
</h3>
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<purchase-order-form-component :data="externalApiResponse.data" :companySegmentIds="companySegmentIds" :section="section + 'GetExternalApiResponse'"
@amount-tally-poTotal="handleAnswerExtra"></purchase-order-form-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- QUESTION TYPE -->
<div class="row m-b-10" v-if="question.question_type === 9">
<div class="col">
<div class="row justify-content-center">
<div class="col-auto text-left">
<div class="form-group form-group-default m-b-0">
<label>Amount Processed <span v-if="externalApiResponse">{{externalApiResponse.data.fixed_currency.short_code}}</span></label>
<input id="cc_get" name="cc_get" type="text" placeholder="0.00" value="0" class="form-control" v-model="answer" v-money="{decimal: '.',thousands: ',', precision: 2}">
</div>
</div>
</div>
</div>
</div>
<div class="row m-b-10" v-if="question.question_type === 8">
<div class="col">
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<h2>
Upload the English PO
</h2>
<!-- <file-upload-component :data="externalApiResponse" section="section"></file-upload-component> -->
<file-input-component :validator="$v.filesA" v-model="filesA">
<template slot="label">
<div class="font-heading fs-11 all-caps">Photo or File</div>
</template>
</file-input-component>
</div>
<div class="col">
<h2>
Upload the China PO
</h2>
<file-input-component :validator="$v.filesB" v-model="filesB">
<template slot="label">
<div class="font-heading fs-11 all-caps">Photo or File</div>
</template>
</file-input-component>
</div>
<div class="col">
<h2>
Upload Bank Slip
</h2>
<file-input-component :validator="$v.filesC" v-model="filesC">
<template slot="label">
<div class="font-heading fs-11 all-caps">Photo or File</div>
</template>
</file-input-component>
</div>
</div>
</div>
</div>
<div class="row m-b-10" v-else-if="question.question_type === 7">
<div class="col">
<div class="row b-a b-primary padding-30 bg-white m-t-25">
<div class="col">
<div class="m-t-25">
<!-- <remark-comment-form-component :data="externalApiResponse" :id="externalApiResponse.data.booking_id" :section="section" module_type="Booking"></remark-comment-form-component> -->
<validation-wrapper-component :validator="$v.answer">
<label>Answer</label>
<input type="text" class="form-control" v-model="answer" @input="formTouched = true">
</validation-wrapper-component>
</div>
<div class="m-t-25">
<!-- <file-upload-component v-model="files" :value="value" v-on:input="$emit('input', $event)"></file-upload-component> -->
<file-input-component :validator="$v.files" v-model="files">
<template slot="label">
<div class="font-heading fs-11 all-caps">Photo or File</div>
</template>
</file-input-component>
</div>
</div>
</div>
</div>
</div>
<div class="row m-b-10" v-else-if="question.question_type === 4" >
<div class="col" v-for="ans in question.question_answers">
<div v-html="ans.display_text"></div>
</div>
</div>
<div class="row m-b-10" v-else-if="question.question_type === 3">
<div class="col">
<file-input-component :validator="$v.files" v-model="files">
<template slot="label">
<div class="font-heading fs-11 all-caps">Photo or File</div>
</template>
</file-input-component>
</div>
</div>
<div class="row m-b-10" v-else-if="question.question_type === 2">
<div class="col" >
<validation-wrapper-component :validator="$v.answer">
<label>Answer</label>
<input type="text" class="form-control" v-model="answer" @input="formTouched = true">
</validation-wrapper-component>
</div>
</div>
<div class="row m-b-10" v-else-if="question.question_type === 1" >
<div class="col question.answers">
<div class="w-100 d-block text-center">
<validation-wrapper-component :validator="$v.answer" class="col">
<button v-for="(ans, index) in question.question_answers"
:class="{
'btn-primary': question.is_previous && question.answer && question.answer.answer_option_id === ans.id && question.answer.answer !== 'fill_po_edit_filled,not_tally',
'btn-danger': question.is_previous && question.answer && question.answer.answer_option_id === ans.id && question.answer.answer === 'fill_po_edit_filled,not_tally'
}"
:key="index"
:answerValue="ans.value" :answerId=ans.id
@click="addClass($event, 'question.answers')"
style="margin: 15px; padding: 15px 40px; min-width: 210px;">
{{ ans.display_text }}
{{
(ans.value === 'fill_po_edit_filled' && answer_extra === null && question.is_previous && question.answer.answer === 'fill_po_edit_filled,not_tally') ||
(ans.value === 'fill_po_edit_filled' && answer_extra !== null && !answer_extra) ||
(ans.value === 'fill_po_edit_filled' && answer_extra === null && !question.is_previous)
? '(NOT TALLY)' : '' }}
</button>
<!-- <div class="col" v-for="ans in question.question_answers">
<div class="col question.answers">
<div class="btn btn-xs btn-block" :answerValue="ans.value" :answerId=ans.id @click="addClass($event, 'question.answers')">{{ ans.display_text }}</div>
</div>
</div> -->
</validation-wrapper-component>
</div>
</div>
</div>
<!-- MAIN QUESTION AND INFO PART 2 -->
<div class="row m-b-20">
<div class="col">
<div v-if="question.question_number === '1688_order_verify'">
<div v-if="externalApiResponse" class="row rounded justify-content-center">
<div class="col-auto text-left">
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
:key="index">
<h2>
{{ index + 1 + '. #' + attribute.value }}
</h2>
</div>
<div class="col-12" v-if="externalApiResponse.data.company.address">
<div class="row justify-content-center align-items-center text-center">
<h6 class="font-heading all-caps bold">Total Paid: </h6>
<h6>
<!-- <span v-if="question.answer && question.question_number === '1688_order_verification'" class="bold m-r-5"
:class="[{'text-danger' : (Math.round((parseFloat(question.answer.answer) + Number.EPSILON) * 1000) / 1000).toFixed(3) !== (Math.round((externalApiResponse.data.paid_amount + Number.EPSILON) * 1000) / 1000).toFixed(3)},
{'text-success' : (Math.round((parseFloat(question.answer.answer) + Number.EPSILON) * 1000) / 1000).toFixed(3) === (Math.round((externalApiResponse.data.paid_amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}]">{{(Math.round(( parseFloat(question.answer.answer) + Number.EPSILON) * 1000) / 1000).toFixed(3)}}/</span> -->
<span class="text-primary bold m-l-5">{{(Math.round((externalApiResponse.data.paid_amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{externalApiResponse.data.fixed_currency.short_code}}</span>
</h6>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- QUESTION NAVIGATION -->
<div class="row m-t-50 m-b-10" v-if="question.question_number !== 'stop_working' && question.question_number !== 'no_work'">
<div class="col p-r-5">
<button class="btn btn-sm btn-default bg-master-lightest btn-block b-rad-none" :disabled="hidePrevious === true" @click="formTouched = true; submitForm('previous')">Previous</button>
</div>
<div class="col p-l-5">
<button class="btn btn-sm btn-primary btn-block b-rad-none" @click="formTouched = true; submitForm('next')">Next</button>
</div>
</div>
<div class="row" v-else>
<div class="col p-l-5">
<button class="btn btn-sm btn-primary btn-block b-rad-none" @click="reloadPage();">Reload Page</button>
</div>
</div>
</div>
</div>
<div class="row" v-if="noMoreWork">
<div class="col">
<div class="row m-b-5 animate__animated animate__fadeInUpBig animate__fast" v-if="error">
<div class="col">
<small class="bold fs-12">{{error}}</small>
</div>
</div>
<div class="row m-b-10">
<div class="col p-l-5">
<button class="btn btn-sm btn-primary btn-block b-rad-none" @click="reloadPage();">Reload Page</button>
</div>
</div>
</div>
</div>
</div>
<!-- Debug box -->
<div class="debug-box" v-if="question">
<div id="debug-meta">
{{question.question_number}}<span v-if="externalApiResponse && externalApiResponse.data">, Booking Id: {{ externalApiResponse.data.id }}</span><br>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
import { VMoney } from 'v-money'
export default {
data(){
return {
section: 'adminWorkFlowForm',
showAnswerInput: false,
showUploadInput: false,
showUploadInputA: false,
showUploadInputB: false,
showUploadInputC: false,
formTouched: false,
question: null,
answer: '',
answerId: 0,
direction: 'next',
documentType: 1,
files: [],
filesA: [], //English PO
filesB: [], //China PO
filesC: [], //Bank Slip
error: null,
// From AdminWorkFlowSectionComponent
sessionId: null,
externalApiResponse: null,
externalApiUrl: "",
timer: null,
elapsedTime: 0,
stepTime: 0,
interval: 1000,
isFetching: false,
noMoreWork: false,
answer_extra: null,
}
},
validations() {
return {
answer: {
required: this.formTouched && this.showAnswerInput ? required : false,
},
files: {
required: this.formTouched && this.showUploadInput ? required : false,
},
filesA: {
required: this.formTouched && this.showUploadInputA ? required : false, //English PO
},
filesB: {
required: this.formTouched && this.showUploadInputB ? required : false, //China PO
},
filesC: {
required: this.formTouched && this.showUploadInputC ? required : false, //Bank Slip
}
};
},
mounted() {
if (this.sessionId == null) {
this.sessionId = this.generateSessionId();
}
},
computed: {
pendingQueue () {
return this.$store.getters.isInCompleteQueue(this.section);
},
pendingQueueForSecondApiCall () {
return this.$store.getters.isInCompleteQueue(this.section+ 'GetExternalApiResponse');
},
formattedTime() {
const hours = String(Math.floor(this.elapsedTime / 3600)).padStart(2, '0');
const minutes = String(Math.floor((this.elapsedTime % 3600) / 60)).padStart(2, '0');
const seconds = String(this.elapsedTime % 60).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
},
companySegmentIds() {
return (this.externalApiResponse && this.externalApiResponse.data)?.booking?.company?.segments?.map(obj => parseInt(obj.id)) ?? [];
},
hidePrevious(){
// return (this.question && this.question.is_start) || (this.question && this.question.is_no_going_back && this.question.is_no_going_back === 1)
return (this.question && this.question.question_number === 'node_0') || (this.question && this.question.is_no_going_back && this.question.is_no_going_back === 1)
},
isLoading(){
return this.$store.getters.isLoading(this.section) || this.$store.getters.isLoading(this.section + 'GetExternalApiResponse') || this.isFetching;
},
questionTitle(){
return !this.question.question_title.includes('_') ? this.question.question_title : '';
}
},
watch: {
pendingQueue(inComplete, oldValue){
if(inComplete){
this.fetchFirstQuestion();
}
},
pendingQueueForSecondApiCall(inComplete, oldValue){
if(inComplete){
this.fetchAdditionalQuestionInfo();
}
}
},
created(){
this.$store.dispatch('updateListQueue', {'name': this.section});
this.$store.dispatch('updateListQueue', {'name': this.section + 'GetExternalApiResponse'});
},
methods: {
fetchFirstQuestion(){
this.submit(route('api.questionnaires.first.question', 0), 'get', this.section, false, false);
},
fetchAdditionalQuestionInfo(){
// console.log('fetchAdditionalQuestionInfo: ', JSON.stringify(this.question));
if(this.externalApiUrl){
this.isFetching = true;
this.externalApiResponse = null;
this.submit(this.externalApiUrl, 'get', this.section + 'GetExternalApiResponse', false, false);
}
},
successHandler(response, section){
if(section === 'adminWorkFlowFormGetExternalApiResponse'){
// console.log('adminWorkFlowFormGetExternalApiResponse: ', JSON.stringify(section), ' response: ', JSON.stringify(response));
this.$store.dispatch('completeList', {'name': this.section + 'GetExternalApiResponse', 'data': []});
if(response.data)
{
this.externalApiResponse = { data: response.data };
}
else if(response.payload.data)
{
this.externalApiResponse = { data: response.payload.data } ;
}
this.isFetching = false;
}
else{
this.$store.dispatch('completeList', {'name': this.section, 'data': []});
this.question = response.payload.data;
this.reset();
this.removeClass('question.answers');
// if(this.question.answer != null && this.direction == "previous"){
// console.log("previous: " + this.question.answer.answer);
// this.answer = this.question.answer.answer;
// }
if (this.question.question_metadata != null) {
this.externalApiResponse = { data: this.question.question_metadata };
}
//if there is only 1 answer, select answer by default
if(this.question.question_answers.length === 1){
this.answer = this.question.question_answers[0].value;
this.answerId = this.question.question_answers[0].id;
}
if(this.question.is_previous && this.question.answer){
this.externalApiResponse = { data: JSON.parse(this.question.answer.question_metadata) };
if(this.question.question_type === 1){ //MULTIPLE_CHOICES
this.answer = this.question.answer.answer;
this.answerId = this.question.answer.answer_option_id;
}
else if(this.question.question_type === 9){ //FLOAT_MONEY
this.answer = this.externalApiResponse.data.amount_processed * 100;
}
else if(this.question.question_type === 7){ //REMARKS_WITH_DOCUMENT_UPLOAD
this.answer = JSON.parse(this.question.answer.answer).text;
}
}
else {
if(this.question && this.question.url){
let url = this.question.url;
if(!this.isURL(this.question.url)){
if(this.question.url === 'api.admin_work_flow.fetch_model_attributes'){
url = this.route(this.question.url, this.externalApiResponse.data.id);
}
else if(this.question.url === 'api.booking.show'){
url = this.route(this.question.url, this.externalApiResponse.data.marking);
}
else{
url = this.route(this.question.url);
}
}
this.externalApiUrl = url;
}
if(this.question.is_start){
this.elapsedTime = 0;
}
this.fetchAdditionalQuestionInfo();
}
}
},
errorHandler(error, statusCode, section){
if(section === 'adminWorkFlowFormGetExternalApiResponse' && statusCode === 404){
this.noMoreWork = true;
this.error = '[' + this.question.question_title + '] All records processed. Please reload page to continue' ;
}
else{
this.error = 'We are sorry, something went wrong. Please inform tech team.';
}
this.isFetching = false;
},
submitForm(direction) {
this.parameters = {}
this.parameters.question = this.question;
this.parameters.answer = this.answer;
this.parameters.answerId = this.answerId;
this.parameters.answerObj = this.question.question_answers.find(item => item.id === Number(this.answerId));
this.parameters.questionContext = {};
this.error = null;
this.direction = direction;
if(this.question.question_type === 3){
this.showAnswerInput = false;
this.showUploadInput = true;
this.parameters.files = this.files;
}
else if(this.question.question_type === 1 || (this.question.question_type === 2)){
this.showAnswerInput = true;
this.showUploadInput = false;
}
else if(this.question.question_type === 7){
this.showAnswerInput = true;
this.showUploadInput = false;
this.parameters.files = this.files;
}
else if(this.question.question_type === 8){
this.showAnswerInput = false;
this.showUploadInput = false;
this.showUploadInputA = true;
this.showUploadInputB = true;
this.showUploadInputC = true;
this.parameters.filesA = this.filesA;
this.parameters.filesB = this.filesB;
this.parameters.filesC = this.filesC;
}
this.parameters.questionContext.timeUsedSeconds = this.stepTime;
const baseData = {
id: this.externalApiResponse?.data?.id,
company: this.externalApiResponse?.data?.company,
bank: this.externalApiResponse?.data?.bank,
fixed_currency: this.externalApiResponse?.data?.fixed_currency,
marking: this.externalApiResponse?.data?.marking,
payment_history: this.externalApiResponse?.data?.payment_history,
amount: this.externalApiResponse?.data?.amount,
paid_amount: this.externalApiResponse?.data?.paid_amount,
amount_processed: this.externalApiResponse?.data?.amount_processed,
amount_to_be_deducted: this.externalApiResponse?.data?.amount_to_be_deducted,
};
let trimmedData = baseData;
if (this.question.question_number === '1688_order_verify') {
trimmedData = {
...trimmedData, // Spread existing attributes
amount_processed: parseFloat(this.answer.replace(/,/g, '')),
};
this.externalApiResponse.data = trimmedData;
}
this.parameters.questionContext.questionMetadata = trimmedData;
this.parameters.questionContext.user_id = this.$store.getters.getUserId;
this.parameters.questionContext.session_id = this.sessionId;
if(direction === 'next'){
if (this.parameters.answer === 'start_work') this.startWork();
if (this.parameters.answer === 'stop_working') this.stopTimer();
this.submit(this.route('api.questionnaires.next.question'), 'post', this.section, false, false);
}
else if(direction === 'previous'){
console.log('previous');
this.formTouched = false;
this.parameters.isPrevious = true;
this.submit(this.route('api.questionnaires.next.question'), 'post', this.section, false, false);
}
},
reset() {
this.parameters = null;
this.answer = '';
this.answerId = 0;
this.files = [];
this.filesA = [];
this.filesB = [];
this.filesC = [];
this.formTouched = false;
this.showAnswerInput = false;
this.showUploadInput = false;
this.showUploadInputA = false;
this.showUploadInputB = false;
this.showUploadInputC = false;
this.error = null;
this.stepTime = 0;
this.externalApiUrl = "";
this.answer_extra = null;
},
addClass(event, cls) {
const div = event.target;
this.answer = div.getAttribute('answerValue');
this.answerId = div.getAttribute('answerId');
this.removeClass(cls);
if(this.answer === 'fill_po_edit_filled' && !this.answer_extra){
this.answer = div.getAttribute('answerValue') + ",not_tally"
div.classList.add('btn-danger');
}
else{
div.classList.add('btn-primary');
}
},
removeClass(cls) {
document.getElementsByClassName(cls).forEach(el => {
const btnPrimaryEl = el.getElementsByClassName('btn-primary')[0];
if (btnPrimaryEl) {
btnPrimaryEl.classList.remove('btn-primary');
}
const btnDangerEl = el.getElementsByClassName('btn-danger')[0];
if (btnDangerEl) {
btnDangerEl.classList.remove('btn-danger');
}
});
},
startTimer() {
if (!this.timer) {
this.timer = setInterval(() => {
this.elapsedTime++;
this.stepTime++;
}, this.interval);
} else {
console.log("time has already started");
}
},
stopTimer() { clearInterval(this.timer); this.timer = null; },
startWork() { this.startTimer(); },
endWork() { this.stopTimer(); console.log("work is ended"); },
generateSessionId() {
return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
},
isURL(string) {
try {
new URL(string);
return true;
} catch (err) {
return false;
}
},
reloadPage() {
if (typeof window !== "undefined") {
window.location.reload();
}
},
handleAnswerExtra({ isDone }) {
this.answer = '';
this.answerId = 0;
this.removeClass('question.answers');
this.answer_extra = isDone;
},
},
directives: {money: VMoney}
}
</script>
<style scoped>
.debug-box {
position: fixed;
bottom: 0px;
left: 0px;
width: 400px;
background-color: rgba(0, 0, 0, 0.8);
color: white;
font-size: 10px;
border-radius: 5px;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2);
z-index: 1000;
}
</style>

Some files were not shown because too many files have changed in this diff Show More