diff --git a/.env.example b/.env.example index 91c8e15c..428d747b 100644 --- a/.env.example +++ b/.env.example @@ -58,6 +58,13 @@ BILLPLZ_X_SIGNATURE_KEY="S-HdAU6QDubUrMErxJ-PCpgw" BILLPLZ_COLLECTION_ID="t0cggbdd" BILLPLZ_WALLET_COLLECTION_ID="t0cggbdd" + PERFEXCRM_BASE_URL="" PERFEXCRM_API_KEY="" PERFEXCRM_IS_ENABLED="false" + + +VOUCHERIFY_APPLICATION_ID="" +VOUCHERIFY_CLIENT_SECRET_KEY="" +VOUCHERIFY_VERSION="v2018-08-01" +VOUCHERIFY_URL="https://as1.api.voucherify.io" diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..ae145ac7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [{ + "name": "Listen for XDebug on Docker", + "type": "php", + "request": "launch", + "port": 9002, + "pathMappings": { + "/var/www/html": "${workspaceFolder}", + }, + "log": true + }] +} diff --git a/app/Classes/General/Eloquent/Filters/Code.php b/app/Classes/General/Eloquent/Filters/Code.php new file mode 100644 index 00000000..dc98ed43 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/Code.php @@ -0,0 +1,20 @@ +where('code', '=', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/DateIn.php b/app/Classes/General/Eloquent/Filters/DateIn.php new file mode 100644 index 00000000..b1704eb4 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DateIn.php @@ -0,0 +1,19 @@ +whereIn('date', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/HasAccountStatementId.php b/app/Classes/General/Eloquent/Filters/HasAccountStatementId.php new file mode 100644 index 00000000..05cfe9d8 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasAccountStatementId.php @@ -0,0 +1,22 @@ +whereHas('statementTransaction', function ($query) use ($value) { + $query->where('account_statement_id', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/HasActiveReward.php b/app/Classes/General/Eloquent/Filters/HasActiveReward.php new file mode 100644 index 00000000..5770ca6e --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasActiveReward.php @@ -0,0 +1,44 @@ +type, RoleTypes::ADMIN_ROLES)){ + // $userId = $value !== 1 ? $value : Auth::user()->id; + $userId = $value; + return $builder->where('user_id', $userId) + ->where(function ($query) { + $query->whereHas('reward', function ($subquery) { + $subquery->where('is_active', true); + }) + ->orWhereDoesntHave('reward'); + }) + ->whereDoesntHave('voucher.redemptions.transaction.booking.company.employees', function ($query) use ($userId) { + $query->where('user_id', $userId); + }); + } + else{ + return $builder->where('user_id', Auth::user()->id) + ->where(function ($query) { + $query->whereHas('reward', function ($subquery) { + $subquery->where('is_active', true); + }) + ->orWhereDoesntHave('reward'); + }) + ->whereDoesntHave('voucher.redemptions.transaction.owner'); + } + } +} diff --git a/app/Classes/General/Eloquent/Filters/HasUsedVoucher.php b/app/Classes/General/Eloquent/Filters/HasUsedVoucher.php new file mode 100644 index 00000000..fe6cdd92 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasUsedVoucher.php @@ -0,0 +1,30 @@ +where('user_id', Auth::user()->id) + ->where(function ($query) { + $query->whereHas('voucher', function ($subquery) { + $subquery->whereHas('redemptions', function ($subsubquery) { + $subsubquery->whereHas('transaction', function ($subsubsubquery) { + $subsubsubquery->whereHas('owner'); + }); + }); + }); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/HasUsedVoucherForAdmin.php b/app/Classes/General/Eloquent/Filters/HasUsedVoucherForAdmin.php new file mode 100644 index 00000000..83b38ccb --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasUsedVoucherForAdmin.php @@ -0,0 +1,38 @@ +id; + + return $builder->where('user_id', $userId) + ->where(function ($query) use ($userId){ + $query->whereHas('voucher', function ($subquery) use ($userId){ + $subquery->whereHas('redemptions', function ($subsubquery) use ($userId){ + $subsubquery->whereHas('transaction', function ($subsubsubquery) use ($userId){ + $subsubsubquery->whereHas('booking', function ($s4query) use ($userId){ + $s4query->whereHas('company', function ($s5query) use ($userId) { + $s5query->whereHas('employees', function ($s6query) use ($userId){ + $s6query->where('user_id', $userId); + }); + }); + }); + }); + }); + }); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/IsActive.php b/app/Classes/General/Eloquent/Filters/IsActive.php new file mode 100644 index 00000000..c18d5d85 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsActive.php @@ -0,0 +1,20 @@ +where('is_active', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/IsMapped.php b/app/Classes/General/Eloquent/Filters/IsMapped.php new file mode 100644 index 00000000..12282d3b --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsMapped.php @@ -0,0 +1,20 @@ +whereHas('owners') : $builder->whereDoesntHave('owners'); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php b/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php new file mode 100644 index 00000000..c9c92438 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php @@ -0,0 +1,24 @@ +withCount(['owners' => function ($query){ + $query->where('status', ApprovalStatus::PENDING_VERIFICATION); + }]); + return $value ? $query->having('owners_count', '>', 1) : $query->having('owners_count', '=', 1); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/MaxAmount.php b/app/Classes/General/Eloquent/Filters/MaxAmount.php new file mode 100644 index 00000000..3a0476c8 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/MaxAmount.php @@ -0,0 +1,20 @@ +where('amount', '<=', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/MinAmount.php b/app/Classes/General/Eloquent/Filters/MinAmount.php new file mode 100644 index 00000000..52b72636 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/MinAmount.php @@ -0,0 +1,20 @@ +where('amount', '>=', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/OwnerId.php b/app/Classes/General/Eloquent/Filters/OwnerId.php new file mode 100644 index 00000000..eac7e32d --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/OwnerId.php @@ -0,0 +1,20 @@ +where('owner_id', $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/PayFor.php b/app/Classes/General/Eloquent/Filters/PayFor.php new file mode 100644 index 00000000..b8a2ba75 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/PayFor.php @@ -0,0 +1,19 @@ +where('pay_for', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/PayForIn.php b/app/Classes/General/Eloquent/Filters/PayForIn.php new file mode 100644 index 00000000..3fe98f00 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/PayForIn.php @@ -0,0 +1,19 @@ +whereIn('pay_for', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/RandomName.php b/app/Classes/General/Eloquent/Filters/RandomName.php new file mode 100644 index 00000000..54024e96 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/RandomName.php @@ -0,0 +1,20 @@ +where('is_active', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionAccountId.php b/app/Classes/General/Eloquent/Filters/StatementTransactionAccountId.php new file mode 100644 index 00000000..b07bede9 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionAccountId.php @@ -0,0 +1,22 @@ +whereHas('account', function ($query) use ($value) { + $query->where('statement_accounts.id', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php new file mode 100644 index 00000000..f2dfe546 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->whereIn('statement_transaction_owners.status', $value); + }); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerTypeIn.php b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerTypeIn.php new file mode 100644 index 00000000..e23d0bcc --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerTypeIn.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->whereIn('type', $value); + }); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/TransactionId.php b/app/Classes/General/Eloquent/Filters/TransactionId.php new file mode 100644 index 00000000..b764c8fb --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/TransactionId.php @@ -0,0 +1,20 @@ +where('transaction_id', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/UserId.php b/app/Classes/General/Eloquent/Filters/UserId.php new file mode 100644 index 00000000..7b8cb9e9 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/UserId.php @@ -0,0 +1,20 @@ +where('user_id', $value); + } + +} diff --git a/app/Classes/General/Helper.php b/app/Classes/General/Helper.php index 2474624e..ac89540b 100644 --- a/app/Classes/General/Helper.php +++ b/app/Classes/General/Helper.php @@ -29,8 +29,8 @@ class Helper * @param $log * @return array */ - static function debugLoggerForPerfexCRM($log){ - if($log){ + static function debugLogger($log){ + if($log && isset($log['message'])){ $message = $log['message']; $substring = 'No data were found'; if (isset($message)) { diff --git a/app/Classes/General/Interfaces/Voucherifiable.php b/app/Classes/General/Interfaces/Voucherifiable.php new file mode 100644 index 00000000..b42427e1 --- /dev/null +++ b/app/Classes/General/Interfaces/Voucherifiable.php @@ -0,0 +1,11 @@ +make(CreateBankStatementTransactionOwnersProcessor::class))->execute(); + } + + public function delay($delay) + { + // Add delay in seconds to the job + $this->delay = $delay; + return $this; + } +} diff --git a/app/Classes/Jobs/SendUserPaymentProofUploadedEmail.php b/app/Classes/Jobs/SendUserPaymentProofUploadedEmail.php new file mode 100644 index 00000000..072dfa15 --- /dev/null +++ b/app/Classes/Jobs/SendUserPaymentProofUploadedEmail.php @@ -0,0 +1,47 @@ +user = $user; + $this->booking = $booking; + $this->file = $file; + } + + + public function handle() + { + $this->user->notify(new PaymentProofUploadedEmail($this->user, $this->booking, $this->file)); + } +} diff --git a/app/Classes/Jobs/UpdatePerfexCRMInvoice.php b/app/Classes/Jobs/UpdatePerfexCRMInvoice.php index ee395033..e9094f84 100644 --- a/app/Classes/Jobs/UpdatePerfexCRMInvoice.php +++ b/app/Classes/Jobs/UpdatePerfexCRMInvoice.php @@ -66,7 +66,7 @@ class UpdatePerfexCRMInvoice implements ShouldQueue } else { // Log::error(json_encode('UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed')); $log['message'] = 'UpdatePerfexCRMInvoice CreatePerfexCRMInvoiceProcessor failed'; - Helper::debugLoggerForPerfexCRM($log); + Helper::debugLogger($log); } } else{ diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ApproveDuplicateBankStatementDetailsStatusLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ApproveDuplicateBankStatementDetailsStatusLogic.php new file mode 100644 index 00000000..e7d1a7c9 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ApproveDuplicateBankStatementDetailsStatusLogic.php @@ -0,0 +1,283 @@ + 'Update Duplicate Bank Statement Details Status', + 'message' => 'You have successfully updated the Statement Transation Status' + ]; + } + + /** @var UpdatesBankStatementTransactionOwnerStatus */ + private $updatesBankStatementTransactionOwnerStatus; + + /** + * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus + */ + public function __construct(UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus) + { + $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; + } + +// /** +// * Perform logic for approving or rejecting a statement transaction owner and handle matching records. +// * +// * @param Request $request The request object containing route parameters and data. +// * @return JsonResponse The JSON response indicating the result of the logic. +// */ +// public function logic(Request $request): JsonResponse +// { +// // Determine the status based on the 'status' route parameter +// $status = $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED; +// +// // Find the statement transaction owner based on the 'id' route parameter +// $owner = StatementTransactionOwner::find($request->route('id')); +// +// // Execute an update action to change the status of the owner +// $this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status); +// +// // If the status is 'approved', reject all other owners with the same system, owner type, and owner ID +// if ($status === ApprovalStatus::APPROVED) { +// StatementTransactionOwner::where('system', $owner->system) +// ->where('owner_type', $owner->owner_type) +// ->where('owner_id', $owner->owner_id) +// ->where('id', '!=', $owner->id) +// ->update(['status' => ApprovalStatus::REJECTED]); +// } +// +// // Find all siblings (owners with the same statement transaction ID) +// $siblings = StatementTransactionOwner::where('statement_transaction_id', $owner->statement_transaction_id) +// ->where('id', '!=', $owner->id) +// ->get(); +// +// // Process each sibling +// foreach ($siblings as $sibling) { +// +// // If the status is 'approved', reject the sibling and save the changes +// if ($status === ApprovalStatus::APPROVED) { +// $sibling->status = ApprovalStatus::REJECTED; +// $sibling->save(); +// } +// +// // Find all twins (owners with the same system, owner type, and owner ID) +// $twins = StatementTransactionOwner::where('system', $sibling->system) +// ->where('owner_type', $sibling->owner_type) +// ->where('owner_id', $sibling->owner_id) +// ->where('id', '!=', $sibling->id) +// ->get(); +// +// // Process each twin +// foreach ($twins as $twin) { +// // Find all owners with the same statement transaction ID as the twin +// $owners = StatementTransactionOwner::where('statement_transaction_id', $twin->statement_transaction_id) +// ->where('id', '!=', $twin->id) +// ->get(); +// +// // If there is only one owner (the twin itself), execute an update action to change its status to 'approved' +// if (count($owners) === 1) { +// $this->updatesBankStatementTransactionOwnerStatus->execute($twin, ApprovalStatus::APPROVED); +// } +// } +// } +// +// // Find all remaining matching owners for the related transaction +// $remainingMatches = StatementTransactionOwner::where('system', $owner->system) +// ->where('owner_type', $owner->owner_type) +// ->where('owner_id', $owner->owner_id) +// ->where('status', ApprovalStatus::PENDING_VERIFICATION) // Consider only pending owners +// ->get(); +// +// // Process each remaining match +// foreach ($remainingMatches as $match) { +// // Find all owners with the same statement transaction ID as the match +// $owners = StatementTransactionOwner::where('statement_transaction_id', $match->statement_transaction_id) +// ->where('id', '!=', $match->id) +// ->get(); +// +// // If there is only one owner (the match itself), execute an update action to change its status to 'approved' +// if (count($owners) === 1) { +// $this->updatesBankStatementTransactionOwnerStatus->execute($match, ApprovalStatus::APPROVED); +// } +// } +// +// // Return an empty response +// return $this->response([]); +// } + + /** + * Perform logic for approving or rejecting a statement transaction owner and handle matching records. + * + * @param Request $request The request object containing route parameters and data. + * @return JsonResponse The JSON response indicating the result of the logic. + */ + public function logic(Request $request): JsonResponse + { + // Determine the approval status + $status = $this->getApprovalStatus($request); + + // Find the statement transaction owner + $owner = $this->getOwner($request); + + // Update owner status + $this->updateOwnerStatus($owner, $status); + + // If the status is 'approved', handle the approval process + if ($status === ApprovalStatus::APPROVED) { + $this->handleApprovedStatus($owner); + } + + // Check and approve remaining matches if any + $this->checkAndApproveRemainingMatches($owner); + + // Return an empty response + return $this->response([]); + } + + private function getApprovalStatus(Request $request): int + { + return $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED; + } + + private function getOwner(Request $request): StatementTransactionOwner + { + return StatementTransactionOwner::find($request->route('id')); + } + + private function updateOwnerStatus(StatementTransactionOwner $owner, int $status): void + { + $this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status); + } + + private function handleApprovedStatus(StatementTransactionOwner $owner): void + { + // Reject all other owners with the same system, owner type, and owner ID + $this->rejectOtherOwners($owner); + + // Find all siblings and process them + $siblings = $this->getSiblings($owner); + $this->processSiblings($siblings); + } + + private function rejectOtherOwners(StatementTransactionOwner $owner): void + { + StatementTransactionOwner::where('system', $owner->system) + ->where('owner_type', $owner->owner_type) + ->where('owner_id', $owner->owner_id) + ->where('id', '!=', $owner->id) + ->update(['status' => ApprovalStatus::REJECTED]); + } + + private function getSiblings(StatementTransactionOwner $owner): Collection + { + return StatementTransactionOwner::where('statement_transaction_id', $owner->statement_transaction_id) + ->where('id', '!=', $owner->id) + ->get(); + } + + private function processSiblings(Collection $siblings): void + { + foreach ($siblings as $sibling) { + $this->processSibling($sibling); + } + } + + private function processSibling(StatementTransactionOwner $sibling): void + { + // Reject the sibling and save the changes + $sibling->status = ApprovalStatus::REJECTED; + $sibling->save(); + + // Find all twins and process them + $twins = $this->getTwins($sibling); + $this->processTwins($twins); + } + + private function getTwins(StatementTransactionOwner $sibling): Collection + { + return StatementTransactionOwner::where('system', $sibling->system) + ->where('owner_type', $sibling->owner_type) + ->where('owner_id', $sibling->owner_id) + ->where('id', '!=', $sibling->id) + ->get(); + } + + private function processTwins(Collection $twins): void + { + foreach ($twins as $twin) { + $this->processTwin($twin); + } + } + + private function processTwin(StatementTransactionOwner $twin): void + { + // Find all owners with the same statement transaction ID as the twin + $owners = $this->getOwners($twin); + + // If there is only one owner (the twin itself), approve it + if ($owners->count() === 1) { + $this->updateOwnerStatus($twin, ApprovalStatus::APPROVED); + } + } + + private function getOwners(StatementTransactionOwner $transactionOwner): Collection + { + return StatementTransactionOwner::where('statement_transaction_id', $transactionOwner->statement_transaction_id) + ->where('id', '!=', $transactionOwner->id) + ->get(); + } + + private function checkAndApproveRemainingMatches(StatementTransactionOwner $owner): void + { + // Find all remaining matching owners for the related transaction + $remainingMatches = $this->getRemainingMatches($owner); + + // Process each remaining match + foreach ($remainingMatches as $match) { + $this->processRemainingMatch($match); + } + } + + private function getRemainingMatches(StatementTransactionOwner $owner): Collection + { + return StatementTransactionOwner::where('system', $owner->system) + ->where('owner_type', $owner->owner_type) + ->where('owner_id', $owner->owner_id) + ->where('status', ApprovalStatus::PENDING_VERIFICATION) + ->get(); + } + + private function processRemainingMatch(StatementTransactionOwner $match): void + { + // Find all owners with the same statement transaction ID as the match + $owners = $this->getOwners($match); + + // If there is only one owner (the match itself), approve it + if ($owners->count() === 1) { + $this->updateOwnerStatus($match, ApprovalStatus::APPROVED); + } + + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/GroupApproveStatementTransactionLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/GroupApproveStatementTransactionLogic.php new file mode 100644 index 00000000..3b220146 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/GroupApproveStatementTransactionLogic.php @@ -0,0 +1,86 @@ + 'Group Approve Statement Transaction', + 'message' => 'You have successfully approveed a group of Statement Transation' + ]; + } + + /** @var ListsBankStatementTransactions */ + private $listsBankStatementTransactions; + + /** @var UpdatesBankStatementTransactionOwnerStatus */ + private $updatesBankStatementTransactionOwnerStatus; + + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + + /** + * @param ListsBankStatementTransactions $listsBankStatementTransactions + * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus + * @param UpdatesTransactionStatus $updatesTransactionStatus + */ + public function __construct(ListsBankStatementTransactions $listsBankStatementTransactions, UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus, UpdatesTransactionStatus $updatesTransactionStatus) + { + $this->listsBankStatementTransactions = $listsBankStatementTransactions; + $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; + $this->updatesTransactionStatus = $updatesTransactionStatus; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ + public function logic(Request $request): JsonResponse + { + + $filters = [ + "min_amount" => 0, + "is_mapped" => true, + "is_mapped_with_multiple" => false, + "statement_transaction_owner_type_in" => [1, 2], + "statement_transaction_owner_status_in" => [1] + ]; + $statementTransactions = $this->listsBankStatementTransactions->execute($filters); + + foreach ($statementTransactions as $statementTransaction) { + $owners = $statementTransaction->owners; + + if (count($owners)) { + $this->updatesBankStatementTransactionOwnerStatus->execute($owners->first(), ApprovalStatus::APPROVED); + + // automatically approve payment if pending verification + +// if ($statementTrasactionOwner->owner->type !== StatementTransactionOwnerType::SALES) continue; +// if ($statementTrasactionOwner->owner->status === ApprovalStatus::PENDING_VERIFICATION ) { +// $this->updatesTransactionStatus->execute($statementTrasactionOwner->owner, ApprovalStatus::APPROVED); +// } + } + } + + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php new file mode 100644 index 00000000..0355c554 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php @@ -0,0 +1,147 @@ + 'Import Bank Statement Transactions Details', + 'message' => 'You have successfully updated the Bank Statement Transactions Details' + ]; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + + $files = $request->file('files'); + + $object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports'); + foreach ($object->getFiles() as $file){ + $collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true); + + $sheet = $collection->first()->skip(1); + + $statementDetails = $sheet->first(); + + $accountNumber = $statementDetails[0]; + $accountType = $statementDetails[1]; + $accountName = $statementDetails[2]; + $accountCurrency = $statementDetails[3]; + $dateFrom = carbon::parse(str_replace(' MY (UTC+08:00)', '', $statementDetails[4])); + $dateTo = carbon::parse(str_replace(' MY (UTC+08:00)', '', $statementDetails[5])); + $totalDebit = $statementDetails[6]; + $totalCredit = $statementDetails[7]; + $beginBalance = $statementDetails[8]; + $endBalance = $statementDetails[9]; + $account = StatementAccount::updateOrCreate( + ['number' => $accountNumber], + [ + 'type' => $accountType, + 'name' => $accountName, + 'currency' => $accountCurrency, + ] + ); + + $statement = AccountStatement::where('date_from', $dateFrom) + ->where('date_to', $dateTo) + ->where('total_amount', $totalDebit ?: $totalCredit,) + ->where('begin_balance', $beginBalance) + ->where('end_balance', $endBalance)->first(); + + + if(!$statement){ + $statement = new AccountStatement([ + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + 'total_amount' => $totalDebit ?: $totalCredit, + 'begin_balance' => $beginBalance, + 'end_balance' => $endBalance, + ]); + } + + $account->statements()->save($statement); + +// CreateBankStatementTransactionOwners::dispatch($statement); + + + $sheet->map(function ($row) use ($statement, $account) { + $transactionRef = $row[15]; + $amount = $row[17] !== '-' ? ((float) str_replace(',', '', $row[17])) : (-((float) str_replace(',', '', $row[16]))); + $transactionDate = $row[10] !== '-' ? carbon::parse(str_replace(' MY (UTC+08:00)', '', $row[10]) . $row[11]) : null; + $postingDate = carbon::createFromFormat('d/M/Y H:i', str_replace(' MY (UTC+08:00)', '', $row[12]) . str_replace(' MY (UTC+08:00)', '', $row[13])); + $transactionDescription = is_numeric($row[14]) ? (int) sprintf('%.2f', $row[14]) : $row[14]; + $tellerId = $row[19]; + $branchChannel = $row[20]; + $transactionCode = $row[21]; + $endBalance = $row[22]; + $description2 = $row[25]; + $description3 = $row[26]; + $description4 = $row[27]; + $description5 = $row[28]; + $transaction = new StatementTransaction([ + 'transaction_ref' => $transactionRef, + 'amount' => $amount, + 'transaction_date' => $transactionDate, + 'posting_date' => $postingDate, + 'transaction_description' => $transactionDescription, + 'teller_id' => $tellerId, + 'branch_channel' => $branchChannel, + 'transaction_code' => $transactionCode, + 'end_balance' => $endBalance, + 'transaction_description_2' => $description2, + 'transaction_description_3' => $description3, + 'transaction_description_4' => $description4, + 'transaction_description_5' => $description5, + ]); + + // Check if the transaction already exists for this statement + $existingTransaction = StatementTransaction::where('transaction_ref', $transactionRef) + ->where('posting_date', $postingDate) + ->where('amount', $amount) + ->where('transaction_description', $transactionDescription) + ->where('teller_id', $tellerId) + ->where('branch_channel', $branchChannel) + ->where('transaction_code', $transactionCode) + ->where('end_balance', $endBalance) + ->first(); + + if (!$existingTransaction) { + $statement->transactions()->save($transaction); + } + + return $transaction; + }); + } + + + + return $this->response([]); + + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementDetailsLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementDetailsLogic.php new file mode 100644 index 00000000..10cffba1 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementDetailsLogic.php @@ -0,0 +1,52 @@ + 'Retrieved Bank Statement Details', + 'message' => 'You have successfully retrieved a Bank Statement Details' + ]; + } + + + /** @var ListsBankStatementDetails */ + private $listsBankStatementDetails; + + /** + * ListBankStatementDetailsLogic constructor. + * @param ListsBankStatementDetails $listsBankStatementDetails + */ + public function __construct(ListsBankStatementDetails $listsBankStatementDetails) + { + $this->listsBankStatementDetails = $listsBankStatementDetails; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsBankStatementDetails->execute($this->listsBankStatementDetails->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(BankStatementDetailResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementTransactionsLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementTransactionsLogic.php new file mode 100644 index 00000000..3d2b8273 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementTransactionsLogic.php @@ -0,0 +1,53 @@ + 'Retrieved Bank Statement Transactions', + 'message' => 'You have successfully retrieved a Bank Statement Transactions' + ]; + } + + + /** @var ListsBankStatementTransactions */ + private $listsBankStatementTransactions; + + /** + * @param ListsBankStatementTransactions $listsBankStatementTransactions + */ + public function __construct(ListsBankStatementTransactions $listsBankStatementTransactions) + { + $this->listsBankStatementTransactions = $listsBankStatementTransactions; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsBankStatementTransactions->execute($this->listsBankStatementTransactions->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(BankStatementTransactionResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php new file mode 100644 index 00000000..d9eab3db --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php @@ -0,0 +1,152 @@ + 'Updated Bank Statement Transactions Details', + 'message' => 'You have successfully updated the Bank Statement Transactions Details' + ]; + } + + /** @var FetchesBankStatementTransaction */ + private $fetchesBankStatementTransaction; + + + /** @var ChecksBillNumber */ + private $checksBillNumber; + + /** + * @param FetchesBankStatementTransaction $fetchesBankStatementTransaction + * @param ChecksBillNumber $checksBillNumber + */ + public function __construct(FetchesBankStatementTransaction $fetchesBankStatementTransaction, ChecksBillNumber $checksBillNumber) + { + $this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction; + $this->checksBillNumber = $checksBillNumber; + } + + public function logic(Request $request): JsonResponse + { + $bankStatementTransaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]); + + $transactionReference = $request->input('transaction_reference'); + $systemReference = $request->input('system_references'); + + $owner_type = $owner_id = $owner_reference = $statementTransactionOwnerType = $system = null; + + $salesSystems = ['lite', 'cntr', 'probashi', 'pets']; + $allowedSystems = array_merge($salesSystems, ['exchange', 'izyim']); + + if (!in_array($systemReference, $allowedSystems) && !in_array($request->input('pay_for'), ['internal_bank_transfer', 'others'])) { + throw new MalformedRequestException('System Reference not allowed'); + } + + switch ($request->input('pay_for')) { + case 'sales': + $owner_reference = $transactionReference; + $statementTransactionOwnerType = StatementTransactionOwnerType::SALES; + break; + + case 'top_up': + $owner_reference = $transactionReference; + $statementTransactionOwnerType = StatementTransactionOwnerType::WALLET_TOP_UP; + break; + + case 'internal_bank_transfer': + $statementTransactionOwnerType = StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN; + break; + + case 'others': + $owner_reference = $transactionReference; + $statementTransactionOwnerType = StatementTransactionOwnerType::NON_OPERATIONAL; + break; + + default: + throw new MalformedRequestException('Transaction Type Not Allowed'); + } + + if (in_array($systemReference, ['exchange', 'izyim'])) { + $transaction = $this->checksBillNumber->execute($transactionReference, $systemReference); + + if (is_array($transaction) && empty($transaction)) { + throw new MalformedRequestException('Transaction Not Found.'); + } + + if($systemReference === 'exchange') { + $owner_type = Transaction::class; + $owner_id = $transaction->id; + if($transaction->type === TransactionType::TOP_UP) { + $owner_reference = $transaction->owner->owner->reference; + } + + if($transaction->type === TransactionType::PAYMENT && $transaction->owner_type === Booking::class) { + $owner_reference = $transaction->owner->marking; + } + + } + + if($systemReference === 'izyim') { + $transaction = $transaction[0]; + $payFor = $request->input('pay_for'); + $transactionType = $transaction['type']; + + if (($payFor === 'sales' && !in_array($transactionType, [ShippingTransactionType::PAYMENT, ShippingTransactionType::GROUP_PAYMENT])) + || ($payFor === 'top_up' && !in_array($transactionType, [ShippingTransactionType::TOP_UP, ShippingTransactionType::GROUP_PAYMENT]))) { + throw new MalformedRequestException('Transaction Type does not match.'); + } + + $owner_type = Transaction::class; + $owner_id = $transaction['owner_id']; + $owner_reference = $transaction['owner_reference']; + + } + } + + $system = $systemReference != null ? SystemType::SYSTEM_NAMES[$systemReference] : ''; + + $this->createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference); + + return $this->response([]); + } + + private function createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference) + { + $ownerData = [ + 'type' => $statementTransactionOwnerType, + 'system' => $system, + 'owner_type' => $owner_type, + 'owner_id' => $owner_id, + 'owner_reference' => $owner_reference, + ]; + + $bankStatementTransaction->owners()->firstOrCreate($ownerData); + + } + + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php new file mode 100644 index 00000000..3a006e9e --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php @@ -0,0 +1,81 @@ + 'Update Statement Transaction Status', + 'message' => 'You have successfully updated the Statement Transation Status' + ]; + } + + /** @var FetchesBankStatementTransaction */ + private $fetchesBankStatementTransaction; + + /** @var UpdatesBankStatementTransactionOwnerStatus */ + private $updatesBankStatementTransactionOwnerStatus; + + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + + /** + * UpdateAnnouncementLogic constructor. + * @param FetchesBankStatementTransaction $fetchesBankStatementTransaction + * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus + * @param UpdatesTransactionStatus $updatesTransactionStatus + */ + public function __construct( + FetchesBankStatementTransaction $fetchesBankStatementTransaction, + UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus, + UpdatesTransactionStatus $updatesTransactionStatus + ) { + $this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction; + $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; + $this->updatesTransactionStatus = $updatesTransactionStatus; + } + + /** + * @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 + { + $statementTrasaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]); + + $statementTrasactionOwner = $statementTrasaction->owners->first(); + + $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)); + } +} diff --git a/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTransactionObject.php b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTransactionObject.php new file mode 100644 index 00000000..30bac5d3 --- /dev/null +++ b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTransactionObject.php @@ -0,0 +1,137 @@ +statement_transaction_id = $statement_transaction_id; + $this->type = $type; + $this->system = $system; + $this->owner_type = $owner_type; + $this->owner_id = $owner_id; + $this->invoice_reference = $invoice_reference; + $this->receipt_reference = $receipt_reference; + $this->is_auto_mapped = $is_auto_mapped; + $this->status = $status; + } + + /** + * @return int + */ + public function getStatementTransactionId(): int + { + return $this->statement_transaction_id; + } + + /** + * @return int + */ + public function getType(): int + { + return $this->type; + } + + /** + * @return string + */ + public function getOwnerType(): string + { + return $this->owner_type; + } + + /** + * @return int + */ + public function getOwnerId(): int + { + return $this->owner_id; + } + + /** + * @return string + */ + public function getSystem(): string + { + return $this->system; + } + + /** + * @return string + */ + public function getInvoiceReference(): string + { + return $this->invoice_reference; + } + + /** + * @return string + */ + public function getReceipteReference(): string + { + return $this->receipt_reference; + } + + /** + * @return Boolean + */ + public function getIsAutoMapped(): Boolean + { + return $this->is_auto_mapped; + } + + /** + * @return int + */ + public function getStatus(): int + { + return $this->status; + } +} diff --git a/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTrasactionOwnerObject.php b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTrasactionOwnerObject.php new file mode 100644 index 00000000..07fe876e --- /dev/null +++ b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTrasactionOwnerObject.php @@ -0,0 +1,46 @@ +pay_for = $pay_for; + $this->system_references = $system_references; + } + + /** + * @return string + */ + public function getPayFor(): string + { + return $this->pay_for; + } + + /** + * @return string + */ + public function getSystemReferences(): string + { + return $this->system_references; + } +} diff --git a/app/Classes/Modules/Accounting/Processors/ChecksBillNumber.php b/app/Classes/Modules/Accounting/Processors/ChecksBillNumber.php new file mode 100644 index 00000000..adc5c7b1 --- /dev/null +++ b/app/Classes/Modules/Accounting/Processors/ChecksBillNumber.php @@ -0,0 +1,37 @@ + false]); + $response = $client->request('GET', $url . '?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"bill_no":"' . $bill_no . '"}'); + $body = $response->getBody(); + $data = json_decode($body, true); + $payload = $data['payload']; + return $payload['data']; + } catch (\Exception $exception) { + throw new MalformedRequestException($exception->getMessage()); + } + } + + if ($system_reference == 'exchange') { + $transaction = Transaction::where('bill_no', $bill_no)->first(); + if ($transaction) { + return $transaction; + } + } + + // if not found + throw new MalformedRequestException('Bill Number Not Found.'); + } +} diff --git a/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php new file mode 100644 index 00000000..90dd7048 --- /dev/null +++ b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php @@ -0,0 +1,255 @@ +whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + })->orderBy('posting_date')->get(); + +// $transactions = StatementTransaction::whereDoesntHave('owners')->where('amount', '<', 0)->get(); + + foreach ($transactions as $transaction) { + + if($transaction->amount > 0){ + + // Exchange Sales + $creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SALES, + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id'=> $creditTransaction->id, + 'owner_reference'=> $creditTransaction->owner->marking, + ]); + } + + + // Shipping Portal Sales + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date), 2); + foreach ($creditTransactions as $creditTransaction) { + if($creditTransaction['owner_type'] === Wallet::class) continue; + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SALES, + 'system' => 'SHIPPING_PORTAL', + 'owner_type' => $creditTransaction['owner_type'], + 'owner_id'=> $creditTransaction['owner_id'], + 'owner_reference'=> $creditTransaction['owner_reference'], + ]); + } + + // Exchange Wallet Top Up + $creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::WALLET_TOP_UP, + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id'=> $creditTransaction->id, + 'owner_reference'=> $creditTransaction->owner->owner->reference, + ]); + } + + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date), 5); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::WALLET_TOP_UP, + 'system' => 'SHIPPING_PORTAL', + 'owner_type' => $creditTransaction['owner_type'], + 'owner_id'=> $creditTransaction['owner_id'], + 'owner_reference'=> $creditTransaction['owner_reference'], + ]); + } + + // fpx charge refund + if($transaction->transaction_description === 'DUITNOW S/CHRG REFUND'){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::FPX_CHARGE_REFUND + ]); + } + + // Customer Refund + + // INTERNAL_BANK_TRANSFER_IN + if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN + ]); + } + + + } + + if($transaction->amount < 0){ + + // Supplier Purchase Order Payments + foreach (['YSN', 'HCK', 'ATVANTIC', 'HIGH HILL'] as $reference){ + if(str_contains($transaction->transaction_description.' '.$transaction->transaction_description_2.' '.$transaction->transaction_description_3.' '.$transaction->transaction_description_4.' '.$transaction->transaction_description_5 , $reference)) { + $paymentDateStart = $transaction->posting_date->startOfDay()->subDays(1); + $paymentDateEnd = $transaction->posting_date->endOfDay(); + + if($paymentDateStart->dayOfWeek === Carbon::SUNDAY){ + $paymentDateStart->subDays(2); + } + $issuer = Company::where('name', 'like', '%'.$reference.'%')->get()->pluck('id'); + + $debitTransactions = Group::whereIn('issuer', $issuer)->where('amount', '>=', (($transaction->amount * -1) - 0.01)) + ->where('amount', '<=', (($transaction->amount * -1) + 0.01))->whereDate('created_at', '>=', $paymentDateStart)->whereDate('created_at', '<=', $paymentDateEnd)->get(); + + foreach ($debitTransactions as $debitTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SUPPLIER_PAYMENT, + 'system' => 'EXCHANGE', + 'owner_type' => Group::class, + 'owner_id'=> $debitTransaction->id, + 'owner_reference'=> $debitTransaction->reference + ]); + } + + } + } + + // Exchange Wallet Withdrawal + $debitTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::DEBIT_NOTE, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($debitTransactions as $debitTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::WALLET_WITHDRAWAL, + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id'=> $debitTransaction->id, + 'owner_reference'=> $debitTransaction->owner->owner->marking, + ]); + } + + // SALARY + + // STATUTORY + if(str_contains($transaction->transaction_description_2, 'PEMBANGUNAN SUMBER') || str_contains($transaction->transaction_description_2, 'HASIL') || str_contains($transaction->transaction_description_2, 'PERTUBUHAN KESELAMAT') || str_contains($transaction->transaction_description_2, 'KUMPULAN WANG SIMPAN')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::STATUTORY + ]); + } + + // FPX_CHARGE + if($transaction->transaction_description === 'DR DUITNOW S/CHRG' || str_contains($transaction->transaction_description, 'Manual FPX') || str_contains($transaction->transaction_description, 'CMS - DR FPX CHG')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::FPX_CHARGE + ]); + } + + // BANK_CHARGE + if($transaction->transaction_description === 'CMS - DR CORP CHG' || $transaction->transaction_description === 'MONTHLY PROFIT DEBIT'){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::BANK_CHARGE + ]); + } + + // CREDIT_CARD_PAYMENT + if(str_contains($transaction->transaction_description_2, 'VISA CARD')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::CREDIT_CARD_PAYMENT + ]); + } + + // INTERNAL_BANK_TRANSFER_OUT + if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE') || str_contains($transaction->transaction_description_2, 'CIEF WORLWIDE') || str_contains($transaction->transaction_description_2, 'IZYIM GLOBAL')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_OUT + ]); + } + + // non-operational charges + if(str_contains($transaction->transaction_description_2, 'HIRE PURCHASE') || str_contains($transaction->transaction_description_2, 'TENAGA NASIONAL') || str_contains($transaction->transaction_description, 'CABLE CHARGE') || str_contains($transaction->transaction_description_2, 'CTOS DATA SYSTEMS') || str_contains($transaction->transaction_description_2, 'MAXIS')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::NON_OPERATIONAL + ]); + } + } + } + } + + private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $model = Transaction::class) { + $query = $model::whereIn('status', $statuses) + ->where(function ($query) use ($ownerType, $paymentMethod, $type) { + if ($ownerType) { + $query->where('owner_type', $ownerType); + } + + if ($paymentMethod) { + $query->where('payment_method', '!=', $paymentMethod); + } + + if ($type) { + $query->where('type', $type); + } + }) + ->whereDate('created_at', $date->format('Y-m-d')) + ->where('amount', '>', ($amount - 0.01)) + ->where('amount', '<', ($amount + 0.01)); + + return $query->get(); + } + + private function getTransactionsFromShippingPortal($amount, $dateRange, $type){ + $url = 'https://izyim.cief-malaysia.com/public/api/v1/transactions/mappable/query'; + return $this->getFromShippingPortal($amount, $dateRange, $url, $type); + } + + private function getGroupsFromShippingPortal($amount, $dateRange, $type){ + $url = 'https://izyim.cief-malaysia.com/public/api/v1/groups/query'; + return $this->getFromShippingPortal($amount, $dateRange, $url, $type); + } + + private function getFromShippingPortal($amount, $dateRange, $url, $type){ + try{ + + $client = new \GuzzleHttp\Client(['verify' => false]); + $response = $client->request('GET', $url.'?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2],"type":2,"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).',"type_in:"['.$type.']}'); + $body = $response->getBody(); + $data = json_decode($body, true); + $payload = $data['payload']; + $transactions2 = $payload['data']; + return $transactions2; + }catch(\Exception $exception){ + Log::error($exception); + return []; + } + } + + private function getDateRange(string $dateStr) { + // Create a DateTime object from the input string + $date = strtotime($dateStr); + + // Get the first day of the month + $today = date('Y-m-d', strtotime('-1 day', $date)); + + // Get the first day of the next month + $nextDay = date('Y-m-d', strtotime('+1 day', $date)); + + return [ + 'start_date' => $today, + 'end_date' => $nextDay, + ]; + } +} diff --git a/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php b/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php new file mode 100644 index 00000000..f80c3c0e --- /dev/null +++ b/app/Classes/Modules/Accounting/Processors/ListShippingPortalTransactions.php @@ -0,0 +1,35 @@ + false]); + $response = $client->request('GET', $url . '?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters=' . json_encode($filters)); + $body = $response->getBody(); + $data = json_decode($body, true); + $payload = $data['payload']; + return $data['payload']['data']; + + } catch (\Exception $exception) { + // dd($exception->getMessage()); + throw new MalformedRequestException($exception->getMessage()); + // preg_match('/\{.*\}/s', $exception->getMessage(), $matches); + // $jsonError = json_decode($matches[0]); + // Retrieved Transactions failed + // throw new MalformedRequestException($jsonError->title); + } + + // if not found + throw new MalformedRequestException('Bill Number Not Found.'); + } +} diff --git a/app/Classes/Modules/Accounting/Services/CreatesBankStatementTransactionOwner.php b/app/Classes/Modules/Accounting/Services/CreatesBankStatementTransactionOwner.php new file mode 100644 index 00000000..92a3ea33 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/CreatesBankStatementTransactionOwner.php @@ -0,0 +1,34 @@ +statement_transaction_id = $object->getStatementTransactionId(); + $model->type = $object->getType(); + $model->system = $object->getSystem(); + $model->owner_type = $object->getOwnerType(); + $model->owner_id = $object->getOwnerId(); + $model->invoice_reference = $object->getInvoiceReference(); + $model->receipt_reference = $object->getReceipteReference(); + $model->is_auto_mapped = $object->getIsAutoMapped(); + $model->status = $object->getStatus(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php new file mode 100644 index 00000000..22de5c39 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransaction.php b/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransaction.php new file mode 100644 index 00000000..d2bff6f9 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransaction.php @@ -0,0 +1,32 @@ +repository = $repository; + } + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php new file mode 100644 index 00000000..6e085e44 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php @@ -0,0 +1,32 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/ListsBankStatementTransactions.php b/app/Classes/Modules/Accounting/Services/ListsBankStatementTransactions.php new file mode 100644 index 00000000..8d4fbf69 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/ListsBankStatementTransactions.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php new file mode 100644 index 00000000..e87121ac --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php @@ -0,0 +1,25 @@ +system_references = $object->getSystemReferences(); + $model->pay_for = $object->getPayFor(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwner.php b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwner.php new file mode 100644 index 00000000..6af3d3d6 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwner.php @@ -0,0 +1,32 @@ +statement_transaction_id = $object->getStatementTransactionId(); + $model->type = $object->getType(); + $model->system = $object->getSystem(); + $model->owner_type = $object->getOwnerType(); + $model->owner_id = $object->getOwnerId(); + $model->invoice_reference = $object->getInvoiceReference(); + $model->receipt_reference = $object->getReceipteReference(); + $model->is_auto_mapped = $object->getIsAutoMapped(); + $model->status = $object->getStatus(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwnerStatus.php b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwnerStatus.php new file mode 100644 index 00000000..abcda20a --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwnerStatus.php @@ -0,0 +1,22 @@ +status = $status; + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php index 0e920fe9..fa6a9ae7 100644 --- a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php +++ b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php @@ -11,7 +11,9 @@ use App\Classes\Modules\Companies\Processors\AssignEmployeeProcessor; use App\Classes\Modules\Companies\Processors\AssignSegmentProcessor; use App\Classes\Modules\Companies\Processors\CreateCompanyProcessor; use App\Classes\Modules\Contacts\Processors\CreateContactProcessor; -use App\Classes\Modules\PerfexCRM\Processors\CreatePerfexCRMLeadProcessor; +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; +use App\Classes\Modules\Vouchers\Processors\Voucherify\NewCustomerToVoucherifyProcessor; +use App\Classes\Modules\Vouchers\Processors\CreateVoucherProcessor; use App\Classes\Modules\Companies\DataTransferObjects\EmploymentObject; use App\Classes\Modules\PerfexCRM\DataTransferObjects\CreateLeadPerfexCRMObject; use App\Classes\ValueObjects\Constants\ApprovalStatus; @@ -19,12 +21,15 @@ use App\Classes\ValueObjects\Constants\BusinessType; use App\Classes\ValueObjects\Constants\CompanyType; use App\Classes\ValueObjects\Constants\RoleTypes; use App\Classes\Jobs\CreatePerfexCRMCustomer; +use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject; use App\Models\Company; +use App\Models\Segment; use App\Models\User; +use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; - +use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment; class CreateCustomerLogic extends AbstractControllerLogic { @@ -60,8 +65,17 @@ class CreateCustomerLogic extends AbstractControllerLogic /** @var GenerateEmailVerificationAttemptProcessor */ private $generateEmailVerificationAttemptProcessor; - /** @var CreatePerfexCRMLeadProcessor */ - private $createPerfexCRMLeadProcessor; + /** @var CreatesSeasonalSegment */ + private $createsSeasonalSegment; + + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** @var NewCustomerToVoucherifyProcessor */ + private $newCustomerToVoucherifyProcessor; + + /** @var CreateVoucherProcessor */ + private $createVoucherProcessor; /** * CreateCustomerLogic constructor. @@ -72,10 +86,13 @@ class CreateCustomerLogic extends AbstractControllerLogic * @param AssignSegmentProcessor $assignSegmentProcessor * @param AuthenticationProcessor $authenticationProcessor * @param GenerateEmailVerificationAttemptProcessor $generateEmailVerificationAttemptProcessor - * @param CreatePerfexCRMLeadProcessor $createPerfexCRMLeadProcessor + * @param CreatesSeasonalSegment $createsSeasonalSegment + * @param CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor + * @param NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor + * @param CreateVoucherProcessor $createVoucherProcessor */ public function __construct(CreateUserProcessor $createUserProcessor, CreateCompanyProcessor $createCompanyProcessor, CreateContactProcessor $createContactProcessor, AssignEmployeeProcessor $assignEmployeeProcessor, AssignSegmentProcessor $assignSegmentProcessor, AuthenticationProcessor $authenticationProcessor, GenerateEmailVerificationAttemptProcessor $generateEmailVerificationAttemptProcessor, - CreatePerfexCRMLeadProcessor $createPerfexCRMLeadProcessor) + CreatesSeasonalSegment $createsSeasonalSegment, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor, NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor, CreateVoucherProcessor $createVoucherProcessor) { $this->createUserProcessor = $createUserProcessor; $this->createCompanyProcessor = $createCompanyProcessor; @@ -84,7 +101,10 @@ class CreateCustomerLogic extends AbstractControllerLogic $this->assignSegmentProcessor = $assignSegmentProcessor; $this->authenticationProcessor = $authenticationProcessor; $this->generateEmailVerificationAttemptProcessor = $generateEmailVerificationAttemptProcessor; - $this->createPerfexCRMLeadProcessor = $createPerfexCRMLeadProcessor; + $this->createsSeasonalSegment = $createsSeasonalSegment; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; + $this->newCustomerToVoucherifyProcessor = $newCustomerToVoucherifyProcessor; + $this->createVoucherProcessor = $createVoucherProcessor; } /** @@ -98,6 +118,7 @@ class CreateCustomerLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { + /** @var User $user */ $user = $this->createUserProcessor->execute($request, RoleTypes::USER, App::environment(['local']) ? ApprovalStatus::APPROVED : ApprovalStatus::PENDING_VERIFICATION); @@ -109,10 +130,21 @@ class CreateCustomerLogic extends AbstractControllerLogic $Object = new EmploymentObject($company, $user); $this->assignEmployeeProcessor->execute($Object); + // assign STANDARD_SEGMENT to all new customers $this->assignSegmentProcessor->execute($company); + // assign other standard segment to all new customers + $otherDefaultSegmentToAdd = Segment::whereIn('name', ['HONEY TRAP NEW REGISTRATION'])->get(); + $start_date = Carbon::now(); + $end_date = Carbon::now()->addDays(30); + foreach ($otherDefaultSegmentToAdd as $segment) { + $seasonalSegmentObject = new SeasonalSegmentObject($company->id, $segment->id, $start_date, $end_date ?? null); + + $this->createsSeasonalSegment->execute($seasonalSegmentObject); + $this->assignSegmentProcessor->execute($company, $segment->id); + } + if(config('perfexcrm.is_enabled') == 'true'){ - //$this->createPerfexCRMLeadProcessor->execute($request); $createLeadPerfexCRMObject = new CreateLeadPerfexCRMObject( $request->input('name'), $request->input('email'), @@ -125,7 +157,11 @@ class CreateCustomerLogic extends AbstractControllerLogic $this->generateEmailVerificationAttemptProcessor->execute($user); - return $this->response($this->authenticationProcessor->execute($request)); + $this->newCustomerToVoucherifyProcessor->execute($company->id, $user, true); + + $this->createVoucherProcessor->execute($user, 'WELCOME50%OFF'); + + return $this->response($this->authenticationProcessor->execute($request, false)); } } diff --git a/app/Classes/Modules/Accounts/Processors/AuthenticationProcessor.php b/app/Classes/Modules/Accounts/Processors/AuthenticationProcessor.php index fabb88c6..707369e9 100644 --- a/app/Classes/Modules/Accounts/Processors/AuthenticationProcessor.php +++ b/app/Classes/Modules/Accounts/Processors/AuthenticationProcessor.php @@ -8,6 +8,9 @@ use App\Classes\Modules\Accounts\Services\AuthenticationRedirect; use App\Classes\Modules\Accounts\Services\FetchesUser; use App\Classes\Modules\Accounts\Services\GeneratesAuthenticationToken; use App\Classes\Modules\Accounts\Standards\Rules\CanAuthenticateUser; +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; +use App\Classes\Modules\Vouchers\Processors\Voucherify\NewCustomerToVoucherifyProcessor; +use App\Classes\ValueObjects\Constants\Milestones; use Illuminate\Http\Request; class AuthenticationProcessor @@ -28,6 +31,13 @@ class AuthenticationProcessor /** @var AuthenticationRedirect */ private $authenticationRedirect; + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** @var NewCustomerToVoucherifyProcessor */ + private $newCustomerToVoucherifyProcessor; + + /** * AuthenticationProcessor constructor. * @param CanAuthenticateUser $canAuthenticateUser @@ -35,14 +45,18 @@ class AuthenticationProcessor * @param GeneratesAuthenticationToken $generatesAuthenticationToken * @param FetchesUser $fetchesUser * @param AuthenticationRedirect $authenticationRedirect + * @param CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor + * @param NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor */ - public function __construct(CanAuthenticateUser $canAuthenticateUser, AuthenticatesUser $authenticatesUser, GeneratesAuthenticationToken $generatesAuthenticationToken, FetchesUser $fetchesUser, AuthenticationRedirect $authenticationRedirect) + public function __construct(CanAuthenticateUser $canAuthenticateUser, AuthenticatesUser $authenticatesUser, GeneratesAuthenticationToken $generatesAuthenticationToken, FetchesUser $fetchesUser, AuthenticationRedirect $authenticationRedirect, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor, NewCustomerToVoucherifyProcessor $newCustomerToVoucherifyProcessor) { $this->canAuthenticateUser = $canAuthenticateUser; $this->authenticatesUser = $authenticatesUser; $this->generatesAuthenticationToken = $generatesAuthenticationToken; $this->fetchesUser = $fetchesUser; $this->authenticationRedirect = $authenticationRedirect; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; + $this->newCustomerToVoucherifyProcessor = $newCustomerToVoucherifyProcessor; } @@ -53,7 +67,7 @@ class AuthenticationProcessor * @throws \App\Classes\Exceptions\AccessUnauthorisedException * @throws \App\Classes\Exceptions\RequestValidationException */ - public function execute(Request $request): array { + public function execute(Request $request, bool $isSignIn = true): array { $object = new AuthenticationCredentialsObject($request->input('email'), $request->input('password')); @@ -63,8 +77,13 @@ class AuthenticationProcessor $user = $this->fetchesUser->execute(['email' => $object->getEmail()]); + if($isSignIn){ + $this->newCustomerToVoucherifyProcessor->execute(0, $user, false); + } + + //cief todo: case study 1 + //$this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_1]); + return ['access_token' => $this->generatesAuthenticationToken->execute($user), 'redirect_url' => $this->authenticationRedirect->url($user)]; - } - -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Addresses/ControllersLogic/CreateAddressLogic.php b/app/Classes/Modules/Addresses/ControllersLogic/CreateAddressLogic.php index 8fd6fe7f..a760254f 100644 --- a/app/Classes/Modules/Addresses/ControllersLogic/CreateAddressLogic.php +++ b/app/Classes/Modules/Addresses/ControllersLogic/CreateAddressLogic.php @@ -9,10 +9,12 @@ use App\Classes\Modules\Addresses\Services\FetchesDistrict; use App\Classes\Modules\Addresses\Standards\Rules\CanCreateAddress; use App\Classes\Modules\Addresses\DataTransferObjects\AddressObject; use App\Classes\Modules\Companies\Services\FetchesCompany; +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; use App\Http\Resources\AddressResource; use ErrorException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use App\Classes\ValueObjects\Constants\Milestones; class CreateAddressLogic extends AbstractControllerLogic { @@ -39,19 +41,25 @@ class CreateAddressLogic extends AbstractControllerLogic /** @var CreatesAddress */ private $createsAddress; + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** * CreateAddressLogic constructor. * @param CanCreateAddress $canCreateAddress * @param FetchesDistrict $fetchesDistrict * @param FetchesCompany $fetchesCompany * @param CreatesAddress $createsAddress + * @param CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor */ - public function __construct(CanCreateAddress $canCreateAddress, FetchesDistrict $fetchesDistrict, FetchesCompany $fetchesCompany, CreatesAddress $createsAddress) + public function __construct(CanCreateAddress $canCreateAddress, FetchesDistrict $fetchesDistrict, FetchesCompany $fetchesCompany, CreatesAddress $createsAddress, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor) { $this->canCreateAddress = $canCreateAddress; $this->fetchesDistrict = $fetchesDistrict; $this->fetchesCompany = $fetchesCompany; $this->createsAddress = $createsAddress; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } /** @@ -70,10 +78,15 @@ class CreateAddressLogic extends AbstractControllerLogic $this->canCreateAddress->passes($object); - $query = $this->createsAddress->execute($this->fetchesCompany->execute(['id' => $request->input('company_id')]), $object); + $company = $this->fetchesCompany->execute(['id' => $request->input('company_id')]); + $query = $this->createsAddress->execute($company, $object); + + //cief todo: case study 6 + // $user = $company->employees()->first(); + // $this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_6]); return $this->resourceResponse(new AddressResource($query)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php b/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php index dc67848a..bb9a3468 100644 --- a/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php +++ b/app/Classes/Modules/Banks/ControllersLogic/CreateBankLogic.php @@ -8,7 +8,10 @@ use App\Classes\Modules\Banks\Standards\Rules\CanCreateBank; use App\Classes\Modules\Banks\Services\CreatesBank; use App\Classes\Modules\Banks\Services\CreatesBankLog; use App\Classes\Modules\Banks\DataTransferObjects\BankObject; +use App\Classes\Modules\Companies\Services\FetchesCompany; +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; use App\Http\Resources\BankResource; +use App\Classes\ValueObjects\Constants\Milestones; use ErrorException; use Illuminate\Http\JsonResponse; @@ -36,21 +39,34 @@ class CreateBankLogic extends AbstractControllerLogic /** @var CreatesBankLog */ private $createsBankLog; + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** * CreateBankLogic constructor. * @param CanCreateBank $canCreateBank * @param CreatesBank $createsBank * @param CreatesBankLog $createsBankLog + * @param FetchesCompany $fetchesCompany + * @param CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor */ public function __construct( CanCreateBank $canCreateBank, CreatesBank $createsBank, - CreatesBankLog $createsBankLog + CreatesBankLog $createsBankLog, + FetchesCompany $fetchesCompany, + CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor ) { $this->canCreateBank = $canCreateBank; $this->createsBank = $createsBank; $this->createsBankLog = $createsBankLog; + $this->fetchesCompany = $fetchesCompany; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } /** @@ -73,7 +89,12 @@ class CreateBankLogic extends AbstractControllerLogic // $bankLog = $this->createsBankLog->execute($bank); + //cief todo: case study 4 + // $company = $this->fetchesCompany->execute(['id' => $request->input('company_id')]); + // $user = $company->employees()->first(); + // $this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_4]); + return $this->resourceResponse(new BankResource($bank)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php index a93a3654..61f78dcb 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php @@ -14,7 +14,8 @@ use App\Classes\Modules\Bookings\Services\GeneratesBookingMarking; use App\Classes\Modules\Bookings\DataTransferObjects\BookingObject; use App\Classes\Modules\PerfexCRM\Processors\BookingToPerfexCRMProcessor; - +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; +use App\Classes\ValueObjects\Constants\Milestones; use App\Http\Resources\BookingResource; use App\Models\Booking; @@ -50,6 +51,10 @@ class CreateBookingLogic extends AbstractControllerLogic /** @var BookingToPerfexCRMProcessor */ private $bookingToPerfexCRMProcessor; + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + + /** * CreateBookingLogic constructor. * @param CanCreateBooking $canCreateBooking @@ -57,14 +62,16 @@ class CreateBookingLogic extends AbstractControllerLogic * @param GeneratesBookingMarking $generatesBookingMarking * @param FetchesCompany $fetchesCompany * @param BookingToPerfexCRMProcessor $bookingToPerfexCRMProcessor + * @param CheckMilestoneForRewardProcessor $checkMilestonesForRewardProcessor */ - public function __construct(CanCreateBooking $canCreateBooking, CreatesBooking $createsBooking, GeneratesBookingMarking $generatesBookingMarking, FetchesCompany $fetchesCompany, BookingToPerfexCRMProcessor $bookingToPerfexCRMProcessor) + public function __construct(CanCreateBooking $canCreateBooking, CreatesBooking $createsBooking, GeneratesBookingMarking $generatesBookingMarking, FetchesCompany $fetchesCompany, BookingToPerfexCRMProcessor $bookingToPerfexCRMProcessor, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor) { $this->canCreateBooking = $canCreateBooking; $this->createsBooking = $createsBooking; $this->generatesBookingMarking = $generatesBookingMarking; $this->fetchesCompany = $fetchesCompany; $this->bookingToPerfexCRMProcessor = $bookingToPerfexCRMProcessor; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } @@ -90,6 +97,10 @@ class CreateBookingLogic extends AbstractControllerLogic $this->bookingToPerfexCRMProcessor->execute($booking); } + //cief todo: case study 5 + // $user = $company->employees()->first(); + // $this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_5]); + return $this->resourceResponse(new BookingResource($booking)); } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index 99b09c3f..c429d91d 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -5,29 +5,27 @@ namespace App\Classes\Modules\Bookings\ControllersLogic; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; -use App\Classes\Modules\Bookings\DataTransferObjects\CalculationObject; use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit; -use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; -//use App\Classes\Modules\Vouchers\DataTransferObjects\VoucherObject; use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; use App\Classes\Modules\Transactions\Services\CreatesTransaction; use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; -//use App\Classes\Modules\Vouchers\Services\FetchesVoucher; -//use App\Classes\Modules\Vouchers\Services\CreatesVoucher; -//use App\Classes\Modules\Vouchers\Services\CreatesVoucherRedemption; -//use App\Classes\Modules\Vouchers\Services\RedeemsVoucher; use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance; use App\Classes\ValueObjects\Constants\ApprovalStatus; 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\Transactions\Processors\CreateCashBackTransactionProcessor; +use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor; + use App\Models\Booking; use App\Models\Transaction; +use App\Models\Wallet; use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -35,30 +33,6 @@ use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; class CreateBookingPaymentLogic extends AbstractControllerLogic { - /** - * @param FetchesBookingQuotation $fetchBookingQuotation - * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit - * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber - * @param CreatesTransaction $createsTransaction - * @param CalculatesBookingOutstanding $calculatesBookingOutstanding - * @param CreatesBillplzBill $createsBillplzBill - * @param UpdatesWalletBalance $updatesWalletBalance - * @param UpdatesTransactionStatus $updatesTransactionStatus - * @param RecalculatesWalletBalance $recalculatesWalletBalance - */ - public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingOutstanding $calculatesBookingOutstanding, CreatesBillplzBill $createsBillplzBill, UpdatesWalletBalance $updatesWalletBalance, UpdatesTransactionStatus $updatesTransactionStatus, RecalculatesWalletBalance $recalculatesWalletBalance) - { - $this->fetchBookingQuotation = $fetchBookingQuotation; - $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; - $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; - $this->createsTransaction = $createsTransaction; - $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; - $this->createsBillplzBill = $createsBillplzBill; - $this->updatesWalletBalance = $updatesWalletBalance; - $this->updatesTransactionStatus = $updatesTransactionStatus; - $this->recalculatesWalletBalance = $recalculatesWalletBalance; - } - /** * @return array @@ -94,157 +68,113 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var UpdatesTransactionStatus */ private $updatesTransactionStatus; + /** @var CreateCashBackTransactionProcessor */ + private $createCashBackTransactionProcessor; /** @var RecalculatesWalletBalance */ private $recalculatesWalletBalance; -// -// /** @var FetchesVoucher */ -// private $fetchesVoucher; -// -// /** @var CreatesVoucher */ -// private $createsVoucher; -// -// /** @var CreatesVoucherRedemption */ -// private $createsVoucherRedemption; -// -// /** @var RedeemsVoucher */ -// private $redeemsVoucher; + /** @var BookingToVoucherifyProcessor */ + private $bookingToVoucherifyProcessor; + /** + * CreateBookingPaymentLogic constructor. + * @param FetchesBookingQuotation $fetchBookingQuotation + * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit + * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber + * @param CreatesTransaction $createsTransaction + * @param CalculatesBookingOutstanding $calculatesBookingOutstanding + * @param CreatesBillplzBill $createsBillplzBill + * @param UpdatesWalletBalance $updatesWalletBalance + * @param UpdatesTransactionStatus $updatesTransactionStatus + * @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor + * @param RecalculatesWalletBalance $recalculatesWalletBalance + * @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor + */ + 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) + { + $this->fetchBookingQuotation = $fetchBookingQuotation; + $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createsTransaction = $createsTransaction; + $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; + $this->createsBillplzBill = $createsBillplzBill; + $this->updatesWalletBalance = $updatesWalletBalance; + $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; + $this->recalculatesWalletBalance = $recalculatesWalletBalance; + $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; + } + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ public function logic(Request $request) : JsonResponse { + $voucherCode = $request->input('voucher_code'); + $booking = Booking::find($request->route('id')); - $conversionObject = $this->createConversionObject($request, $booking); + $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')]); - $this->validatePayment($request, $booking, $conversionObject); + $outstanding = $this->calculatesBookingOutstanding->execute($booking); - $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $request->input('voucher_code')); + if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); + + $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode); + + $paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company); $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); - $paymentReference = $this->handlePaymentMethods($request, $booking, $configurations, $billNumber); + $paymentReference = null; - $object = $this->createTransactionObject($booking, $configurations, $billNumber, $paymentReference); + $amount = $configurations->getTotal(); + + if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY){ + $billPlzBill = $this->createsBillplzBill->execute($booking->company->name, $request->user()->email, 'This payment is made for transfer ref. '.$booking->marking, $configurations->getTotal(), $billNumber, $request->input('bank_code')); + $paymentReference = $billPlzBill->id; + } + + if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){ + /** @var Wallet $wallet */ + $wallet = $booking->company->wallets()->first(); + + if((float) number_format(($wallet->amount - $amount),2) < 0){ + throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.'); + } + + $transaction_object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], ''); + $transaction = $this->createsTransaction->execute($wallet, $transaction_object); + + $paymentReference = $billNumber; + + $walletBalance = $this->recalculatesWalletBalance->execute($wallet); + $this->updatesWalletBalance->execute($wallet, $walletBalance); + } + + $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); + + $object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, + $configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(), + $configurations->getTotal(), $configurations->getForeignTotal(), 1, + $configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(), + $configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes($paymentAttemptLimit), ApprovalStatus::PENDING_SUBMISSION, [], $paymentReference); /** @var Transaction $transaction */ $transaction = $this->createsTransaction->execute($booking, $object); +// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); - if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY){ - $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::PENDING_VERIFICATION); + $this->bookingToVoucherifyProcessor->execute($booking->company->employees()->first(), $transaction, $booking->company->id, $configurations->getSubTotal(), $configurations->getVoucherDiscountAmount(), $voucherCode); + + if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){ + $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); } - // $this->handleVoucherCode($request, $booking, $configurations, $transaction); //todo-important: uncomment this for voucherify - return $this->resourceResponse(new TransactionResource($transaction)); } - private function createConversionObject(Request $request, Booking $booking): CurrencyConversionObject - { - return 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')] - ); - } - private function validatePayment(Request $request, Booking $booking, CurrencyConversionObject $conversionObject): void - { - $outstanding = $this->calculatesBookingOutstanding->execute($booking); - - if($conversionObject->getAmount() > round($outstanding, 2)) - throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); - } - - private function handlePaymentMethods(Request $request, Booking $booking, CalculationObject $configurations, string $billNumber): ?string - { - $paymentReference = null; - - if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY) - $paymentReference = $this->handlePaymentGateway($request, $booking, $configurations, $billNumber); - - if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET) - $paymentReference = $this->handleWalletPayment($request, $booking, $configurations, $billNumber); - - return $paymentReference; - } - - private function handlePaymentGateway(Request $request, Booking $booking, CalculationObject $configurations, string $billNumber): string - { - $billPlzBill = $this->createsBillplzBill->execute( - $booking->company->name, - $request->user()->email, - 'This payment is made for transfer ref. '.$booking->marking, - $configurations->getTotal(), - $billNumber, - $request->input('bank_code') - ); - - return $billPlzBill->id; - } - - private function handleWalletPayment(Request $request, Booking $booking, CalculationObject $configurations, string $billNumber): string - { - $wallet = $booking->company->wallets()->first(); - - $amount = $configurations->getTotal(); - if((float) number_format(($wallet->amount - $amount),2) < 0) - throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.'); - - $transactionObject = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], ''); - $this->createsTransaction->execute($wallet, $transactionObject); - - $walletBalance = $this->recalculatesWalletBalance->execute($wallet); - $this->updatesWalletBalance->execute($wallet, $walletBalance); - - return $billNumber; - } - - private function createTransactionObject(Booking $booking, CalculationObject $configurations, string $billNumber, ?string $paymentReference): TransactionObject - { - return new TransactionObject( - $billNumber, - TransactionType::PAYMENT, - 1, - $booking->company->id, - $configurations->getConfigurations()->getBankId(), - $configurations->getConversionObject()->getPaymentMethod(), - $configurations->getTotal(), - $configurations->getForeignTotal(), - 1, - $configurations->getConversionObject()->getCurrencyId(), - $configurations->getConfigurations()->getRate(), - $configurations->getTax(), - $configurations->getServiceCharge(), - Carbon::now()->addMinutes($this->fetchesCompanyPaymentAttemptLimit->execute($booking->company)), - ApprovalStatus::PENDING_SUBMISSION, - [], - $paymentReference - ); - } - -// private function handleVoucherCode(Request $request, Booking $booking, CalculationObject $configurations, Transaction $transaction): void -// { -// $voucherCode = $request->input('voucher_code'); -// if($voucherCode){ -// $employee = $booking->company->employees()->first(); -// -// $result = $this->redeemsVoucher->execute($voucherCode, $configurations->getSubTotal(), $employee); -// $voucher = $result->voucher; -// $redemptionId = $result->id; -// -// $voucherValue = $voucher->discount->amount_off ?? ($voucher->discount->percent_off ?? 0.00); -// $voucherObject = new VoucherObject($voucher->code, $voucher->metadata->name ?? "", $voucher->discount->type, $voucherValue); -// $voucher = $this->createsVoucher->execute($voucherObject); -// -// if(!$voucher) -// $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]); -// -// $this->createsVoucherRedemption->execute($redemptionId, $voucher->id, $employee->id, $configurations->getVoucherDiscountAmount()); -// } -// } } - diff --git a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php index 3a981946..8effdb2f 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php @@ -69,8 +69,11 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic $outstanding = $this->calculatesBookingOutstanding->execute($booking); 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 + $voucherCode = $request->input('voucherCode'); + return $this->response(['data' => $this->generatesBookingQuotation->execute( - $this->fetchBookingQuotation->execute($booking->company, $conversionObject), + $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode), $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company), $conversionObject )]); @@ -78,4 +81,4 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php index 4fc89d44..ef6124cf 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/UploadPurchaseOrderLogic.php @@ -84,7 +84,7 @@ class UploadPurchaseOrderLogic extends AbstractControllerLogic $this->createsFile->execute($document, $object); - if(in_array($booking->company->id, [199, 510])){ + if(!in_array($booking->company->id, [199, 510])){ $this->createPurchaseOrderFor1688OrderProcessor->execute($booking); } diff --git a/app/Classes/Modules/Bookings/DataTransferObjects/CalculationObject.php b/app/Classes/Modules/Bookings/DataTransferObjects/CalculationObject.php index aeeba8f7..db79848d 100644 --- a/app/Classes/Modules/Bookings/DataTransferObjects/CalculationObject.php +++ b/app/Classes/Modules/Bookings/DataTransferObjects/CalculationObject.php @@ -5,6 +5,7 @@ namespace App\Classes\Modules\Bookings\DataTransferObjects; use App\Classes\General\Interfaces\DataTransferObject; use App\Classes\Modules\Companies\DataTransferObjects\CompanyServiceConfigurationsObject; use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; +use App\Classes\Modules\Vouchers\DataTransferObjects\ValidatedVoucherObject; class CalculationObject implements DataTransferObject { @@ -15,15 +16,20 @@ class CalculationObject implements DataTransferObject /** @var CompanyServiceConfigurationsObject */ private $configurations; + /** @var ValidatedVoucherObject */ + private $voucherObject; + /** * CalculationObject constructor. * @param CurrencyConversionObject $conversionObject * @param CompanyServiceConfigurationsObject $configurations + * @param ValidatedVoucherObject $voucherObject */ - public function __construct(CurrencyConversionObject $conversionObject, CompanyServiceConfigurationsObject $configurations) + public function __construct(CurrencyConversionObject $conversionObject, CompanyServiceConfigurationsObject $configurations, ?ValidatedVoucherObject $voucherObject = null) { $this->conversionObject = $conversionObject; $this->configurations = $configurations; + $this->voucherObject = $voucherObject; } /** @@ -43,6 +49,15 @@ class CalculationObject implements DataTransferObject return $this->configurations; } + + /** + * @return ValidatedVoucherObject + */ + public function getValidatedVoucherObject(): ?ValidatedVoucherObject + { + return $this->voucherObject; + } + public function getConvertibleTotal(){ return $this->getConversionObject()->getType() === 1 ? $this->getConversionObject()->getAmount() : $this->getConversionTotal(); } @@ -87,9 +102,31 @@ class CalculationObject implements DataTransferObject return ($this->getSubTotal()/100) * $this->getConfigurations()->getTax(); } - public function getTotal(): float { - return $this->getSubTotal() + $this->getTax(); + public function getVoucherDiscountAmount(): float { + if($this->getValidatedVoucherObject()){ + return $this->getValidatedVoucherObject()->getTotalDiscountAmount(); + } + else{ + return 0.00; + } } + public function getVoucherCode(): string { + if($this->getValidatedVoucherObject()){ + return $this->getValidatedVoucherObject()->getCode(); + } + else{ + return ""; + } + } -} \ No newline at end of file + public function getTotal(): float { + if($this->getValidatedVoucherObject()){ + return $this->getValidatedVoucherObject()->getTotalAmount() + $this->getTax(); + } + else{ + return $this->getSubTotal() + $this->getTax(); + } + } + +} diff --git a/app/Classes/Modules/Bookings/Services/FetchesBookingQuotation.php b/app/Classes/Modules/Bookings/Services/FetchesBookingQuotation.php index 17d776fd..5fe85c35 100644 --- a/app/Classes/Modules/Bookings/Services/FetchesBookingQuotation.php +++ b/app/Classes/Modules/Bookings/Services/FetchesBookingQuotation.php @@ -7,7 +7,10 @@ use App\Classes\Exceptions\MalformedRequestException; use App\Classes\Modules\Bookings\DataTransferObjects\CalculationObject; use App\Classes\Modules\Companies\Services\FetchesCompanyServiceSettings; use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; +use App\Classes\Modules\Vouchers\DataTransferObjects\ValidatedVoucherObject; +use App\Classes\Modules\Vouchers\DataTransferObjects\ValidateVoucherifyVoucherObject; use App\Classes\Modules\Currencies\Services\FetchesCurrency; +use App\Classes\Modules\Vouchers\Services\Voucherify\ValidatesVoucherifyVoucher; use App\Models\Company; use App\Models\Currency; @@ -20,25 +23,30 @@ class FetchesBookingQuotation /** @var FetchesCurrency */ private $fetchesCurrency; + /** @var ValidatesVoucherifyVoucher */ + private $validatesVoucherifyVoucher; + /** * FetchesBookingQuotation constructor. * @param FetchesCompanyServiceSettings $fetchesCompanyServiceSettings * @param FetchesCurrency $fetchesCurrency */ - public function __construct(FetchesCompanyServiceSettings $fetchesCompanyServiceSettings, FetchesCurrency $fetchesCurrency) + public function __construct(FetchesCompanyServiceSettings $fetchesCompanyServiceSettings, FetchesCurrency $fetchesCurrency, ValidatesVoucherifyVoucher $validatesVoucherifyVoucher) { $this->fetchesCompanyServiceSettings = $fetchesCompanyServiceSettings; $this->fetchesCurrency = $fetchesCurrency; + $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher; } /** * @param Company $company * @param CurrencyConversionObject $conversionObject + * @param string $voucherCode * @return CalculationObject * @throws MalformedRequestException */ - public function execute(Company $company, CurrencyConversionObject $conversionObject){ + public function execute(Company $company, CurrencyConversionObject $conversionObject, ?string $voucherCode = null){ if($conversionObject->getAmount() <= 0) throw new MalformedRequestException('Your transfer must be greater than zero.'); $configurations = $this->fetchesCompanyServiceSettings->execute($company, $conversionObject); @@ -47,9 +55,24 @@ class FetchesBookingQuotation $currency = $this->fetchesCurrency->execute(['id' => $conversionObject->getCurrencyId()]); if($conversionObject->getAmount() > $configurations->getMaxLimit()) throw new MalformedRequestException('Your transfer can\'t be greater than '.number_format( floatval(str_replace(',', '', $configurations->getMaxLimit())), 2, '.', ',').' '.$currency->short_code); - $calculationObject = new CalculationObject($conversionObject, $configurations); + $calculationObject = new CalculationObject($conversionObject, $configurations, null); + + //Voucherify + if($voucherCode){ + $employee = $company->employees()->first(); + $validateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject($company->id, $voucherCode, $calculationObject->getSubTotal(), $employee); + $result = $this->validatesVoucherifyVoucher->execute($validateVoucherifyVoucherObject); + $voucher = [ + "code" => $result->code, + "discount" => property_exists($result, 'discount') ? $result->discount : null, + "metadata" => $result->metadata, + "order" => $result->order, + ]; + $validatedVoucherObject = new ValidatedVoucherObject(isset($voucher['metadata']->name) ? $voucher['metadata']->name: "", $voucher['code'], $voucher['discount']->type ?? 'AMOUNT', $voucher['order']->total_discount_amount, $voucher['order']->total_amount); + $calculationObject = new CalculationObject($conversionObject, $configurations, $validatedVoucherObject); + } return $calculationObject; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php b/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php index 6fe93c97..8ea1b88d 100644 --- a/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php +++ b/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php @@ -34,7 +34,9 @@ class GeneratesBookingQuotation 'total' => $calculationObject->getTotal(), 'date' => Carbon::now()->timezone('Asia/Singapore')->format('h:i a, jS M, Y \G\M\T T'), 'receive_date' => $receive_date, - 'payment_attempt_limit' => CarbonInterval::days($days)->hours($hours)->minutes($minutes)->forHumans() + 'payment_attempt_limit' => CarbonInterval::days($days)->hours($hours)->minutes($minutes)->forHumans(), + 'voucher_discount_amount' => $calculationObject->getVoucherDiscountAmount(), + 'voucher_code' => $calculationObject->getVoucherCode(), ]; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php b/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php index f814057f..1148a6d6 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/AssignCompanyToSegmentLogic.php @@ -7,8 +7,10 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Companies\Processors\AssignSegmentProcessor; use App\Classes\Modules\Companies\Services\FetchesCompany; use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject; +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment; use App\Http\Resources\CompanyResource; +use App\Classes\ValueObjects\Constants\Milestones; use App\Models\SeasonalSegment; use Carbon\Carbon; use Illuminate\Http\JsonResponse; @@ -36,17 +38,22 @@ class AssignCompanyToSegmentLogic extends AbstractControllerLogic /** @var CreatesSeasonalSegment */ private $createsSeasonalSegment; + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + /** * AssignCompanyToSegmentLogic constructor. * @param FetchesCompany $fetchesCompany * @param AssignSegmentProcessor $assignCompanyToSegmentProcessor * @param CreatesSeasonalSegment $createsSeasonalSegment + * @param CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor */ - public function __construct(FetchesCompany $fetchesCompany, AssignSegmentProcessor $assignCompanyToSegmentProcessor, CreatesSeasonalSegment $createsSeasonalSegment) + public function __construct(FetchesCompany $fetchesCompany, AssignSegmentProcessor $assignCompanyToSegmentProcessor, CreatesSeasonalSegment $createsSeasonalSegment, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor) { $this->fetchesCompany = $fetchesCompany; $this->assignCompanyToSegmentProcessor = $assignCompanyToSegmentProcessor; $this->createsSeasonalSegment = $createsSeasonalSegment; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } /** @@ -75,12 +82,16 @@ class AssignCompanyToSegmentLogic extends AbstractControllerLogic // } $start_date = $request->input('starting_on') ? $request->input('starting_on') : Carbon::now(); - + $seasonalSegmentObject = new SeasonalSegmentObject($company->id, $request->input('segment_id'), $start_date, $request->input('ending_on') ?? null); $this->createsSeasonalSegment->execute($seasonalSegmentObject); - } - + } + + //cief todo: case study 3 + // $user = $company->employees()->first(); + // $this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_3]); + return $this->resourceResponse(new CompanyResource($company)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/CreateIdentificationDocumentLogic.php b/app/Classes/Modules/Companies/ControllersLogic/CreateIdentificationDocumentLogic.php index c08dc4ef..6a818ea8 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/CreateIdentificationDocumentLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/CreateIdentificationDocumentLogic.php @@ -10,9 +10,11 @@ use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; use App\Classes\Modules\Documents\Services\CreatesDocument; use App\Classes\Modules\Documents\Services\CreatesFiles; use App\Classes\Modules\PerfexCRM\Processors\NewLeadTaskToPerfexCRMProcessor; +use App\Classes\Modules\Milestones\Processors\CheckMilestonesForRewardProcessor; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\CompanyType; use App\Classes\ValueObjects\Constants\DocumentType; +use App\Classes\ValueObjects\Constants\Milestones; use App\Models\Company; use App\Models\Document; use Illuminate\Http\JsonResponse; @@ -46,6 +48,9 @@ class CreateIdentificationDocumentLogic extends AbstractControllerLogic /** @var NewLeadTaskToPerfexCRMProcessor */ private $newLeadTaskToPerfexCRMProcessor; + /** @var CheckMilestonesForRewardProcessor */ + private $checkMilestonesForRewardProcessor; + /** * CreateIdentificationDocumentLogic constructor. * @param FetchesCompany $fetchesCompany @@ -53,14 +58,16 @@ class CreateIdentificationDocumentLogic extends AbstractControllerLogic * @param CreatesFiles $createsFile * @param UpdatesCompanyStatus $updatesCompanyStatus * @param NewLeadTaskToPerfexCRMProcessor $newLeadTaskToPerfexCRMProcessor + * @param CheckMilestoneForRewardProcessor $checkMilestonesForRewardProcessor */ - public function __construct(FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesCompanyStatus $updatesCompanyStatus, NewLeadTaskToPerfexCRMProcessor $newLeadTaskToPerfexCRMProcessor) + public function __construct(FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesCompanyStatus $updatesCompanyStatus, NewLeadTaskToPerfexCRMProcessor $newLeadTaskToPerfexCRMProcessor, CheckMilestonesForRewardProcessor $checkMilestonesForRewardProcessor) { $this->fetchesCompany = $fetchesCompany; $this->createsDocument = $createsDocument; $this->createsFile = $createsFile; $this->updatesCompanyStatus = $updatesCompanyStatus; $this->newLeadTaskToPerfexCRMProcessor = $newLeadTaskToPerfexCRMProcessor; + $this->checkMilestonesForRewardProcessor = $checkMilestonesForRewardProcessor; } /** @@ -87,6 +94,10 @@ class CreateIdentificationDocumentLogic extends AbstractControllerLogic $this->newLeadTaskToPerfexCRMProcessor->execute($company); } + //cief todo: case study 2 + // $user = $company->employees()->first(); + // $this->checkMilestonesForRewardProcessor->execute($user, [Milestones::MILESTONE_2]); + return $this->response([]); } diff --git a/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php b/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php index 736853fc..2a425f7d 100644 --- a/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php +++ b/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php @@ -28,7 +28,7 @@ class FileObject implements DataTransferObject */ public function getData() { - return in_array($this->getExtension(), ['pdf', 'excel']) ? $this->data : (new imageManager())->make($this->data); + return in_array($this->getExtension(), ['pdf', 'excel', 'text']) ? $this->data : (new imageManager())->make($this->data); } /** @@ -66,7 +66,7 @@ class FileObject implements DataTransferObject */ public function getDecodedData(): string { - return in_array($this->getExtension(), ['pdf', 'excel']) ? + return in_array($this->getExtension(), ['pdf', 'excel', 'text']) ? base64_decode((explode('base64,', $this->getData()))[1]): $this->getData()->encode('data-url')->encoded; } @@ -83,4 +83,4 @@ class FileObject implements DataTransferObject -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php b/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php index 8741b823..0e0cf6b9 100644 --- a/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php +++ b/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php @@ -36,7 +36,7 @@ class ConvertsBase64ToFile foreach ($files as $file) { $object = new FileObject($file); - in_array($object->getExtension(), ['pdf', 'excel']) ? $this->generatePDF($object) : $this->generateImage($object); + in_array($object->getExtension(), ['pdf', 'excel', 'text']) ? $this->generatePDF($object) : $this->generateImage($object); } @@ -122,4 +122,4 @@ class ConvertsBase64ToFile ]); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php new file mode 100644 index 00000000..90780750 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php @@ -0,0 +1,149 @@ +request = $request; + } + + public function headings(): array + { + return [ + 'DocNo', + 'DocDate', + 'DebtorCode', + 'Ref', + 'DebtorName', + 'CurrencyCode', + 'ShipInfo', + 'ItemCode', + 'DetailDescription', + 'FurtherDescription', + 'Qty', + 'UnitPrice', + 'AccNo', + 'DeptNo' + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + return StatementTransactionOwner::whereNull('invoice_reference') + ->whereIn('type', [StatementTransactionOwnerType::SALES, StatementTransactionOwnerType::WALLET_TOP_UP]) + ->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED]); + } + + /** + * @param Transaction $transaction + * + * @return array + */ + public function map($transaction): array + { + $logArray = [ + 'counter' => $this->counter, + 'system' => $transaction->system, + 'StatementTransactionOwner_id' => $transaction->id, + 'transaction_table_id' => $transaction->owner_id, + ]; + $this->counter += 1; + $logArray = json_encode($logArray); + + $filePath = storage_path('logs/exports_invoice_transactions.log'); + $errorFilePath = storage_path('logs/exports_invoice_transactions_error.log'); + $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' ' . $logArray . PHP_EOL; + file_put_contents($filePath, $textToAppend, FILE_APPEND); + + if ($transaction->system == 'EXCHANGE') { + $row = (App()->make($transaction->owner_type))->where('id', $transaction->owner_id)->first(); + $company = $row->type === TransactionType::PAYMENT ? $row->owner->company : $row->owner->owner; + + $booking = $row->owner; + + return [ + '<>', + $row->updated_at->format('m/d/Y H:m'), + $company->debtor, + $row->type === TransactionType::PAYMENT ? $booking->marking : $company->reference, + '', + 'MYR', + $row->type === TransactionType::PAYMENT ? $booking->marking : $row->bill_no, + $row->type === TransactionType::PAYMENT ? '' : 'W1', + $row->type === TransactionType::PAYMENT ? 'PLEASE REFER TO THE ATTACHED APPENDIX REF ' . $booking->marking : 'CREDIT SALES', + '', + 1, + round($row->amount, 2), + '500-0000', + 'CIEF' + ]; + } else { + $row = (App()->make(ListShippingPortalTransactions::class))->execute([ + 'id' => $transaction->owner_id, + 'with_company' => true, + ]); + + if (empty($row) || $row[0]['status'] != 'success') { + $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Fetch Shipping Transaction Fail ' . json_encode([ + 'id' => $transaction->owner_id, + 'with_company' => true, + 'StatementTransactionOwner_id' => $transaction->id, + ]) . PHP_EOL; + file_put_contents($errorFilePath, $textToAppend, FILE_APPEND); + + $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); + + return []; + } + + $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' + ]; + } + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php b/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php index f9493b17..31d814a0 100644 --- a/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsPaymentTransactions.php @@ -32,6 +32,7 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading 'DocNo', 'DocDate', 'DebtorCode', + 'Ref', 'DebtorName', 'CurrencyCode', 'ShipInfo', @@ -67,7 +68,7 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading if($start_date && $end_date) { $query->whereBetween('created_at', [ - Carbon::parse($start_date)->format('Y-m-d 0:00:00'), + Carbon::parse($start_date)->format('Y-m-d 0:00:00'), Carbon::parse($end_date)->format('Y-m-d 23:59:59') ]); } @@ -95,6 +96,7 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading '<>', $transaction->updated_at->format('m/d/Y H:m'), $company->debtor, + $booking->marking, '', 'MYR', $booking->marking, @@ -107,4 +109,4 @@ class ExportsPaymentTransactions implements FromQuery, WithHeadings, WithHeading 'CIEF' ]; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Imports/Services/BankStatementImport.php b/app/Classes/Modules/Imports/Services/BankStatementImport.php new file mode 100644 index 00000000..a552b1b0 --- /dev/null +++ b/app/Classes/Modules/Imports/Services/BankStatementImport.php @@ -0,0 +1,82 @@ +has('account_number')) { + // If the row has an account number, create a new statement account + $accountNumber = $row->get('account_number'); + $accountType = $row->get('account_type'); + $accountName = $row->get('account_name'); + $accountCurrency = $row->get('account_currency'); + + $account = StatementAccount::updateOrCreate( + ['number' => $accountNumber], + [ + 'type' => $accountType, + 'name' => $accountName, + 'currency' => $accountCurrency, + ] + ); + } else { + // Otherwise, create a new statement transaction for the current statement account + $dateFrom = $row->get('date_from'); + $dateTo = $row->get('date_to'); + $totalAmount = $row->get('total_amount'); + $beginBalance = $row->get('begin_balance'); + $endBalance = $row->get('end_balance'); + + $statement = AccountStatement::updateOrCreate( + [ + 'account_id' => $account->id, + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + ], + [ + 'total_amount' => $totalAmount, + 'begin_balance' => $beginBalance, + 'end_balance' => $endBalance, + ] + ); + + $transactionDate = $row->get('transaction_date'); + $transactionTime = $row->get('transaction_time'); + $postingDate = $row->get('posting_date'); + $transactionDescription = $row->get('transaction_description'); + $transactionRef = $row->get('transaction_ref'); + $amount = $row->get('amount'); + $tellerId = $row->get('teller_id'); + $branchChannel = $row->get('branch_channel'); + $transactionCode = $row->get('transaction_code'); + + $transaction = new StatementTransaction([ + 'statement_id' => $statement->id, + 'transaction_date' => $transactionDate, + 'transaction_time' => $transactionTime, + 'posting_date' => $postingDate, + 'transaction_description' => $transactionDescription, + 'transaction_ref' => $transactionRef, + 'amount' => $amount, + 'teller_id' => $tellerId, + 'branch_channel' => $branchChannel, + 'transaction_code' => $transactionCode, + ]); + + $transaction->save(); + } + } + } +} diff --git a/app/Classes/Modules/Milestones/ControllersLogic/CreateMilestoneLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/CreateMilestoneLogic.php new file mode 100644 index 00000000..db30f789 --- /dev/null +++ b/app/Classes/Modules/Milestones/ControllersLogic/CreateMilestoneLogic.php @@ -0,0 +1,70 @@ + 'Create Milestone', + 'message' => 'You have successfully created a milestone' + ]; + } + + /** @var FetchesReward */ + private $fetchesReward; + + /** @var CreateMilestoneProcessor */ + private $createMilestoneProcessor; + + /** @var AssignRewardProcessor */ + private $assignRewardProcessor; + + /** + * CreateMilestoneLogic constructor. + */ + public function __construct(CreateMilestoneProcessor $createMilestoneProcessor, FetchesReward $fetchesReward, AssignRewardProcessor $assignRewardProcessor) + { + $this->createMilestoneProcessor = $createMilestoneProcessor; + $this->fetchesReward = $fetchesReward; + $this->assignRewardProcessor = $assignRewardProcessor; + } + + /** + * @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 + { + $milestone = $this->createMilestoneProcessor->execute($request); + + //Extra checking to make sure that each reward exists + $rewardIds = $request->input('reward_ids'); + foreach ($rewardIds as $rewardId) { + /** @var Reward $reward */ + $this->fetchesReward->execute(['id' => $rewardId]); + } + + $object = new AchievementObject($milestone, $rewardIds); + $this->assignRewardProcessor->execute($object); + + return $this->resourceResponse(new MilestoneResource($milestone)); + } +} diff --git a/app/Classes/Modules/Milestones/ControllersLogic/DeleteMilestoneLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/DeleteMilestoneLogic.php new file mode 100644 index 00000000..5a2fbef2 --- /dev/null +++ b/app/Classes/Modules/Milestones/ControllersLogic/DeleteMilestoneLogic.php @@ -0,0 +1,74 @@ + 'Delete Milestone', + 'message' => 'You have successfully deleted the Milestone' + ]; + } + + /** @var CanDeleteMilestone */ + private $canDeleteMilestone; + + /** @var DeletesMilestone */ + private $deletesMilestone; + + /** @var FetchesMilestone */ + private $fetchesMiestone; + + + /** + * DeleteMilestoneLogic constructor. + * @param CanDeleteMilestone $canDeleteMilestone + * @param DeletesMilestone $deletesMilestone + * @param FetchesMilestone $fetchesMiestone + */ + public function __construct( + CanDeleteMilestone $canDeleteMilestone, + DeletesMilestone $deletesMilestone, + FetchesMilestone $fetchesMiestone + ) + { + $this->canDeleteMilestone = $canDeleteMilestone; + $this->deletesMilestone = $deletesMilestone; + $this->fetchesMiestone = $fetchesMiestone; + } + + /** + * @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->fetchesMiestone->execute(['id' => $request->route('id')]); + $this->canDeleteMilestone->passes(); + $this->deletesMilestone->execute($query); + + return $this->resourceResponse(new MilestoneResource($query)); + } + +} diff --git a/app/Classes/Modules/Milestones/ControllersLogic/ListMilestoneProgressLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestoneProgressLogic.php new file mode 100644 index 00000000..55c78eb2 --- /dev/null +++ b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestoneProgressLogic.php @@ -0,0 +1,50 @@ + 'Retrieved Milestone Progress', + 'message' => 'You have successfully retrieved a list of Milestone Progress' + ]; + } + + + /** @var ListsMilestoneProgress */ + private $listsMilestoneProgress; + + /** + * ListMilestoneProgressLogic constructor. + * @param ListsMilestoneProgress $listsMilestoneProgress + */ + public function __construct(ListsMilestoneProgress $listsMilestoneProgress) + { + $this->listsMilestoneProgress = $listsMilestoneProgress; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsMilestoneProgress->execute($this->listsMilestoneProgress->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(MilestoneProgressResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Milestones/ControllersLogic/ListMilestonesLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestonesLogic.php new file mode 100644 index 00000000..202041e8 --- /dev/null +++ b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestonesLogic.php @@ -0,0 +1,50 @@ + 'Retrieved Milestones', + 'message' => 'You have successfully retrieved a list of Milestones' + ]; + } + + + /** @var ListsMilestones */ + private $listsMilestones; + + /** + * ListMilestonesLogic constructor. + * @param ListsMilestones $listsMilestones + */ + public function __construct(ListsMilestones $listsMilestones) + { + $this->listsMilestones = $listsMilestones; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsMilestones->execute($this->listsMilestones->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(MilestoneResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Milestones/ControllersLogic/UpdateMilestoneLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/UpdateMilestoneLogic.php new file mode 100644 index 00000000..a7a0c655 --- /dev/null +++ b/app/Classes/Modules/Milestones/ControllersLogic/UpdateMilestoneLogic.php @@ -0,0 +1,73 @@ + 'Update Milestone', + 'message' => 'You have successfully updated a milestone' + ]; + } + + /** @var FetchesReward */ + private $fetchesReward; + + /** @var UpdateMilestoneProcessor */ + private $updateMilestoneProcessor; + + /** @var AssignRewardProcessor */ + private $assignRewardProcessor; + + /** + * UpdateMilestoneLogic constructor. + * @param UpdateMilestoneProcessor $updateMilestoneProcessor + * @param FetchesReward $fetchesReward + * @param AssignRewardProcessor $assignRewardProcessor + */ + public function __construct(UpdateMilestoneProcessor $updateMilestoneProcessor, FetchesReward $fetchesReward, AssignRewardProcessor $assignRewardProcessor) + { + $this->updateMilestoneProcessor = $updateMilestoneProcessor; + $this->fetchesReward = $fetchesReward; + $this->assignRewardProcessor = $assignRewardProcessor; + } + + /** + * @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 + { + $milestone = $this->updateMilestoneProcessor->execute($request); + + //Extra checking to make sure that each reward exists + $rewardIds = $request->input('reward_ids'); + foreach ($rewardIds as $rewardId) { + /** @var Reward $reward */ + $this->fetchesReward->execute(['id' => $rewardId]); + } + + $object = new AchievementObject($milestone, $rewardIds); + $this->assignRewardProcessor->execute($object); + + return $this->resourceResponse(new MilestoneResource($milestone)); + } +} diff --git a/app/Classes/Modules/Milestones/DataTransferObjects/AchievementObject.php b/app/Classes/Modules/Milestones/DataTransferObjects/AchievementObject.php new file mode 100644 index 00000000..c5eccfd4 --- /dev/null +++ b/app/Classes/Modules/Milestones/DataTransferObjects/AchievementObject.php @@ -0,0 +1,43 @@ +milestone = $milestone; + $this->rewardIds = $rewardIds; + } + + /** + * @return Milestone + */ + public function getMilestone(): Milestone + { + return $this->milestone; + } + + /** + * @return array + */ + public function getRewardIds(): array + { + return $this->rewardIds; + } + +} diff --git a/app/Classes/Modules/Milestones/DataTransferObjects/MilestoneObject.php b/app/Classes/Modules/Milestones/DataTransferObjects/MilestoneObject.php new file mode 100644 index 00000000..306a0502 --- /dev/null +++ b/app/Classes/Modules/Milestones/DataTransferObjects/MilestoneObject.php @@ -0,0 +1,55 @@ +id = $id; + $this->name = $name; + $this->description = $description; + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } +} diff --git a/app/Classes/Modules/Milestones/Processors/AssignRewardProcessor.php b/app/Classes/Modules/Milestones/Processors/AssignRewardProcessor.php new file mode 100644 index 00000000..a76166d5 --- /dev/null +++ b/app/Classes/Modules/Milestones/Processors/AssignRewardProcessor.php @@ -0,0 +1,44 @@ +canAssignReward = $canAssignReward; + $this->assignsReward = $assignsReward; + } + + /** + * @param AchievementObject $object + * @return Model + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function execute(AchievementObject $object): Model { + + $this->canAssignReward->passes($object); + + return $this->assignsReward->execute($object); + } + +} diff --git a/app/Classes/Modules/Milestones/Processors/CheckMilestonesForRewardProcessor.php b/app/Classes/Modules/Milestones/Processors/CheckMilestonesForRewardProcessor.php new file mode 100644 index 00000000..597fa8bc --- /dev/null +++ b/app/Classes/Modules/Milestones/Processors/CheckMilestonesForRewardProcessor.php @@ -0,0 +1,170 @@ +createsVoucherifyVoucher = $createsVoucherifyVoucher; + $this->createsVoucher = $createsVoucher; + $this->fetchesMilestone = $fetchesMilestone; + $this->createsMilestoneProgress = $createsMilestoneProgress; + $this->createsUserReward = $createsUserReward; + $this->fetchesVoucherifyVoucher = $fetchesVoucherifyVoucher; + $this->fetchesVoucher = $fetchesVoucher; + $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher; + } + + + /** + * @param User $user + * @param array $milestone_constants + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \Voucherify\ClientException + */ + public function execute(User $user, array $milestone_constants) + { + try{ + foreach($milestone_constants as $constant) + { + //update milestone progress + /** @var Milestone $milestone */ + $milestone = $this->fetchesMilestone->execute(['name' => $constant]); + $result = null; + if($user->milestoneProgress->count() > 0){ + $result = $user->milestoneProgress->where('milestone_id', $milestone->id)->first(); + } + + if(!$result){ + $result = $this->createsMilestoneProgress->execute($milestone, $user->id); + } + + if($result){ + //fetch completed miletones + $completedMilestones = MilestoneProgress::where('user_id', $user->id)->pluck('milestone_id'); + $rewards = $milestone->rewards; + + //check for rewards that have a corresponding milestone count + if(count($rewards) > 0){ + $this->checkMilestoneForReward($user, $completedMilestones, $rewards); + } + } + } + } catch (\Exception $e) { + Log::error($e); + } + } + + /** + * @param User $user + * @param object $completedMilestones + * @param object $rewards + * @return void + */ + private function checkMilestoneForReward(User $user, object $completedMilestones, object $rewards) + { + $milestoneIds = $rewards[0]->milestones->pluck('id'); + if ($milestoneIds->intersect($completedMilestones)->count() >= count($rewards[0]->milestones)) { + /** @var Reward $reward */ + foreach ($rewards as $reward) { + $result = null; + if (!$user->rewards->contains('reward_id', $reward->id)) { + $voucherId = 0; + $voucherName = ''; + $voucherType = ""; + if($reward->type == RewardType::REWARD_AMOUNT){ + //Voucherify - Create Voucher + $result = $this->createsVoucherifyVoucher->execute($user, intval($reward->value)); + } + else{ + //Voucherify - Validates Voucher + $ValidateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject(0, $reward->value, 0.00, $user); + $voucherifyVoucherValidated = $this->validatesVoucherifyVoucher->execute($ValidateVoucherifyVoucherObject); + + if(!isset($voucherifyVoucherValidated->reason)){ + //Voucherify - Get Voucher + $result = $this->fetchesVoucherifyVoucher->execute($user, $reward->value); + } + } + + if($result && !isset($result->reason)){ + $voucherName = isset($result->campaign) ? $result->campaign : $reward->name; + $voucherType = $result->discount->type; + $voucherValue = isset($result->discount->amount_off) ? $result->discount->amount_off : $result->discount->percent_off; + $voucherStartDate = $result->start_date; + $voucherEndDate = $result->expiration_date; + + //create voucher + $voucherObject = new VoucherObject($result->code, isset($voucherName) ? $voucherName : "", $voucherType, $voucherValue, $voucherStartDate, $voucherEndDate); + $voucher = $this->createsVoucher->execute($voucherObject); + if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]); + $voucherId = $voucher->id; + + //create reward to user (user_reward) + $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count(); + if($voucherCount == 0){ + $this->createsUserReward->execute($reward, $user, $voucherId); + } + } + else{ + Log::info('CheckMilestonesForRewardProcessor: no voucher fetched or created for reward '. json_encode($reward)); + } + } + } + } + } +} diff --git a/app/Classes/Modules/Milestones/Processors/CreateMilestoneProcessor.php b/app/Classes/Modules/Milestones/Processors/CreateMilestoneProcessor.php new file mode 100644 index 00000000..3c562d8d --- /dev/null +++ b/app/Classes/Modules/Milestones/Processors/CreateMilestoneProcessor.php @@ -0,0 +1,51 @@ +canCreateMilestone = $canCreateMilestone; + $this->createsMilestone = $createsMilestone; + } + + + /** + * @param Request $request + * @return Model + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function execute(Request $request): Model + { + $userMilestoneObject = new MilestoneObject( + $request->input('id'), + $request->input('name'), + $request->input('description') + ); + + $this->canCreateMilestone->passes($userMilestoneObject); + + return $this->createsMilestone->execute($userMilestoneObject); + } + +} diff --git a/app/Classes/Modules/Milestones/Processors/UpdateMilestoneProcessor.php b/app/Classes/Modules/Milestones/Processors/UpdateMilestoneProcessor.php new file mode 100644 index 00000000..b1fdbc14 --- /dev/null +++ b/app/Classes/Modules/Milestones/Processors/UpdateMilestoneProcessor.php @@ -0,0 +1,59 @@ +canUpdateMilestone = $canUpdateMilestone; + $this->updatesMilestone = $updatesMilestone; + $this->fetchesMilestone = $fetchesMilestone; + } + + + /** + * @param Request $request + * @return Model + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function execute(Request $request): Model + { + $milestoneObject = new MilestoneObject( + $request->input('id'), + $request->input('name'), + $request->input('description') + ); + + $this->canUpdateMilestone->passes($milestoneObject); + + $milestone = $this->fetchesMilestone->execute(['id' => $request->route('id')]); + + return $this->updatesMilestone->execute($milestone, $milestoneObject); + } + +} diff --git a/app/Classes/Modules/Milestones/Services/AssignsReward.php b/app/Classes/Modules/Milestones/Services/AssignsReward.php new file mode 100644 index 00000000..4d50a89e --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/AssignsReward.php @@ -0,0 +1,29 @@ +getMilestone()->rewards()->attach($object->getReward()->id); + $object->getMilestone()->rewards()->sync($object->getRewardIds()); + + return $object->getMilestone(); + + } catch (QueryException $exception){ + throw new MalformedRequestException($exception); + } + } +} diff --git a/app/Classes/Modules/Milestones/Services/CheckIfMilestoneProgressExists.php b/app/Classes/Modules/Milestones/Services/CheckIfMilestoneProgressExists.php new file mode 100644 index 00000000..f6b73820 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/CheckIfMilestoneProgressExists.php @@ -0,0 +1,27 @@ +repository = $repository; + } + + public function execute(int $user_id, int $milestone_id): bool { + return $this->repository->where('user_id', $user_id)->where('milestone_id', $milestone_id)->exists(); + } + +} diff --git a/app/Classes/Modules/Milestones/Services/CreatesMilestone.php b/app/Classes/Modules/Milestones/Services/CreatesMilestone.php new file mode 100644 index 00000000..9ec9e2d1 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/CreatesMilestone.php @@ -0,0 +1,25 @@ +name = $object->getName(); + $model->description = $object->getDescription(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Milestones/Services/CreatesMilestoneProgress.php b/app/Classes/Modules/Milestones/Services/CreatesMilestoneProgress.php new file mode 100644 index 00000000..bd04c624 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/CreatesMilestoneProgress.php @@ -0,0 +1,40 @@ +isMilestoneProgressExists = $isMilestoneProgressExists; + } + + + /** + * @param Milestone $milestone + * @param string $userId + * @return \Illuminate\Database\Eloquent\Model|null + */ + public function execute(Milestone $milestone, string $userId) { + if(!$this->isMilestoneProgressExists->execute($userId, $milestone->id)) + { + $model = new MilestoneProgress(); + $model->user_id = $userId; + + return $this->handler($milestone->progress(), $model); + } + + return null; + } +} diff --git a/app/Classes/Modules/Milestones/Services/DeletesMilestone.php b/app/Classes/Modules/Milestones/Services/DeletesMilestone.php new file mode 100644 index 00000000..f829d8e1 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/DeletesMilestone.php @@ -0,0 +1,19 @@ +handler($model); + } +} diff --git a/app/Classes/Modules/Milestones/Services/FetchesMilestone.php b/app/Classes/Modules/Milestones/Services/FetchesMilestone.php new file mode 100644 index 00000000..e0c12881 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/FetchesMilestone.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Milestones/Services/ListsMilestoneProgress.php b/app/Classes/Modules/Milestones/Services/ListsMilestoneProgress.php new file mode 100644 index 00000000..2cdf6de2 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/ListsMilestoneProgress.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Milestones/Services/ListsMilestones.php b/app/Classes/Modules/Milestones/Services/ListsMilestones.php new file mode 100644 index 00000000..dae7c076 --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/ListsMilestones.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Milestones/Services/UpdatesMilestone.php b/app/Classes/Modules/Milestones/Services/UpdatesMilestone.php new file mode 100644 index 00000000..fbe463aa --- /dev/null +++ b/app/Classes/Modules/Milestones/Services/UpdatesMilestone.php @@ -0,0 +1,24 @@ +name = $object->getName(); + $model->description = $object->getDescription(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanAssignReward.php b/app/Classes/Modules/Milestones/Standards/Rules/CanAssignReward.php new file mode 100644 index 00000000..35e8b31d --- /dev/null +++ b/app/Classes/Modules/Milestones/Standards/Rules/CanAssignReward.php @@ -0,0 +1,57 @@ +milestoneRewardValidation = $milestoneRewardValidation; + } + + /** + * @return bool + */ + protected function authorized(): bool + { + if (!Auth::user()->can('edit milestone')) { + return false; + } + return true; + } + + /** + * @param AchievementObject $object + * @return bool + * @throws \App\Classes\Exceptions\RequestValidationException + */ + protected function validators($object): bool + { + return $this->milestoneRewardValidation->validate($object); + } + + /** + * @param AchievementObject $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } + +} diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanCreateMilestone.php b/app/Classes/Modules/Milestones/Standards/Rules/CanCreateMilestone.php new file mode 100644 index 00000000..be25b64f --- /dev/null +++ b/app/Classes/Modules/Milestones/Standards/Rules/CanCreateMilestone.php @@ -0,0 +1,54 @@ +milestoneValidation = $milestoneValidation; + } + + /** + * @return bool + */ + protected function authorized(): bool + { + if (!Auth::user()->can('add milestone')) { + return false; + } + return true; + } + + /** + * @param MilestoneObject $object + * @return bool + * @throws \App\Classes\Exceptions\RequestValidationException + */ + protected function validators($object): bool + { + return $this->milestoneValidation->validate($object, 'POST'); + } + + /** + * @param MilestoneObject $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } +} diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanDeleteMilestone.php b/app/Classes/Modules/Milestones/Standards/Rules/CanDeleteMilestone.php new file mode 100644 index 00000000..c0c7fde7 --- /dev/null +++ b/app/Classes/Modules/Milestones/Standards/Rules/CanDeleteMilestone.php @@ -0,0 +1,44 @@ +can('delete milestone')) { + return false; + } + + return true; + + } + + /** + * @param MilestoneObject $object + * @return bool + */ + protected function validators($object): bool + { + return true; + + } + + + /** + * @param MilestoneObject $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } + +} diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanUpdateMilestone.php b/app/Classes/Modules/Milestones/Standards/Rules/CanUpdateMilestone.php new file mode 100644 index 00000000..9f7db714 --- /dev/null +++ b/app/Classes/Modules/Milestones/Standards/Rules/CanUpdateMilestone.php @@ -0,0 +1,54 @@ +milestoneValidation = $milestoneValidation; + } + + /** + * @return bool + */ + protected function authorized(): bool + { + if (!Auth::user()->can('edit milestone')) { + return false; + } + return true; + } + + /** + * @param MilestoneObject $object + * @return bool + * @throws \App\Classes\Exceptions\RequestValidationException + */ + protected function validators($object): bool + { + return $this->milestoneValidation->validate($object, 'PUT'); + } + + /** + * @param MilestoneObject $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } +} diff --git a/app/Classes/Modules/Milestones/Standards/Validators/MilestoneRewardValidation.php b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneRewardValidation.php new file mode 100644 index 00000000..a282b010 --- /dev/null +++ b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneRewardValidation.php @@ -0,0 +1,40 @@ + $object->getMilestone()->id, + 'reward_ids' => $object->getRewardIds() + ]; + } + + /** + * @return array + */ + protected function rules(): array + { + return [ + 'milestone_id' => 'required', + 'reward_ids' => 'required', + ]; + } + + /** + * @return array + */ + protected function messages(): array + { + return []; + } +} diff --git a/app/Classes/Modules/Milestones/Standards/Validators/MilestoneValidation.php b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneValidation.php new file mode 100644 index 00000000..81ffbced --- /dev/null +++ b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneValidation.php @@ -0,0 +1,68 @@ + $object->getName(), + 'description' => $object->getDescription(), + ]; + + return $data; + } + + /** + * @param null|string $type + * @return array + */ + protected function rules(?string $type = 'POST'): array { + if ($type == 'POST') { //crete + return [ + 'name' => [ + 'required', + function ($attribute, $value, $fail) { + // Check if milestone name already exists in the database + $existingMilestone = Milestone::where('name', $value)->first(); + if ($existingMilestone) { + $fail("The {$attribute} milestone name already exists in the database."); + } + }, + ], + 'description' => [ + 'required', + ] + ]; + + } + elseif($type == 'PUT') { //update + return [ + 'name' => [ + 'required', + ], + 'description' => [ + 'required', + ] + ]; + } + } + + /** + * @return array + */ + protected function messages(): array { + return []; + } + +} diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php index ed280098..6cd4f052 100644 --- a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php +++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php @@ -44,8 +44,7 @@ class FetchesPerfexCRMTask return (object) $data; }else{ - // Log::error($response); - Helper::debugLoggerForPerfexCRM($response); + Helper::debugLogger($response); return null; } }catch(\Exception $exception){ diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php index 27ff3165..6d91c081 100644 --- a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php +++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php @@ -38,8 +38,7 @@ class UpdatesPerfexCRMCustomer $data = $response->json(); return (object) $data; }else{ - // Log::error($response); - Helper::debugLoggerForPerfexCRM($response); + Helper::debugLogger($response); return null; } }catch(\Exception $exception){ diff --git a/app/Classes/Modules/Rewards/ControllersLogic/CreateRewardLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/CreateRewardLogic.php new file mode 100644 index 00000000..dc6fecf2 --- /dev/null +++ b/app/Classes/Modules/Rewards/ControllersLogic/CreateRewardLogic.php @@ -0,0 +1,67 @@ + 'Create/Update Reward', + 'message' => 'You have successfully created/updated a reward' + ]; + } + + /** @var CreatesReward */ + private $createsReward; + + /** @var CanCreateReward */ + private $canCreateReward; + + /** + * CreateRewardLogic constructor. + */ + public function __construct(CreatesReward $createsMilestone, CanCreateReward $canCreateReward) + { + $this->createsReward = $createsMilestone; + $this->canCreateReward = $canCreateReward; + } + + /** + * @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 + { + $userRewardObject = new RewardObject( + $request->input('id'), + $request->input('name'), + $request->input('description'), + $request->input('value'), + $request->input('type'), + $request->input('is_active'), + $request->input('order'), + ); + + $this->canCreateReward->passes($userRewardObject); + + $result = $this->createsReward->execute($userRewardObject); + + return $this->response(['data' => $result]); + } + + +} diff --git a/app/Classes/Modules/Rewards/ControllersLogic/DeleteRewardLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/DeleteRewardLogic.php new file mode 100644 index 00000000..6b2c306c --- /dev/null +++ b/app/Classes/Modules/Rewards/ControllersLogic/DeleteRewardLogic.php @@ -0,0 +1,74 @@ + 'Delete Reward', + 'message' => 'You have successfully deleted the Reward' + ]; + } + + /** @var CanDeleteReward */ + private $canDeleteReward; + + /** @var DeletesReward */ + private $deletesReward; + + /** @var FetchesReward */ + private $fetchesReward; + + + /** + * DeleteRewardLogic constructor. + * @param CanDeleteReward $canDeleteReward + * @param DeletesReward $deletesReward + * @param FetchesReward $fetchesReward + */ + public function __construct( + CanDeleteReward $canDeleteReward, + DeletesReward $deletesReward, + FetchesReward $fetchesReward + ) + { + $this->canDeleteReward = $canDeleteReward; + $this->deletesReward = $deletesReward; + $this->fetchesReward = $fetchesReward; + } + + /** + * @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->fetchesReward->execute(['id' => $request->route('id')]); + $this->canDeleteReward->passes(); + $this->deletesReward->execute($query); + + return $this->resourceResponse(new RewardResource($query)); + } + +} diff --git a/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsDetailsLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsDetailsLogic.php new file mode 100644 index 00000000..0f3cfda8 --- /dev/null +++ b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsDetailsLogic.php @@ -0,0 +1,49 @@ + 'Retrieved Rewards Details', + 'message' => 'You have successfully retrieved a list of Rewards Details' + ]; + } + + /** @var ListsRewards */ + private $listsRewards; + + /** + * ListRewardsDetailsLogic constructor. + * @param ListsRewards $listsRewards + */ + public function __construct(ListsRewards $listsRewards) + { + $this->listsRewards = $listsRewards; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsRewards->execute($this->listsRewards->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(RewardDetailsResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsLogic.php new file mode 100644 index 00000000..593947e6 --- /dev/null +++ b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsLogic.php @@ -0,0 +1,50 @@ + 'Retrieved Rewards', + 'message' => 'You have successfully retrieved a list of Rewards' + ]; + } + + + /** @var ListsRewards */ + private $listsRewards; + + /** + * ListRewardsLogic constructor. + * @param ListsRewards $listsRewards + */ + public function __construct(ListsRewards $listsRewards) + { + $this->listsRewards = $listsRewards; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsRewards->execute($this->listsRewards->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(RewardResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Rewards/DataTransferObjects/RewardObject.php b/app/Classes/Modules/Rewards/DataTransferObjects/RewardObject.php new file mode 100644 index 00000000..45092222 --- /dev/null +++ b/app/Classes/Modules/Rewards/DataTransferObjects/RewardObject.php @@ -0,0 +1,106 @@ +id = $id; + $this->name = $name; + $this->description = $description; + $this->value = $value; + $this->type = $type; + $this->isActive = $isActive; + $this->order = $order; + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @return bool + */ + public function getIsActive(): bool + { + return $this->isActive; + } + + /** + * @return int + */ + public function getOrder(): int + { + return $this->order ?? 9999; + } +} diff --git a/app/Classes/Modules/Rewards/Services/CreatesReward.php b/app/Classes/Modules/Rewards/Services/CreatesReward.php new file mode 100644 index 00000000..c9a16fdc --- /dev/null +++ b/app/Classes/Modules/Rewards/Services/CreatesReward.php @@ -0,0 +1,45 @@ +name = $object->getName(); + // $model->description = $object->getDescription(); + // $model->value = $object->getValue(); + // $model->type = $object->getType(); + // $model->is_active = $object->getIsActive(); + // return $this->handler($model); + + try{ + $data = [ + 'id' => $object->getId(), + 'name' => $object->getName(), + 'description' => $object->getDescription(), + 'value' => $object->getValue(), + 'type' => $object->getType(), + 'is_active' => $object->getIsActive(), + 'order' => $object->getOrder(), + ]; + + return Reward::upsert([$data], ['id'], ['name', 'description', 'value', 'type', 'is_active', 'order']); + + } catch (QueryException $exception){ + throw new MalformedRequestException($exception); + } + } +} diff --git a/app/Classes/Modules/Rewards/Services/CreatesUserReward.php b/app/Classes/Modules/Rewards/Services/CreatesUserReward.php new file mode 100644 index 00000000..8a7f5d29 --- /dev/null +++ b/app/Classes/Modules/Rewards/Services/CreatesUserReward.php @@ -0,0 +1,32 @@ +user_id = $user->id; + $model->voucher_id = $voucherId; + return $this->handler($reward->users(), $model); + } + else{ + $model = new UserReward(); + $model->voucher_id = $voucherId; + return $this->handler($user->rewards(), $model); + } + } +} diff --git a/app/Classes/Modules/Rewards/Services/DeletesReward.php b/app/Classes/Modules/Rewards/Services/DeletesReward.php new file mode 100644 index 00000000..5d3d9133 --- /dev/null +++ b/app/Classes/Modules/Rewards/Services/DeletesReward.php @@ -0,0 +1,19 @@ +handler($model); + } +} diff --git a/app/Classes/Modules/Rewards/Services/FetchesReward.php b/app/Classes/Modules/Rewards/Services/FetchesReward.php new file mode 100644 index 00000000..ab336092 --- /dev/null +++ b/app/Classes/Modules/Rewards/Services/FetchesReward.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Rewards/Services/ListsRewards.php b/app/Classes/Modules/Rewards/Services/ListsRewards.php new file mode 100644 index 00000000..79d0d1d4 --- /dev/null +++ b/app/Classes/Modules/Rewards/Services/ListsRewards.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Rewards/Services/ListsUserRewards.php b/app/Classes/Modules/Rewards/Services/ListsUserRewards.php new file mode 100644 index 00000000..d81761cd --- /dev/null +++ b/app/Classes/Modules/Rewards/Services/ListsUserRewards.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Rewards/Standards/Rules/CanCreateReward.php b/app/Classes/Modules/Rewards/Standards/Rules/CanCreateReward.php new file mode 100644 index 00000000..5249b35c --- /dev/null +++ b/app/Classes/Modules/Rewards/Standards/Rules/CanCreateReward.php @@ -0,0 +1,55 @@ +rewardValidation = $rewardValidation; + } + + /** + * @return bool + */ + protected function authorized(): bool + { + if (!Auth::user()->can('add reward')) { + return false; + } + + return true; + } + + /** + * @param RewardObject $object + * @return bool + * @throws \App\Classes\Exceptions\RequestValidationException + */ + protected function validators($object): bool + { + return true; // $this->rewardValidation->validate($object, 'POST'); + } + + /** + * @param RewardObject $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } +} diff --git a/app/Classes/Modules/Rewards/Standards/Rules/CanDeleteReward.php b/app/Classes/Modules/Rewards/Standards/Rules/CanDeleteReward.php new file mode 100644 index 00000000..888b0d1e --- /dev/null +++ b/app/Classes/Modules/Rewards/Standards/Rules/CanDeleteReward.php @@ -0,0 +1,44 @@ +can('delete reward')) { + return false; + } + + return true; + + } + + /** + * @param RewardObject $object + * @return bool + */ + protected function validators($object): bool + { + return true; + + } + + + /** + * @param RewardObject $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } + +} diff --git a/app/Classes/Modules/Rewards/Standards/Validators/RewardValidation.php b/app/Classes/Modules/Rewards/Standards/Validators/RewardValidation.php new file mode 100644 index 00000000..18d875dc --- /dev/null +++ b/app/Classes/Modules/Rewards/Standards/Validators/RewardValidation.php @@ -0,0 +1,55 @@ + $object->getName(), + 'description' => $object->getDescription(), + ]; + + return $data; + } + + /** + * @param null|string $type + * @return array + */ + protected function rules(): array { + return [ + 'name' => [ + 'required', + function ($attribute, $value, $fail) { + // Check if reward name already exists in the database + $existingMilestone = Reward::where('name', $value)->first(); + if ($existingMilestone) { + $fail("The {$attribute} reward name already exists in the database."); + } + }, + ], + 'description' => [ + 'required', + ] + ]; + } + + /** + * @return array + */ + protected function messages(): array { + return []; + } + +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php index 733f2288..3771c559 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php @@ -83,8 +83,10 @@ class CreateBulkPurchaseOrderTransactionLogic extends AbstractControllerLogic foreach($completed_transactions as $transaction){ $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); + $voucherRedemption = $transaction->voucherRedemption; + // purchase order - $this->invoiceDocumentProcessor->execute($transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER); + $this->invoiceDocumentProcessor->execute($transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption); } } } diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php index fdda9019..46534d77 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php @@ -14,6 +14,7 @@ use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\CompanyType; use App\Classes\ValueObjects\Constants\DocumentType; +use App\Classes\Jobs\SendUserPaymentProofUploadedEmail; use App\Models\Company; use App\Models\Document; use Illuminate\Http\JsonResponse; @@ -50,6 +51,9 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic /** @var CreateInvoiceTransactionProcessor */ private $createInvoiceTransactionProcessor; + /** @var SendUserPaymentProofUploadedEmail */ + private $sendUserPaymentProofUploadedEmail; + /** * CreatePaymentProofDocumentLogic constructor. * @param FetchesTransaction $fetchesTransaction @@ -58,13 +62,14 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic * @param UpdatesTransactionStatus $updatesTransactionStatus * @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor */ - public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor) + public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor, SendUserPaymentProofUploadedEmail $sendUserPaymentProofUploadedEmail) { $this->fetchesTransaction = $fetchesTransaction; $this->createsDocument = $createsDocument; $this->createsFile = $createsFile; $this->updatesTransactionStatus = $updatesTransactionStatus; $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor; + $this->sendUserPaymentProofUploadedEmail = $sendUserPaymentProofUploadedEmail; } /** @@ -82,12 +87,21 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic /** @var Document $document */ $document = $this->createsDocument->execute($transaction, $object); - $this->createsFile->execute($document, $object); + $file = $this->createsFile->execute($document, $object); $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); $this->createInvoiceTransactionProcessor->execute($transaction->owner->booking); + // send email to customer + // todo: a function to send a proof to the receipiant, they have to give us a email of the receipiant and also need to submiited purchase order + $companyEmployee = $transaction->owner->booking->company->employees; + foreach ($companyEmployee as $employee) { + if (app()->environment('production') || in_array($employee->email, ['cief.enquirycntr@gmail.com', 'tech.ciefmalaysia@gmail.com'])) { + $this->sendUserPaymentProofUploadedEmail::dispatch($employee, $transaction->owner->booking, $file[0]); + } + } + return $this->response([]); } diff --git a/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php new file mode 100644 index 00000000..c16ac453 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php @@ -0,0 +1,64 @@ + 'Retrieved Company Transaction Statement', + 'message' => 'You have successfully retrieved company transaction statement' + ]; + } + + /** + * FetchCompanyAccountBalanceLogic constructor. + */ + public function __construct() + { + } + + public function logic(Request $request): JsonResponse + { + $companyId = $request->route('id'); + + $transactions = Transaction::where(function ($query) use ($companyId) { + $query + ->where('type', TransactionType::PAYMENT) + ->where('owner_type', Booking::class) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereHas('booking', function ($query) use ($companyId) { + $query->where('company_id', $companyId); + }); + }) + ->orWhere(function ($query) use ($companyId) { + $query->whereHas('owner', function ($query) use ($companyId) { + $query->where('owner_id', $companyId); + $query->where('owner_type', Company::class); + }) + ->where('owner_type', Wallet::class) + ->where('type', '!=', TransactionType::PAYMENT); + }) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->orderBy('created_at', 'desc') + ->get(); + + return $this->collectionResponse(PaymentTransactionResource::collection($transactions)); + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php index e824547e..0500d6ae 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php @@ -7,6 +7,9 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Transactions\Services\FetchesTransaction; use App\Classes\Modules\Transactions\Services\ListsTransactions; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; +use App\Classes\Modules\Vouchers\Services\FetchesVoucherRedemption; +use App\Classes\Modules\Vouchers\Services\CreatesVoucherRedemption; +use App\Classes\Modules\Vouchers\Services\RollbacksRedemption; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Http\Resources\BookingResource; use App\Http\Resources\TransactionResource; @@ -32,16 +35,27 @@ class SuspendTransactionLogic extends AbstractControllerLogic /** @var UpdatesTransactionStatus */ private $updatesTransactionStatus; + /** @var RollbacksRedemption */ + private $rollbacksRedemption; + + /** @var FetchesVoucherRedemption */ + private $fetchesVoucherRedemption; + + /** @var CreatesVoucherRedemption */ + private $createsVoucherRedemption; /** * SuspendTransactionLogic constructor. * @param FetchesTransaction $fetchesTransaction * @param UpdatesTransactionStatus $updatesTransactionStatus */ - public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus) + public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, RollbacksRedemption $rollbacksRedemption, FetchesVoucherRedemption $fetchesVoucherRedemption, CreatesVoucherRedemption $createsVoucherRedemption) { $this->fetchesTransaction = $fetchesTransaction; $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->rollbacksRedemption = $rollbacksRedemption; + $this->fetchesVoucherRedemption = $fetchesVoucherRedemption; + $this->createsVoucherRedemption = $createsVoucherRedemption; } @@ -52,6 +66,14 @@ class SuspendTransactionLogic extends AbstractControllerLogic $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::SUSPENDED); + if($transaction->voucherRedemption) { + $result = $this->rollbacksRedemption->execute($transaction->voucherRedemption->redemption_id); + if($result){ + $redemptionId = $result->id; + $this->createsVoucherRedemption->execute($transaction, $transaction->voucherRedemption->voucher, $redemptionId, $transaction->voucherRedemption->value); + } + } + return $this->response([]); } diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php index b0af2cbf..6215a1e1 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php @@ -38,11 +38,11 @@ class CreateInvoiceDocumentProcessor * @return void * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function execute($transaction, $purchaseOrder, $supplier, $document_type) + public function execute($transaction, $purchaseOrder, $supplier, $document_type, $voucherRedemption = null) { $lowercaseDocumentType = strtolower($document_type); - $order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType, ['transaction' => $transaction, 'po_order_transaction' => $purchaseOrder, 'supplier' => $supplier]); + $order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType, ['transaction' => $transaction, 'po_order_transaction' => $purchaseOrder, 'supplier' => $supplier, 'voucher_redemption' => $voucherRedemption]); if($purchaseOrder->booking->service_id === 4) { $purchaseOrderDocuments = $purchaseOrder->booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get(); diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php index 391f6db9..7f105dc8 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php @@ -116,9 +116,13 @@ class CreateInvoiceTransactionProcessor return; } + // $transaction = $booking->transactions() + // ->where('type', TransactionType::PAYMENT) + // ->first(); + $transaction = $booking->transactions() - ->where('type', TransactionType::PAYMENT) - ->first(); + ->where('type', TransactionType::PAYMENT) + ->latest()->get()[0]; $billNumber = $this->generatesTransactionBillNumber->execute('INV-'); @@ -153,16 +157,18 @@ class CreateInvoiceTransactionProcessor ); $invoice_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); + $voucherRedemption = $transaction->voucherRedemption; + $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); // purchase order - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER); + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption); // deliver order - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER); + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER, $voucherRedemption); // invoice - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE); + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE, $voucherRedemption); $billNumber = $this->generatesTransactionBillNumber->execute('SPDO-'); @@ -191,7 +197,7 @@ class CreateInvoiceTransactionProcessor $supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); // supply deliver order - $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER); + $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null); $this->updatesBookingStatus->execute($booking, ApprovalStatus::COMPLETED); diff --git a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php index 9d6b9fe4..d92e3e9a 100644 --- a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php +++ b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php @@ -5,19 +5,27 @@ namespace App\Classes\Modules\Transactions\Services; use App\Classes\General\Eloquent\AbstractUpdateRecord; use App\Classes\Modules\PerfexCRM\Processors\TransactionToPerfexCRMProcessorV2; use App\Models\Transaction; -use App\Classes\Modules\PerfexCRM\Processors\TransactionToPerfexCRMProcessor; +use App\Classes\Modules\Vouchers\Processors\Voucherify\TransactionToVoucherifyProcessor; +use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\TransactionType; class UpdatesTransactionStatus extends AbstractUpdateRecord { /** @var TransactionToPerfexCRMProcessorV2 */ private $transactionToPerfexCRMProcessor; + /** @var TransactionToVoucherifyProcessor */ + private $transactionToVoucherifyProcessor; + /** + * UpdatesTransactionStatus constructor. * @param TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor + * @param TransactionToVoucherifyProcessor $transactionToVoucherifyProcessor */ - public function __construct(TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor) + public function __construct(TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor, TransactionToVoucherifyProcessor $transactionToVoucherifyProcessor) { $this->transactionToPerfexCRMProcessor = $transactionToPerfexCRMProcessor; + $this->transactionToVoucherifyProcessor = $transactionToVoucherifyProcessor; } @@ -35,6 +43,11 @@ class UpdatesTransactionStatus extends AbstractUpdateRecord if(config('perfexcrm.is_enabled') == 'true'){ $this->transactionToPerfexCRMProcessor->execute($transaction, $status); } + + if($status == ApprovalStatus::APPROVED && ($transaction->type == TransactionType::PAYMENT || $transaction->type == TransactionType::TOP_UP)){ + $this->transactionToVoucherifyProcessor->execute($transaction, "PAID"); + } + return $result; } } diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/CreateVoucherLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/CreateVoucherLogic.php new file mode 100644 index 00000000..a5d65544 --- /dev/null +++ b/app/Classes/Modules/Vouchers/ControllersLogic/CreateVoucherLogic.php @@ -0,0 +1,49 @@ + 'Create Voucher', + 'message' => 'You have successfully created a voucher' + ]; + } + + /** @var CreateVoucherProcessor */ + private $createVoucherProcessor; + + /** + * CreateVoucherLogic constructor. + * @param CreateVoucherProcessor $createVoucherProcessor + */ + public function __construct(CreateVoucherProcessor $createVoucherProcessor) + { + $this->createVoucherProcessor = $createVoucherProcessor; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $user = User::where('id', $request->input('userId'))->first(); + $result = $this->createVoucherProcessor->execute($user, $request->input('voucherCode'), null); + return $this->response(['data' => $result]); + } +} diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php new file mode 100644 index 00000000..16356f0f --- /dev/null +++ b/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php @@ -0,0 +1,46 @@ +listsUserRewards = $listsUserRewards; + } + + /** + * @return array + */ + protected function notification():array { + return [ + 'title' => 'Retrieved User Vouchers', + 'message' => 'You have successfully retrieved a list of user vouchers' + ]; + } + + /** @var ListsUserRewards */ + private $listsUserRewards; + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsUserRewards->execute($this->listsUserRewards->deserializeFilters($request->input('filters'))); + return $this->collectionResponse(UserRewardResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php new file mode 100644 index 00000000..4b820eb1 --- /dev/null +++ b/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php @@ -0,0 +1,53 @@ + 'Fetch Voucher', + 'message' => 'You have successfully fetched a voucher' + ]; + } + + /** @var ValidatesVoucherifyVoucher */ + private $validatesVoucherifyVoucher; + + /** + * ValidateVoucherLogic constructor. + * @param ValidatesVoucherifyVoucher $validatesVoucherifyVoucher + */ + public function __construct(ValidatesVoucherifyVoucher $validatesVoucherifyVoucher) + { + $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + $booking = Booking::find($request->input('itemId')); + $employee = $booking->company->employees()->first(); + + $validateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject($booking->company_id, $request->input('voucherCode'), $request->input('amount'), $employee); + $result = $this->validatesVoucherifyVoucher->execute($validateVoucherifyVoucherObject); + return $this->response(['data' => $result]); + } + +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyCustomerObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyCustomerObject.php new file mode 100644 index 00000000..9678dafe --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyCustomerObject.php @@ -0,0 +1,74 @@ +companyId = $companyId; + $this->user = $user; + $this->isNew = $isNew; + $this->acquisitionChannel = $acquisitionChannel; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + + /** + * @return User + */ + public function getUser(): User + { + return $this->user; + } + + + /** + * @return bool + */ + public function getIsNew(): bool + { + return $this->isNew; + } + + /** + * @return string + */ + public function getAcquisitionChannel(): string + { + if(!$this->isNew){ + return ""; + } + return $this->acquisitionChannel; + } + +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php new file mode 100644 index 00000000..92dfd592 --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php @@ -0,0 +1,82 @@ +employee = $employee; + $this->companyId = $companyId; + $this->amount = $amount; + $this->isNoVoucher = $isNoVoucher; + $this->isTopUpWallet = $isTopUpWallet; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + /** + * @return float + */ + public function getAmount(): float + { + return $this->amount; + } + + /** + * @return User + */ + public function getEmployee(): User + { + return $this->employee; + } + + /** + * @return bool + */ + public function getIsNoVoucher(): bool + { + return $this->isNoVoucher; + } + + /** + * @return bool + */ + public function getIsTopUpWallet(): bool + { + return $this->isTopUpWallet; + } +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php new file mode 100644 index 00000000..01f5431c --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php @@ -0,0 +1,69 @@ +companyId = $companyId; + $this->promoCode = $promoCode; + $this->amount = $amount; + $this->employee = $employee; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + + /** + * @return string + */ + public function getPromoCode(): string + { + return $this->promoCode; + } + + /** + * @return string + */ + public function getAmount(): string + { + return $this->amount; + } + + /** + * @return object + */ + public function getEmployee(): object + { + return $this->employee; + } + +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/UpdateVoucherifyOrderObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/UpdateVoucherifyOrderObject.php new file mode 100644 index 00000000..bc49ee86 --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/UpdateVoucherifyOrderObject.php @@ -0,0 +1,42 @@ +id = $id; + $this->status = $status; + } + + /** + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * @return string + */ + public function getStatus(): string + { + return $this->status; + } +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/ValidateVoucherifyVoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidateVoucherifyVoucherObject.php new file mode 100644 index 00000000..b48bee16 --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidateVoucherifyVoucherObject.php @@ -0,0 +1,70 @@ +companyId = $companyId; + $this->voucherCode = $voucherCode; + $this->amount = $amount; + $this->user = $user; + } + + /** + * @return int + */ + public function getCompanyId(): int + { + return $this->companyId; + } + + /** + * @return string + */ + public function getVoucherCode(): string + { + return $this->voucherCode; + } + + /** + * @return float + */ + public function getAmount(): float + { + return $this->amount; + } + + /** + * @return User + */ + public function getUser(): User + { + return $this->user; + } + +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/ValidatedVoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidatedVoucherObject.php new file mode 100644 index 00000000..c3e766c2 --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidatedVoucherObject.php @@ -0,0 +1,82 @@ +voucher_name = $voucher_name; + $this->code = $code; + $this->discount_type = $discount_type; + $this->total_discount_amount = $total_discount_amount; + $this->total_amount = $total_amount; + } + + /** + * @return string + */ + public function getVoucherName(): string + { + return $this->voucher_name; + } + + /** + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * @return string + */ + public function getDiscountType(): string + { + return $this->discount_type; + } + + /** + * @return float + */ + public function getTotalDiscountAmount(): float + { + return $this->total_discount_amount / 100; + } + + /** + * @return float + */ + public function getTotalAmount(): float + { + return $this->total_amount / 100; + } + +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherEntityObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherEntityObject.php new file mode 100644 index 00000000..692b221b --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherEntityObject.php @@ -0,0 +1,41 @@ +id = $id; + $this->type = $type; + } + + /** + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } +} diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php new file mode 100644 index 00000000..f624b3cc --- /dev/null +++ b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php @@ -0,0 +1,111 @@ +code = $code; + $this->name = $name; + $this->type = $type; + $this->value = $value; + $this->startDate = $startDate; + $this->endDate = $endDate; + } + + /** + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * @return string + */ + public function getName(): ?string + { + return $this->name; + } + + + /** + * @return string + */ + public function getType(): ?string + { + return $this->type; + } + + + /** + * @return float + */ + public function getValue(): ?float + { + return $this->value; + } + + /** + * @return DateTime + */ + public function getStartDate(): ?DateTime + { + try { + if(!$this->startDate) return null; + $dateTime = new DateTime($this->startDate); + return $dateTime; + } catch (\Exception $e) { + Log::error($e); + return null; + } + } + + /** + * @return DateTime + */ + public function getEndDate(): ?DateTime + { + try { + if(!$this->endDate) return null; + $dateTime = new DateTime($this->endDate); + return $dateTime; + } catch (\Exception $e) { + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Processors/CreateVoucherProcessor.php b/app/Classes/Modules/Vouchers/Processors/CreateVoucherProcessor.php new file mode 100644 index 00000000..690ad59f --- /dev/null +++ b/app/Classes/Modules/Vouchers/Processors/CreateVoucherProcessor.php @@ -0,0 +1,114 @@ +fetchesVoucherifyVoucher = $fetchesVoucherifyVoucher; + $this->createsVoucher = $createsVoucher; + $this->createsUserReward = $createsUserReward; + $this->fetchesVoucher = $fetchesVoucher; + $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher; + } + + + /** + * @param ?User $userParam + * @param string $voucherCodeInput + * @return array + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function execute(?User $userParam, string $voucherCodeInput) { + try{ + $result = null; + $user = Auth::user(); /** @var User $user */ + if($user && isset($user->type) && in_array($user->type, RoleTypes::ADMIN_ROLES) && $userParam){ + $user = User::where('id', $userParam->id)->first(); + } + else{ + $user = $userParam ? $userParam : $user; + } + + //Voucherify - Validates Voucher + $ValidateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject(0, $voucherCodeInput, 0.00, $user); + $voucherifyVoucherValidated = $this->validatesVoucherifyVoucher->execute($ValidateVoucherifyVoucherObject); + + if(isset($voucherifyVoucherValidated->reason)){ + $result = []; + $result['reason'] = $voucherifyVoucherValidated->reason; + } + else if($voucherifyVoucherValidated){ + $voucher = $this->recordVoucherInfo($user, $voucherCodeInput); + $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count(); + if($voucherCount == 0){ + $result = $this->createsUserReward->execute(null, $user, $voucher->id); + } + else{ + $result['reason'] = 'Voucher already added'; + } + } + + return $result; + } catch (\Exception $e) { + Log::error($e); + } + } + + private function recordVoucherInfo(User $user, string $voucherCodeInput){ + //Voucherify - Get Voucher + $voucherifyVoucherFetched = $this->fetchesVoucherifyVoucher->execute($user, $voucherCodeInput); + + $voucherName = $voucherifyVoucherFetched->campaign; + $voucherType = $voucherifyVoucherFetched->discount->type; + $voucherValue = isset($voucherifyVoucherFetched->discount->amount_off) ? $voucherifyVoucherFetched->discount->amount_off : $voucherifyVoucherFetched->discount->percent_off; + $voucherCode = $voucherifyVoucherFetched->code; + $voucherStartDate = $voucherifyVoucherFetched->start_date; + $voucherEndDate = $voucherifyVoucherFetched->expiration_date; + + $voucherObject= new VoucherObject($voucherCode, isset($voucherName) ? $voucherName : "Voucherify Voucher Added Manually", $voucherType, $voucherValue, $voucherStartDate, $voucherEndDate); + $voucher = $this->createsVoucher->execute($voucherObject); + if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherCodeInput]); + return $voucher; + } +} diff --git a/app/Classes/Modules/Vouchers/Processors/Voucherify/BookingToVoucherifyProcessor.php b/app/Classes/Modules/Vouchers/Processors/Voucherify/BookingToVoucherifyProcessor.php new file mode 100644 index 00000000..3df08a4c --- /dev/null +++ b/app/Classes/Modules/Vouchers/Processors/Voucherify/BookingToVoucherifyProcessor.php @@ -0,0 +1,163 @@ +createsVoucher = $createsVoucher; + $this->fetchesVoucher = $fetchesVoucher; + $this->createsVoucherRedemption = $createsVoucherRedemption; + $this->redeemsVoucherifyVoucher = $redeemsVoucherifyVoucher; + $this->createsVoucherifyOrder = $createsVoucherifyOrder; + $this->createsVoucherEntityMapping = $createsVoucherEntityMapping; + $this->createsUserReward = $createsUserReward; + } + + + /** + * @param User $user + * @param Transaction $transaction + * @param int $companyId + * @param float $amount + * @param float $voucherDiscountAmount + * @param string $voucherCode + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \Voucherify\ClientException + */ + public function execute(User $user, Transaction $transaction, int $companyId, float $amount, float $voucherDiscountAmount, ?string $voucherCode = "") + { + try{ + $voucherify_customer_id = ""; + $voucherify_order_id = ""; + if($voucherCode){ + $redeemVoucherifyVoucherObject = new RedeemVoucherifyVoucherObject($companyId, $voucherCode, $amount, $user); + $redeemVoucherResult = $this->redeemsVoucherifyVoucher->execute($redeemVoucherifyVoucherObject); + $redeemedVoucher = $redeemVoucherResult->voucher; + $redemptionId = $redeemVoucherResult->id; + + if($redeemVoucherResult && isset($redeemVoucherResult->order)){ + $voucherify_order_id = $redeemVoucherResult->order->id; + } + + if($redeemVoucherResult && isset($redeemVoucherResult->customer)){ + $voucherify_customer_id = $redeemVoucherResult->customer->id; + } + + $voucher = $this->recordVoucherInfo($redeemedVoucher, $transaction); + $this->createsVoucherRedemption->execute($transaction, $voucher, $redemptionId, $voucherDiscountAmount); + $this->recordVoucherForUserInfo($user, $voucher); + } + else{ + $createVoucherifyOrderObject = new CreateVoucherifyOrderObject($user, $companyId, $amount, true, $transaction->type == TransactionType::TOP_UP); + $createVoucherufyOrderResult = $this->createsVoucherifyOrder->execute($createVoucherifyOrderObject); + + if($createVoucherufyOrderResult && isset($createVoucherufyOrderResult->id)){ + $voucherify_order_id = $createVoucherufyOrderResult->id; + if(isset($createVoucherufyOrderResult->customer)){ + $voucherify_customer_id = $createVoucherufyOrderResult->customer->id; + } + } + } + + $this->recordVoucherifyOrderInfo($voucherify_order_id, $transaction); + $this->recordVoucherifyCustomerInfo($voucherify_customer_id, $user); + + } catch (\Exception $e) { + Log::error($e); + } + } + + private function recordVoucherInfo(object $redeemedVoucher){ + //Create records at 3 tables + $voucherValue = isset($redeemedVoucher->discount->amount_off) ? $redeemedVoucher->discount->amount_off : $redeemedVoucher->discount->percent_off; + $voucherType = $redeemedVoucher->discount ? $redeemedVoucher->discount->type : null; + + $voucherObject= new VoucherObject($redeemedVoucher->code, isset($redeemedVoucher->metadata->name) ? $redeemedVoucher->metadata->name : "", $voucherType, $voucherValue); + $voucher = $this->createsVoucher->execute($voucherObject); + if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]); + + return $voucher; + } + + private function recordVoucherForUserInfo(User $user, Voucher $voucher){ + //create reward to user (user_reward) + $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count(); + if($voucherCount == 0){ + $this->createsUserReward->execute(null, $user, $voucher->id); + } + } + + private function recordVoucherifyOrderInfo(string $voucherify_order_id, Transaction $transaction){ + //Update Database - 1 table + if($voucherify_order_id){ + $voucherify_entity = $transaction->voucherifyEntities()->first(); + if(!$voucherify_entity){ + $voucherEntityObject = new VoucherEntityObject($voucherify_order_id, VoucherifyEntityType::ORDER); + $this->createsVoucherEntityMapping->execute($transaction, $voucherEntityObject); + } + } + } + + private function recordVoucherifyCustomerInfo(string $voucherify_customer_id, User $user){ + //Update Database - 1 table + if($voucherify_customer_id){ + $voucherify_entity = $user->voucherifyEntities()->first(); + if(!$voucherify_entity){ + $voucherEntityObject = new VoucherEntityObject($voucherify_customer_id, VoucherifyEntityType::CUSTOMER); + $this->createsVoucherEntityMapping->execute($user, $voucherEntityObject); + } + } + } +} diff --git a/app/Classes/Modules/Vouchers/Processors/Voucherify/NewCustomerToVoucherifyProcessor.php b/app/Classes/Modules/Vouchers/Processors/Voucherify/NewCustomerToVoucherifyProcessor.php new file mode 100644 index 00000000..006bcd94 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Processors/Voucherify/NewCustomerToVoucherifyProcessor.php @@ -0,0 +1,55 @@ +createsVoucherEntityMapping = $createsVoucherEntityMapping; + $this->createsVoucherifyCustomer = $createsVoucherifyCustomer; + } + + + /** + * @param int $companyId + * @param User $user + * @param bool $isNew + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(int $companyId, User $user, bool $isNew) + { + try + { + $createVoucherifyCustomerObject = new CreateVoucherifyCustomerObject($companyId, $user, $isNew); + $result = $this->createsVoucherifyCustomer->execute($createVoucherifyCustomerObject); + + if($result && isset($result->id)){ + $voucherEntityObject = new VoucherEntityObject($result->id, VoucherifyEntityType::CUSTOMER); + $this->createsVoucherEntityMapping->execute($createVoucherifyCustomerObject->getUser(), $voucherEntityObject); + } + } catch (\Exception $e) { + Log::error($e); + } + } +} diff --git a/app/Classes/Modules/Vouchers/Processors/Voucherify/TransactionToVoucherifyProcessor.php b/app/Classes/Modules/Vouchers/Processors/Voucherify/TransactionToVoucherifyProcessor.php new file mode 100644 index 00000000..46dbf7a1 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Processors/Voucherify/TransactionToVoucherifyProcessor.php @@ -0,0 +1,44 @@ +updatesVoucherifyOrder = $updatesVoucherifyOrder; + } + + + /** + * @param Transaction $transaction + * @param string $status + * @return void + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \Voucherify\ClientException + */ + public function execute(Transaction $transaction, string $status) + { + try{ + $voucherify_entity = $transaction->voucherifyEntities()->first(); + if($voucherify_entity){ + $updateVoucherifyOrderObject = new UpdateVoucherifyOrderObject($voucherify_entity->voucherify_entity_id, $status); + $this->updatesVoucherifyOrder->execute($updateVoucherifyOrderObject); + } + } catch (\Exception $e) { + Log::error($e); + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/CheckIfVoucherExists.php b/app/Classes/Modules/Vouchers/Services/CheckIfVoucherExists.php new file mode 100644 index 00000000..9bd38dfd --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/CheckIfVoucherExists.php @@ -0,0 +1,27 @@ +repository = $repository; + } + + public function execute(string $code): bool { + return $this->repository->where('code', $code)->exists(); + } + +} diff --git a/app/Classes/Modules/Vouchers/Services/CreatesVoucher.php b/app/Classes/Modules/Vouchers/Services/CreatesVoucher.php new file mode 100644 index 00000000..9162dda8 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/CreatesVoucher.php @@ -0,0 +1,42 @@ +voucherExists = $voucherExists; + } + + /** + * @param VoucherObject $object + * @return \Illuminate\Database\Eloquent\Model + * @throws \App\Classes\Exceptions\MalformedRequestException + */ + public function execute(VoucherObject $object) { + if(!$this->voucherExists->execute($object->getCode())) + { + $model = new Voucher(); + $model->code = $object->getCode(); + $model->name = $object->getName(); + $model->type = $object->getType(); + $model->value = $object->getValue(); + $model->start_date = $object->getStartDate(); + $model->end_date = $object->getEndDate(); + return $this->handler($model); + } + return null; + } +} diff --git a/app/Classes/Modules/Vouchers/Services/CreatesVoucherEntityMapping.php b/app/Classes/Modules/Vouchers/Services/CreatesVoucherEntityMapping.php new file mode 100644 index 00000000..1a1edb28 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/CreatesVoucherEntityMapping.php @@ -0,0 +1,24 @@ +voucherify_entity_id = $object->getId(); + $model->voucherify_entity_type = $object->getType(); + return $this->handler($voucherifable->voucherifyEntities(), $model); + } +} diff --git a/app/Classes/Modules/Vouchers/Services/CreatesVoucherRedemption.php b/app/Classes/Modules/Vouchers/Services/CreatesVoucherRedemption.php new file mode 100644 index 00000000..c032031e --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/CreatesVoucherRedemption.php @@ -0,0 +1,28 @@ +voucher_id = $voucher->id; + $model->redemption_id = $redemptionId; + $model->value = $value; + + return $this->handler($transaction->voucherRedemption(), $model); + } +} diff --git a/app/Classes/Modules/Vouchers/Services/FetchesVoucher.php b/app/Classes/Modules/Vouchers/Services/FetchesVoucher.php new file mode 100644 index 00000000..4abc873a --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/FetchesVoucher.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Vouchers/Services/FetchesVoucherRedemption.php b/app/Classes/Modules/Vouchers/Services/FetchesVoucherRedemption.php new file mode 100644 index 00000000..0efc163d --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/FetchesVoucherRedemption.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Vouchers/Services/RollbacksRedemption.php b/app/Classes/Modules/Vouchers/Services/RollbacksRedemption.php new file mode 100644 index 00000000..6bc3bf23 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/RollbacksRedemption.php @@ -0,0 +1,38 @@ +voucherifyClient = createVoucherifyClient(); + } + + /** + * @param string $redemptionId + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(string $redemptionId) + { + try { + $result = $this->voucherifyClient->redemptions->rollback($redemptionId); + return $result; + } catch (\Voucherify\ClientException $e) { + Log::error('RollbacksRedemption error:' . $e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyCustomer.php b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyCustomer.php new file mode 100644 index 00000000..a15fe9c3 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyCustomer.php @@ -0,0 +1,66 @@ +voucherifyClient = createVoucherifyClient(); + } + + /** + * @param CreateVoucherifyCustomerObject $object + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(CreateVoucherifyCustomerObject $object) + { + try { + + $customerObj = [ + "source_id" => $object->getUser()->id, + "name" => $object->getUser()->name, + "email" => $object->getUser()->email, + "address" => [ + "city" => '', + "country" => '', + "line_1" => '', + "line_2" => '', + "postal_code" => '', + "state" => '', + ], + ]; + + if ($object->getIsNew()) { + $customerObj['metadata']["new_customer"] = date('Y-m-d H:i:s'); + } + if ($object->getCompanyId()) { + $customerObj['metadata']["exchange_company_id"] = $object->getCompanyId(); + $customerObj['metadata']["exchange_user_id"] = $object->getUser()->id; + } + if ($object->getAcquisitionChannel()) { + $customerObj['metadata']["acquisition"] = $object->getAcquisitionChannel(); + } + + $result = $this->voucherifyClient->customers->create($customerObj); + return $result; + } catch (\Voucherify\ClientException $e) { + // throw $e; + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php new file mode 100644 index 00000000..e5e6d404 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php @@ -0,0 +1,61 @@ +voucherifyClient = createVoucherifyClient(); + } + + + /** + * @param CreateVoucherifyOrderObject $obj + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(CreateVoucherifyOrderObject $obj) + { + try { + $orderObj = [ + "customer" => [ + "source_id" => $obj->getEmployee()->id, + "name" => $obj->getEmployee()->name, + "email" => $obj->getEmployee()->email, + "metadata" => [ + "exchange_company_id" => $obj->getCompanyId(), + "exchange_user_id" => $obj->getEmployee()->id + ] + ], + "amount" => $obj->getAmount() * 100, //converting it to cents + ]; + + if ($obj->getIsNoVoucher()) { + $orderObj['metadata']["no_voucher"] = true; + } + + if ($obj->getIsTopUpWallet()) { + $orderObj['metadata']["is_wallet_top_up"] = true; + } + + $result = $this->voucherifyClient->orders->create($orderObj); + return $result; + } catch (\Voucherify\ClientException $e) { + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyVoucher.php new file mode 100644 index 00000000..ad95946b --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyVoucher.php @@ -0,0 +1,59 @@ +voucherifyClient = createVoucherifyClient(); + } + + /** + * @param User $user + * @param int $amount + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(User $user, int $amount) + { + $startDate = Carbon::now(); + $expirationDate = $startDate->copy()->addMonths(12)->endOfDay(); + try { + $result = $this->voucherifyClient->vouchers->create([ + "code" => Str::random(10), + "type" => "DISCOUNT_VOUCHER", + "discount" => [ + "type" => "AMOUNT", + "amount_off" => $amount * 100, + ], + "redemption" => [ + "quantity" => 1 + ], + "metadata" => [ + "email" => $user->email + ], + "start_date" => $startDate->toIso8601String(), + "expiration_date" => $expirationDate->toIso8601String() + ]); + return $result; + } catch (\Voucherify\ClientException $e) { + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/FetchesVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/FetchesVoucherifyVoucher.php new file mode 100644 index 00000000..8c4d33b7 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/FetchesVoucherifyVoucher.php @@ -0,0 +1,47 @@ +voucherifyClient = createVoucherifyClient(); + } + + /** + * @param User $user + * @param string $voucherifyVoucherCode + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(User $user, string $voucherifyVoucherCode) + { + try { + $result = $this->voucherifyClient->vouchers->get($voucherifyVoucherCode); + + if (isset($result->metadata) && isset($result->metadata->email)) { + if($user->email != $result->metadata->email){ + $result->reason = 'Invalid Code'; + } + } + + return $result; + } catch (\Voucherify\ClientException $e) { + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php new file mode 100644 index 00000000..bd12e27f --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php @@ -0,0 +1,50 @@ +voucherifyClient = createVoucherifyClient(); + } + + /** + * @param RedeemVoucherifyVoucherObject $redeemVoucherifyVoucherObject + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(RedeemVoucherifyVoucherObject $redeemVoucherifyVoucherObject) + { + try { + $result = $this->voucherifyClient->redemptions->redeem($redeemVoucherifyVoucherObject->getPromoCode(), [ + "customer" => [ + "source_id" => $redeemVoucherifyVoucherObject->getEmployee()->id, + "name" => $redeemVoucherifyVoucherObject->getEmployee()->name, + "email" => $redeemVoucherifyVoucherObject->getEmployee()->email, + "metadata" => [ + "exchange_company_id" => $redeemVoucherifyVoucherObject->getCompanyId(), + "exchange_user_id" => $redeemVoucherifyVoucherObject->getEmployee()->id + ] + ], + "order" => [ + "amount" => $redeemVoucherifyVoucherObject->getAmount() * 100 //converting it to cents + ] + ]); + return $result; + } catch (\Voucherify\ClientException $e) { + throw $e; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/UpdatesVoucherifyOrder.php b/app/Classes/Modules/Vouchers/Services/Voucherify/UpdatesVoucherifyOrder.php new file mode 100644 index 00000000..80aae605 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/UpdatesVoucherifyOrder.php @@ -0,0 +1,43 @@ +voucherifyClient = createVoucherifyClient(); + } + + + /** + * @param UpdateVoucherifyOrderObject $obj + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(UpdateVoucherifyOrderObject $obj) + { + try { + $result = $this->voucherifyClient->orders->update([ + "id" => $obj->getId(), + "status" => $obj->getStatus(), + ]); + return $result; + } catch (\Voucherify\ClientException $e) { + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/ValidatesVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/ValidatesVoucherifyVoucher.php new file mode 100644 index 00000000..3fa322d5 --- /dev/null +++ b/app/Classes/Modules/Vouchers/Services/Voucherify/ValidatesVoucherifyVoucher.php @@ -0,0 +1,70 @@ +voucherifyClient = createVoucherifyClient(); + } + + /** + * @param ValidateVoucherifyVoucherObject $validateVoucherifyVoucherObject + * @return null|object + * @throws \Voucherify\ClientException + */ + public function execute(ValidateVoucherifyVoucherObject $validateVoucherifyVoucherObject) + { + try { + $validateVoucherObj = [ + "customer" => [ + "source_id" => $validateVoucherifyVoucherObject->getUser()->id, + "name" => $validateVoucherifyVoucherObject->getUser()->name, + "email" => $validateVoucherifyVoucherObject->getUser()->email, + "metadata" => [ + "exchange_company_id" => $validateVoucherifyVoucherObject->getCompanyId(), + "exchange_user_id" => $validateVoucherifyVoucherObject->getUser()->id + ] + ] + ]; + if ($validateVoucherifyVoucherObject->getAmount()) { + $validateVoucherObj['order'] = [ + "amount" => $validateVoucherifyVoucherObject->getAmount() * 100 //converting it to cents + ]; + } + + $result = $this->voucherifyClient->validations->validateVoucher($validateVoucherifyVoucherObject->getVoucherCode(), $validateVoucherObj); + + if (isset($result->metadata) && isset($result->metadata->email)) { + if($validateVoucherifyVoucherObject->getUser()->email != $result->metadata->email){ + $result->reason = 'Invalid Code'; + } + } + + if (isset($result->reason)) { + Helper::debugLogger('ValidatesVoucherifyVoucher error: '. $result->reason); + $result->reason = 'Invalid Code'; + } + + return $result; + } catch (\Voucherify\ClientException $e) { + // throw $e; + Log::error($e); + return null; + } + } +} diff --git a/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php b/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php index c60cbf54..86e344ec 100644 --- a/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php +++ b/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php @@ -13,6 +13,7 @@ use App\Classes\Modules\Wallets\Services\GeneratesWalletCode; use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill; use App\Classes\Modules\Transactions\Services\CreatesTransaction; +use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\ApprovalStatus; @@ -56,6 +57,8 @@ class TopUpWalletLogic extends AbstractControllerLogic /** @var CreatesTransaction */ private $createsTransaction; + /** @var BookingToVoucherifyProcessor */ + private $bookingToVoucherifyProcessor; /** * TopUpWalletLogic constructor. @@ -65,8 +68,9 @@ class TopUpWalletLogic extends AbstractControllerLogic * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber * @param CreatesBillplzBill $createsBillplzBill * @param CreatesTransaction $createsTransaction + * @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor */ - public function __construct(FetchesCompany $fetchesCompany, GeneratesWalletCode $generatesWalletCode, CreatesWallet $createsWallet, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesBillplzBill $createsBillplzBill, CreatesTransaction $createsTransaction) + public function __construct(FetchesCompany $fetchesCompany, GeneratesWalletCode $generatesWalletCode, CreatesWallet $createsWallet, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesBillplzBill $createsBillplzBill, CreatesTransaction $createsTransaction, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor) { $this->fetchesCompany = $fetchesCompany; $this->generatesWalletCode = $generatesWalletCode; @@ -74,6 +78,7 @@ class TopUpWalletLogic extends AbstractControllerLogic $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; $this->createsBillplzBill = $createsBillplzBill; $this->createsTransaction = $createsTransaction; + $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; } /** @@ -108,6 +113,8 @@ class TopUpWalletLogic extends AbstractControllerLogic $transaction = $this->createsTransaction->execute($wallet, $transaction_object); + $this->bookingToVoucherifyProcessor->execute($company->employees()->first(), $transaction, $company->id, $amount, 0); + return $this->resourceResponse(new WalletTransactionResource($transaction)); } } diff --git a/app/Classes/Notifications/PaymentProofUploadedEmail.php b/app/Classes/Notifications/PaymentProofUploadedEmail.php new file mode 100644 index 00000000..1fdc8e14 --- /dev/null +++ b/app/Classes/Notifications/PaymentProofUploadedEmail.php @@ -0,0 +1,54 @@ +user = $user; + $this->booking = $booking; + $this->file = $file; + } + + + public function toMail() + { + $attachedFile = null; + $file_info = $this->file->getFileAttribute($this->file)->file->file_info; + foreach ($file_info as $fileCount => $fileVal) { + if (isset($fileVal->original)) { + $file_path = $fileVal->original->file; + $attachedFile = storage_path('app/documents/' . $file_path); + } + } + + return (new MailMessage) + ->subject('Transfer Completed (REF: ' . $this->booking->marking . ')') + // ->attach($attachedFile) // todo-new: add attachement + ->view('emails.accounts.payment_proof_email', ['user' => $this->user, 'booking' => $this->booking]); + } + + +} diff --git a/app/Classes/ValueObjects/Constants/FileType.php b/app/Classes/ValueObjects/Constants/FileType.php index d5404e7a..fc6dbf1b 100644 --- a/app/Classes/ValueObjects/Constants/FileType.php +++ b/app/Classes/ValueObjects/Constants/FileType.php @@ -23,6 +23,7 @@ class FileType 'application/pdf' => 'pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'excel', 'application/vnd.ms-excel' => 'excel', + 'text/plain' => 'text', ]; -} \ No newline at end of file +} diff --git a/app/Classes/ValueObjects/Constants/MilestoneCreationOptions.php b/app/Classes/ValueObjects/Constants/MilestoneCreationOptions.php new file mode 100644 index 00000000..4631b25b --- /dev/null +++ b/app/Classes/ValueObjects/Constants/MilestoneCreationOptions.php @@ -0,0 +1,19 @@ + 'MILESTONE 1', 'id' => Milestones::MILESTONE_1], + ['text' => 'MILESTONE 2', 'id' => Milestones::MILESTONE_2], + ['text' => 'MILESTONE 3', 'id' => Milestones::MILESTONE_3], + // ['text' => 'MILESTONE 4', 'id' => Milestones::MILESTONE_4], + // ['text' => 'MILESTONE 5', 'id' => Milestones::MILESTONE_5], + // ['text' => 'MILESTONE 6', 'id' => Milestones::MILESTONE_6], + // ['text' => 'MILESTONE 7', 'id' => Milestones::MILESTONE_7], + // ['text' => 'MILESTONE 8', 'id' => Milestones::MILESTONE_8], + // ['text' => 'MILESTONE 9', 'id' => Milestones::MILESTONE_9], + // ['text' => 'MILESTONE 10', 'id' => Milestones::MILESTONE_10], + ]; +} diff --git a/app/Classes/ValueObjects/Constants/Milestones.php b/app/Classes/ValueObjects/Constants/Milestones.php new file mode 100644 index 00000000..19961d68 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/Milestones.php @@ -0,0 +1,17 @@ + 'No', 'id' => 0], + ['text' => 'Yes', 'id' => 1], + ]; + + const OPTIONS_TYPE = [ + ['text' => 'User Specific', 'id' => RewardType::REWARD_INDIVIDUAL], + ['text' => 'Amount', 'id' => RewardType::REWARD_AMOUNT], + ['text' => 'Code', 'id' => RewardType::REWARD_CODE], + ]; +} diff --git a/app/Classes/ValueObjects/Constants/RewardType.php b/app/Classes/ValueObjects/Constants/RewardType.php new file mode 100644 index 00000000..1add2ecd --- /dev/null +++ b/app/Classes/ValueObjects/Constants/RewardType.php @@ -0,0 +1,10 @@ + self::EXCHANGE, + 'shipping_portal' => self::SHIPPING_PORTAL, + 'izyim' => self::SHIPPING_PORTAL, + ]; +} diff --git a/app/Classes/ValueObjects/Constants/VoucherifyEntityType.php b/app/Classes/ValueObjects/Constants/VoucherifyEntityType.php new file mode 100644 index 00000000..e9e0a061 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/VoucherifyEntityType.php @@ -0,0 +1,10 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Accounting/BankStatementController.php b/app/Http/Controllers/Accounting/BankStatementController.php new file mode 100644 index 00000000..ff1324b4 --- /dev/null +++ b/app/Http/Controllers/Accounting/BankStatementController.php @@ -0,0 +1,365 @@ +input('account'); + $search = $request->input('search'); + + $accounts = StatementAccount::all(); + + $statementsQuery = AccountStatement::query(); + + if ($selectedAccount) { + $statementsQuery->where('statement_account_id', $selectedAccount); + } + + if ($search) { + $statementsQuery->where(function ($query) use ($search) { + $query->where('date_from', 'LIKE', "%$search%") + ->orWhere('date_to', 'LIKE', "%$search%") + ->orWhere('total_amount', 'LIKE', "%$search%") + ->orWhere('begin_balance', 'LIKE', "%$search%") + ->orWhere('end_balance', 'LIKE', "%$search%"); + }); + } + + $statements = $statementsQuery->orderBy('date_from')->paginate(10); + + return view('pages.accounting.bank-statements.index', compact('accounts', 'selectedAccount', 'search', 'statements')); + } + + public function indexv2(Request $request) + { + $selectedAccount = $request->input('account'); + $search = $request->input('search'); + + $accounts = StatementAccount::all(); + + $statementsQuery = AccountStatement::query(); + + if ($selectedAccount) { + $statementsQuery->where('statement_account_id', $selectedAccount); + } + + if ($search) { + $statementsQuery->where(function ($query) use ($search) { + $query->where('date_from', 'LIKE', "%$search%") + ->orWhere('date_to', 'LIKE', "%$search%") + ->orWhere('total_amount', 'LIKE', "%$search%") + ->orWhere('begin_balance', 'LIKE', "%$search%") + ->orWhere('end_balance', 'LIKE', "%$search%"); + }); + } + + $statements = $statementsQuery->paginate(10); + + return view('pages.accounting.bank-statements.indexv2', compact('accounts', 'selectedAccount', 'search', 'statements')); + } + + public function import(Request $request, ImportBankStatementLogic $logic): JsonResponse + { + return $logic->execute($request); + } + + public function rerun() + { + CreateBankStatementTransactionOwners::dispatch(); + return redirect()->back()->with('success', 'Rerun triggered successfully'); + } + + public function show(AccountStatement $statement, Request $request) + { + $transactions = $statement->transactions(); + // $account = $statement->account(); + // dd(json_encode($account->where('id', '>=', 1)->first())); + // dd(json_encode($transactions->where('id', '>=', 1)->first())); + if ($request->get('transaction_filter')) { + $transactionFilter = $request->get('transaction_filter'); + $transactions = $transactions->where('transaction_description', 'LIKE', "%$transactionFilter%"); + } + + if ($request->get('from_amount_filter')) { + $fromAmountFilter = $request->get('from_amount_filter'); + $transactions = $transactions->where('amount', '>=', $fromAmountFilter); + } + + if ($request->get('to_amount_filter')) { + $toAmountFilter = $request->get('to_amount_filter'); + $transactions = $transactions->where('amount', '<=', $toAmountFilter); + } + + // $transactions = $transactions->paginate(100); + $transactions = $transactions->get(); + echo $this->process3_merged($transactions); + + //return view('pages.accounting.bank-statements.show', compact('statement', 'transactions')); + } + + public function download(AccountStatement $statement) + { + $transactions = $statement->transactions; + + $csvExporter = new \Laracsv\Export(); + $csvExporter->build($transactions, ['transaction_date', 'transaction_time', 'posting_date', 'transaction_description', 'transaction_ref', 'debit', 'credit']) + ->download($statement->date_from->format('Y-m-d') . '_' . $statement->date_to->format('Y-m-d') . '_statement.csv'); + } + + public function fetch(Request $request, ListBankStatementDetailsLogic $logic): JsonResponse + { + return $logic->execute($request); + } + + public function transactions(Request $request, ListBankStatementTransactionsLogic $logic): JsonResponse + { + return $logic->execute($request); + } + + public function update(Request $request, UpdateBankStatementDetailLogic $logic): JsonResponse + { + return $logic->execute($request); + } + + private function process3_merged($transactions){ + + // $statement = $transactions[0]->statement(); + // dd(json_encode($statement->first())); + + $headers = [ + 'Date', + 'Bank', + 'Description', + 'Credit', + 'Debit', + 'Pay For', + 'System', + 'System Reference', + 'Human Reference', + 'Multiple', + 'Match?', + 'System Amount' + ]; + + $branches = [ + 0 => 'MBB Cyber', + 1 => 'MBB SS2', + ]; + + $yes = 'Yes'; + $no = 'No'; + + $table = ''; + $count = 0; + + foreach ($transactions as $row) { + $isExist = StatementTransactionOwner::where('statement_transaction_id', $row->id)->first(); + + if ($isExist) { + continue; + } + + $count++; + $credit = 0.00; + $debit = 0.00; + + // dd(json_encode($row['posting_date'])); + $date = new DateTime($row['posting_date']); + $description = $row['transaction_description_2']; + + if($row['amount'] < 0){ + $debit = (float) $row['amount']; + } + else{ + $credit = (float) $row['amount']; + } + + $creditTransactions = []; + $debitTransactions = []; + + $system = ''; + $systemReference = null; + $systemAmount = null; + + if($credit){ + $creditTransactions = $this->getTransactions($date, $credit, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $transaction) { + $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no; + $systemAmount[] = $transaction->amount; + $system[] = 'EXCHANGE'; + } + + $creditTransactions = $this->getTransactions($date, $credit, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $transaction) { + $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no; + $systemAmount[] = $transaction->amount; + $system[] = 'EXCHANGE'; + } + + $creditTransactions = $this->getTransactionsFromShippingPortal($credit, $this->getDateRange($row['posting_date'])); + foreach ($creditTransactions as $transaction) { + $systemReference[] = $transaction['order']['reference']; + $systemAmount[] = $transaction['amount']; + $system[] = 'SHIPPING'; + } + } + + if($debit){ + $debitTransactions = $this->getTransactions($date, $debit, null, null, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], Group::class); + + if(!count($debitTransactions)) { + foreach (['YSN', 'HCK', 'ATVANTIC', 'HIGH HILL'] as $reference){ + if(str_contains($description, $reference)) { + $paymentDate = date('Y-m-d', strtotime('+1 day', strtotime($row['posting_date']))); //$date->addDays(1)->format('Y-m-d'); + + if($reference = 'ATVANTIC'){ + $paymentDate = date('Y-m-d', strtotime($row['posting_date']));//$date->format('Y-m-d'); + } + $issuer = Company::where('name', 'like', '%'.$reference.'%')->get()->pluck('id'); + $debitTransactions = Group::whereIn('issuer', $issuer)->whereDate('created_at', $paymentDate)->get(); + break; + } + } + + } + + foreach ($debitTransactions as $transaction) { + $systemReference[] = $transaction->reference; + $systemAmount[] = $transaction->amount; + $system[] = 'EXCHANGE'; + } + } + + + $multiple = count($creditTransactions) + count($debitTransactions) > 1 ? $yes : $no; + + + + + + $systemReference = $systemReference ? implode(',', $systemReference) : null; + $systemAmount = $systemAmount ? implode(',', $systemAmount) : null; + + $matches = $systemReference == $row['remarkreferences'] ? $yes : $no; + + $table .= ' + + + + + + + + + + + + + '; + + //AccountStatement + // $row->statement()->first()->id) + + $statementTransactionsDetail = new StatementTransactionOwner([ + 'date' => $date, + 'statement_transaction_id' => $row->id, + 'description' => is_null($description) ? "" : $description, + 'credit' => $credit, + 'debit' => $debit, + 'pay_for' => $system, + 'system_references' => is_null($systemReference) ? "" : $systemReference, + 'remark_references' => is_null($row['remarkreferences']) ? "" : $row['remarkreferences'], + 'is_multiple' => $multiple == "Yes" ? 1 : 0, + 'is_matches' => $matches == "Yes" ? 1 : 0, + 'system_amounts'=> is_null($systemAmount) ? "" : $systemAmount, + ]); + + $statementTransactionsDetail->save(); + + if($count == 10){ + break; + } + } + + $table .= '
'.implode('', $headers).'
'.$date->format('d-m-Y').'branch'.$description.''.$credit.''.$debit.''.$row['pay_for'].''.$system.''.$systemReference.''.$row['remarkreferences'].''.$multiple.''.$matches.''.$systemAmount.'
'; + + return $table; + } + + private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $model = Transaction::class) { + $query = $model::whereIn('status', $statuses) + ->where(function ($query) use ($ownerType, $paymentMethod, $type) { + if ($ownerType) { + $query->where('owner_type', $ownerType); + } + + if ($paymentMethod) { + $query->where('payment_method', '!=', $paymentMethod); + } + + if ($type) { + $query->where('type', $type); + } + }) + ->whereDate('created_at', $date->format('Y-m-d')) + ->where('amount', '>', ($amount - 0.01)) + ->where('amount', '<', ($amount + 0.01)); + + return $query->get(); + } + + private function getDateRange(string $dateStr) { + // Create a DateTime object from the input string + $date = strtotime($dateStr); + + // Get the first day of the month + $today = date('Y-m-d', $date); + + // Get the first day of the next month + $nextDay = date('Y-m-d', strtotime('+1 day', $date)); + + return [ + 'start_date' => $today, + 'end_date' => $nextDay, + ]; + } + + private function getTransactionsFromShippingPortal($amount, $dateRange){ + + $client = new \GuzzleHttp\Client(); + $response = $client->request('GET', 'https://izyim.cief-malaysia.com/public/api/v1/list?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2],"type":2,"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).'}'); + $body = $response->getBody(); + $data = json_decode($body, true); + $payload = $data['payload']; + $transactions2 = $payload['data']; + // $filters = [ + // ['field' => 'created_at', 'value' => '2023-03-01 08:07:00'], + // ]; + // $transactions2 = $this->getTransactions3($transactions2, $filters); + return $transactions2; + } + +} diff --git a/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php b/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php new file mode 100644 index 00000000..9f242704 --- /dev/null +++ b/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php b/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php new file mode 100644 index 00000000..dd45e571 --- /dev/null +++ b/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php index 1f7594f8..74384157 100644 --- a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php +++ b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php @@ -9,8 +9,8 @@ use App\Classes\Modules\Exports\Services\ExportsBookingTransactions; use App\Classes\Modules\Exports\Services\ExportsLeadsTransactions; use App\Classes\Modules\Exports\Services\ExportsNullDebtors; use App\Classes\Modules\Exports\Services\ExportsPaymentTransactions; - use App\Classes\Modules\Exports\Services\ExportsWalletTransactions; +use App\Classes\Modules\Exports\Services\ExportsInvoiceTransactions; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -57,6 +57,13 @@ class ExportCustomersToExcelController return $response; } + public function invoiceTransactions(Request $request){ + $exportsTransactions = new ExportsInvoiceTransactions($request); + $response = $exportsTransactions->download('invoice-transactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } + public function bookingTransactions(ExportsBookingTransactions $exportsBookingTransactions, Request $request){ $response = $exportsBookingTransactions->download('bookingTransactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); ob_end_clean(); diff --git a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php new file mode 100644 index 00000000..a59f9463 --- /dev/null +++ b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php @@ -0,0 +1,99 @@ +input('files'), '', ApprovalStatus::APPROVED, 'imports'); + $file = json_decode($object->getFiles()[0])->file_info->original->file; + + $import = new GenericImport(); + Excel::import($import, $file); + $excelRows = $import->rows; + $excelRows = $excelRows->toArray(); + + foreach ($excelRows as $row) { + dd($row); + // $row['debtor_code'] + + // attempt 1 - try map by amount and date + // $transactionDate = $this->changeExcelDate($row['date']); + // $transaction = Transaction::where('original_amount', $row['total'])->whereDate('created_at', $transactionDate)->get(); + // if ($transaction) { + // // check company + // // $company = Company::where('debtor', $row['debtor_code'])->first(); + // // dd($company); + // // try to verify is it the correct transaction + // } + + // Shipping Info + // TOPUP -> map with transaction.bill_no + if (str_starts_with($row['shipping_info'], 'TOPUP')) { + // find in exchange first, if cannont then find in izyim + // (App()->make(ChecksBillNumber::class))->execute($bill_no, 'exchange'); + } + + // if 5 digits -> exchange booking reference + // find transation + // find statement_transaction_owners, and fill up the details + + // if <5 digits, find the transaction id (order number in izyim), find the payment in izyim + // find transation + // find statement_transaction_owners, and fill up the details + + // dd([ + // 'type' => $statementTransactionOwnerType, + // 'system' => $system, + // // 'owner_type' => Transaction::class, + // // todo-new: make sure owner_type is a class + // 'owner_type' => $owner_type, + // 'owner_id' => $owner_id, + // 'owner_reference' => $owner_reference + // ]); + + // $bankStatementTransaction->owners()->firstOrCreate([ + // 'type' => $statementTransactionOwnerType, + // 'system' => $system, + // // 'owner_type' => Transaction::class, + // // todo-new: make sure owner_type is a class + // 'owner_type' => $owner_type, + // 'owner_id' => $owner_id, + // 'owner_reference' => $owner_reference + // ]); + + + } + } + + public function changeExcelDate($date) + { + $unixTime = (($date - 25569) * 86400); + $date = new DateTime("@$unixTime"); + return $date->format('Y-m-d'); // Change the format to 'Y-m-d' + } +} diff --git a/app/Http/Controllers/Imports/ImportStatementReceiptsController.php b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php new file mode 100644 index 00000000..22111881 --- /dev/null +++ b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php @@ -0,0 +1,50 @@ +input('files'), '', ApprovalStatus::APPROVED, 'imports'); + $file = json_decode($object->getFiles()[0])->file_info->original->file; + + $import = new GenericImport(); + Excel::import($import, $file); + $excelRows = $import->rows; + $excelRows = $excelRows->toArray(); + + foreach ($excelRows as $row) { + // if has date column + // $transactionDate = $this->changeExcelDate($row['date']); + } + } + + public function changeExcelDate($date) + { + $unixTime = (($date - 25569) * 86400); + $date = new DateTime("@$unixTime"); + return $date->format('Y-m-d'); // Change the format to 'Y-m-d' + } +} diff --git a/app/Http/Controllers/Milestones/CreateMilestoneController.php b/app/Http/Controllers/Milestones/CreateMilestoneController.php new file mode 100644 index 00000000..d5d2ba0c --- /dev/null +++ b/app/Http/Controllers/Milestones/CreateMilestoneController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Milestones/DeleteMilestoneController.php b/app/Http/Controllers/Milestones/DeleteMilestoneController.php new file mode 100644 index 00000000..084ff35e --- /dev/null +++ b/app/Http/Controllers/Milestones/DeleteMilestoneController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Milestones/ListMilestoneProgressController.php b/app/Http/Controllers/Milestones/ListMilestoneProgressController.php new file mode 100644 index 00000000..03e2aa10 --- /dev/null +++ b/app/Http/Controllers/Milestones/ListMilestoneProgressController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Milestones/ListMilestonesController.php b/app/Http/Controllers/Milestones/ListMilestonesController.php new file mode 100644 index 00000000..b592c0d4 --- /dev/null +++ b/app/Http/Controllers/Milestones/ListMilestonesController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Milestones/UpdateMilestoneController.php b/app/Http/Controllers/Milestones/UpdateMilestoneController.php new file mode 100644 index 00000000..e3dce17d --- /dev/null +++ b/app/Http/Controllers/Milestones/UpdateMilestoneController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Rewards/CreateRewardController.php b/app/Http/Controllers/Rewards/CreateRewardController.php new file mode 100644 index 00000000..55ea98c9 --- /dev/null +++ b/app/Http/Controllers/Rewards/CreateRewardController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Rewards/DeleteRewardController.php b/app/Http/Controllers/Rewards/DeleteRewardController.php new file mode 100644 index 00000000..bc8e90e9 --- /dev/null +++ b/app/Http/Controllers/Rewards/DeleteRewardController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Rewards/ListRewardsController.php b/app/Http/Controllers/Rewards/ListRewardsController.php new file mode 100644 index 00000000..2d02d898 --- /dev/null +++ b/app/Http/Controllers/Rewards/ListRewardsController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Rewards/ListRewardsDetailsController.php b/app/Http/Controllers/Rewards/ListRewardsDetailsController.php new file mode 100644 index 00000000..bd853b00 --- /dev/null +++ b/app/Http/Controllers/Rewards/ListRewardsDetailsController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Transactions/FetchCompanyTransactionStatementController.php b/app/Http/Controllers/Transactions/FetchCompanyTransactionStatementController.php new file mode 100644 index 00000000..a1d8c3cb --- /dev/null +++ b/app/Http/Controllers/Transactions/FetchCompanyTransactionStatementController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Vouchers/CreateVoucherController.php b/app/Http/Controllers/Vouchers/CreateVoucherController.php new file mode 100644 index 00000000..f89c93ac --- /dev/null +++ b/app/Http/Controllers/Vouchers/CreateVoucherController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Vouchers/ListUserVouchersController.php b/app/Http/Controllers/Vouchers/ListUserVouchersController.php new file mode 100644 index 00000000..df8cb610 --- /dev/null +++ b/app/Http/Controllers/Vouchers/ListUserVouchersController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Vouchers/ValidateVoucherController.php b/app/Http/Controllers/Vouchers/ValidateVoucherController.php new file mode 100644 index 00000000..86c4c4e1 --- /dev/null +++ b/app/Http/Controllers/Vouchers/ValidateVoucherController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Resources/BankStatementTransactionOwnerResource.php b/app/Http/Resources/BankStatementTransactionOwnerResource.php new file mode 100644 index 00000000..468d9ec5 --- /dev/null +++ b/app/Http/Resources/BankStatementTransactionOwnerResource.php @@ -0,0 +1,60 @@ +system === 'EXCHANGE') { + if($this->owner_type === Transaction::class){ + if($this->type === StatementTransactionOwnerType::SALES){ + $referenceLink = route('booking.details', $this->owner_reference); + } + + if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){ + $referenceLink = route('booking.details', $this->owner_reference); + } + } + } + + if($this->system === 'SHIPPING_PORTAL') { + if($this->owner_type === Transaction::class){ + if($this->type === StatementTransactionOwnerType::SALES){ + $referenceLink = 'https://izyim.cief-malaysia.com/order/show/'. $this->owner_reference; + } + if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){ + $referenceLink = 'https://izyim.cief-malaysia.com/wallet/'. $this->owner_reference .'/details'; + } + } + } + + return [ + 'id' => $this->id, + 'type' => $this->type, + 'system' => $this->system, + 'owner_type' => $this->owner_type, + 'owner_id' => $this->owner_id, + 'reference' => $this->owner_reference, + 'reference_link' => $referenceLink, + 'invoice_reference' => $this->invoice_reference, + 'receipt_reference' => $this->receipt_reference, + 'status' => $this->status + ]; + } +} diff --git a/app/Http/Resources/BankStatementTransactionResource.php b/app/Http/Resources/BankStatementTransactionResource.php new file mode 100644 index 00000000..993b8c93 --- /dev/null +++ b/app/Http/Resources/BankStatementTransactionResource.php @@ -0,0 +1,41 @@ + $this->id, + 'account_number' => $this->statement->account->number, + 'account_type' => $this->statement->account->type, + 'account_name' => $this->statement->account->name, + 'account_statement_id' => $this->statement->id, + 'account_statement_date_from' => $this->statement->date_from, + 'account_statement_date_to' => $this->statement->date_to, + 'posting_date' => $this->posting_date->format('d-m-Y g:i A'), + 'amount' => $this->amount, + 'transaction_description_1' => $this->transaction_description, + 'transaction_description_2' => $this->transaction_description_2, + 'transaction_description_3' => $this->transaction_description_3, + 'transaction_description_4' => $this->transaction_description_4, + 'transaction_description_5' => $this->transaction_description_5, + 'owners' => [ + 'approved' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::APPROVED])->get()), + 'pending_verification' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION])->get()), + 'rejected' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::REJECTED])->get()) + ] + ]; + } +} diff --git a/app/Http/Resources/MilestoneProgressResource.php b/app/Http/Resources/MilestoneProgressResource.php new file mode 100644 index 00000000..ed4c465f --- /dev/null +++ b/app/Http/Resources/MilestoneProgressResource.php @@ -0,0 +1,23 @@ + $this->id, + 'milestone_id' => $this->milestone_id, + 'created_at' => $this->created_at + ]; + } +} diff --git a/app/Http/Resources/MilestoneResource.php b/app/Http/Resources/MilestoneResource.php new file mode 100644 index 00000000..f336f937 --- /dev/null +++ b/app/Http/Resources/MilestoneResource.php @@ -0,0 +1,28 @@ +rewards->pluck('id')->map(function ($id) { + return (int) $id; + })->toArray(); + + return [ + 'id' => $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'reward_ids' => $rewardIds + ]; + } +} diff --git a/app/Http/Resources/MilestoneWIthMiltestoneProgressResource.php b/app/Http/Resources/MilestoneWIthMiltestoneProgressResource.php new file mode 100644 index 00000000..4b4176b6 --- /dev/null +++ b/app/Http/Resources/MilestoneWIthMiltestoneProgressResource.php @@ -0,0 +1,30 @@ +route('user_id'); + if(!$userId){ + $userId = Auth::user()->id; + } + + return [ + 'id' => $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'milestone_progress' => new MilestoneProgressResource($this->progress->where('user_id', $userId)->first()), + ]; + } +} diff --git a/app/Http/Resources/PaymentTransactionResource.php b/app/Http/Resources/PaymentTransactionResource.php new file mode 100644 index 00000000..456ece3d --- /dev/null +++ b/app/Http/Resources/PaymentTransactionResource.php @@ -0,0 +1,56 @@ +type, [TransactionType::BILL, TransactionType::REFUND])? $this->owner->owner : $this->owner; + + $booking_marking = ''; + switch ($this->owner_type) { + case Booking::class: + $booking_marking = $booking->marking; + break; + case Wallet::class: + $booking_marking = $this->booking->marking; + break; + } + + return [ + 'id' => $this->id, + 'booking_marking' => $booking_marking, + '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, + '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') + ]; + } +} diff --git a/app/Http/Resources/RewardDetailsResource.php b/app/Http/Resources/RewardDetailsResource.php new file mode 100644 index 00000000..2e18cdb6 --- /dev/null +++ b/app/Http/Resources/RewardDetailsResource.php @@ -0,0 +1,36 @@ +route('user_id'); + if(!$userId){ + $userId = Auth::user()->id; + } + + $userReward = $this->users->where('user_id', $userId)->first(); + + return [ + 'id' => $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'is_active' => $this->is_active, + 'milestones' => $this->milestones, + 'milestones_progress' => MilestoneWIthMiltestoneProgressResource::collection($this->milestones), + 'user_rewards' => new UserRewardResource($userReward), + 'voucher' => $userReward ? $userReward->voucher : null + ]; + } +} diff --git a/app/Http/Resources/RewardResource.php b/app/Http/Resources/RewardResource.php new file mode 100644 index 00000000..e400fc6a --- /dev/null +++ b/app/Http/Resources/RewardResource.php @@ -0,0 +1,28 @@ + $this->id, + 'name' => $this->name, + 'description' => $this->description, + 'is_active' => $this->is_active, + 'type' => $this->type, + 'value' => $this->value, + 'order' => $this->order, + 'milestones' => MilestoneResource::collection($this->milestones) + ]; + } +} diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index e35dcc36..27fd17ab 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -48,7 +48,8 @@ class TransactionResource extends JsonResource 'interval' => [ 'value' => $days->gt(Carbon::now()) ? '+' : '-', 'duration' => $days->diff(Carbon::now())->format('%d'), - ] + ], + 'redemption' => new VoucherRedemptionResource($this->voucherRedemption) ]; } } diff --git a/app/Http/Resources/UserRewardResource.php b/app/Http/Resources/UserRewardResource.php new file mode 100644 index 00000000..ed693b4c --- /dev/null +++ b/app/Http/Resources/UserRewardResource.php @@ -0,0 +1,25 @@ + $this->id, + 'user_id' => $this->user_id, + 'reward' => new RewardResource($this->reward), + 'voucher' => new VoucherResource($this->voucher), + 'created_at' => $this->created_at + ]; + } +} diff --git a/app/Http/Resources/VoucherRedemptionResource.php b/app/Http/Resources/VoucherRedemptionResource.php new file mode 100644 index 00000000..16ae693f --- /dev/null +++ b/app/Http/Resources/VoucherRedemptionResource.php @@ -0,0 +1,25 @@ + $this->id, + 'voucher_id' => $this->voucher_id, + 'transaction_id' => $this->transaction_id, + 'redemption_id' => $this->redemption_id, + 'value' => (float) $this->value + ]; + } +} diff --git a/app/Http/Resources/VoucherResource.php b/app/Http/Resources/VoucherResource.php new file mode 100644 index 00000000..54193eeb --- /dev/null +++ b/app/Http/Resources/VoucherResource.php @@ -0,0 +1,31 @@ +redemptions->filter(function ($redemption) { + return $redemption->transaction && $redemption->transaction->owner; + }); + return [ + 'id' => $this->id, + 'name' => $this->name, + 'code' => $this->code, + 'type' => $this->type, + 'value' => (float) $this->value, + 'start_date' => $this->start_date, + 'end_date' => $this->end_date, + 'is_redeemed' => $filteredRedemptions->count() > 0 + ]; + } +} diff --git a/app/Models/AccountStatement.php b/app/Models/AccountStatement.php new file mode 100644 index 00000000..d9e53d60 --- /dev/null +++ b/app/Models/AccountStatement.php @@ -0,0 +1,35 @@ + 'date', + 'date_to' => 'date', + ]; + + public function account() + { + return $this->belongsTo(StatementAccount::class, 'statement_account_id', 'id'); + } + + public function transactions() + { + return $this->hasMany(StatementTransaction::class); + } +} diff --git a/app/Models/Employee.php b/app/Models/Employee.php index 65dab67d..6d133186 100644 --- a/app/Models/Employee.php +++ b/app/Models/Employee.php @@ -31,4 +31,13 @@ class Employee extends AbstractModel { return $this->hasOne(User::class, 'user_id', 'id'); } + + /** + * @return belongsToMany + */ + public function milestones() + { + return $this->belongsToMany(Milestone::class, 'milestone_progress', 'user_id', 'milestone_id') + ->withPivot('created_at'); + } } diff --git a/app/Models/Milestone.php b/app/Models/Milestone.php new file mode 100644 index 00000000..391410cc --- /dev/null +++ b/app/Models/Milestone.php @@ -0,0 +1,22 @@ +hasMany(MilestoneProgress::class, 'milestone_id'); + } + + public function rewards() + { + return $this->belongsToMany(Reward::class, MilestoneReward::class); + } +} diff --git a/app/Models/MilestoneProgress.php b/app/Models/MilestoneProgress.php new file mode 100644 index 00000000..7e03cba0 --- /dev/null +++ b/app/Models/MilestoneProgress.php @@ -0,0 +1,23 @@ +belongsTo(Milestone::class, 'milestone_id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } +} diff --git a/app/Models/MilestoneReward.php b/app/Models/MilestoneReward.php new file mode 100644 index 00000000..1d5f04e3 --- /dev/null +++ b/app/Models/MilestoneReward.php @@ -0,0 +1,8 @@ +belongsToMany(Milestone::class, MilestoneReward::class); + } + + /** + * @return HasMany + */ + public function users(): HasMany + { + return $this->HasMany(UserReward::class, 'reward_id', 'id'); + } +} diff --git a/app/Models/StatementAccount.php b/app/Models/StatementAccount.php new file mode 100644 index 00000000..741d2c5f --- /dev/null +++ b/app/Models/StatementAccount.php @@ -0,0 +1,23 @@ +hasMany(AccountStatement::class); + } +} diff --git a/app/Models/StatementTransaction.php b/app/Models/StatementTransaction.php new file mode 100644 index 00000000..80d6759f --- /dev/null +++ b/app/Models/StatementTransaction.php @@ -0,0 +1,50 @@ + 'datetime', + ]; + + public function account() + { + return $this->hasOneDeep(StatementAccount::class, [AccountStatement::class], ['id', 'id'], ['account_statement_id', 'statement_account_id']); + } + + public function statement() + { + return $this->belongsTo(AccountStatement::class, 'account_statement_id', 'id'); + } + + public function owners() + { + return $this->hasMany(StatementTransactionOwner::class); + } +} diff --git a/app/Models/StatementTransactionOwner.php b/app/Models/StatementTransactionOwner.php new file mode 100644 index 00000000..52e8d1fc --- /dev/null +++ b/app/Models/StatementTransactionOwner.php @@ -0,0 +1,29 @@ +belongsTo(StatementTransaction::class, 'statement_transaction_id', 'id'); + } +} diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index cf2dddb9..05fbfde7 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Classes\General\Interfaces\Documentable; use App\Classes\General\Interfaces\Transactionable; +use App\Classes\General\Interfaces\Voucherifiable; use App\Classes\General\Traits\LogData; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\TransactionType; @@ -19,15 +20,14 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; use Staudenmeir\EloquentHasManyDeep\HasTableAlias; -class Transaction extends AbstractModel implements Documentable, Transactionable +class Transaction extends AbstractModel implements Documentable, Transactionable, Voucherifiable { use HasTableAlias; use SoftDeletes; use LogData; protected $casts = [ - 'type' => 'int', - 'status' => 'int', + 'type' => 'int' ]; protected $table = 'transactions'; @@ -201,4 +201,13 @@ class Transaction extends AbstractModel implements Documentable, Transactionable { return $query->whereIn('status', [ApprovalStatus::APPROVED]); } + + /** + * @return MorphMany + */ + public function voucherifyEntities(): MorphMany + { + return $this->morphMany(VoucherEntityMapping::class, 'owner'); + } + } diff --git a/app/Models/User.php b/app/Models/User.php index 6fd8b6b0..4bc7ec17 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,8 +2,11 @@ namespace App\Models; +use App\Classes\General\Interfaces\Voucherifiable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\SoftDeletes; use Spatie\Permission\Traits\HasRoles; @@ -22,7 +25,8 @@ class User extends AbstractModel implements JWTSubject, AuthenticatableContract, AuthorizableContract, - CanResetPasswordContract + CanResetPasswordContract, + Voucherifiable { use HasRoles, Notifiable, Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail, SoftDeletes; @@ -65,4 +69,36 @@ class User extends AbstractModel implements { return $this->belongsToMany(Company::class, (new Employee())->getTable(), 'user_id', 'company_id'); } + + // /** + // * @return HasMany + // */ + // public function redemptions(): HasMany + // { + // return $this->HasMany(VoucherRedemption::class, 'user_id', 'id'); + // } + + /** + * @return HasMany + */ + public function milestoneProgress(): HasMany + { + return $this->HasMany(MilestoneProgress::class, 'user_id', 'id'); + } + + /** + * @return MorphMany + */ + public function voucherifyEntities(): MorphMany + { + return $this->morphMany(VoucherEntityMapping::class, 'owner'); + } + + /** + * @return HasMany + */ + public function rewards(): HasMany + { + return $this->HasMany(UserReward::class, 'user_id', 'id'); + } } diff --git a/app/Models/UserReward.php b/app/Models/UserReward.php new file mode 100644 index 00000000..40309447 --- /dev/null +++ b/app/Models/UserReward.php @@ -0,0 +1,28 @@ +belongsTo(Reward::class, 'reward_id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + public function voucher() + { + return $this->belongsTo(Voucher::class, 'voucher_id'); + } +} diff --git a/app/Models/Voucher.php b/app/Models/Voucher.php new file mode 100644 index 00000000..b8df7dca --- /dev/null +++ b/app/Models/Voucher.php @@ -0,0 +1,19 @@ +HasMany(VoucherRedemption::class, 'voucher_id', 'id'); + } +} diff --git a/app/Models/VoucherEntityMapping.php b/app/Models/VoucherEntityMapping.php new file mode 100644 index 00000000..2df0eacd --- /dev/null +++ b/app/Models/VoucherEntityMapping.php @@ -0,0 +1,23 @@ +morphTo(); + } + +} diff --git a/app/Models/VoucherRedemption.php b/app/Models/VoucherRedemption.php new file mode 100644 index 00000000..aa37487b --- /dev/null +++ b/app/Models/VoucherRedemption.php @@ -0,0 +1,36 @@ +BelongsTo(Voucher::class, 'voucher_id', 'id'); + } + + /** + * @return BelongsTo + */ + public function transaction(): BelongsTo + { + return $this->BelongsTo(Transaction::class, 'transaction_id', 'id'); + } + + /** + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->BelongsTo(User::class, 'user_id', 'id'); + } +} diff --git a/composer.json b/composer.json index 349234b1..47acc131 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "maatwebsite/excel": "^3.1", "mpdf/mpdf": "^8.1", "rinvex/countries": "^6.1", + "rspective/voucherify": " v2.0.*", "smalot/pdfparser": "^2.2", "spatie/laravel-activitylog": "^3.14", "spatie/laravel-permission": "^3.17", @@ -57,6 +58,9 @@ "classmap": [ "database/seeds", "database/factories" + ], + "files": [ + "app/Classes/General/VoucherifyHelper.php" ] }, "autoload-dev": { diff --git a/config/perfexcrm.php b/config/perfexcrm.php index f67040f5..7644a489 100644 --- a/config/perfexcrm.php +++ b/config/perfexcrm.php @@ -1,7 +1,7 @@ env('PERFEXCRM_BASE_URL', 'http://192.168.1.100:8084'), //cief todo: Update crm api domain here + 'base_url' => env('PERFEXCRM_BASE_URL', 'http://192.168.1.101:8084'), //cief todo: Update crm api domain here 'api_key' => env('PERFEXCRM_API_KEY', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZXhjaGFuZ2Utc2hpcHBpbmciLCJuYW1lIjoiRXhjaGFuZ2UgYW5kIFNoaXBwaW5nIFBvcnRhbCIsIkFQSV9USU1FIjoxNjc1MDg2Mzc4fQ.SGAHWl5stcxQwp55TBGeMRVTdlLeWQIbsvJh5glyVvs'), 'is_enabled' => env('PERFEXCRM_IS_ENABLED', 'true'), ]; diff --git a/config/voucherify.php b/config/voucherify.php new file mode 100644 index 00000000..c97fab5b --- /dev/null +++ b/config/voucherify.php @@ -0,0 +1,8 @@ + env('VOUCHERIFY_APPLICATION_ID', ''), + 'client_secret_key' => env('VOUCHERIFY_CLIENT_SECRET_KEY', ''), + 'version' => env('VOUCHERIFY_VERSION', ''), + 'url' => env('VOUCHERIFY_URL', ''), +]; diff --git a/database/migrations/2023_03_26_190633_create_statement_accounts_table.php b/database/migrations/2023_03_26_190633_create_statement_accounts_table.php new file mode 100644 index 00000000..348c5ec0 --- /dev/null +++ b/database/migrations/2023_03_26_190633_create_statement_accounts_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('number')->unique(); + $table->string('type'); + $table->string('name'); + $table->string('currency'); + $table->timestamps(); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('statement_accounts'); + } +} diff --git a/database/migrations/2023_03_26_190827_create_account_statements_table.php b/database/migrations/2023_03_26_190827_create_account_statements_table.php new file mode 100644 index 00000000..43945692 --- /dev/null +++ b/database/migrations/2023_03_26_190827_create_account_statements_table.php @@ -0,0 +1,39 @@ +id(); + $table->unsignedBigInteger('statement_account_id'); + $table->date('date_from'); + $table->date('date_to'); + $table->float('total_amount'); + $table->float('begin_balance'); + $table->float('end_balance'); + $table->timestamps(); + + $table->foreign('statement_account_id')->references('id')->on('statement_accounts'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('account_statements'); + } +} diff --git a/database/migrations/2023_03_26_190934_create_statement_transactions_table.php b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php new file mode 100644 index 00000000..29cacb80 --- /dev/null +++ b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php @@ -0,0 +1,47 @@ +id(); + $table->unsignedBigInteger('account_statement_id'); + $table->dateTime('transaction_date')->nullable(); + $table->dateTime('posting_date'); + $table->string('transaction_description')->nullable(); + $table->string('transaction_description_2')->nullable(); + $table->string('transaction_description_3')->nullable(); + $table->string('transaction_description_4')->nullable(); + $table->string('transaction_description_5')->nullable(); + $table->string('transaction_ref')->nullable(); + $table->float('amount', 15, 2)->unsigned(false); + $table->string('teller_id')->nullable(); + $table->string('branch_channel'); + $table->string('transaction_code'); + $table->string('end_balance')->nullable(); + $table->timestamps(); + + $table->foreign('account_statement_id')->references('id')->on('account_statements'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('statement_transactions'); + } +} diff --git a/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php new file mode 100644 index 00000000..5d22c520 --- /dev/null +++ b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php @@ -0,0 +1,45 @@ +id(); + $table->foreignId('statement_transaction_id'); + $table->integer('type')->default(StatementTransactionOwnerType::UNKNOWN); + $table->string('system')->nullable(); + $table->string('owner_type')->nullable(); + $table->bigInteger('owner_id')->nullable(); + $table->string('owner_reference')->nullable(); + $table->string('invoice_reference')->nullable(); + $table->string('receipt_reference')->nullable(); + $table->string('is_auto_mapped')->default(false); + $table->integer('status')->default(ApprovalStatus::PENDING_VERIFICATION); + + $table->foreign('statement_transaction_id')->references('id')->on('statement_transactions'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('statement_transaction_owners'); + } +} diff --git a/database/migrations/2023_05_27_050522_create_vouchers_table.php b/database/migrations/2023_05_27_050522_create_vouchers_table.php new file mode 100644 index 00000000..a769a699 --- /dev/null +++ b/database/migrations/2023_05_27_050522_create_vouchers_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('name')->nullable(); + $table->string('code'); + $table->string('type')->nullable(); + $table->decimal('value', 8, 2)->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('vouchers'); + } +} diff --git a/database/migrations/2023_05_27_050523_create_voucher_redemptions_table.php b/database/migrations/2023_05_27_050523_create_voucher_redemptions_table.php new file mode 100644 index 00000000..7e46bc98 --- /dev/null +++ b/database/migrations/2023_05_27_050523_create_voucher_redemptions_table.php @@ -0,0 +1,41 @@ +id(); + $table->unsignedBigInteger('voucher_id'); + $table->unsignedBigInteger('transaction_id'); + $table->string('redemption_id'); + $table->decimal('value', 8, 2)->nullable(); + // $table->unsignedBigInteger('user_id'); + $table->timestamps(); + + // Define foreign key constraints + $table->foreign('voucher_id')->references('id')->on('vouchers'); + $table->foreign('transaction_id')->references('id')->on('transactions'); + // $table->foreign('user_id')->references('id')->on('users'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('voucher_redemptions'); + } +} diff --git a/database/migrations/2023_06_12_191228_create_rewards_table.php b/database/migrations/2023_06_12_191228_create_rewards_table.php new file mode 100644 index 00000000..04ea1133 --- /dev/null +++ b/database/migrations/2023_06_12_191228_create_rewards_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('name'); + $table->text('description')->nullable(); + $table->boolean('is_active')->default(true); + $table->integer('type')->default(0); + $table->string('value'); + $table->integer('order')->default(9999); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('rewards'); + } +} diff --git a/database/migrations/2023_06_13_062800_create_milestones_table.php b/database/migrations/2023_06_13_062800_create_milestones_table.php new file mode 100644 index 00000000..8785e734 --- /dev/null +++ b/database/migrations/2023_06_13_062800_create_milestones_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('name'); + $table->string('description'); + // $table->unsignedBigInteger('reward_id'); + $table->softDeletes(); + $table->timestamps(); + + // $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('milestones'); + } +} diff --git a/database/migrations/2023_06_13_213411_create_milestone_progress_table.php b/database/migrations/2023_06_13_213411_create_milestone_progress_table.php new file mode 100644 index 00000000..3476eb5a --- /dev/null +++ b/database/migrations/2023_06_13_213411_create_milestone_progress_table.php @@ -0,0 +1,39 @@ +id(); + // $table->unsignedBigInteger('company_id'); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('milestone_id'); + $table->softDeletes(); + $table->timestamps(); + + // $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('milestone_id')->references('id')->on('milestones')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('milestone_progress'); + } +} diff --git a/database/migrations/2023_06_20_192419_create_user_rewards_table.php b/database/migrations/2023_06_20_192419_create_user_rewards_table.php new file mode 100644 index 00000000..42288a87 --- /dev/null +++ b/database/migrations/2023_06_20_192419_create_user_rewards_table.php @@ -0,0 +1,39 @@ +id(); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('reward_id'); + $table->unsignedBigInteger('voucher_id'); + $table->softDeletes(); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + // $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade'); + $table->foreign('voucher_id')->references('id')->on('vouchers')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_rewards'); + } +} diff --git a/database/migrations/2023_07_02_210132_create_milestone_reward.php b/database/migrations/2023_07_02_210132_create_milestone_reward.php new file mode 100644 index 00000000..2a00d7df --- /dev/null +++ b/database/migrations/2023_07_02_210132_create_milestone_reward.php @@ -0,0 +1,35 @@ +unsignedBigInteger('milestone_id'); + $table->unsignedBigInteger('reward_id'); + // $table->timestamps(); + + $table->foreign('milestone_id')->references('id')->on('milestones')->onDelete('cascade'); + $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('milestone_reward'); + } +} diff --git a/database/migrations/2023_07_05_081135_create_voucher_entity_mappings.php b/database/migrations/2023_07_05_081135_create_voucher_entity_mappings.php new file mode 100644 index 00000000..5bae07b0 --- /dev/null +++ b/database/migrations/2023_07_05_081135_create_voucher_entity_mappings.php @@ -0,0 +1,36 @@ +id(); + $table->string('owner_type'); + $table->unsignedBigInteger('owner_id'); + $table->string('voucherify_entity_type'); + $table->string('voucherify_entity_id'); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('voucher_entity_mappings'); + } +} diff --git a/database/migrations/2023_07_08_155628_add_start_date_and_end_date_to_vouchers_table.php b/database/migrations/2023_07_08_155628_add_start_date_and_end_date_to_vouchers_table.php new file mode 100644 index 00000000..a88e8e0d --- /dev/null +++ b/database/migrations/2023_07_08_155628_add_start_date_and_end_date_to_vouchers_table.php @@ -0,0 +1,33 @@ +timestamp('start_date')->nullable(); + $table->timestamp('end_date')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('vouchers', function (Blueprint $table) { + $table->dropColumn(['start_date', 'end_date']); + }); + } +} diff --git a/database/seeds/AdminUserPermissionsTableSeeder.php b/database/seeds/AdminUserPermissionsTableSeeder.php index 132184d7..0c3d5acf 100644 --- a/database/seeds/AdminUserPermissionsTableSeeder.php +++ b/database/seeds/AdminUserPermissionsTableSeeder.php @@ -19,7 +19,7 @@ class AdminUserPermissionsTableSeeder extends Seeder app()['cache']->forget('spatie.permission.cache'); // admin permissions - $permissions = [ + $permissions = [ ['name' => 'view document', 'guard_name' => 'web'], ['name' => 'add document', 'guard_name' => 'web'], ['name' => 'edit document', 'guard_name' => 'web'], @@ -63,7 +63,14 @@ class AdminUserPermissionsTableSeeder extends Seeder ['name' => 'view booking', 'guard_name' => 'web'], ['name' => 'add booking', 'guard_name' => 'web'], ['name' => 'edit booking', 'guard_name' => 'web'], - ['name' => 'delete booking', 'guard_name' => 'web'] + ['name' => 'delete booking', 'guard_name' => 'web'], + + ['name' => 'add milestone', 'guard_name' => 'web'], + ['name' => 'add reward', 'guard_name' => 'web'], + ['name' => 'edit milestone', 'guard_name' => 'web'], + ['name' => 'delete milestone', 'guard_name' => 'web'], + ['name' => 'delete reward', 'guard_name' => 'web'], + ]; foreach ($permissions as $permission){ diff --git a/docker-setup/Dockerfile b/docker-setup/Dockerfile index 13ce0249..71255f30 100644 --- a/docker-setup/Dockerfile +++ b/docker-setup/Dockerfile @@ -2,6 +2,8 @@ FROM php:7.4-fpm WORKDIR /var/www/html +RUN pecl install xdebug-2.9.8 && docker-php-ext-enable xdebug + RUN docker-php-ext-install pdo pdo_mysql RUN apt-get update && apt-get install -y \ @@ -25,4 +27,15 @@ RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - RUN apt-get -y install nodejs RUN chown -R www-data:www-data /var/www -RUN chmod 755 /var/www \ No newline at end of file +RUN chmod 755 /var/www + +# Configure xdebug +RUN echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +RUN echo "xdebug.remote_autostart=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +RUN echo "xdebug.remote_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +RUN echo "xdebug.remote_port=9002" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +RUN echo "xdebug.idekey=VSCODE" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + +# Moved to docker-setup folder +# RUN echo 'pm.max_children = 15' >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ +# echo 'pm.max_requests = 500' >> /usr/local/etc/php-fpm.d/zz-docker.conf diff --git a/docker-setup/docker-compose.yml b/docker-setup/docker-compose.yml index deb02207..4dcfa8c2 100644 --- a/docker-setup/docker-compose.yml +++ b/docker-setup/docker-compose.yml @@ -44,8 +44,7 @@ services: container_name: exchange-php volumes: - ../:/var/www/html - ports: - - "9002:9000" + - ./php/default.conf:/usr/local/etc/php-fpm.d/zz-docker.conf networks: - exchange-staging ################################################################# diff --git a/docker-setup/nginx/default.conf b/docker-setup/nginx/default.conf index b594e701..c1811261 100644 --- a/docker-setup/nginx/default.conf +++ b/docker-setup/nginx/default.conf @@ -23,6 +23,13 @@ server { fastcgi_intercept_errors on; fastcgi_keep_conn on; fastcgi_param PHP_VALUE "auto_prepend_file= \n allow_url_include=Off"; + + # Xdebug configuration + # fastcgi_param XDEBUG_MODE debug; + # fastcgi_param XDEBUG_CLIENT_HOST host.docker.internal; + # fastcgi_param XDEBUG_CLIENT_PORT 9002; + # fastcgi_param XDEBUG_IDE_KEY VSCODE; + proxy_send_timeout 3600; proxy_read_timeout 3600; fastcgi_send_timeout 3600; diff --git a/docker-setup/php/default.conf b/docker-setup/php/default.conf new file mode 100644 index 00000000..dced3233 --- /dev/null +++ b/docker-setup/php/default.conf @@ -0,0 +1,8 @@ +[global] +daemonize = no + +[www] +listen = 9000 + +pm.max_children = 15 +pm.max_requests = 500 diff --git a/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue b/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue new file mode 100644 index 00000000..b3d50d7b --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue @@ -0,0 +1,138 @@ + + + diff --git a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue new file mode 100644 index 00000000..db2fc252 --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue @@ -0,0 +1,161 @@ + + + diff --git a/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue new file mode 100644 index 00000000..54db23df --- /dev/null +++ b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue @@ -0,0 +1,59 @@ + + diff --git a/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue b/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue new file mode 100644 index 00000000..9a5e0058 --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue @@ -0,0 +1,232 @@ + + diff --git a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue new file mode 100644 index 00000000..7bbf87aa --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue @@ -0,0 +1,272 @@ + + diff --git a/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue b/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue index f0c58095..9c89c89d 100644 --- a/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue +++ b/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue @@ -30,7 +30,6 @@ - {{ serviceType.id != 4 ? '*Please ensure to use the appropriate company name for corporate transfers, instead of personal names.' : ''}}
diff --git a/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue b/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue index f189c97c..68db519b 100644 --- a/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue +++ b/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue @@ -269,6 +269,24 @@
{{(Math.round((this.parameters.calculation.total + Number.EPSILON) * 100) / 100).toFixed(2)}}
+ @@ -301,7 +319,8 @@ dropdownStatus: false, account_no: '', step:1, - + promoCode:"" + } }, computed: { @@ -332,6 +351,51 @@ this.account_no = ''; this.parameters.bankAccount = {}; } + /* + applyPromocoe(){ + let promoRequestObj = { + category : 'New cat 4', + discount: { + "percent_off": 10.0, + "type": "PERCENT" + }, + redemption: { + quantity: 10 + }, + start_date: "2016-01-01T00:00:00Z", + expiration_date: "2016-12-31T23:59:59Z", + order: { + amount: 10, + currency: "USD", + id: "order_1234523" + } + }; + this.isLoading = true; + + let url = `https://as1.api.voucherify.io/v1/vouchers/${this.promoCode}/redemption`; + + return fetch(url, { + method: 'POST', + responseType: 'json', + body: JSON.stringify(promoRequestObj), + headers: { + 'content-type': 'application/json', + 'X-App-Token': 'b839bfa1-d8c1-4846-a367-05d466f48cfd', + 'X-App-Id': 'a180e3cb-ee34-469b-876f-d4a1139b82c6' + } + }).then(response => { + + console.log(response); + + if(response.status === 401 && window.location.href !== route('login')){ + dispatch('userAuthentication', {access_token: '', redirect_url: '/'}); + } + + return response; + + }) + } + */ }, mixins: [FormHandler] } diff --git a/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue b/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue new file mode 100644 index 00000000..3d441d53 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue @@ -0,0 +1,120 @@ + + diff --git a/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue b/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue new file mode 100644 index 00000000..a236bf8a --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue @@ -0,0 +1,134 @@ + + + \ No newline at end of file diff --git a/resources/assets/vue/components/bookings/elements/ListVouchers.vue b/resources/assets/vue/components/bookings/elements/ListVouchers.vue new file mode 100644 index 00000000..e59ff653 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/ListVouchers.vue @@ -0,0 +1,81 @@ + + + diff --git a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue index 702745c7..ce4ba5a7 100644 --- a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue +++ b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue @@ -33,6 +33,11 @@
{{ item.status === 2 ? 'Received' : item.status === 4 ? 'Rejected' : 'Submitted'}} On: {{item.updated_at}}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -86,6 +91,11 @@
{{ item.transaction_bill.status === 1 ? 'Paid On: ' + item.updated_at : 'Transferred On:' + item.transaction_bill.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -150,6 +160,17 @@
MYR {{(Math.round((item.service_charge + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
Voucher
+
+
+
- MYR {{(Math.round((item.redemption.value + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
- MYR 0.00
+
+
Tax
@@ -285,7 +306,7 @@ this.data.transaction_refunds.forEach(function(refunds) { TotalRequestedRefund += refunds.status === 1 ? refunds.original_amount : 0; }); - + return TotalRequestedRefund; }, totalRequestedConvertRefund() { diff --git a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue index 37a51a43..554530af 100644 --- a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue +++ b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue @@ -224,7 +224,45 @@
-
+
+
+ +
+
+ + +
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+ Apply a voucher +
+ + {{ voucherCodeFailedReason }} + Voucher applied +
+
@@ -406,7 +444,7 @@
- +
@@ -468,6 +506,17 @@
MYR {{(Math.round((calculation.sub_total + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
Voucher
+
+
+
- MYR {{(Math.round((calculation.voucher_discount_amount + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
- MYR 0.00
+
+
Tax {{(Math.round((calculation.tax + Number.EPSILON) * 10) / 10).toFixed(1)}}%
@@ -540,12 +589,18 @@ id: '', status: false }, - onlinePayment: { + onlinePayment: { id: '', status: false }, amount: (Math.round((this.data.outstanding_amount + Number.EPSILON) * 100) / 100).toFixed(2), - calculation: null + calculation: null, + voucherCode: '', + voucherCodeFailedReason: '', + voucherValidated: false, + voucherIsChecking: false, + showApplyVoucher: false, + voucherListDropDownStatus: false, } }, validations () { @@ -580,6 +635,7 @@ submitForm(){ this.parameters = { payment_method: this.paymentMethod.id, + voucherCode: this.voucherCode, amount: this.amount }; @@ -587,7 +643,22 @@ this.calculation = null; }, successHandler(response){ - this.calculation = response.payload.data; + if(!response.payload.data){ + this.voucherValidated = false; + this.voucherIsChecking = false; + this.voucherCodeFailedReason = 'Something went wrong. Please contact customer service.' + } + else if(response.payload.data.valid !== undefined && response.payload.data.code){ //applyVoucherCode() + this.voucherValidated = true; + if(response.payload.data.reason){ + this.voucherCodeFailedReason = response.payload.data.reason; + this.voucherValidated = false; + } + this.voucherIsChecking = false; + } + else { //submitForm() + this.calculation = response.payload.data; + } }, errorHandler(error) { this.error = error.message; @@ -600,9 +671,46 @@ cancelQuotation(){ this.calculation = null; this.expandPayment = false; + }, + updatedBankDetails(bank){ + this.item.bank = bank; + }, + removeVoucher(){ + this.voucherCodeFailedReason = ''; + this.voucherCode = ''; + this.showApplyVoucher = false; + }, + applyVoucherCode(){ + this.voucherIsChecking = true; + this.voucherCodeFailedReason = ''; + this.voucherValidated = false; + this.parameters = { + voucherCode: this.voucherCode, + amount: this.amount, + itemId: this.item.id + }; + if(this.voucherCode.trim() !== ''){ + this.submit(route('api.voucher.validate'), 'post', '', false, false); + } + else{ + this.voucherValidated = true; + this.voucherIsChecking = false; + } + }, + updateVoucherFlag(event){ + this.voucherCodeFailedReason = ''; + this.voucherValidated = false; + this.voucherCode = event.target.value; + }, + toggleDropdown() { + this.voucherListDropDownStatus = !this.voucherListDropDownStatus; + }, + handleSelectedVoucher(value){ + this.voucherCode = value; + this.voucherListDropDownStatus = !this.voucherListDropDownStatus; } }, mixins: [componentHandler], directives: {money: VMoney} } - \ No newline at end of file + diff --git a/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue b/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue index e670a450..0fc1cd49 100644 --- a/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue +++ b/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue @@ -28,7 +28,7 @@ v-bind:key="currency.id">
+ @click="updateCurrency(currency)">
@@ -59,6 +59,19 @@ diff --git a/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue b/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue new file mode 100644 index 00000000..8f25ab60 --- /dev/null +++ b/resources/assets/vue/components/companies/elements/SingleUserRewardItemComponent.vue @@ -0,0 +1,30 @@ + + diff --git a/resources/assets/vue/components/companies/forms/IdentificationVerificationFormComponent.vue b/resources/assets/vue/components/companies/forms/IdentificationVerificationFormComponent.vue index 2c1bfdea..b3669524 100644 --- a/resources/assets/vue/components/companies/forms/IdentificationVerificationFormComponent.vue +++ b/resources/assets/vue/components/companies/forms/IdentificationVerificationFormComponent.vue @@ -13,7 +13,7 @@
- +
diff --git a/resources/assets/vue/components/companies/sections/AddVoucherSectionComponent.vue b/resources/assets/vue/components/companies/sections/AddVoucherSectionComponent.vue new file mode 100644 index 00000000..65b0c2ed --- /dev/null +++ b/resources/assets/vue/components/companies/sections/AddVoucherSectionComponent.vue @@ -0,0 +1,106 @@ + + + diff --git a/resources/assets/vue/components/companies/sections/CustomerRewardsAdminSectionComponent.vue b/resources/assets/vue/components/companies/sections/CustomerRewardsAdminSectionComponent.vue new file mode 100644 index 00000000..9429593a --- /dev/null +++ b/resources/assets/vue/components/companies/sections/CustomerRewardsAdminSectionComponent.vue @@ -0,0 +1,119 @@ + + + diff --git a/resources/assets/vue/components/companies/sections/CustomerRewardsSectionComponent.vue b/resources/assets/vue/components/companies/sections/CustomerRewardsSectionComponent.vue new file mode 100644 index 00000000..83567aaf --- /dev/null +++ b/resources/assets/vue/components/companies/sections/CustomerRewardsSectionComponent.vue @@ -0,0 +1,112 @@ + + + diff --git a/resources/assets/vue/components/general/forms/GeneralConfirmationFormComponent.vue b/resources/assets/vue/components/general/forms/GeneralConfirmationFormComponent.vue new file mode 100644 index 00000000..8ac707bb --- /dev/null +++ b/resources/assets/vue/components/general/forms/GeneralConfirmationFormComponent.vue @@ -0,0 +1,60 @@ + + diff --git a/resources/assets/vue/components/general/forms/SelectComponent.vue b/resources/assets/vue/components/general/forms/SelectComponent.vue index 394ec13b..1f902903 100644 --- a/resources/assets/vue/components/general/forms/SelectComponent.vue +++ b/resources/assets/vue/components/general/forms/SelectComponent.vue @@ -33,7 +33,11 @@ $(this.$el).val(value).trigger('change'); }, options: function(options){ - $(this.$el).select2({ data: options }); + $(this.$el).select2({ + data: options, + minimumResultsForSearch: 6, + theme: 'bootstrap', + containerCssClass: 'form-control'}); this.value ? $(this.$el).val(this.value).trigger('change'):null; } }, diff --git a/resources/assets/vue/components/general/forms/SelectableHardCodedComponent.vue b/resources/assets/vue/components/general/forms/SelectableHardCodedComponent.vue new file mode 100644 index 00000000..05a4e007 --- /dev/null +++ b/resources/assets/vue/components/general/forms/SelectableHardCodedComponent.vue @@ -0,0 +1,22 @@ + + + diff --git a/resources/assets/vue/components/settings/elements/MilestoneSingleItemComponent.vue b/resources/assets/vue/components/settings/elements/MilestoneSingleItemComponent.vue new file mode 100644 index 00000000..ab4f013e --- /dev/null +++ b/resources/assets/vue/components/settings/elements/MilestoneSingleItemComponent.vue @@ -0,0 +1,89 @@ + + diff --git a/resources/assets/vue/components/settings/elements/RewardChecklistComponent.vue b/resources/assets/vue/components/settings/elements/RewardChecklistComponent.vue new file mode 100644 index 00000000..453bdbe5 --- /dev/null +++ b/resources/assets/vue/components/settings/elements/RewardChecklistComponent.vue @@ -0,0 +1,34 @@ + + + diff --git a/resources/assets/vue/components/settings/elements/RewardSingleItemComponent.vue b/resources/assets/vue/components/settings/elements/RewardSingleItemComponent.vue new file mode 100644 index 00000000..a8510027 --- /dev/null +++ b/resources/assets/vue/components/settings/elements/RewardSingleItemComponent.vue @@ -0,0 +1,93 @@ + + + diff --git a/resources/assets/vue/components/settings/forms/AddMilestoneFormComponent.vue b/resources/assets/vue/components/settings/forms/AddMilestoneFormComponent.vue new file mode 100644 index 00000000..461a447b --- /dev/null +++ b/resources/assets/vue/components/settings/forms/AddMilestoneFormComponent.vue @@ -0,0 +1,112 @@ + + diff --git a/resources/assets/vue/components/settings/forms/AddRewardFormComponent.vue b/resources/assets/vue/components/settings/forms/AddRewardFormComponent.vue new file mode 100644 index 00000000..b00be4b0 --- /dev/null +++ b/resources/assets/vue/components/settings/forms/AddRewardFormComponent.vue @@ -0,0 +1,158 @@ + + diff --git a/resources/assets/vue/components/settings/forms/DeleteMilestoneFormComponent.vue b/resources/assets/vue/components/settings/forms/DeleteMilestoneFormComponent.vue new file mode 100644 index 00000000..63e0980c --- /dev/null +++ b/resources/assets/vue/components/settings/forms/DeleteMilestoneFormComponent.vue @@ -0,0 +1,33 @@ + + diff --git a/resources/assets/vue/components/settings/forms/DeleteRewardFormComponent.vue b/resources/assets/vue/components/settings/forms/DeleteRewardFormComponent.vue new file mode 100644 index 00000000..ecde176a --- /dev/null +++ b/resources/assets/vue/components/settings/forms/DeleteRewardFormComponent.vue @@ -0,0 +1,33 @@ + + diff --git a/resources/assets/vue/components/settings/sections/MilestoneListComponent.vue b/resources/assets/vue/components/settings/sections/MilestoneListComponent.vue new file mode 100644 index 00000000..def19717 --- /dev/null +++ b/resources/assets/vue/components/settings/sections/MilestoneListComponent.vue @@ -0,0 +1,79 @@ + + + + diff --git a/resources/assets/vue/components/wallets/elements/WalletComponent.vue b/resources/assets/vue/components/wallets/elements/WalletComponent.vue index e2343899..c7789454 100644 --- a/resources/assets/vue/components/wallets/elements/WalletComponent.vue +++ b/resources/assets/vue/components/wallets/elements/WalletComponent.vue @@ -52,6 +52,11 @@
+
diff --git a/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue b/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue index f827134d..39f84398 100644 --- a/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue +++ b/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue @@ -19,13 +19,18 @@
MYR {{(Math.round((item.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
-
+
Submitted On: {{ item.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -66,6 +71,11 @@
Paid On: {{ item.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
+
+
+
Bill Number: {{ item.bill_no }}
+
+
diff --git a/resources/views/emails/accounts/payment_proof_email.blade.php b/resources/views/emails/accounts/payment_proof_email.blade.php new file mode 100644 index 00000000..b3653cdb --- /dev/null +++ b/resources/views/emails/accounts/payment_proof_email.blade.php @@ -0,0 +1,11 @@ +@extends('emails.layout.base') + +@section('content') +

Dear {{ucwords($user->name)}},

+

Thank you for using CIEF Exchange service!

+

We have completed your transfer transaction for the ref. no {{$booking->marking}}. If you would like to view the transaction, please click on the following link:

+
+
If you have questions, contact us at here. We truly appreciate your trust in our services and look forward to serving you in the future.
+@endsection \ No newline at end of file diff --git a/resources/views/pages/accounting/bank-statements/bank_statement.blade.php b/resources/views/pages/accounting/bank-statements/bank_statement.blade.php new file mode 100644 index 00000000..ed1f0db0 --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/bank_statement.blade.php @@ -0,0 +1,12 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ + + +
+
+@endsection diff --git a/resources/views/pages/accounting/bank-statements/details.blade.php b/resources/views/pages/accounting/bank-statements/details.blade.php new file mode 100644 index 00000000..ac83c27b --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/details.blade.php @@ -0,0 +1,8 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ +
+
+@endsection diff --git a/resources/views/pages/accounting/bank-statements/index.blade.php b/resources/views/pages/accounting/bank-statements/index.blade.php new file mode 100644 index 00000000..cb38d912 --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/index.blade.php @@ -0,0 +1,370 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+
+ + {{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
Complete
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{-- --}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Bank Statements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Transactions Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Miscellaneous Transactions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Reports & Analysis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + @foreach($accounts as $account) + + + + + + @endforeach + +
AccountCurrency
{{$account->number}}
{{$account->type}}
{{$account->currency}} + Open +
+
+
+
+
+
+
+
+
+
+
+ + +
+ +
+
+
+ Rerun +
+
+
+
+ + +
+ + Clear +
+
+
+ + + + + + + + + + + + + + + + @foreach($statements as $statement) + + + + + + + + + + @endforeach + +
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->number }}{{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} + View +
+ {{ $statements->links() }} +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
Deposit Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Withdrawal Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+@endsection +{{--@extends('layouts.base_portal')--}} +{{--@section('inner_content')--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
Import Bank Statement
--}} + +{{--
--}} +{{-- @if (session('success'))--}} +{{--
--}} +{{-- {{ session('success') }}--}} +{{--
--}} +{{-- @endif--}} +{{-- @if (session('error'))--}} +{{--
--}} +{{-- {{ session('error') }}--}} +{{--
--}} +{{-- @endif--}} + +{{--
--}} +{{-- @csrf--}} +{{--
--}} +{{-- --}} +{{-- --}} +{{--
--}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- @if (session('message'))--}} +{{--
--}} +{{-- {{ session('message') }}--}} +{{--
--}} +{{-- @endif--}} +{{--
Bank Statements--}} +{{-- View All--}} +{{--
--}} + +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--@endsection--}} diff --git a/resources/views/pages/accounting/bank-statements/indexv2.blade.php b/resources/views/pages/accounting/bank-statements/indexv2.blade.php new file mode 100644 index 00000000..f18a77ae --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/indexv2.blade.php @@ -0,0 +1,461 @@ +@extends('layouts.base_portal') +@section('inner_content') + + +
+
+
+

Accounting Dashboard

+
+
+
+
+
+
+
+
Import Bank Statement
+ +
+ @if (session('success')) +
+ {{ session('success') }} +
+ @endif + @if (session('error')) +
+ {{ session('error') }} +
+ @endif + +
+ @csrf +
+ + +
+ +
+
+
+
+
+
+
+
+
Transaction Filtering
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateTransaction TypeAccount NameDescriptionStatus
2023-04-11DepositBank Account 1Salary PaymentPending
2023-04-09WithdrawalBank Account 2Vendor PaymentReconciled
2023-04-07DepositBank Account 3Online Order PaymentException
+
+
+ + + +
+
+

Step 1: Start Mapping bank Transactions

+

This is the first step in the wizard.

+
+
+

Step 2: List of automatically mapped bank transactions

+

This is the second step in the wizard.

+
+
+

Step 3: List of bank transactions that require manual mapping

+

This is the third step in the wizard.

+
+
+

Step 4: Export invoices to accounting software

+

This is the fourth step in the wizard.

+
+
+

Step 5: Import invoices from accounting software

+

This is the fifth step in the wizard.

+
+
+

Step 6: Export payment receipts

+

This is the sixth step in the wizard.

+
+
+

Step 7: Import payment receipts

+

This is the final step in the wizard.

+
+
+ + +
    +
  • +
  • +
  • +
+ +
+
+

Tab 3 Content

+

Aliquam vitae magna sit amet tellus bibendum posuere. Integer nec leo quis arcu tincidunt eleifend. Suspendisse potenti. Proin ullamcorper sodales nisl vel tincidunt. Etiam malesuada, mauris sit amet tincidunt facilisis, nisl enim aliquet turpis, ac pretium lacus nulla a libero. Vivamus euismod ex vel sapien dignissim, a fringilla enim fringilla. Sed sit amet lobortis augue. Aenean ut neque ac elit interdum pretium vel vel purus. Duis ut magna at ante dignissim efficitur. Pellentesque molestie bibendum ipsum a malesuada. Nulla cursus vehicula felis vel dapibus. Suspendisse eget vulputate ex. In pulvinar tincidunt justo, eu viverra orci malesuada id. Sed posuere arcu ac diam finibus posuere.

+
+
+
+
+ + +
+ + Clear +
+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + @foreach($statements as $statement) + + + + + + + + + + @endforeach + +
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->name }} ({{ $statement->account->number }}){{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} + View +
+ {{ $statements->links() }} +
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateTransaction TypeAccount NameDescriptionStatus
2023-04-11DepositBank Account 1Salary PaymentPending
2023-04-09WithdrawalBank Account 2Vendor PaymentReconciled
2023-04-07DepositBank Account 3Online Order PaymentException
+
+
+
+
+ + +
+ + Clear +
+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + @foreach($statements as $statement) + + + + + + + + + + @endforeach + +
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->name }} ({{ $statement->account->number }}){{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} + View +
+ {{ $statements->links() }} +
+
+

Tab 3 Content

+

Aliquam vitae magna sit amet tellus bibendum posuere. Integer nec leo quis arcu tincidunt eleifend. Suspendisse potenti. Proin ullamcorper sodales nisl vel tincidunt. Etiam malesuada, mauris sit amet tincidunt facilisis, nisl enim aliquet turpis, ac pretium lacus nulla a libero. Vivamus euismod ex vel sapien dignissim, a fringilla enim fringilla. Sed sit amet lobortis augue. Aenean ut neque ac elit interdum pretium vel vel purus. Duis ut magna at ante dignissim efficitur. Pellentesque molestie bibendum ipsum a malesuada. Nulla cursus vehicula felis vel dapibus. Suspendisse eget vulputate ex. In pulvinar tincidunt justo, eu viverra orci malesuada id. Sed posuere arcu ac diam finibus posuere.

+
+
+
+
+
+
+
+
+
+ + + + ``` + Next, we add some custom CSS styles to the wizard to make it look more appealing. + php + Copy code + + Finally, we add some JavaScript code to handle the wizard functionality. We use the Bootstrap Wizard plugin to enable the wizard navigation buttons. + php + Copy code + +@endsection diff --git a/resources/views/pages/accounting/bank-statements/show.blade.php b/resources/views/pages/accounting/bank-statements/show.blade.php new file mode 100644 index 00000000..31a50f2d --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/show.blade.php @@ -0,0 +1,123 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+
+
Statement: {{ $statement->date_from->format('M d, Y') }} - {{ $statement->date_to->format('M d, Y') }}
+

Account: {{ $statement->account->name }} ({{ $statement->account->number }})

+{{--

Total Debit: {{ number_format($statement->total_debit, 2) }}

--}} +{{--

Total Credit: {{ number_format($statement->total_credit, 2) }}

--}} +

Beginning Balance: {{ number_format($statement->begin_balance, 2) }}

+

Ending Balance: {{ number_format($statement->end_balance, 2) }}

+
+
+ +
+
+
Transactions
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + @foreach ($transactions as $transaction) + @php + $from = $transaction->transaction_description_2; + $type = ''; + $paymentMethod = ''; + + if(!$transaction->transaction_description_2){ + $from = $transaction->transaction_description; + } + + if(str_contains($transaction->transaction_description_3, 'FPX')){ + $paymentMethod = 'FPX'; + } + + if(str_contains($transaction->transaction_description_3, 'A/C')){ + $paymentMethod = 'Bank Transfer'; + } + + if(str_contains($transaction->transaction_description_3, 'IBFT')){ + $paymentMethod = 'Bank Transfer'; + } + + if(str_contains($transaction->transaction_description_3, 'FOREIGN TT')){ + $paymentMethod = 'International Transfer'; + } + + if($transaction->amount > 0) { + $type = 'Sale'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description_3, 'ESI')) { + $type = 'Statutory Payment'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description_3, 'ESI')) { + $type = 'Statutory Payment'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description, 'DR DUITNOW S/CHRG')) { + $type = 'Bank Charge'; + $paymentMethod = 'Bank Deduction'; + + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description, 'CMS - DR CORP CHG')) { + $type = 'Card Payment'; + $paymentMethod = 'Card'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description_3, 'ELECTRONIC')) { + $type = 'Electric Bill'; + } + + @endphp + + + + + + + + + + @endforeach + +
DatetimeFromTypePayment MethodDebitCredit
{{ $transaction->posting_date->format('d-m-Y') }}{{ $transaction->posting_date->format('g:i A') }}{{ $from }}{{ $type }}{{ $paymentMethod }}{{ $transaction->amount < 0 ? number_format($transaction->amount, 2) : 0.00}}{{ $transaction->amount > 0 ? number_format($transaction->amount, 2) : 0.00}}
+
+
+
+ {{ $transactions->appends(request()->query())->links() }} +
+
+@endsection + diff --git a/resources/views/pages/bookings/filter.blade.php b/resources/views/pages/bookings/filter.blade.php new file mode 100644 index 00000000..f187597c --- /dev/null +++ b/resources/views/pages/bookings/filter.blade.php @@ -0,0 +1,17 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+
+
+
+
+ Transfer Filters +
+
+ +
+
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/pages/customers/reward.blade.php b/resources/views/pages/customers/reward.blade.php new file mode 100644 index 00000000..6332f664 --- /dev/null +++ b/resources/views/pages/customers/reward.blade.php @@ -0,0 +1,27 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ +
+
+
+ Vouchers +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+@endsection diff --git a/resources/views/pages/pdfs/deliver_order.blade.php b/resources/views/pages/pdfs/deliver_order.blade.php index f91a179a..5cd724bd 100644 --- a/resources/views/pages/pdfs/deliver_order.blade.php +++ b/resources/views/pages/pdfs/deliver_order.blade.php @@ -85,7 +85,18 @@ @php $subtotal = 0; @endphp - + @if($voucher_redemption) + @php + $voucher_discount = $voucher_redemption->value * -1; + $original_price = 1 / $voucher_redemption->transaction->currency_rate; + $currency_rate = $voucher_redemption->transaction->currency_rate; + @endphp + @else + @php + $voucher_discount = 0; + $currency_rate = $transaction->currency_rate; + @endphp + @endif @foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail) {{ $key + 1 }} @@ -94,7 +105,7 @@ {{ $transaction_detail->quantity }} @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2) }} + {{ number_format( (1/$currency_rate) * $transaction_detail->price, 2) }} @else {{ number_format($transaction_detail->price, 2) }} @endif @@ -102,10 +113,10 @@ @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format((float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} + {{ number_format((float)number_format( (1/$currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} @php - $subtotal += number_format((float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2,'.',''); + $subtotal += number_format((float)number_format( (1/$currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2,'.',''); @endphp @else {{ number_format((float)number_format($transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} @@ -133,12 +144,21 @@ {{ number_format($transaction->service_charge, 2) }} + @if($voucher_redemption) + + + Voucher ({{ $voucher_redemption->voucher->code }}) + + -{{ $voucher_redemption->value }} + + + @endif Adjustment @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format((float)number_format( (1/$transaction->currency_rate) * $transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} + {{ number_format((float)number_format( (1/$currency_rate) * $transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} @else {{ number_format((float)number_format($transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} @endif @@ -156,9 +176,9 @@ Total @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format( ((1/$transaction->currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax, 2) }} + {{ number_format( ((1/$currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax + $voucher_discount, 2) }} @else - {{ number_format($transaction->amount + $transaction->service_charge + $transaction->tax, 2) }} + {{ number_format($transaction->amount + $transaction->service_charge + $transaction->tax + $voucher_discount, 2) }} @endif diff --git a/resources/views/pages/pdfs/invoice.blade.php b/resources/views/pages/pdfs/invoice.blade.php index 0df3cb98..e489df26 100644 --- a/resources/views/pages/pdfs/invoice.blade.php +++ b/resources/views/pages/pdfs/invoice.blade.php @@ -84,7 +84,18 @@ @php $subtotal = 0; @endphp - + @if($voucher_redemption) + @php + $voucher_discount = $voucher_redemption->value * -1; + $original_price = 1 / $voucher_redemption->transaction->currency_rate; + $currency_rate = $voucher_redemption->transaction->currency_rate; + @endphp + @else + @php + $voucher_discount = 0; + $currency_rate = $transaction->currency_rate + @endphp + @endif @foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail) {{ $key + 1 }} @@ -93,7 +104,7 @@ {{ $transaction_detail->quantity }} @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2) }} + {{ number_format( (1/$currency_rate) * $transaction_detail->price, 2) }} @else {{ number_format($transaction_detail->price, 2) }} @endif @@ -101,10 +112,10 @@ @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format((float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} + {{ number_format((float)number_format( (1/$currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} @php - $subtotal += number_format((float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2,'.',''); + $subtotal += number_format((float)number_format( (1/$currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2,'.',''); @endphp @else {{ number_format((float)number_format($transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} @@ -132,12 +143,21 @@ {{ number_format($transaction->service_charge, 2) }} + @if($voucher_redemption) + + + Voucher ({{ $voucher_redemption->voucher->code }}) + + -{{ $voucher_redemption->value }} + + + @endif Adjustment @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format((float)number_format( (1/$transaction->currency_rate) * $transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} + {{ number_format((float)number_format( (1/$currency_rate) * $transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} @else {{ number_format((float)number_format($transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} @endif @@ -154,10 +174,15 @@ Total + @php + $service_charge = (float)number_format($transaction->service_charge, 2,'.',''); + $tax = (float)number_format($transaction->tax, 2,'.',''); + $rounded_voucher = (float)number_format($voucher_discount, 2,'.',''); + @endphp @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format( ((1/$transaction->currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax, 2) }} + {{ number_format( ((1/$transaction->currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax + $rounded_voucher, 2) }} @else - {{ number_format($transaction->amount + $transaction->service_charge + $transaction->tax, 2) }} + {{ number_format($transaction->amount + $transaction->service_charge + $transaction->tax + $rounded_voucher, 2) }} @endif diff --git a/resources/views/pages/pdfs/purchase_order.blade.php b/resources/views/pages/pdfs/purchase_order.blade.php index c3ac5e8a..85608c28 100644 --- a/resources/views/pages/pdfs/purchase_order.blade.php +++ b/resources/views/pages/pdfs/purchase_order.blade.php @@ -91,7 +91,18 @@ @php $subtotal = 0; @endphp - + @if($voucher_redemption) + @php + $voucher_discount = $voucher_redemption->value * -1; + $original_price = 1 / $voucher_redemption->transaction->currency_rate; + $currency_rate = $voucher_redemption->transaction->currency_rate; + @endphp + @else + @php + $voucher_discount = 0; + $currency_rate = $transaction->currency_rate; + @endphp + @endif @foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail) {{ $key + 1 }} @@ -100,7 +111,7 @@ {{ $transaction_detail->quantity }} @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2) }} + {{ number_format( (1/$currency_rate) * $transaction_detail->price, 2) }} @else {{ number_format($transaction_detail->price, 2) }} @endif @@ -108,10 +119,10 @@ @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format((float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} + {{ number_format((float)number_format( (1/$currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} @php - $subtotal += number_format((float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2,'.',''); + $subtotal += number_format((float)number_format( (1/$currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2,'.',''); @endphp @else {{ number_format((float)number_format($transaction_detail->price, 2,'.','')*$transaction_detail->quantity,2) }} @@ -139,12 +150,21 @@ {{ number_format($transaction->service_charge, 2) }} + @if($voucher_redemption) + + + Voucher ({{ $voucher_redemption->voucher->code }}) + + -{{ $voucher_redemption->value }} + + + @endif Adjustment @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format((float)number_format( (1/$transaction->currency_rate) * $transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} + {{ number_format((float)number_format( (1/$currency_rate) * $transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} @else {{ number_format((float)number_format($transaction->amount, 2,'.','') - (float)number_format($subtotal, 2,'.',''),2) }} @endif @@ -162,9 +182,9 @@ Total @if($transaction->booking()->first()->fix_currency_id !== 1) - {{ number_format( ((1/$transaction->currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax, 2) }} + {{ number_format( ((1/$currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax + $voucher_discount, 2) }} @else - {{ number_format($transaction->amount + $transaction->service_charge + $transaction->tax, 2) }} + {{ number_format($transaction->amount + $transaction->service_charge + $transaction->tax + $voucher_discount, 2) }} @endif diff --git a/resources/views/pages/rewards/customers.blade.php b/resources/views/pages/rewards/customers.blade.php new file mode 100644 index 00000000..edbabb74 --- /dev/null +++ b/resources/views/pages/rewards/customers.blade.php @@ -0,0 +1,23 @@ +
+
+
+
+
+ Vouchers +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
diff --git a/resources/views/pages/rewards/index.blade.php b/resources/views/pages/rewards/index.blade.php new file mode 100644 index 00000000..2a1daa8b --- /dev/null +++ b/resources/views/pages/rewards/index.blade.php @@ -0,0 +1,4 @@ +@extends('layouts.base_portal') +@section('inner_content') + @include('pages.rewards.customers') +@endsection diff --git a/resources/views/pages/settings.blade.php b/resources/views/pages/settings.blade.php index 100fb380..f79ddcd2 100644 --- a/resources/views/pages/settings.blade.php +++ b/resources/views/pages/settings.blade.php @@ -5,6 +5,7 @@
+
@@ -46,7 +47,7 @@
Labels
-
+
@@ -69,7 +70,7 @@
Seasonal Segment
-
+
@@ -208,7 +209,7 @@
Custom Segments
-
+ @@ -216,7 +217,7 @@ -
+
@@ -240,9 +241,57 @@
+
+
+
+
+
+
+
+
+ +
+
+
Milestones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
Rewards
+
+
+
+
+
+
+
+
+ +
@@ -294,7 +343,7 @@
- +
+
+ +
+
+
+
+
+ Milestones +
+
+
+ + + + +
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ Rewards +
+
+
+ + + + +
+
+
+
+ + + +
+
+
+
+
+
@@ -621,4 +738,4 @@
-@endsection \ No newline at end of file +@endsection diff --git a/resources/views/pages/transactions/history.blade.php b/resources/views/pages/transactions/history.blade.php new file mode 100644 index 00000000..49a8145c --- /dev/null +++ b/resources/views/pages/transactions/history.blade.php @@ -0,0 +1,8 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ +
+
+@endsection \ No newline at end of file diff --git a/resources/views/partials/header.blade.php b/resources/views/partials/header.blade.php index e5031575..b618e334 100644 --- a/resources/views/partials/header.blade.php +++ b/resources/views/partials/header.blade.php @@ -61,6 +61,9 @@ + @@ -114,4 +117,4 @@ @endif - \ No newline at end of file + diff --git a/routes/accounting.php b/routes/accounting.php new file mode 100644 index 00000000..c7112947 --- /dev/null +++ b/routes/accounting.php @@ -0,0 +1,19 @@ + 'accounting', 'as' => 'accounting.', 'namespace' => 'Accounting'], function () { + Route::post('/import', 'BankStatementController@import')->name('statement.import'); + Route::get('/bank_account', 'BankStatementController@transactions')->name('bank.transaction'); + Route::group(['prefix' => 'statements/{id}', 'as' => 'statement.'], function () { + Route::get('/details', 'BankStatementController@fetch')->name('details'); + Route::put('/details/update', 'BankStatementController@update')->name('details.update'); + }); + + Route::post('bankStatement/{id}/details/{status}', 'ApproveDuplicateBankStatementDetailsStatusController@update')->where('status', 'approve|reject')->name('bankStatement.details.status.update'); + + Route::group(['prefix' => 'statement_transaction', 'as' => 'statement_transaction.'], function () { + 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'); + }); +}); diff --git a/routes/api.php b/routes/api.php index b3e0002c..08673517 100644 --- a/routes/api.php +++ b/routes/api.php @@ -30,6 +30,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function Route::get('/storage/{fileName}/fetch', 'Documents\RenderDocumentController@fileStorageServe')->where(['fileName' => '.*'])->name('storage.document.file'); Route::post('/import/update-debtor/f614e339d7058904a831aad742e24d55', 'Imports\ImportUpdateDebtorController@import')->name('debtor.import'); Route::post('/import/upload-honey-trap', 'Imports\ImportHoneyTrapController@import')->name('honey_trap.upload'); + Route::post('/import/upload-import-invoices', 'Imports\ImportStatementInvoiceController@import')->name('import_invoices.upload'); + Route::post('/import/upload-import-receipt', 'Imports\ImportStatementReceiptsController@import')->name('import_receipts.upload'); require __DIR__ . '/company.php'; @@ -55,6 +57,14 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function require __DIR__ . '/wallet.php'; + require __DIR__ . '/voucher.php'; + + require __DIR__ . '/accounting.php'; + + require __DIR__ . '/reward.php'; + + require __DIR__ . '/milestone.php'; + // require __DIR__ . '/rate.php'; // require __DIR__ . '/receipt.php'; diff --git a/routes/milestone.php b/routes/milestone.php new file mode 100644 index 00000000..4777e8c8 --- /dev/null +++ b/routes/milestone.php @@ -0,0 +1,11 @@ + 'milestone', 'as' => 'milestone.', 'namespace' => 'Milestones'], function () { + Route::post('/create', 'CreateMilestoneController@create')->name('create'); + Route::put('/update/{id}', 'UpdateMilestoneController@update')->name('update'); + Route::get('/list', 'ListMilestonesController@list')->name('list'); + Route::get('/user/{id}/progress/list', 'ListMilestoneProgressController@list')->name('progress.list'); + Route::delete('/delete/{id}', 'DeleteMilestoneController@delete')->name('delete'); +}); diff --git a/routes/reward.php b/routes/reward.php new file mode 100644 index 00000000..c58b19f5 --- /dev/null +++ b/routes/reward.php @@ -0,0 +1,11 @@ + 'reward', 'as' => 'reward.', 'namespace' => 'Rewards'], function () { + Route::post('/create', 'CreateRewardController@create')->name('create'); + Route::get('/list', 'ListRewardsController@list')->name('list'); + Route::get('/list/details', 'ListRewardsDetailsController@list')->name('list.details'); + Route::get('/list/details/{user_id}', 'ListRewardsDetailsController@list')->name('list.details.admin'); + Route::delete('/delete/{id}', 'DeleteRewardController@delete')->name('delete'); +}); diff --git a/routes/transaction.php b/routes/transaction.php index b3dd6434..708c2439 100644 --- a/routes/transaction.php +++ b/routes/transaction.php @@ -22,6 +22,7 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' => Route::get('wallet/list', 'ListWalletTransactionsController@list')->name('wallet.list'); Route::get('/company/{id}/account/balance', 'FetchCompanyAccountBalanceController@fetch')->name('company.account.balance'); + Route::get('/company/{id}/transaction/fetch', 'FetchCompanyTransactionStatementController@fetch')->name('company.transaction.fetch'); Route::get('/bank/{id}/account/balance', 'FetchBankAccountBalanceController@fetch')->name('bank.account.balance'); diff --git a/routes/voucher.php b/routes/voucher.php new file mode 100644 index 00000000..17c980f5 --- /dev/null +++ b/routes/voucher.php @@ -0,0 +1,10 @@ + 'voucher', 'as' => 'voucher.', 'namespace' => 'Vouchers'], function () { + Route::post('fetch', 'ValidateVoucherController@validate')->name('validate'); + Route::post('create', 'CreateVoucherController@create')->name('create'); + // Route::post('redeem', 'RedeemVoucherController@redeem')->name('redeem'); + Route::get('/user/list', 'ListUserVouchersController@list')->name('user.list'); +}); diff --git a/routes/web.php b/routes/web.php index 2547d087..609b06ea 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ $id]); })->name('customer.profile'); +Route::get('/customer/{marking}/transactions', function ($marking) { + $id = \App\Models\Company::where('reference', '=', $marking)->first()->id; + return view('pages.transactions.history', ['id' => $id]); +})->name('account.statment'); + Route::get('/payments', function () { return view('pages.payments'); })->name('payments'); @@ -89,6 +97,10 @@ Route::get('/transfers', function () { return view('pages.bookings.index'); })->name('bookings'); +Route::get('/list-transfer', function () { + return view('pages.bookings.filter'); +})->name('list_transfer'); + Route::get('/bookings/urgent', function () { return view('pages.urgent_list'); })->name('bookings.urgent'); @@ -216,6 +228,7 @@ Route::get('/export/null-debtor/f614e339d7058904a831aad742e24d55', 'Exports\Expo Route::get('/export/payment-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@paymentTransactions')->name('paymentTransactions.export'); Route::get('/export/wallet-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@walletTransactions')->name('walletTransactions.export'); Route::get('/export/booking-transactions', 'Exports\ExportCustomersToExcelController@bookingTransactions')->name('export.transactions.booking'); +Route::get('/export/invoice-transactions/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@invoiceTransactions')->name('invoiceTransactions.export'); Route::get('/products', function (\App\Classes\Modules\Exports\Services\ExportsProducts $exportsProducts) { $bookings = Booking::where(function($query){ @@ -557,6 +570,18 @@ Route::get('/currency-rate-history', function () { return view('pages.rate_histories')->with('paymentMethods', $paymentMethods); })->name('currency_rate.history'); +Route::get('/statements', [BankStatementController::class, 'index'])->name('statements.index'); +Route::get('/statements/v2', [BankStatementController::class, 'indexv2'])->name('statements.indexv2'); +Route::post('/statements/import', [BankStatementController::class, 'import'])->name('statements.import'); +Route::get('/statements/{statement}/details', function ($statement) { + return view('pages.accounting.bank-statements.details', ['statement' => $statement]); +})->name('statements.transactions.details'); +Route::get('/statements/{account}/transactions', function ($account) { + return view('pages.accounting.bank-statements.bank_statement', ['account' => $account]); +})->name('statements.account.transactions'); +Route::get('/statements/{statement}', [BankStatementController::class, 'show'])->name('statements.show'); +Route::get('/statements/mapping/rerun', [BankStatementController::class, 'rerun'])->name('statements.rerun'); +Route::get('/statements/{statement}/download', 'StatementController@download')->name('statements.download'); Route::get('/bank-record', 'Imports\ImportBankRecordController@import'); Route::get('/po/outsource/check', function(){ @@ -600,4 +625,45 @@ Route::get('/po/outsource/check', function(){ Route::get('/upload-honey-trap', function () { return view('pages.honey_trap'); -})->name('upload_honey_trap'); \ No newline at end of file +})->name('upload_honey_trap'); + +Route::get('/open-purchase-order/{marking}/{from_date}/{to_date}', function ($marking, $from_date, $to_date, DeletesTransaction $deletesTransaction, DeletesDocument $deletesDocument) { + $company_id = Company::where('reference', $marking)->first()->id; + + $startDate = Carbon::createFromFormat('d-m-Y', $from_date)->startOfDay(); + $endDate = Carbon::createFromFormat('d-m-Y', $to_date)->endOfDay(); + + $bookings = Booking::where('company_id', $company_id)->whereBetween('created_at', [$startDate, $endDate])->get(); + + foreach ($bookings as $booking) { + $booking->status = ApprovalStatus::APPROVED; + $booking->save(); + + $transaction = $booking->transactions()->whereIn('type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->get(); + foreach ($transaction as $key => $row) { + $deletesTransaction->execute($row); + } + + $document = $booking->documents()->whereIn('document_type', [DocumentType::PURCHASE_ORDER, DocumentType::INVOICE, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->get(); + foreach ($document as $key => $row) { + $deletesDocument->execute($row); + } + + $puchase_order = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first(); + if($puchase_order) { + $puchase_order->status = ApprovalStatus::PENDING_SUBMISSION; + $puchase_order->save(); + } + + dump('done - ' . $booking->marking); + } +}); + +Route::get('/vouchers', function () { + return view('pages.rewards.index'); +})->name('rewards'); + +Route::get('/customer/vouchers/{marking}', function ($marking) { + $id = \App\Models\Company::where('reference', '=', $marking)->first()->employees->first()->id; + return view('pages.customers.reward', ['id' => $id]); +})->name('customer.reward');