diff --git a/app/Classes/Exceptions/JobResourceNotFoundException.php b/app/Classes/Exceptions/JobResourceNotFoundException.php new file mode 100644 index 00000000..a8ef358e --- /dev/null +++ b/app/Classes/Exceptions/JobResourceNotFoundException.php @@ -0,0 +1,11 @@ +getMessage(), + $exception->getTrace()[0]['file'], + $exception->getTrace()[0]['line'] + )); + } + else{ + log::error($exception); + } + return (new ApiResponseObject($this->getNotificationTitle().' failed', $exception->getMessage(), $exception->getCode() ? $exception->getCode() : HttpStatus::SERVER_ERROR))->handler(); diff --git a/app/Classes/General/Eloquent/AbstractFetchRecord.php b/app/Classes/General/Eloquent/AbstractFetchRecord.php index 248deee7..503fb369 100644 --- a/app/Classes/General/Eloquent/AbstractFetchRecord.php +++ b/app/Classes/General/Eloquent/AbstractFetchRecord.php @@ -4,9 +4,11 @@ namespace App\Classes\General\Eloquent; use App\Classes\Exceptions\ResourceNotFoundException; +use App\Classes\Exceptions\JobResourceNotFoundException; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Psy\Exception\ErrorException; +use Illuminate\Support\Facades\Log; abstract class AbstractFetchRecord extends AbstractGetRecord { @@ -27,12 +29,18 @@ abstract class AbstractFetchRecord extends AbstractGetRecord * @return Model * @throws ResourceNotFoundException */ - public function getResults(Builder $query): Model { + public function getResults(Builder $query, array $param = []): Model { if(!$query->exists()){ - throw new ResourceNotFoundException('Unable to find any record based on the criteria provided'); + $table = $query->getModel()->getTable(); + if($table ==='job_results'){ + throw new JobResourceNotFoundException('Unable to find any job based on the criteria provided'); + } + else{ + throw new ResourceNotFoundException('Unable to find any record based on the criteria provided'); + } } return $query->first(); } -} \ No newline at end of file +} diff --git a/app/Classes/General/Eloquent/AbstractGetRecord.php b/app/Classes/General/Eloquent/AbstractGetRecord.php index f927d818..1a7eac3e 100644 --- a/app/Classes/General/Eloquent/AbstractGetRecord.php +++ b/app/Classes/General/Eloquent/AbstractGetRecord.php @@ -30,11 +30,25 @@ abstract class AbstractGetRecord return $this->filters->only(self::DECORATION_FILTERS); } + // /** + // * @param null|string $json + // * @return array + // */ + // public function deserializeFilters(?string $json): array { + // return $json !== null ? collect(json_decode($json))->toArray() : []; + // } + /** - * @param null|string $json + * @param null|string $param * @return array */ - public function deserializeFilters(?string $json): array { + public function deserializeFilters($param): array { + if(gettype($param) == "array"){ + $json = implode(',', $param); + } + else{ + $json = $param; + } return $json !== null ? collect(json_decode($json))->toArray() : []; } @@ -50,9 +64,9 @@ abstract class AbstractGetRecord * @param array $filters * @return mixed */ - public function handler(array $filters){ + public function handler(array $filters, array $params = []){ $this->filters = collect($filters); - return $this->getResults($this->applyFiltersToQuery()); + return $this->getResults($this->applyFiltersToQuery(), $params); } @@ -65,6 +79,6 @@ abstract class AbstractGetRecord * @param Builder $query * @return mixed */ - abstract function getResults(Builder $query); + abstract function getResults(Builder $query, array $params = []); -} \ No newline at end of file +} diff --git a/app/Classes/General/Eloquent/AbstractListRecord.php b/app/Classes/General/Eloquent/AbstractListRecord.php index 7b7d0df9..f898f108 100644 --- a/app/Classes/General/Eloquent/AbstractListRecord.php +++ b/app/Classes/General/Eloquent/AbstractListRecord.php @@ -17,11 +17,11 @@ abstract class AbstractListRecord extends AbstractGetRecord * @return mixed * @throws MalformedRequestException */ - public function execute(array $filters = []){ + public function execute(array $filters = [], array $param = []){ try{ - return $this->handler($filters); + return $this->handler($filters, $param); } catch (QueryException $exception){ log::error($exception); @@ -30,18 +30,24 @@ abstract class AbstractListRecord extends AbstractGetRecord } + /** * @param Builder $query * @return mixed */ - public function getResults(Builder $query) { + public function getResults(Builder $query, array $param = []) { $filters = $this->getDecorationFilters(); if($filters->has('order_by')){ $query = $query->orderBy($filters->get('order_by')->column, $filters->get('order_by')->DESC ? 'DESC': 'ASC'); } - return $filters->has('per_page') ? $query->paginate($filters->get('per_page')) : $query->get(); + if(!empty($param)){ + return $filters->has('per_page') ? $query->paginate($filters->get('per_page'), ['*'], 'page', $param['page']) : $query->get(); //page data from query parameters e.g ?page=1 + } + else{ + return $filters->has('per_page') ? $query->paginate($filters->get('per_page')) : $query->get(); + } } diff --git a/app/Classes/General/Eloquent/Filters/JobId.php b/app/Classes/General/Eloquent/Filters/JobId.php new file mode 100644 index 00000000..42ac51a4 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/JobId.php @@ -0,0 +1,20 @@ +where('job_id', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/OrderByIdDesc.php b/app/Classes/General/Eloquent/Filters/OrderByIdDesc.php new file mode 100644 index 00000000..547ec9bd --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/OrderByIdDesc.php @@ -0,0 +1,20 @@ +orderBy('id', 'desc'); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/RequestSignature.php b/app/Classes/General/Eloquent/Filters/RequestSignature.php new file mode 100644 index 00000000..67dde0a3 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/RequestSignature.php @@ -0,0 +1,19 @@ +where('request_signature', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/ResultNotNull.php b/app/Classes/General/Eloquent/Filters/ResultNotNull.php new file mode 100644 index 00000000..3f6a0341 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/ResultNotNull.php @@ -0,0 +1,18 @@ +whereNotNull('result'); + } +} diff --git a/app/Classes/General/Helper.php b/app/Classes/General/Helper.php index ac89540b..45c72f53 100644 --- a/app/Classes/General/Helper.php +++ b/app/Classes/General/Helper.php @@ -2,6 +2,7 @@ namespace App\Classes\General; +use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; @@ -42,4 +43,27 @@ class Helper } } } + + /** + * @param null|string $param + * @return array + */ + static function deserializeFilters($param): array { + if(gettype($param) == "array"){ + $json = implode(',', $param); + } + else{ + $json = $param; + } + return $json !== null ? collect(json_decode($json))->toArray() : []; + } + + /** + * @param ResourceCollection $collection + * @return array + */ + static function collectionResponse(ResourceCollection $collection){ + return json_decode($collection->response()->getContent(), true); + } + } diff --git a/app/Classes/Jobs/ListBookingsJob.php b/app/Classes/Jobs/ListBookingsJob.php new file mode 100644 index 00000000..b021b71d --- /dev/null +++ b/app/Classes/Jobs/ListBookingsJob.php @@ -0,0 +1,52 @@ +listGenericJobObject = $listGenericJobObject; + } + + public function handle() + { + $rawPayload = $this->job->payload(); + if(isset($rawPayload['data']['commandName'])){ + $this->listGenericJobObject->setJobCommandName($rawPayload['data']['commandName']); + } + + if(isset($rawPayload['data']['command'])){ + $this->listGenericJobObject->setJobCommand($rawPayload['data']['command']); + } + + $result = (App()->make(ListBookingsJobProcessor::class))->execute($this->listGenericJobObject); + } + + public function getJobId(){ + return $this->job->getJobId(); + } +} diff --git a/app/Classes/Jobs/ListDocumentsJob.php b/app/Classes/Jobs/ListDocumentsJob.php new file mode 100644 index 00000000..6e93062b --- /dev/null +++ b/app/Classes/Jobs/ListDocumentsJob.php @@ -0,0 +1,63 @@ +listGenericJobObject = $listGenericJobObject; + } + + public function handle() + { + $rawPayload = $this->job->payload(); + if(isset($rawPayload['data']['commandName'])){ + $this->listGenericJobObject->setJobCommandName($rawPayload['data']['commandName']); + } + + if(isset($rawPayload['data']['command'])){ + $this->listGenericJobObject->setJobCommand($rawPayload['data']['command']); + } + + $result = (App()->make(ListDocumentsJobProcessor::class))->execute($this->listGenericJobObject); + + //cief todo: Insert into DB: job id, query result, timestamp + // Store the result in the job_results table + + //cief todo: why cannot save data in table like this + // $model = new JobResult(); + // $model->job_id = $this->job->getJobId(); + // $model->result = json_encode($result); + // $model->save(); + + // Log::error(json_encode($model->id)); + } + + public function getJobId(){ + return $this->job->getJobId(); + } +} diff --git a/app/Classes/Jobs/ListTransactionsJob.php b/app/Classes/Jobs/ListTransactionsJob.php new file mode 100644 index 00000000..a2b28656 --- /dev/null +++ b/app/Classes/Jobs/ListTransactionsJob.php @@ -0,0 +1,52 @@ +listGenericJobObject = $listGenericJobObject; + } + + public function handle() + { + $rawPayload = $this->job->payload(); + if(isset($rawPayload['data']['commandName'])){ + $this->listGenericJobObject->setJobCommandName($rawPayload['data']['commandName']); + } + + if(isset($rawPayload['data']['command'])){ + $this->listGenericJobObject->setJobCommand($rawPayload['data']['command']); + } + + $result = (App()->make(ListTransactionsJobProcessor::class))->execute($this->listGenericJobObject); + } + + public function getJobId(){ + return $this->job->getJobId(); + } +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/ListBookingJobLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/ListBookingJobLogic.php new file mode 100644 index 00000000..ef3570f7 --- /dev/null +++ b/app/Classes/Modules/Bookings/ControllersLogic/ListBookingJobLogic.php @@ -0,0 +1,75 @@ + 'List Booking Job', + 'message' => 'You have successfully submit a job to list bookings' + ]; + } + + /** @var CreatesJobResult */ + private $createsJobResult; + + /** + * ListPackingListsJobLogic constructor. + * @param CreatesJobResult $createsJobResult + */ + public function __construct(CreatesJobResult $createsJobResult) + { + $this->createsJobResult = $createsJobResult; + } + + + /** + * @param Request $request + * @return JsonResponse + */ + public function logic(Request $request) : JsonResponse + { + $jobId = uniqid(); + + $user = Auth::user(); + $userInfo = (object) [ + 'type' => $user->type, + ]; + + $userInfoJson = json_encode($userInfo); + $requestSignature = md5($userInfoJson . $request->fullUrl()); + + $listGenericJobObject = new ListGenericJobObject( + $request->fullUrl(), + $request->all(), + $requestSignature, + null, + $jobId, + $userInfo + ); + + ListBookingsJob::dispatch($listGenericJobObject); + + $result = []; + $result['job_id'] = $jobId; + + $this->createsJobResult->execute($listGenericJobObject); + + return $this->response(['data' => $result]); + } + +} diff --git a/app/Classes/Modules/Bookings/Processors/ListBookingsJobProcessor.php b/app/Classes/Modules/Bookings/Processors/ListBookingsJobProcessor.php new file mode 100644 index 00000000..2a09eb12 --- /dev/null +++ b/app/Classes/Modules/Bookings/Processors/ListBookingsJobProcessor.php @@ -0,0 +1,46 @@ +listsBookings = $listsBookings; + $this->updateJobResultProcessor = $updateJobResultProcessor; + } + + /** + * @param ListGenericJobObject $listGenericJobObject + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\JobResourceNotFoundException + */ + public function execute(ListGenericJobObject $listGenericJobObject) { + + $query = $this->listsBookings->execute($this->listsBookings->deserializeFilters($listGenericJobObject->getPayload()['filters']), ['page' => $listGenericJobObject->getPayload()['page']]); + foreach ($query->items() as &$item) { + $item['userInfo'] = $listGenericJobObject->getUserInfo(); + } + $resultCurrent = Helper::collectionResponse(ListBookingJobResource::collection($query)); + $this->updateJobResultProcessor->execute($listGenericJobObject, $resultCurrent); + } +} diff --git a/app/Classes/Modules/Documents/ControllersLogic/ListDocumentJobLogic.php b/app/Classes/Modules/Documents/ControllersLogic/ListDocumentJobLogic.php new file mode 100644 index 00000000..5e895ad8 --- /dev/null +++ b/app/Classes/Modules/Documents/ControllersLogic/ListDocumentJobLogic.php @@ -0,0 +1,75 @@ + 'List Document Job', + 'message' => 'You have successfully submit a job to list documents' + ]; + } + + /** @var CreatesJobResult */ + private $createsJobResult; + + /** + * ListDocumentJobLogic constructor. + * @param CreatesJobResult $createsJobResult + */ + public function __construct(CreatesJobResult $createsJobResult) + { + $this->createsJobResult = $createsJobResult; + } + + + /** + * @param Request $request + * @return JsonResponse + */ + public function logic(Request $request) : JsonResponse + { + $jobId = uniqid(); + + $user = Auth::user(); + $userInfo = (object) [ + 'email' => $user->email, + 'type' => $user->type, + ]; + + $userInfoJson = json_encode($userInfo); + $requestSignature = md5($userInfoJson . $request->fullUrl()); + + $listGenericJobObject = new ListGenericJobObject( + $request->fullUrl(), + $request->all(), + $requestSignature, + null, + $jobId, + $userInfo + ); + + ListDocumentsJob::dispatch($listGenericJobObject); + + $result = []; + $result['job_id'] = $jobId; + + $this->createsJobResult->execute($listGenericJobObject); + + return $this->response(['data' => $result]); + } +} diff --git a/app/Classes/Modules/Documents/Processors/ListDocumentsJobProcessor.php b/app/Classes/Modules/Documents/Processors/ListDocumentsJobProcessor.php new file mode 100644 index 00000000..70777f0c --- /dev/null +++ b/app/Classes/Modules/Documents/Processors/ListDocumentsJobProcessor.php @@ -0,0 +1,47 @@ +listsDocuments = $listsDocuments; + $this->updateJobResultProcessor = $updateJobResultProcessor; + } + + /** + * @param ListGenericJobObject $listGenericJobObject + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\JobResourceNotFoundException + */ + public function execute(ListGenericJobObject $listGenericJobObject) { + + $query = $this->listsDocuments->execute($this->listsDocuments->deserializeFilters($listGenericJobObject->getPayload()['filters']), ['page' => $listGenericJobObject->getPayload()['page']]); + foreach ($query->items() as &$item) { + $item['userInfo'] = $listGenericJobObject->getUserInfo(); + } + $resultCurrent = Helper::collectionResponse(ListDocumentJobResource::collection($query)); + $this->updateJobResultProcessor->execute($listGenericJobObject, $resultCurrent); + } +} + diff --git a/app/Classes/Modules/Jobs/ControllersLogic/FetchJobResultLogic.php b/app/Classes/Modules/Jobs/ControllersLogic/FetchJobResultLogic.php new file mode 100644 index 00000000..c4b72cf0 --- /dev/null +++ b/app/Classes/Modules/Jobs/ControllersLogic/FetchJobResultLogic.php @@ -0,0 +1,51 @@ + 'Retrieved Data', + 'message' => 'You have successfully retrieved data' + ]; + } + + /** @var FetchesJobResultProcessor */ + private $fetchesJobResultProcessor; + + /** + * FetchJobResultLogic constructor. + * @param FetchesJobResultProcessor $fetchesJobResultProcessor + */ + public function __construct(FetchesJobResultProcessor $fetchesJobResultProcessor) + { + $this->fetchesJobResultProcessor = $fetchesJobResultProcessor; + } + + + /** + * @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 + { + $query = $this->fetchesJobResultProcessor->execute($request); + return $this->resourceResponse(new JobResultResource($query)); + } + +} diff --git a/app/Classes/Modules/Jobs/DataTransferObjects/ListGenericJobObject.php b/app/Classes/Modules/Jobs/DataTransferObjects/ListGenericJobObject.php new file mode 100644 index 00000000..953d77d7 --- /dev/null +++ b/app/Classes/Modules/Jobs/DataTransferObjects/ListGenericJobObject.php @@ -0,0 +1,118 @@ +name = $name; + $this->payload = $payload; + $this->jobId = $jobId; + $this->requestSignature = $requestSignature; + $this->resultSignature = $resultSignature; + $this->userInfo = $userInfo; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return array + */ + public function getPayload(): array + { + return $this->payload; + } + + /** + * @return string + */ + public function getJobId(): string + { + return $this->jobId; + } + + /** + * @return string + */ + public function getRequestSignature(): string + { + return $this->requestSignature; + } + + /** + * @return string + */ + public function getResultSignature(): ?string + { + return $this->resultSignature; + } + + /** + * @return object + */ + public function getUserInfo(): object + { + return $this->userInfo; + } + + /** + * @return string + */ + public function getJobCommandName(): string + { + return $this->jobCommandName; + } + + /** + * @return string + */ + public function getJobCommand(): string + { + return $this->jobCommand; + } + + + public function setJobCommandName(string $jobCommandName) + { + $this->jobCommandName = $jobCommandName; + } + + public function setJobCommand(string $jobCommand) + { + $this->jobCommand = $jobCommand; + } + +} diff --git a/app/Classes/Modules/Jobs/DataTransferObjects/UpdateJobResultObject.php b/app/Classes/Modules/Jobs/DataTransferObjects/UpdateJobResultObject.php new file mode 100644 index 00000000..636c56b0 --- /dev/null +++ b/app/Classes/Modules/Jobs/DataTransferObjects/UpdateJobResultObject.php @@ -0,0 +1,60 @@ +result = $result; + $this->resultSignature = $resultSignature; + $this->jobCommandName = $jobCommandName; + $this->jobCommand = $jobCommand; + } + + /** + * @return string + */ + public function getResult(): string + { + return $this->result; + } + + /** + * @return array + */ + public function getResultSignature(): string + { + return $this->resultSignature; + } + + /** + * @return string + */ + public function getJobCommandName(): string + { + return $this->jobCommandName; + } + + /** + * @return string + */ + public function getJobCommand(): string + { + return $this->jobCommand; + } +} diff --git a/app/Classes/Modules/Jobs/Processors/FetchesJobResultProcessor.php b/app/Classes/Modules/Jobs/Processors/FetchesJobResultProcessor.php new file mode 100644 index 00000000..89783fe8 --- /dev/null +++ b/app/Classes/Modules/Jobs/Processors/FetchesJobResultProcessor.php @@ -0,0 +1,47 @@ +fetchesJobResult = $fetchesJobResult; + } + + + /** + * @param Request $request + * @return Model + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\JobResourceNotFoundException + * @throws \App\Classes\Exceptions\ResourceNotFoundException + */ + public function execute(Request $request){ + + $res1 = $this->fetchesJobResult->execute(['job_id' => $request->route('job_id')]); + if($request->route('is_last')){ + $res2 = $this->fetchesJobResult->execute(['request_signature' => $res1->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]); + return $res2; + } + + if(!$res1->result){ + throw new JobResourceNotFoundException('Unable to find any job based on the criteria provided'); + } + + return $res1; + } +} diff --git a/app/Classes/Modules/Jobs/Processors/UpdateJobResultProcessor.php b/app/Classes/Modules/Jobs/Processors/UpdateJobResultProcessor.php new file mode 100644 index 00000000..0106417d --- /dev/null +++ b/app/Classes/Modules/Jobs/Processors/UpdateJobResultProcessor.php @@ -0,0 +1,64 @@ +fetchesJobResult = $fetchesJobResult; + $this->updatesJobResult = $updatesJobResult; + } + + /** + * @param ListGenericJobObject $listGenericJobObject + * @param array $resultCurrent + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\JobResourceNotFoundException + */ + public function execute(ListGenericJobObject $listGenericJobObject, $resultCurrent) { + $jobResultCurrent = $this->fetchesJobResult->execute(['job_id' => $listGenericJobObject->getJobId()]); + $resultCurrentJson = json_encode($resultCurrent); + $resultSignatureCurrent = md5($resultCurrentJson); + + try{ + $jobResultExisting = $this->fetchesJobResult->execute(['request_signature' => $jobResultCurrent->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]); + $resultSignatureExisting = $jobResultExisting->result_signature; + //if($resultSignatureExisting != $resultSignatureCurrent){ + $this->updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $listGenericJobObject->getJobCommandName(), $listGenericJobObject->getJobCommand()); + //} + } catch (JobResourceNotFoundException $exception){ + $this->updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $listGenericJobObject->getJobCommandName(), $listGenericJobObject->getJobCommand()); + } + } + + private function updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $jobCommandName, $jobCommand){ + $updateJobResultObject = new UpdateJobResultObject( + $resultCurrentJson, + $resultSignatureCurrent, + $jobCommandName, + $jobCommand + ); + $create = $this->updatesJobResult->execute($jobResultCurrent, $updateJobResultObject); + } +} diff --git a/app/Classes/Modules/Jobs/Services/CreatesJobResult.php b/app/Classes/Modules/Jobs/Services/CreatesJobResult.php new file mode 100644 index 00000000..e70e0e6a --- /dev/null +++ b/app/Classes/Modules/Jobs/Services/CreatesJobResult.php @@ -0,0 +1,26 @@ +job_id = $listGenericJobObject->getJobId(); + $model->request_signature = $listGenericJobObject->getRequestSignature(); + $model->result_signature = $listGenericJobObject->getResultSignature(); + $model->url = $listGenericJobObject->getName(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Jobs/Services/FetchesJobResult.php b/app/Classes/Modules/Jobs/Services/FetchesJobResult.php new file mode 100644 index 00000000..1cc99cb6 --- /dev/null +++ b/app/Classes/Modules/Jobs/Services/FetchesJobResult.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Jobs/Services/ListsJobResult.php b/app/Classes/Modules/Jobs/Services/ListsJobResult.php new file mode 100644 index 00000000..55f3b267 --- /dev/null +++ b/app/Classes/Modules/Jobs/Services/ListsJobResult.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Jobs/Services/UpdatesJobResult.php b/app/Classes/Modules/Jobs/Services/UpdatesJobResult.php new file mode 100644 index 00000000..ab3d9385 --- /dev/null +++ b/app/Classes/Modules/Jobs/Services/UpdatesJobResult.php @@ -0,0 +1,28 @@ +result = $updateJobResultObject->getResult(); + $model->result_signature = $updateJobResultObject->getResultSignature(); + $model->job_command_name = $updateJobResultObject->getJobCommandName(); + $model->job_command = $updateJobResultObject->getJobCommand(); + + return $this->handler($model); + + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/ListTransactionsJobLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/ListTransactionsJobLogic.php new file mode 100644 index 00000000..654a360f --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/ListTransactionsJobLogic.php @@ -0,0 +1,74 @@ + 'List Transaction Job', + 'message' => 'You have successfully submit a job to list transactions' + ]; + } + + /** @var CreatesJobResult */ + private $createsJobResult; + + /** + * ListTransactionsJobLogic constructor. + * @param CreatesJobResult $createsJobResult + */ + public function __construct(CreatesJobResult $createsJobResult) + { + $this->createsJobResult = $createsJobResult; + } + + + /** + * @param Request $request + * @return JsonResponse + */ + public function logic(Request $request) : JsonResponse + { + $jobId = uniqid(); + + $user = Auth::user(); + $userInfo = (object) [ + 'type' => $user->type, + ]; + + $userInfoJson = json_encode($userInfo); + $requestSignature = md5($userInfoJson . $request->fullUrl()); + + $listGenericJobObject = new ListGenericJobObject( + $request->fullUrl(), + $request->all(), + $requestSignature, + null, + $jobId, + $userInfo + ); + + ListTransactionsJob::dispatch($listGenericJobObject); + + $result = []; + $result['job_id'] = $jobId; + + $this->createsJobResult->execute($listGenericJobObject); + + return $this->response(['data' => $result]); + } + +} diff --git a/app/Classes/Modules/Transactions/Processors/ListTransactionsJobProcessor.php b/app/Classes/Modules/Transactions/Processors/ListTransactionsJobProcessor.php new file mode 100644 index 00000000..475d4715 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/ListTransactionsJobProcessor.php @@ -0,0 +1,45 @@ +listsTransactions = $listsTransactions; + $this->updateJobResultProcessor = $updateJobResultProcessor; + } + + /** + * @param ListGenericJobObject $listGenericJobObject + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\JobResourceNotFoundException + */ + public function execute(ListGenericJobObject $listGenericJobObject) { + + $query = $this->listsTransactions->execute($this->listsTransactions->deserializeFilters($listGenericJobObject->getPayload()['filters']), ['page' => $listGenericJobObject->getPayload()['page']]); + + $resultCurrent = Helper::collectionResponse(ListTransactionJobResource::collection($query)); + $this->updateJobResultProcessor->execute($listGenericJobObject, $resultCurrent); + } +} + diff --git a/app/Http/Controllers/Bookings/ListBookingsJobController.php b/app/Http/Controllers/Bookings/ListBookingsJobController.php new file mode 100644 index 00000000..9b32fc77 --- /dev/null +++ b/app/Http/Controllers/Bookings/ListBookingsJobController.php @@ -0,0 +1,22 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Documents/ListDocumentsJobController.php b/app/Http/Controllers/Documents/ListDocumentsJobController.php new file mode 100644 index 00000000..8b763085 --- /dev/null +++ b/app/Http/Controllers/Documents/ListDocumentsJobController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Jobs/FetchJobResultController.php b/app/Http/Controllers/Jobs/FetchJobResultController.php new file mode 100644 index 00000000..cfef2f5c --- /dev/null +++ b/app/Http/Controllers/Jobs/FetchJobResultController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Transactions/ListTransactionsJobController.php b/app/Http/Controllers/Transactions/ListTransactionsJobController.php new file mode 100644 index 00000000..fb341d5d --- /dev/null +++ b/app/Http/Controllers/Transactions/ListTransactionsJobController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Resources/BookingResource.php b/app/Http/Resources/BookingResource.php index f3a7881d..70fbc95c 100644 --- a/app/Http/Resources/BookingResource.php +++ b/app/Http/Resources/BookingResource.php @@ -11,11 +11,9 @@ use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\DocumentType; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; -use Illuminate\Support\Facades\Log; class BookingResource extends JsonResource { - /** * Transform the resource into an array. * diff --git a/app/Http/Resources/CompanyResource.php b/app/Http/Resources/CompanyResource.php index 85ecee16..eff2bd1b 100644 --- a/app/Http/Resources/CompanyResource.php +++ b/app/Http/Resources/CompanyResource.php @@ -15,7 +15,6 @@ use App\Models\SegmentConstant; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Log; class CompanyResource extends JsonResource { diff --git a/app/Http/Resources/DocumentResource.php b/app/Http/Resources/DocumentResource.php index 59933a67..c0f801bb 100644 --- a/app/Http/Resources/DocumentResource.php +++ b/app/Http/Resources/DocumentResource.php @@ -3,10 +3,7 @@ namespace App\Http\Resources; use App\Models\Booking; -use App\Models\Company; -use App\Models\Document; use Carbon\Carbon; -use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Resources\Json\JsonResource; class DocumentResource extends JsonResource diff --git a/app/Http/Resources/JobResultResource.php b/app/Http/Resources/JobResultResource.php new file mode 100644 index 00000000..51bc1898 --- /dev/null +++ b/app/Http/Resources/JobResultResource.php @@ -0,0 +1,22 @@ + $this->job_id, + 'result' => $this->result, + ]; + } +} diff --git a/app/Http/Resources/ListBookingJobResource.php b/app/Http/Resources/ListBookingJobResource.php new file mode 100644 index 00000000..06f8c38d --- /dev/null +++ b/app/Http/Resources/ListBookingJobResource.php @@ -0,0 +1,81 @@ +userInfo = $userInfo ?? ($resource->userInfo ?? null); + } + + /** + * 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, $this->userInfo), + '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)) - floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)), + 'fixed_currency' => new CurrencyResource($this->fixedCurrency), + 'convertible_currency' => new CurrencyResource($this->convertibleCurrency), + 'conversion_currency' => new CurrencyResource($this->conversionCurrency), + 'documents' => [ + 'purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::PURCHASE_ORDER)->first()), + 'delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::DELIVER_ORDER)->first()), + 'invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::INVOICE)->first()), + 'supplier_delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->first()), + 'proforma_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::PROFORMA_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->orderByDesc('id')->first()), + 'ecommerce_purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->first()), + ], + 'status' => $this->status, + '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'), + $this->mergeWhen($this->relationLoaded('transactions'), [ + 'purchase_order' => new TransactionResource($this->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first()), + 'payment_attempts' => TransactionResource::collection( + $this->transactions() + ->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION) + ->whereDate('expires_on', '>=', Carbon::now()) + ->get() + ), + 'expired_payment_attempts' => TransactionResource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString())->get()), + '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]); + })->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()) + ]) + ]; + } +} diff --git a/app/Http/Resources/ListDocumentJobResource.php b/app/Http/Resources/ListDocumentJobResource.php new file mode 100644 index 00000000..edf549ef --- /dev/null +++ b/app/Http/Resources/ListDocumentJobResource.php @@ -0,0 +1,31 @@ + $this->id, + 'reference' => $this->reference, + 'status' => (int) $this->status, + 'document_type' => $this->document_type, + 'owner' => $this->relationLoaded('owner') ? ($this->owner instanceof Booking ? new BookingV2Resource($this->owner, $this->userInfo) : new CompanyV2Resource($this->owner, $this->userInfo)) : null, + 'files' => FileResource::collection($this->files), + 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A') + ]; + } +} diff --git a/app/Http/Resources/ListTransactionJobResource.php b/app/Http/Resources/ListTransactionJobResource.php new file mode 100644 index 00000000..6c4c0ece --- /dev/null +++ b/app/Http/Resources/ListTransactionJobResource.php @@ -0,0 +1,54 @@ +type, [TransactionType::BILL, TransactionType::REFUND])? $this->owner->owner : $this->owner; + $days = $this->created_at->endOfDay()->addWeekdays($booking->service_id === 3 ? 3 : 1); + + return [ + 'id' => $this->id, + 'booking' => new BookingResource($booking), + 'type' => (int) $this->type, + 'bill_no' => $this->bill_no, + 'payment_reference' => $this->payment_reference, + 'payment_method' => (float) $this->payment_method, + 'recipient_bank_account' => new BankResource($booking->bank), + 'issuer_name' => $this->issuerCompany->name, + 'issuer_id' => $this->issuerCompany->id, + 'amount' => (double) $this->amount, + 'original_amount' => (double) $this->original_amount, + 'currency' => new CurrencyResource($this->currency), + 'original_currency' => new CurrencyResource($this->original_currency), + 'service_charge' => (double) $this->service_charge, + 'tax' => (double) $this->tax, + 'currency_rate' => (double) $this->currency_rate, + 'status' => (int) $this->status, + 'details' => TransactionDetailResource::collection($this->transactionDetails), + 'documents' => new DocumentResource($this->documents()->first()), + 'transaction_bill' => new TransactionResource($this->when((int) $this->type === TransactionType::PAYMENT, $this->transactions()->bills()->first())), + 'transaction_refunds' => TransactionResource::collection($this->when((int) $this->type === TransactionType::PAYMENT, $this->transactions()->refunds()->get())), + 'expires_on' => Carbon::parse($this->expires_on)->format('d-m-Y h:i:s A'), + 'updated_at' => Carbon::parse($this->updated_at)->format('d-m-Y h:i:s A'), + 'interval' => [ + 'value' => $days->gt(Carbon::now()) ? '+' : '-', + 'duration' => $days->diff(Carbon::now())->format('%d'), + ], + 'redemption' => new VoucherRedemptionResource($this->voucherRedemption) + ]; + } +} diff --git a/app/Http/Resources/V2/BookingV2Resource.php b/app/Http/Resources/V2/BookingV2Resource.php new file mode 100644 index 00000000..ee17181e --- /dev/null +++ b/app/Http/Resources/V2/BookingV2Resource.php @@ -0,0 +1,82 @@ +userInfo = $userInfo ?? ($resource->userInfo ?? null); + } + + /** + * 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 CompanyV2Resource($this->company, $this->userInfo), + 'bank' => new V1\BankResource($this->bank), + 'service' => new V1\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)) - floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)), + 'fixed_currency' => new V1\CurrencyResource($this->fixedCurrency), + 'convertible_currency' => new V1\CurrencyResource($this->convertibleCurrency), + 'conversion_currency' => new V1\CurrencyResource($this->conversionCurrency), + 'documents' => [ + 'purchase_order' => new V1\DocumentResource($this->documents()->where('document_type', DocumentType::PURCHASE_ORDER)->first()), + 'delivery_order' => new V1\DocumentResource($this->documents()->where('document_type', DocumentType::DELIVER_ORDER)->first()), + 'invoice' => new V1\DocumentResource($this->documents()->where('document_type', DocumentType::INVOICE)->first()), + 'supplier_delivery_order' => new V1\DocumentResource($this->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->first()), + 'proforma_invoice' => new V1\DocumentResource($this->documents()->where('document_type', DocumentType::PROFORMA_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->orderByDesc('id')->first()), + 'ecommerce_purchase_order' => new V1\DocumentResource($this->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->first()), + ], + 'status' => $this->status, + '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'), + $this->mergeWhen($this->relationLoaded('transactions'), [ + 'purchase_order' => new V1\TransactionResource($this->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first()), + 'payment_attempts' => V1\TransactionResource::collection( + $this->transactions() + ->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION) + ->whereDate('expires_on', '>=', Carbon::now()) + ->get() + ), + 'expired_payment_attempts' => V1\TransactionResource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString())->get()), + 'payment_history' => V1\TransactionResource::collection($this->transactions()->where(function($query){ + $query->where(function($query){ + $query->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED]); + })->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()) + ]) + ]; + } +} diff --git a/app/Http/Resources/V2/CompanyV2Resource.php b/app/Http/Resources/V2/CompanyV2Resource.php new file mode 100644 index 00000000..64fca7c7 --- /dev/null +++ b/app/Http/Resources/V2/CompanyV2Resource.php @@ -0,0 +1,100 @@ +userInfo = $userInfo; + } + + /** + * Transform the resource into an array. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function toArray($request) + { + $lastPayment = $this->transactions()->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->orderBy('id', 'DESC')->first(); + $totalPayments = $this->transactions()->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->sum('amount'); + + $segment = SegmentConstant::where('reference', SegmentConstants::SUPPLIER_CURRENCIES)->where('detail->id', $this->id)->first(); + $serviceCharge = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $this->id)->first(); + + $userResource = null; + + $userInfoEmail = $this->userInfo && isset($this->userInfo->email) ? $this->userInfo->email : null; + $userInfoType = $this->userInfo && isset($this->userInfo->type) ? $this->userInfo->type : null; + + if(!$userInfoEmail && Auth::user()){ + $userInfoEmail = Auth::user()->email; + } + if(!$userInfoType && Auth::user()){ + $userInfoType = Auth::user()->type; + } + + if(!is_null($userInfoEmail) && !is_null($userInfoType)){ + $userResource = new V1\UserResource($userInfoType === RoleTypes::USER ? $this->employees()->where('email', '=', $userInfoEmail)->first() : $this->employees()->orderBy('id', 'DESC')->first()); + } + + return [ + 'id' => $this->id, + 'name' => $this->name, + 'reference' => $this->reference, + 'debtor' => $this->debtor, + 'type' => (int) $this->type, + 'business_type' => (int) $this->business_type, + 'status' => (int) $this->status, + 'contact' => new V1\ContactResource ($this->when($this->has('contacts'), $this->contacts->first())), + 'address' => new V1\AddressResource($this->when($this->has('addresses'), $this->addresses->where('billing', true)->first())), + 'employee' => $userResource, + 'identification' => new V1\DocumentResource($this->documents->whereIn('document_type', DocumentType::IDENTIFICATION_DOCUMENTS)->first()), + 'bookings' => $this->whenLoaded('bookings', $this->bookings()->orderBy('id', 'DESC')->get(), []), + 'confirmed_bookings' => $this->bookings()->whereHas('transactions', function ($query){ + $query->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + })->count(), + 'total_payments' => (float) $totalPayments, + 'average_spending_per_day' => (float) $totalPayments / ($this->created_at->diff(Carbon::now())->days === 0 ? 1 : $this->created_at->diff(Carbon::now())->days), + 'average_spending_per_booking' => (float) $totalPayments > 0 ? $totalPayments / $this->bookings()->whereHas('transactions', function ($query){ + $query->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + })->count() : $totalPayments, + 'last_payment' => $lastPayment ? $lastPayment->created_at->diffForHumans() : 'No Payments', + 'personal_banks' => V1\BankResource::collection($this->banks->where('type', BankAccountType::PERSONAL)), + 'recipient_banks' => [ + 'accounts' => V1\BankResource::collection($this->banks->where('type', BankAccountType::EXTERNAL)), + 'default' => new V1\BankResource($this->banks->where('type', BankAccountType::EXTERNAL)->where('default', true)->first()) + ], + 'segments' => V1\SegmentResource::collection($this->segments), + 'seasonalSegment' => $this->whenLoaded('seasonalSegments', V1\SeasonalSegmentResource::collection($this->seasonalSegments)), + 'services' => (new FetchesCompanyServices())->getServices($this->servicesConfigurations()), + 'wallet' => $this->whenLoaded('wallets', new V1\WalletResource($this->wallets()->with('transactions')->first()), new V1\WalletResource($this->wallets()->first())), + 'created_at' => $this->created_at->format('d-m-Y'), + $this->mergeWhen($this->business_type === BusinessType::CURRENCY_VENDOR, [ + 'currencies' => $segment ? V1\CurrencyResource::collection(Currency::whereIn('id', $segment->detail->currencies)->get()) : [], + 'service_charge' => $serviceCharge + ]) + + ]; + } +} diff --git a/app/Models/JobResult.php b/app/Models/JobResult.php new file mode 100644 index 00000000..f30b5076 --- /dev/null +++ b/app/Models/JobResult.php @@ -0,0 +1,13 @@ +id(); + $table->string('job_id', 50); + $table->longText('result')->nullable(); + $table->timestamps(); + + // $table->foreign('job_id')->references('id')->on('jobs')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('job_results'); + } +} diff --git a/database/migrations/2023_08_29_063531_add_new_column_to_job_results_table.php b/database/migrations/2023_08_29_063531_add_new_column_to_job_results_table.php new file mode 100644 index 00000000..8e6b8342 --- /dev/null +++ b/database/migrations/2023_08_29_063531_add_new_column_to_job_results_table.php @@ -0,0 +1,36 @@ +longText('url')->after('result')->nullable(); + $table->string('job_command_name')->after('url')->nullable(); + $table->longText('job_command')->after('job_command_name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('job_results', function (Blueprint $table) { + $table->dropColumn('url'); + $table->dropColumn('job_command_name'); + $table->dropColumn('job_command'); + }); + } +} diff --git a/database/migrations/2023_12_11_193200_add_new_column_2_to_job_results_table.php b/database/migrations/2023_12_11_193200_add_new_column_2_to_job_results_table.php new file mode 100644 index 00000000..12c6d57f --- /dev/null +++ b/database/migrations/2023_12_11_193200_add_new_column_2_to_job_results_table.php @@ -0,0 +1,34 @@ +string('request_signature')->after('job_id')->nullable(); + $table->string('result_signature')->after('request_signature')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('job_results', function (Blueprint $table) { + $table->dropColumn('request_signature'); + $table->dropColumn('result_signature'); + }); + } +} diff --git a/resources/assets/vue/components/bookings/sections/AdminPaymentsBillingSectionComponent.vue b/resources/assets/vue/components/bookings/sections/AdminPaymentsBillingSectionComponent.vue new file mode 100644 index 00000000..966ccbbc --- /dev/null +++ b/resources/assets/vue/components/bookings/sections/AdminPaymentsBillingSectionComponent.vue @@ -0,0 +1,136 @@ + + diff --git a/resources/assets/vue/components/bookings/sections/AdminPaymentsBillingSectionPollingComponent.vue b/resources/assets/vue/components/bookings/sections/AdminPaymentsBillingSectionPollingComponent.vue new file mode 100644 index 00000000..47d14857 --- /dev/null +++ b/resources/assets/vue/components/bookings/sections/AdminPaymentsBillingSectionPollingComponent.vue @@ -0,0 +1,136 @@ + + diff --git a/resources/assets/vue/components/bookings/sections/SupplierPendingOrdersSectionComponent.vue b/resources/assets/vue/components/bookings/sections/SupplierPendingOrdersSectionComponent.vue index 7df9c125..d2b69080 100644 --- a/resources/assets/vue/components/bookings/sections/SupplierPendingOrdersSectionComponent.vue +++ b/resources/assets/vue/components/bookings/sections/SupplierPendingOrdersSectionComponent.vue @@ -117,11 +117,17 @@
+ +
@@ -165,7 +171,7 @@ } }, created(){ - this.submit(route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]}), 'get', 'pendingOrdersSection', false, false) + this.submit(route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]}), 'get', 'pendingOrdersSection', false, false); //cief todo: Uncaught (in promise) null }, methods: { successHandler(response){ @@ -210,4 +216,4 @@ } - \ No newline at end of file + diff --git a/resources/assets/vue/components/general/elements/ListPollingComponent.vue b/resources/assets/vue/components/general/elements/ListPollingComponent.vue new file mode 100644 index 00000000..fcd3b8b8 --- /dev/null +++ b/resources/assets/vue/components/general/elements/ListPollingComponent.vue @@ -0,0 +1,213 @@ + + + diff --git a/resources/assets/vue/general/mixins/aws/requestV2.js b/resources/assets/vue/general/mixins/aws/requestV2.js new file mode 100644 index 00000000..12b27542 --- /dev/null +++ b/resources/assets/vue/general/mixins/aws/requestV2.js @@ -0,0 +1,49 @@ +export default { + methods: { + poll(url, method, section, successNotification = true, errorNotification = true){ + if(!this.validate()){ return; } + if (section) { + this.$store.dispatch('toggleLoading', {name: section, status: true}) + } + this.$store.dispatch('crudRequestV2', { + endpoint: url, + method: method, + parameters: this.parameters + }).then(response => { + let statusCode = response.status, + success = response.ok; + + response.json().then(response => { + + if(!success){ + this.openModal(); + errorNotification ? this.$store.dispatch('createNotification', {title: response.title, message: response.message, type: 'error'}): null; + this.errorHandler(response, statusCode); return; + } + + successNotification ? this.$store.dispatch('createNotification', {title: response.title, message: response.message, type: 'success'}): null; + this.successHandler(response) + + + }); + }).catch((error) => { + this.$store.dispatch('createNotification', {title: 'Unexpected Error', message: 'An unexpected error has occurred. Try again!', type: 'error'}); + }).then(() => { + if (section) { + this.$store.dispatch('toggleLoading', {name: section, status: false}) + } + }) + + }, + validate() { + if(this.$v){ + this.$v.$touch(); + return !this.$v.$invalid; + } + return true; + }, + successHandler(response){}, + errorHandler(response){} + } + +} diff --git a/resources/assets/vue/general/mixins/tabHandler.js b/resources/assets/vue/general/mixins/tabHandler.js new file mode 100644 index 00000000..81790ed0 --- /dev/null +++ b/resources/assets/vue/general/mixins/tabHandler.js @@ -0,0 +1,24 @@ +export default { + data() { + return { + activeTab: null, + displayedTabs: [], + }; + }, + methods: { + setActiveTab(event) { + const tabName = event.currentTarget.getAttribute('tab-name'); + // console.log(`Tab "${tabName}" clicked`); + this.activeTab = tabName; + if (!this.displayedTabs.includes(tabName)) { + this.displayedTabs.push(tabName); + } + }, + isActiveTab(tabName) { + return this.activeTab === tabName; + }, + showTabContent(tabName) { + return this.displayedTabs.includes(tabName); + }, + }, +} diff --git a/resources/assets/vue/vuex/modules/crudRequestV2.js b/resources/assets/vue/vuex/modules/crudRequestV2.js new file mode 100644 index 00000000..355b1edb --- /dev/null +++ b/resources/assets/vue/vuex/modules/crudRequestV2.js @@ -0,0 +1,51 @@ +export default { + actions: { + crudRequestV2({getters, dispatch}, {endpoint, method, parameters}){ + return dispatch('ensureReCaptchaIsSet').then(function () { + const queryDomain = endpoint.split('?')[0]; + let encodedParams = endpoint.split('?')[1]; + let decodedParams = fullyDecodeURI(encodedParams); + const queryParams = encodeURIComponent(decodedParams); + encodedParams = queryParams.toString(); + let filteredEncodedParams = encodedParams.replace(/%3D/g,'='); + filteredEncodedParams = filteredEncodedParams.replace(/%26/g,'&'); + let combinedAbsoluteUrl = queryDomain; + if(filteredEncodedParams !== undefined && filteredEncodedParams !== 'undefined'){ + combinedAbsoluteUrl = queryDomain + '?' + filteredEncodedParams; + } + + // return fetch(endpoint, { + return fetch(combinedAbsoluteUrl, { + method: method, + responseType: 'json', + body: parameters ? JSON.stringify(parameters):null, + headers: { + 'content-type': 'application/json', + 'Authorization': 'Bearer '+getters.getAccessToken, + 'captcha-token': getters.getReCaptcha + } + }).then(response => { + + if(response.status === 401 && window.location.href !== route('login')){ + dispatch('userAuthentication', {access_token: '', redirect_url: '/'}); + } + + return response; + + }) + }); + } + } +} + +function isEncoded(uri) { + uri = uri || ''; + return uri !== decodeURIComponent(uri); +} + +function fullyDecodeURI(uri){ + while (isEncoded(uri)){ + uri = decodeURIComponent(uri); + } + return uri; +} diff --git a/resources/assets/vue/vuex/store.js b/resources/assets/vue/vuex/store.js index 2c3d911b..ff10c673 100644 --- a/resources/assets/vue/vuex/store.js +++ b/resources/assets/vue/vuex/store.js @@ -4,6 +4,7 @@ import toggleSection from './modules/toggleSection' import toggleLoading from './modules/toggleLoading' import createNotification from './modules/createNotification' import crudRequest from './modules/crudRequest' +import crudRequestV2 from './modules/crudRequestV2' import authentication from './modules/authentication' import loadRequestQueue from './modules/loadRequestQueue' @@ -16,6 +17,7 @@ export default new Vuex.Store({ loadRequestQueue, createNotification, crudRequest, + crudRequestV2, authentication } -}) \ No newline at end of file +}) diff --git a/resources/views/pages/billings.blade.php b/resources/views/pages/billings.blade.php index 4bc012d5..da84f8b9 100644 --- a/resources/views/pages/billings.blade.php +++ b/resources/views/pages/billings.blade.php @@ -3,129 +3,7 @@
- -
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
Invoice
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
Purchase Order
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
Delivery Order
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
Supplier Delivery Order
-
-
-
-
-
-
-
-
-
-
-
-
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
-
-
-
+
-@endsection \ No newline at end of file +@endsection diff --git a/resources/views/pages/billings_experiment.blade.php b/resources/views/pages/billings_experiment.blade.php new file mode 100644 index 00000000..d1caee2d --- /dev/null +++ b/resources/views/pages/billings_experiment.blade.php @@ -0,0 +1,9 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ + +
+
+@endsection diff --git a/routes/api.php b/routes/api.php index 8b4bada0..460bd772 100644 --- a/routes/api.php +++ b/routes/api.php @@ -67,6 +67,10 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function require __DIR__ . '/milestone.php'; + // require __DIR__ . '/accounting.php'; //cief todo: To check if this is needed + + require __DIR__ . '/job.php'; + // require __DIR__ . '/rate.php'; // require __DIR__ . '/receipt.php'; diff --git a/routes/currency.php b/routes/currency.php index b621aad6..ba55c9a2 100644 --- a/routes/currency.php +++ b/routes/currency.php @@ -1,4 +1,4 @@ - 'document', 'as' => 'document.', 'namespace' => 'Documents'], function () { Route::get('/list', 'ListDocumentsController@list')->name('list'); + Route::get('/list/job', 'ListDocumentsJobController@list')->name('list.job'); Route::delete('/{id}/delete', 'DeleteDocumentController@delete')->name('delete'); Route::put('/{id}/approve', 'ApproveDocumentController@approve')->name('status.approve'); Route::put('/{id}/reject', 'RejectDocumentController@reject')->name('status.reject'); Route::put('/{id}/reference/update', 'UpdateDocumentReferenceController@update')->name('reference.update'); -}); \ No newline at end of file +}); diff --git a/routes/job.php b/routes/job.php new file mode 100644 index 00000000..028e9468 --- /dev/null +++ b/routes/job.php @@ -0,0 +1,8 @@ + 'job', 'as' => 'job.', 'namespace' => 'Jobs'], function () { + Route::get('/fetch/{job_id}', 'FetchJobResultController@fetch')->name('fetch'); + Route::get('/fetch/{job_id}/{is_last}', 'FetchJobResultController@fetch')->name('fetch.last.attempt'); +}); diff --git a/routes/web.php b/routes/web.php index bf2892bb..a6590c17 100644 --- a/routes/web.php +++ b/routes/web.php @@ -92,6 +92,12 @@ Route::get('/billings', function () { return view('pages.billings'); })->name('billings'); +/* Vue Polling Experiment - Starts */ +Route::get('/billings-experiment', function () { + return view('pages.billings_experiment'); +})->name('billings.experiment'); +/* Vue Polling Experiment - Ends */ + Route::get('/currency_orders', function () { return view('pages.currency_orders'); })->name('currency_orders'); @@ -830,13 +836,13 @@ Route::get('/invoice/{marking}/{started_at}/{ended_at}/fix', function($marking, ->withTrashed() ->orderBy('created_at', 'asc') ->first(); - + // get the first bill_no $firstBillNo = $firstInvoice->bill_no; if (strpos($firstBillNo, '-deleted') !== false) { $firstBillNo = substr($firstBillNo, 0, strpos($firstBillNo, '-deleted')); } - + // update currentInvoice bill_no to '-deleted-' $currentInvoice = $booking->transactions()->where('type', TransactionType::INVOICE)->first(); $currentInvoice->bill_no = $currentInvoice->bill_no ."-deleted-" . Str::random(10); @@ -859,4 +865,4 @@ Route::get('/invoice/{marking}/{started_at}/{ended_at}/fix', function($marking, } } ); -})->name('invoice.fix.byCustomerMarking'); \ No newline at end of file +})->name('invoice.fix.byCustomerMarking');