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/GroupByImportedDate.php b/app/Classes/General/Eloquent/Filters/GroupByImportedDate.php new file mode 100644 index 00000000..25f20fa3 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/GroupByImportedDate.php @@ -0,0 +1,18 @@ +groupby('imported_date'); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/ImportedDateFrom.php b/app/Classes/General/Eloquent/Filters/ImportedDateFrom.php new file mode 100644 index 00000000..140bbb20 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/ImportedDateFrom.php @@ -0,0 +1,18 @@ +whereDate('imported_date', '>=', date('Y-m-d',strtotime($value))); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/ImportedDateTo.php b/app/Classes/General/Eloquent/Filters/ImportedDateTo.php new file mode 100644 index 00000000..11bd3857 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/ImportedDateTo.php @@ -0,0 +1,18 @@ +whereDate('imported_date', '<=', date('Y-m-d',strtotime($value))); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/IsNotFullyRefunded.php b/app/Classes/General/Eloquent/Filters/IsNotFullyRefunded.php new file mode 100644 index 00000000..fdbb2994 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsNotFullyRefunded.php @@ -0,0 +1,24 @@ +withSum(['transactions as total_refund_amount' => function($q) { + $q->refunds()->where('status', ApprovalStatus::APPROVED); + }], 'original_amount') + ->having('total_refund_amount', '<', DB::raw('original_amount')); + } +} 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/Eloquent/Filters/StatementTransactionInvoiceReference.php b/app/Classes/General/Eloquent/Filters/StatementTransactionInvoiceReference.php new file mode 100644 index 00000000..9e3ba1e9 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionInvoiceReference.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->where('Invoice_reference', $value); + }); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerReference.php b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerReference.php new file mode 100644 index 00000000..c0a7beaf --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerReference.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->where('owner_reference', $value); + }); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionReceiptReference.php b/app/Classes/General/Eloquent/Filters/StatementTransactionReceiptReference.php new file mode 100644 index 00000000..f808f784 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionReceiptReference.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->where('receipt_reference', $value); + }); + } + +} 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/Accounting/ControllersLogic/HistoryImportedTransactionMappedControllerLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/HistoryImportedTransactionMappedControllerLogic.php new file mode 100644 index 00000000..2f96c613 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/HistoryImportedTransactionMappedControllerLogic.php @@ -0,0 +1,49 @@ + 'Retrieved History Imported Invoices', + 'message' => 'You have successfully retrieved history imported invoices' + ]; + } + + /** @var ListTransactionMappingLogs */ + private $listTransactionMappingLogs; + + /** + * UpdateAnnouncementLogic constructor. + * @param ListTransactionMappingLogs $listTransactionMappingLogs + */ + public function __construct( + ListTransactionMappingLogs $listTransactionMappingLogs + ) { + $this->listTransactionMappingLogs = $listTransactionMappingLogs; + } + + /** + * @param Request $request + * @return JsonResponse + */ + public function logic(Request $request): JsonResponse + { + $query = $this->listTransactionMappingLogs->execute($this->listTransactionMappingLogs->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(TransactionMappingLogResource::collection($query)); + } +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php index 3a006e9e..b208c2e3 100644 --- a/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php +++ b/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php @@ -2,16 +2,14 @@ namespace App\Classes\Modules\Accounting\ControllersLogic; -use App\Classes\General\Abstracts\AbstractControllerLogic; -use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransaction; -use App\Http\Resources\BankStatementTransactionResource; -use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus; -use App\Classes\ValueObjects\Constants\ApprovalStatus; -use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType; -use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; - +use Illuminate\Http\JsonResponse; +use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\General\Abstracts\AbstractControllerLogic; +use App\Http\Resources\BankStatementTransactionOwnerResource; +use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType; +use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransactionOwner; +use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus; class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic { @@ -27,29 +25,23 @@ class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic ]; } - /** @var FetchesBankStatementTransaction */ - private $fetchesBankStatementTransaction; + /** @var FetchesBankStatementTransactionOwner */ + private $fetchesBankStatementTransactionOwner; /** @var UpdatesBankStatementTransactionOwnerStatus */ private $updatesBankStatementTransactionOwnerStatus; - /** @var UpdatesTransactionStatus */ - private $updatesTransactionStatus; - /** * UpdateAnnouncementLogic constructor. - * @param FetchesBankStatementTransaction $fetchesBankStatementTransaction + * @param FetchesBankStatementTransactionOwner $fetchesBankStatementTransactionOwner * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus - * @param UpdatesTransactionStatus $updatesTransactionStatus */ public function __construct( - FetchesBankStatementTransaction $fetchesBankStatementTransaction, - UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus, - UpdatesTransactionStatus $updatesTransactionStatus + FetchesBankStatementTransactionOwner $fetchesBankStatementTransactionOwner, + UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus ) { - $this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction; + $this->fetchesBankStatementTransactionOwner = $fetchesBankStatementTransactionOwner; $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; - $this->updatesTransactionStatus = $updatesTransactionStatus; } /** @@ -61,21 +53,10 @@ class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic */ public function logic(Request $request): JsonResponse { - $statementTrasaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]); - - $statementTrasactionOwner = $statementTrasaction->owners->first(); + $statementTrasactionOwner = $this->fetchesBankStatementTransactionOwner->execute(['id' => $request->route('id')]); $this->updatesBankStatementTransactionOwnerStatus->execute($statementTrasactionOwner, $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED); - // todo-new: approve payments status, need to check the owner(if system is shipping, need to api with shipping portal) - // if ($request->route('status') == 'approve') { - // if ($statementTrasactionOwner->transaction->type === StatementTransactionOwnerType::SALES) { - // if ($statementTrasactionOwner->owner->status === ApprovalStatus::PENDING_VERIFICATION) { - // $this->updatesTransactionStatus->execute($statementTrasactionOwner->owner, ApprovalStatus::APPROVED); - // } - // } - // } - - return $this->resourceResponse(new BankStatementTransactionResource($statementTrasaction)); + return $this->resourceResponse(new BankStatementTransactionOwnerResource($statementTrasactionOwner)); } } diff --git a/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php b/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php index f80c3c0e..18ec9c36 100644 --- a/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php +++ b/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php @@ -13,6 +13,7 @@ class ListShippingPortalTransactions { try { $url = 'https://izyim.cief-malaysia.com/public/api/v1/transactions/mappable/query/with-details'; + // $url = 'http://127.0.0.1:8001/public/api/v1/transactions/mappable/query/with-details'; $client = new \GuzzleHttp\Client(['verify' => false]); $response = $client->request('GET', $url . '?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters=' . json_encode($filters)); $body = $response->getBody(); diff --git a/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransactionOwner.php b/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransactionOwner.php new file mode 100644 index 00000000..17c620f5 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransactionOwner.php @@ -0,0 +1,31 @@ +repository = $repository; + } + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/ListTransactionMappingLogs.php b/app/Classes/Modules/Accounting/Services/ListTransactionMappingLogs.php new file mode 100644 index 00000000..fdeff835 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/ListTransactionMappingLogs.php @@ -0,0 +1,32 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index c429d91d..ab654675 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -19,6 +19,7 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; use App\Http\Resources\TransactionResource; +use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor; @@ -77,6 +78,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var BookingToVoucherifyProcessor */ private $bookingToVoucherifyProcessor; + /** @var CalculatesBookingRefundAmount */ + private $calculatesBookingRefundAmount; + /** * CreateBookingPaymentLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -90,8 +94,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic * @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) + 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; @@ -104,6 +109,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; $this->recalculatesWalletBalance = $recalculatesWalletBalance; $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; + $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; } /** @@ -119,7 +125,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $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); + $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 .'.'); diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php index d31ee2ce..0648629e 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php @@ -15,10 +15,10 @@ use App\Classes\Modules\Transactions\Services\CreatesTransaction; use App\Classes\Modules\Transactions\Services\FetchesTransaction; use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; -use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Classes\Modules\Transactions\DataTransferObjects\TransactionRefundCalculationObject; +use App\Classes\ValueObjects\Constants\PaymentMethodType; class CreateBookingRefundLogic extends AbstractControllerLogic { @@ -48,9 +48,6 @@ class CreateBookingRefundLogic extends AbstractControllerLogic /** @var CreatesTransaction */ private $createsTransaction; - /** @var CalculatesBookingRefundAmount */ - private $calculatesBookingRefundAmount; - /** * CreateBookingPaymentLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -58,16 +55,14 @@ class CreateBookingRefundLogic extends AbstractControllerLogic * @param UpdatesTransactionStatus $updatesTransactionStatus * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber * @param CreatesTransaction $createsTransaction - * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount */ - public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingRefundAmount $calculatesBookingRefundAmount) + public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction) { $this->fetchBookingQuotation = $fetchBookingQuotation; $this->fetchesTransaction = $fetchesTransaction; $this->updatesTransactionStatus = $updatesTransactionStatus; $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; $this->createsTransaction = $createsTransaction; - $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; } /** @@ -80,25 +75,30 @@ class CreateBookingRefundLogic extends AbstractControllerLogic $transaction = $this->fetchesTransaction->execute(['id' => $request->route('payment_id')]); + if ($transaction->transactions()->bills()->first()) { + throw new MalformedRequestException('Booking under white form cannot request for refund'); + } + $booking = $transaction->owner; $billNumber = $this->generatesTransactionBillNumber->execute('RFD-'); - $refund = $transaction->transactions()->refunds()->sum('amount'); + $refund = $transaction->transactions()->refunds()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->sum('original_amount'); if($refund + $request->input('amount') > $transaction->original_amount) throw new MalformedRequestException('Your refund must not be greater than '. $transaction->original_amount .'.'); - $amount = $transaction->booking->fix_currency_id == 1 ? $request->input('amount') : $request->input('amount') / $transaction->currency_rate; + // $transactionRefundCalculationObject = new TransactionRefundCalculationObject($booking, $transaction, $request->input('amount')); + // $transactionRefundCalculationObject->init(); - - $transactionRefundCalculationObject = new TransactionRefundCalculationObject($booking, $transaction, $amount); - $transactionRefundCalculationObject->init(); + $refundAmount = bcdiv($request->input('amount'), $transaction->currency_rate, 7); + // refund service charges if is fully refund + $refundTotal = ($refund + $request->input('amount')) == $transaction->original_amount ? $refundAmount + $transaction->service_charge + $transaction->tax : $refundAmount; $object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id, - 1, $transactionRefundCalculationObject->getConversionObject()->getPaymentMethod(), - $transactionRefundCalculationObject->getRefundTotalAmount(), $transactionRefundCalculationObject->getAmount(), 1, - $transactionRefundCalculationObject->getConversionObject()->getCurrencyId(), $transactionRefundCalculationObject->getTransaction()->currency_rate, - $transactionRefundCalculationObject->getRefundTax(), $transactionRefundCalculationObject->getRefundServiceCharge(), null, ApprovalStatus::PENDING_VERIFICATION, [], $transaction->bill_no); + 1, PaymentMethodType::CASH, + $refundTotal, $request->input('amount'), 1, + $transaction->original_currency_id, $transaction->currency_rate, + 0, 0, null, ApprovalStatus::PENDING_VERIFICATION, [], $transaction->bill_no); $transaction = $this->createsTransaction->execute($transaction, $object); diff --git a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingLogic.php index d9857548..d1f2286e 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingLogic.php @@ -18,7 +18,7 @@ class FetchBookingLogic extends AbstractControllerLogic */ protected function notification():array { return [ - 'title' => 'Retrieved Address', + 'title' => 'Retrieved Booking', 'message' => 'You have successfully retrieved a Address' ]; } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php index 8effdb2f..e9a42101 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php @@ -14,6 +14,7 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Models\Booking; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic { @@ -40,6 +41,9 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic /** @var CalculatesBookingOutstanding */ private $calculatesBookingOutstanding; + /** @var CalculatesBookingRefundAmount */ + private $calculatesBookingRefundAmount; + /** * FetchBookingPaymentQuotationLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -47,12 +51,13 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit * @param CalculatesBookingOutstanding $calculatesBookingOutstanding */ - public function __construct(FetchesBookingQuotation $fetchBookingQuotation, GeneratesBookingQuotation $generatesBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, CalculatesBookingOutstanding $calculatesBookingOutstanding) + public function __construct(FetchesBookingQuotation $fetchBookingQuotation, GeneratesBookingQuotation $generatesBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingRefundAmount $calculatesBookingRefundAmount) { $this->fetchBookingQuotation = $fetchBookingQuotation; $this->generatesBookingQuotation = $generatesBookingQuotation; $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; + $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; } /** @@ -66,7 +71,7 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic $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); + $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 '.$booking->fixedCurrency->short_code.' '. number_format((float)$outstanding, 2, '.', ',')); //Voucherify 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/Bookings/Services/CalculatesBookingRefundAmount.php b/app/Classes/Modules/Bookings/Services/CalculatesBookingRefundAmount.php index 14a4a9ef..239545af 100644 --- a/app/Classes/Modules/Bookings/Services/CalculatesBookingRefundAmount.php +++ b/app/Classes/Modules/Bookings/Services/CalculatesBookingRefundAmount.php @@ -2,19 +2,30 @@ namespace App\Classes\Modules\Bookings\Services; - use App\Classes\ValueObjects\Constants\ApprovalStatus; -use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; -use Carbon\Carbon; class CalculatesBookingRefundAmount { + public function execute(Booking $booking, int $type, ?string $payment_reference = null): float + { + $refundAmounts = $booking->transactions()->payments()->get()->map(function ($payment) use ($type) { + return $this->calculateRefundAmount($payment, $type); + }); - public function execute(Booking $booking, int $type, ?string $payment_reference = NULL){ - return $type === 1 ? - $booking->transactions()->refunds($payment_reference) - ->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total') : $booking->transactions()->refunds($payment_reference)->sum('original_amount'); + $totalRefundAmount = $refundAmounts->sum(); + + return $totalRefundAmount; } -} \ No newline at end of file + public function calculateRefundAmount($payment, int $type): float + { + $refundTransactions = $payment->transactions()->refunds()->whereIn('status', [ApprovalStatus::APPROVED]); + + if ($type === 1) { + return $refundTransactions->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total'); + } + + return $refundTransactions->sum('original_amount'); + } +} 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/Exports/Services/ExportsImportedInvoiceMappeds.php b/app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php index e629675f..ead22543 100644 --- a/app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php +++ b/app/Classes/Modules/Exports/Services/ExportsImportedInvoiceMappeds.php @@ -59,7 +59,6 @@ class ExportsImportedInvoiceMappeds implements FromQuery, WithHeadings, WithHead */ public function map($transaction): array { - // dd($transaction); $this->count += 1; $data = $transaction->data; return [ diff --git a/app/Classes/Modules/Exports/Services/ExportsImportedReceiptMappeds.php b/app/Classes/Modules/Exports/Services/ExportsImportedReceiptMappeds.php new file mode 100644 index 00000000..efe95fd3 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsImportedReceiptMappeds.php @@ -0,0 +1,86 @@ +dateTime = $request->input('date').' '.$request->input('time'); + $this->count = 0; + } + + public function headings(): array + { + return [ + 'Check', + 'Doc No', + 'Doc Date', + 'Debtor Code', + 'Company Name', + 'Description', + 'Payment Amount', + 'Created User', + 'Curr.', + 'To Home Rate', + 'Local Payment Amount', + 'Cancelled', + 'Mapped Status', + 'Mapped Reference No', + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + return TransactionMappingLog::where('imported_date', $this->dateTime); + } + + /** + * @param Transaction $transaction + * + * @return array + */ + public function map($transaction): array + { + $this->count += 1; + $data = $transaction->data; + return [ + $this->count, + Arr::get($data,'doc_no'), + Arr::get($data,'doc_date'), + Arr::get($data,'debtor_code'), + Arr::get($data,'company_name'), + Arr::get($data,'description'), + Arr::get($data,'payment_amount'), + Arr::get($data,'created_user'), + Arr::get($data,'curr'), + Arr::get($data,'to_home_rate'), + Arr::get($data,'local_payment_amount'), + Arr::get($data,'cancelled'), + Arr::get($data,'2nd_doc_no'), + Arr::get($data,'mapped_status'), + Arr::get($data,'mapped_result_reference'), + ]; + + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php index a7041b1c..85df0be8 100644 --- a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php @@ -108,13 +108,13 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading '500-0000', 'CIEF' ]; - } else { + } elseif ($statementTransactionOwner->owner_id) { $row = (App()->make(ListShippingPortalTransactions::class))->execute([ 'id' => $statementTransactionOwner->owner_id, 'with_company' => true, ]); - if (empty($row) || $row[0]['status'] != 'success') { + if (!empty($row) && $row[0]['status'] == 'success') { $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Fetch Shipping Transaction Fail ' . json_encode([ 'id' => $statementTransactionOwner->owner_id, 'with_company' => true, @@ -125,46 +125,46 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Shipping Portal Respnose ' . json_encode($row) . PHP_EOL; file_put_contents($errorFilePath, $textToAppend, FILE_APPEND); - Log::info('Error in Exports Invoice Transactions ' . $this->counter); + $row = $row[0]; return [ - 'Transaction Not Found', - $transaction->posting_date->format('m/d/Y H:m'), - $transaction->transaction_description.' - '.$transaction->transaction_description_2, - $statementTransactionOwner->system, + '<>', + Carbon::parse($row['created_at'])->format('m/d/Y H:m'), + $row['debtor_code'], + $row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['marking'], '', + 'MYR', + $row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['bill_no'], + $row['type'] === ShippingTransactionType::PAYMENT ? '' : 'W1', + $row['type'] === ShippingTransactionType::PAYMENT ? 'PLEASE REFER TO THE ATTACHED APPENDIX REF `' . $row['order_reference'] : 'CREDIT SALES', '', - '', - '', - '', - '', - 0, - $transaction->amount, - '', - '', - '', - '' + 1, + round($row['amount'], 2), + '500-0000', + 'CIEF' ]; + } - - $row = $row[0]; - - return [ - '<>', - Carbon::parse($row['updated_at'])->format('m/d/Y H:m'), - $row['debtor_code'], - $row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['marking'], - '', - 'MYR', - $row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['bill_no'], - $row['type'] === ShippingTransactionType::PAYMENT ? '' : 'W1', - $row['type'] === ShippingTransactionType::PAYMENT ? 'PLEASE REFER TO THE ATTACHED APPENDIX REF `' . $row['order_reference'] : 'CREDIT SALES', - '', - 1, - round($row['amount'], 2), - '500-0000', - 'CIEF' - ]; } + + return [ + 'Transaction Not Found', + $transaction->posting_date->format('m/d/Y H:m'), + $transaction->transaction_description.' - '.$transaction->transaction_description_2, + $statementTransactionOwner->system, + '', + '', + '', + '', + '', + '', + 0, + $transaction->amount, + '', + '', + '', + '' + ]; } + } 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/CreateSupplierTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php index 5dd7ab82..ca0f77fd 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php @@ -3,6 +3,7 @@ namespace App\Classes\Modules\Transactions\ControllersLogic; +use App\Classes\Exceptions\MalformedRequestException; use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor; use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Models\Document; @@ -18,6 +19,7 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Companies\Services\FetchesCompany; use App\Classes\Modules\Documents\Services\CreatesDocument; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; +use App\Classes\Modules\Transactions\Services\FetchesTransaction; class CreateSupplierTransactionLogic extends AbstractControllerLogic { @@ -48,6 +50,9 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic /** @var GeneratesTransactionBillNumber */ private $generatesTransactionBillNumber; + /** @var FetchesTransaction */ + private $fetchesTransaction; + /** * CreateSupplierTransactionLogic constructor. @@ -56,14 +61,16 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic * @param CreatesDocument $createsDocument * @param CreatesFiles $createsFile * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber + * @param FetchesTransaction $fetchesTransaction */ - public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber) + public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber, FetchesTransaction $fetchesTransaction) { $this->fetchesCompany = $fetchesCompany; $this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor; $this->createsDocument = $createsDocument; $this->createsFile = $createsFile; $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->fetchesTransaction = $fetchesTransaction; } public function logic(Request $request) : JsonResponse @@ -75,6 +82,23 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic $payments = $request->input('payments'); + // todo-refund: activate this for partial refund + foreach($payments as $payment){ + $payment = $this->fetchesTransaction->execute(['id' => $payment['id']]); + + $pendingRefundRequest = $payment->transactions()->refunds()->where('status', ApprovalStatus::PENDING_VERIFICATION)->first(); + + if ($pendingRefundRequest) { + throw new MalformedRequestException('Unable to create supplier order for pending refund request payment'); + } + + $totalRefund = $payment->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->sum('original_amount'); + + if ($payment->original_amount - $totalRefund <= 0) { + throw new MalformedRequestException('Unable to create supplier order for fully refunded payment'); + } + } + $this->createSupplierTransactionProcessor->execute($supplier, $rate, $payments); if(!count($this->createSupplierTransactionProcessor->getBills())) return $this->response([]); 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/ControllersLogic/UpdateRefundTransactionStatusLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php index b86e4878..09e4b102 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php @@ -13,6 +13,8 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use App\Classes\Modules\Wallets\Processors\CreditWalletProcessor; +use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount; +use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic @@ -43,6 +45,12 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic /** @var CreditWalletProcessor */ private $creditWalletProcessor; + /** @var CalculatesBookingPayableAmount */ + private $calculatesBookingPayableAmount; + + /** @var CalculatesBookingRefundAmount */ + private $calculatesBookingRefundAmount; + /** * CreatePaymentVerificationDocumentLogic constructor. * @param FetchesCompany $fetchesCompany @@ -50,14 +58,18 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic * @param UpdatesTransactionStatus $updatesTransactionStatus * @param DeletesDocument $deletesDocument * @param CreditWalletProcessor $creditWalletProcessor + * @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount + * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount */ - public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor) + public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount) { $this->fetchesCompany = $fetchesCompany; $this->fetchesTransaction = $fetchesTransaction; $this->updatesTransactionStatus = $updatesTransactionStatus; $this->deletesDocument = $deletesDocument; $this->creditWalletProcessor = $creditWalletProcessor; + $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount; + $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; } /** @@ -67,19 +79,24 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]); + $refundTransaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]); - $transaction = $this->updatesTransactionStatus->execute($transaction, $request->input('status')); + $refundTransaction = $this->updatesTransactionStatus->execute($refundTransaction, $request->route('status')); - $booking = $transaction->owner->owner; + $paymentTransaction = $refundTransaction->owner; - $reference = 'Credit Voucher for Overpaid for Ref. '.$booking->marking; + $booking = $paymentTransaction->owner; - if ($transaction->status == ApprovalStatus::APPROVED) { - $this->creditWalletProcessor->execute($booking->company, $transaction->type, $transaction->amount, $reference); + $reference = $refundTransaction->amount == $paymentTransaction->amount ? 'Fully Refund for Ref. ' . $booking->marking : 'Partially Refund for Ref. ' . $booking->marking; + + if ($refundTransaction->status == ApprovalStatus::APPROVED) { + $this->creditWalletProcessor->execute($booking->company, $refundTransaction->type, $refundTransaction->amount, $reference); } - + $paidAmount = $paymentTransaction->original_amount - $this->calculatesBookingRefundAmount->calculateRefundAmount($paymentTransaction, $booking->fix_currency_id); + if (!$paidAmount > 0) { + $this->updatesTransactionStatus->execute($paymentTransaction, ApprovalStatus::REFUNDED); + } return $this->response([]); } diff --git a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php index 9b944a5b..b514b404 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php @@ -81,15 +81,19 @@ class CreateSupplierTransactionProcessor if($payment->status !== ApprovalStatus::APPROVED) continue; + $totalRefund = $payment->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->sum('original_amount'); + + $original_amount_after_refund = $payment->original_amount - $totalRefund; + $this->updatesTransactionStatus->execute($payment, ApprovalStatus::COMPLETED); $billNumber = $this->generatesTransactionBillNumber->execute('SPLR-'); $constant = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $supplier->id)->first(); - $serviceCharge = $this->calculatesTransactionServiceCharge->execute($payment->original_amount, $rate, $constant); + $serviceCharge = $this->calculatesTransactionServiceCharge->execute($original_amount_after_refund, $rate, $constant); $object = new TransactionObject($billNumber, TransactionType::BILL, $supplier->id, 1, $supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH, - $payment->original_amount * (1 / $rate), $payment->original_amount, 1, $payment->original_currency_id, + $original_amount_after_refund * (1 / $rate), $original_amount_after_refund, 1, $payment->original_currency_id, $rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_SUBMISSION); /** @var Transaction $billTransaction */ @@ -101,7 +105,7 @@ class CreateSupplierTransactionProcessor $transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant); $object = new TransactionObject($transferFeeNumber, TransactionType::TRANSFER_FEE, 1, $supplier->id, $supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH, - $payment->original_amount, $payment->original_amount, $payment->original_currency_id, $payment->original_currency_id, + $original_amount_after_refund, $original_amount_after_refund, $payment->original_currency_id, $payment->original_currency_id, 1, 0, $transferFee, null, ApprovalStatus::PENDING_VERIFICATION); $this->pushTransferFee($this->createsTransaction->execute($billTransaction, $object)); 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/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php index 4b820eb1..30e7fe12 100644 --- a/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php +++ b/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php @@ -44,10 +44,16 @@ class ValidateVoucherLogic extends AbstractControllerLogic { $booking = Booking::find($request->input('itemId')); $employee = $booking->company->employees()->first(); + $amount = $this->floatvalue($request->input('amount')); - $validateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject($booking->company_id, $request->input('voucherCode'), $request->input('amount'), $employee); + $validateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject($booking->company_id, $request->input('voucherCode'), $amount, $employee); $result = $this->validatesVoucherifyVoucher->execute($validateVoucherifyVoucherObject); return $this->response(['data' => $result]); } + private function floatvalue($val){ + $val = str_replace(",",".",$val); + $val = preg_replace('/\.(?=.*\.)/', '', $val); + return floatval($val); + } } diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php index f624b3cc..d4fd1e7b 100644 --- a/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php @@ -3,6 +3,7 @@ namespace App\Classes\Modules\Vouchers\DataTransferObjects; use App\Classes\General\Interfaces\DataTransferObject; +use Carbon\Carbon; use DateTime; use Illuminate\Support\Facades\Log; @@ -86,7 +87,8 @@ class VoucherObject implements DataTransferObject { try { if(!$this->startDate) return null; - $dateTime = new DateTime($this->startDate); + // $dateTime = new DateTime($this->startDate); + $dateTime = Carbon::parse($this->startDate)->tz('Asia/Kuala_Lumpur'); return $dateTime; } catch (\Exception $e) { Log::error($e); @@ -101,7 +103,7 @@ class VoucherObject implements DataTransferObject { try { if(!$this->endDate) return null; - $dateTime = new DateTime($this->endDate); + $dateTime = Carbon::parse($this->endDate)->tz('Asia/Kuala_Lumpur'); return $dateTime; } catch (\Exception $e) { Log::error($e); diff --git a/app/Console/Commands/AutoFillPurchaseOrderCommand.php b/app/Console/Commands/AutoFillPurchaseOrderCommand.php new file mode 100644 index 00000000..65a6fa93 --- /dev/null +++ b/app/Console/Commands/AutoFillPurchaseOrderCommand.php @@ -0,0 +1,113 @@ +generatesPurchaseOrderProducts = $generatesPurchaseOrderProducts; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + // 5. If purchase order not fill up in 2 month, auto fill up it + $bookings = Booking::where('status', ApprovalStatus::APPROVED) + ->where('created_at', '<', now()->subDays(60)->endOfDay()) + ->whereHas('transactions', function($transaction) { + return $transaction->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + }) + ->whereDoesntHave('transactions', function($transaction){ + $transaction->where('type', TransactionType::PURCHASE_ORDER); + $transaction->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED]); + })->get(); + + foreach ($bookings as $booking) { + $po = Transaction::where('type', TransactionType::PURCHASE_ORDER) + ->where('status', ApprovalStatus::APPROVED)->where('issuer', $booking->company_id) + ->select('*', DB::raw('abs(amount - ' . $booking->fix_amount . ') as nearest_price'))->orderBy('nearest_price')->first(); + + + if (!$po) { + $po = Transaction::where('type', TransactionType::PURCHASE_ORDER) + ->where('status', ApprovalStatus::APPROVED)->select('*', DB::raw('abs(amount - ' . $booking->fix_amount . ') as nearest_price'))->orderBy('nearest_price')->first(); + } + + $products = $this->generatesPurchaseOrderProducts->execute($po, $booking->fix_amount); + + $deference = $booking->fix_amount - $products->sum('total'); + + if($deference > -150 && $deference < 150 && $deference != 0) { + + $products->push([ + 'description' => $deference < 0 ? 'Discount':'Shipping Fee', + 'quantity' => 1, + 'stockCode' => '', + 'total' => $deference, + 'unit_price' => $deference + ]); + } + + $billNumber = $this->generatesTransactionBillNumber->execute('XPO-'); + + $total = $products->sum('total'); + + $object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1, + 1, PaymentMethodType::CASH, + $total, $total, $booking->fix_currency_id, $booking->fix_currency_id, + 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products->toArray()); + + $this->createPurchaseOrderTransactionProcessor->execute($booking, $object); + } + } +} diff --git a/app/Console/Commands/ExpiredBookingCommand.php b/app/Console/Commands/ExpiredBookingCommand.php new file mode 100644 index 00000000..ca690c67 --- /dev/null +++ b/app/Console/Commands/ExpiredBookingCommand.php @@ -0,0 +1,98 @@ +updatesBookingStatus = $updatesBookingStatus; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + // 1. Cancel booking without payment & purchase order (1 month) + $bookings = Booking::where('status', ApprovalStatus::APPROVED) + ->where('created_at', '<', now()->subDays(30)->endOfDay()) + ->where(function ($query) { + $query->whereDoesntHave('transactions') + ->orWhereDoesntHave('transactions', function($transaction) { + return $transaction->where('type', TransactionType::PURCHASE_ORDER)->orWhere(function ($q) { + $q->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + }); + }); + })->get(); + + foreach ($bookings as $booking) { + $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED); + Log::info("Expired Booking without payment & purchase order, booking id: " . $booking->id); + $transactions = $booking->transactions; + + foreach ($transactions as $transaction) { + $transaction->status = ApprovalStatus::EXPIRED; + $transaction->save(); + Log::info("Expired Transaction id: {$transaction->id} from Booking id: {$booking->id}"); + } + } + + // 2. Cancel booking without payment but with purchase order (2 month) + $bookings = Booking::where('status', ApprovalStatus::APPROVED) + ->where('created_at', '<', now()->subDays(60)->endOfDay()) + ->where(function ($query) { + $query->whereDoesntHave('transactions', function($transaction) { + return $transaction->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + })->whereHas('transactions', function($transaction) { + return $transaction->where('type', TransactionType::PURCHASE_ORDER); + }); + })->get(); + + foreach ($bookings as $booking) { + $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED); + Log::info("Expired Booking without payment but with purchase order, booking id: " . $booking->id); + $transactions = $booking->transactions; + + foreach ($transactions as $transaction) { + $transaction->status = ApprovalStatus::EXPIRED; + $transaction->save(); + Log::info("Expired Transaction id: {$transaction->id} from Booking id: {$booking->id}"); + } + } + } +} diff --git a/app/Console/Commands/ExpiredRefundedBookingCommand.php b/app/Console/Commands/ExpiredRefundedBookingCommand.php new file mode 100644 index 00000000..db7e00a7 --- /dev/null +++ b/app/Console/Commands/ExpiredRefundedBookingCommand.php @@ -0,0 +1,162 @@ +updatesBookingStatus = $updatesBookingStatus; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createsTransaction = $createsTransaction; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + // 3. Cancel fully refunded payment & cancel booking + $transactions = Transaction::where('type', TransactionType::CREDIT_NOTE)->where('payment_reference', 'LIKE', "%refund%")->get(); + + foreach ($transactions as $transaction) { + // get the booking marking + $payment_reference = explode(" ", trim($transaction->payment_reference)); + // $marking = substr($transaction->payment_reference, -5); + $marking = trim(end($payment_reference)); + + if (!preg_match('/^[0-9]+$/', $marking)) { + $payment_reference = explode(".", trim($transaction->payment_reference)); + $marking = trim(end($payment_reference)); + } + + // for a special payment reference on transaction id: 140231 + if (!preg_match('/^[0-9]+$/', $marking)) { + $payment_reference = explode("No", trim($transaction->payment_reference)); + $marking = end($payment_reference); + } + + // for a special payment reference on transaction id: 152013 + if (!preg_match('/^[0-9]+$/', $marking)) { + $payment_reference = explode(" ", trim($transaction->payment_reference)); + $marking = end($payment_reference); + $marking = prev($payment_reference); + } + + if (preg_match('/^[0-9]+$/', $marking)) { + $booking = Booking::where('marking', $marking)->first(); + + if ($booking) { + $bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first(); + if (!$bookingPayment) { + $bookingPaymentCount = $booking->transactions()->payments()->count(); + if ($bookingPaymentCount > 1) { + Log::info("Credit note transaction id: {$transaction->id}, there are {$bookingPaymentCount} payment for the booking."); + foreach ($booking->transactions()->payments()->get() as $bp) { + if ($transaction->amount - $bp->amount < 0.01) { + $bookingPayment = $bp; + break; + } + } + } + + if (!$bookingPayment) { + $bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED, ApprovalStatus::REJECTED])->orderBy('id', 'DESC')->first(); + } + $status = ApprovalStatus::APPROVAL_STATUS_ID[$bookingPayment->status]; + Log::info("Credit note transaction id: {$transaction->id}, the payment for the booking is in status {$status}"); + } + $bookingPaymentAmount = $bookingPayment->amount; + // check if the booking is fully refund + $amountDifference = bcsub($transaction->amount, $bookingPaymentAmount, 7); + + if (abs($amountDifference) < 0.01) { + // rejecting booking payment transaction + // $bookingPayment->status = ApprovalStatus::REJECTED; + // $bookingPayment->save(); + + //expired booking + // $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED); + Log::info("Credit note transaction id: {$transaction->id} is fully refunded, the refunded amount was {$transaction->amount} the payment reference is: {$transaction->payment_reference}"); + // Log::info("Credit note transaction id: {$transaction->id}, Rejected Booking Transaction Payment id: {$bookingPayment->id}, the payment amount was {$bookingPayment->amount}"); + // Log::info("Credit note transaction id: {$transaction->id}, Expired Booking id: {$booking->id}"); + } else { + Log::info("Credit note transaction id: {$transaction->id} is not fully refunded, the refunded amount was {$transaction->amount}, the payment amount was {$bookingPayment->amount}, the payment reference is: {$transaction->payment_reference}"); + } + + $refund = $bookingPayment->transactions()->refunds()->where('amount', $transaction->amount)->where('status', ApprovalStatus::APPROVED)->first(); + + $bookingInWhiteForm = $bookingPayment->transactions()->bills()->first(); + + if ($refund) { + Log::info("Credit note transaction id: {$transaction->id}, already created same amount of refund transaction for same booking payment transaction"); + } + + if ($bookingInWhiteForm) { + Log::info("Credit note transaction id: {$transaction->id}, booking is in white form"); + } + + if (!$refund && !$bookingInWhiteForm) { + $billNumber = $this->generatesTransactionBillNumber->execute('RFD-'); + + $object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id, + 1, PaymentMethodType::CASH, + $transaction->amount, $transaction->amount * $bookingPayment->currency_rate, 1, + $bookingPayment->original_currency_id, $bookingPayment->currency_rate, + 0, 0, null, ApprovalStatus::APPROVED, [], $bookingPayment->bill_no); + + $transaction = $this->createsTransaction->execute($bookingPayment, $object); + } + } else { + Log::info("Credit note transaction id: {$transaction->id}, booking marking not found, the payment reference is: {$transaction->payment_reference}"); + } + } else { + Log::info("Credit note transaction id: {$transaction->id} does not have booking marking, the payment reference is: {$transaction->payment_reference}"); + } + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index d6557046..412ed70f 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -43,6 +43,14 @@ class Kernel extends ConsoleKernel ->hourly() ->appendOutputTo(storage_path().'/logs/delete-bulk-download-files.log') ->withoutOverlapping(); + + $schedule->command('booking:expired') + ->dailyAt('02:00') + ->withoutOverlapping(); + + $schedule->command('purchaseOrder:autoFill') + ->dailyAt('03:00') + ->withoutOverlapping(); } /** diff --git a/app/Http/Controllers/Accounting/HistoryImportedTransactionMappedController.php b/app/Http/Controllers/Accounting/HistoryImportedTransactionMappedController.php new file mode 100644 index 00000000..8b4a2cde --- /dev/null +++ b/app/Http/Controllers/Accounting/HistoryImportedTransactionMappedController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file 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/Exports/ExportCustomersToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php index 733c84fa..0f351c7d 100644 --- a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php +++ b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php @@ -18,6 +18,7 @@ use Maatwebsite\Excel\Excel; use App\Classes\Modules\Exports\Services\ExportsImportedInvoiceMappeds; use App\Models\TransactionMappingLog; use App\Classes\Modules\Exports\Services\ExportsReceiptTransactions; +use App\Classes\Modules\Exports\Services\ExportsImportedReceiptMappeds; class ExportCustomersToExcelController { @@ -91,4 +92,10 @@ class ExportCustomersToExcelController ob_end_clean(); return $response; } + + public function importedReceiptMapped(ExportsImportedReceiptMappeds $exportsImportedReceiptMappeds, Request $request) { + $response = $exportsImportedReceiptMappeds->download($request->input('fileName').'.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } } \ No newline at end of file diff --git a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php index 75953f2b..e4c35eb5 100644 --- a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php +++ b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php @@ -100,6 +100,7 @@ class ImportStatementInvoiceController TransactionMappingLog::create([ 'imported_date'=>$importDate, + 'type' => 'invoices', 'data'=>$row, ]); array_push($data, $row); diff --git a/app/Http/Controllers/Imports/ImportStatementReceiptsController.php b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php index a43f2dda..6c117642 100644 --- a/app/Http/Controllers/Imports/ImportStatementReceiptsController.php +++ b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php @@ -69,6 +69,7 @@ class ImportStatementReceiptsController TransactionMappingLog::create([ 'imported_date'=>$importDate, + 'type' => 'receipts', 'data'=>$row, ]); array_push($data, $row); 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..6803eec9 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. * @@ -34,7 +32,8 @@ class BookingResource extends JsonResource '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)), + // 'outstanding_amount' => floatval((App()->make(CalculatesBookingOutstanding::class))->execute($this->resource)) + 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), 'convertible_currency' => new CurrencyResource($this->convertibleCurrency), 'conversion_currency' => new CurrencyResource($this->conversionCurrency), @@ -60,7 +59,7 @@ class BookingResource extends JsonResource '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]); + $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]); 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/TransactionMappingLogResource.php b/app/Http/Resources/TransactionMappingLogResource.php new file mode 100644 index 00000000..954294f1 --- /dev/null +++ b/app/Http/Resources/TransactionMappingLogResource.php @@ -0,0 +1,23 @@ + $this->id, + 'imported_date' => $this->imported_date, + 'type' => $this->type + ]; + } +} diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index 27fd17ab..09967f4c 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; use Carbon\Carbon; @@ -43,8 +44,10 @@ class TransactionResource extends JsonResource '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())), + 'refunded_amount' => $this->booking ? floatval((App()->make(CalculatesBookingRefundAmount::class))->calculateRefundAmount($this->resource, $this->booking->fix_currency_id)) : null, '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'), + 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'), 'interval' => [ 'value' => $days->gt(Carbon::now()) ? '+' : '-', 'duration' => $days->diff(Carbon::now())->format('%d'), 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 @@ + 'array', diff --git a/config/logging.php b/config/logging.php index fb872693..d0d0a009 100644 --- a/config/logging.php +++ b/config/logging.php @@ -104,6 +104,10 @@ return [ 'path' => storage_path('logs/regenerateInvoice.log'), 'level' => 'info', ], + 'guzzleShippingPortal' => [ + 'driver' => 'errorlog', + 'level' => 'debug', + ], ], ]; diff --git a/database/migrations/2023_08_08_124848_create_job_results_table.php b/database/migrations/2023_08_08_124848_create_job_results_table.php new file mode 100644 index 00000000..ef448407 --- /dev/null +++ b/database/migrations/2023_08_08_124848_create_job_results_table.php @@ -0,0 +1,35 @@ +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/database/migrations/2023_12_26_152448_add_type_to_transaction_mapping_logs_table.php b/database/migrations/2023_12_26_152448_add_type_to_transaction_mapping_logs_table.php new file mode 100644 index 00000000..3927920a --- /dev/null +++ b/database/migrations/2023_12_26_152448_add_type_to_transaction_mapping_logs_table.php @@ -0,0 +1,40 @@ +string('type',50)->default('invoices')->after('imported_date'); + }); + + foreach (DB::table('transaction_mapping_logs')->get() as $key => $value) { + $data = json_decode($value->data); + DB::table('transaction_mapping_logs')->where('id',$value->id)->update([ + 'type' => (isset($data->description) ? 'receipts' : 'invoices') + ]); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('transaction_mapping_logs', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } +} diff --git a/resources/assets/vue/components/accounting/elements/HistoryImportedInvoices.vue b/resources/assets/vue/components/accounting/elements/HistoryImportedInvoices.vue new file mode 100644 index 00000000..d3d68cfb --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/HistoryImportedInvoices.vue @@ -0,0 +1,39 @@ + + + diff --git a/resources/assets/vue/components/accounting/elements/HistoryImportedReceipts.vue b/resources/assets/vue/components/accounting/elements/HistoryImportedReceipts.vue new file mode 100644 index 00000000..00138fa5 --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/HistoryImportedReceipts.vue @@ -0,0 +1,39 @@ + + + diff --git a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue index 0c193871..787f2f37 100644 --- a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue +++ b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue @@ -74,7 +74,7 @@
{{ owner.reference }} -
+
@@ -89,6 +89,21 @@ > + + + + + +
@@ -143,7 +158,7 @@ contentText="Are you sure you want to reject this mapping?" modalType="delete" class="text-center" - :apiRoute="route('api.accounting.statement_transaction.owner.status.update', item.id, 'reject')" + :apiRoute="route('api.accounting.statement_transaction.owner.status.update', item.owners.pending_verification[0].id, 'reject')" apiMethod="post" :section="section" > diff --git a/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue b/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue index f10f9181..185da370 100644 --- a/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue +++ b/resources/assets/vue/components/accounting/sections/ImportedInvoiceMappedComponent.vue @@ -98,21 +98,17 @@ }, methods: { appendComponentTitle() { - this.componentTitle = this.section == 'importInvoiceMapping' ? 'Imported Invoices Mapped' : 'Imported Receipts Mapped'; + this.componentTitle = 'Imported Invoices Mapped'; }, appendComponentTableHeader() { - if (this.section == 'importInvoiceMapping') { - this.tableHeaders = ['No','Doc No','Date','Debtor Code','Debtor Name','Shipping Info','Net Total','Cancelled','Mapped Status','Mapped Reference No','Payment Received Date']; - } else { - this.tableHeaders = ['No','OR No','Date','Creditor Code','Creditor Name','Shipping Info','Net Total','Cancelled','Mapped Status','Mapped Reference No']; - } + this.tableHeaders = ['No','Doc No','Date','Debtor Code','Debtor Name','Shipping Info','Net Total','Cancelled','Mapped Status','Mapped Reference No','Payment Received Date']; }, importInvoice(){ this.isLoading = true; this.parameters = { files: this.files }; - this.submit(this.route('api.'+(this.section == 'importInvoiceMapping' ? 'import_invoices' : 'import_receipts')+'.upload'), 'post', this.section, true, false); + this.submit(this.route('api.import_invoices.upload'), 'post', this.section, true, false); }, successHandler(response){ @@ -128,7 +124,7 @@ downloadInvoiceMapped() { var arrDateTime = this.importedDate.split(" "); - const fileName = this.section == 'importInvoiceMapping' ? 'InvoiceMapped' : 'ReceiptMapped'; + const fileName = 'importInvoiceMapping'; window.open(this.route('importedInvoiceMapped.export')+'?date='+arrDateTime[0]+'&time='+arrDateTime[1]+'&fileName='+fileName, '_blank'); }, diff --git a/resources/assets/vue/components/accounting/sections/ImportedReceiptMappedComponent.vue b/resources/assets/vue/components/accounting/sections/ImportedReceiptMappedComponent.vue new file mode 100644 index 00000000..25db762e --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/ImportedReceiptMappedComponent.vue @@ -0,0 +1,138 @@ + + + \ No newline at end of file diff --git a/resources/assets/vue/components/accounting/sections/ReportTransactionsMappedComponent.vue b/resources/assets/vue/components/accounting/sections/ReportTransactionsMappedComponent.vue index e3d4e0d2..86da623c 100644 --- a/resources/assets/vue/components/accounting/sections/ReportTransactionsMappedComponent.vue +++ b/resources/assets/vue/components/accounting/sections/ReportTransactionsMappedComponent.vue @@ -3,19 +3,74 @@
-
-
-
-
Mapped Report
+
+ +
+
+
+
Mapped Report
+
+
+
+
+
History Imported Invoices Report
+
+
+
+
+
History Imported Receipts Report
+
+
-
+
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
+
+ + + + +
+
+ + + + +
+
+
+
Mapped Report
+
+
+
+
@@ -35,7 +90,7 @@
- + @@ -44,22 +99,175 @@
+ + +
+
+
+
+
+
+
+ + + + +
+
+ + + + +
+
+
+
History Imported Invoices Report
+
+
+
+
+
+
+
+
Date
+
+
+
+ + + + +
+
+
+
+ + +
+
+
+
+
+
+
+ + + + +
+
+ + + + +
+
+
+
History Imported Receipts Report
+
+
+
+
+
+
+
+
Date
+
+
+
+ + + + +
+
+
+
\ No newline at end of file + }, + mixins: [FormHandler, ModalFormHandler] +} + diff --git a/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue b/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue index 42d5682e..24ec68a6 100644 --- a/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue +++ b/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue @@ -115,7 +115,7 @@ approveRefund(status){ this.isLoading = true; this.parameters.status = status; - this.submit(this.route('api.transaction.refund.status.update', this.data.id), 'put', 'listRefundTransactionSection', true, true); + this.submit(this.route('api.transaction.refund.status.update', this.data.id, status), 'put', 'listRefundTransactionSection', true, true); }, }, mixins: [componentHandler, staticFormHandler] diff --git a/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue b/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue index e4877e96..309f2b9b 100644 --- a/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue +++ b/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue @@ -56,6 +56,16 @@ {{item.original_currency.short_code}}
+
+
+
+
Refunded Amount
+
+ {{(Math.round((totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}} +
+
+
+
@@ -95,7 +105,7 @@
Amount
- {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}} + {{(Math.round((item.original_amount - totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
@@ -124,11 +134,20 @@ active: false, } }, + computed: { + totalRefunds() { + var TotalRequestedRefund = 0; + this.data.transaction_refunds.forEach(function(refunds) { + TotalRequestedRefund += refunds.status === 2 ? refunds.original_amount : 0; + }); + return TotalRequestedRefund; + } + }, methods: { activate(){ this.active = !this.active; this.$emit('input', this.item) - } + }, }, mixins: [componentHandler] } diff --git a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue index 9020fbce..10246745 100644 --- a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue +++ b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue @@ -252,21 +252,21 @@
- +
Apply a voucher -
+
{{ voucherCodeFailedReason }} Voucher applied
- +
diff --git a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue index 9a1bf774..9b8045f9 100644 --- a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue @@ -240,7 +240,11 @@ }, watch: { 'data': function () { - this.products = this.data.purchase_order.details + if (this.data && this.data.purchase_order && this.data.purchase_order.details) { + this.products = this.data.purchase_order.details; + } else { + this.products = []; + } } }, methods: { diff --git a/resources/assets/vue/components/bookings/forms/SupplierPlaceOrderFormComponent.vue b/resources/assets/vue/components/bookings/forms/SupplierPlaceOrderFormComponent.vue index 4ec71edd..0f27eb95 100644 --- a/resources/assets/vue/components/bookings/forms/SupplierPlaceOrderFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/SupplierPlaceOrderFormComponent.vue @@ -115,7 +115,12 @@ computed: { total(){ return this.payments.reduce(function (total, currentValue) { - return total + currentValue.original_amount; + return total + currentValue.original_amount - currentValue.transaction_refunds.reduce(function (totalRefund, refundTransaction) { + if (refundTransaction.status === 2) { + return totalRefund + refundTransaction.original_amount; + } + return totalRefund; + }, 0); }, 0); }, 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..fa345d58 100644 --- a/resources/assets/vue/components/bookings/sections/SupplierPendingOrdersSectionComponent.vue +++ b/resources/assets/vue/components/bookings/sections/SupplierPendingOrdersSectionComponent.vue @@ -122,6 +122,11 @@ +
@@ -165,7 +170,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){ @@ -195,6 +200,8 @@ }, updateList(){ + // todo-refund: activate this for partial refund + // this.$refs.pendingOrdersList.updateFilters({per_page: 10000, status: 2, type: 1, original_currency_id_in: [this.selectedCurrency.id], transaction_service_id: this.selectedService.id, is_not_fully_refunded: true}); this.$refs.pendingOrdersList.updateFilters({per_page: 10000, status: 2, type: 1, original_currency_id_in: [this.selectedCurrency.id], transaction_service_id: this.selectedService.id}); this.selectedSupplier.status = false; @@ -210,4 +217,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/resources/views/pages/pdfs/purchase_order_table.blade.php b/resources/views/pages/pdfs/purchase_order_table.blade.php index 20fac9b8..cc47100e 100644 --- a/resources/views/pages/pdfs/purchase_order_table.blade.php +++ b/resources/views/pages/pdfs/purchase_order_table.blade.php @@ -15,11 +15,12 @@ $subtotal = "0"; $voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 2) : "0"; $displayedSubtotal = 0; + $currency_id = $transaction->owner->fix_currency_id; @endphp @foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail) @php - $exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 7); + $exactUnitPrice = ($currency_id) === 1 ? $transaction_detail->price : bcdiv($transaction_detail->price, $transaction->currency_rate, 7); $displayUnitPrice = round($exactUnitPrice, 2); $itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5); $displayedItemTotal = round(bcmul($displayUnitPrice, $transaction_detail->quantity, 7), 2); diff --git a/routes/accounting.php b/routes/accounting.php index 4fe0a1ad..ddeecb21 100644 --- a/routes/accounting.php +++ b/routes/accounting.php @@ -16,4 +16,7 @@ Route::group(['prefix' => 'accounting', 'as' => 'accounting.', 'namespace' => 'A Route::post('/owner/group-approve', 'GroupApproveStatementTransactionController@approve')->name('owner.groupApprove'); Route::post('/{id}/owner/{status}', 'UpdateStatementTransactionStatusController@update')->where('status', 'approve|reject')->name('owner.status.update'); }); + + Route::get('history/imported', 'HistoryImportedTransactionMappedController@getImported')->name('history.imported'); + }); 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/transaction.php b/routes/transaction.php index 1996b012..d20d160d 100644 --- a/routes/transaction.php +++ b/routes/transaction.php @@ -12,7 +12,7 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' => route::post('{id}/bill/verification', 'CreatePaymentProofDocumentController@verify')->name('bill.verification'); route::post('{id}/bill/pay', 'CreatePaymentProofDocumentController@pay')->name('bill.pay'); Route::put('/{id}/bill/{status}', 'UpdatePaymentTransactionStatusController@update')->where('status', 'pending|complete')->name('bill.status'); - Route::put('/{id}/refund/status/update', 'UpdateRefundTransactionStatusController@update')->name('refund.status.update'); + Route::put('/{id}/refund/status/update/{status}', 'UpdateRefundTransactionStatusController@update')->name('refund.status.update'); route::delete('{id}/bill/delete', 'DeletePaymentProofDocumentController@delete')->name('bill.delete'); diff --git a/routes/web.php b/routes/web.php index 41b1f7dd..fd50e085 100644 --- a/routes/web.php +++ b/routes/web.php @@ -96,6 +96,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'); @@ -116,7 +122,7 @@ Route::get('/transfer/{marking}', function ($marking) { return view('pages.bookings.profile', ['marking' => $marking]); })->name('booking.details'); -Route::get('/transfer/{marking}/latest-invoice', function ($marking) { +Route::get('/transfer/{marking}/latest/{document_type}', function ($marking, $document_type) { $booking= Booking::where('marking', $marking)->first(); $purchaseOrder = $booking->transactions() @@ -130,7 +136,23 @@ Route::get('/transfer/{marking}/latest-invoice', function ($marking) { $supplier = Company::where('id', $transaction->receiver)->first(); - $lowercaseDocumentType = strtolower(DocumentType::INVOICE); + $lowercaseDocumentType = null; + switch ($document_type) { + case 'po': + $lowercaseDocumentType = DocumentType::PURCHASE_ORDER; + break; + case 'do': + $lowercaseDocumentType = DocumentType::DELIVER_ORDER; + break; + case 'sdo': + $lowercaseDocumentType = DocumentType::SUPPLIER_DELIVER_ORDER; + break; + default: + $lowercaseDocumentType = DocumentType::INVOICE; + break; + } + + $lowercaseDocumentType = strtolower($lowercaseDocumentType); $voucherRedemption = $transaction->voucherRedemption; @@ -267,6 +289,7 @@ Route::get('/export/booking-transactions', 'Exports\ExportCustomersToExcelContro Route::get('/export/invoice-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@invoiceTransactions')->name('invoiceTransactions.export'); Route::get('/export/receipt-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@receiptTransactions')->name('receiptTransactions.export'); Route::get('/export/imported-invoice-mapped', 'Exports\ExportCustomersToExcelController@importedInvoiceMapped')->name('importedInvoiceMapped.export'); +Route::get('/export/imported-receipt-mapped', 'Exports\ExportCustomersToExcelController@importedReceiptMapped')->name('importedReceiptMapped.export'); Route::get('/products', function (\App\Classes\Modules\Exports\Services\ExportsProducts $exportsProducts) { $bookings = Booking::where(function($query){ @@ -817,13 +840,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); @@ -846,4 +869,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');